Introduce cpu_usage module
This commit is contained in:
		
							parent
							
								
									c36fe3a004
								
							
						
					
					
						commit
						888adb57ec
					
				|  | @ -39,6 +39,7 @@ | |||
| #if defined(HAVE_CPU_LINUX) || defined(HAVE_CPU_BSD) | ||||
| #include "modules/cpu.hpp" | ||||
| #include "modules/cpu_frequency.hpp" | ||||
| #include "modules/cpu_usage.hpp" | ||||
| #include "modules/load.hpp" | ||||
| #endif | ||||
| #include "modules/idle_inhibitor.hpp" | ||||
|  |  | |||
|  | @ -0,0 +1,32 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <fmt/format.h> | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <fstream> | ||||
| #include <numeric> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "AButton.hpp" | ||||
| #include "util/sleeper_thread.hpp" | ||||
| 
 | ||||
| namespace waybar::modules { | ||||
| 
 | ||||
| class CpuUsage : public AButton { | ||||
|  public: | ||||
|   CpuUsage(const std::string&, const Json::Value&); | ||||
|   ~CpuUsage() = default; | ||||
|   auto update() -> void; | ||||
| 
 | ||||
|  private: | ||||
|   std::tuple<std::vector<uint16_t>, std::string> getCpuUsage(); | ||||
|   std::vector<std::tuple<size_t, size_t>> parseCpuinfo(); | ||||
| 
 | ||||
|   std::vector<std::tuple<size_t, size_t>> prev_times_; | ||||
| 
 | ||||
|   util::SleeperThread thread_; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace waybar::modules
 | ||||
|  | @ -192,6 +192,8 @@ if is_linux | |||
|         'src/modules/cpu/linux.cpp', | ||||
|         'src/modules/cpu_frequency/common.cpp', | ||||
|         'src/modules/cpu_frequency/linux.cpp', | ||||
|         'src/modules/cpu_usage/common.cpp', | ||||
|         'src/modules/cpu_usage/linux.cpp', | ||||
|         'src/modules/memory/common.cpp', | ||||
|         'src/modules/memory/linux.cpp', | ||||
|     ) | ||||
|  | @ -201,6 +203,8 @@ elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd | |||
|     src_files += files( | ||||
|         'src/modules/cpu/bsd.cpp', | ||||
|         'src/modules/cpu/common.cpp', | ||||
|         'src/modules/cpu_usage/bsd.cpp', | ||||
|         'src/modules/cpu_usage/common.cpp', | ||||
|         'src/modules/memory/bsd.cpp', | ||||
|         'src/modules/memory/common.cpp', | ||||
|     ) | ||||
|  |  | |||
|  | @ -102,6 +102,9 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { | |||
|     if (ref == "cpu_frequency") { | ||||
|       return new waybar::modules::CpuFrequency(id, config_[name]); | ||||
|     } | ||||
|     if (ref == "cpu_usage") { | ||||
|       return new waybar::modules::CpuUsage(id, config_[name]); | ||||
|     } | ||||
|     if (ref == "load") { | ||||
|       return new waybar::modules::Load(id, config_[name]); | ||||
|     } | ||||
|  |  | |||
|  | @ -0,0 +1,102 @@ | |||
| #include <spdlog/spdlog.h> | ||||
| // clang-format off
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/sysctl.h> | ||||
| // clang-format on
 | ||||
| #include <unistd.h>  // sysconf
 | ||||
| 
 | ||||
| #include <cmath>    // NAN
 | ||||
| #include <cstdlib>  // malloc
 | ||||
| 
 | ||||
| #include "modules/cpu_usage.hpp" | ||||
| 
 | ||||
| #if defined(__NetBSD__) || defined(__OpenBSD__) | ||||
| #include <sys/sched.h> | ||||
| #else | ||||
| #include <sys/resource.h> | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__NetBSD__) | ||||
| typedef uint64_t cp_time_t; | ||||
| #else | ||||
| typedef long cp_time_t; | ||||
| #endif | ||||
| #if defined(__NetBSD__) || defined(__OpenBSD__) | ||||
| typedef uint64_t pcp_time_t; | ||||
| #else | ||||
| typedef long pcp_time_t; | ||||
| #endif | ||||
| 
 | ||||
| std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo() { | ||||
|   cp_time_t sum_cp_time[CPUSTATES]; | ||||
|   size_t sum_sz = sizeof(sum_cp_time); | ||||
|   int ncpu = sysconf(_SC_NPROCESSORS_CONF); | ||||
|   size_t sz = CPUSTATES * (ncpu + 1) * sizeof(pcp_time_t); | ||||
|   pcp_time_t *cp_time = static_cast<pcp_time_t *>(malloc(sz)), *pcp_time = cp_time; | ||||
| #if defined(__NetBSD__) | ||||
|   int mib[] = { | ||||
|       CTL_KERN, | ||||
|       KERN_CP_TIME, | ||||
|   }; | ||||
|   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { | ||||
|     throw std::runtime_error("sysctl kern.cp_time failed"); | ||||
|   } | ||||
|   for (int state = 0; state < CPUSTATES; state++) { | ||||
|     cp_time[state] = sum_cp_time[state]; | ||||
|   } | ||||
|   pcp_time += CPUSTATES; | ||||
|   if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { | ||||
|     throw std::runtime_error("sysctl kern.cp_time failed"); | ||||
|   } | ||||
| #elif defined(__OpenBSD__) | ||||
|   { | ||||
|     int mib[] = { | ||||
|         CTL_KERN, | ||||
|         KERN_CPTIME, | ||||
|     }; | ||||
|     if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), sum_cp_time, &sum_sz, NULL, 0)) { | ||||
|       throw std::runtime_error("sysctl kern.cp_time failed"); | ||||
|     } | ||||
|   } | ||||
|   for (int state = 0; state < CPUSTATES; state++) { | ||||
|     cp_time[state] = sum_cp_time[state]; | ||||
|   } | ||||
|   pcp_time = cp_time; | ||||
|   sz /= ncpu + 1; | ||||
|   { | ||||
|     int mib[] = { | ||||
|         CTL_KERN, | ||||
|         KERN_CPTIME2, | ||||
|         0, | ||||
|     }; | ||||
|     for (int cpu = 0; cpu < ncpu; cpu++) { | ||||
|       mib[2] = cpu; | ||||
|       pcp_time += CPUSTATES; | ||||
|       if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), pcp_time, &sz, NULL, 0)) { | ||||
|         throw std::runtime_error("sysctl kern.cp_time2 failed"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| #else | ||||
|   if (sysctlbyname("kern.cp_time", sum_cp_time, &sum_sz, NULL, 0)) { | ||||
|     throw std::runtime_error("sysctl kern.cp_time failed"); | ||||
|   } | ||||
|   for (int state = 0; state < CPUSTATES; state++) { | ||||
|     cp_time[state] = sum_cp_time[state]; | ||||
|   } | ||||
|   pcp_time += CPUSTATES; | ||||
|   if (sysctlbyname("kern.cp_times", pcp_time, &sz, NULL, 0)) { | ||||
|     throw std::runtime_error("sysctl kern.cp_times failed"); | ||||
|   } | ||||
| #endif | ||||
|   std::vector<std::tuple<size_t, size_t>> cpuinfo; | ||||
|   for (int cpu = 0; cpu < ncpu + 1; cpu++) { | ||||
|     pcp_time_t total = 0, *single_cp_time = &cp_time[cpu * CPUSTATES]; | ||||
|     for (int state = 0; state < CPUSTATES; state++) { | ||||
|       total += single_cp_time[state]; | ||||
|     } | ||||
|     cpuinfo.emplace_back(single_cp_time[CP_IDLE], total); | ||||
|   } | ||||
|   free(cp_time); | ||||
|   return cpuinfo; | ||||
| } | ||||
|  | @ -0,0 +1,78 @@ | |||
| #include "modules/cpu_usage.hpp" | ||||
| 
 | ||||
| // In the 80000 version of fmt library authors decided to optimize imports
 | ||||
| // and moved declarations required for fmt::dynamic_format_arg_store in new
 | ||||
| // header fmt/args.h
 | ||||
| #if (FMT_VERSION >= 80000) | ||||
| #include <fmt/args.h> | ||||
| #else | ||||
| #include <fmt/core.h> | ||||
| #endif | ||||
| 
 | ||||
| waybar::modules::CpuUsage::CpuUsage(const std::string& id, const Json::Value& config) | ||||
|     : AButton(config, "cpu_usage", id, "{usage}%", 10) { | ||||
|   thread_ = [this] { | ||||
|     dp.emit(); | ||||
|     thread_.sleep_for(interval_); | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| auto waybar::modules::CpuUsage::update() -> void { | ||||
|   // TODO: as creating dynamic fmt::arg arrays is buggy we have to calc both
 | ||||
|   auto [cpu_usage, tooltip] = getCpuUsage(); | ||||
|   if (tooltipEnabled()) { | ||||
|     button_.set_tooltip_text(tooltip); | ||||
|   } | ||||
|   auto format = format_; | ||||
|   auto total_usage = cpu_usage.empty() ? 0 : cpu_usage[0]; | ||||
|   auto state = getState(total_usage); | ||||
|   if (!state.empty() && config_["format-" + state].isString()) { | ||||
|     format = config_["format-" + state].asString(); | ||||
|   } | ||||
| 
 | ||||
|   if (format.empty()) { | ||||
|     event_box_.hide(); | ||||
|   } else { | ||||
|     event_box_.show(); | ||||
|     auto icons = std::vector<std::string>{state}; | ||||
|     fmt::dynamic_format_arg_store<fmt::format_context> store; | ||||
|     store.push_back(fmt::arg("usage", total_usage)); | ||||
|     store.push_back(fmt::arg("icon", getIcon(total_usage, icons))); | ||||
|     for (size_t i = 1; i < cpu_usage.size(); ++i) { | ||||
|       auto core_i = i - 1; | ||||
|       auto core_format = fmt::format("usage{}", core_i); | ||||
|       store.push_back(fmt::arg(core_format.c_str(), cpu_usage[i])); | ||||
|       auto icon_format = fmt::format("icon{}", core_i); | ||||
|       store.push_back(fmt::arg(icon_format.c_str(), getIcon(cpu_usage[i], icons))); | ||||
|     } | ||||
|     label_->set_markup(fmt::vformat(format, store)); | ||||
|   } | ||||
| 
 | ||||
|   // Call parent update
 | ||||
|   AButton::update(); | ||||
| } | ||||
| 
 | ||||
| std::tuple<std::vector<uint16_t>, std::string> waybar::modules::CpuUsage::getCpuUsage() { | ||||
|   if (prev_times_.empty()) { | ||||
|     prev_times_ = parseCpuinfo(); | ||||
|     std::this_thread::sleep_for(std::chrono::milliseconds(100)); | ||||
|   } | ||||
|   std::vector<std::tuple<size_t, size_t>> curr_times = parseCpuinfo(); | ||||
|   std::string tooltip; | ||||
|   std::vector<uint16_t> usage; | ||||
|   for (size_t i = 0; i < curr_times.size(); ++i) { | ||||
|     auto [curr_idle, curr_total] = curr_times[i]; | ||||
|     auto [prev_idle, prev_total] = prev_times_[i]; | ||||
|     const float delta_idle = curr_idle - prev_idle; | ||||
|     const float delta_total = curr_total - prev_total; | ||||
|     uint16_t tmp = 100 * (1 - delta_idle / delta_total); | ||||
|     if (i == 0) { | ||||
|       tooltip = fmt::format("Total: {}%", tmp); | ||||
|     } else { | ||||
|       tooltip = tooltip + fmt::format("\nCore{}: {}%", i - 1, tmp); | ||||
|     } | ||||
|     usage.push_back(tmp); | ||||
|   } | ||||
|   prev_times_ = curr_times; | ||||
|   return {usage, tooltip}; | ||||
| } | ||||
|  | @ -0,0 +1,31 @@ | |||
| #include <filesystem> | ||||
| 
 | ||||
| #include "modules/cpu_usage.hpp" | ||||
| 
 | ||||
| std::vector<std::tuple<size_t, size_t>> waybar::modules::CpuUsage::parseCpuinfo() { | ||||
|   const std::string data_dir_ = "/proc/stat"; | ||||
|   std::ifstream info(data_dir_); | ||||
|   if (!info.is_open()) { | ||||
|     throw std::runtime_error("Can't open " + data_dir_); | ||||
|   } | ||||
|   std::vector<std::tuple<size_t, size_t>> cpuinfo; | ||||
|   std::string line; | ||||
|   while (getline(info, line)) { | ||||
|     if (line.substr(0, 3).compare("cpu") != 0) { | ||||
|       break; | ||||
|     } | ||||
|     std::stringstream sline(line.substr(5)); | ||||
|     std::vector<size_t> times; | ||||
|     for (size_t time = 0; sline >> time; times.push_back(time)) | ||||
|       ; | ||||
| 
 | ||||
|     size_t idle_time = 0; | ||||
|     size_t total_time = 0; | ||||
|     if (times.size() >= 4) { | ||||
|       idle_time = times[3]; | ||||
|       total_time = std::accumulate(times.begin(), times.end(), 0); | ||||
|     } | ||||
|     cpuinfo.emplace_back(idle_time, total_time); | ||||
|   } | ||||
|   return cpuinfo; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue