On Thu, Oct 17, 2019 at 05:55:20AM -0600, Todd C. Miller wrote:
> On Wed, 16 Oct 2019 10:23:52 -0500, Scott Cheloha wrote:
>
> > Tracking the process starting time as an uptime fixes the classic
> > "init(8) started in 1969" bug in ps(1) when your CMOS battery dies.
> >
> > In general it lets us track how long a process has been running
> > correctly regardless of whether or not the kernel UTC clock has
> > jumped.
>
> One comment inline.
>
> > NetBSD and FreeBSD have already done this.
> >
> > For kernel core dumps we need to do a bit of shuffling in libkvm to
> > determine the approximate starting time. The granularity is much more
> > limited in this case, but it's a start.
> >
> > I have added nanoboottime(9) here because it simplifies the math
> > in kern_acct.c. I can commit that first in a separate patch.
> >
> > ok?
> >
> > Index: sys/kern/kern_sysctl.c
> > ===================================================================
> > RCS file: /cvs/src/sys/kern/kern_sysctl.c,v
> > retrieving revision 1.366
> > diff -u -p -r1.366 kern_sysctl.c
> > --- sys/kern/kern_sysctl.c 21 Aug 2019 20:44:09 -0000 1.366
> > +++ sys/kern/kern_sysctl.c 16 Oct 2019 15:15:31 -0000
> > @@ -1637,7 +1637,8 @@ fill_kproc(struct process *pr, struct ki
> > struct session *s = pr->ps_session;
> > struct tty *tp;
> > struct vmspace *vm = pr->ps_vmspace;
> > - struct timespec ut, st;
> > + struct timespec st, ut;
> > + struct timeval booted, starting_uptime, starting_utc;
> > int isthread;
> >
> > isthread = p != NULL;
> > @@ -1679,6 +1680,13 @@ fill_kproc(struct process *pr, struct ki
> > ki->p_cpuid = CPU_INFO_UNIT(p->p_cpu);
> > #endif
> > }
> > +
> > + /* Convert starting uptime to a starting UTC time. */
> > + TIMESPEC_TO_TIMEVAL(&starting_uptime, &pr->ps_start);
> > + microboottime(&booted);
> > + timeradd(&booted, &starting_uptime, &starting_utc);
> > + ki->p_ustart_sec = starting_utc.tv_sec;
> > + ki->p_ustart_usec = starting_utc.tv_usec;
>
> FILL_KPROC only fills in p_ustart_sec and p_ustart_usec for non-zombie
> processes. You probably want to do the same. You are also missing
> a diff to sys/sysctl.h to remove p_ustart_sec and p_ustart_usec from
> FILL_KPROC.
Any reason we don't update those fields for zombies? Are they really
never interesting after the process has exited or died?
> > /* get %cpu and schedule state: just one thread or sum of all? */
> > if (isthread) {
>
> The rest looks fine, modulo the nlist issue bluhm@ noticed.
Updated diff below.
Index: sys/kern/kern_sysctl.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sysctl.c,v
retrieving revision 1.366
diff -u -p -r1.366 kern_sysctl.c
--- sys/kern/kern_sysctl.c 21 Aug 2019 20:44:09 -0000 1.366
+++ sys/kern/kern_sysctl.c 21 Oct 2019 17:14:06 -0000
@@ -1637,7 +1637,8 @@ fill_kproc(struct process *pr, struct ki
struct session *s = pr->ps_session;
struct tty *tp;
struct vmspace *vm = pr->ps_vmspace;
- struct timespec ut, st;
+ struct timespec st, ut;
+ struct timeval booted, starting_uptime, starting_utc;
int isthread;
isthread = p != NULL;
@@ -1673,6 +1674,13 @@ fill_kproc(struct process *pr, struct ki
ki->p_uutime_usec = ut.tv_nsec/1000;
ki->p_ustime_sec = st.tv_sec;
ki->p_ustime_usec = st.tv_nsec/1000;
+
+ /* Convert starting uptime to a starting UTC time. */
+ TIMESPEC_TO_TIMEVAL(&starting_uptime, &pr->ps_start);
+ microboottime(&booted);
+ timeradd(&booted, &starting_uptime, &starting_utc);
+ ki->p_ustart_sec = starting_utc.tv_sec;
+ ki->p_ustart_usec = starting_utc.tv_usec;
#ifdef MULTIPROCESSOR
if (p->p_cpu != NULL)
Index: sys/kern/kern_acct.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_acct.c,v
retrieving revision 1.41
diff -u -p -r1.41 kern_acct.c
--- sys/kern/kern_acct.c 6 Oct 2019 16:24:14 -0000 1.41
+++ sys/kern/kern_acct.c 21 Oct 2019 17:14:06 -0000
@@ -171,7 +171,7 @@ acct_process(struct proc *p)
struct acct acct;
struct process *pr = p->p_p;
struct rusage *r;
- struct timespec ut, st, tmp;
+ struct timespec booted, elapsed, realstart, uptime, ut, st, tmp;
int t;
struct vnode *vp;
int error = 0;
@@ -203,10 +203,12 @@ acct_process(struct proc *p)
acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_nsec);
/* (3) The elapsed time the command ran (and its starting time) */
- acct.ac_btime = pr->ps_start.tv_sec;
- getnanotime(&tmp);
- timespecsub(&tmp, &pr->ps_start, &tmp);
- acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_nsec);
+ nanouptime(&uptime);
+ nanoboottime(&booted);
+ timespecadd(&booted, &uptime, &realstart);
+ acct.ac_btime = realstart.tv_sec;
+ timespecsub(&uptime, &pr->ps_start, &elapsed);
+ acct.ac_etime = encode_comp_t(elapsed.tv_sec, elapsed.tv_nsec);
/* (4) The average amount of memory used */
r = &p->p_ru;
Index: sys/kern/kern_fork.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_fork.c,v
retrieving revision 1.215
diff -u -p -r1.215 kern_fork.c
--- sys/kern/kern_fork.c 21 Oct 2019 10:24:01 -0000 1.215
+++ sys/kern/kern_fork.c 21 Oct 2019 17:14:06 -0000
@@ -462,7 +462,7 @@ fork1(struct proc *curp, int flags, void
/*
* For new processes, set accounting bits and mark as complete.
*/
- getnanotime(&pr->ps_start);
+ nanouptime(&pr->ps_start);
pr->ps_acflag = AFORK;
atomic_clearbits_int(&pr->ps_flags, PS_EMBRYO);
Index: sys/kern/init_main.c
===================================================================
RCS file: /cvs/src/sys/kern/init_main.c,v
retrieving revision 1.290
diff -u -p -r1.290 init_main.c
--- sys/kern/init_main.c 21 Jun 2019 09:39:48 -0000 1.290
+++ sys/kern/init_main.c 21 Oct 2019 17:14:06 -0000
@@ -517,7 +517,7 @@ main(void *framep)
* munched in mi_switch() after the time got set.
*/
LIST_FOREACH(pr, &allprocess, ps_list) {
- getnanotime(&pr->ps_start);
+ nanouptime(&pr->ps_start);
TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
nanouptime(&p->p_cpu->ci_schedstate.spc_runtime);
timespecclear(&p->p_rtime);
Index: sys/kern/kern_tc.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_tc.c,v
retrieving revision 1.48
diff -u -p -r1.48 kern_tc.c
--- sys/kern/kern_tc.c 3 Jun 2019 01:27:30 -0000 1.48
+++ sys/kern/kern_tc.c 21 Oct 2019 17:14:06 -0000
@@ -167,6 +167,15 @@ microboottime(struct timeval *tvp)
}
void
+nanoboottime(struct timespec *tsp)
+{
+ struct bintime bt;
+
+ binboottime(&bt);
+ BINTIME_TO_TIMESPEC(&bt, tsp);
+}
+
+void
binuptime(struct bintime *bt)
{
struct timehands *th;
Index: lib/libkvm/kvm_proc2.c
===================================================================
RCS file: /cvs/src/lib/libkvm/kvm_proc2.c,v
retrieving revision 1.29
diff -u -p -r1.29 kvm_proc2.c
--- lib/libkvm/kvm_proc2.c 23 Jun 2019 16:57:02 -0000 1.29
+++ lib/libkvm/kvm_proc2.c 21 Oct 2019 17:14:06 -0000
@@ -117,16 +117,40 @@ kvm_proclist(kvm_t *kd, int op, int arg,
struct process process, process2;
struct pgrp pgrp;
struct tty tty;
+ struct timeval elapsed, monostart, monostop, realstart, realstop;
+ struct nlist nl[3];
struct sigacts sa, *sap;
struct vmspace vm, *vmp;
struct plimit limits, *limp;
pid_t parent_pid, leader_pid;
int cnt = 0;
int dothreads = 0;
+ int i;
dothreads = op & KERN_PROC_SHOW_THREADS;
op &= ~KERN_PROC_SHOW_THREADS;
+ /* Anchor a time to compare process starting times from. */
+ nl[0].n_name = "_time_second";
+ nl[1].n_name = "_time_uptime";
+ nl[2].n_name = NULL;
+ if (kvm_nlist(kd, nl) != 0) {
+ for (i = 0; nl[i].n_type != 0; ++i)
+ continue;
+ _kvm_err(kd, kd->program, "%s: no such symbol", nl[i].n_name);
+ return (-1);
+ }
+ timerclear(&realstop);
+ timerclear(&monostop);
+ if (KREAD(kd, nl[0].n_value, &realstop.tv_sec)) {
+ _kvm_err(kd, kd->program, "cannot read time_second");
+ return (-1);
+ }
+ if (KREAD(kd, nl[1].n_value, &monostop.tv_sec)) {
+ _kvm_err(kd, kd->program, "cannot read time_uptime");
+ return (-1);
+ }
+
/*
* Modelled on sysctl_doproc() in sys/kern/kern_sysctl.c
*/
@@ -288,6 +312,18 @@ kvm_proclist(kvm_t *kd, int op, int arg,
} else {
kp.p_tpgid = -1;
kp.p_tdev = NODEV;
+ }
+
+ /* Convert the starting uptime to a starting UTC time. */
+ if ((process.ps_flags & PS_ZOMBIE) == 0) {
+ monostart.tv_sec = kp.p_ustart_sec;
+ monostart.tv_usec = kp.p_ustart_usec;
+ timersub(&monostop, &monostart, &elapsed);
+ if (elapsed.tv_sec < 0)
+ timerclear(&elapsed);
+ timersub(&realstop, &elapsed, &realstart);
+ kp.p_ustart_sec = realstart.tv_sec;
+ kp.p_ustart_usec = realstart.tv_usec;
}
/* update %cpu for all threads */
Index: sys/sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
retrieving revision 1.274
diff -u -p -r1.274 proc.h
--- sys/sys/proc.h 21 Oct 2019 10:24:01 -0000 1.274
+++ sys/sys/proc.h 21 Oct 2019 17:14:06 -0000
@@ -260,7 +260,7 @@ struct process {
#define ps_endcopy ps_refcnt
int ps_refcnt; /* Number of references. */
- struct timespec ps_start; /* starting time. */
+ struct timespec ps_start; /* starting uptime. */
struct timeout ps_realit_to; /* real-time itimer trampoline. */
};
Index: sys/sys/sysctl.h
===================================================================
RCS file: /cvs/src/sys/sys/sysctl.h,v
retrieving revision 1.195
diff -u -p -r1.195 sysctl.h
--- sys/sys/sysctl.h 21 Aug 2019 20:44:09 -0000 1.195
+++ sys/sys/sysctl.h 21 Oct 2019 17:14:06 -0000
@@ -670,9 +670,6 @@ do {
\
\
(kp)->p_uvalid = 1; \
\
- (kp)->p_ustart_sec = (pr)->ps_start.tv_sec; \
- (kp)->p_ustart_usec = (pr)->ps_start.tv_nsec/1000; \
- \
(kp)->p_uru_maxrss = (p)->p_ru.ru_maxrss; \
(kp)->p_uru_ixrss = (p)->p_ru.ru_ixrss; \
(kp)->p_uru_idrss = (p)->p_ru.ru_idrss; \
Index: sys/sys/time.h
===================================================================
RCS file: /cvs/src/sys/sys/time.h,v
retrieving revision 1.46
diff -u -p -r1.46 time.h
--- sys/sys/time.h 3 Aug 2019 22:53:45 -0000 1.46
+++ sys/sys/time.h 21 Oct 2019 17:14:06 -0000
@@ -293,6 +293,7 @@ void getmicrouptime(struct timeval *);
void binboottime(struct bintime *);
void microboottime(struct timeval *);
+void nanoboottime(struct timespec *);
struct proc;
int clock_gettime(struct proc *, clockid_t, struct timespec *);
Index: share/man/man9/microtime.9
===================================================================
RCS file: /cvs/src/share/man/man9/microtime.9,v
retrieving revision 1.18
diff -u -p -r1.18 microtime.9
--- share/man/man9/microtime.9 19 Jan 2019 01:53:44 -0000 1.18
+++ share/man/man9/microtime.9 21 Oct 2019 17:14:06 -0000
@@ -41,6 +41,7 @@
.Nm getnanotime ,
.Nm nanouptime ,
.Nm getnanouptime ,
+.Nm nanoboottime ,
.Nm bintime ,
.Nm binuptime ,
.Nm binboottime
@@ -81,6 +82,10 @@
.Fc
.Ft void
.Fo getnanouptime
+.Fa "struct timespec *tv"
+.Fc
+.Ft void
+.Fo nanoboottime
.Fa "struct timespec *tv"
.Fc
.Ft void