diff mbox series

[RFC,bpf-next,8/8] tools/bpftool: show PIDs with FDs open against BPF map/prog/link/btf

Message ID 20200612223150.1177182-9-andriin@fb.com
State RFC
Delegated to: BPF Maintainers
Headers show
Series libbpf ksym support and bpftool show PIDs | expand

Commit Message

Andrii Nakryiko June 12, 2020, 10:31 p.m. UTC
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

Comments

Alexei Starovoitov June 13, 2020, 3:45 a.m. UTC | #1
On Fri, Jun 12, 2020 at 03:31:50PM -0700, Andrii Nakryiko wrote:
> 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

This is awesome.

Why extra flag though? I think it's so useful that everyone would want to see
this by default. Also the word 'pid' has kernel meaning or user space meaning?
Looks like kernel then bpftool should say 'tid'.
Could you capture comm as well and sort it by comm, like:

$ sudo ./bpftool link show
82: cgroup  prog 1992
        cgroup_id 0  attach_type egress
        systemd(1), firewall(913709 913732), logger(913733 913734)
Andrii Nakryiko June 13, 2020, 5:57 a.m. UTC | #2
On Fri, Jun 12, 2020 at 8:45 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Fri, Jun 12, 2020 at 03:31:50PM -0700, Andrii Nakryiko wrote:
> > 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
>
> This is awesome.

Thanks.

>
> Why extra flag though? I think it's so useful that everyone would want to see

No good reason apart from "being safe by default". If turned on by
default, bpftool would need to probe for bpf_iter support first. I can
add probing and do this by default.

> this by default. Also the word 'pid' has kernel meaning or user space meaning?
> Looks like kernel then bpftool should say 'tid'.

No, its process ID in user-space sense. See task->tgid in
pid_iter.bpf.c. I figured thread ID isn't all that useful.

> Could you capture comm as well and sort it by comm, like:
>
> $ sudo ./bpftool link show
> 82: cgroup  prog 1992
>         cgroup_id 0  attach_type egress
>         systemd(1), firewall(913709 913732), logger(913733 913734)

Yep, comm is useful, I'll add that. Grouping by comm is kind of a
pain, though, plus usually there will be one process only. So let me
start with doing comm (pid) for each PID independently. I think that
will be as good in practice.
Arnaldo Carvalho de Melo June 13, 2020, 10:14 p.m. UTC | #3
Em Fri, Jun 12, 2020 at 10:57:59PM -0700, Andrii Nakryiko escreveu:
> On Fri, Jun 12, 2020 at 8:45 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> >
> > On Fri, Jun 12, 2020 at 03:31:50PM -0700, Andrii Nakryiko wrote:
> > > 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
> >
> > This is awesome.

Indeed.
 
> Thanks.
> 
> >
> > Why extra flag though? I think it's so useful that everyone would want to see

Agreed.
 
> No good reason apart from "being safe by default". If turned on by
> default, bpftool would need to probe for bpf_iter support first. I can
> add probing and do this by default.

I think this is the way to go.
 
> > this by default. Also the word 'pid' has kernel meaning or user space meaning?
> > Looks like kernel then bpftool should say 'tid'.
> 
> No, its process ID in user-space sense. See task->tgid in
> pid_iter.bpf.c. I figured thread ID isn't all that useful.
> 
> > Could you capture comm as well and sort it by comm, like:
> >
> > $ sudo ./bpftool link show
> > 82: cgroup  prog 1992
> >         cgroup_id 0  attach_type egress
> >         systemd(1), firewall(913709 913732), logger(913733 913734)
> 
> Yep, comm is useful, I'll add that. Grouping by comm is kind of a
> pain, though, plus usually there will be one process only. So let me
> start with doing comm (pid) for each PID independently. I think that
> will be as good in practice.
Toke Høiland-Jørgensen June 15, 2020, 9:04 a.m. UTC | #4
Arnaldo Carvalho de Melo <acme@kernel.org> writes:

> Em Fri, Jun 12, 2020 at 10:57:59PM -0700, Andrii Nakryiko escreveu:
>> On Fri, Jun 12, 2020 at 8:45 PM Alexei Starovoitov
>> <alexei.starovoitov@gmail.com> wrote:
>> >
>> > On Fri, Jun 12, 2020 at 03:31:50PM -0700, Andrii Nakryiko wrote:
>> > > 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
>> >
>> > This is awesome.
>
> Indeed.
>  
>> Thanks.
>> 
>> >
>> > Why extra flag though? I think it's so useful that everyone would want to see
>
> Agreed.
>  
>> No good reason apart from "being safe by default". If turned on by
>> default, bpftool would need to probe for bpf_iter support first. I can
>> add probing and do this by default.
>
> I think this is the way to go.

+1

And also +1 on the awesomeness of this feature! :)

-Toke
Quentin Monnet June 15, 2020, 9:30 a.m. UTC | #5
2020-06-15 11:04 UTC+0200 ~ Toke Høiland-Jørgensen <toke@redhat.com>
> Arnaldo Carvalho de Melo <acme@kernel.org> writes:
> 
>> Em Fri, Jun 12, 2020 at 10:57:59PM -0700, Andrii Nakryiko escreveu:
>>> On Fri, Jun 12, 2020 at 8:45 PM Alexei Starovoitov
>>> <alexei.starovoitov@gmail.com> wrote:
>>>>
>>>> On Fri, Jun 12, 2020 at 03:31:50PM -0700, Andrii Nakryiko wrote:
>>>>> 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
>>>>
>>>> This is awesome.
>>
>> Indeed.
>>  
>>> Thanks.
>>>
>>>>
>>>> Why extra flag though? I think it's so useful that everyone would want to see
>>
>> Agreed.
>>  
>>> No good reason apart from "being safe by default". If turned on by
>>> default, bpftool would need to probe for bpf_iter support first. I can
>>> add probing and do this by default.
>>
>> I think this is the way to go.
> 
> +1
> 
> And also +1 on the awesomeness of this feature! :)
> 
> -Toke
> 

Thanks a lot Andrii, the feature looks great indeed.

Thank you for the clean-up and refactoring in bpftool and its Makefile
as well, I am happy to confirm that test_bpftool_build.sh still passes
on my setup with your changes.

Quentin
diff mbox series

Patch

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index bdb6e38c6c5c..06f436e8191a 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -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
 
diff --git a/tools/bpf/bpftool/btf.c b/tools/bpf/bpftool/btf.c
index faac8189b285..448c89d1faee 100644
--- a/tools/bpf/bpftool/btf.c
+++ b/tools/bpf/bpftool/btf.c
@@ -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;
 }
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index fca57ee8fafe..d959ce838dfb 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -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;
 }
 
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index bf4d7487552a..53a546e1d9a8 100644
--- a/tools/bpf/bpftool/main.c
+++ b/tools/bpf/bpftool/main.c
@@ -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;
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index aad7be74e8a7..286134b05687 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -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);
 
diff --git a/tools/bpf/bpftool/map.c b/tools/bpf/bpftool/map.c
index b9eee19b094c..1fc8b674049e 100644
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -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;
 }
 
diff --git a/tools/bpf/bpftool/pids.c b/tools/bpf/bpftool/pids.c
new file mode 100644
index 000000000000..fb0a2d7213b4
--- /dev/null
+++ b/tools/bpf/bpftool/pids.c
@@ -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
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 53d47610ff58..6b430f9af2af 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -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;
 }
 
diff --git a/tools/bpf/bpftool/skeleton/pid_iter.bpf.c b/tools/bpf/bpftool/skeleton/pid_iter.bpf.c
new file mode 100644
index 000000000000..9eedf5e56aa7
--- /dev/null
+++ b/tools/bpf/bpftool/skeleton/pid_iter.bpf.c
@@ -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";