Commit | Line | Data |
---|---|---|
d53dee3f AN |
1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2020 Facebook */ | |
3 | #include <errno.h> | |
4 | #include <stdbool.h> | |
5 | #include <stdio.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <unistd.h> | |
d6699f8e | 9 | |
d53dee3f | 10 | #include <bpf/bpf.h> |
d6699f8e | 11 | #include <bpf/hashmap.h> |
d53dee3f AN |
12 | |
13 | #include "main.h" | |
14 | #include "skeleton/pid_iter.h" | |
15 | ||
16 | #ifdef BPFTOOL_WITHOUT_SKELETONS | |
17 | ||
d6699f8e | 18 | int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type) |
d53dee3f | 19 | { |
d53dee3f AN |
20 | return -ENOTSUP; |
21 | } | |
d6699f8e QM |
22 | void delete_obj_refs_table(struct hashmap *map) {} |
23 | void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix) {} | |
24 | void emit_obj_refs_json(struct hashmap *map, __u32 id, json_writer_t *json_writer) {} | |
d53dee3f AN |
25 | |
26 | #else /* BPFTOOL_WITHOUT_SKELETONS */ | |
27 | ||
28 | #include "pid_iter.skel.h" | |
29 | ||
d6699f8e | 30 | static void add_ref(struct hashmap *map, struct pid_iter_entry *e) |
d53dee3f | 31 | { |
d6699f8e | 32 | struct hashmap_entry *entry; |
d53dee3f AN |
33 | struct obj_refs *refs; |
34 | struct obj_ref *ref; | |
d6699f8e | 35 | int err, i; |
d53dee3f | 36 | void *tmp; |
d53dee3f | 37 | |
d6699f8e QM |
38 | hashmap__for_each_key_entry(map, entry, u32_as_hash_field(e->id)) { |
39 | refs = entry->value; | |
d53dee3f AN |
40 | |
41 | for (i = 0; i < refs->ref_cnt; i++) { | |
42 | if (refs->refs[i].pid == e->pid) | |
43 | return; | |
44 | } | |
45 | ||
46 | tmp = realloc(refs->refs, (refs->ref_cnt + 1) * sizeof(*ref)); | |
47 | if (!tmp) { | |
48 | p_err("failed to re-alloc memory for ID %u, PID %d, COMM %s...", | |
49 | e->id, e->pid, e->comm); | |
50 | return; | |
51 | } | |
52 | refs->refs = tmp; | |
53 | ref = &refs->refs[refs->ref_cnt]; | |
54 | ref->pid = e->pid; | |
55 | memcpy(ref->comm, e->comm, sizeof(ref->comm)); | |
56 | refs->ref_cnt++; | |
57 | ||
58 | return; | |
59 | } | |
60 | ||
61 | /* new ref */ | |
62 | refs = calloc(1, sizeof(*refs)); | |
63 | if (!refs) { | |
64 | p_err("failed to alloc memory for ID %u, PID %d, COMM %s...", | |
65 | e->id, e->pid, e->comm); | |
66 | return; | |
67 | } | |
68 | ||
d53dee3f AN |
69 | refs->refs = malloc(sizeof(*refs->refs)); |
70 | if (!refs->refs) { | |
71 | free(refs); | |
72 | p_err("failed to alloc memory for ID %u, PID %d, COMM %s...", | |
73 | e->id, e->pid, e->comm); | |
74 | return; | |
75 | } | |
76 | ref = &refs->refs[0]; | |
77 | ref->pid = e->pid; | |
78 | memcpy(ref->comm, e->comm, sizeof(ref->comm)); | |
79 | refs->ref_cnt = 1; | |
d6699f8e QM |
80 | |
81 | err = hashmap__append(map, u32_as_hash_field(e->id), refs); | |
82 | if (err) | |
83 | p_err("failed to append entry to hashmap for ID %u: %s", | |
84 | e->id, strerror(errno)); | |
d53dee3f AN |
85 | } |
86 | ||
87 | static int __printf(2, 0) | |
88 | libbpf_print_none(__maybe_unused enum libbpf_print_level level, | |
89 | __maybe_unused const char *format, | |
90 | __maybe_unused va_list args) | |
91 | { | |
92 | return 0; | |
93 | } | |
94 | ||
d6699f8e | 95 | int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type) |
d53dee3f | 96 | { |
d53dee3f | 97 | struct pid_iter_entry *e; |
932c6055 AN |
98 | char buf[4096 / sizeof(*e) * sizeof(*e)]; |
99 | struct pid_iter_bpf *skel; | |
d53dee3f AN |
100 | int err, ret, fd = -1, i; |
101 | libbpf_print_fn_t default_print; | |
102 | ||
d6699f8e QM |
103 | *map = hashmap__new(hash_fn_for_key_as_id, equal_fn_for_key_as_id, NULL); |
104 | if (!*map) { | |
105 | p_err("failed to create hashmap for PID references"); | |
106 | return -1; | |
107 | } | |
d53dee3f AN |
108 | set_max_rlimit(); |
109 | ||
110 | skel = pid_iter_bpf__open(); | |
111 | if (!skel) { | |
112 | p_err("failed to open PID iterator skeleton"); | |
113 | return -1; | |
114 | } | |
115 | ||
116 | skel->rodata->obj_type = type; | |
117 | ||
118 | /* we don't want output polluted with libbpf errors if bpf_iter is not | |
119 | * supported | |
120 | */ | |
121 | default_print = libbpf_set_print(libbpf_print_none); | |
122 | err = pid_iter_bpf__load(skel); | |
123 | libbpf_set_print(default_print); | |
124 | if (err) { | |
125 | /* too bad, kernel doesn't support BPF iterators yet */ | |
126 | err = 0; | |
127 | goto out; | |
128 | } | |
129 | err = pid_iter_bpf__attach(skel); | |
130 | if (err) { | |
131 | /* if we loaded above successfully, attach has to succeed */ | |
132 | p_err("failed to attach PID iterator: %d", err); | |
133 | goto out; | |
134 | } | |
135 | ||
136 | fd = bpf_iter_create(bpf_link__fd(skel->links.iter)); | |
137 | if (fd < 0) { | |
138 | err = -errno; | |
139 | p_err("failed to create PID iterator session: %d", err); | |
140 | goto out; | |
141 | } | |
142 | ||
143 | while (true) { | |
144 | ret = read(fd, buf, sizeof(buf)); | |
145 | if (ret < 0) { | |
00fa1d83 YS |
146 | if (errno == EAGAIN) |
147 | continue; | |
d53dee3f AN |
148 | err = -errno; |
149 | p_err("failed to read PID iterator output: %d", err); | |
150 | goto out; | |
151 | } | |
152 | if (ret == 0) | |
153 | break; | |
154 | if (ret % sizeof(*e)) { | |
155 | err = -EINVAL; | |
156 | p_err("invalid PID iterator output format"); | |
157 | goto out; | |
158 | } | |
159 | ret /= sizeof(*e); | |
160 | ||
161 | e = (void *)buf; | |
162 | for (i = 0; i < ret; i++, e++) { | |
d6699f8e | 163 | add_ref(*map, e); |
d53dee3f AN |
164 | } |
165 | } | |
166 | err = 0; | |
167 | out: | |
168 | if (fd >= 0) | |
169 | close(fd); | |
170 | pid_iter_bpf__destroy(skel); | |
171 | return err; | |
172 | } | |
173 | ||
d6699f8e | 174 | void delete_obj_refs_table(struct hashmap *map) |
d53dee3f | 175 | { |
d6699f8e QM |
176 | struct hashmap_entry *entry; |
177 | size_t bkt; | |
178 | ||
179 | if (!map) | |
180 | return; | |
181 | ||
182 | hashmap__for_each_entry(map, entry, bkt) { | |
183 | struct obj_refs *refs = entry->value; | |
d53dee3f | 184 | |
d53dee3f AN |
185 | free(refs->refs); |
186 | free(refs); | |
187 | } | |
d6699f8e QM |
188 | |
189 | hashmap__free(map); | |
d53dee3f AN |
190 | } |
191 | ||
d6699f8e | 192 | void emit_obj_refs_json(struct hashmap *map, __u32 id, |
54b66c22 | 193 | json_writer_t *json_writer) |
d53dee3f | 194 | { |
d6699f8e | 195 | struct hashmap_entry *entry; |
d53dee3f | 196 | |
d6699f8e | 197 | if (hashmap__empty(map)) |
d53dee3f AN |
198 | return; |
199 | ||
d6699f8e QM |
200 | hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) { |
201 | struct obj_refs *refs = entry->value; | |
202 | int i; | |
203 | ||
d53dee3f AN |
204 | if (refs->ref_cnt == 0) |
205 | break; | |
206 | ||
54b66c22 QM |
207 | jsonw_name(json_writer, "pids"); |
208 | jsonw_start_array(json_writer); | |
d53dee3f | 209 | for (i = 0; i < refs->ref_cnt; i++) { |
d6699f8e QM |
210 | struct obj_ref *ref = &refs->refs[i]; |
211 | ||
54b66c22 QM |
212 | jsonw_start_object(json_writer); |
213 | jsonw_int_field(json_writer, "pid", ref->pid); | |
214 | jsonw_string_field(json_writer, "comm", ref->comm); | |
215 | jsonw_end_object(json_writer); | |
d53dee3f | 216 | } |
54b66c22 | 217 | jsonw_end_array(json_writer); |
d53dee3f AN |
218 | break; |
219 | } | |
220 | } | |
221 | ||
d6699f8e | 222 | void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix) |
d53dee3f | 223 | { |
d6699f8e | 224 | struct hashmap_entry *entry; |
d53dee3f | 225 | |
d6699f8e | 226 | if (hashmap__empty(map)) |
d53dee3f AN |
227 | return; |
228 | ||
d6699f8e QM |
229 | hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) { |
230 | struct obj_refs *refs = entry->value; | |
231 | int i; | |
232 | ||
d53dee3f AN |
233 | if (refs->ref_cnt == 0) |
234 | break; | |
235 | ||
236 | printf("%s", prefix); | |
237 | for (i = 0; i < refs->ref_cnt; i++) { | |
d6699f8e QM |
238 | struct obj_ref *ref = &refs->refs[i]; |
239 | ||
d53dee3f AN |
240 | printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid); |
241 | } | |
242 | break; | |
243 | } | |
244 | } | |
245 | ||
246 | ||
247 | #endif |