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