1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <pretty/sizes.h>
6 #include <zircon/status.h>
7 #include <zircon/syscalls.h>
8 #include <zircon/syscalls/exception.h>
9 #include <zircon/syscalls/object.h>
10 #include <zircon/time.h>
11 #include <zircon/types.h>
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <inttypes.h>
17 #include <math.h>
18 #include <stdbool.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <unistd.h>
24
25 #include "resources.h"
26
27 // TODO: dynamically compute this based on what it returns
28 #define MAX_CPUS 32
29
cpustats(zx_handle_t root_resource,zx_duration_t delay)30 static zx_status_t cpustats(zx_handle_t root_resource, zx_duration_t delay) {
31 static zx_duration_t last_idle_time[MAX_CPUS];
32 static zx_info_cpu_stats_t old_stats[MAX_CPUS];
33 zx_info_cpu_stats_t stats[MAX_CPUS];
34
35 // retrieve the system stats
36 size_t actual, avail;
37 zx_status_t err = zx_object_get_info(root_resource, ZX_INFO_CPU_STATS, &stats, sizeof(stats), &actual, &avail);
38 if (err != ZX_OK) {
39 fprintf(stderr, "ZX_INFO_CPU_STATS returns %d (%s)\n", err, zx_status_get_string(err));
40 return err;
41 }
42
43 if (actual < avail) {
44 fprintf(stderr, "WARNING: actual cpus reported %zu less than available cpus %zu\n",
45 actual, avail);
46 }
47
48 printf("cpu load"
49 " sched (cs ylds pmpts irq_pmpts)"
50 " excep"
51 " pagef"
52 " sysc"
53 " ints (hw tmr tmr_cb)"
54 " ipi (rs gen)\n");
55 for (size_t i = 0; i < actual; i++) {
56 zx_duration_t idle_time = stats[i].idle_time;
57
58 zx_duration_t delta_time = zx_duration_sub_duration(idle_time, last_idle_time[i]);
59 zx_duration_t busy_time;
60 if (delay > delta_time) {
61 busy_time = zx_duration_sub_duration(delay, delta_time);
62 } else {
63 busy_time = 0;
64 }
65 unsigned int busypercent = zx_duration_mul_int64(busy_time, 10000) / delay;
66
67 printf("%3zu"
68 " %3u.%02u%%"
69 " %9lu %4lu %5lu %9lu"
70 " %6lu"
71 " %5lu"
72 " %5lu"
73 " %8lu %4lu %6lu"
74 " %8lu %4lu"
75 "\n",
76 i,
77 busypercent / 100, busypercent % 100,
78 stats[i].context_switches - old_stats[i].context_switches,
79 stats[i].yields - old_stats[i].yields,
80 stats[i].preempts - old_stats[i].preempts,
81 stats[i].irq_preempts - old_stats[i].irq_preempts,
82 stats[i].exceptions - old_stats[i].exceptions,
83 stats[i].page_faults - old_stats[i].page_faults,
84 stats[i].syscalls - old_stats[i].syscalls,
85 stats[i].ints - old_stats[i].ints,
86 stats[i].timer_ints - old_stats[i].timer_ints,
87 stats[i].timers - old_stats[i].timers,
88 stats[i].reschedule_ipis - old_stats[i].reschedule_ipis,
89 stats[i].generic_ipis - old_stats[i].generic_ipis);
90
91 old_stats[i] = stats[i];
92 last_idle_time[i] = idle_time;
93 }
94
95 return ZX_OK;
96 }
97
print_mem_stat(const char * label,size_t bytes)98 static void print_mem_stat(const char* label, size_t bytes) {
99 char buf[MAX_FORMAT_SIZE_LEN];
100 const char unit = 'M';
101 printf("%15s: %8sB / %10zuB\n",
102 label,
103 format_size_fixed(buf, sizeof(buf), bytes, unit),
104 bytes);
105 }
106
memstats(zx_handle_t root_resource)107 static zx_status_t memstats(zx_handle_t root_resource) {
108 zx_info_kmem_stats_t stats;
109 zx_status_t err = zx_object_get_info(
110 root_resource, ZX_INFO_KMEM_STATS, &stats, sizeof(stats), NULL, NULL);
111 if (err != ZX_OK) {
112 fprintf(stderr, "ZX_INFO_KMEM_STATS returns %d (%s)\n",
113 err, zx_status_get_string(err));
114 return err;
115 }
116
117 const int width = 80 / 8 - 1;
118 printf("%*s %*s %*s %*s %*s %*s %*s %*s %*s\n",
119 width, "mem total",
120 width, "free",
121 width, "VMOs",
122 width, "kheap",
123 width, "kfree",
124 width, "wired",
125 width, "mmu",
126 width, "ipc",
127 width, "other");
128
129 const size_t fields[] = {
130 stats.total_bytes,
131 stats.free_bytes,
132 stats.vmo_bytes,
133 stats.total_heap_bytes - stats.free_heap_bytes,
134 stats.free_heap_bytes,
135 stats.wired_bytes,
136 stats.mmu_overhead_bytes,
137 stats.ipc_bytes,
138 stats.other_bytes,
139 };
140 char line[128] = {};
141 for (unsigned int i = 0; i < countof(fields); i++) {
142 const char unit = 'M';
143 char buf[MAX_FORMAT_SIZE_LEN];
144 format_size_fixed(buf, sizeof(buf), fields[i], unit);
145
146 char stage[MAX_FORMAT_SIZE_LEN + 8];
147 snprintf(stage, sizeof(stage), "%*s ", width, buf);
148
149 strlcat(line, stage, sizeof(line));
150
151 // TODO(dbort): Save some history so we can show deltas over time.
152 // Maybe have a few buckets like 1s, 10s, 1m.
153 }
154 printf("%s\n", line);
155 return ZX_OK;
156 }
157
print_help(FILE * f)158 static void print_help(FILE* f) {
159 fprintf(f, "Usage: kstats [options]\n");
160 fprintf(f, "Options:\n");
161 fprintf(f, " -c Print system CPU stats\n");
162 fprintf(f, " -m Print system memory stats\n");
163 fprintf(f, " -d <delay> Delay in seconds (default 1 second)\n");
164 fprintf(f, " -n <times> Run this many times and then exit\n");
165 fprintf(f, " -t Print timestamp for each report\n");
166 fprintf(f, "\nCPU stats columns:\n");
167 fprintf(f, "\tcpu: cpu #\n");
168 fprintf(f, "\tload: percentage load\n");
169 fprintf(f, "\tsched (cs ylds pmpts irq_pmpts): scheduler statistics\n");
170 fprintf(f, "\t\tcs: context switches\n");
171 fprintf(f, "\t\tylds: explicit thread yields\n");
172 fprintf(f, "\t\tpmpts: thread preemption events\n");
173 fprintf(f, "\t\tirq_pmpts: thread preemption events from interrupt\n");
174
175 fprintf(f, "\texcep: exceptions (undefined instruction, bad memory access, etc)\n");
176 fprintf(f, "\tpagef: page faults\n");
177 fprintf(f, "\tsysc: syscalls\n");
178 fprintf(f, "\tints (hw tmr tmr_cb): interrupt statistics\n");
179 fprintf(f, "\t\thw: hardware interrupts\n");
180 fprintf(f, "\t\ttmr: timer interrupts\n");
181 fprintf(f, "\t\ttmr_cb: kernel timer events\n");
182 fprintf(f, "\tipi (rs gen): inter-processor-interrupts\n");
183 fprintf(f, "\t\trs: reschedule events\n");
184 fprintf(f, "\t\tgen: generic interprocessor interrupts\n");
185 }
186
main(int argc,char ** argv)187 int main(int argc, char** argv) {
188 bool cpu_stats = false;
189 bool mem_stats = false;
190 zx_duration_t delay = ZX_SEC(1);
191 int num_loops = -1;
192 bool timestamp = false;
193
194 int c;
195 while ((c = getopt(argc, argv, "cd:n:hmt")) > 0) {
196 switch (c) {
197 case 'c':
198 cpu_stats = true;
199 break;
200 case 'd':
201 delay = ZX_SEC(atoi(optarg));
202 if (delay == 0) {
203 fprintf(stderr, "Bad -d value '%s'\n", optarg);
204 print_help(stderr);
205 return 1;
206 }
207 break;
208 case 'n':
209 num_loops = atoi(optarg);
210 if (num_loops == 0) {
211 fprintf(stderr, "Bad -n value '%s'\n", optarg);
212 print_help(stderr);
213 return 1;
214 }
215 break;
216 case 'h':
217 print_help(stdout);
218 return 0;
219 case 'm':
220 mem_stats = true;
221 break;
222 case 't':
223 timestamp = true;
224 break;
225 default:
226 fprintf(stderr, "Unknown option\n");
227 print_help(stderr);
228 return 1;
229 }
230 }
231
232 if (!cpu_stats && !mem_stats) {
233 fprintf(stderr, "No statistics selected\n");
234 print_help(stderr);
235 return 1;
236 }
237
238 zx_handle_t root_resource;
239 zx_status_t ret = get_root_resource(&root_resource);
240 if (ret != ZX_OK) {
241 return ret;
242 }
243
244 // set stdin to non blocking so we can intercept ctrl-c.
245 // TODO: remove once ctrl-c works in the shell
246 fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
247
248 for (;;) {
249 zx_time_t next_deadline = zx_deadline_after(delay);
250
251 // Print the current UTC time with milliseconds as
252 // an ISO 8601 string.
253 if (timestamp) {
254 struct timespec now;
255 timespec_get(&now, TIME_UTC);
256 struct tm nowtm;
257 gmtime_r(&now.tv_sec, &nowtm);
258 char tbuf[40];
259 strftime(tbuf, sizeof(tbuf), "%FT%T", &nowtm);
260 printf("\n--- %s.%03ldZ ---\n", tbuf, now.tv_nsec / (1000 * 1000));
261 }
262
263 if (cpu_stats) {
264 ret |= cpustats(root_resource, delay);
265 }
266 if (mem_stats) {
267 ret |= memstats(root_resource);
268 }
269
270 if (ret != ZX_OK)
271 break;
272
273 if (num_loops > 0) {
274 if (--num_loops == 0) {
275 break;
276 }
277 } else {
278 // TODO: replace once ctrl-c works in the shell
279 char c;
280 int err;
281 while ((err = read(STDIN_FILENO, &c, 1)) > 0) {
282 if (c == 0x3)
283 return 0;
284 }
285 }
286
287 zx_nanosleep(next_deadline);
288 }
289
290 zx_handle_close(root_resource);
291
292 return ret;
293 }
294