Annotation of src/usr.bin/top/utils.c, Revision 1.21
1.21 ! otto 1: /* $OpenBSD: utils.c,v 1.20 2007/07/27 13:57:50 deraadt Exp $ */
1.1 downsj 2:
3: /*
4: * Top users/processes display for Unix
5: * Version 3
6: *
1.6 deraadt 7: * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8: * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1.1 downsj 9: *
1.6 deraadt 10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22: * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 downsj 29: */
30:
31: /*
32: * This file contains various handy utilities used by top.
33: */
34:
1.14 otto 35: #include <sys/param.h>
36: #include <sys/sysctl.h>
1.16 millert 37: #include <err.h>
1.2 downsj 38: #include <stdio.h>
39: #include <string.h>
40: #include <stdlib.h>
1.19 otto 41: #include <stdint.h>
1.2 downsj 42:
1.1 downsj 43: #include "top.h"
1.14 otto 44: #include "machine.h"
1.13 deraadt 45: #include "utils.h"
1.1 downsj 46:
1.9 pvalchev 47: int
48: atoiwi(char *str)
1.1 downsj 49: {
1.12 deraadt 50: size_t len;
1.17 otto 51: const char *errstr;
52: int i;
1.1 downsj 53:
1.10 deraadt 54: len = strlen(str);
55: if (len != 0) {
56: if (strncmp(str, "infinity", len) == 0 ||
57: strncmp(str, "all", len) == 0 ||
58: strncmp(str, "maximum", len) == 0) {
59: return (Infinity);
1.17 otto 60: }
61: i = (int)strtonum(str, 0, INT_MAX, &errstr);
62: if (errstr) {
1.10 deraadt 63: return (Invalid);
1.17 otto 64: } else
65: return (i);
1.1 downsj 66: }
1.10 deraadt 67: return (0);
1.1 downsj 68: }
69:
70: /*
1.11 millert 71: * itoa - convert integer (decimal) to ascii string.
1.1 downsj 72: */
1.9 pvalchev 73: char *
74: itoa(int val)
1.1 downsj 75: {
1.10 deraadt 76: static char buffer[16]; /* result is built here */
77:
78: /*
79: * 16 is sufficient since the largest number we will ever convert
80: * will be 2^32-1, which is 10 digits.
81: */
1.11 millert 82: (void)snprintf(buffer, sizeof(buffer), "%d", val);
83: return (buffer);
1.1 downsj 84: }
85:
86: /*
1.11 millert 87: * format_uid(uid) - like itoa, except for uid_t and the number is right
88: * justified in a 6 character field to match uname_field in top.c.
1.1 downsj 89: */
1.9 pvalchev 90: char *
1.11 millert 91: format_uid(uid_t uid)
1.1 downsj 92: {
1.11 millert 93: static char buffer[16]; /* result is built here */
1.1 downsj 94:
1.11 millert 95: /*
96: * 16 is sufficient since the largest uid we will ever convert
97: * will be 2^32-1, which is 10 digits.
98: */
99: (void)snprintf(buffer, sizeof(buffer), "%6u", uid);
100: return (buffer);
1.1 downsj 101: }
102:
103: /*
1.10 deraadt 104: * digits(val) - return number of decimal digits in val. Only works for
105: * positive numbers. If val <= 0 then digits(val) == 0.
1.1 downsj 106: */
1.9 pvalchev 107: int
108: digits(int val)
1.1 downsj 109: {
1.10 deraadt 110: int cnt = 0;
1.1 downsj 111:
1.10 deraadt 112: while (val > 0) {
113: cnt++;
114: val /= 10;
115: }
116: return (cnt);
1.1 downsj 117: }
118:
119: /*
120: * string_index(string, array) - find string in array and return index
121: */
1.9 pvalchev 122: int
123: string_index(char *string, char **array)
1.1 downsj 124: {
1.10 deraadt 125: int i = 0;
1.1 downsj 126:
1.10 deraadt 127: while (*array != NULL) {
128: if (strcmp(string, *array) == 0)
129: return (i);
130: array++;
131: i++;
1.1 downsj 132: }
1.10 deraadt 133: return (-1);
1.1 downsj 134: }
135:
136: /*
137: * argparse(line, cntp) - parse arguments in string "line", separating them
1.10 deraadt 138: * out into an argv-like array, and setting *cntp to the number of
139: * arguments encountered. This is a simple parser that doesn't understand
140: * squat about quotes.
1.1 downsj 141: */
1.9 pvalchev 142: char **
143: argparse(char *line, int *cntp)
1.1 downsj 144: {
1.10 deraadt 145: char **argv, **argarray, *args, *from, *to;
146: int cnt, ch, length, lastch;
147:
148: /*
149: * unfortunately, the only real way to do this is to go thru the
150: * input string twice.
151: */
152:
153: /* step thru the string counting the white space sections */
154: from = line;
155: lastch = cnt = length = 0;
156: while ((ch = *from++) != '\0') {
157: length++;
158: if (ch == ' ' && lastch != ' ')
159: cnt++;
160: lastch = ch;
1.1 downsj 161: }
162:
1.10 deraadt 163: /*
164: * add three to the count: one for the initial "dummy" argument, one
165: * for the last argument and one for NULL
166: */
167: cnt += 3;
168:
169: /* allocate a char * array to hold the pointers */
1.20 deraadt 170: if ((argarray = calloc(cnt, sizeof(char *))) == NULL)
1.16 millert 171: err(1, NULL);
1.10 deraadt 172:
173: /* allocate another array to hold the strings themselves */
1.16 millert 174: if ((args = malloc(length + 2)) == NULL)
175: err(1, NULL);
1.10 deraadt 176:
177: /* initialization for main loop */
178: from = line;
179: to = args;
180: argv = argarray;
181: lastch = '\0';
182:
183: /* create a dummy argument to keep getopt happy */
184: *argv++ = to;
185: *to++ = '\0';
186: cnt = 2;
187:
188: /* now build argv while copying characters */
189: *argv++ = to;
190: while ((ch = *from++) != '\0') {
191: if (ch != ' ') {
192: if (lastch == ' ') {
193: *to++ = '\0';
194: *argv++ = to;
195: cnt++;
196: }
197: *to++ = ch;
198: }
199: lastch = ch;
1.1 downsj 200: }
1.10 deraadt 201: *to++ = '\0';
202:
203: /* set cntp and return the allocated array */
204: *cntp = cnt;
205: return (argarray);
1.1 downsj 206: }
207:
208: /*
1.10 deraadt 209: * percentages(cnt, out, new, old, diffs) - calculate percentage change
1.21 ! otto 210: * between array "old" and "new", putting the percentages in "out".
1.10 deraadt 211: * "cnt" is size of each array and "diffs" is used for scratch space.
212: * The array "old" is updated on each call.
213: * The routine assumes modulo arithmetic. This function is especially
1.21 ! otto 214: * useful on BSD machines for calculating cpu state percentages.
1.1 downsj 215: */
1.9 pvalchev 216: int
1.16 millert 217: percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs)
1.1 downsj 218: {
1.16 millert 219: int64_t change, total_change, *dp, half_total;
1.10 deraadt 220: int i;
221:
222: /* initialization */
223: total_change = 0;
224: dp = diffs;
225:
226: /* calculate changes for each state and the overall change */
227: for (i = 0; i < cnt; i++) {
228: if ((change = *new - *old) < 0) {
229: /* this only happens when the counter wraps */
1.19 otto 230: change = INT64_MAX - *old + *new;
1.10 deraadt 231: }
232: total_change += (*dp++ = change);
233: *old++ = *new++;
1.1 downsj 234: }
235:
1.10 deraadt 236: /* avoid divide by zero potential */
237: if (total_change == 0)
238: total_change = 1;
239:
240: /* calculate percentages based on overall change, rounding up */
241: half_total = total_change / 2l;
242: for (i = 0; i < cnt; i++)
243: *out++ = ((*diffs++ * 1000 + half_total) / total_change);
244:
245: /* return the total in case the caller wants to use it */
246: return (total_change);
1.1 downsj 247: }
248:
1.10 deraadt 249: /*
250: * format_time(seconds) - format number of seconds into a suitable display
251: * that will fit within 6 characters. Note that this routine builds its
252: * string in a static area. If it needs to be called more than once without
253: * overwriting previous data, then we will need to adopt a technique similar
254: * to the one used for format_k.
1.1 downsj 255: */
256:
1.10 deraadt 257: /*
258: * Explanation: We want to keep the output within 6 characters. For low
259: * values we use the format mm:ss. For values that exceed 999:59, we switch
260: * to a format that displays hours and fractions: hhh.tH. For values that
261: * exceed 999.9, we use hhhh.t and drop the "H" designator. For values that
262: * exceed 9999.9, we use "???".
1.1 downsj 263: */
264:
1.9 pvalchev 265: char *
266: format_time(time_t seconds)
1.1 downsj 267: {
1.10 deraadt 268: static char result[10];
1.1 downsj 269:
1.10 deraadt 270: /* sanity protection */
271: if (seconds < 0 || seconds > (99999l * 360l)) {
272: strlcpy(result, " ???", sizeof result);
273: } else if (seconds >= (1000l * 60l)) {
274: /* alternate (slow) method displaying hours and tenths */
275: snprintf(result, sizeof(result), "%5.1fH",
276: (double) seconds / (double) (60l * 60l));
277:
278: /*
279: * It is possible that the snprintf took more than 6
280: * characters. If so, then the "H" appears as result[6]. If
281: * not, then there is a \0 in result[6]. Either way, it is
282: * safe to step on.
283: */
284: result[6] = '\0';
285: } else {
286: /* standard method produces MMM:SS */
287: /* we avoid printf as must as possible to make this quick */
288: snprintf(result, sizeof(result), "%3d:%02d", seconds / 60,
289: seconds % 60);
290: }
291: return (result);
1.1 downsj 292: }
293:
294: /*
295: * format_k(amt) - format a kilobyte memory value, returning a string
1.10 deraadt 296: * suitable for display. Returns a pointer to a static
297: * area that changes each call. "amt" is converted to a
298: * string with a trailing "K". If "amt" is 10000 or greater,
299: * then it is formatted as megabytes (rounded) with a
300: * trailing "M".
1.1 downsj 301: */
302:
303: /*
304: * Compromise time. We need to return a string, but we don't want the
305: * caller to have to worry about freeing a dynamically allocated string.
306: * Unfortunately, we can't just return a pointer to a static area as one
1.8 deraadt 307: * of the common uses of this function is in a large call to snprintf where
1.1 downsj 308: * it might get invoked several times. Our compromise is to maintain an
309: * array of strings and cycle thru them with each invocation. We make the
310: * array large enough to handle the above mentioned case. The constant
311: * NUM_STRINGS defines the number of strings in this array: we can tolerate
312: * up to NUM_STRINGS calls before we start overwriting old information.
313: * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
314: * to convert the modulo operation into something quicker. What a hack!
315: */
316:
317: #define NUM_STRINGS 8
318:
1.9 pvalchev 319: char *
320: format_k(int amt)
1.1 downsj 321: {
1.10 deraadt 322: static char retarray[NUM_STRINGS][16];
1.12 deraadt 323: static int idx = 0;
1.11 millert 324: char *ret, tag = 'K';
1.10 deraadt 325:
1.12 deraadt 326: ret = retarray[idx];
327: idx = (idx + 1) % NUM_STRINGS;
1.10 deraadt 328:
329: if (amt >= 10000) {
330: amt = (amt + 512) / 1024;
331: tag = 'M';
332: if (amt >= 10000) {
333: amt = (amt + 512) / 1024;
334: tag = 'G';
335: }
1.1 downsj 336: }
1.11 millert 337: snprintf(ret, sizeof(retarray[0]), "%d%c", amt, tag);
1.10 deraadt 338: return (ret);
1.14 otto 339: }
340:
341: int
342: find_pid(pid_t pid)
343: {
344: struct kinfo_proc2 *pbase, *cur;
1.15 pat 345: int nproc;
1.14 otto 346:
347: if ((pbase = getprocs(KERN_PROC_KTHREAD, 0, &nproc)) == NULL)
348: quit(23);
349:
1.15 pat 350: for (cur = pbase; cur < &pbase[nproc]; cur++)
1.14 otto 351: if (cur->p_pid == pid)
352: return 1;
353: return 0;
1.1 downsj 354: }