1 /* 2 * Copyright 2009, Intel Corporation 3 * Copyright 2009, Sun Microsystems, Inc 4 * 5 * This file is part of PowerTOP 6 * 7 * This program file is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; version 2 of the License. 10 * 11 * This program is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program in a file named COPYING; if not, write to the 18 * Free Software Foundation, Inc., 19 * 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301 USA 21 * 22 * Authors: 23 * Arjan van de Ven <arjan@linux.intel.com> 24 * Eric C Saxe <eric.saxe@sun.com> 25 * Aubrey Li <aubrey.li@intel.com> 26 */ 27 28 /* 29 * GPL Disclaimer 30 * 31 * For the avoidance of doubt, except that if any license choice other 32 * than GPL or LGPL is available it will apply instead, Sun elects to 33 * use only the General Public License version 2 (GPLv2) at this time 34 * for any software where a choice of GPL license versions is made 35 * available with the language indicating that GPLv2 or any later 36 * version may be used, or where a choice of which version of the GPL 37 * is applied is otherwise unspecified. 38 */ 39 40 #include <stdlib.h> 41 #include <string.h> 42 #include <curses.h> 43 #include "powertop.h" 44 45 static WINDOW *title_bar_window; 46 static WINDOW *cstate_window; 47 static WINDOW *wakeup_window; 48 static WINDOW *acpi_power_window; 49 static WINDOW *eventstat_window; 50 static WINDOW *suggestion_window; 51 static WINDOW *status_bar_window; 52 53 #define print(win, y, x, fmt, args...) \ 54 if (PT_ON_DUMP) \ 55 (void) printf(fmt, ## args); \ 56 else \ 57 (void) mvwprintw(win, y, x, fmt, ## args); 58 59 char g_status_bar_slots[PT_BAR_NSLOTS][PT_BAR_LENGTH]; 60 char g_suggestion_key; 61 62 static int maxx, maxy; 63 64 static void 65 zap_windows(void) 66 { 67 if (title_bar_window) { 68 (void) delwin(title_bar_window); 69 title_bar_window = NULL; 70 } 71 if (cstate_window) { 72 (void) delwin(cstate_window); 73 cstate_window = NULL; 74 } 75 if (wakeup_window) { 76 (void) delwin(wakeup_window); 77 wakeup_window = NULL; 78 } 79 if (acpi_power_window) { 80 (void) delwin(acpi_power_window); 81 acpi_power_window = NULL; 82 } 83 if (eventstat_window) { 84 (void) delwin(eventstat_window); 85 eventstat_window = NULL; 86 } 87 if (suggestion_window) { 88 (void) delwin(suggestion_window); 89 suggestion_window = NULL; 90 } 91 if (status_bar_window) { 92 (void) delwin(status_bar_window); 93 status_bar_window = NULL; 94 } 95 } 96 97 void 98 cleanup_curses(void) 99 { 100 (void) endwin(); 101 } 102 103 /* 104 * This part was re-written to be human readable and easy to modify. Please 105 * try to keep it that way and help us save some time. 106 * 107 * Friendly reminder: 108 * subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x) 109 */ 110 void 111 setup_windows(void) 112 { 113 /* 114 * These variables are used to properly set the initial y position and 115 * number of lines in each subwindow, as the number of supported CPU 116 * states affects their placement. 117 */ 118 int cstate_lines, event_lines, pos_y; 119 120 getmaxyx(stdscr, maxy, maxx); 121 122 zap_windows(); 123 124 cstate_lines = TITLE_LINE + max((g_max_cstate+1), g_npstates); 125 126 pos_y = 0; 127 title_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0); 128 129 pos_y += NEXT_LINE + BLANK_LINE; 130 cstate_window = subwin(stdscr, cstate_lines, maxx, pos_y, 0); 131 132 pos_y += cstate_lines + BLANK_LINE; 133 wakeup_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0); 134 135 pos_y += NEXT_LINE; 136 acpi_power_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0); 137 138 pos_y += NEXT_LINE + BLANK_LINE; 139 event_lines = maxy - SINGLE_LINE_SW - NEXT_LINE - LENGTH_SUGG_SW - 140 pos_y; 141 eventstat_window = subwin(stdscr, event_lines, maxx, pos_y, 0); 142 143 pos_y += event_lines + NEXT_LINE; 144 suggestion_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0); 145 146 pos_y += BLANK_LINE + NEXT_LINE; 147 status_bar_window = subwin(stdscr, SINGLE_LINE_SW, maxx, pos_y, 0); 148 149 (void) strcpy(g_status_bar_slots[0], _(" Q - Quit ")); 150 (void) strcpy(g_status_bar_slots[1], _(" R - Refresh ")); 151 152 (void) werase(stdscr); 153 (void) wrefresh(status_bar_window); 154 } 155 156 void 157 initialize_curses(void) 158 { 159 (void) initscr(); 160 (void) start_color(); 161 162 /* 163 * Enable keyboard mapping 164 */ 165 (void) keypad(stdscr, TRUE); 166 167 /* 168 * Tell curses not to do NL->CR/NL on output 169 */ 170 (void) nonl(); 171 172 /* 173 * Take input chars one at a time, no wait for \n 174 */ 175 (void) cbreak(); 176 177 /* 178 * Dont echo input 179 */ 180 (void) noecho(); 181 182 /* 183 * Turn off cursor 184 */ 185 (void) curs_set(0); 186 187 (void) init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); 188 (void) init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE); 189 (void) init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED); 190 (void) init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED); 191 (void) init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW); 192 (void) init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN); 193 (void) init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE); 194 (void) init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK); 195 196 (void) atexit(cleanup_curses); 197 } 198 199 void 200 show_title_bar(void) 201 { 202 int i, x = 0, y = 0; 203 char title_pad[10]; 204 205 (void) wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); 206 (void) wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); 207 (void) werase(title_bar_window); 208 209 (void) snprintf(title_pad, 10, "%%%ds", 210 (maxx - strlen(TITLE))/2 + strlen(TITLE)); 211 /* LINTED: E_SEC_PRINTF_VAR_FMT */ 212 print(title_bar_window, y, x, title_pad, TITLE); 213 214 (void) wrefresh(title_bar_window); 215 (void) werase(status_bar_window); 216 217 for (i = 0; i < PT_BAR_NSLOTS; i++) { 218 if (strlen(g_status_bar_slots[i]) == 0) 219 continue; 220 (void) wattron(status_bar_window, A_REVERSE); 221 print(status_bar_window, y, x, "%s", g_status_bar_slots[i]); 222 (void) wattroff(status_bar_window, A_REVERSE); 223 x += strlen(g_status_bar_slots[i]) + 1; 224 } 225 (void) wnoutrefresh(status_bar_window); 226 } 227 228 void 229 show_cstates(void) 230 { 231 char c[100]; 232 int i; 233 double total_pstates = 0.0, avg, res; 234 uint64_t p0_speed, p1_speed; 235 236 if (!PT_ON_DUMP) { 237 (void) werase(cstate_window); 238 (void) wattrset(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT)); 239 (void) wbkgd(cstate_window, COLOR_PAIR(PT_COLOR_DEFAULT)); 240 } 241 242 print(cstate_window, 0, 0, "%s\tAvg\tresidency\n", g_msg_idle_state); 243 res = (((double)g_cstate_info[0].total_time / g_total_c_time)) * 100; 244 (void) sprintf(c, "C0 (cpu running)\t\t(%.1f%%)\n", (float)res); 245 print(cstate_window, 1, 0, "%s", c); 246 247 for (i = 1; i <= g_max_cstate; i++) { 248 /* 249 * In situations where the load is too intensive, the system 250 * might not transition at all. 251 */ 252 if (g_cstate_info[i].events > 0) 253 avg = (((double)g_cstate_info[i].total_time/ 254 MICROSEC)/g_cstate_info[i].events); 255 else 256 avg = 0; 257 258 res = ((double)g_cstate_info[i].total_time/g_total_c_time) 259 * 100; 260 261 (void) sprintf(c, "C%d\t\t\t%.1fms\t(%.1f%%)\n", i, (float)avg, 262 (float)res); 263 print(cstate_window, i + 1, 0, "%s", c); 264 } 265 266 print(cstate_window, 0, 48, "%s\n", g_msg_freq_state); 267 268 if (g_npstates < 2) { 269 (void) sprintf(c, "%4lu Mhz\t%.1f%%", 270 (long)g_pstate_info[0].speed, 100.0); 271 print(cstate_window, 1, 48, "%s\n", c); 272 } else { 273 for (i = 0; i < g_npstates; i++) { 274 total_pstates += (double)(g_pstate_info[i].total_time/ 275 g_ncpus_observed/MICROSEC); 276 } 277 278 /* 279 * display ACPI_PSTATE from P(n) to P(1) 280 */ 281 for (i = 0; i < g_npstates - 1; i++) { 282 (void) sprintf(c, "%4lu Mhz\t%.1f%%", 283 (long)g_pstate_info[i].speed, 284 100 * (g_pstate_info[i].total_time/g_ncpus_observed/ 285 MICROSEC/total_pstates)); 286 print(cstate_window, i+1, 48, "%s\n", c); 287 } 288 289 /* 290 * Display ACPI_PSTATE P0 according to if turbo 291 * mode is supported 292 */ 293 if (g_turbo_supported) { 294 p1_speed = g_pstate_info[g_npstates - 2].speed; 295 296 /* 297 * If g_turbo_ratio <= 1.0, it will be ignored. 298 * we display P(0) as P(1) + 1. 299 */ 300 if (g_turbo_ratio <= 1.0) { 301 p0_speed = p1_speed + 1; 302 } else { 303 /* 304 * If g_turbo_ratio > 1.0, that means turbo 305 * mode works. So, P(0) = ratio * P(1); 306 */ 307 p0_speed = (uint64_t)(p1_speed * g_turbo_ratio); 308 if (p0_speed < (p1_speed + 1)) 309 p0_speed = p1_speed + 1; 310 } 311 /* 312 * Reset the ratio for the next round 313 */ 314 g_turbo_ratio = 0.0; 315 316 /* 317 * Setup the string for the display 318 */ 319 (void) sprintf(c, "%4lu Mhz(turbo)\t%.1f%%", 320 (long)p0_speed, 321 100 * (g_pstate_info[i].total_time/ 322 g_ncpus_observed/MICROSEC/total_pstates)); 323 } else { 324 (void) sprintf(c, "%4lu Mhz\t%.1f%%", 325 (long)g_pstate_info[i].speed, 326 100 * (g_pstate_info[i].total_time/ 327 g_ncpus_observed/MICROSEC/total_pstates)); 328 } 329 print(cstate_window, i+1, 48, "%s\n", c); 330 } 331 332 if (!PT_ON_DUMP) 333 (void) wnoutrefresh(cstate_window); 334 } 335 336 void 337 show_acpi_power_line(uint32_t flag, double rate, double rem_cap, double cap, 338 uint32_t state) 339 { 340 char buffer[1024]; 341 342 (void) sprintf(buffer, _("no ACPI power usage estimate available")); 343 344 if (!PT_ON_DUMP) 345 (void) werase(acpi_power_window); 346 if (flag) { 347 char *c; 348 (void) sprintf(buffer, "Power usage (ACPI estimate): %.3fW", 349 rate); 350 (void) strcat(buffer, " "); 351 c = &buffer[strlen(buffer)]; 352 switch (state) { 353 case 0: 354 (void) sprintf(c, "(running on AC power, fully " 355 "charged)"); 356 break; 357 case 1: 358 (void) sprintf(c, "(discharging: %3.1f hours)", 359 (uint32_t)rem_cap/rate); 360 break; 361 case 2: 362 (void) sprintf(c, "(charging: %3.1f hours)", 363 (uint32_t)(cap - rem_cap)/rate); 364 break; 365 case 4: 366 (void) sprintf(c, "(##critically low battery power##)"); 367 break; 368 } 369 370 } 371 print(acpi_power_window, 0, 0, "%s\n", buffer); 372 if (!PT_ON_DUMP) 373 (void) wnoutrefresh(acpi_power_window); 374 } 375 376 void 377 show_wakeups(double interval) 378 { 379 char c[100]; 380 int i, event_sum = 0; 381 event_info_t *event = g_event_info; 382 383 if (!PT_ON_DUMP) { 384 (void) werase(wakeup_window); 385 (void) wbkgd(wakeup_window, COLOR_PAIR(PT_COLOR_RED)); 386 (void) wattron(wakeup_window, A_BOLD); 387 } 388 389 /* 390 * calculate the actual total event number 391 */ 392 for (i = 0; i < g_top_events; i++, event++) 393 event_sum += event->total_count; 394 395 /* 396 * g_total_events is the sum of the number of Cx->C0 transition, 397 * So when the system is very busy, the idle thread will have no 398 * chance or very seldom to be scheduled, this could cause >100% 399 * event report. Re-assign g_total_events to the actual event 400 * number is a way to avoid this issue. 401 */ 402 if (event_sum > g_total_events) 403 g_total_events = event_sum; 404 405 (void) sprintf(c, "Wakeups-from-idle per second: %4.1f\tinterval: " 406 "%.1fs", (double)(g_total_events/interval), interval); 407 print(wakeup_window, 0, 0, "%s\n", c); 408 409 if (!PT_ON_DUMP) 410 (void) wnoutrefresh(wakeup_window); 411 } 412 413 void 414 show_eventstats(double interval) 415 { 416 char c[100]; 417 int i; 418 double events; 419 event_info_t *event = g_event_info; 420 421 if (!PT_ON_DUMP) { 422 (void) werase(eventstat_window); 423 (void) wattrset(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT)); 424 (void) wbkgd(eventstat_window, COLOR_PAIR(PT_COLOR_DEFAULT)); 425 } 426 427 /* 428 * Sort the event report list 429 */ 430 if (g_top_events > EVENT_NUM_MAX) 431 g_top_events = EVENT_NUM_MAX; 432 433 qsort((void *)g_event_info, g_top_events, sizeof (event_info_t), 434 event_compare); 435 436 if (PT_ON_CPU) 437 (void) sprintf(c, "Top causes for wakeups on CPU %d:\n", 438 g_observed_cpu); 439 else 440 (void) sprintf(c, "Top causes for wakeups:\n"); 441 442 print(eventstat_window, 0, 0, "%s", c); 443 444 for (i = 0; i < g_top_events; i++, event++) { 445 446 if (g_total_events > 0 && event->total_count > 0) 447 events = (double)event->total_count/ 448 (double)g_total_events; 449 else 450 continue; 451 452 (void) sprintf(c, "%4.1f%% (%5.1f)", 100 * events, 453 (double)event->total_count/interval); 454 print(eventstat_window, i+1, 0, "%s", c); 455 print(eventstat_window, i+1, 16, "%20s :", 456 event->offender_name); 457 print(eventstat_window, i+1, 40, "%-64s\n", 458 event->offense_name); 459 } 460 461 if (!PT_ON_DUMP) 462 (void) wnoutrefresh(eventstat_window); 463 } 464 465 void 466 show_suggestion(char *sug) 467 { 468 (void) werase(suggestion_window); 469 print(suggestion_window, 0, 0, "%s", sug); 470 (void) wnoutrefresh(suggestion_window); 471 } 472 473 void 474 update_windows(void) 475 { 476 (void) doupdate(); 477 } 478