summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordds <dds@FreeBSD.org>2007-05-22 06:51:38 +0000
committerdds <dds@FreeBSD.org>2007-05-22 06:51:38 +0000
commit9ed27d10382c7a5e19ef98f5201d68ed00734006 (patch)
tree22afa8b2db7c7547de2d9babcfaa07cd7d1bbcb6 /sys
parentb6fc65f3b511764ceab1cb5f1d80483816c1fd88 (diff)
downloadFreeBSD-src-9ed27d10382c7a5e19ef98f5201d68ed00734006.zip
FreeBSD-src-9ed27d10382c7a5e19ef98f5201d68ed00734006.tar.gz
Increase precision of time values in the process accounting
structure, while maintaining backward compatibility with legacy file and record formats.
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_acct.c144
-rw-r--r--sys/sys/acct.h71
2 files changed, 166 insertions, 49 deletions
diff --git a/sys/kern/kern_acct.c b/sys/kern/kern_acct.c
index 39c430f..6783fba 100644
--- a/sys/kern/kern_acct.c
+++ b/sys/kern/kern_acct.c
@@ -101,18 +101,26 @@ __FBSDID("$FreeBSD$");
* Leffler, et al.: The Design and Implementation of the 4.3BSD
* UNIX Operating System (Addison Welley, 1989)
* on pages 62-63.
+ * On May 2007 the historic 3 bits base 8 exponent, 13 bit fraction
+ * compt_t representation described in the above reference was replaced
+ * with that of IEEE-754 floats.
*
* Arguably, to simplify accounting operations, this mechanism should
* be replaced by one in which an accounting log file (similar to /dev/klog)
* is read by a user process, etc. However, that has its own problems.
*/
+/* Floating point definitions from <float.h>. */
+#define FLT_MANT_DIG 24 /* p */
+#define FLT_MAX_EXP 128 /* emax */
+
/*
* Internal accounting functions.
* The former's operation is described in Leffler, et al., and the latter
* was provided by UCB with the 4.4BSD-Lite release
*/
-static comp_t encode_comp_t(u_long, u_long);
+static uint32_t encode_timeval(struct timeval);
+static uint32_t encode_long(long);
static void acctwatch(void);
static void acct_thread(void *);
static int acct_disable(struct thread *);
@@ -325,7 +333,7 @@ acct_disable(struct thread *td)
int
acct_process(struct thread *td)
{
- struct acct acct;
+ struct acctv2 acct;
struct timeval ut, st, tmp;
struct plimit *newlim, *oldlim;
struct proc *p;
@@ -363,8 +371,8 @@ acct_process(struct thread *td)
/* (2) The amount of user and system time that was used */
calcru(p, &ut, &st);
- acct.ac_utime = encode_comp_t(ut.tv_sec, ut.tv_usec);
- acct.ac_stime = encode_comp_t(st.tv_sec, st.tv_usec);
+ acct.ac_utime = encode_timeval(ut);
+ acct.ac_stime = encode_timeval(st);
/* (3) The elapsed time the command ran (and its starting time) */
tmp = boottime;
@@ -372,20 +380,22 @@ acct_process(struct thread *td)
acct.ac_btime = tmp.tv_sec;
microuptime(&tmp);
timevalsub(&tmp, &p->p_stats->p_start);
- acct.ac_etime = encode_comp_t(tmp.tv_sec, tmp.tv_usec);
+ acct.ac_etime = encode_timeval(tmp);
/* (4) The average amount of memory used */
r = &p->p_stats->p_ru;
tmp = ut;
timevaladd(&tmp, &st);
+ /* Convert tmp (i.e. u + s) into hz units to match ru_i*. */
t = tmp.tv_sec * hz + tmp.tv_usec / tick;
if (t)
- acct.ac_mem = (r->ru_ixrss + r->ru_idrss + r->ru_isrss) / t;
+ acct.ac_mem = encode_long((r->ru_ixrss + r->ru_idrss +
+ + r->ru_isrss) / t);
else
acct.ac_mem = 0;
/* (5) The number of disk I/O operations done */
- acct.ac_io = encode_comp_t(r->ru_inblock + r->ru_oublock, 0);
+ acct.ac_io = encode_long(r->ru_inblock + r->ru_oublock);
/* (6) The UID and GID of the process */
acct.ac_uid = p->p_ucred->cr_ruid;
@@ -400,9 +410,15 @@ acct_process(struct thread *td)
SESS_UNLOCK(p->p_session);
/* (8) The boolean flags that tell how the process terminated, etc. */
- acct.ac_flag = p->p_acflag;
+ acct.ac_flagx = p->p_acflag;
PROC_UNLOCK(p);
+ /* Setup ancillary structure fields. */
+ acct.ac_flagx |= ANVER;
+ acct.ac_zero = 0;
+ acct.ac_version = 2;
+ acct.ac_len = acct.ac_len2 = sizeof(acct);
+
/*
* Eliminate any file size rlimit.
*/
@@ -428,44 +444,102 @@ acct_process(struct thread *td)
return (ret);
}
+/* FLOAT_CONVERSION_START (Regression testing; don't remove this line.) */
+
+/* Convert timevals and longs into IEEE-754 bit patterns. */
+
+/* Mantissa mask (MSB is implied, so subtract 1). */
+#define MANT_MASK ((1 << (FLT_MANT_DIG - 1)) - 1)
+
/*
- * Encode_comp_t converts from ticks in seconds and microseconds
- * to ticks in 1/AHZ seconds. The encoding is described in
- * Leffler, et al., on page 63.
+ * We calculate integer values to a precision of approximately
+ * 28 bits.
+ * This is high-enough precision to fill the 24 float bits
+ * and low-enough to avoid overflowing the 32 int bits.
*/
+#define CALC_BITS 28
-#define MANTSIZE 13 /* 13 bit mantissa. */
-#define EXPSIZE 3 /* Base 8 (3 bit) exponent. */
-#define MAXFRACT ((1 << MANTSIZE) - 1) /* Maximum fractional value. */
+/* log_2(1000000). */
+#define LOG2_1M 20
-static comp_t
-encode_comp_t(u_long s, u_long us)
+/*
+ * Convert the elements of a timeval into a 32-bit word holding
+ * the bits of a IEEE-754 float.
+ * The float value represents the timeval's value in microsecond units.
+ */
+static uint32_t
+encode_timeval(struct timeval tv)
{
- int exp, rnd;
-
- exp = 0;
- rnd = 0;
- s *= AHZ;
- s += us / (1000000 / AHZ); /* Maximize precision. */
+ int log2_s;
+ int val, exp; /* Unnormalized value and exponent */
+ int norm_exp; /* Normalized exponent */
+ int shift;
- while (s > MAXFRACT) {
- rnd = s & (1 << (EXPSIZE - 1)); /* Round up? */
- s >>= EXPSIZE; /* Base 8 exponent == 3 bit shift. */
- exp++;
+ /*
+ * First calculate value and exponent to about CALC_BITS precision.
+ * Note that the following conditionals have been ordered so that
+ * the most common cases appear first.
+ */
+ if (tv.tv_sec == 0) {
+ if (tv.tv_usec == 0)
+ return (0);
+ exp = 0;
+ val = tv.tv_usec;
+ } else {
+ /*
+ * Calculate the value to a precision of approximately
+ * CALC_BITS.
+ */
+ log2_s = fls(tv.tv_sec) - 1;
+ if (log2_s + LOG2_1M < CALC_BITS) {
+ exp = 0;
+ val = 1000000 * tv.tv_sec + tv.tv_usec;
+ } else {
+ exp = log2_s + LOG2_1M - CALC_BITS;
+ val = (unsigned int)(((u_int64_t)1000000 * tv.tv_sec +
+ tv.tv_usec) >> exp);
+ }
}
+ /* Now normalize and pack the value into an IEEE-754 float. */
+ norm_exp = fls(val) - 1;
+ shift = FLT_MANT_DIG - norm_exp - 1;
+#ifdef ACCT_DEBUG
+ printf("val=%d exp=%d shift=%d log2(val)=%d\n",
+ val, exp, shift, norm_exp);
+ printf("exp=%x mant=%x\n", FLT_MAX_EXP - 1 + exp + norm_exp,
+ ((shift > 0 ? (val << shift) : (val >> -shift)) & MANT_MASK));
+#endif
+ return (((FLT_MAX_EXP - 1 + exp + norm_exp) << (FLT_MANT_DIG - 1)) |
+ ((shift > 0 ? val << shift : val >> -shift) & MANT_MASK));
+}
- /* If we need to round up, do it (and handle overflow correctly). */
- if (rnd && (++s > MAXFRACT)) {
- s >>= EXPSIZE;
- exp++;
- }
+/*
+ * Convert a non-negative long value into the bit pattern of
+ * an IEEE-754 float value.
+ */
+static uint32_t
+encode_long(long val)
+{
+ int norm_exp; /* Normalized exponent */
+ int shift;
- /* Clean it up and polish it off. */
- exp <<= MANTSIZE; /* Shift the exponent into place */
- exp += s; /* and add on the mantissa. */
- return (exp);
+ KASSERT(val >= 0, ("encode_long: -ve value %ld", val));
+ if (val == 0)
+ return (0);
+ norm_exp = fls(val) - 1;
+ shift = FLT_MANT_DIG - norm_exp - 1;
+#ifdef ACCT_DEBUG
+ printf("val=%d shift=%d log2(val)=%d\n",
+ val, shift, norm_exp);
+ printf("exp=%x mant=%x\n", FLT_MAX_EXP - 1 + exp + norm_exp,
+ ((shift > 0 ? (val << shift) : (val >> -shift)) & MANT_MASK));
+#endif
+ return (((FLT_MAX_EXP - 1 + norm_exp) << (FLT_MANT_DIG - 1)) |
+ ((shift > 0 ? val << shift : val >> -shift) & MANT_MASK));
}
+/* FLOAT_CONVERSION_END (Regression testing; don't remove this line.) */
+
/*
* Periodically check the filesystem to see if accounting
* should be turned on or off. Beware the case where the vnode
diff --git a/sys/sys/acct.h b/sys/sys/acct.h
index 167a90c..7c118ed 100644
--- a/sys/sys/acct.h
+++ b/sys/sys/acct.h
@@ -38,45 +38,88 @@
#ifndef _SYS_ACCT_H_
#define _SYS_ACCT_H_
+#ifdef _KERNEL
+#define float uint32_t
+#endif
+
+#define AC_COMM_LEN 16
+
/*
- * Accounting structures; these use a comp_t type which is a 3 bits base 8
- * exponent, 13 bit fraction ``floating point'' number. Units are 1/AHZ
- * seconds.
+ * Accounting structure version 2 (current).
+ * The first byte is always zero.
+ * Time units are microseconds.
*/
-typedef u_int16_t comp_t;
-#define AC_COMM_LEN 16
-struct acct {
+struct acctv2 {
+ uint8_t ac_zero; /* zero identifies new version */
+ uint8_t ac_version; /* record version number */
+ uint16_t ac_len; /* record length */
+
char ac_comm[AC_COMM_LEN]; /* command name */
- comp_t ac_utime; /* user time */
- comp_t ac_stime; /* system time */
- comp_t ac_etime; /* elapsed time */
+ float ac_utime; /* user time */
+ float ac_stime; /* system time */
+ float ac_etime; /* elapsed time */
time_t ac_btime; /* starting time */
uid_t ac_uid; /* user id */
gid_t ac_gid; /* group id */
- u_int16_t ac_mem; /* average memory usage */
- comp_t ac_io; /* count of IO blocks */
+ float ac_mem; /* average memory usage */
+ float ac_io; /* count of IO blocks */
__dev_t ac_tty; /* controlling tty */
+ uint16_t ac_len2; /* record length */
+ union {
+ __dev_t ac_align; /* force v1 compatible alignment */
+
#define AFORK 0x01 /* forked but not exec'ed */
/* ASU is no longer supported */
#define ASU 0x02 /* used super-user permissions */
#define ACOMPAT 0x04 /* used compatibility mode */
#define ACORE 0x08 /* dumped core */
#define AXSIG 0x10 /* killed by a signal */
- u_int8_t ac_flag; /* accounting flags */
+#define ANVER 0x20 /* new record version */
+
+ uint8_t ac_flag; /* accounting flags */
+ } ac_trailer;
+
+#define ac_flagx ac_trailer.ac_flag
+};
+
+
+/*
+ * Legacy accounting structure (rev. 1.5-1.18).
+ * The first byte is always non-zero.
+ * Some fields use a comp_t type which is a 3 bits base 8
+ * exponent, 13 bit fraction ``floating point'' number.
+ * Units are 1/AHZV1 seconds.
+ */
+
+typedef uint16_t comp_t;
+
+struct acctv1 {
+ char ac_comm[AC_COMM_LEN]; /* command name */
+ comp_t ac_utime; /* user time */
+ comp_t ac_stime; /* system time */
+ comp_t ac_etime; /* elapsed time */
+ time_t ac_btime; /* starting time */
+ uid_t ac_uid; /* user id */
+ gid_t ac_gid; /* group id */
+ uint16_t ac_mem; /* average memory usage */
+ comp_t ac_io; /* count of IO blocks */
+ __dev_t ac_tty; /* controlling tty */
+ uint8_t ac_flag; /* accounting flags */
};
/*
- * 1/AHZ is the granularity of the data encoded in the comp_t fields.
+ * 1/AHZV1 is the granularity of the data encoded in the comp_t fields.
* This is not necessarily equal to hz.
*/
-#define AHZ 64
+#define AHZV1 64
#ifdef _KERNEL
struct thread;
int acct_process(struct thread *td);
+#undef float
#endif
#endif /* !_SYS_ACCT_H_ */
OpenPOWER on IntegriCloud