ohmu
·
2026-10-15
1#include "top.h"
2#include "common/debug.h"
3#include "common/mallocHelper.h"
4
5#include <errno.h>
6#include <string.h>
7#include <sys/sysctl.h>
8#include <libproc.h>
9
10const char* ffTopGetProcessSnapshot(FFlist* snapshots, FFTopTypes showTypes) {
11 int npids = proc_listallpids(nullptr, 0);
12 if (npids <= 0) {
13 FF_DEBUG("proc_listallpids(nullptr, 0) failed: %s", strerror(errno));
14 return "proc_listallpids(nullptr, 0) failed";
15 }
16 // `proc_listallpids` returns the number of pids, but it wants the size of the buffer in bytes:
17 // passing the count back would only let the kernel fill a quarter of it.
18 const int pidCapacity = npids + npids / 8 + 1;
19 FF_AUTO_FREE pid_t* pids = malloc((size_t) pidCapacity * sizeof(pid_t));
20 if (pids == nullptr) {
21 FF_DEBUG("malloc(%zu) for %d pids failed", (size_t) pidCapacity * sizeof(pid_t), pidCapacity);
22 return "malloc() failed";
23 }
24 npids = proc_listallpids(pids, pidCapacity * (int) sizeof(pid_t));
25 if (npids <= 0) {
26 FF_DEBUG("proc_listallpids(pids, %d) failed: %s", pidCapacity * (int) sizeof(pid_t), strerror(errno));
27 return "proc_listallpids(pids, bufferSize) failed";
28 }
29
30 uint32_t count = (uint32_t) npids;
31
32 for (uint32_t i = 0; i < count; ++i) {
33 pid_t pid = pids[i];
34
35 struct proc_taskallinfo proc;
36 if (proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &proc, sizeof(proc)) != sizeof(proc)) {
37 continue;
38 }
39
40 if (proc.pbsd.pbi_flags & PROC_FLAG_SYSTEM) {
41 continue;
42 }
43
44 FFTopProcessSnapshot* item = FF_LIST_ADD(FFTopProcessSnapshot, *snapshots);
45 ffStrbufInitS(&item->name, proc.pbsd.pbi_name);
46 if (item->name.length == 0) {
47 ffStrbufInitS(&item->name, proc.pbsd.pbi_comm);
48 }
49 item->pid = (uint32_t) pid;
50 item->cpuTime = (proc.ptinfo.pti_total_user + proc.ptinfo.pti_total_system) / 1000000u; // ns -> ms
51 item->memBytes = proc.ptinfo.pti_resident_size;
52 item->startTime = proc.pbsd.pbi_start_tvsec * 1000u + proc.pbsd.pbi_start_tvusec / 1000u; // convert to ms
53 item->threads = (uint32_t) proc.ptinfo.pti_threadnum;
54
55 // FF_LIST_ADD does not zero the element, and top.c subtracts both counters for every
56 // process it samples, whether or not they were collected.
57 item->bytesRead = 0;
58 item->bytesWritten = 0;
59 if (showTypes & FF_TOP_TYPE_DISK) {
60 struct rusage_info_v2 rusage;
61 if (proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t*) &rusage) == 0) {
62 item->bytesRead = rusage.ri_diskio_bytesread;
63 item->bytesWritten = rusage.ri_diskio_byteswritten;
64 }
65 }
66 }
67
68 return nullptr;
69}