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 unsigned long long used_ns_a, used_ns_b; 102 const char *user_a, *user_b; 103 104 switch (a_key) { 105 case STATISTIC_PID: return COMP(a_a->pid, a_b->pid); 106 107 case STATISTIC_COMMAND: return COMP(strcmp(a_a->command, a_b->command),0); 108 109 case STATISTIC_CPU: 110 timersub(&a_a->total_time, &a_a->p_total_time, &tv_a); 111 timersub(&a_b->total_time, &a_b->p_total_time, &tv_b); 112 113 if(tv_a.tv_sec == tv_b.tv_sec) { 114 return COMP(tv_a.tv_usec, tv_b.tv_usec); 115 } else { 116 return COMP(tv_a.tv_sec, tv_b.tv_sec); 117 } 118 119 case STATISTIC_CPU_ME: 120 used_ns_a = a_a->cpu_billed_to_me - a_a->p_cpu_billed_to_me; 121 used_ns_b = a_b->cpu_billed_to_me - a_b->p_cpu_billed_to_me; 122 return COMP(used_ns_a, used_ns_b); 123 124 case STATISTIC_CPU_OTHERS: 125 used_ns_a = a_a->cpu_billed_to_others - a_a->p_cpu_billed_to_others; 126 used_ns_b = a_b->cpu_billed_to_others - a_b->p_cpu_billed_to_others; 127 return COMP(used_ns_a, used_ns_b); 128 129 case STATISTIC_BOOSTS: 130 { 131 int res; 132 res = COMP(a_a->boost_last_donating_seq, a_b->boost_last_donating_seq); 133 if (res) return res; 134 res = COMP(!a_a->boost_donating, !a_b->boost_donating); 135 if (res) return res; 136 return COMP(a_a->boosts - a_a->p_boosts, a_b->boosts - a_b->p_boosts); 137 } 138 139 140 case STATISTIC_TIME: 141 tv_a = a_a->total_time; 142 tv_b = a_b->total_time; 143 144 if(tv_a.tv_sec == tv_b.tv_sec) { 145 return COMP(tv_a.tv_usec, tv_b.tv_usec); 146 } else { 147 return COMP(tv_a.tv_sec, tv_b.tv_sec); 148 } 149 150 case STATISTIC_THREADS: return COMP(a_a->th, a_b->th); 151 152 case STATISTIC_WORKQUEUE: return COMP(a_a->wq_nthreads, a_b->wq_nthreads); 153 154 case STATISTIC_PORTS: return COMP(a_a->prt, a_b->prt); 155 156 case STATISTIC_MREGION: return COMP(a_a->reg, a_b->reg); 157 158#ifdef TOP_ANONYMOUS_MEMORY 159 case STATISTIC_RMEM: return COMP(a_a->anonymous, a_b->anonymous); 160 161 case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt); 162 163 case STATISTIC_PURG: return COMP(a_a->purgeable, a_b->purgeable); 164 165 case STATISTIC_COMPRESSED: return COMP(a_a->compressed, a_b->compressed); 166#else 167 case STATISTIC_RPRVT: return COMP(a_a->rprvt, a_b->rprvt); 168 169 case STATISTIC_RSHRD: return COMP(a_a->rshrd, a_b->rshrd); 170 171 case STATISTIC_RSIZE: return COMP(a_a->rsize, a_b->rsize); 172#endif 173 174 case STATISTIC_VSIZE: return COMP(a_a->vsize, a_b->vsize); 175 176 case STATISTIC_VPRVT: return COMP(a_a->vprvt, a_b->vprvt); 177 178 case STATISTIC_PGRP: return COMP(a_a->pgrp, a_b->pgrp); 179 180 case STATISTIC_PPID: return COMP(a_a->ppid, a_b->ppid); 181 182 case STATISTIC_KPRVT: return COMP(a_a->palloc - a_a->pfree, 183 a_b->palloc - a_b->pfree); 184 185 case STATISTIC_KSHRD: return COMP(a_a->salloc - a_a->sfree, 186 a_b->salloc - a_b->sfree); 187 188 case STATISTIC_PSTATE: { 189 const char *a = libtop_state_str(a_a->state); 190 const char *b = libtop_state_str(a_b->state); 191 192 return COMP(strcmp(a, b), 0); 193 } 194 case STATISTIC_UID: return COMP(a_a->uid, a_b->uid); 195 196 case STATISTIC_FAULTS: 197 return COMP(a_a->faults.now, a_b->faults.now); 198 199 case STATISTIC_COW_FAULTS: 200 return COMP(a_a->cow_faults.now, a_b->cow_faults.now); 201 202 case STATISTIC_MESSAGES_SENT: 203 return COMP(a_a->messages_sent.now, a_b->messages_sent.now); 204 205 case STATISTIC_MESSAGES_RECEIVED: 206 return COMP(a_a->messages_recv.now, a_b->messages_recv.now); 207 208 case STATISTIC_SYSBSD: 209 return COMP(a_a->syscalls_bsd.now, a_b->syscalls_bsd.now); 210 211 case STATISTIC_SYSMACH: 212 return COMP(a_a->syscalls_mach.now, a_b->syscalls_mach.now); 213 214 case STATISTIC_CSW: 215 return COMP(a_a->csw.now, a_b->csw.now); 216 217 case STATISTIC_PAGEINS: 218 return COMP(a_a->pageins.now, a_b->pageins.now); 219 220 case STATISTIC_IDLEWAKE: 221 return COMP(a_a->power.task_platform_idle_wakeups, a_b->power.task_platform_idle_wakeups); 222 223 case STATISTIC_POWERSCORE: { 224 uint64_t a_idlew = a_a->power.task_platform_idle_wakeups - a_a->p_power.task_platform_idle_wakeups; 225 uint64_t b_idlew = a_b->power.task_platform_idle_wakeups - a_b->p_power.task_platform_idle_wakeups; 226 timersub(&a_a->total_time, &a_a->p_total_time, &tv_a); 227 timersub(&a_b->total_time, &a_b->p_total_time, &tv_b); 228 uint64_t a_usec = tv_a.tv_usec + (UINT64_C(1000000) * tv_a.tv_sec) + (UINT64_C(500) * a_idlew); 229 uint64_t b_usec = tv_b.tv_usec + (UINT64_C(1000000) * tv_b.tv_sec) + (UINT64_C(500) * b_idlew); 230 231 // kernel gets a free ride 232 if (a_a->pid == 0) { 233 a_usec = UINT64_C(0); 234 } else if (a_b->pid == 0) { 235 b_usec = UINT64_C(0); 236 } 237 return COMP(a_usec, b_usec); 238 } 239 240 case STATISTIC_USER: 241 /* Handle the == case first, since it's common. */ 242 if (a_a->uid == a_b->uid) return 0; 243 244 user_a = libtop_username(a_a->uid); 245 user_b = libtop_username(a_b->uid); 246 247 return COMP(strcmp(user_a, user_b),0); 248 } 249 250 return 0; 251} 252 253#undef COMP 254 255void top_sample(void) { 256 if(libtop_sample(/*calculate mreg, vprvt and more see libtop.h*/ 257 top_prefs_get_mmr(), 258 /*framework stats*/ 259 top_prefs_get_frameworks())) { 260 endwin(); 261 fprintf(stderr, "error: while gathering a libtop sample.\n" 262 "The permissions and/or ownership are incorrect " 263 "for this executable, or you are testing without sudo.\n"); 264 _exit(EXIT_FAILURE); 265 } 266} 267 268void top_insert(void *ptr) { 269 struct statistics_controller *c = ptr; 270 const libtop_psamp_t *psample; 271 char *user; 272 unsigned long uid = 0; 273 int nprocs; 274 pid_t pid; 275 bool have_pid = false; 276 277 c->reset_insertion(c); 278 279 top_sample(); 280 281 /* 282 * The ordering is important here, because the libtop_psort actually 283 * updates the tsamp->nprocs. 284 */ 285 libtop_psort(top_sort, NULL); 286 287 tsamp = libtop_tsamp(); 288 289 if(top_globalstats_update(c->globalstats, tsamp)) 290 top_log("An error occurred while updating global stats.\n"); 291 292 /* The user has requested only displaying processes owned by user. */ 293 user = top_prefs_get_user(); 294 295 if(user) 296 uid = top_prefs_get_user_uid(); 297 298 nprocs = top_prefs_get_nprocs(); 299 300 if(0 == nprocs) 301 return; 302 303 have_pid = top_prefs_get_pid(&pid); 304 305 for(psample = libtop_piterate(); psample; psample = libtop_piterate()) { 306 if(user && psample->uid != uid) 307 continue; 308 309 if(have_pid) { 310 if(pid != psample->pid) 311 continue; 312 } 313 314 c->insert_sample(c, psample); 315 316 /* 317 * If nprocs is -1 (the default), or otherwise negative, 318 * then display all. 319 */ 320 if(nprocs > 0) 321 --nprocs; 322 323 if(0 == nprocs) { 324 break; 325 } 326 } 327} 328 329 330/* Return true if a non-fatal error occurred. */ 331bool top_layout(void *ptr) { 332 struct statistics_controller *c = ptr; 333 int lines = LINES, cols = COLS; 334 int consumed_height = 0; 335 int pstatheight; 336 337 //top_log("%s\n", __func__); 338 339 top_do_relayout = false; 340 341 //fprintf(stderr, "laying out: lines %d cols %d\n", lines, cols); 342 //werase(c->parent); 343 344 if(ERR == wresize(c->parent, lines, cols)) { 345 top_log("error: wresizing parent!\n"); 346 return true; 347 } 348 349 top_globalstats_reset(c->globalstats); 350 351 if(top_globalstats_resize(c->globalstats, cols, lines, 352 &consumed_height)) { 353 top_log("error: performing global stats resize!\n"); 354 return true; 355 } 356 357 user_input_set_position(consumed_height, 0); 358 359 pstatheight = lines - consumed_height - 1; 360 if(pstatheight <= 0) 361 return true; 362 363 //fprintf(stderr, "consumed_height %d\n", consumed_height); 364 365 if(layout_statistics(c, cols, pstatheight, 366 /*y*/ consumed_height + 1)) { 367 top_log("error: performing statistic layout!\n"); 368 return true; 369 } 370 371 return false; 372} 373 374struct draw_state { 375 int xoffset; 376}; 377 378static bool top_draw_iterator(struct statistic *s, void *ptr) { 379 struct draw_state *state = ptr; 380 381 s->callbacks.draw(s, /*x*/ state->xoffset); 382 383 state->xoffset = 1; 384 385 /* more iterations */ 386 return true; 387} 388 389void top_draw(void *ptr) { 390 struct statistics_controller *c = ptr; 391 struct draw_state state; 392 393 werase(c->parent); 394 395 top_globalstats_draw(c->globalstats); 396 397 user_input_draw(ptr, stdscr); 398 399 state.xoffset = 0; 400 401 c->iterate(c, top_draw_iterator, &state); 402 403 /* This moves the insertion cursor to the lower right. */ 404 wmove(stdscr, LINES - 1, COLS - 1); 405 406 update_panels(); 407 doupdate(); 408} 409