master
c 342 lines 11.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "apps_plugin.h"
4
5 #if defined(OS_MACOS)
6
7 usec_t system_current_time_ut;
8 mach_timebase_info_data_t mach_info;
9
10 void apps_os_init_macos(void) {
11 mach_timebase_info(&mach_info);
12 }
13
14 uint64_t apps_os_get_total_memory_macos(void) {
15 uint64_t ret = 0;
16 int mib[2] = {CTL_HW, HW_MEMSIZE};
17 size_t size = sizeof(ret);
18 if (sysctl(mib, 2, &ret, &size, NULL, 0) == -1) {
19 netdata_log_error("Failed to get total memory using sysctl");
20 return 0;
21 }
22
23 return ret;
24 }
25
26 bool apps_os_read_pid_fds_macos(struct pid_stat *p, void *ptr __maybe_unused) {
27 static struct proc_fdinfo *fds = NULL;
28 static int fdsCapacity = 0;
29
30 int bufferSize = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, NULL, 0);
31 if (bufferSize <= 0) {
32 netdata_log_error("Failed to get the size of file descriptors for PID %d", p->pid);
33 return false;
34 }
35
36 // Resize buffer if necessary
37 if (bufferSize > fdsCapacity) {
38 if(fds)
39 freez(fds);
40
41 fds = mallocz(bufferSize);
42 fdsCapacity = bufferSize;
43 }
44
45 int num_fds = proc_pidinfo(p->pid, PROC_PIDLISTFDS, 0, fds, bufferSize) / PROC_PIDLISTFD_SIZE;
46 if (num_fds <= 0) {
47 netdata_log_error("Failed to get the file descriptors for PID %d", p->pid);
48 return false;
49 }
50
51 for (int i = 0; i < num_fds; i++) {
52 switch (fds[i].proc_fdtype) {
53 case PROX_FDTYPE_VNODE: {
54 struct vnode_fdinfowithpath vi;
55 if (proc_pidfdinfo(p->pid, fds[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vi, sizeof(vi)) > 0)
56 p->openfds.files++;
57 else
58 p->openfds.other++;
59
60 break;
61 }
62 case PROX_FDTYPE_SOCKET: {
63 p->openfds.sockets++;
64 break;
65 }
66 case PROX_FDTYPE_PIPE: {
67 p->openfds.pipes++;
68 break;
69 }
70
71 default:
72 p->openfds.other++;
73 break;
74 }
75 }
76
77 return true;
78 }
79
80 bool apps_os_get_pid_cmdline_macos(struct pid_stat *p, char *cmdline, size_t maxBytes) {
81 int mib[3] = {CTL_KERN, KERN_PROCARGS2, p->pid};
82 static char *args = NULL;
83 static size_t size = 0;
84
85 size_t new_size;
86 if (sysctl(mib, 3, NULL, &new_size, NULL, 0) == -1) {
87 return false;
88 }
89
90 if (new_size > size) {
91 if (args)
92 freez(args);
93
94 args = (char *)mallocz(new_size);
95 size = new_size;
96 }
97
98 memset(cmdline, 0, new_size < maxBytes ? new_size : maxBytes);
99
100 size_t used_size = size;
101 if (sysctl(mib, 3, args, &used_size, NULL, 0) == -1)
102 return false;
103
104 int argc;
105 memcpy(&argc, args, sizeof(argc));
106 char *ptr = args + sizeof(argc);
107 used_size -= sizeof(argc);
108
109 // Skip the executable path
110 while (*ptr && used_size > 0) {
111 ptr++;
112 used_size--;
113 }
114
115 // Copy only the arguments to the cmdline buffer, skipping the environment variables
116 size_t i = 0, copied_args = 0;
117 bool inArg = false;
118 for (; used_size > 0 && i < maxBytes - 1 && copied_args < argc; --used_size, ++ptr) {
119 if (*ptr == '\0') {
120 if (inArg) {
121 cmdline[i++] = ' '; // Replace nulls between arguments with spaces
122 inArg = false;
123 copied_args++;
124 }
125 } else {
126 cmdline[i++] = *ptr;
127 inArg = true;
128 }
129 }
130
131 if (i > 0 && cmdline[i - 1] == ' ')
132 i--; // Remove the trailing space if present
133
134 cmdline[i] = '\0'; // Null-terminate the string
135
136 return true;
137 }
138
139 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
140 bool apps_os_read_pid_io_macos(struct pid_stat *p, void *ptr) {
141 struct pid_info *pi = ptr;
142
143 // On MacOS, the proc_pid_rusage provides disk_io_statistics which includes io bytes read and written
144 // but does not provide the same level of detail as Linux, like separating logical and physical I/O bytes.
145 pid_incremental_rate(io, PDF_LREAD, pi->rusageinfo.ri_diskio_bytesread);
146 pid_incremental_rate(io, PDF_LWRITE, pi->rusageinfo.ri_diskio_byteswritten);
147
148 return true;
149 }
150 #else
151 bool apps_os_read_pid_io_macos(struct pid_stat *p __maybe_unused, void *ptr __maybe_unused) {
152 return false;
153 }
154 #endif
155
156 bool apps_os_read_pid_limits_macos(struct pid_stat *p __maybe_unused, void *ptr __maybe_unused) {
157 return false;
158 }
159
160 bool apps_os_read_pid_status_macos(struct pid_stat *p, void *ptr) {
161 struct pid_info *pi = ptr;
162
163 p->uid = pi->bsdinfo.pbi_uid;
164 p->gid = pi->bsdinfo.pbi_gid;
165 p->values[PDF_VMSIZE] = pi->taskinfo.pti_virtual_size;
166 p->values[PDF_VMRSS] = pi->taskinfo.pti_resident_size;
167 // p->values[PDF_VMSWAP] = rusageinfo.ri_swapins + rusageinfo.ri_swapouts; // This is not directly available, consider an alternative representation
168 p->values[PDF_VOLCTX] = pi->taskinfo.pti_csw;
169 // p->values[PDF_NVOLCTX] = taskinfo.pti_nivcsw;
170
171 return true;
172 }
173
174 static inline void get_current_time(void) {
175 struct timeval current_time;
176 gettimeofday(&current_time, NULL);
177 system_current_time_ut = timeval_usec(&current_time);
178 }
179
180 // bool apps_os_read_global_cpu_utilization_macos(void) {
181 // static kernel_uint_t utime_raw = 0, stime_raw = 0, ntime_raw = 0;
182 // static usec_t collected_usec = 0, last_collected_usec = 0;
183 //
184 // host_cpu_load_info_data_t cpuinfo;
185 // mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
186 //
187 // if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) != KERN_SUCCESS) {
188 // // Handle error
189 // goto cleanup;
190 // }
191 //
192 // last_collected_usec = collected_usec;
193 // collected_usec = now_monotonic_usec();
194 //
195 // calls_counter++;
196 //
197 // // Convert ticks to time
198 // // Note: MacOS does not separate nice time from user time in the CPU stats, so you might need to adjust this logic
199 // kernel_uint_t global_ntime = 0; // Assuming you want to keep track of nice time separately
200 //
201 // incremental_rate(global_utime, utime_raw, cpuinfo.cpu_ticks[CPU_STATE_USER] + cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
202 // incremental_rate(global_ntime, ntime_raw, cpuinfo.cpu_ticks[CPU_STATE_NICE], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
203 // incremental_rate(global_stime, stime_raw, cpuinfo.cpu_ticks[CPU_STATE_SYSTEM], collected_usec, last_collected_usec, CPU_TO_NANOSECONDCORES);
204 //
205 // global_utime += global_ntime;
206 //
207 // if(unlikely(global_iterations_counter == 1)) {
208 // global_utime = 0;
209 // global_stime = 0;
210 // global_gtime = 0;
211 // }
212 //
213 // return 1;
214 //
215 // cleanup:
216 // global_utime = 0;
217 // global_stime = 0;
218 // global_gtime = 0;
219 // return 0;
220 // }
221
222 bool apps_os_read_pid_stat_macos(struct pid_stat *p, void *ptr) {
223 struct pid_info *pi = ptr;
224
225 p->ppid = pi->proc.kp_eproc.e_ppid;
226
227 // Update command name and target if changed
228 char comm[PROC_PIDPATHINFO_MAXSIZE];
229 int ret = proc_name(p->pid, comm, sizeof(comm));
230 if (ret <= 0)
231 strncpyz(comm, "unknown", sizeof(comm) - 1);
232
233 update_pid_comm(p, comm);
234
235 kernel_uint_t userCPU = (pi->taskinfo.pti_total_user * mach_info.numer) / mach_info.denom;
236 kernel_uint_t systemCPU = (pi->taskinfo.pti_total_system * mach_info.numer) / mach_info.denom;
237
238 // Map the values from taskinfo to the pid_stat structure
239 pid_incremental_rate(stat, PDF_MINFLT, pi->taskinfo.pti_faults);
240 pid_incremental_rate(stat, PDF_MAJFLT, pi->taskinfo.pti_pageins);
241 pid_incremental_cpu(stat, PDF_UTIME, userCPU);
242 pid_incremental_cpu(stat, PDF_STIME, systemCPU);
243 p->values[PDF_THREADS] = pi->taskinfo.pti_threadnum;
244
245 usec_t started_ut = timeval_usec(&pi->proc.kp_proc.p_starttime);
246 p->values[PDF_UPTIME] = (system_current_time_ut > started_ut) ? (system_current_time_ut - started_ut) / USEC_PER_SEC : 0;
247
248 // Note: Some values such as guest time, cutime, cstime, etc., are not directly available in MacOS.
249 // You might need to approximate or leave them unset depending on your needs.
250
251 if(unlikely(debug_enabled)) {
252 debug_log_int("READ PROC/PID/STAT for MacOS: process: '%s' on target '%s' VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", threads=%d",
253 pid_stat_comm(p), (p->target) ? string2str(p->target->name) : "UNSET",
254 p->values[PDF_UTIME],
255 p->values[PDF_STIME],
256 p->values[PDF_MINFLT],
257 p->values[PDF_MAJFLT],
258 p->values[PDF_THREADS]);
259 }
260
261 // MacOS doesn't have a direct concept of process state like Linux,
262 // so updating process state count might need a different approach.
263
264 return true;
265 }
266
267 bool apps_os_collect_all_pids_macos(void) {
268 // Mark all processes as unread before collecting new data
269 struct pid_stat *p;
270 static pid_t *pids = NULL;
271 static int allocatedProcessCount = 0;
272
273 // Get the number of processes
274 int numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
275 if (numberOfProcesses <= 0) {
276 netdata_log_error("Failed to retrieve the process count");
277 return false;
278 }
279
280 // Allocate or reallocate space to hold all the process IDs if necessary
281 if (numberOfProcesses > allocatedProcessCount) {
282 // Allocate additional space to avoid frequent reallocations
283 allocatedProcessCount = numberOfProcesses + 100;
284 pids = reallocz(pids, allocatedProcessCount * sizeof(pid_t));
285 }
286
287 // this is required, otherwise the PIDs become totally random
288 memset(pids, 0, allocatedProcessCount * sizeof(pid_t));
289
290 // get the list of PIDs
291 numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, pids, allocatedProcessCount * sizeof(pid_t));
292 if (numberOfProcesses <= 0) {
293 netdata_log_error("Failed to retrieve the process IDs");
294 return false;
295 }
296
297 get_current_time();
298
299 // Collect data for each process
300 for (int i = 0; i < numberOfProcesses; ++i) {
301 pid_t pid = pids[i];
302 if (pid <= 0) continue;
303
304 struct pid_info pi = { 0 };
305
306 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
307
308 size_t procSize = sizeof(pi.proc);
309 if(sysctl(mib, 4, &pi.proc, &procSize, NULL, 0) == -1) {
310 netdata_log_error("Failed to get proc for PID %d", pid);
311 continue;
312 }
313 if(procSize == 0) // no such process
314 continue;
315
316 int st = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &pi.taskinfo, sizeof(pi.taskinfo));
317 if (st <= 0) {
318 netdata_log_error("Failed to get task info for PID %d", pid);
319 continue;
320 }
321
322 st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &pi.bsdinfo, sizeof(pi.bsdinfo));
323 if (st <= 0) {
324 netdata_log_error("Failed to get BSD info for PID %d", pid);
325 continue;
326 }
327
328 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 110000
329 st = proc_pid_rusage(pid, RUSAGE_INFO_V4, (rusage_info_t *)&pi.rusageinfo);
330 if (st < 0) {
331 netdata_log_error("Failed to get resource usage info for PID %d", pid);
332 continue;
333 }
334 #endif
335
336 incrementally_collect_data_for_pid(pid, &pi);
337 }
338
339 return true;
340 }
341
342 #endif