Hi,
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.
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;
/* get %cpu and schedule state: just one thread or sum of all? */
if (isthread) {
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 16 Oct 2019 15:15:31 -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.214
diff -u -p -r1.214 kern_fork.c
--- sys/kern/kern_fork.c 15 Oct 2019 10:05:43 -0000 1.214
+++ sys/kern/kern_fork.c 16 Oct 2019 15:15:31 -0000
@@ -469,7 +469,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 16 Oct 2019 15:15:31 -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 16 Oct 2019 15:15:31 -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 16 Oct 2019 15:15:31 -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[0].n_name = "_time_uptime";
+ nl[0].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
*/
@@ -289,6 +313,16 @@ kvm_proclist(kvm_t *kd, int op, int arg,
kp.p_tpgid = -1;
kp.p_tdev = NODEV;
}
+
+ /* Convert the starting uptime to a starting UTC time. */
+ 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 */
if (dothreads) {
Index: sys/sys/proc.h
===================================================================
RCS file: /cvs/src/sys/sys/proc.h,v
retrieving revision 1.273
diff -u -p -r1.273 proc.h
--- sys/sys/proc.h 2 Aug 2019 02:17:35 -0000 1.273
+++ sys/sys/proc.h 16 Oct 2019 15:15:31 -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/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 16 Oct 2019 15:15:31 -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 16 Oct 2019 15:15:31 -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