xref: /openbmc/linux/tools/bpf/bpftool/pids.c (revision 75016ca3)
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>
9 
10 #include <bpf/bpf.h>
11 #include <bpf/hashmap.h>
12 
13 #include "main.h"
14 #include "skeleton/pid_iter.h"
15 
16 #ifdef BPFTOOL_WITHOUT_SKELETONS
17 
18 int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type)
19 {
20 	return -ENOTSUP;
21 }
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) {}
25 
26 #else /* BPFTOOL_WITHOUT_SKELETONS */
27 
28 #include "pid_iter.skel.h"
29 
30 static void add_ref(struct hashmap *map, struct pid_iter_entry *e)
31 {
32 	struct hashmap_entry *entry;
33 	struct obj_refs *refs;
34 	struct obj_ref *ref;
35 	int err, i;
36 	void *tmp;
37 
38 	hashmap__for_each_key_entry(map, entry, u32_as_hash_field(e->id)) {
39 		refs = entry->value;
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 
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;
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));
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 
95 int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type)
96 {
97 	struct pid_iter_entry *e;
98 	char buf[4096 / sizeof(*e) * sizeof(*e)];
99 	struct pid_iter_bpf *skel;
100 	int err, ret, fd = -1, i;
101 	libbpf_print_fn_t default_print;
102 
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 	}
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) {
146 			if (errno == EAGAIN)
147 				continue;
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++) {
163 			add_ref(*map, e);
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 
174 void delete_obj_refs_table(struct hashmap *map)
175 {
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;
184 
185 		free(refs->refs);
186 		free(refs);
187 	}
188 
189 	hashmap__free(map);
190 }
191 
192 void emit_obj_refs_json(struct hashmap *map, __u32 id,
193 			json_writer_t *json_writer)
194 {
195 	struct hashmap_entry *entry;
196 
197 	if (hashmap__empty(map))
198 		return;
199 
200 	hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) {
201 		struct obj_refs *refs = entry->value;
202 		int i;
203 
204 		if (refs->ref_cnt == 0)
205 			break;
206 
207 		jsonw_name(json_writer, "pids");
208 		jsonw_start_array(json_writer);
209 		for (i = 0; i < refs->ref_cnt; i++) {
210 			struct obj_ref *ref = &refs->refs[i];
211 
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);
216 		}
217 		jsonw_end_array(json_writer);
218 		break;
219 	}
220 }
221 
222 void emit_obj_refs_plain(struct hashmap *map, __u32 id, const char *prefix)
223 {
224 	struct hashmap_entry *entry;
225 
226 	if (hashmap__empty(map))
227 		return;
228 
229 	hashmap__for_each_key_entry(map, entry, u32_as_hash_field(id)) {
230 		struct obj_refs *refs = entry->value;
231 		int i;
232 
233 		if (refs->ref_cnt == 0)
234 			break;
235 
236 		printf("%s", prefix);
237 		for (i = 0; i < refs->ref_cnt; i++) {
238 			struct obj_ref *ref = &refs->refs[i];
239 
240 			printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid);
241 		}
242 		break;
243 	}
244 }
245 
246 
247 #endif
248