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