File: | obj-scan-build/procfs/../../procfs/rootdir.c |
Location: | line 312, column 7 |
Description: | The left operand of '/' is a garbage value |
1 | /* Hurd /proc filesystem, permanent files of the root directory. | |||
2 | Copyright (C) 2010,13 Free Software Foundation, Inc. | |||
3 | ||||
4 | This file is part of the GNU Hurd. | |||
5 | ||||
6 | The GNU Hurd is free software; you can redistribute it and/or | |||
7 | modify it under the terms of the GNU General Public License as | |||
8 | published by the Free Software Foundation; either version 2, or (at | |||
9 | your option) any later version. | |||
10 | ||||
11 | The GNU Hurd is distributed in the hope that it will be useful, but | |||
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
14 | General Public License for more details. | |||
15 | ||||
16 | You should have received a copy of the GNU General Public License | |||
17 | along with this program; if not, write to the Free Software | |||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |||
19 | ||||
20 | #include <mach/gnumach.h> | |||
21 | #include <mach/vm_param.h> | |||
22 | #include <mach/vm_statistics.h> | |||
23 | #include <mach/vm_cache_statistics.h> | |||
24 | #include <mach/default_pager.h> | |||
25 | #include <mach_debug/mach_debug_types.h> | |||
26 | #include <hurd/paths.h> | |||
27 | #include <stdio.h> | |||
28 | #include <unistd.h> | |||
29 | #include <fcntl.h> | |||
30 | #include <sys/time.h> | |||
31 | #include <sys/utsname.h> | |||
32 | #include <sys/stat.h> | |||
33 | #include <argz.h> | |||
34 | #include <ps.h> | |||
35 | #include "procfs.h" | |||
36 | #include "procfs_dir.h" | |||
37 | #include "main.h" | |||
38 | ||||
39 | #include "mach_debug_U.h" | |||
40 | ||||
41 | /* This implements a directory node with the static files in /proc. | |||
42 | NB: the libps functions for host information return static storage; | |||
43 | using them would require locking and as a consequence it would be | |||
44 | more complicated, not simpler. */ | |||
45 | ||||
46 | ||||
47 | /* Helper functions */ | |||
48 | ||||
49 | /* We get the boot time by using that of the kernel process. */ | |||
50 | static error_t | |||
51 | get_boottime (struct ps_context *pc, struct timeval *tv) | |||
52 | { | |||
53 | struct proc_stat *ps; | |||
54 | error_t err; | |||
55 | ||||
56 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
57 | if (err) | |||
58 | return err; | |||
59 | ||||
60 | err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC0x00040); | |||
61 | if (err || !(proc_stat_flags (ps)((ps)->flags) & PSTAT_TASK_BASIC0x00040)) | |||
62 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
63 | ||||
64 | if (! err) | |||
65 | { | |||
66 | task_basic_info_t tbi = proc_stat_task_basic_info (ps)((ps)->task_basic_info); | |||
67 | tv->tv_sec = tbi->creation_time.seconds; | |||
68 | tv->tv_usec = tbi->creation_time.microseconds; | |||
69 | } | |||
70 | ||||
71 | _proc_stat_free (ps); | |||
72 | return err; | |||
73 | } | |||
74 | ||||
75 | /* We get the idle time by querying the kernel's idle thread. */ | |||
76 | static error_t | |||
77 | get_idletime (struct ps_context *pc, struct timeval *tv) | |||
78 | { | |||
79 | struct proc_stat *ps, *pst; | |||
80 | thread_basic_info_t tbi; | |||
81 | error_t err; | |||
82 | int i; | |||
83 | ||||
84 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
85 | if (err) | |||
86 | return err; | |||
87 | ||||
88 | pst = NULL((void*)0), tbi = NULL((void*)0); | |||
89 | ||||
90 | err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS0x00100); | |||
91 | if (err || !(proc_stat_flags (ps)((ps)->flags) & PSTAT_NUM_THREADS0x00100)) | |||
92 | { | |||
93 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
94 | goto out; | |||
95 | } | |||
96 | ||||
97 | /* Look for the idle thread */ | |||
98 | for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE0x2); i++) | |||
99 | { | |||
100 | if (pst) | |||
101 | _proc_stat_free (pst); | |||
102 | ||||
103 | pst = NULL((void*)0), tbi = NULL((void*)0); | |||
104 | if (i >= proc_stat_num_threads (ps)((ps)->num_threads)) | |||
105 | { | |||
106 | err = ESRCH((0x10 << 26) | ((3) & 0x3fff)); | |||
107 | goto out; | |||
108 | } | |||
109 | ||||
110 | err = proc_stat_thread_create (ps, i, &pst); | |||
111 | if (err) | |||
112 | continue; | |||
113 | ||||
114 | err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC0x00200); | |||
115 | if (err || ! (proc_stat_flags (pst)((pst)->flags) & PSTAT_THREAD_BASIC0x00200)) | |||
116 | continue; | |||
117 | ||||
118 | tbi = proc_stat_thread_basic_info (pst)((pst)->thread_basic_info); | |||
119 | } | |||
120 | ||||
121 | /* We found it! */ | |||
122 | tv->tv_sec = tbi->system_time.seconds; | |||
123 | tv->tv_usec = tbi->system_time.microseconds; | |||
124 | err = 0; | |||
125 | ||||
126 | out: | |||
127 | if (pst) _proc_stat_free (pst); | |||
128 | _proc_stat_free (ps); | |||
129 | return err; | |||
130 | } | |||
131 | ||||
132 | static error_t | |||
133 | get_swapinfo (default_pager_info_t *info) | |||
134 | { | |||
135 | mach_port_t defpager; | |||
136 | error_t err; | |||
137 | ||||
138 | defpager = file_name_lookup (_SERVERS_DEFPAGER"/servers/" "default-pager", O_READ0x0001, 0); | |||
139 | if (defpager == MACH_PORT_NULL((mach_port_t) 0)) | |||
140 | return errno(*__errno_location ()); | |||
141 | ||||
142 | err = default_pager_info (defpager, info); | |||
143 | mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), defpager); | |||
144 | ||||
145 | return err; | |||
146 | } | |||
147 | ||||
148 | ||||
149 | /* Content generators */ | |||
150 | ||||
151 | static error_t | |||
152 | rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) | |||
153 | { | |||
154 | struct utsname uts; | |||
155 | int r; | |||
156 | ||||
157 | r = uname (&uts); | |||
158 | if (r < 0) | |||
159 | return errno(*__errno_location ()); | |||
160 | ||||
161 | *contents_len = asprintf (contents, | |||
162 | "Linux version 2.6.1 (%s %s %s %s)\n", | |||
163 | uts.sysname, uts.release, uts.version, uts.machine); | |||
164 | ||||
165 | return 0; | |||
166 | } | |||
167 | ||||
168 | static error_t | |||
169 | rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) | |||
170 | { | |||
171 | struct timeval time, boottime, idletime; | |||
172 | double up_secs, idle_secs; | |||
173 | error_t err; | |||
174 | ||||
175 | err = gettimeofday (&time, NULL((void*)0)); | |||
176 | if (err < 0) | |||
177 | return errno(*__errno_location ()); | |||
178 | ||||
179 | err = get_boottime (hook, &boottime); | |||
180 | if (err) | |||
181 | return err; | |||
182 | ||||
183 | err = get_idletime (hook, &idletime); | |||
184 | if (err) | |||
185 | return err; | |||
186 | ||||
187 | timersub (&time, &boottime, &time)do { (&time)->tv_sec = (&time)->tv_sec - (& boottime)->tv_sec; (&time)->tv_usec = (&time)-> tv_usec - (&boottime)->tv_usec; if ((&time)->tv_usec < 0) { --(&time)->tv_sec; (&time)->tv_usec += 1000000; } } while (0); | |||
188 | up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; | |||
189 | idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; | |||
190 | ||||
191 | /* The second field is the total idle time. As far as I know we don't | |||
192 | keep track of it. However, procps uses it to compute "USER_HZ", and | |||
193 | proc(5) specifies that it should be equal to USER_HZ times the idle value | |||
194 | in ticks from /proc/stat. So we assume a completely idle system both here | |||
195 | and there to make that work. */ | |||
196 | *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); | |||
197 | ||||
198 | return 0; | |||
199 | } | |||
200 | ||||
201 | static error_t | |||
202 | rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) | |||
203 | { | |||
204 | struct timeval boottime, time, idletime; | |||
205 | struct vm_statistics vmstats; | |||
206 | unsigned long up_ticks, idle_ticks; | |||
207 | error_t err; | |||
208 | ||||
209 | err = gettimeofday (&time, NULL((void*)0)); | |||
210 | if (err < 0) | |||
211 | return errno(*__errno_location ()); | |||
212 | ||||
213 | err = get_boottime (hook, &boottime); | |||
214 | if (err) | |||
215 | return err; | |||
216 | ||||
217 | err = get_idletime (hook, &idletime); | |||
218 | if (err) | |||
219 | return err; | |||
220 | ||||
221 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
222 | if (err) | |||
223 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
224 | ||||
225 | timersub (&time, &boottime, &time)do { (&time)->tv_sec = (&time)->tv_sec - (& boottime)->tv_sec; (&time)->tv_usec = (&time)-> tv_usec - (&boottime)->tv_usec; if ((&time)->tv_usec < 0) { --(&time)->tv_sec; (&time)->tv_usec += 1000000; } } while (0); | |||
226 | up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; | |||
227 | idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; | |||
228 | ||||
229 | *contents_len = asprintf (contents, | |||
230 | "cpu %lu 0 0 %lu 0 0 0 0 0\n" | |||
231 | "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" | |||
232 | "intr 0\n" | |||
233 | "page %d %d\n" | |||
234 | "btime %lu\n", | |||
235 | up_ticks - idle_ticks, idle_ticks, | |||
236 | up_ticks - idle_ticks, idle_ticks, | |||
237 | vmstats.pageins, vmstats.pageouts, | |||
238 | boottime.tv_sec); | |||
239 | ||||
240 | return 0; | |||
241 | } | |||
242 | ||||
243 | static error_t | |||
244 | rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) | |||
245 | { | |||
246 | host_load_info_data_t hli; | |||
247 | mach_msg_type_number_t cnt; | |||
248 | error_t err; | |||
249 | ||||
250 | cnt = HOST_LOAD_INFO_COUNT(sizeof(host_load_info_data_t)/sizeof(integer_t)); | |||
251 | err = host_info (mach_host_self (), HOST_LOAD_INFO4, (host_info_t) &hli, &cnt); | |||
252 | if (err) | |||
253 | return err; | |||
254 | ||||
255 | assert (cnt == HOST_LOAD_INFO_COUNT)((cnt == (sizeof(host_load_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_load_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 255, __PRETTY_FUNCTION__)); | |||
256 | *contents_len = asprintf (contents, | |||
257 | "%.2f %.2f %.2f 1/0 0\n", | |||
258 | hli.avenrun[0] / (double) LOAD_SCALE1000, | |||
259 | hli.avenrun[1] / (double) LOAD_SCALE1000, | |||
260 | hli.avenrun[2] / (double) LOAD_SCALE1000); | |||
261 | ||||
262 | return 0; | |||
263 | } | |||
264 | ||||
265 | static error_t | |||
266 | rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) | |||
267 | { | |||
268 | host_basic_info_data_t hbi; | |||
269 | mach_msg_type_number_t cnt; | |||
270 | struct vm_statistics vmstats; | |||
271 | struct vm_cache_statistics cache_stats; | |||
272 | default_pager_info_t swap; | |||
273 | error_t err; | |||
274 | ||||
275 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
276 | if (err) | |||
| ||||
277 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
278 | ||||
279 | err = vm_cache_statistics (mach_task_self ()((__mach_task_self_ + 0)), &cache_stats); | |||
280 | if (err) | |||
281 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
282 | ||||
283 | cnt = HOST_BASIC_INFO_COUNT(sizeof(host_basic_info_data_t)/sizeof(integer_t)); | |||
284 | err = host_info (mach_host_self (), HOST_BASIC_INFO1, (host_info_t) &hbi, &cnt); | |||
285 | if (err) | |||
286 | return err; | |||
287 | ||||
288 | err = get_swapinfo (&swap); | |||
289 | if (err) | |||
290 | return err; | |||
291 | ||||
292 | assert (cnt == HOST_BASIC_INFO_COUNT)((cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 292, __PRETTY_FUNCTION__)); | |||
293 | *contents_len = asprintf (contents, | |||
294 | "MemTotal: %14lu kB\n" | |||
295 | "MemFree: %14lu kB\n" | |||
296 | "Buffers: %14lu kB\n" | |||
297 | "Cached: %14lu kB\n" | |||
298 | "Active: %14lu kB\n" | |||
299 | "Inactive: %14lu kB\n" | |||
300 | "Mlocked: %14lu kB\n" | |||
301 | "SwapTotal:%14lu kB\n" | |||
302 | "SwapFree: %14lu kB\n" | |||
303 | , | |||
304 | (long unsigned) hbi.memory_size / 1024, | |||
305 | (long unsigned) vmstats.free_count * PAGE_SIZE(1 << 12) / 1024, | |||
306 | 0UL, | |||
307 | (long unsigned) cache_stats.cache_count * PAGE_SIZE(1 << 12) / 1024, | |||
308 | (long unsigned) vmstats.active_count * PAGE_SIZE(1 << 12) / 1024, | |||
309 | (long unsigned) vmstats.inactive_count * PAGE_SIZE(1 << 12) / 1024, | |||
310 | (long unsigned) vmstats.wire_count * PAGE_SIZE(1 << 12) / 1024, | |||
311 | (long unsigned) swap.dpi_total_space / 1024, | |||
312 | (long unsigned) swap.dpi_free_space / 1024); | |||
| ||||
313 | ||||
314 | return 0; | |||
315 | } | |||
316 | ||||
317 | static error_t | |||
318 | rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) | |||
319 | { | |||
320 | host_basic_info_data_t hbi; | |||
321 | mach_msg_type_number_t cnt; | |||
322 | struct vm_statistics vmstats; | |||
323 | error_t err; | |||
324 | ||||
325 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
326 | if (err) | |||
327 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
328 | ||||
329 | cnt = HOST_BASIC_INFO_COUNT(sizeof(host_basic_info_data_t)/sizeof(integer_t)); | |||
330 | err = host_info (mach_host_self (), HOST_BASIC_INFO1, (host_info_t) &hbi, &cnt); | |||
331 | if (err) | |||
332 | return err; | |||
333 | ||||
334 | assert (cnt == HOST_BASIC_INFO_COUNT)((cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 334, __PRETTY_FUNCTION__)); | |||
335 | *contents_len = asprintf (contents, | |||
336 | "nr_free_pages %lu\n" | |||
337 | "nr_inactive_anon %lu\n" | |||
338 | "nr_active_anon %lu\n" | |||
339 | "nr_inactive_file %lu\n" | |||
340 | "nr_active_file %lu\n" | |||
341 | "nr_unevictable %lu\n" | |||
342 | "nr_mlock %lu\n" | |||
343 | "pgpgin %lu\n" | |||
344 | "pgpgout %lu\n" | |||
345 | "pgfault %lu\n", | |||
346 | (long unsigned) vmstats.free_count, | |||
347 | /* FIXME: how can we distinguish the anon/file pages? Maybe we can | |||
348 | ask the default pager how many it manages? */ | |||
349 | (long unsigned) vmstats.inactive_count, | |||
350 | (long unsigned) vmstats.active_count, | |||
351 | (long unsigned) 0, | |||
352 | (long unsigned) 0, | |||
353 | (long unsigned) vmstats.wire_count, | |||
354 | (long unsigned) vmstats.wire_count, | |||
355 | (long unsigned) vmstats.pageins, | |||
356 | (long unsigned) vmstats.pageouts, | |||
357 | (long unsigned) vmstats.faults); | |||
358 | ||||
359 | return 0; | |||
360 | } | |||
361 | ||||
362 | static error_t | |||
363 | rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) | |||
364 | { | |||
365 | struct ps_context *pc = hook; | |||
366 | struct proc_stat *ps; | |||
367 | error_t err; | |||
368 | ||||
369 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
370 | if (err) | |||
371 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
372 | ||||
373 | err = proc_stat_set_flags (ps, PSTAT_ARGS0x02000); | |||
374 | if (err || ! (proc_stat_flags (ps)((ps)->flags) & PSTAT_ARGS0x02000)) | |||
375 | { | |||
376 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
377 | goto out; | |||
378 | } | |||
379 | ||||
380 | *contents_len = proc_stat_args_len (ps)((ps)->args_len); | |||
381 | *contents = malloc (*contents_len); | |||
382 | if (! *contents) | |||
383 | { | |||
384 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
385 | goto out; | |||
386 | } | |||
387 | ||||
388 | memcpy (*contents, proc_stat_args (ps)((ps)->args), *contents_len); | |||
389 | argz_stringify (*contents, *contents_len, ' '); | |||
390 | (*contents)[*contents_len - 1] = '\n'; | |||
391 | ||||
392 | out: | |||
393 | _proc_stat_free (ps); | |||
394 | return err; | |||
395 | } | |||
396 | ||||
397 | static int | |||
398 | rootdir_fakeself_exists (void *dir_hook, const void *entry_hook) | |||
399 | { | |||
400 | return opt_fake_self >= 0; | |||
401 | } | |||
402 | ||||
403 | static error_t | |||
404 | rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) | |||
405 | { | |||
406 | *contents_len = asprintf (contents, "%d", opt_fake_self); | |||
407 | return 0; | |||
408 | } | |||
409 | ||||
410 | /* The mtab translator to use by default for the "mounts" node. */ | |||
411 | #define MTAB_TRANSLATOR"/hurd/mtab" "/hurd/mtab" | |||
412 | ||||
413 | static struct node *rootdir_mounts_node; | |||
414 | static pthread_spinlock_t rootdir_mounts_node_lock = | |||
415 | PTHREAD_SPINLOCK_INITIALIZER((__pthread_spinlock_t) 0); | |||
416 | ||||
417 | static struct node * | |||
418 | rootdir_mounts_make_node (void *dir_hook, const void *entry_hook) | |||
419 | { | |||
420 | struct node *np, *prev; | |||
421 | ||||
422 | pthread_spin_lock (&rootdir_mounts_node_lock); | |||
423 | np = rootdir_mounts_node; | |||
424 | pthread_spin_unlock (&rootdir_mounts_node_lock); | |||
425 | ||||
426 | if (np != NULL((void*)0)) | |||
427 | { | |||
428 | netfs_nref (np); | |||
429 | return np; | |||
430 | } | |||
431 | ||||
432 | np = procfs_make_node (entry_hook, dir_hook); | |||
433 | if (np == NULL((void*)0)) | |||
434 | return NULL((void*)0); | |||
435 | ||||
436 | procfs_node_chtype (np, S_IFREG0100000 | S_IPTRANS000010000000); | |||
437 | procfs_node_chmod (np, 0444); | |||
438 | ||||
439 | pthread_spin_lock (&rootdir_mounts_node_lock); | |||
440 | prev = rootdir_mounts_node; | |||
441 | if (rootdir_mounts_node == NULL((void*)0)) | |||
442 | rootdir_mounts_node = np; | |||
443 | pthread_spin_unlock (&rootdir_mounts_node_lock); | |||
444 | ||||
445 | if (prev != NULL((void*)0)) | |||
446 | { | |||
447 | procfs_cleanup (np); | |||
448 | np = prev; | |||
449 | } | |||
450 | ||||
451 | return np; | |||
452 | } | |||
453 | ||||
454 | static error_t | |||
455 | rootdir_mounts_get_translator (void *hook, char **argz, size_t *argz_len) | |||
456 | { | |||
457 | static const char const mtab_argz[] = MTAB_TRANSLATOR"/hurd/mtab" "\0/"; | |||
458 | ||||
459 | *argz = malloc (sizeof mtab_argz); | |||
460 | if (! *argz) | |||
461 | return ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
462 | ||||
463 | memcpy (*argz, mtab_argz, sizeof mtab_argz); | |||
464 | *argz_len = sizeof mtab_argz; | |||
465 | return 0; | |||
466 | } | |||
467 | ||||
468 | static int | |||
469 | rootdir_mounts_exists (void *dir_hook, const void *entry_hook) | |||
470 | { | |||
471 | static int translator_exists = -1; | |||
472 | if (translator_exists == -1) | |||
473 | translator_exists = access (MTAB_TRANSLATOR"/hurd/mtab", F_OK0|X_OK1) == 0; | |||
474 | return translator_exists; | |||
475 | } | |||
476 | ||||
477 | static error_t | |||
478 | rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) | |||
479 | { | |||
480 | error_t err; | |||
481 | FILE *m; | |||
482 | const char header[] = | |||
483 | "cache obj slab bufs objs bufs" | |||
484 | " total reclaimable\n" | |||
485 | "name flags size size /slab usage count" | |||
486 | " memory memory\n"; | |||
487 | cache_info_array_t cache_info; | |||
488 | size_t mem_usage, mem_reclaimable, mem_total, mem_total_reclaimable; | |||
489 | mach_msg_type_number_t cache_info_count; | |||
490 | int i; | |||
491 | ||||
492 | cache_info = NULL((void*)0); | |||
493 | cache_info_count = 0; | |||
494 | ||||
495 | err = host_slab_info (mach_host_self(), &cache_info, &cache_info_count); | |||
496 | if (err) | |||
497 | return err; | |||
498 | ||||
499 | m = open_memstream (contents, contents_len); | |||
500 | if (m == NULL((void*)0)) | |||
501 | { | |||
502 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
503 | goto out; | |||
504 | } | |||
505 | ||||
506 | fprintf (m, "%s", header); | |||
507 | ||||
508 | mem_total = 0; | |||
509 | mem_total_reclaimable = 0; | |||
510 | ||||
511 | for (i = 0; i < cache_info_count; i++) | |||
512 | { | |||
513 | mem_usage = (cache_info[i].nr_slabs * cache_info[i].slab_size) | |||
514 | >> 10; | |||
515 | mem_total += mem_usage; | |||
516 | mem_reclaimable = (cache_info[i].flags & CACHE_FLAGS_NO_RECLAIM0x04) | |||
517 | ? 0 : (cache_info[i].nr_free_slabs | |||
518 | * cache_info[i].slab_size) >> 10; | |||
519 | mem_total_reclaimable += mem_reclaimable; | |||
520 | fprintf (m, | |||
521 | "%-21s %04x %7zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", | |||
522 | cache_info[i].name, cache_info[i].flags, | |||
523 | cache_info[i].obj_size, cache_info[i].slab_size >> 10, | |||
524 | cache_info[i].bufs_per_slab, cache_info[i].nr_objs, | |||
525 | cache_info[i].nr_bufs, mem_usage, mem_reclaimable); | |||
526 | } | |||
527 | ||||
528 | fprintf (m, "total: %zuk, reclaimable: %zuk\n", | |||
529 | mem_total, mem_total_reclaimable); | |||
530 | ||||
531 | fclose (m); | |||
532 | ||||
533 | out: | |||
534 | vm_deallocate (mach_task_self ()((__mach_task_self_ + 0)), | |||
535 | cache_info, cache_info_count * sizeof *cache_info); | |||
536 | return err; | |||
537 | } | |||
538 | ||||
539 | /* Glue logic and entries table */ | |||
540 | ||||
541 | static struct node * | |||
542 | rootdir_file_make_node (void *dir_hook, const void *entry_hook) | |||
543 | { | |||
544 | /* The entry hook we use is actually a procfs_node_ops for the file to be | |||
545 | created. The hook associated to these newly created files (and passed | |||
546 | to the generators above as a consequence) is always the same global | |||
547 | ps_context, which we get from rootdir_make_node as the directory hook. */ | |||
548 | return procfs_make_node (entry_hook, dir_hook); | |||
549 | } | |||
550 | ||||
551 | static struct node * | |||
552 | rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) | |||
553 | { | |||
554 | struct node *np = procfs_make_node (entry_hook, dir_hook); | |||
555 | if (np) | |||
556 | procfs_node_chtype (np, S_IFLNK0120000); | |||
557 | return np; | |||
558 | } | |||
559 | ||||
560 | static const struct procfs_dir_entry rootdir_entries[] = { | |||
561 | { | |||
562 | .name = "self", | |||
563 | .hook = & (struct procfs_node_ops) { | |||
564 | .get_contents = rootdir_gc_fakeself, | |||
565 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
566 | }, | |||
567 | .ops = { | |||
568 | .make_node = rootdir_symlink_make_node, | |||
569 | .exists = rootdir_fakeself_exists, | |||
570 | } | |||
571 | }, | |||
572 | { | |||
573 | .name = "version", | |||
574 | .hook = & (struct procfs_node_ops) { | |||
575 | .get_contents = rootdir_gc_version, | |||
576 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
577 | }, | |||
578 | }, | |||
579 | { | |||
580 | .name = "uptime", | |||
581 | .hook = & (struct procfs_node_ops) { | |||
582 | .get_contents = rootdir_gc_uptime, | |||
583 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
584 | }, | |||
585 | }, | |||
586 | { | |||
587 | .name = "stat", | |||
588 | .hook = & (struct procfs_node_ops) { | |||
589 | .get_contents = rootdir_gc_stat, | |||
590 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
591 | }, | |||
592 | }, | |||
593 | { | |||
594 | .name = "loadavg", | |||
595 | .hook = & (struct procfs_node_ops) { | |||
596 | .get_contents = rootdir_gc_loadavg, | |||
597 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
598 | }, | |||
599 | }, | |||
600 | { | |||
601 | .name = "meminfo", | |||
602 | .hook = & (struct procfs_node_ops) { | |||
603 | .get_contents = rootdir_gc_meminfo, | |||
604 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
605 | }, | |||
606 | }, | |||
607 | { | |||
608 | .name = "vmstat", | |||
609 | .hook = & (struct procfs_node_ops) { | |||
610 | .get_contents = rootdir_gc_vmstat, | |||
611 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
612 | }, | |||
613 | }, | |||
614 | { | |||
615 | .name = "cmdline", | |||
616 | .hook = & (struct procfs_node_ops) { | |||
617 | .get_contents = rootdir_gc_cmdline, | |||
618 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
619 | }, | |||
620 | }, | |||
621 | { | |||
622 | .name = "mounts", | |||
623 | .hook = & (struct procfs_node_ops) { | |||
624 | .get_translator = rootdir_mounts_get_translator, | |||
625 | }, | |||
626 | .ops = { | |||
627 | .make_node = rootdir_mounts_make_node, | |||
628 | .exists = rootdir_mounts_exists, | |||
629 | } | |||
630 | }, | |||
631 | { | |||
632 | .name = "slabinfo", | |||
633 | .hook = & (struct procfs_node_ops) { | |||
634 | .get_contents = rootdir_gc_slabinfo, | |||
635 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
636 | }, | |||
637 | }, | |||
638 | #ifdef PROFILE | |||
639 | /* In order to get a usable gmon.out file, we must apparently use exit(). */ | |||
640 | { | |||
641 | .name = "exit", | |||
642 | .ops = { | |||
643 | .make_node = exit, | |||
644 | }, | |||
645 | }, | |||
646 | #endif | |||
647 | {} | |||
648 | }; | |||
649 | ||||
650 | struct node | |||
651 | *rootdir_make_node (struct ps_context *pc) | |||
652 | { | |||
653 | static const struct procfs_dir_ops ops = { | |||
654 | .entries = rootdir_entries, | |||
655 | .entry_ops = { | |||
656 | .make_node = rootdir_file_make_node, | |||
657 | }, | |||
658 | }; | |||
659 | return procfs_dir_make_node (&ops, pc); | |||
660 | } | |||
661 |