1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <libintl.h>
36 #include <locale.h>
37 
38 #include "rcapd.h"
39 #include "utils.h"
40 #include "rcapd_stat.h"
41 #include "statcommon.h"
42 
43 static char mode[RC_MODE_LEN];
44 static rcapd_stat_hdr_t hdr;
45 static int global;
46 static int unformatted;
47 static time_t stat_mod = 0;
48 
49 static uint_t timestamp_fmt = NODATE;
50 
51 typedef struct col {
52 	rcid_t		col_id;
53 	char		col_name[LC_NAME_LEN];
54 	uint64_t	col_nproc;
55 	uint64_t	col_vmsize;
56 	uint64_t	col_rsssize;
57 	uint64_t	col_rsslimit;
58 	uint64_t	col_paged_eff;
59 	uint64_t	col_paged_eff_old;
60 	uint64_t	col_paged_eff_avg;
61 	uint64_t	col_paged_att;
62 	uint64_t	col_paged_att_old;
63 	uint64_t	col_paged_att_avg;
64 	uint64_t	col_count;
65 	int		col_fresh;
66 	struct col	*col_next;
67 	struct col	*col_prev;
68 	lcollection_stat_t	col_src_stat;
69 	lcollection_stat_t	col_old_stat;
70 } col_t;
71 
72 static col_t *col_head;
73 static int ncol;
74 
75 static col_t *
col_find(rcid_t id)76 col_find(rcid_t id)
77 {
78 	col_t *col;
79 	for (col = col_head; col != NULL; col = col->col_next)
80 		if (col->col_id.rcid_type == id.rcid_type &&
81 		    col->col_id.rcid_val == id.rcid_val)
82 			return (col);
83 	return (NULL);
84 }
85 
86 static col_t *
col_insert(rcid_t id)87 col_insert(rcid_t id)
88 {
89 	col_t *new_col;
90 
91 	new_col = malloc(sizeof (col_t));
92 	if (new_col == NULL) {
93 		(void) fprintf(stderr, gettext("rcapstat: malloc() failed\n"));
94 		exit(E_ERROR);
95 	}
96 	(void) memset(new_col, 0, sizeof (col_t));
97 	new_col->col_next = col_head;
98 	new_col->col_id = id;
99 	if (col_head != NULL)
100 		col_head->col_prev = new_col;
101 	col_head = new_col;
102 	ncol++;
103 	return (new_col);
104 }
105 
106 static void
col_remove(col_t * col)107 col_remove(col_t *col)
108 {
109 	if (col->col_prev != NULL)
110 		col->col_prev->col_next = col->col_next;
111 	if (col->col_next != NULL)
112 		col->col_next->col_prev = col->col_prev;
113 	if (col_head == col)
114 		col_head = col->col_next;
115 	ncol--;
116 	free(col);
117 }
118 
119 static void
usage()120 usage()
121 {
122 	(void) fprintf(stderr,
123 	    gettext("usage: rcapstat [-g] [-p | -z] [-T d|u] "
124 	    "[interval [count]]\n"));
125 	exit(E_USAGE);
126 }
127 
128 static void
format_size(char * str,uint64_t size,int length)129 format_size(char *str, uint64_t size, int length)
130 {
131 	char tag = 'K';
132 	if (size >= 10000) {
133 		size = (size + 512) / 1024;
134 		tag = 'M';
135 		if (size >= 10000) {
136 			size = (size + 512) / 1024;
137 			tag = 'G';
138 		}
139 	}
140 	(void) snprintf(str, length, "%4lld%c", size, tag);
141 }
142 
143 static int
read_stats(rcid_type_t stat_type)144 read_stats(rcid_type_t stat_type)
145 {
146 	int fd;
147 	int proc_fd;
148 	char procfile[20];
149 	uint64_t pid;
150 	col_t *col, *col_next;
151 	lcollection_report_t report;
152 	struct stat st;
153 
154 	if ((fd = open(STAT_FILE_DEFAULT, O_RDONLY)) < 0) {
155 		warn(gettext("rcapd is not active\n"));
156 		return (E_ERROR);
157 	}
158 
159 	if (fstat(fd, &st) == 0)
160 		stat_mod = st.st_mtime;
161 
162 	if (read(fd, &hdr, sizeof (hdr)) != sizeof (hdr)) {
163 		(void) fprintf(stderr,
164 		    gettext("rcapstat: can't read stat file header: %s\n"),
165 		    strerror(errno));
166 		(void) close(fd);
167 		return (E_ERROR);
168 	}
169 
170 	/*
171 	 * Check if rcapd is running
172 	 */
173 	pid = hdr.rs_pid;
174 	(void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid);
175 	if ((proc_fd = open(procfile, O_RDONLY)) < 0) {
176 		warn(gettext("rcapd is not active\n"));
177 		(void) close(fd);
178 		return (E_ERROR);
179 	}
180 	(void) close(proc_fd);
181 
182 	(void) strncpy(mode, hdr.rs_mode, RC_MODE_LEN);
183 	for (col = col_head; col != NULL; col = col->col_next) {
184 		col->col_fresh = 0;
185 		col->col_paged_eff = 0;
186 		col->col_paged_att = 0;
187 	}
188 
189 	while (read(fd, &report, sizeof (report)) == sizeof (report)) {
190 		if (report.lcol_id.rcid_type != stat_type)
191 			continue;
192 
193 		col = col_find(report.lcol_id);
194 		if (col == NULL) {
195 			col = col_insert(report.lcol_id);
196 			col->col_paged_eff_old = col->col_paged_eff =
197 			    report.lcol_stat.lcols_pg_eff;
198 			col->col_paged_att_old = col->col_paged_att =
199 			    report.lcol_stat.lcols_pg_att;
200 			col->col_count = 0;
201 		}
202 		(void) strncpy(col->col_name, report.lcol_name, LC_NAME_LEN);
203 		col->col_vmsize = report.lcol_image_size;
204 		col->col_rsssize = report.lcol_rss;
205 		col->col_rsslimit = report.lcol_rss_cap;
206 		col->col_fresh = 1;
207 		if (report.lcol_stat.lcols_pg_eff > col->col_paged_eff_old) {
208 			col->col_paged_eff =
209 			    report.lcol_stat.lcols_pg_eff -
210 			    col->col_paged_eff_old;
211 			if (report.lcol_stat.lcols_scan_count > col->col_count)
212 				col->col_paged_eff_avg =
213 				    col->col_paged_eff /
214 				    (report.lcol_stat.lcols_scan_count -
215 				    col->col_count);
216 		} else {
217 			col->col_paged_eff_avg = 0;
218 		}
219 		if (report.lcol_stat.lcols_pg_att > col->col_paged_att_old) {
220 			col->col_paged_att =
221 			    report.lcol_stat.lcols_pg_att -
222 			    col->col_paged_att_old;
223 			if (report.lcol_stat.lcols_scan_count > col->col_count)
224 				col->col_paged_att_avg =
225 				    col->col_paged_att /
226 				    (report.lcol_stat.lcols_scan_count -
227 				    col->col_count);
228 		} else {
229 			col->col_paged_att_avg = 0;
230 		}
231 		col->col_paged_eff_old = report.lcol_stat.lcols_pg_eff;
232 		col->col_paged_att_old = report.lcol_stat.lcols_pg_att;
233 		col->col_nproc =
234 		    report.lcol_stat.lcols_proc_in -
235 		    report.lcol_stat.lcols_proc_out;
236 		col->col_count = report.lcol_stat.lcols_scan_count;
237 		col->col_src_stat = report.lcol_stat;
238 	}
239 
240 	/*
241 	 * Remove stale data
242 	 */
243 	col = col_head;
244 	while (col != NULL) {
245 		col_next = col->col_next;
246 		if (col->col_fresh == 0)
247 			col_remove(col);
248 		col = col_next;
249 	}
250 	(void) close(fd);
251 	return (E_SUCCESS);
252 }
253 
254 /*
255  * Print each collection's interval statistics.
256  */
257 /*ARGSUSED*/
258 static void
print_unformatted_stats(void)259 print_unformatted_stats(void)
260 {
261 	col_t *col;
262 
263 #define	DELTA(field) \
264 	(col->col_src_stat.field - col->col_old_stat.field)
265 
266 	col = col_head;
267 	while (col != NULL) {
268 		if (bcmp(&col->col_src_stat, &col->col_old_stat,
269 		    sizeof (col->col_src_stat)) == 0) {
270 			col = col->col_next;
271 			continue;
272 		}
273 		(void) printf("%s %s status: succeeded/attempted (k): "
274 		    "%llu/%llu, ineffective/scans/unenforced/samplings:  "
275 		    "%llu/%llu/%llu/%llu, RSS min/max (k): %llu/%llu, cap %llu "
276 		    "kB, processes/thpt: %llu/%llu, %llu scans over %lld ms\n",
277 		    mode, col->col_name, DELTA(lcols_pg_eff),
278 		    DELTA(lcols_pg_att), DELTA(lcols_scan_ineffective),
279 		    DELTA(lcols_scan), DELTA(lcols_unenforced_cap),
280 		    DELTA(lcols_rss_sample), col->col_src_stat.lcols_min_rss,
281 		    col->col_src_stat.lcols_max_rss, col->col_rsslimit,
282 		    (col->col_src_stat.lcols_proc_in -
283 		    col->col_old_stat.lcols_proc_out), DELTA(lcols_proc_out),
284 		    DELTA(lcols_scan_count),
285 		    NSEC2MSEC(DELTA(lcols_scan_time_complete)));
286 		col->col_old_stat = col->col_src_stat;
287 
288 		col = col->col_next;
289 	}
290 
291 	if (global)
292 		(void) printf(gettext("physical memory utilization: %3u%%   "
293 		    "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
294 		    hdr.rs_pressure_cap);
295 #undef DELTA
296 }
297 
298 static void
print_stats(rcid_type_t stat_type)299 print_stats(rcid_type_t stat_type)
300 {
301 	col_t *col;
302 	char size[6];
303 	char limit[6];
304 	char rss[6];
305 	char nproc[6];
306 	char paged_att[6];
307 	char paged_eff[6];
308 	char paged_att_avg[6];
309 	char paged_eff_avg[6];
310 	static int count = 0;
311 
312 	/*
313 	 * Print a header once every 20 times if we're only displaying reports
314 	 * for one collection (10 times if -g is used).  Print a header every
315 	 * interval otherwise.
316 	 */
317 	if (count == 0 || ncol != 1)
318 		(void) printf("%6s %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
319 		    "id", (stat_type == RCIDT_PROJECT ?  "project" : "zone"),
320 		    "nproc", "vm", "rss", "cap",
321 		    "at", "avgat", "pg", "avgpg");
322 	if (++count >= 20 || (count >= 10 && global != 0) || ncol != 1)
323 		count = 0;
324 
325 	for (col = col_head; col != NULL; col = col->col_next) {
326 		if (col->col_id.rcid_type != stat_type)
327 			continue;
328 
329 		if (col->col_paged_att == 0)
330 			(void) strlcpy(nproc, "-", sizeof (nproc));
331 		else
332 			(void) snprintf(nproc, sizeof (nproc), "%lld",
333 			    col->col_nproc);
334 		format_size(size, col->col_vmsize, 6);
335 		format_size(rss, col->col_rsssize, 6);
336 		format_size(limit, col->col_rsslimit, 6);
337 		format_size(paged_att, col->col_paged_att, 6);
338 		format_size(paged_eff, col->col_paged_eff, 6);
339 		format_size(paged_att_avg, col->col_paged_att_avg, 6);
340 		format_size(paged_eff_avg, col->col_paged_eff_avg, 6);
341 		(void) printf("%6lld %-15s %5s %5s %5s %5s %5s %5s %5s %5s\n",
342 		    col->col_id.rcid_val, col->col_name,
343 		    nproc,
344 		    size, rss, limit,
345 		    paged_att, paged_att_avg,
346 		    paged_eff, paged_eff_avg);
347 	}
348 	if (global)
349 		(void) printf(gettext("physical memory utilization: %3u%%   "
350 		    "cap enforcement threshold: %3u%%\n"), hdr.rs_pressure_cur,
351 		    hdr.rs_pressure_cap);
352 }
353 
354 int
main(int argc,char * argv[])355 main(int argc, char *argv[])
356 {
357 	int interval = 5;
358 	int count;
359 	int always = 1;
360 	int opt;
361 	int projects = 0;
362 	int zones = 0;
363 	/* project reporting is the default if no option is specified */
364 	rcid_type_t stat_type = RCIDT_PROJECT;
365 
366 	(void) setlocale(LC_ALL, "");
367 	(void) textdomain(TEXT_DOMAIN);
368 	(void) setpname("rcapstat");
369 
370 	global = unformatted = 0;
371 	while ((opt = getopt(argc, argv, "gpuzT:")) != (int)EOF) {
372 		switch (opt) {
373 		case 'g':
374 			global = 1;
375 			break;
376 		case 'p':
377 			projects = 1;
378 			stat_type = RCIDT_PROJECT;
379 			break;
380 		case 'u':
381 			unformatted = 1;
382 			break;
383 		case 'z':
384 			stat_type = RCIDT_ZONE;
385 			zones = 1;
386 			break;
387 		case 'T':
388 			if (optarg) {
389 				if (*optarg == 'u')
390 					timestamp_fmt = UDATE;
391 				else if (*optarg == 'd')
392 					timestamp_fmt = DDATE;
393 				else
394 					usage();
395 			} else {
396 				usage();
397 			}
398 			break;
399 		default:
400 			usage();
401 		}
402 	}
403 
404 	if (argc > optind)
405 		if ((interval = xatoi(argv[optind++])) <= 0)
406 			die(gettext("invalid interval specified\n"));
407 	if (argc > optind) {
408 		if ((count = xatoi(argv[optind++])) <= 0)
409 			die(gettext("invalid count specified\n"));
410 		always = 0;
411 	}
412 	if (argc > optind || (projects > 0 && zones > 0))
413 		usage();
414 
415 	while (always || count-- > 0) {
416 		if (read_stats(stat_type) != E_SUCCESS)
417 			return (E_ERROR);
418 		if (timestamp_fmt != NODATE)
419 			print_timestamp(timestamp_fmt);
420 		if (!unformatted) {
421 			print_stats(stat_type);
422 			(void) fflush(stdout);
423 			if (count || always)
424 				(void) sleep(interval);
425 		} else {
426 			struct stat st;
427 
428 			print_unformatted_stats();
429 			(void) fflush(stdout);
430 			while (stat(STAT_FILE_DEFAULT, &st) == 0 &&
431 			    st.st_mtime == stat_mod)
432 				(void) usleep((useconds_t)(0.2 * MICROSEC));
433 		}
434 	}
435 
436 	return (E_SUCCESS);
437 }
438