65 using clock = std::chrono::steady_clock;
73 : pid_(
pid.value_or(current_pid())),
74 ncpu_(std::max(1u, std::thread::hardware_concurrency()))
92 std::scoped_lock lk(m_);
93 auto proc_ns = process_cpu_time_ns_(pid_);
94 auto sys_ns = system_cpu_time_ns_();
96 if (proc_ns && sys_ns) {
98 auto dproc = (*proc_ns >= last_->proc_ns) ? (*proc_ns - last_->proc_ns) : 0ULL;
99 auto dsys = (*sys_ns >= last_->sys_ns) ? (*sys_ns - last_->sys_ns) : 0ULL;
101 cpu_total_capacity_ =
103 ? (
static_cast<double>(dproc) * 100.0) /
static_cast<double>(dsys)
106 last_ = sample_state{ *proc_ns, *sys_ns };
108 cpu_total_capacity_ = 0.0;
123 std::scoped_lock lk(m_);
124 return cpu_total_capacity_ < 0.0 ? 0.0 : cpu_total_capacity_;
136 std::scoped_lock lk(m_);
137 return (cpu_total_capacity_ < 0.0 ? 0.0 : cpu_total_capacity_)
138 *
static_cast<double>(ncpu_);
147 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
148 if (snap == INVALID_HANDLE_VALUE)
return 0;
151 te.dwSize =
sizeof(te);
153 std::uint32_t count = 0;
154 if (Thread32First(snap, &te)) {
156 if (te.th32OwnerProcessID == pid_) ++count;
157 }
while (Thread32Next(snap, &te));
161#elif defined(__linux__)
162 std::ifstream f(
"/proc/" + std::to_string(pid_) +
"/status");
165 while (std::getline(f, line)) {
166 if (line.rfind(
"Threads:", 0) == 0) {
167 std::istringstream iss(line.substr(8));
174#elif defined(__APPLE__)
175 proc_taskinfo info{};
176 if (read_task_info_(pid_, &info)) {
177 return static_cast<std::uint32_t
>(info.pti_threadnum);
180 mach_port_t task = task_for_pid_port_(pid_);
181 if (task == MACH_PORT_NULL)
return 0;
183 thread_act_array_t thread_list =
nullptr;
185 if (task_threads(task, &thread_list, &
thread_count) != KERN_SUCCESS) {
189 for (mach_msg_type_number_t i = 0; i <
thread_count; ++i) {
190 mach_port_deallocate(mach_task_self(), thread_list[i]);
192 vm_deallocate(mach_task_self(),
reinterpret_cast<vm_address_t
>(thread_list),
193 static_cast<vm_size_t
>(
thread_count *
sizeof(thread_act_t)));
210 HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
214 PROCESS_MEMORY_COUNTERS_EX pmc{};
215 std::uint64_t ws = 0;
216 if (GetProcessMemoryInfo(h,
217 reinterpret_cast<PROCESS_MEMORY_COUNTERS*
>(&pmc),
sizeof(pmc))) {
218 ws =
static_cast<std::uint64_t
>(pmc.WorkingSetSize);
222#elif defined(__linux__)
223 std::ifstream f(
"/proc/" + std::to_string(pid_) +
"/statm");
226 std::uint64_t size=0, resident=0;
227 f >> size >> resident;
228 long page = ::sysconf(_SC_PAGESIZE);
229 if (page <= 0)
return 0;
231 return resident *
static_cast<std::uint64_t
>(page);
232#elif defined(__APPLE__)
233 proc_taskinfo proc_info{};
234 if (read_task_info_(pid_, &proc_info)) {
235 return static_cast<std::uint64_t
>(proc_info.pti_resident_size);
238 mach_port_t task = task_for_pid_port_(pid_);
239 if (task == MACH_PORT_NULL)
return 0;
241 task_basic_info_data_t mach_info{};
242 mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
243 if (task_info(task, TASK_BASIC_INFO,
244 reinterpret_cast<task_info_t
>(&mach_info), &count) != KERN_SUCCESS)
247 return static_cast<std::uint64_t
>(mach_info.resident_size);
258 struct sample_state {
259 std::uint64_t proc_ns;
260 std::uint64_t sys_ns;
269 return GetCurrentProcessId();
275#if defined(__APPLE__)
276 static std::uint64_t mach_absolute_to_ns_(std::uint64_t ticks) {
277 mach_timebase_info_data_t timebase{};
278 if (mach_timebase_info(&timebase) != KERN_SUCCESS || timebase.denom == 0) {
281 const long double ns =
static_cast<long double>(ticks) *
282 static_cast<long double>(timebase.numer) /
283 static_cast<long double>(timebase.denom);
284 return static_cast<std::uint64_t
>(ns);
287 static bool read_task_info_(
pid_type pid, proc_taskinfo* out_info) {
288 if (out_info ==
nullptr) {
292 proc_taskinfo info{};
293 const int bytes = proc_pidinfo(
pid, PROC_PIDTASKINFO, 0, &info, PROC_PIDTASKINFO_SIZE);
294 if (bytes != PROC_PIDTASKINFO_SIZE) {
309 static mach_port_t task_for_pid_port_(
pid_type pid) {
310 if (
pid == current_pid())
311 return mach_task_self();
313 mach_port_t task = MACH_PORT_NULL;
314 if (task_for_pid(mach_task_self(),
pid, &task) != KERN_SUCCESS)
315 return MACH_PORT_NULL;
326 static std::optional<std::uint64_t> process_cpu_time_ns_(
pid_type pid) {
328 HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
pid);
329 if (!h)
return std::nullopt;
331 FILETIME ft_create{}, ft_exit{}, ft_kernel{}, ft_user{};
332 if (!GetProcessTimes(h, &ft_create, &ft_exit, &ft_kernel, &ft_user)) {
337 auto to100 = [](
const FILETIME& ft) -> std::uint64_t {
339 u.LowPart = ft.dwLowDateTime;
340 u.HighPart = ft.dwHighDateTime;
341 return static_cast<std::uint64_t
>(u.QuadPart);
344 std::uint64_t total_ns = (to100(ft_kernel) + to100(ft_user)) * 100ULL;
348#elif defined(__linux__)
349 std::ifstream f(
"/proc/" + std::to_string(
pid) +
"/stat");
350 if (!f)
return std::nullopt;
356 f >> dummy >> comm >> state;
357 if (!f)
return std::nullopt;
360 for (
int i = 0; i < 10; ++i) { std::string tmp; f >> tmp;
if (!f)
return std::nullopt; }
362 std::uint64_t ut = 0, st = 0;
364 if (!f)
return std::nullopt;
366 long hz = ::sysconf(_SC_CLK_TCK);
367 if (hz <= 0)
return std::nullopt;
369 long double sec =
static_cast<long double>(ut + st) /
static_cast<long double>(hz);
370 return static_cast<std::uint64_t
>(sec * 1.0e9L);
372#elif defined(__APPLE__)
373 proc_taskinfo proc_info{};
374 if (read_task_info_(
pid, &proc_info)) {
375 const std::uint64_t total =
static_cast<std::uint64_t
>(proc_info.pti_total_user + proc_info.pti_total_system);
376 return mach_absolute_to_ns_(total);
379 mach_port_t task = task_for_pid_port_(
pid);
380 if (task == MACH_PORT_NULL)
return std::nullopt;
382 task_thread_times_info_data_t mach_info{};
383 mach_msg_type_number_t count = TASK_THREAD_TIMES_INFO_COUNT;
384 if (task_info(task, TASK_THREAD_TIMES_INFO,
385 reinterpret_cast<task_info_t
>(&mach_info), &count) != KERN_SUCCESS)
388 auto tv2ns = [](time_value_t tv) -> std::uint64_t {
389 return static_cast<std::uint64_t
>(tv.seconds) * 1000000000ULL +
390 static_cast<std::uint64_t
>(tv.microseconds) * 1000ULL;
393 return tv2ns(mach_info.user_time) + tv2ns(mach_info.system_time);
404 static std::optional<std::uint64_t> system_cpu_time_ns_() {
406 FILETIME ft_idle{}, ft_kernel{}, ft_user{};
407 if (!GetSystemTimes(&ft_idle, &ft_kernel, &ft_user))
410 auto to100 = [](
const FILETIME& ft) -> std::uint64_t {
412 u.LowPart = ft.dwLowDateTime;
413 u.HighPart = ft.dwHighDateTime;
414 return static_cast<std::uint64_t
>(u.QuadPart);
417 return (to100(ft_kernel) + to100(ft_user)) * 100ULL;
419#elif defined(__linux__)
420 std::ifstream f(
"/proc/stat");
421 if (!f)
return std::nullopt;
425 if (!f || cpu !=
"cpu")
return std::nullopt;
427 std::uint64_t u=0,n=0,s=0,i=0,iw=0,irq=0,si=0,st=0;
428 f >> u >> n >> s >> i >> iw >> irq >> si >> st;
429 if (!f)
return std::nullopt;
431 long hz = ::sysconf(_SC_CLK_TCK);
432 if (hz <= 0)
return std::nullopt;
434 long double sec =
static_cast<long double>(u + n + s + i + iw + irq + si + st)
435 /
static_cast<long double>(hz);
436 return static_cast<std::uint64_t
>(sec * 1.0e9L);
438#elif defined(__APPLE__)
439 host_cpu_load_info_data_t info{};
440 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
441 if (host_statistics64(mach_host_self(), HOST_CPU_LOAD_INFO,
442 reinterpret_cast<host_info64_t
>(&info), &count) != KERN_SUCCESS)
445 std::uint64_t ticks =
446 static_cast<std::uint64_t
>(info.cpu_ticks[CPU_STATE_USER]) +
447 static_cast<std::uint64_t
>(info.cpu_ticks[CPU_STATE_SYSTEM]) +
448 static_cast<std::uint64_t
>(info.cpu_ticks[CPU_STATE_IDLE]) +
449 static_cast<std::uint64_t
>(info.cpu_ticks[CPU_STATE_NICE]);
451 long hz = ::sysconf(_SC_CLK_TCK);
452 if (hz <= 0) hz = 100;
454 long double sec =
static_cast<long double>(ticks) /
static_cast<long double>(hz);
455 return static_cast<std::uint64_t
>(sec * 1.0e9L);
462 mutable std::mutex m_;
463 std::optional<sample_state> last_;
464 double cpu_total_capacity_ = 0.0;