@@ -150,6 +150,8 @@ $(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o $(BPFTOOL_BOOTSTRAP)
$(OUTPUT)prog.o: $(OUTPUT)profiler.skel.h
+$(OUTPUT)pids.o: $(OUTPUT)pid_iter.skel.h
+
endif
endif
@@ -809,6 +809,22 @@ show_btf_plain(struct bpf_btf_info *info, int fd,
printf("%s%u", n++ == 0 ? " map_ids " : ",",
obj->obj_id);
}
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ printf(" pids");
+ for (i = 0; i < pids->pid_cnt; i++)
+ printf("%c%d", i == 0 ? ' ' : ',', pids->pids[i]);
+ break;
+ }
+ }
printf("\n");
}
@@ -841,6 +857,25 @@ show_btf_json(struct bpf_btf_info *info, int fd,
jsonw_uint(json_wtr, obj->obj_id);
}
jsonw_end_array(json_wtr); /* map_ids */
+ /* PIDs */
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ jsonw_name(json_wtr, "pids");
+ jsonw_start_array(json_wtr);
+ for (i = 0; i < pids->pid_cnt; i++)
+ jsonw_int(json_wtr, pids->pids[i]);
+ jsonw_end_array(json_wtr);
+ break;
+ }
+ }
jsonw_end_object(json_wtr); /* btf object */
}
@@ -893,6 +928,8 @@ static int do_show(int argc, char **argv)
close(fd);
return err;
}
+ if (show_pids)
+ build_obj_pids_table(&pids_table, BPF_OBJ_BTF);
if (fd >= 0) {
err = show_btf(fd, &btf_prog_table, &btf_map_table);
@@ -939,6 +976,8 @@ static int do_show(int argc, char **argv)
exit_free:
delete_btf_table(&btf_prog_table);
delete_btf_table(&btf_map_table);
+ if (show_pids)
+ delete_obj_pids_table(&pids_table);
return err;
}
@@ -143,6 +143,25 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
}
jsonw_end_array(json_wtr);
}
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ jsonw_name(json_wtr, "pids");
+ jsonw_start_array(json_wtr);
+ for (i = 0; i < pids->pid_cnt; i++)
+ jsonw_int(json_wtr, pids->pids[i]);
+ jsonw_end_array(json_wtr);
+ break;
+ }
+ }
+
jsonw_end_object(json_wtr);
return 0;
@@ -212,6 +231,22 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
printf("\n\tpinned %s", obj->path);
}
}
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ printf("\n\tpids:");
+ for (i = 0; i < pids->pid_cnt; i++)
+ printf("%c%d", i == 0 ? ' ' : ',', pids->pids[i]);
+ break;
+ }
+ }
printf("\n");
@@ -257,6 +292,8 @@ static int do_show(int argc, char **argv)
if (show_pinned)
build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
+ if (show_pids)
+ build_obj_pids_table(&pids_table, BPF_OBJ_LINK);
if (argc == 2) {
fd = link_parse_fd(&argc, &argv);
@@ -296,6 +333,9 @@ static int do_show(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
+ if (show_pids)
+ delete_obj_pids_table(&pids_table);
+
return errno == ENOENT ? 0 : -1;
}
@@ -25,12 +25,14 @@ json_writer_t *json_wtr;
bool pretty_output;
bool json_output;
bool show_pinned;
+bool show_pids;
bool block_mount;
bool verifier_logs;
bool relaxed_maps;
struct pinned_obj_table prog_table;
struct pinned_obj_table map_table;
struct pinned_obj_table link_table;
+struct obj_pids_table pids_table;
static void __noreturn clean_and_exit(int i)
{
@@ -369,6 +371,7 @@ int main(int argc, char **argv)
pretty_output = false;
json_output = false;
show_pinned = false;
+ show_pids = false;
block_mount = false;
bin_name = argv[0];
@@ -377,7 +380,7 @@ int main(int argc, char **argv)
hash_init(link_table.table);
opterr = 0;
- while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
+ while ((opt = getopt_long(argc, argv, "Vhpjfomnd",
options, NULL)) >= 0) {
switch (opt) {
case 'V':
@@ -401,6 +404,9 @@ int main(int argc, char **argv)
case 'f':
show_pinned = true;
break;
+ case 'o':
+ show_pids = true;
+ break;
case 'm':
relaxed_maps = true;
break;
@@ -127,11 +127,13 @@ static const char * const attach_type_name[__MAX_BPF_ATTACH_TYPE] = {
extern const char * const map_type_name[];
extern const size_t map_type_name_size;
+/* keep in sync with the definition in skeleton/pid_iter.bpf.c */
enum bpf_obj_type {
BPF_OBJ_UNKNOWN,
BPF_OBJ_PROG,
BPF_OBJ_MAP,
BPF_OBJ_LINK,
+ BPF_OBJ_BTF,
};
extern const char *bin_name;
@@ -139,12 +141,14 @@ extern const char *bin_name;
extern json_writer_t *json_wtr;
extern bool json_output;
extern bool show_pinned;
+extern bool show_pids;
extern bool block_mount;
extern bool verifier_logs;
extern bool relaxed_maps;
extern struct pinned_obj_table prog_table;
extern struct pinned_obj_table map_table;
extern struct pinned_obj_table link_table;
+extern struct obj_pids_table pids_table;
void __printf(1, 2) p_err(const char *fmt, ...);
void __printf(1, 2) p_info(const char *fmt, ...);
@@ -168,12 +172,26 @@ struct pinned_obj {
struct hlist_node hash;
};
+struct obj_pids_table {
+ DECLARE_HASHTABLE(table, 16);
+};
+
+struct obj_pids {
+ struct hlist_node node;
+ __u32 id;
+ int pid_cnt;
+ int *pids;
+};
+
struct btf;
struct bpf_line_info;
int build_pinned_obj_table(struct pinned_obj_table *table,
enum bpf_obj_type type);
void delete_pinned_obj_table(struct pinned_obj_table *tab);
+__weak int build_obj_pids_table(struct obj_pids_table *table,
+ enum bpf_obj_type type);
+__weak void delete_obj_pids_table(struct obj_pids_table *table);
void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
@@ -508,6 +508,24 @@ static int show_map_close_json(int fd, struct bpf_map_info *info)
}
jsonw_end_array(json_wtr);
}
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ jsonw_name(json_wtr, "pids");
+ jsonw_start_array(json_wtr);
+ for (i = 0; i < pids->pid_cnt; i++)
+ jsonw_int(json_wtr, pids->pids[i]);
+ jsonw_end_array(json_wtr);
+ break;
+ }
+ }
jsonw_end_object(json_wtr);
@@ -596,6 +614,22 @@ static int show_map_close_plain(int fd, struct bpf_map_info *info)
if (frozen)
printf("%sfrozen", info->btf_id ? " " : "");
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ printf("\n\tpids:");
+ for (i = 0; i < pids->pid_cnt; i++)
+ printf("%c%d", i == 0 ? ' ' : ',', pids->pids[i]);
+ break;
+ }
+ }
printf("\n");
return 0;
}
@@ -654,6 +688,8 @@ static int do_show(int argc, char **argv)
if (show_pinned)
build_pinned_obj_table(&map_table, BPF_OBJ_MAP);
+ if (show_pids)
+ build_obj_pids_table(&pids_table, BPF_OBJ_MAP);
if (argc == 2)
return do_show_subset(argc, argv);
@@ -697,6 +733,9 @@ static int do_show(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
+ if (show_pids)
+ delete_obj_pids_table(&pids_table);
+
return errno == ENOENT ? 0 : -1;
}
new file mode 100644
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (C) 2020 Facebook */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <bpf/bpf.h>
+
+#include "main.h"
+
+#ifdef BPFTOOL_WITHOUT_SKELETONS
+
+int build_obj_pids_table(struct obj_pids_table *table, enum bpf_obj_type type)
+{
+ p_err("bpftool built without PID iterator support");
+ return -ENOTSUP;
+}
+void delete_obj_pids_table(struct obj_pids_table *table) {}
+
+#else /* BPFTOOL_WITHOUT_SKELETONS */
+
+#include "pid_iter.skel.h"
+
+int build_obj_pids_table(struct obj_pids_table *table, enum bpf_obj_type type)
+{
+ struct obj_pids *pids;
+ struct pid_iter_bpf *skel;
+ FILE *f = NULL;
+ int err, ret, fd = -1, pid, i;
+ __u32 id;
+ bool found_id, found_pid;
+
+ hash_init(table->table);
+ set_max_rlimit();
+
+ skel = pid_iter_bpf__open();
+ if (!skel) {
+ p_err("failed to open PID iterator");
+ return -1;
+ }
+
+ skel->rodata->obj_type = type;
+
+ err = pid_iter_bpf__load(skel);
+ if (err) {
+ p_err("failed to load PID iterator: %d", err);
+ goto out;
+ }
+ err = pid_iter_bpf__attach(skel);
+ if (err) {
+ p_err("failed to attach PID iterator: %d", err);
+ goto out;
+ }
+
+ fd = bpf_iter_create(bpf_link__fd(skel->links.iter));
+ if (fd < 0) {
+ err = -errno;
+ p_err("failed to create PID iterator session: %d", err);
+ goto out;
+ }
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ err = -errno;
+ goto out;
+ }
+
+ while (true) {
+ ret = fscanf(f, "%d %u\n", &pid, &id);
+ if (ret == EOF && feof(f))
+ break;
+ if (ret != 2) {
+ err = -EINVAL;
+ p_err("invalid PID iterator output format");
+ goto out;
+ }
+
+ found_id = false;
+ hash_for_each_possible(table->table, pids, node, id) {
+ if (pids->id != id)
+ continue;
+ found_id = true;
+
+ found_pid = false;
+ for (i = 0; i < pids->pid_cnt; i++) {
+ if (pids->pids[i] == pid) {
+ found_pid = true;
+ break;
+ }
+ }
+ if (!found_pid) {
+ void *tmp;
+
+ tmp = realloc(pids->pids, pids->pid_cnt + 1);
+ if (!tmp) {
+ p_err("failed to re-alloc memory for ID %u, PID %d...",
+ id, pid);
+ break;
+ }
+ pids->pids = tmp;
+ pids->pids[pids->pid_cnt] = pid;
+ pids->pid_cnt++;
+ }
+ break;
+ }
+ if (!found_id) {
+ pids = calloc(1, sizeof(*pids));
+ if (!pids) {
+ p_err("failed to alloc memory for ID %u, PID %d...", id, pid);
+ continue;
+ }
+
+ pids->id = id;
+ pids->pids = malloc(sizeof(*pids->pids));
+ if (!pids->pids) {
+ free(pids);
+ p_err("failed to alloc memory for ID %u, PID %d...", id, pid);
+ continue;
+ }
+ pids->pids[0] = pid;
+ pids->pid_cnt = 1;
+ hash_add(table->table, &pids->node, id);
+ }
+ }
+ err = 0;
+out:
+ if (f)
+ fclose(f);
+ else if (fd >= 0)
+ close(fd);
+ pid_iter_bpf__destroy(skel);
+ return err;
+}
+
+void delete_obj_pids_table(struct obj_pids_table *table)
+{
+ struct obj_pids *pids;
+ struct hlist_node *tmp;
+ unsigned int bkt;
+
+ hash_for_each_safe(table->table, bkt, tmp, pids, node) {
+ hash_del(&pids->node);
+ free(pids->pids);
+ free(pids);
+ }
+}
+
+#endif
@@ -190,6 +190,24 @@ static void print_prog_json(struct bpf_prog_info *info, int fd)
jsonw_end_array(json_wtr);
}
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ jsonw_name(json_wtr, "pids");
+ jsonw_start_array(json_wtr);
+ for (i = 0; i < pids->pid_cnt; i++)
+ jsonw_int(json_wtr, pids->pids[i]);
+ jsonw_end_array(json_wtr);
+ break;
+ }
+ }
jsonw_end_object(json_wtr);
}
@@ -256,6 +274,23 @@ static void print_prog_plain(struct bpf_prog_info *info, int fd)
if (info->btf_id)
printf("\n\tbtf_id %d", info->btf_id);
+ if (!hash_empty(pids_table.table)) {
+ struct obj_pids *pids;
+ int i;
+
+ hash_for_each_possible(pids_table.table, pids, node, info->id) {
+ if (pids->id != info->id)
+ continue;
+ if (pids->pid_cnt == 0)
+ break;
+
+ printf("\n\tpids:");
+ for (i = 0; i < pids->pid_cnt; i++)
+ printf("%c%d", i == 0 ? ' ' : ',', pids->pids[i]);
+ break;
+ }
+ }
+
printf("\n");
}
@@ -321,6 +356,8 @@ static int do_show(int argc, char **argv)
if (show_pinned)
build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
+ if (show_pids)
+ build_obj_pids_table(&pids_table, BPF_OBJ_PROG);
if (argc == 2)
return do_show_subset(argc, argv);
@@ -362,6 +399,9 @@ static int do_show(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
+ if (show_pids)
+ delete_obj_pids_table(&pids_table);
+
return err;
}
new file mode 100644
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_tracing.h>
+
+/* keep in sync with the definition in main.h */
+enum bpf_obj_type {
+ BPF_OBJ_UNKNOWN,
+ BPF_OBJ_PROG,
+ BPF_OBJ_MAP,
+ BPF_OBJ_LINK,
+ BPF_OBJ_BTF,
+};
+
+extern const void bpf_link_fops __ksym;
+extern const void bpf_map_fops __ksym;
+extern const void bpf_prog_fops __ksym;
+extern const void btf_fops __ksym;
+
+const volatile enum bpf_obj_type obj_type = BPF_OBJ_UNKNOWN;
+
+static __always_inline __u32 get_obj_id(void *ent, enum bpf_obj_type type)
+{
+ switch (type) {
+ case BPF_OBJ_PROG:
+ return BPF_CORE_READ((struct bpf_prog *)ent, aux, id);
+ case BPF_OBJ_MAP:
+ return BPF_CORE_READ((struct bpf_map *)ent, id);
+ case BPF_OBJ_BTF:
+ return BPF_CORE_READ((struct btf *)ent, id);
+ case BPF_OBJ_LINK:
+ return BPF_CORE_READ((struct bpf_link *)ent, id);
+ default:
+ return 0;
+ }
+}
+
+SEC("iter/task_file")
+int iter(struct bpf_iter__task_file *ctx)
+{
+ struct file *file = ctx->file;
+ struct task_struct *task = ctx->task;
+ const void *fops;
+ int id;
+
+ if (!file || !task)
+ return 0;
+
+ switch (obj_type) {
+ case BPF_OBJ_PROG:
+ fops = &bpf_prog_fops;
+ break;
+ case BPF_OBJ_MAP:
+ fops = &bpf_map_fops;
+ break;
+ case BPF_OBJ_BTF:
+ fops = &btf_fops;
+ break;
+ case BPF_OBJ_LINK:
+ fops = &bpf_link_fops;
+ break;
+ default:
+ return 0;
+ }
+
+ if (file->f_op != fops)
+ return 0;
+
+ id = get_obj_id(file->private_data, obj_type);
+ BPF_SEQ_PRINTF(ctx->meta->seq, "%d %d\n", task->tgid, id);
+
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
Add bpf_iter-based way to find all the processes that hold open FDs against BPF object (map, prog, link, btf). Add new flag (-o, for "ownership", given -p is already taken) to trigger collection and output of these PIDs. Sample output for each of 4 BPF objects: $ sudo ./bpftool -o prog show 1992: cgroup_skb name egress_alt tag 9ad187367cf2b9e8 gpl loaded_at 2020-06-12T14:18:10-0700 uid 0 xlated 48B jited 59B memlock 4096B map_ids 2074 btf_id 460 pids: 913709,913732,913733,913734 2062: cgroup_device tag 8c42dee26e8cd4c2 gpl loaded_at 2020-06-12T14:37:52-0700 uid 0 xlated 648B jited 409B memlock 4096B pids: 1 $ sudo ./bpftool -o map show 2074: array name test_cgr.bss flags 0x400 key 4B value 8B max_entries 1 memlock 8192B btf_id 460 pids: 913709,913732,913733,913734 $ sudo ./bpftool -o link show 82: cgroup prog 1992 cgroup_id 0 attach_type egress pids: 913709,913732,913733,913734 86: cgroup prog 1992 cgroup_id 0 attach_type egress pids: 913709,913732,913733,913734 $ sudo ./bpftool -o btf show 460: size 1527B prog_ids 1992,1991 map_ids 2074 pids 913709,913732,913733,913734 Signed-off-by: Andrii Nakryiko <andriin@fb.com> --- tools/bpf/bpftool/Makefile | 2 + tools/bpf/bpftool/btf.c | 39 ++++++ tools/bpf/bpftool/link.c | 40 ++++++ tools/bpf/bpftool/main.c | 8 +- tools/bpf/bpftool/main.h | 18 +++ tools/bpf/bpftool/map.c | 39 ++++++ tools/bpf/bpftool/pids.c | 150 ++++++++++++++++++++++ tools/bpf/bpftool/prog.c | 40 ++++++ tools/bpf/bpftool/skeleton/pid_iter.bpf.c | 77 +++++++++++ 9 files changed, 412 insertions(+), 1 deletion(-) create mode 100644 tools/bpf/bpftool/pids.c create mode 100644 tools/bpf/bpftool/skeleton/pid_iter.bpf.c