├── LICENSE ├── Makefile ├── README.md ├── debian ├── changelog ├── control ├── copyright ├── freqtop.manpages ├── rules └── source │ └── format ├── freqtop.1 ├── freqtop.c └── images └── screenshot0.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bram Stolk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | freqtop: freqtop.c 2 | $(CC) -D_POSIX_C_SOURCE=199309L -std=c99 -Wall -g -o freqtop freqtop.c -lm 3 | 4 | clean: 5 | rm -f ./freqtop 6 | 7 | install: freqtop 8 | install -d ${DESTDIR}/usr/bin 9 | install -m 755 freqtop ${DESTDIR}/usr/bin/ 10 | 11 | uninstall: 12 | sudo rm -f ${DESTDIR}/usr/bin/freqtop 13 | 14 | DISTFILES=\ 15 | freqtop.c \ 16 | freqtop.1 \ 17 | Makefile \ 18 | README.md \ 19 | LICENSE \ 20 | images 21 | 22 | 23 | 24 | tarball: 25 | tar cvzf ../freqtop_1.0.orig.tar.gz ${DISTFILES} 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # freqtop 2 | Monitor for the CPU Frequency Scaling under Linux. 3 | 4 | ![screenshot](images/screenshot0.png "screenshot") 5 | 6 | 7 | ## Introduction 8 | 9 | The freqtop tool will show all logical cores in your system, with the bars representing the frequency scale. 10 | 11 | The green bar is at the minimum frequency level. 12 | 13 | The yellow bar is at the base frequency level. 14 | 15 | The red bar is at the turbo frequency level. 16 | 17 | The orange ticks will show the load for each core. When at the top, the core is fully utilized. When at the bottom, the core is fully idle. 18 | 19 | ## Keys 20 | 21 | Press ESCAPE to stop freqtop. 22 | 23 | ## Known issues 24 | 25 | * ~~Does not respond to resizing of terminal.~~ 26 | * ~~Missing manual page.~~ 27 | 28 | ## Copyright 29 | 30 | Freqtop is (c)2020 by Bram Stolk and licensed under the MIT license. 31 | 32 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | freqtop (1.0-1) kinetic; urgency=medium 2 | 3 | * Initial release. 4 | 5 | -- Bram Stolk Fri, 09 Sep 2022 10:54:00 -0700 6 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: freqtop 2 | Section: contrib/utils 3 | Priority: optional 4 | Maintainer: Bram Stolk 5 | Rules-Requires-Root: no 6 | Build-Depends: 7 | debhelper-compat (= 13) 8 | Standards-Version: 4.6.1 9 | Homepage: https://github.com/stolk/freqtop 10 | 11 | Package: freqtop 12 | Architecture: any 13 | Depends: ${shlibs:Depends}, ${misc:Depends} 14 | Description: Shows CPU core frequencies in live graph. 15 | For each logical CPU core, its frequency is plotted in a live bar graph. 16 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: freqtop 3 | Upstream-Contact: Bram Stolk 4 | Source: https://github.com/stolk/freqtop 5 | 6 | Files: * 7 | Copyright: 2021-2022 Bram Stolk 8 | License: MIT 9 | 10 | License: MIT 11 | MIT License 12 | . 13 | Copyright (c) 2022 Bram Stolk 14 | . 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | . 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | . 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /debian/freqtop.manpages: -------------------------------------------------------------------------------- 1 | freqtop.1 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # Output every command that modifies files on the build system. 4 | export DH_VERBOSE = 1 5 | 6 | 7 | # See FEATURE AREAS in dpkg-buildflags(1). 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # See ENVIRONMENT in dpkg-buildflags(1). 11 | # Package maintainers to append CFLAGS. 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # Package maintainers to append LDFLAGS. 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | 17 | %: 18 | dh $@ 19 | 20 | 21 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /freqtop.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" (C) Copyright 2022 Bram Stolk , 3 | .\" 4 | .\" First parameter, NAME, should be all caps 5 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 6 | .\" other parameters are allowed: see man(7), man(1) 7 | .TH FREQTOP 1 "September 9 2022" 8 | .\" Please adjust this date whenever revising the manpage. 9 | .\" 10 | .\" Some roff macros, for reference: 11 | .\" .nh disable hyphenation 12 | .\" .hy enable hyphenation 13 | .\" .ad l left justify 14 | .\" .ad b justify to both left and right margins 15 | .\" .nf disable filling 16 | .\" .fi enable filling 17 | .\" .br insert line break 18 | .\" .sp insert n+1 empty lines 19 | .\" for manpage-specific macros, see man(7) 20 | .SH NAME 21 | freqtop \- program to monitor CPU core frequency 22 | .sp 1 23 | .SH SYNOPSIS 24 | .B freqtop 25 | .sp 1 26 | .SH DESCRIPTION 27 | The \fBfreqtop\fP tool will show all logical cores in your system, with the bars representing the frequency scale. 28 | .sp 1 29 | The green bar is at the minimum frequency level. 30 | .sp 1 31 | The yellow bar is at the base frequency level. 32 | .sp 1 33 | The red bar is at the turbo frequency level. 34 | .sp 1 35 | The orange ticks will show the load for each core. When at the top, the core is fully utilized. When at the bottom, the core is fully idle. 36 | .sp 1 37 | .SH KEYS 38 | Press ESCAPE to stop freqtop. 39 | .sp 1 40 | -------------------------------------------------------------------------------- /freqtop.c: -------------------------------------------------------------------------------- 1 | // freqtop.c 2 | // 3 | // by Abraham Stolk. 4 | // This software falls under MIT license. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if defined(__FreeBSD__) 19 | # include 20 | # include 21 | #endif 22 | 23 | 24 | static int termw=0, termh=0; 25 | static int doubleres=0; 26 | static int blend=1; 27 | static unsigned char termbg[3] = { 0,0,0 }; 28 | 29 | static int imw=0; 30 | static int imh=0; 31 | static uint32_t* im=0; 32 | static char* legend=0; 33 | 34 | static int tabw=0; 35 | static int barw=0; 36 | static int barh=0; 37 | static int res=0; 38 | static int marginx=0; 39 | 40 | static int resized=1; 41 | 42 | static int highest_freq; // highest max freq over all cpus. 43 | 44 | 45 | #if defined(_WIN64) 46 | # include 47 | static void get_terminal_size(void) 48 | { 49 | const HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE ); 50 | CONSOLE_SCREEN_BUFFER_INFO info; 51 | GetConsoleScreenBufferInfo( hStdout, &info ); 52 | termw = info.dwSize.X; 53 | termh = info.dwSize.Y; 54 | if ( !termw ) termw = 80; 55 | } 56 | static int oldcodepage=0; 57 | static void set_console_mode(void) 58 | { 59 | DWORD mode=0; 60 | const HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE ); 61 | GetConsoleMode( hStdout, &mode ); 62 | mode = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING; 63 | SetConsoleMode( hStdout, mode ); 64 | oldcodepage = GetConsoleCP(); 65 | SetConsoleCP( 437 ); 66 | doubleres = 1; 67 | } 68 | #else 69 | static void get_terminal_size(void) 70 | { 71 | FILE* f = popen( "stty size", "r" ); 72 | if ( !f ) 73 | { 74 | fprintf( stderr, "Failed to determine terminal size using stty.\n" ); 75 | exit( 1 ); 76 | } 77 | const int num = fscanf( f, "%d %d", &termh, &termw ); 78 | assert( num == 2 ); 79 | pclose( f ); 80 | } 81 | static void set_console_mode() 82 | { 83 | doubleres=1; 84 | } 85 | #endif 86 | 87 | 88 | 89 | static void setup_image( int num_cpus ) 90 | { 91 | if (im) free(im); 92 | if (legend) free(legend); 93 | 94 | imw = termw; 95 | imh = 2*(termh-1); 96 | const size_t sz = imw*imh*4; 97 | im = (uint32_t*) malloc(sz); 98 | memset( im, 0x00, sz ); 99 | 100 | legend = (char*) malloc( imw*(imh/2) ); 101 | memset( legend, 0x00, imw*(imh/2) ); 102 | 103 | // Figure out layout. 104 | tabw = (imw-4) / num_cpus; 105 | tabw = tabw < 2 ? 2 : tabw; 106 | const int bspa = tabw-1; 107 | barw = bspa > 4 ? 4 : bspa; 108 | barh = imh-4; 109 | marginx = (imw - tabw*num_cpus)/2; 110 | marginx += (bspa-barw)/2; 111 | 112 | // Draw border into image. 113 | for ( int y=0; y highest_freq ? freq_max[i] : highest_freq; 132 | 133 | // Set up the legend. 134 | char label_bas[16]; 135 | char label_min[16]; 136 | char label_max[16]; 137 | snprintf( label_bas, sizeof(label_bas), "%3.1f", freq_bas[0] / 1000000.0f ); 138 | snprintf( label_min, sizeof(label_min), "%3.1f", freq_min[0] / 1000000.0f ); 139 | snprintf( label_max, sizeof(label_max), "%3.1f", highest_freq / 1000000.0f ); 140 | int y=1; int x=1; 141 | sprintf( legend + y * imw + x, "%s", label_max ); 142 | if ( freq_bas[0] != freq_max[0] ) 143 | { 144 | y=(imh/2) - (barh/2) * freq_bas[0] / (float) highest_freq; 145 | sprintf( legend + y * imw + x, "%s", label_bas ); 146 | } 147 | y=(imh/2) - (barh/2) * freq_min[0] / (float) highest_freq; 148 | sprintf( legend + y * imw + x, "%s", label_min ); 149 | } 150 | 151 | 152 | static void sigwinchHandler(int sig) 153 | { 154 | resized = 1; 155 | } 156 | 157 | 158 | static struct termios orig_termios; 159 | 160 | void disableRawMode() 161 | { 162 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); 163 | } 164 | 165 | 166 | void enableRawMode() 167 | { 168 | tcgetattr(STDIN_FILENO, &orig_termios); 169 | atexit(disableRawMode); 170 | struct termios raw = orig_termios; 171 | raw.c_lflag &= ~(ECHO); // Don't echo key presses. 172 | raw.c_lflag &= ~(ICANON); // Read by char, not by line. 173 | raw.c_cc[VMIN] = 0; // No minimum nr of chars. 174 | raw.c_cc[VTIME] = 0; // No waiting time. 175 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); 176 | } 177 | 178 | 179 | #define RESETALL "\x1b[0m" 180 | 181 | #define CURSORHOME "\x1b[1;1H" 182 | 183 | #define CLEARSCREEN "\x1b[2J" 184 | 185 | 186 | #if defined(_WIN64) 187 | # define HALFBLOCK "\xdf" // Uses IBM PC Codepage 437 char 223 188 | #else 189 | # define HALFBLOCK "▀" // Uses Unicode char U+2580 190 | #endif 191 | 192 | // Note: image has alpha pre-multied. Mimic GL_ONE + GL_ONE_MINUS_SRC_ALPHA 193 | #define BLEND \ 194 | { \ 195 | const int t0 = 255; \ 196 | const int t1 = 255-a; \ 197 | r = ( r * t0 + termbg[0] * t1 ) / 255; \ 198 | g = ( g * t0 + termbg[1] * t1 ) / 255; \ 199 | b = ( b * t0 + termbg[2] * t1 ) / 255; \ 200 | } 201 | 202 | static void print_image_double_res( int w, int h, unsigned char* data, char* legend ) 203 | { 204 | if ( h & 1 ) 205 | h--; 206 | const int linesz = 32768; 207 | char line[ linesz ]; 208 | 209 | for ( int y=0; y 0 ); 263 | fclose(f); 264 | return atoi( line ); 265 | } 266 | 267 | 268 | int get_cpu_coreid( int cpu ) 269 | { 270 | char fname[128]; 271 | char line [128]; 272 | snprintf( fname, sizeof(fname), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu ); 273 | FILE* f = fopen( fname, "rb" ); 274 | if ( !f ) return -1; 275 | const int numread = fread( line, 1, sizeof(line), f ); 276 | assert( numread > 0 ); 277 | fclose(f); 278 | return atoi( line ); 279 | } 280 | 281 | 282 | #if defined(__FreeBSD__) 283 | static int* mib_fr=0; 284 | static int get_cur_freq_via_sysctl( int num_cpus, int cpunr ) 285 | { 286 | if ( !mib_fr ) 287 | { 288 | mib_fr = (int*) malloc(num_cpus * 4 * sizeof(int)); 289 | for ( int cpu=0; cpu 0 ); 334 | rewind(f); 335 | if ( numr < sizeof(info) ) 336 | { 337 | info[numr] = 0; 338 | for ( int cpu=0; cpu> 0 ) & 0xff; 424 | termbg[ 1 ] = ( bg >> 8 ) & 0xff; 425 | termbg[ 0 ] = ( bg >> 16 ) & 0xff; 426 | blend = 1; 427 | } 428 | 429 | // Step 0: Windows cmd.exe needs to be put in proper console mode. 430 | set_console_mode(); 431 | 432 | // How many cores in this system? 433 | const int num_cpus = sysconf( _SC_NPROCESSORS_ONLN ); 434 | fprintf( stderr, "Found %d cpus.\n", num_cpus ); 435 | 436 | int freq_bas[ num_cpus ]; // Base frequencies. 437 | int freq_min[ num_cpus ]; // Min frequencies. 438 | int freq_max[ num_cpus ]; // Max frequencies. 439 | int freq_cur[ num_cpus ]; // Current frequencies. 440 | float usages[ num_cpus ]; // Core loads. 441 | int coreids [ num_cpus ]; // Core ids (could map to hyperthread sibling.) 442 | int rank [ num_cpus ]; // Ordering when displaying bars. 443 | int policy [ num_cpus ]; // Which scaling policy to use. Sometimes (rPi4) cores share 1 policy. 444 | int lastpolicy=-1; // Which policy did we see last. 445 | 446 | #if defined(__FreeBSD__) 447 | (void) policy; 448 | int mib_fl[num_cpus][4]; 449 | for ( int cpu=0; cpu=fl && fnr<20 ) 469 | { 470 | char* mid=0; 471 | char* end=0; 472 | int fr = (int) strtol(p, &mid, 10); 473 | int wa = (int) strtol(mid+1, &end, 10); 474 | p = end; 475 | fr *= 1000; 476 | if (!fnr || frfreq_max[cpu]) freq_max[cpu]=fr; 478 | if (!fnr || wawatt_max) watt_max=wa; 480 | fnr++; 481 | } 482 | fprintf(stderr,"cpu %d scales between %d .. %d with wattage between %d .. %d\n", cpu, freq_min[cpu], freq_max[cpu], watt_min, watt_max); 483 | coreids[cpu] = cpu; // BAD: We assume no hyperthreading on BSD. 484 | freq_bas[cpu] = (freq_max[cpu] + freq_min[cpu])/2; 485 | } 486 | #else 487 | for ( int cpu=0; cpu corehi ) 523 | corehi = coreids[ i ]; 524 | if ( corehi>-1 && corehi < num_cpus-1 ) 525 | { 526 | // Hyperthreading: reorder the cpus so that physical and hyperthread core are paired. 527 | int written=0; 528 | for ( int physcore=0; physcore<=corehi; ++physcore ) 529 | { 530 | for ( int i=0; i= freq_min[cpu] ) { r=0xa0; g=0xa0; b=0x00; } 602 | if ( f >= freq_bas[cpu] ) { r=0xc0; g=0x00; b=0x00; } 603 | if ( f > freq_max[cpu] ) { r=0x00; g=0x00; b=0x00; } 604 | uint32_t lo = 0x30 * i / barh; 605 | uint32_t hi = 0x30 + 0xc0 * i / barh; 606 | r = r < lo ? lo : r > hi ? hi : r; 607 | g = g < lo ? lo : g > hi ? hi : g; 608 | b = b < lo ? lo : b > hi ? hi : b; 609 | if ( f > fcur ) 610 | { 611 | r=(r>>2); 612 | g=(g>>2); 613 | b=(b>>2); 614 | a=(a>>2); 615 | } 616 | uint32_t c = (a<<24) | (b<<16) | (g<<8) | (r<<0); 617 | for ( int bx=1; bx<=barw; ++bx ) 618 | { 619 | int x = marginx + ra * tabw + bx; 620 | int y = imh - 2 - i; 621 | if ( x < imw ) 622 | im[ y*imw + x ] = c; 623 | } 624 | } 625 | } 626 | #if defined(__FreeBSD__) 627 | get_usages_via_sysctl( num_cpus, usages ); 628 | #else 629 | get_usages( num_cpus, usages ); 630 | #endif 631 | for ( int r=0; r