diff --git a/CMakeLists.txt b/CMakeLists.txt index cbd2825..eacc923 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,5 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_executable(tmux-mem-cpu-load tmux-mem-cpu-load.cpp) install(TARGETS tmux-mem-cpu-load - RUNTIME - DESTINATION bin + RUNTIME DESTINATION bin ) diff --git a/README.rst b/README.rst index a1fe17a..f0c4adf 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ Example output:: ^ ^ ^ ^ ^ ^ ^ | | | | | | | - 1 2 3 4 5 6 G + 1 2 3 4 5 6 7 1. Currently used memory. 2. Available memory. @@ -48,10 +48,13 @@ Installation Dependencies ------------ +Currently only tested on Linux. Mac OSX is known not to work. Patches or +hardware are welcome. + Building ~~~~~~~~ -* >= cmake_ -2.6 +* >= CMake_ -2.6 * C++ compiler (e.g. gcc/g++) @@ -107,6 +110,6 @@ Matt McCormick (thewtex) .. _tmux: http://tmux.sourceforge.net/ -.. _cmake: http://www.cmake.org +.. _CMake: http://www.cmake.org .. _`project homepage`: http://github.com/thewtex/tmux-mem-cpu-load .. _`terminals with 256 color support`: http://misc.flogisoft.com/bash/tip_colors_and_formatting#terminals_compatibility diff --git a/tmux-mem-cpu-load.cpp b/tmux-mem-cpu-load.cpp index fd6b994..501ae64 100644 --- a/tmux-mem-cpu-load.cpp +++ b/tmux-mem-cpu-load.cpp @@ -19,11 +19,90 @@ #include #include #include - #include // sleep +#include // for floorf +// Apple specific. +#if defined(__APPLE__) && defined(__MACH__) +// Mach kernel includes for getting memory and CPU statistics +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include // for sysctl +# include // for integer types +#endif + +// if we are on a BSD system +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +// TODO: Includes and *BSD support +# define BSD_BASED 1 +#endif + +// Tmux color lookup tables for the different metrics. #include "luts.h" +// Function declarations. +float cpu_percentage( unsigned int cpu_usage_delay ); +std::string cpu_string( unsigned int cpu_usage_delay, + unsigned int graph_lines, + bool use_colors = false ); +std::string mem_string( bool use_colors = false ); +std::string load_string( bool use_colors = false ); + + +#if defined(BSD_BASED) || (defined(__APPLE__) && defined(__MACH__)) +// OSX or BSD based system, use BSD APIs instead + +// See: http://www.opensource.apple.com/source/xnu/xnu-201/osfmk/mach/host_info.h +// and: http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/ +host_cpu_load_info_data_t _get_cpu_percentage() +{ + kern_return_t error; + mach_msg_type_number_t count; + host_cpu_load_info_data_t r_load; + mach_port_t mach_port; + + count = HOST_CPU_LOAD_INFO_COUNT; + mach_port = mach_host_self(); + error = host_statistics(mach_port, HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count); + if (error != KERN_SUCCESS) + { + return host_cpu_load_info_data_t(); + } + + return r_load; +} + +float cpu_percentage( unsigned int cpu_usage_delay ) +{ + // Get the load times from the XNU kernel + host_cpu_load_info_data_t load1 = _get_cpu_percentage(); + usleep(cpu_usage_delay); + host_cpu_load_info_data_t load2 = _get_cpu_percentage(); + + // Current load times + unsigned long long current_user = load1.cpu_ticks[CPU_STATE_USER]; + unsigned long long current_system = load1.cpu_ticks[CPU_STATE_SYSTEM]; + unsigned long long current_nice = load1.cpu_ticks[CPU_STATE_NICE]; + unsigned long long current_idle = load1.cpu_ticks[CPU_STATE_IDLE]; + // Next load times + unsigned long long next_user = load2.cpu_ticks[CPU_STATE_USER]; + unsigned long long next_system = load2.cpu_ticks[CPU_STATE_SYSTEM]; + unsigned long long next_nice = load2.cpu_ticks[CPU_STATE_NICE]; + unsigned long long next_idle = load2.cpu_ticks[CPU_STATE_IDLE]; + // Difference between the two + unsigned long long diff_user = next_user - current_user; + unsigned long long diff_system = next_system - current_system; + unsigned long long diff_nice = next_nice - current_nice; + unsigned long long diff_idle = next_idle - current_idle; + +#else // Linux float cpu_percentage( unsigned int cpu_usage_delay ) { std::string stat_line; @@ -79,13 +158,14 @@ float cpu_percentage( unsigned int cpu_usage_delay ) diff_system = next_system - current_system; diff_nice = next_nice - current_nice; diff_idle = next_idle - current_idle; +#endif // platform return static_cast(diff_user + diff_system + diff_nice)/static_cast(diff_user + diff_system + diff_nice + diff_idle)*100.0; } std::string cpu_string( unsigned int cpu_usage_delay, unsigned int graph_lines, - bool use_colors = false ) + bool use_colors ) { std::string meter( graph_lines + 2, ' ' ); meter[0] = '['; @@ -99,7 +179,7 @@ std::string cpu_string( unsigned int cpu_usage_delay, percentage = cpu_percentage( cpu_usage_delay ); float meter_step = 99.9 / graph_lines; meter_count = 1; - while( meter_count*meter_step < percentage ) + while(meter_count*meter_step < percentage) { meter[meter_count] = '|'; meter_count++; @@ -121,15 +201,56 @@ std::string cpu_string( unsigned int cpu_usage_delay, return oss.str(); } -std::string mem_string( bool use_colors = false ) + +std::string mem_string( bool use_colors ) { + std::ostringstream oss; +#if defined(BSD_BASED) || (defined(__APPLE__) && defined(__MACH__)) +// OSX or BSD based system, use BSD APIs instead + +#if defined(__APPLE__) && defined(__MACH__) + // These values are in bytes + int64_t total_mem; + int64_t used_mem; + int64_t unused_mem; + vm_size_t page_size; + mach_port_t mach_port; + mach_msg_type_number_t count; + vm_statistics_data_t vm_stats; + + // Get total physical memory + int mib[2]; + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + size_t length = sizeof(int64_t); + sysctl(mib, 2, &total_mem, &length, NULL, 0); + + mach_port = mach_host_self(); + count = sizeof(vm_stats) / sizeof(natural_t); + if (KERN_SUCCESS == host_page_size(mach_port, &page_size) && + KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count)) + { + unused_mem = (int64_t)vm_stats.free_count * (int64_t)page_size; + + used_mem = ((int64_t)vm_stats.active_count + + (int64_t)vm_stats.inactive_count + + (int64_t)vm_stats.wire_count) * (int64_t)page_size; + } + + // To kilobytes +#endif // Apple + // TODO BSD + + used_mem /= 1024; + total_mem /= 1024; + +#else // Linux unsigned int total_mem; unsigned int used_mem; unsigned int unused_mem; size_t line_start_pos; size_t line_end_pos; std::istringstream iss; - std::ostringstream oss; std::string mem_line; std::ifstream meminfo_file( "/proc/meminfo" ); @@ -154,6 +275,8 @@ std::string mem_string( bool use_colors = false ) } meminfo_file.close(); +#endif // platform + if( use_colors ) { oss << mem_lut[(100 * used_mem) / total_mem]; @@ -167,27 +290,47 @@ std::string mem_string( bool use_colors = false ) return oss.str(); } -std::string load_string( bool use_colors = false ) + +std::string load_string( bool use_colors ) { + std::ostringstream oss; + +#if defined(BSD_BASED) || (defined(__APPLE__) && defined(__MACH__)) + // Both apple and BSD style systems have these api calls + + // Only get 3 load averages + int nelem = 3; + double averages[3]; + // based on: http://www.opensource.apple.com/source/Libc/Libc-262/gen/getloadavg.c + if( getloadavg(averages, nelem) < 0 ) + { + oss << "0.00 0.00 0.00"; // couldn't get averages. + } + else + { + for(int i = 0; i < nelem; ++i) + { + // Round to nearest, make sure this is only a 0.00 value not a 0.0000 + float avg = floorf(static_cast(averages[i]) * 100 + 0.5) / 100; + oss << avg << " "; + } + } + +#else // Linux std::ifstream loadavg_file( "/proc/loadavg" ); std::string load_line; std::getline( loadavg_file, load_line ); loadavg_file.close(); + +#endif // platform + if( use_colors ) { - std::ostringstream oss; std::ifstream stat_file( "/proc/stat" ); std::string stat_line; std::getline( stat_file, stat_line ); - unsigned int number_of_cpus = 0; - std::getline( stat_file, stat_line ); - do - { - ++number_of_cpus; - std::getline( stat_file, stat_line ); - } - while( stat_line.compare( 0, 3, "cpu" ) == 0 && stat_file.good() ); - stat_file.close(); + // Likely does not work on BSD, but not tested + unsigned int number_of_cpus = sysconf( _SC_NPROCESSORS_ONLN ); std::istringstream iss( load_line.substr( 0, 4 ) ); float recent_load; @@ -199,17 +342,19 @@ std::string load_string( bool use_colors = false ) { load_percent = 100; } - oss << load_lut[load_percent]; - oss << load_line.substr( 0, 14 ); - oss << "#[fg=default,bg=default]"; - - return oss.str(); } - return load_line.substr( 0, 14 ); + oss << load_line.substr( 0, 14 ); + if( use_colors ) + { + oss << "#[fg=default,bg=default]"; + } + + return oss.str(); } + int main(int argc, char** argv) { unsigned int cpu_usage_delay = 900000;