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