$(OUTPUT)prog.o: $(OUTPUT)profiler.skel.h
 
+$(OUTPUT)pids.o: $(OUTPUT)pid_iter.skel.h
+
 endif
 endif
 
 
                        printf("%s%u", n++ == 0 ? "  map_ids " : ",",
                               obj->obj_id);
        }
+       emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
 
        printf("\n");
 }
                        jsonw_uint(json_wtr, obj->obj_id);
        }
        jsonw_end_array(json_wtr);      /* map_ids */
+
+       emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
+
        jsonw_end_object(json_wtr);     /* btf object */
 }
 
                        close(fd);
                return err;
        }
+       build_obj_refs_table(&refs_table, BPF_OBJ_BTF);
 
        if (fd >= 0) {
                err = show_btf(fd, &btf_prog_table, &btf_map_table);
 exit_free:
        delete_btf_table(&btf_prog_table);
        delete_btf_table(&btf_map_table);
+       delete_obj_refs_table(&refs_table);
 
        return err;
 }
 
                }
                jsonw_end_array(json_wtr);
        }
+
+       emit_obj_refs_json(&refs_table, info->id, json_wtr);
+
        jsonw_end_object(json_wtr);
 
        return 0;
                                printf("\n\tpinned %s", obj->path);
                }
        }
+       emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
 
        printf("\n");
 
 
        if (show_pinned)
                build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
+       build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
 
        if (argc == 2) {
                fd = link_parse_fd(&argc, &argv);
        if (json_output)
                jsonw_end_array(json_wtr);
 
+       delete_obj_refs_table(&refs_table);
+
        return errno == ENOENT ? 0 : -1;
 }
 
 
 struct pinned_obj_table prog_table;
 struct pinned_obj_table map_table;
 struct pinned_obj_table link_table;
+struct obj_refs_table refs_table;
 
 static void __noreturn clean_and_exit(int i)
 {
 
 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;
 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_refs_table refs_table;
 
 void __printf(1, 2) p_err(const char *fmt, ...);
 void __printf(1, 2) p_info(const char *fmt, ...);
        struct hlist_node hash;
 };
 
+struct obj_refs_table {
+       DECLARE_HASHTABLE(table, 16);
+};
+
+struct obj_ref {
+       int pid;
+       char comm[16];
+};
+
+struct obj_refs {
+       struct hlist_node node;
+       __u32 id;
+       int ref_cnt;
+       struct obj_ref *refs;
+};
+
 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_refs_table(struct obj_refs_table *table,
+                               enum bpf_obj_type type);
+__weak void delete_obj_refs_table(struct obj_refs_table *table);
+__weak void emit_obj_refs_json(struct obj_refs_table *table, __u32 id,
+                              json_writer_t *json_wtr);
+__weak void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id,
+                               const char *prefix);
 void print_dev_plain(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
 void print_dev_json(__u32 ifindex, __u64 ns_dev, __u64 ns_inode);
 
 
                jsonw_end_array(json_wtr);
        }
 
+       emit_obj_refs_json(&refs_table, info->id, json_wtr);
+
        jsonw_end_object(json_wtr);
 
        return 0;
        if (frozen)
                printf("%sfrozen", info->btf_id ? "  " : "");
 
+       emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
+
        printf("\n");
        return 0;
 }
 
        if (show_pinned)
                build_pinned_obj_table(&map_table, BPF_OBJ_MAP);
+       build_obj_refs_table(&refs_table, BPF_OBJ_MAP);
 
        if (argc == 2)
                return do_show_subset(argc, argv);
        if (json_output)
                jsonw_end_array(json_wtr);
 
+       delete_obj_refs_table(&refs_table);
+
        return errno == ENOENT ? 0 : -1;
 }
 
 
--- /dev/null
+// 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"
+#include "skeleton/pid_iter.h"
+
+#ifdef BPFTOOL_WITHOUT_SKELETONS
+
+int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type)
+{
+       p_err("bpftool built without PID iterator support");
+       return -ENOTSUP;
+}
+void delete_obj_refs_table(struct obj_refs_table *table) {}
+
+#else /* BPFTOOL_WITHOUT_SKELETONS */
+
+#include "pid_iter.skel.h"
+
+static void add_ref(struct obj_refs_table *table, struct pid_iter_entry *e)
+{
+       struct obj_refs *refs;
+       struct obj_ref *ref;
+       void *tmp;
+       int i;
+
+       hash_for_each_possible(table->table, refs, node, e->id) {
+               if (refs->id != e->id)
+                       continue;
+
+               for (i = 0; i < refs->ref_cnt; i++) {
+                       if (refs->refs[i].pid == e->pid)
+                               return;
+               }
+
+               tmp = realloc(refs->refs, (refs->ref_cnt + 1) * sizeof(*ref));
+               if (!tmp) {
+                       p_err("failed to re-alloc memory for ID %u, PID %d, COMM %s...",
+                             e->id, e->pid, e->comm);
+                       return;
+               }
+               refs->refs = tmp;
+               ref = &refs->refs[refs->ref_cnt];
+               ref->pid = e->pid;
+               memcpy(ref->comm, e->comm, sizeof(ref->comm));
+               refs->ref_cnt++;
+
+               return;
+       }
+
+       /* new ref */
+       refs = calloc(1, sizeof(*refs));
+       if (!refs) {
+               p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
+                     e->id, e->pid, e->comm);
+               return;
+       }
+
+       refs->id = e->id;
+       refs->refs = malloc(sizeof(*refs->refs));
+       if (!refs->refs) {
+               free(refs);
+               p_err("failed to alloc memory for ID %u, PID %d, COMM %s...",
+                     e->id, e->pid, e->comm);
+               return;
+       }
+       ref = &refs->refs[0];
+       ref->pid = e->pid;
+       memcpy(ref->comm, e->comm, sizeof(ref->comm));
+       refs->ref_cnt = 1;
+       hash_add(table->table, &refs->node, e->id);
+}
+
+static int __printf(2, 0)
+libbpf_print_none(__maybe_unused enum libbpf_print_level level,
+                 __maybe_unused const char *format,
+                 __maybe_unused va_list args)
+{
+       return 0;
+}
+
+int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type)
+{
+       char buf[4096];
+       struct pid_iter_bpf *skel;
+       struct pid_iter_entry *e;
+       int err, ret, fd = -1, i;
+       libbpf_print_fn_t default_print;
+
+       hash_init(table->table);
+       set_max_rlimit();
+
+       skel = pid_iter_bpf__open();
+       if (!skel) {
+               p_err("failed to open PID iterator skeleton");
+               return -1;
+       }
+
+       skel->rodata->obj_type = type;
+
+       /* we don't want output polluted with libbpf errors if bpf_iter is not
+        * supported
+        */
+       default_print = libbpf_set_print(libbpf_print_none);
+       err = pid_iter_bpf__load(skel);
+       libbpf_set_print(default_print);
+       if (err) {
+               /* too bad, kernel doesn't support BPF iterators yet */
+               err = 0;
+               goto out;
+       }
+       err = pid_iter_bpf__attach(skel);
+       if (err) {
+               /* if we loaded above successfully, attach has to succeed */
+               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;
+       }
+
+       while (true) {
+               ret = read(fd, buf, sizeof(buf));
+               if (ret < 0) {
+                       err = -errno;
+                       p_err("failed to read PID iterator output: %d", err);
+                       goto out;
+               }
+               if (ret == 0)
+                       break;
+               if (ret % sizeof(*e)) {
+                       err = -EINVAL;
+                       p_err("invalid PID iterator output format");
+                       goto out;
+               }
+               ret /= sizeof(*e);
+
+               e = (void *)buf;
+               for (i = 0; i < ret; i++, e++) {
+                       add_ref(table, e);
+               }
+       }
+       err = 0;
+out:
+       if (fd >= 0)
+               close(fd);
+       pid_iter_bpf__destroy(skel);
+       return err;
+}
+
+void delete_obj_refs_table(struct obj_refs_table *table)
+{
+       struct obj_refs *refs;
+       struct hlist_node *tmp;
+       unsigned int bkt;
+
+       hash_for_each_safe(table->table, bkt, tmp, refs, node) {
+               hash_del(&refs->node);
+               free(refs->refs);
+               free(refs);
+       }
+}
+
+void emit_obj_refs_json(struct obj_refs_table *table, __u32 id, json_writer_t *json_wtr)
+{
+       struct obj_refs *refs;
+       struct obj_ref *ref;
+       int i;
+
+       if (hash_empty(table->table))
+               return;
+
+       hash_for_each_possible(table->table, refs, node, id) {
+               if (refs->id != id)
+                       continue;
+               if (refs->ref_cnt == 0)
+                       break;
+
+               jsonw_name(json_wtr, "pids");
+               jsonw_start_array(json_wtr);
+               for (i = 0; i < refs->ref_cnt; i++) {
+                       ref = &refs->refs[i];
+                       jsonw_start_object(json_wtr);
+                       jsonw_int_field(json_wtr, "pid", ref->pid);
+                       jsonw_string_field(json_wtr, "comm", ref->comm);
+                       jsonw_end_object(json_wtr);
+               }
+               jsonw_end_array(json_wtr);
+               break;
+       }
+}
+
+void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id, const char *prefix)
+{
+       struct obj_refs *refs;
+       struct obj_ref *ref;
+       int i;
+
+       if (hash_empty(table->table))
+               return;
+
+       hash_for_each_possible(table->table, refs, node, id) {
+               if (refs->id != id)
+                       continue;
+               if (refs->ref_cnt == 0)
+                       break;
+
+               printf("%s", prefix);
+               for (i = 0; i < refs->ref_cnt; i++) {
+                       ref = &refs->refs[i];
+                       printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid);
+               }
+               break;
+       }
+}
+
+
+#endif
 
                jsonw_end_array(json_wtr);
        }
 
+       emit_obj_refs_json(&refs_table, info->id, json_wtr);
+
        jsonw_end_object(json_wtr);
 }
 
        if (info->btf_id)
                printf("\n\tbtf_id %d", info->btf_id);
 
+       emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
+
        printf("\n");
 }
 
 
        if (show_pinned)
                build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
+       build_obj_refs_table(&refs_table, BPF_OBJ_PROG);
 
        if (argc == 2)
                return do_show_subset(argc, argv);
        if (json_output)
                jsonw_end_array(json_wtr);
 
+       delete_obj_refs_table(&refs_table);
+
        return err;
 }
 
 
--- /dev/null
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/* Copyright (c) 2020 Facebook */
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include <bpf/bpf_tracing.h>
+#include "pid_iter.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;
+       struct pid_iter_entry e;
+       const void *fops;
+
+       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;
+
+       e.pid = task->tgid;
+       e.id = get_obj_id(file->private_data, obj_type);
+       bpf_probe_read(&e.comm, sizeof(e.comm), task->group_leader->comm);
+       bpf_seq_write(ctx->meta->seq, &e, sizeof(e));
+
+       return 0;
+}
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
 
--- /dev/null
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/* Copyright (c) 2020 Facebook */
+#ifndef __PID_ITER_H
+#define __PID_ITER_H
+
+struct pid_iter_entry {
+       __u32 id;
+       int pid;
+       char comm[16];
+};
+
+#endif