1/* 2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdbool.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <curses.h> 28#include "top.h" 29#include "libtop.h" 30#include "globalstats.h" 31#include "layout.h" 32#include "log.h" 33#include "userinput.h" 34#include "preferences.h" 35 36const libtop_tsamp_t *tsamp; 37 38static int sort_subcomp(int a_key, const libtop_psamp_t *a_a, 39 const libtop_psamp_t *a_b); 40 41static bool top_do_relayout = false; 42 43/* This may not be called in common update patterns. */ 44void top_relayout_force(void) { 45 top_do_relayout = true; 46} 47 48void top_relayout(struct statistics_controller *c, int type, int maxwidth) { 49 top_do_relayout = true; 50} 51 52bool top_need_relayout(void) { 53 return top_do_relayout; 54} 55 56void *top_create(WINDOW *wmain) { 57 void *gstats; 58 struct statistics_controller *controller; 59 60 gstats = top_globalstats_create(wmain); 61 62 if(NULL == gstats) { 63 endwin(); 64 fprintf(stderr, "unable to create global stats!\n"); 65 _exit(EXIT_FAILURE); 66 } 67 68 controller = create_statistics_controller(wmain); 69 70 if(NULL == controller) { 71 endwin(); 72 fprintf(stderr, "unable to create controller for main window!\n"); 73 _exit(EXIT_FAILURE); 74 } 75 76 controller->globalstats = gstats; 77 78 return controller; 79} 80 81 82static int top_sort(void *a_data, const libtop_psamp_t *a, const libtop_psamp_t *b) { 83 int retval; 84 85 retval = sort_subcomp(top_prefs_get_sort(), a, b) * 86 (top_prefs_get_ascending() ? 1 : -1); 87 if (retval == 0) { 88 retval = sort_subcomp(top_prefs_get_secondary_sort(), a, b) * 89 (top_prefs_get_secondary_ascending() ? 1 : -1); 90 } 91 92 return retval; 93} 94 95#define COMP(a,b) (((a)==(b)?0: \ 96 ((a)<(b)?-1:1))) 97 98static int sort_subcomp(int a_key, const libtop_psamp_t *a_a, 99 const libtop_psamp_t *a_b) { 100 struct timeval tv_a, tv_b; 101 const char *user_a, *user_b; 102 103 switch (a_key) { 104 case STATISTIC_PID: return COMP(a_a->pid, a_b->pid); 105 106 case STATISTIC_COMMAND: return COMP(strcmp(a_a->command, a_b->command),0); 107 108 case STATISTIC_CPU: 109 timersub(&a_a->total_time, &a_a->p_total_time, &tv_a); 110 timersub(&a_b->total_time, &a_b->p_total_time, &tv_b); 111 112 if(tv_a.tv_sec == tv_b.tv_sec) { 113 return COMP(tv_a.tv_usec, tv_b.tv_usec); 114 } else { 115 return COMP(tv_a.tv_sec, tv_b.tv_sec); 116 } 117 118 case STATISTIC_TIME: 119 tv_a = a_a->total_time; 120 tv_b = a_b->total_time; 121 122 if(tv_a.tv_sec == tv_b.tv_sec) { 123 return COMP(tv_a.tv_usec, tv_b.tv_usec); 124 } else { 125 return COMP(tv_a.tv_sec, tv_b.tv_sec); 126 } 127 128 case STATISTIC_THREADS: return COMP(a_a->th, a_b->th); 129 130 case STATISTIC_WORKQUEUE: return COMP(a_a->wq_nthreads, a_b->wq_nthreads); 131 132 case STATISTIC_PORTS: return COMP(a_a->prt, a_b->prt); 133 134 case STATISTIC_MREGION: return COMP(a_a->reg, a_b->reg); 135 136#ifdef TOP_ANONYMOUS_MEMORY 137 case STATISTIC_RMEM: return COMP(a_a->anonymous, a_b->anonymous); 138 139 case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt); 140 141 case STATISTIC_PURG: return COMP(a_a->purgeable, a_b->purgeable); 142 143 case STATISTIC_COMPRESSED: return COMP(a_a->compressed, a_b->compressed); 144#else 145 case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt); 146 147 case STATISTIC_RSHRD: return COMP(a_a->rshrd, a_b->rshrd); 148 149 case STATISTIC_RSIZE: return COMP(a_a->rsize, a_b->rsize); 150#endif 151 152 case STATISTIC_VSIZE: return COMP(a_a->vsize, a_b->vsize); 153 154 case STATISTIC_VPRVT: return COMP(a_a->vprvt, a_b->vprvt); 155 156 case STATISTIC_PGRP: return COMP(a_a->pgrp, a_b->pgrp); 157 158 case STATISTIC_PPID: return COMP(a_a->ppid, a_b->ppid); 159 160 case STATISTIC_KPRVT: return COMP(a_a->palloc - a_a->pfree, 161 a_b->palloc - a_b->pfree); 162 163 case STATISTIC_KSHRD: return COMP(a_a->salloc - a_a->sfree, 164 a_b->salloc - a_b->sfree); 165 166 case STATISTIC_PSTATE: { 167 const char *a = libtop_state_str(a_a->state); 168 const char *b = libtop_state_str(a_b->state); 169 170 return COMP(strcmp(a, b), 0); 171 } 172 case STATISTIC_UID: return COMP(a_a->uid, a_b->uid); 173 174 case STATISTIC_FAULTS: 175 return COMP(a_a->faults.now, a_b->faults.now); 176 177 case STATISTIC_COW_FAULTS: 178 return COMP(a_a->cow_faults.now, a_b->cow_faults.now); 179 180 case STATISTIC_MESSAGES_SENT: 181 return COMP(a_a->messages_sent.now, a_b->messages_sent.now); 182 183 case STATISTIC_MESSAGES_RECEIVED: 184 return COMP(a_a->messages_recv.now, a_b->messages_recv.now); 185 186 case STATISTIC_SYSBSD: 187 return COMP(a_a->syscalls_bsd.now, a_b->syscalls_bsd.now); 188 189 case STATISTIC_SYSMACH: 190 return COMP(a_a->syscalls_mach.now, a_b->syscalls_mach.now); 191 192 case STATISTIC_CSW: 193 return COMP(a_a->csw.now, a_b->csw.now); 194 195 case STATISTIC_PAGEINS: 196 return COMP(a_a->pageins.now, a_b->pageins.now); 197 198 case STATISTIC_IDLEWAKE: 199 return COMP(a_a->power.task_platform_idle_wakeups, a_b->power.task_platform_idle_wakeups); 200 201 case STATISTIC_POWERSCORE: { 202 uint64_t a_idlew = a_a->power.task_platform_idle_wakeups - a_a->p_power.task_platform_idle_wakeups; 203 uint64_t b_idlew = a_b->power.task_platform_idle_wakeups - a_b->p_power.task_platform_idle_wakeups; 204 timersub(&a_a->total_time, &a_a->p_total_time, &tv_a); 205 timersub(&a_b->total_time, &a_b->p_total_time, &tv_b); 206 uint64_t a_usec = tv_a.tv_usec + (UINT64_C(1000000) * tv_a.tv_sec) + (UINT64_C(500) * a_idlew); 207 uint64_t b_usec = tv_b.tv_usec + (UINT64_C(1000000) * tv_b.tv_sec) + (UINT64_C(500) * b_idlew); 208 209 // kernel gets a free ride 210 if (a_a->pid == 0) { 211 a_usec = UINT64_C(0); 212 } else if (a_b->pid == 0) { 213 b_usec = UINT64_C(0); 214 } 215 return COMP(a_usec, b_usec); 216 } 217 218 case STATISTIC_USER: 219 /* Handle the == case first, since it's common. */ 220 if (a_a->uid == a_b->uid) return 0; 221 222 user_a = libtop_username(a_a->uid); 223 user_b = libtop_username(a_b->uid); 224 225 return COMP(strcmp(user_a, user_b),0); 226 } 227 228 return 0; 229} 230 231#undef COMP 232 233void top_sample(void) { 234 if(libtop_sample(/*calculate mreg, vprvt and more see libtop.h*/ 235 top_prefs_get_mmr(), 236 /*framework stats*/ 237 top_prefs_get_frameworks())) { 238 endwin(); 239 fprintf(stderr, "error: while gathering a libtop sample.\n" 240 "The permissions and/or ownership are incorrect " 241 "for this executable, or you are testing without sudo.\n"); 242 _exit(EXIT_FAILURE); 243 } 244} 245 246void top_insert(void *ptr) { 247 struct statistics_controller *c = ptr; 248 const libtop_psamp_t *psample; 249 char *user; 250 unsigned long uid = 0; 251 int nprocs; 252 pid_t pid; 253 bool have_pid = false; 254 255 c->reset_insertion(c); 256 257 top_sample(); 258 259 /* 260 * The ordering is important here, because the libtop_psort actually 261 * updates the tsamp->nprocs. 262 */ 263 libtop_psort(top_sort, NULL); 264 265 tsamp = libtop_tsamp(); 266 267 if(top_globalstats_update(c->globalstats, tsamp)) 268 top_log("An error occurred while updating global stats.\n"); 269 270 /* The user has requested only displaying processes owned by user. */ 271 user = top_prefs_get_user(); 272 273 if(user) 274 uid = top_prefs_get_user_uid(); 275 276 nprocs = top_prefs_get_nprocs(); 277 278 if(0 == nprocs) 279 return; 280 281 have_pid = top_prefs_get_pid(&pid); 282 283 for(psample = libtop_piterate(); psample; psample = libtop_piterate()) { 284 if(user && psample->uid != uid) 285 continue; 286 287 if(have_pid) { 288 if(pid != psample->pid) 289 continue; 290 } 291 292 c->insert_sample(c, psample); 293 294 /* 295 * If nprocs is -1 (the default), or otherwise negative, 296 * then display all. 297 */ 298 if(nprocs > 0) 299 --nprocs; 300 301 if(0 == nprocs) { 302 break; 303 } 304 } 305} 306 307 308/* Return true if a non-fatal error occurred. */ 309bool top_layout(void *ptr) { 310 struct statistics_controller *c = ptr; 311 int lines = LINES, cols = COLS; 312 int consumed_height = 0; 313 int pstatheight; 314 315 //top_log("%s\n", __func__); 316 317 top_do_relayout = false; 318 319 //fprintf(stderr, "laying out: lines %d cols %d\n", lines, cols); 320 //werase(c->parent); 321 322 if(ERR == wresize(c->parent, lines, cols)) { 323 top_log("error: wresizing parent!\n"); 324 return true; 325 } 326 327 top_globalstats_reset(c->globalstats); 328 329 if(top_globalstats_resize(c->globalstats, cols, lines, 330 &consumed_height)) { 331 top_log("error: performing global stats resize!\n"); 332 return true; 333 } 334 335 user_input_set_position(consumed_height, 0); 336 337 pstatheight = lines - consumed_height - 1; 338 if(pstatheight <= 0) 339 return true; 340 341 //fprintf(stderr, "consumed_height %d\n", consumed_height); 342 343 if(layout_statistics(c, cols, pstatheight, 344 /*y*/ consumed_height + 1)) { 345 top_log("error: performing statistic layout!\n"); 346 return true; 347 } 348 349 return false; 350} 351 352struct draw_state { 353 int xoffset; 354}; 355 356static bool top_draw_iterator(struct statistic *s, void *ptr) { 357 struct draw_state *state = ptr; 358 359 s->callbacks.draw(s, /*x*/ state->xoffset); 360 361 state->xoffset = 1; 362 363 /* more iterations */ 364 return true; 365} 366 367void top_draw(void *ptr) { 368 struct statistics_controller *c = ptr; 369 struct draw_state state; 370 371 werase(c->parent); 372 373 top_globalstats_draw(c->globalstats); 374 375 user_input_draw(ptr, stdscr); 376 377 state.xoffset = 0; 378 379 c->iterate(c, top_draw_iterator, &state); 380 381 /* This moves the insertion cursor to the lower right. */ 382 wmove(stdscr, LINES - 1, COLS - 1); 383 384 update_panels(); 385 doupdate(); 386} 387