bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC bpf-next 0/4] bpftool: introduce prog profile
@ 2020-02-13 21:01 Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 1/4] bpf: allow bpf_perf_event_read_value in all BPF programs Song Liu
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:01 UTC (permalink / raw)
  To: netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu

This set introduces bpftool prog profile command, which uses hardware
counters to profile BPF programs.

This command attaches fentry/fexit programs to a target program. These two
programs read hardware counters before and after the target program and
calculate the difference.

Sending as RFC because 2/4 will be replaced by Eelco Chaudron's work for
bpf_program__set_attach_target().

Please share your comments.

Thanks,
Song

Song Liu (4):
  bpf: allow bpf_perf_event_read_value in all BPF programs
  libbpf: introduce bpf_program__overwrite_section_name()
  bpftool: introduce "prog profile" command
  bpftool: Documentation for bpftool prog profile

 kernel/trace/bpf_trace.c                      |   4 +-
 .../bpftool/Documentation/bpftool-prog.rst    |  17 +
 tools/bpf/bpftool/profiler.skel.h             | 820 ++++++++++++++++++
 tools/bpf/bpftool/prog.c                      | 387 ++++++++-
 tools/bpf/bpftool/skeleton/README             |   3 +
 tools/bpf/bpftool/skeleton/profiler.bpf.c     | 185 ++++
 tools/bpf/bpftool/skeleton/profiler.h         |  47 +
 tools/lib/bpf/libbpf.c                        |  13 +-
 tools/lib/bpf/libbpf.h                        |   4 +
 tools/lib/bpf/libbpf.map                      |   5 +
 10 files changed, 1481 insertions(+), 4 deletions(-)
 create mode 100644 tools/bpf/bpftool/profiler.skel.h
 create mode 100644 tools/bpf/bpftool/skeleton/README
 create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
 create mode 100644 tools/bpf/bpftool/skeleton/profiler.h

--
2.17.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC bpf-next 1/4] bpf: allow bpf_perf_event_read_value in all BPF programs
  2020-02-13 21:01 [RFC bpf-next 0/4] bpftool: introduce prog profile Song Liu
@ 2020-02-13 21:01 ` Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 2/4] libbpf: introduce bpf_program__overwrite_section_name() Song Liu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:01 UTC (permalink / raw)
  To: netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu, Yonghong Song

bpf_perf_event_read_value() is NMI safe. Enable it for all BPF programs.
This can be used in fentry/fexit to profile BPF program and individual
kernel function with hardware counters.

Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Song Liu <songliubraving@fb.com>
---
 kernel/trace/bpf_trace.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 19e793aa441a..4ddd5ac46094 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -843,6 +843,8 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return &bpf_send_signal_proto;
 	case BPF_FUNC_send_signal_thread:
 		return &bpf_send_signal_thread_proto;
+	case BPF_FUNC_perf_event_read_value:
+		return &bpf_perf_event_read_value_proto;
 	default:
 		return NULL;
 	}
@@ -858,8 +860,6 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return &bpf_get_stackid_proto;
 	case BPF_FUNC_get_stack:
 		return &bpf_get_stack_proto;
-	case BPF_FUNC_perf_event_read_value:
-		return &bpf_perf_event_read_value_proto;
 #ifdef CONFIG_BPF_KPROBE_OVERRIDE
 	case BPF_FUNC_override_return:
 		return &bpf_override_return_proto;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC bpf-next 2/4] libbpf: introduce bpf_program__overwrite_section_name()
  2020-02-13 21:01 [RFC bpf-next 0/4] bpftool: introduce prog profile Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 1/4] bpf: allow bpf_perf_event_read_value in all BPF programs Song Liu
@ 2020-02-13 21:01 ` Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 3/4] bpftool: introduce "prog profile" command Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 4/4] bpftool: Documentation for bpftool prog profile Song Liu
  3 siblings, 0 replies; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:01 UTC (permalink / raw)
  To: netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu

This is a temporary solution before Eelco Chaudron's work for
bpf_program__set_attach_target():

https://patchwork.ozlabs.org/patch/1237539/

Signed-off-by: Song Liu <songliubraving@fb.com>
---
 tools/lib/bpf/libbpf.c   | 13 ++++++++++++-
 tools/lib/bpf/libbpf.h   |  4 ++++
 tools/lib/bpf/libbpf.map |  5 +++++
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514b1a524abb..4c29a7181d57 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -238,6 +238,8 @@ struct bpf_program {
 	__u32 line_info_rec_size;
 	__u32 line_info_cnt;
 	__u32 prog_flags;
+
+	char *overwritten_section_name;
 };
 
 struct bpf_struct_ops {
@@ -442,6 +444,7 @@ static void bpf_program__exit(struct bpf_program *prog)
 	zfree(&prog->pin_name);
 	zfree(&prog->insns);
 	zfree(&prog->reloc_desc);
+	zfree(&prog->overwritten_section_name);
 
 	prog->nr_reloc = 0;
 	prog->insns_cnt = 0;
@@ -6637,7 +6640,7 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog)
 {
 	enum bpf_attach_type attach_type = prog->expected_attach_type;
 	__u32 attach_prog_fd = prog->attach_prog_fd;
-	const char *name = prog->section_name;
+	const char *name = prog->overwritten_section_name ? : prog->section_name;
 	int i, err;
 
 	if (!name)
@@ -8396,3 +8399,11 @@ void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s)
 	free(s->progs);
 	free(s);
 }
+
+char *bpf_program__overwrite_section_name(struct bpf_program *prog,
+					  const char *sec_name)
+{
+	prog->overwritten_section_name = strdup(sec_name);
+
+	return prog->overwritten_section_name;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3fe12c9d1f92..02f0d8b57cc4 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -595,6 +595,10 @@ bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
 LIBBPF_API void
 bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
 
+LIBBPF_API char *
+bpf_program__overwrite_section_name(struct bpf_program *prog,
+				    const char *sec_name);
+
 /*
  * A helper function to get the number of possible CPUs before looking up
  * per-CPU maps. Negative errno is returned on failure.
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index b035122142bb..ed26c20729db 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -235,3 +235,8 @@ LIBBPF_0.0.7 {
 		btf__align_of;
 		libbpf_find_kernel_btf;
 } LIBBPF_0.0.6;
+
+LIBBPF_0.0.8 {
+	global:
+		bpf_program__overwrite_section_name;
+} LIBBPF_0.0.7;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-13 21:01 [RFC bpf-next 0/4] bpftool: introduce prog profile Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 1/4] bpf: allow bpf_perf_event_read_value in all BPF programs Song Liu
  2020-02-13 21:01 ` [RFC bpf-next 2/4] libbpf: introduce bpf_program__overwrite_section_name() Song Liu
@ 2020-02-13 21:01 ` Song Liu
  2020-02-13 21:50   ` Toke Høiland-Jørgensen
  2020-02-15  0:45   ` Alexei Starovoitov
  2020-02-13 21:01 ` [RFC bpf-next 4/4] bpftool: Documentation for bpftool prog profile Song Liu
  3 siblings, 2 replies; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:01 UTC (permalink / raw)
  To: netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu

With fentry/fexit programs, it is possible to profile BPF program with
hardware counters. Introduce bpftool "prog profile", which measures key
metrics of a BPF program.

bpftool prog profile command creates per-cpu perf events. Then it attaches
fentry/fexit programs to the target BPF program. The fentry program saves
perf event value to a map. The fexit program reads the perf event again,
and calculates the difference, which is the instructions/cycles used by
the target program.

Example input and output:

  ./bpftool prog profile 20 id 810 cycles instructions
  cycles: duration 20 run_cnt 1368 miss_cnt 665
          counter 503377 enabled 668202 running 351857
  instructions: duration 20 run_cnt 1368 miss_cnt 707
          counter 398625 enabled 502330 running 272014

This command measures cycles and instructions for BPF program with id
810 for 20 seconds. The program has triggered 1368 times. cycles was not
measured in 665 out of these runs, because of perf event multiplexing
(some perf commands are running in the background). In these runs, the BPF
program consumed 503377 cycles. The perf_event enabled and running time
are 668202 and 351857 respectively.

Note that, this approach measures cycles and instructions in very small
increments. So the fentry/fexit programs introduce noticable errors to
the measurement results.

The fentry/fexit programs are generated with BPF skeleton. Currently,
generation of the skeleton requires some manual steps.

Signed-off-by: Song Liu <songliubraving@fb.com>
---
 tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++
 tools/bpf/bpftool/prog.c                  | 387 +++++++++-
 tools/bpf/bpftool/skeleton/README         |   3 +
 tools/bpf/bpftool/skeleton/profiler.bpf.c | 185 +++++
 tools/bpf/bpftool/skeleton/profiler.h     |  47 ++
 5 files changed, 1441 insertions(+), 1 deletion(-)
 create mode 100644 tools/bpf/bpftool/profiler.skel.h
 create mode 100644 tools/bpf/bpftool/skeleton/README
 create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
 create mode 100644 tools/bpf/bpftool/skeleton/profiler.h

diff --git a/tools/bpf/bpftool/profiler.skel.h b/tools/bpf/bpftool/profiler.skel.h
new file mode 100644
index 000000000000..10e99989c03e
--- /dev/null
+++ b/tools/bpf/bpftool/profiler.skel.h
@@ -0,0 +1,820 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/* THIS FILE IS AUTOGENERATED! */
+#ifndef __PROFILER_BPF_SKEL_H__
+#define __PROFILER_BPF_SKEL_H__
+
+#include <stdlib.h>
+#include <bpf/libbpf.h>
+
+struct profiler_bpf {
+	struct bpf_object_skeleton *skeleton;
+	struct bpf_object *obj;
+	struct {
+		struct bpf_map *events;
+		struct bpf_map *fentry_readings;
+		struct bpf_map *accum_readings;
+		struct bpf_map *counts;
+		struct bpf_map *miss_counts;
+		struct bpf_map *rodata;
+	} maps;
+	struct {
+		struct bpf_program *fentry_XXX;
+		struct bpf_program *fexit_XXX;
+	} progs;
+	struct {
+		struct bpf_link *fentry_XXX;
+		struct bpf_link *fexit_XXX;
+	} links;
+	struct profiler_bpf__rodata {
+		__u32 num_cpu;
+		__u32 num_metric;
+	} *rodata;
+};
+
+static void
+profiler_bpf__destroy(struct profiler_bpf *obj)
+{
+	if (!obj)
+		return;
+	if (obj->skeleton)
+		bpf_object__destroy_skeleton(obj->skeleton);
+	free(obj);
+}
+
+static inline int
+profiler_bpf__create_skeleton(struct profiler_bpf *obj);
+
+static inline struct profiler_bpf *
+profiler_bpf__open_opts(const struct bpf_object_open_opts *opts)
+{
+	struct profiler_bpf *obj;
+
+	obj = (typeof(obj))calloc(1, sizeof(*obj));
+	if (!obj)
+		return NULL;
+	if (profiler_bpf__create_skeleton(obj))
+		goto err;
+	if (bpf_object__open_skeleton(obj->skeleton, opts))
+		goto err;
+
+	return obj;
+err:
+	profiler_bpf__destroy(obj);
+	return NULL;
+}
+
+static inline struct profiler_bpf *
+profiler_bpf__open(void)
+{
+	return profiler_bpf__open_opts(NULL);
+}
+
+static inline int
+profiler_bpf__load(struct profiler_bpf *obj)
+{
+	return bpf_object__load_skeleton(obj->skeleton);
+}
+
+static inline struct profiler_bpf *
+profiler_bpf__open_and_load(void)
+{
+	struct profiler_bpf *obj;
+
+	obj = profiler_bpf__open();
+	if (!obj)
+		return NULL;
+	if (profiler_bpf__load(obj)) {
+		profiler_bpf__destroy(obj);
+		return NULL;
+	}
+	return obj;
+}
+
+static inline int
+profiler_bpf__attach(struct profiler_bpf *obj)
+{
+	return bpf_object__attach_skeleton(obj->skeleton);
+}
+
+static inline void
+profiler_bpf__detach(struct profiler_bpf *obj)
+{
+	return bpf_object__detach_skeleton(obj->skeleton);
+}
+
+static inline int
+profiler_bpf__create_skeleton(struct profiler_bpf *obj)
+{
+	struct bpf_object_skeleton *s;
+
+	s = (typeof(s))calloc(1, sizeof(*s));
+	if (!s)
+		return -1;
+	obj->skeleton = s;
+
+	s->sz = sizeof(*s);
+	s->name = "profiler_bpf";
+	s->obj = &obj->obj;
+
+	/* maps */
+	s->map_cnt = 6;
+	s->map_skel_sz = sizeof(*s->maps);
+	s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
+	if (!s->maps)
+		goto err;
+
+	s->maps[0].name = "events";
+	s->maps[0].map = &obj->maps.events;
+
+	s->maps[1].name = "fentry_readings";
+	s->maps[1].map = &obj->maps.fentry_readings;
+
+	s->maps[2].name = "accum_readings";
+	s->maps[2].map = &obj->maps.accum_readings;
+
+	s->maps[3].name = "counts";
+	s->maps[3].map = &obj->maps.counts;
+
+	s->maps[4].name = "miss_counts";
+	s->maps[4].map = &obj->maps.miss_counts;
+
+	s->maps[5].name = "profiler.rodata";
+	s->maps[5].map = &obj->maps.rodata;
+	s->maps[5].mmaped = (void **)&obj->rodata;
+
+	/* programs */
+	s->prog_cnt = 2;
+	s->prog_skel_sz = sizeof(*s->progs);
+	s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
+	if (!s->progs)
+		goto err;
+
+	s->progs[0].name = "fentry_XXX";
+	s->progs[0].prog = &obj->progs.fentry_XXX;
+	s->progs[0].link = &obj->links.fentry_XXX;
+
+	s->progs[1].name = "fexit_XXX";
+	s->progs[1].prog = &obj->progs.fexit_XXX;
+	s->progs[1].link = &obj->links.fexit_XXX;
+
+	s->data_sz = 18256;
+	s->data = (void *)"\
+\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\x50\x40\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x1c\0\
+\x01\0\x85\0\0\0\x08\0\0\0\xbf\x06\0\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\x61\x11\0\0\0\0\0\0\x15\x01\x14\0\0\0\0\0\xbf\xa8\0\0\0\0\0\0\x07\x08\0\0\
+\xe0\xff\xff\xff\xb7\x09\0\0\0\0\0\0\xb7\x07\0\0\0\0\0\0\x63\x9a\xc8\xff\0\0\0\
+\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xc8\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\x85\0\0\0\x01\0\0\0\x7b\x08\0\0\0\0\0\0\x15\0\x23\0\0\0\0\0\x18\x01\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x11\0\0\0\0\0\0\x25\x07\x04\0\x02\0\0\0\x07\x07\
+\0\0\x01\0\0\0\x07\x09\0\0\x01\0\0\0\x07\x08\0\0\x08\0\0\0\x2d\x71\xf0\xff\0\0\
+\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x11\0\0\0\0\0\0\x15\x01\x17\0\0\0\
+\0\0\xb7\x07\0\0\0\0\0\0\xbf\xa8\0\0\0\0\0\0\x07\x08\0\0\xe0\xff\xff\xff\x18\
+\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x05\0\x13\0\0\0\0\0\x79\x81\0\0\0\0\0\0\x79\
+\xa2\xd8\xff\0\0\0\0\x7b\x21\x10\0\0\0\0\0\x79\xa2\xd0\xff\0\0\0\0\x7b\x21\x08\
+\0\0\0\0\0\x79\xa2\xc8\xff\0\0\0\0\x7b\x21\0\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x61\x11\0\0\0\0\0\0\x61\x92\0\0\0\0\0\0\x25\x07\x05\0\x02\0\0\0\
+\x0f\x61\0\0\0\0\0\0\x07\x08\0\0\x08\0\0\0\x07\x07\0\0\x01\0\0\0\xbf\x16\0\0\0\
+\0\0\0\x2d\x72\x02\0\0\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\xbf\x62\0\0\0\
+\0\0\0\x67\x02\0\0\x20\0\0\0\x77\x02\0\0\x20\0\0\0\xbf\xa3\0\0\0\0\0\0\x07\x03\
+\0\0\xc8\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x04\0\0\x18\0\0\0\
+\x85\0\0\0\x37\0\0\0\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x15\0\xe1\xff\0\0\
+\0\0\x05\0\xf1\xff\0\0\0\0\x85\0\0\0\x08\0\0\0\xbf\x06\0\0\0\0\0\0\xb7\x08\0\0\
+\0\0\0\0\x63\x8a\x94\xff\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x11\0\
+\0\0\0\0\0\x15\x01\x1c\0\0\0\0\0\xbf\xa7\0\0\0\0\0\0\x07\x07\0\0\x98\xff\xff\
+\xff\xb7\x09\0\0\0\0\0\0\x05\0\x08\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x61\x11\0\0\0\0\0\0\x25\x09\x14\0\x02\0\0\0\x07\x09\0\0\x01\0\0\0\x07\x08\0\
+\0\x01\0\0\0\x07\x07\0\0\x18\0\0\0\x3d\x19\x10\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x61\x12\0\0\0\0\0\0\x2f\x82\0\0\0\0\0\0\x0f\x62\0\0\0\0\0\0\x67\
+\x02\0\0\x20\0\0\0\x77\x02\0\0\x20\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\xbf\x73\0\0\0\0\0\0\xb7\x04\0\0\x18\0\0\0\x85\0\0\0\x37\0\0\0\x67\0\0\0\x20\0\
+\0\0\x77\0\0\0\x20\0\0\0\x15\0\xe9\xff\0\0\0\0\x05\0\x1a\0\0\0\0\0\xbf\xa2\0\0\
+\0\0\0\0\x07\x02\0\0\x94\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\
+\0\0\x01\0\0\0\x15\0\x14\0\0\0\0\0\x79\x01\0\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\
+\x7b\x10\0\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x11\0\0\0\0\0\0\
+\x15\x01\x0d\0\0\0\0\0\xb7\x07\0\0\0\0\0\0\xbf\xa8\0\0\0\0\0\0\x07\x08\0\0\xa8\
+\xff\xff\xff\xb7\x09\0\0\0\0\0\0\x05\0\x0a\0\0\0\0\0\x18\x01\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\x61\x11\0\0\0\0\0\0\x25\x09\x04\0\x02\0\0\0\x07\x07\0\0\x01\0\0\0\
+\x07\x08\0\0\x18\0\0\0\x07\x09\0\0\x01\0\0\0\x2d\x91\x02\0\0\0\0\0\xb7\0\0\0\0\
+\0\0\0\x95\0\0\0\0\0\0\0\x63\x7a\xfc\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\
+\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\x15\
+\0\xef\xff\0\0\0\0\x79\x01\0\0\0\0\0\0\x15\x01\xed\xff\0\0\0\0\x79\x02\x10\0\0\
+\0\0\0\x7b\x2a\x70\xff\0\0\0\0\x7b\x1a\x88\xff\0\0\0\0\x79\x81\0\0\0\0\0\0\x7b\
+\x1a\x80\xff\0\0\0\0\x79\x01\x08\0\0\0\0\0\x7b\x1a\x68\xff\0\0\0\0\x79\x81\xf8\
+\xff\0\0\0\0\x7b\x1a\x78\xff\0\0\0\0\x79\x86\xf0\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\
+\0\x07\x02\0\0\xfc\xff\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\
+\x01\0\0\0\x79\xa1\x88\xff\0\0\0\0\x15\0\xdc\xff\0\0\0\0\x79\xa2\x80\xff\0\0\0\
+\0\x79\xa3\x70\xff\0\0\0\0\x1f\x32\0\0\0\0\0\0\x79\xa3\x78\xff\0\0\0\0\x79\xa4\
+\x68\xff\0\0\0\0\x1f\x43\0\0\0\0\0\0\x1f\x16\0\0\0\0\0\0\x79\x01\0\0\0\0\0\0\
+\x0f\x16\0\0\0\0\0\0\x7b\x60\0\0\0\0\0\0\x79\x01\x08\0\0\0\0\0\x0f\x31\0\0\0\0\
+\0\0\x7b\x10\x08\0\0\0\0\0\x79\x01\x10\0\0\0\0\0\x0f\x21\0\0\0\0\0\0\x7b\x10\
+\x10\0\0\0\0\0\x3d\x32\xcb\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xfc\xff\
+\xff\xff\x18\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x01\0\0\0\x15\0\xc5\xff\
+\0\0\0\0\x79\x01\0\0\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x10\0\0\0\0\0\0\x05\0\
+\xc1\xff\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x6c\x61\x6e\
+\x67\x20\x76\x65\x72\x73\x69\x6f\x6e\x20\x31\x31\x2e\x30\x2e\x30\x20\x28\x68\
+\x74\x74\x70\x73\x3a\x2f\x2f\x67\x69\x74\x68\x75\x62\x2e\x63\x6f\x6d\x2f\x6c\
+\x6c\x76\x6d\x2f\x6c\x6c\x76\x6d\x2d\x70\x72\x6f\x6a\x65\x63\x74\x2e\x67\x69\
+\x74\x20\x61\x61\x62\x63\x33\x63\x35\x39\x65\x31\x33\x31\x61\x61\x30\x39\x63\
+\x37\x35\x35\x64\x38\x31\x66\x63\x31\x37\x31\x36\x64\x31\x64\x34\x38\x33\x33\
+\x64\x35\x32\x63\x29\0\x73\x6b\x65\x6c\x65\x74\x6f\x6e\x2f\x70\x72\x6f\x66\x69\
+\x6c\x65\x72\x2e\x62\x70\x66\x2e\x63\0\x2f\x68\x6f\x6d\x65\x2f\x73\x6f\x6e\x67\
+\x6c\x69\x75\x62\x72\x61\x76\x69\x6e\x67\x2f\x6c\x6f\x63\x61\x6c\x2f\x6b\x65\
+\x72\x6e\x65\x6c\x2f\x6c\x69\x6e\x75\x78\x2d\x67\x69\x74\x2f\x74\x6f\x6f\x6c\
+\x73\x2f\x62\x70\x66\x2f\x62\x70\x66\x74\x6f\x6f\x6c\0\x6e\x75\x6d\x5f\x63\x70\
+\x75\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x5f\x5f\x75\x33\x32\0\
+\x6e\x75\x6d\x5f\x6d\x65\x74\x72\x69\x63\0\x4c\x49\x43\x45\x4e\x53\x45\0\x63\
+\x68\x61\x72\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\
+\x45\x5f\x5f\0\x65\x76\x65\x6e\x74\x73\0\x74\x79\x70\x65\0\x69\x6e\x74\0\x6b\
+\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x66\
+\x65\x6e\x74\x72\x79\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\0\x61\x63\x63\x75\x6d\
+\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\0\x63\x6f\x75\x6e\x74\x73\0\x6d\x69\x73\
+\x73\x5f\x63\x6f\x75\x6e\x74\x73\0\x62\x70\x66\x5f\x67\x65\x74\x5f\x73\x6d\x70\
+\x5f\x70\x72\x6f\x63\x65\x73\x73\x6f\x72\x5f\x69\x64\0\x62\x70\x66\x5f\x6d\x61\
+\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\0\x62\x70\x66\x5f\x70\x65\
+\x72\x66\x5f\x65\x76\x65\x6e\x74\x5f\x72\x65\x61\x64\x5f\x76\x61\x6c\x75\x65\0\
+\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\x20\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\
+\x69\x6e\x74\0\x5f\x5f\x75\x36\x34\0\x63\x6f\x75\x6e\x74\x65\x72\0\x65\x6e\x61\
+\x62\x6c\x65\x64\0\x72\x75\x6e\x6e\x69\x6e\x67\0\x62\x70\x66\x5f\x70\x65\x72\
+\x66\x5f\x65\x76\x65\x6e\x74\x5f\x76\x61\x6c\x75\x65\0\x5f\x5f\x5f\x5f\x66\x65\
+\x6e\x74\x72\x79\x5f\x58\x58\x58\0\x63\x74\x78\0\x70\x74\x72\x73\0\x69\0\x75\
+\x33\x32\0\x6b\x65\x79\0\x66\x6c\x61\x67\0\x72\x65\x61\x64\x69\x6e\x67\0\x65\
+\x72\x72\0\x5f\x5f\x5f\x5f\x66\x65\x78\x69\x74\x5f\x58\x58\x58\0\x72\x65\x61\
+\x64\x69\x6e\x67\x73\0\x6f\x6e\x65\0\x63\x70\x75\0\x7a\x65\x72\x6f\0\x63\x6f\
+\x75\x6e\x74\0\x75\x36\x34\0\x66\x65\x78\x69\x74\x5f\x75\x70\x64\x61\x74\x65\
+\x5f\x6d\x61\x70\x73\0\x69\x64\0\x61\x66\x74\x65\x72\0\x62\x65\x66\x6f\x72\x65\
+\0\x64\x69\x66\x66\0\x61\x63\x63\x75\x6d\0\x6d\x69\x73\x73\x5f\x63\x6f\x75\x6e\
+\x74\0\x66\x65\x6e\x74\x72\x79\x5f\x58\x58\x58\0\x66\x65\x78\x69\x74\x5f\x58\
+\x58\x58\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\
+\x50\0\0\0\0\0\0\0\x02\0\x30\x9f\x50\0\0\0\0\0\0\0\x88\0\0\0\0\0\0\0\x01\0\x57\
+\xb0\0\0\0\0\0\0\0\xd0\0\0\0\0\0\0\0\x01\0\x57\xd0\0\0\0\0\0\0\0\x20\x01\0\0\0\
+\0\0\0\x02\0\x30\x9f\x20\x01\0\0\0\0\0\0\x70\x01\0\0\0\0\0\0\x01\0\x57\x80\x01\
+\0\0\0\0\0\0\xa8\x01\0\0\0\0\0\0\x01\0\x57\xb8\x01\0\0\0\0\0\0\x20\x02\0\0\0\0\
+\0\0\x01\0\x57\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\
+\0\0\0\0\0\0\0\0\xd0\0\0\0\0\0\0\0\x70\x01\0\0\0\0\0\0\x01\0\x56\x78\x01\0\0\0\
+\0\0\0\xa0\x01\0\0\0\0\0\0\x01\0\x56\xb8\x01\0\0\0\0\0\0\x20\x02\0\0\0\0\0\0\
+\x01\0\x56\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\
+\0\0\0\0\0\0\x50\0\0\0\0\0\0\0\x58\0\0\0\0\0\0\0\x01\0\x57\x58\0\0\0\0\0\0\0\
+\x90\0\0\0\0\0\0\0\x02\0\x7a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\
+\xff\xff\xff\xff\0\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\x01\0\
+\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\
+\0\0\0\x10\0\0\0\0\0\0\0\xf0\x01\0\0\0\0\0\0\x02\0\x31\x9f\0\x02\0\0\0\0\0\0\
+\xa8\x03\0\0\0\0\0\0\x02\0\x31\x9f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\
+\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0\x20\x01\0\0\0\0\0\0\x02\
+\0\x30\x9f\x20\x01\0\0\0\0\0\0\xf0\x01\0\0\0\0\0\0\x02\0\x7a\x2c\0\x02\0\0\0\0\
+\0\0\xa8\x03\0\0\0\0\0\0\x02\0\x7a\x2c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\
+\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x60\0\0\0\0\0\0\0\
+\x02\0\x30\x9f\x60\0\0\0\0\0\0\0\x88\0\0\0\0\0\0\0\x01\0\x59\xa0\0\0\0\0\0\0\0\
+\0\x01\0\0\0\0\0\0\x01\0\x59\x68\x01\0\0\0\0\0\0\xb0\x01\0\0\0\0\0\0\x02\0\x30\
+\x9f\xb0\x01\0\0\0\0\0\0\xc8\x01\0\0\0\0\0\0\x01\0\x59\xd0\x01\0\0\0\0\0\0\xf0\
+\x01\0\0\0\0\0\0\x01\0\x59\0\x02\0\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\x01\0\x59\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\0\
+\x01\0\0\0\0\0\0\x08\x01\0\0\0\0\0\0\x01\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x48\x01\0\0\0\0\0\0\xb0\x01\0\
+\0\0\0\0\0\x01\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\
+\xff\xff\0\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\x01\0\x59\x08\
+\x02\0\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\x03\0\x7a\x94\x01\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x30\x02\0\0\0\0\0\0\
+\xc0\x02\0\0\0\0\0\0\x01\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\
+\xff\xff\xff\xff\0\0\0\0\0\0\0\0\xe8\x02\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\x05\0\
+\x93\x10\x52\x93\x08\0\x03\0\0\0\0\0\0\x08\x03\0\0\0\0\0\0\x08\0\x93\x08\x53\
+\x93\x08\x52\x93\x08\x08\x03\0\0\0\0\0\0\x18\x03\0\0\0\0\0\0\x09\0\x56\x93\x08\
+\x53\x93\x08\x52\x93\x08\x18\x03\0\0\0\0\0\0\x60\x03\0\0\0\0\0\0\x08\0\x93\x08\
+\x53\x93\x08\x52\x93\x08\x60\x03\0\0\0\0\0\0\x80\x03\0\0\0\0\0\0\x05\0\x93\x08\
+\x53\x93\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\
+\0\0\0\0\0\0\0\xc8\x02\0\0\0\0\0\0\x80\x03\0\0\0\0\0\0\x01\0\x50\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff\0\0\0\0\0\0\0\0\x80\x03\0\0\
+\0\0\0\0\xa8\x03\0\0\0\0\0\0\x01\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x11\
+\x01\x25\x0e\x13\x05\x03\x0e\x10\x17\x1b\x0e\x11\x01\x55\x17\0\0\x02\x34\0\x03\
+\x0e\x49\x13\x3f\x19\x3a\x0b\x3b\x0b\x02\x18\0\0\x03\x26\0\x49\x13\0\0\x04\x35\
+\0\x49\x13\0\0\x05\x16\0\x49\x13\x03\x0e\x3a\x0b\x3b\x0b\0\0\x06\x24\0\x03\x0e\
+\x3e\x0b\x0b\x0b\0\0\x07\x01\x01\x49\x13\0\0\x08\x21\0\x49\x13\x37\x0b\0\0\x09\
+\x24\0\x03\x0e\x0b\x0b\x3e\x0b\0\0\x0a\x13\x01\x0b\x0b\x3a\x0b\x3b\x0b\0\0\x0b\
+\x0d\0\x03\x0e\x49\x13\x3a\x0b\x3b\x0b\x38\x0b\0\0\x0c\x0f\0\x49\x13\0\0\x0d\
+\x34\0\x03\x0e\x49\x13\x3a\x0b\x3b\x0b\0\0\x0e\x15\0\x49\x13\x27\x19\0\0\x0f\
+\x15\x01\x49\x13\x27\x19\0\0\x10\x05\0\x49\x13\0\0\x11\x0f\0\0\0\x12\x26\0\0\0\
+\x13\x34\0\x03\x0e\x49\x13\x3a\x0b\x3b\x05\0\0\x14\x13\x01\x03\x0e\x0b\x0b\x3a\
+\x0b\x3b\x05\0\0\x15\x0d\0\x03\x0e\x49\x13\x3a\x0b\x3b\x05\x38\x0b\0\0\x16\x2e\
+\x01\x03\x0e\x3a\x0b\x3b\x0b\x27\x19\x49\x13\x20\x0b\0\0\x17\x05\0\x03\x0e\x3a\
+\x0b\x3b\x0b\x49\x13\0\0\x18\x34\0\x03\x0e\x3a\x0b\x3b\x0b\x49\x13\0\0\x19\x0b\
+\x01\0\0\x1a\x2e\x01\x11\x01\x12\x06\x40\x18\x97\x42\x19\x03\x0e\x3a\x0b\x3b\
+\x0b\x27\x19\x49\x13\x3f\x19\0\0\x1b\x1d\x01\x31\x13\x55\x17\x58\x0b\x59\x0b\
+\x57\x0b\0\0\x1c\x05\0\x31\x13\0\0\x1d\x34\0\x02\x18\x31\x13\0\0\x1e\x34\0\x02\
+\x17\x31\x13\0\0\x1f\x0b\x01\x55\x17\0\0\x20\x2e\x01\x03\x0e\x3a\x0b\x3b\x0b\
+\x27\x19\x20\x0b\0\0\x21\x34\0\x31\x13\0\0\x22\x1d\x01\x31\x13\x11\x01\x12\x06\
+\x58\x0b\x59\x0b\x57\x0b\0\0\x23\x05\0\x02\x17\x31\x13\0\0\x24\x0b\x01\x11\x01\
+\x12\x06\0\0\0\x7a\x05\0\0\x04\0\0\0\0\0\x08\x01\0\0\0\0\x0c\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\x02\0\0\0\0\x3f\0\0\0\x02\x5f\x09\x03\0\0\0\
+\0\0\0\0\0\x03\x44\0\0\0\x04\x49\0\0\0\x05\x54\0\0\0\0\0\0\0\x01\x1a\x06\0\0\0\
+\0\x07\x04\x02\0\0\0\0\x3f\0\0\0\x02\x60\x09\x03\0\0\0\0\0\0\0\0\x02\0\0\0\0\
+\x85\0\0\0\x02\xb9\x09\x03\0\0\0\0\0\0\0\0\x07\x91\0\0\0\x08\x98\0\0\0\x04\0\
+\x06\0\0\0\0\x06\x01\x09\0\0\0\0\x08\x07\x02\0\0\0\0\xb4\0\0\0\x02\x41\x09\x03\
+\0\0\0\0\0\0\0\0\x0a\x18\x02\x3d\x0b\0\0\0\0\xdd\0\0\0\x02\x3e\0\x0b\0\0\0\0\
+\xdd\0\0\0\x02\x3f\x08\x0b\0\0\0\0\xdd\0\0\0\x02\x40\x10\0\x0c\xe2\0\0\0\x07\
+\xee\0\0\0\x08\x98\0\0\0\x04\0\x06\0\0\0\0\x05\x04\x02\0\0\0\0\x0a\x01\0\0\x02\
+\x48\x09\x03\0\0\0\0\0\0\0\0\x0a\x18\x02\x44\x0b\0\0\0\0\x33\x01\0\0\x02\x45\0\
+\x0b\0\0\0\0\xdd\0\0\0\x02\x46\x08\x0b\0\0\0\0\x44\x01\0\0\x02\x47\x10\0\x0c\
+\x38\x01\0\0\x07\xee\0\0\0\x08\x98\0\0\0\x06\0\x0c\x49\x01\0\0\x07\xee\0\0\0\
+\x08\x98\0\0\0\x18\0\x02\0\0\0\0\x6a\x01\0\0\x02\x4f\x09\x03\0\0\0\0\0\0\0\0\
+\x0a\x18\x02\x4b\x0b\0\0\0\0\x33\x01\0\0\x02\x4c\0\x0b\0\0\0\0\xdd\0\0\0\x02\
+\x4d\x08\x0b\0\0\0\0\x44\x01\0\0\x02\x4e\x10\0\x02\0\0\0\0\xa8\x01\0\0\x02\x56\
+\x09\x03\0\0\0\0\0\0\0\0\x0a\x18\x02\x52\x0b\0\0\0\0\x33\x01\0\0\x02\x53\0\x0b\
+\0\0\0\0\xdd\0\0\0\x02\x54\x08\x0b\0\0\0\0\xd1\x01\0\0\x02\x55\x10\0\x0c\xd6\
+\x01\0\0\x07\xee\0\0\0\x08\x98\0\0\0\x08\0\x02\0\0\0\0\xf7\x01\0\0\x02\x5d\x09\
+\x03\0\0\0\0\0\0\0\0\x0a\x18\x02\x59\x0b\0\0\0\0\x33\x01\0\0\x02\x5a\0\x0b\0\0\
+\0\0\xdd\0\0\0\x02\x5b\x08\x0b\0\0\0\0\xd1\x01\0\0\x02\x5c\x10\0\x0d\0\0\0\0\
+\x2b\x02\0\0\x03\xb5\x0c\x30\x02\0\0\x0e\x49\0\0\0\x0d\0\0\0\0\x40\x02\0\0\x03\
+\x21\x0c\x45\x02\0\0\x0f\x55\x02\0\0\x10\x55\x02\0\0\x10\x56\x02\0\0\0\x11\x0c\
+\x5b\x02\0\0\x12\x13\0\0\0\0\x68\x02\0\0\x03\x63\x05\x0c\x6d\x02\0\0\x0f\xee\0\
+\0\0\x10\x55\x02\0\0\x10\x87\x02\0\0\x10\x99\x02\0\0\x10\x49\0\0\0\0\x05\x92\
+\x02\0\0\0\0\0\0\x01\x1e\x06\0\0\0\0\x07\x08\x0c\x9e\x02\0\0\x14\0\0\0\0\x18\
+\x04\xf2\x03\x15\0\0\0\0\x87\x02\0\0\x04\xf3\x03\0\x15\0\0\0\0\x87\x02\0\0\x04\
+\xf4\x03\x08\x15\0\0\0\0\x87\x02\0\0\x04\xf5\x03\x10\0\x16\0\0\0\0\x02\x64\xee\
+\0\0\0\x01\x17\0\0\0\0\x02\x64\x2d\x03\0\0\x18\0\0\0\0\x02\x66\x32\x03\0\0\x18\
+\0\0\0\0\x02\x68\x3e\x03\0\0\x18\0\0\0\0\x02\x67\x3e\x03\0\0\x19\x18\0\0\0\0\
+\x02\x6c\x3e\x03\0\0\0\x19\x18\0\0\0\0\x02\x74\x9e\x02\0\0\x18\0\0\0\0\x02\x75\
+\xee\0\0\0\0\0\x0c\x92\x02\0\0\x07\x99\x02\0\0\x08\x98\0\0\0\x04\0\x05\x49\x03\
+\0\0\0\0\0\0\x05\x15\x05\x54\0\0\0\0\0\0\0\x05\x0c\x1a\0\0\0\0\0\0\0\0\x20\x02\
+\0\0\x01\x5a\0\0\0\0\x02\x64\xee\0\0\0\x17\0\0\0\0\x02\x64\x2d\x03\0\0\x1b\xcf\
+\x02\0\0\0\0\0\0\x02\x64\x05\x1c\xdb\x02\0\0\x1d\x02\x91\x18\xe6\x02\0\0\x1e\0\
+\0\0\0\xf1\x02\0\0\x1e\xa7\0\0\0\xfc\x02\0\0\x1f\x30\0\0\0\x1e\0\x01\0\0\x08\
+\x03\0\0\0\x1f\x60\0\0\0\x1d\x02\x91\0\x15\x03\0\0\x1e\x47\x01\0\0\x20\x03\0\0\
+\0\0\0\x16\0\0\0\0\x02\xa1\xee\0\0\0\x01\x17\0\0\0\0\x02\xa1\x2d\x03\0\0\x18\0\
+\0\0\0\x02\xa3\x30\x04\0\0\x18\0\0\0\0\x02\xa5\x3e\x03\0\0\x18\0\0\0\0\x02\xa4\
+\x3e\x03\0\0\x18\0\0\0\0\x02\xa5\x3e\x03\0\0\x18\0\0\0\0\x02\xa5\x3e\x03\0\0\
+\x18\0\0\0\0\x02\xa6\xee\0\0\0\x18\0\0\0\0\x02\xa7\x3c\x04\0\0\0\x07\x9e\x02\0\
+\0\x08\x98\0\0\0\x04\0\x0c\x41\x04\0\0\x05\x4c\x04\0\0\0\0\0\0\x05\x17\x05\x92\
+\x02\0\0\0\0\0\0\x05\x0e\x20\0\0\0\0\x02\x83\x01\x17\0\0\0\0\x02\x83\x3e\x03\0\
+\0\x17\0\0\0\0\x02\x83\x99\x02\0\0\x18\0\0\0\0\x02\x85\x99\x02\0\0\x18\0\0\0\0\
+\x02\x85\x9e\x02\0\0\x18\0\0\0\0\x02\x85\x99\x02\0\0\x19\x18\0\0\0\0\x02\x8a\
+\x99\x02\0\0\x19\x18\0\0\0\0\x02\x96\x3c\x04\0\0\0\0\0\x1a\0\0\0\0\0\0\0\0\xa8\
+\x03\0\0\x01\x5a\0\0\0\0\x02\xa1\xee\0\0\0\x17\0\0\0\0\x02\xa1\x2d\x03\0\0\x1b\
+\xcb\x03\0\0\xa0\0\0\0\x02\xa1\x05\x1c\xd7\x03\0\0\x1d\x02\x91\x30\xe2\x03\0\0\
+\x1e\x7a\x01\0\0\xed\x03\0\0\x21\xf8\x03\0\0\x1e\xc2\x01\0\0\x03\x04\0\0\x1e\
+\x1e\x02\0\0\x0e\x04\0\0\x1e\xc5\x02\0\0\x19\x04\0\0\x1e\xf8\x02\0\0\x24\x04\0\
+\0\x22\x57\x04\0\0\x18\x02\0\0\0\0\0\0\x90\x01\0\0\x02\xb4\x04\x23\x2b\x03\0\0\
+\x5f\x04\0\0\x1c\x6a\x04\0\0\x1e\x73\x03\0\0\x75\x04\0\0\x1e\xa6\x03\0\0\x80\
+\x04\0\0\x1f\xd0\0\0\0\x1e\x43\x04\0\0\x97\x04\0\0\x24\x68\x03\0\0\0\0\0\0\x40\
+\0\0\0\x1e\x76\x04\0\0\xa3\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa8\x01\0\0\0\0\
+\0\0\xb8\x01\0\0\0\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x50\0\0\0\0\0\0\0\x60\0\0\0\0\0\0\0\x68\0\0\0\0\0\0\0\x90\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x01\0\0\0\0\0\0\x70\x01\0\0\0\0\0\0\xb8\x01\0\0\
+\0\0\0\0\xd8\x01\0\0\0\0\0\0\xe0\x01\0\0\0\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\x01\0\0\0\0\0\0\x10\x02\0\0\0\0\0\0\
+\xa8\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\x02\0\0\0\0\0\0\x58\
+\x03\0\0\0\0\0\0\x68\x03\0\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa8\x03\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\x18\0\0\0\0\0\0\0\x5c\x03\0\
+\0\x5c\x03\0\0\xc8\x05\0\0\0\0\0\0\x03\0\0\x04\x18\0\0\0\x01\0\0\0\x02\0\0\0\0\
+\0\0\0\x06\0\0\0\x02\0\0\0\x40\0\0\0\x0f\0\0\0\x02\0\0\0\x80\0\0\0\0\0\0\0\0\0\
+\0\x02\x04\0\0\0\x1a\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\
+\0\0\0\x03\0\0\0\x05\0\0\0\x04\0\0\0\x1e\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\
+\x32\0\0\0\0\0\0\x0e\x01\0\0\0\x01\0\0\0\0\0\0\0\x03\0\0\x04\x18\0\0\0\x01\0\0\
+\0\x08\0\0\0\0\0\0\0\x06\0\0\0\x02\0\0\0\x40\0\0\0\x0f\0\0\0\x0a\0\0\0\x80\0\0\
+\0\0\0\0\0\0\0\0\x02\x09\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x03\0\0\0\x05\0\0\0\
+\x06\0\0\0\0\0\0\0\0\0\0\x02\x0b\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x03\0\0\0\x05\
+\0\0\0\x18\0\0\0\x39\0\0\0\0\0\0\x0e\x07\0\0\0\x01\0\0\0\0\0\0\0\x03\0\0\x04\
+\x18\0\0\0\x01\0\0\0\x08\0\0\0\0\0\0\0\x06\0\0\0\x02\0\0\0\x40\0\0\0\x0f\0\0\0\
+\x0a\0\0\0\x80\0\0\0\x49\0\0\0\0\0\0\x0e\x0d\0\0\0\x01\0\0\0\0\0\0\0\x03\0\0\
+\x04\x18\0\0\0\x01\0\0\0\x08\0\0\0\0\0\0\0\x06\0\0\0\x02\0\0\0\x40\0\0\0\x0f\0\
+\0\0\x10\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x11\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\
+\x03\0\0\0\x05\0\0\0\x08\0\0\0\x58\0\0\0\0\0\0\x0e\x0f\0\0\0\x01\0\0\0\0\0\0\0\
+\x03\0\0\x04\x18\0\0\0\x01\0\0\0\x08\0\0\0\0\0\0\0\x06\0\0\0\x02\0\0\0\x40\0\0\
+\0\x0f\0\0\0\x10\0\0\0\x80\0\0\0\x5f\0\0\0\0\0\0\x0e\x13\0\0\0\x01\0\0\0\0\0\0\
+\0\0\0\0\x02\x16\0\0\0\x6b\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\
+\x0d\x03\0\0\0\x82\0\0\0\x15\0\0\0\x86\0\0\0\x01\0\0\x0c\x17\0\0\0\0\0\0\0\x01\
+\0\0\x0d\x03\0\0\0\x82\0\0\0\x15\0\0\0\x26\x02\0\0\x01\0\0\x0c\x19\0\0\0\0\0\0\
+\0\0\0\0\x0a\x1c\0\0\0\0\0\0\0\0\0\0\x09\x1d\0\0\0\x7f\x05\0\0\0\0\0\x08\x1e\0\
+\0\0\x85\x05\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\x92\x05\0\0\0\0\0\x0e\x1b\0\0\0\
+\x01\0\0\0\x9a\x05\0\0\0\0\0\x0e\x1b\0\0\0\x01\0\0\0\xa5\x05\0\0\0\0\0\x01\x01\
+\0\0\0\x08\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x21\0\0\0\x05\0\0\0\x04\0\0\0\xaa\
+\x05\0\0\0\0\0\x0e\x22\0\0\0\x01\0\0\0\xb2\x05\0\0\x05\0\0\x0f\0\0\0\0\x06\0\0\
+\0\0\0\0\0\x18\0\0\0\x0c\0\0\0\0\0\0\0\x18\0\0\0\x0e\0\0\0\0\0\0\0\x18\0\0\0\
+\x12\0\0\0\0\0\0\0\x18\0\0\0\x14\0\0\0\0\0\0\0\x18\0\0\0\xb8\x05\0\0\x02\0\0\
+\x0f\0\0\0\0\x1f\0\0\0\0\0\0\0\x04\0\0\0\x20\0\0\0\0\0\0\0\x04\0\0\0\xc0\x05\0\
+\0\x01\0\0\x0f\0\0\0\0\x23\0\0\0\0\0\0\0\x04\0\0\0\0\x74\x79\x70\x65\0\x6b\x65\
+\x79\x5f\x73\x69\x7a\x65\0\x76\x61\x6c\x75\x65\x5f\x73\x69\x7a\x65\0\x69\x6e\
+\x74\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\
+\x5f\0\x65\x76\x65\x6e\x74\x73\0\x66\x65\x6e\x74\x72\x79\x5f\x72\x65\x61\x64\
+\x69\x6e\x67\x73\0\x61\x63\x63\x75\x6d\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\0\
+\x63\x6f\x75\x6e\x74\x73\0\x6d\x69\x73\x73\x5f\x63\x6f\x75\x6e\x74\x73\0\x6c\
+\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\x20\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\
+\x6e\x74\0\x63\x74\x78\0\x66\x65\x6e\x74\x72\x79\x5f\x58\x58\x58\0\x66\x65\x6e\
+\x74\x72\x79\x2f\x58\x58\x58\0\x2f\x68\x6f\x6d\x65\x2f\x73\x6f\x6e\x67\x6c\x69\
+\x75\x62\x72\x61\x76\x69\x6e\x67\x2f\x6c\x6f\x63\x61\x6c\x2f\x6b\x65\x72\x6e\
+\x65\x6c\x2f\x6c\x69\x6e\x75\x78\x2d\x67\x69\x74\x2f\x74\x6f\x6f\x6c\x73\x2f\
+\x62\x70\x66\x2f\x62\x70\x66\x74\x6f\x6f\x6c\x2f\x73\x6b\x65\x6c\x65\x74\x6f\
+\x6e\x2f\x70\x72\x6f\x66\x69\x6c\x65\x72\x2e\x62\x70\x66\x2e\x63\0\x09\x75\x33\
+\x32\x20\x6b\x65\x79\x20\x3d\x20\x62\x70\x66\x5f\x67\x65\x74\x5f\x73\x6d\x70\
+\x5f\x70\x72\x6f\x63\x65\x73\x73\x6f\x72\x5f\x69\x64\x28\x29\x3b\0\x09\x66\x6f\
+\x72\x20\x28\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x6e\x75\x6d\x5f\x6d\
+\x65\x74\x72\x69\x63\x20\x26\x26\x20\x69\x20\x3c\x20\x4d\x41\x58\x5f\x4e\x55\
+\x4d\x5f\x4d\x41\x54\x52\x49\x43\x53\x3b\x20\x69\x2b\x2b\x29\x20\x7b\0\x09\x09\
+\x75\x33\x32\x20\x66\x6c\x61\x67\x20\x3d\x20\x69\x3b\0\x09\x09\x70\x74\x72\x73\
+\x5b\x69\x5d\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\
+\x70\x5f\x65\x6c\x65\x6d\x28\x26\x66\x65\x6e\x74\x72\x79\x5f\x72\x65\x61\x64\
+\x69\x6e\x67\x73\x2c\x20\x26\x66\x6c\x61\x67\x29\x3b\0\x09\x09\x2a\x28\x70\x74\
+\x72\x73\x5b\x69\x5d\x29\x20\x3d\x20\x72\x65\x61\x64\x69\x6e\x67\x3b\0\x09\x09\
+\x6b\x65\x79\x20\x2b\x3d\x20\x6e\x75\x6d\x5f\x63\x70\x75\x3b\0\x69\x6e\x74\x20\
+\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x66\x65\x6e\x74\x72\x79\x5f\x58\x58\x58\
+\x29\0\x09\x09\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x70\x65\x72\x66\x5f\x65\
+\x76\x65\x6e\x74\x5f\x72\x65\x61\x64\x5f\x76\x61\x6c\x75\x65\x28\x26\x65\x76\
+\x65\x6e\x74\x73\x2c\x20\x6b\x65\x79\x2c\x20\x26\x72\x65\x61\x64\x69\x6e\x67\
+\x2c\0\x09\x09\x69\x66\x20\x28\x65\x72\x72\x29\0\x66\x65\x78\x69\x74\x5f\x58\
+\x58\x58\0\x66\x65\x78\x69\x74\x2f\x58\x58\x58\0\x09\x75\x33\x32\x20\x63\x70\
+\x75\x20\x3d\x20\x62\x70\x66\x5f\x67\x65\x74\x5f\x73\x6d\x70\x5f\x70\x72\x6f\
+\x63\x65\x73\x73\x6f\x72\x5f\x69\x64\x28\x29\x3b\0\x09\x75\x33\x32\x20\x69\x2c\
+\x20\x6f\x6e\x65\x20\x3d\x20\x31\x2c\x20\x7a\x65\x72\x6f\x20\x3d\x20\x30\x3b\0\
+\x09\x09\x65\x72\x72\x20\x3d\x20\x62\x70\x66\x5f\x70\x65\x72\x66\x5f\x65\x76\
+\x65\x6e\x74\x5f\x72\x65\x61\x64\x5f\x76\x61\x6c\x75\x65\x28\x26\x65\x76\x65\
+\x6e\x74\x73\x2c\x20\x63\x70\x75\x20\x2b\x20\x69\x20\x2a\x20\x6e\x75\x6d\x5f\
+\x63\x70\x75\x2c\0\x09\x63\x6f\x75\x6e\x74\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\
+\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x63\x6f\x75\x6e\
+\x74\x73\x2c\x20\x26\x7a\x65\x72\x6f\x29\x3b\0\x09\x69\x66\x20\x28\x63\x6f\x75\
+\x6e\x74\x29\x20\x7b\0\x09\x09\x2a\x63\x6f\x75\x6e\x74\x20\x2b\x3d\x20\x31\x3b\
+\0\x09\x09\x66\x6f\x72\x20\x28\x69\x20\x3d\x20\x30\x3b\x20\x69\x20\x3c\x20\x6e\
+\x75\x6d\x5f\x6d\x65\x74\x72\x69\x63\x20\x26\x26\x20\x69\x20\x3c\x20\x4d\x41\
+\x58\x5f\x4e\x55\x4d\x5f\x4d\x41\x54\x52\x49\x43\x53\x3b\x20\x69\x2b\x2b\x29\0\
+\x69\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x66\x65\x78\x69\x74\x5f\
+\x58\x58\x58\x29\0\x09\x62\x65\x66\x6f\x72\x65\x20\x3d\x20\x62\x70\x66\x5f\x6d\
+\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\x6c\x65\x6d\x28\x26\x66\x65\x6e\
+\x74\x72\x79\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\x2c\x20\x26\x69\x64\x29\x3b\0\
+\x09\x69\x66\x20\x28\x62\x65\x66\x6f\x72\x65\x20\x26\x26\x20\x62\x65\x66\x6f\
+\x72\x65\x2d\x3e\x63\x6f\x75\x6e\x74\x65\x72\x29\x20\x7b\0\x09\x09\x64\x69\x66\
+\x66\x2e\x72\x75\x6e\x6e\x69\x6e\x67\x20\x3d\x20\x61\x66\x74\x65\x72\x2d\x3e\
+\x72\x75\x6e\x6e\x69\x6e\x67\x20\x2d\x20\x62\x65\x66\x6f\x72\x65\x2d\x3e\x72\
+\x75\x6e\x6e\x69\x6e\x67\x3b\0\x09\x09\x64\x69\x66\x66\x2e\x65\x6e\x61\x62\x6c\
+\x65\x64\x20\x3d\x20\x61\x66\x74\x65\x72\x2d\x3e\x65\x6e\x61\x62\x6c\x65\x64\
+\x20\x2d\x20\x62\x65\x66\x6f\x72\x65\x2d\x3e\x65\x6e\x61\x62\x6c\x65\x64\x3b\0\
+\x09\x09\x64\x69\x66\x66\x2e\x63\x6f\x75\x6e\x74\x65\x72\x20\x3d\x20\x61\x66\
+\x74\x65\x72\x2d\x3e\x63\x6f\x75\x6e\x74\x65\x72\x20\x2d\x20\x62\x65\x66\x6f\
+\x72\x65\x2d\x3e\x63\x6f\x75\x6e\x74\x65\x72\x3b\0\x09\x09\x61\x63\x63\x75\x6d\
+\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\x70\x5f\x65\
+\x6c\x65\x6d\x28\x26\x61\x63\x63\x75\x6d\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\
+\x2c\x20\x26\x69\x64\x29\x3b\0\x09\x09\x69\x66\x20\x28\x61\x63\x63\x75\x6d\x29\
+\x20\x7b\0\x09\x09\x09\x61\x63\x63\x75\x6d\x2d\x3e\x63\x6f\x75\x6e\x74\x65\x72\
+\x20\x2b\x3d\x20\x64\x69\x66\x66\x2e\x63\x6f\x75\x6e\x74\x65\x72\x3b\0\x09\x09\
+\x09\x61\x63\x63\x75\x6d\x2d\x3e\x65\x6e\x61\x62\x6c\x65\x64\x20\x2b\x3d\x20\
+\x64\x69\x66\x66\x2e\x65\x6e\x61\x62\x6c\x65\x64\x3b\0\x09\x09\x09\x61\x63\x63\
+\x75\x6d\x2d\x3e\x72\x75\x6e\x6e\x69\x6e\x67\x20\x2b\x3d\x20\x64\x69\x66\x66\
+\x2e\x72\x75\x6e\x6e\x69\x6e\x67\x3b\0\x09\x09\x09\x69\x66\x20\x28\x64\x69\x66\
+\x66\x2e\x65\x6e\x61\x62\x6c\x65\x64\x20\x3e\x20\x64\x69\x66\x66\x2e\x72\x75\
+\x6e\x6e\x69\x6e\x67\x29\x20\x7b\0\x09\x09\x09\x09\x6d\x69\x73\x73\x5f\x63\x6f\
+\x75\x6e\x74\x20\x3d\x20\x62\x70\x66\x5f\x6d\x61\x70\x5f\x6c\x6f\x6f\x6b\x75\
+\x70\x5f\x65\x6c\x65\x6d\x28\x26\x6d\x69\x73\x73\x5f\x63\x6f\x75\x6e\x74\x73\
+\x2c\x20\x26\x69\x64\x29\x3b\0\x09\x09\x09\x09\x69\x66\x20\x28\x6d\x69\x73\x73\
+\x5f\x63\x6f\x75\x6e\x74\x29\0\x09\x09\x09\x09\x09\x2a\x6d\x69\x73\x73\x5f\x63\
+\x6f\x75\x6e\x74\x20\x2b\x3d\x20\x31\x3b\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\
+\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x6e\x75\x6d\x5f\x63\x70\x75\0\x6e\x75\
+\x6d\x5f\x6d\x65\x74\x72\x69\x63\0\x63\x68\x61\x72\0\x4c\x49\x43\x45\x4e\x53\
+\x45\0\x2e\x6d\x61\x70\x73\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\
+\x73\x65\0\x9f\xeb\x01\0\x20\0\0\0\0\0\0\0\x24\0\0\0\x24\0\0\0\xb4\x04\0\0\xd8\
+\x04\0\0\0\0\0\0\x08\0\0\0\x91\0\0\0\x01\0\0\0\0\0\0\0\x18\0\0\0\x30\x02\0\0\
+\x01\0\0\0\0\0\0\0\x1a\0\0\0\x10\0\0\0\x91\0\0\0\x1a\0\0\0\0\0\0\0\x9c\0\0\0\
+\xf2\0\0\0\x0c\x9c\x01\0\x10\0\0\0\x9c\0\0\0\x19\x01\0\0\x12\xac\x01\0\x28\0\0\
+\0\x9c\0\0\0\x19\x01\0\0\x02\xac\x01\0\x50\0\0\0\x9c\0\0\0\x54\x01\0\0\x07\xb0\
+\x01\0\x60\0\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\x68\0\0\0\x9c\0\0\0\x64\x01\0\0\x0d\
+\xb8\x01\0\x80\0\0\0\x9c\0\0\0\x64\x01\0\0\x0b\xb8\x01\0\x90\0\0\0\x9c\0\0\0\
+\x19\x01\0\0\x12\xac\x01\0\xa8\0\0\0\x9c\0\0\0\x19\x01\0\0\x02\xac\x01\0\xb0\0\
+\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\xb8\0\0\0\x9c\0\0\0\x19\x01\0\0\x02\xac\x01\0\
+\xd0\0\0\0\x9c\0\0\0\x19\x01\0\0\x12\xcc\x01\0\xe8\0\0\0\x9c\0\0\0\x19\x01\0\0\
+\x02\xcc\x01\0\0\x01\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\x20\x01\0\0\x9c\0\0\0\x9e\
+\x01\0\0\x05\xec\x01\0\x28\x01\0\0\x9c\0\0\0\x9e\x01\0\0\x10\xec\x01\0\x58\x01\
+\0\0\x9c\0\0\0\xb6\x01\0\0\x0a\xf0\x01\0\x70\x01\0\0\x9c\0\0\0\x19\x01\0\0\x12\
+\xcc\x01\0\x78\x01\0\0\x9c\0\0\0\x19\x01\0\0\x02\xcc\x01\0\x80\x01\0\0\x9c\0\0\
+\0\0\0\0\0\0\0\0\0\xa0\x01\0\0\x9c\0\0\0\x19\x01\0\0\x02\xcc\x01\0\xa8\x01\0\0\
+\x9c\0\0\0\xc8\x01\0\0\x05\x90\x01\0\xb8\x01\0\0\x9c\0\0\0\xe1\x01\0\0\x2c\xdc\
+\x01\0\xd8\x01\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\xe0\x01\0\0\x9c\0\0\0\xe1\x01\0\0\
+\x09\xdc\x01\0\x10\x02\0\0\x9c\0\0\0\x1b\x02\0\0\x07\xe4\x01\0\x30\x02\0\0\x30\
+\0\0\0\0\0\0\0\x9c\0\0\0\x3a\x02\0\0\x0c\x90\x02\0\x18\0\0\0\x9c\0\0\0\x61\x02\
+\0\0\x12\x94\x02\0\x20\0\0\0\x9c\0\0\0\x19\x01\0\0\x12\xa8\x02\0\x38\0\0\0\x9c\
+\0\0\0\x19\x01\0\0\x02\xa8\x02\0\x60\0\0\0\x9c\0\0\0\x19\x01\0\0\x12\xa8\x02\0\
+\x78\0\0\0\x9c\0\0\0\x19\x01\0\0\x02\xa8\x02\0\x80\0\0\0\x9c\0\0\0\0\0\0\0\0\0\
+\0\0\x88\0\0\0\x9c\0\0\0\x19\x01\0\0\x02\xa8\x02\0\xa0\0\0\0\x9c\0\0\0\x7c\x02\
+\0\0\x36\xac\x02\0\xb8\0\0\0\x9c\0\0\0\x7c\x02\0\0\x34\xac\x02\0\xc0\0\0\0\x9c\
+\0\0\0\x7c\x02\0\0\x30\xac\x02\0\xc8\0\0\0\x9c\0\0\0\x7c\x02\0\0\x2c\xac\x02\0\
+\xd8\0\0\0\x9c\0\0\0\x7c\x02\0\0\x09\xac\x02\0\x10\x01\0\0\x9c\0\0\0\x1b\x02\0\
+\0\x07\xb4\x02\0\x28\x01\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\x30\x01\0\0\x9c\0\0\0\
+\xba\x02\0\0\x0a\xc0\x02\0\x48\x01\0\0\x9c\0\0\0\xe8\x02\0\0\x06\xc4\x02\0\x50\
+\x01\0\0\x9c\0\0\0\xf6\x02\0\0\x0a\xc8\x02\0\x68\x01\0\0\x9c\0\0\0\x05\x03\0\0\
+\x13\xcc\x02\0\x80\x01\0\0\x9c\0\0\0\x05\x03\0\0\x03\xcc\x02\0\xb0\x01\0\0\x9c\
+\0\0\0\x05\x03\0\0\x13\xcc\x02\0\xc8\x01\0\0\x9c\0\0\0\x05\x03\0\0\x03\xcc\x02\
+\0\xd0\x01\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\xe8\x01\0\0\x9c\0\0\0\x05\x03\0\0\x03\
+\xcc\x02\0\xf0\x01\0\0\x9c\0\0\0\x3f\x03\0\0\x05\x84\x02\0\x10\x02\0\0\x9c\0\0\
+\0\0\0\0\0\0\0\0\0\x18\x02\0\0\x9c\0\0\0\x57\x03\0\0\x0b\x1c\x02\0\x30\x02\0\0\
+\x9c\0\0\0\x8d\x03\0\0\x0d\x24\x02\0\x38\x02\0\0\x9c\0\0\0\x8d\x03\0\0\x18\x24\
+\x02\0\x40\x02\0\0\x9c\0\0\0\x8d\x03\0\0\x06\x24\x02\0\x48\x02\0\0\x9c\0\0\0\
+\xaf\x03\0\0\x2b\x38\x02\0\x60\x02\0\0\x9c\0\0\0\xaf\x03\0\0\x19\x38\x02\0\x68\
+\x02\0\0\x9c\0\0\0\xe2\x03\0\0\x2b\x34\x02\0\x78\x02\0\0\x9c\0\0\0\xe2\x03\0\0\
+\x19\x34\x02\0\x88\x02\0\0\x9c\0\0\0\x15\x04\0\0\x19\x30\x02\0\xa0\x02\0\0\x9c\
+\0\0\0\xaf\x03\0\0\x2b\x38\x02\0\xa8\x02\0\0\x9c\0\0\0\x48\x04\0\0\x0b\x40\x02\
+\0\xc8\x02\0\0\x9c\0\0\0\x7d\x04\0\0\x07\x44\x02\0\xd8\x02\0\0\x9c\0\0\0\0\0\0\
+\0\0\0\0\0\0\x03\0\0\x9c\0\0\0\x15\x04\0\0\x21\x30\x02\0\x08\x03\0\0\x9c\0\0\0\
+\x8c\x04\0\0\x13\x48\x02\0\x20\x03\0\0\x9c\0\0\0\xaf\x04\0\0\x13\x4c\x02\0\x38\
+\x03\0\0\x9c\0\0\0\xd2\x04\0\0\x13\x50\x02\0\x50\x03\0\0\x9c\0\0\0\xf5\x04\0\0\
+\x08\x54\x02\0\x60\x03\0\0\x9c\0\0\0\0\0\0\0\0\0\0\0\x68\x03\0\0\x9c\0\0\0\x1b\
+\x05\0\0\x12\x60\x02\0\x80\x03\0\0\x9c\0\0\0\x54\x05\0\0\x09\x64\x02\0\x88\x03\
+\0\0\x9c\0\0\0\x68\x05\0\0\x12\x68\x02\0\x0c\0\0\0\xff\xff\xff\xff\x04\0\x08\0\
+\x08\x7c\x0b\0\x14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x02\0\0\0\0\0\0\x14\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\x71\x02\0\0\x04\0\xcf\0\0\0\x08\
+\x01\x01\xfb\x0e\x0d\0\x01\x01\x01\x01\0\0\0\x01\0\0\x01\x2f\x75\x73\x72\x2f\
+\x69\x6e\x63\x6c\x75\x64\x65\x2f\x61\x73\x6d\x2d\x67\x65\x6e\x65\x72\x69\x63\0\
+\x73\x6b\x65\x6c\x65\x74\x6f\x6e\0\x2f\x64\x61\x74\x61\x2f\x75\x73\x65\x72\x73\
+\x2f\x73\x6f\x6e\x67\x6c\x69\x75\x62\x72\x61\x76\x69\x6e\x67\x2f\x6b\x65\x72\
+\x6e\x65\x6c\x2f\x6c\x69\x6e\x75\x78\x2d\x67\x69\x74\x2f\x74\x6f\x6f\x6c\x73\
+\x2f\x6c\x69\x62\x2f\x62\x70\x66\0\x2f\x75\x73\x72\x2f\x69\x6e\x63\x6c\x75\x64\
+\x65\x2f\x6c\x69\x6e\x75\x78\0\0\x69\x6e\x74\x2d\x6c\x6c\x36\x34\x2e\x68\0\x01\
+\0\0\x70\x72\x6f\x66\x69\x6c\x65\x72\x2e\x62\x70\x66\x2e\x63\0\x02\0\0\x62\x70\
+\x66\x5f\x68\x65\x6c\x70\x65\x72\x5f\x64\x65\x66\x73\x2e\x68\0\x03\0\0\x62\x70\
+\x66\x2e\x68\0\x04\0\0\x70\x72\x6f\x66\x69\x6c\x65\x72\x2e\x68\0\x02\0\0\0\x04\
+\x02\0\x09\x02\0\0\0\0\0\0\0\0\x03\xe3\0\x01\x05\x0c\x0a\x15\x05\x12\x32\x05\
+\x02\x06\x3c\x03\x95\x7f\x20\x03\xeb\0\x20\x05\x07\x06\x3d\x06\x03\x94\x7f\x20\
+\x05\x0d\x06\x03\xee\0\x2e\x05\x0b\x06\x3c\x03\x92\x7f\x20\x05\x12\x06\x03\xeb\
+\0\x20\x05\x02\x06\x3c\x05\0\x03\x95\x7f\x20\x05\x02\x03\xeb\0\x20\x05\x12\x06\
+\x44\x05\x02\x06\x3c\x03\x8d\x7f\x20\x05\x05\x06\x03\xfb\0\x66\x05\x10\x06\x20\
+\x05\x0a\x06\x67\x05\x12\x03\x77\x3c\x05\x02\x06\x20\x05\0\x03\x8d\x7f\x20\x05\
+\x02\x03\xf3\0\x4a\x05\x05\x06\x03\x71\x20\x05\x2c\x03\x13\x2e\x05\0\x06\x03\
+\x89\x7f\x4a\x05\x09\x03\xf7\0\x20\x05\x07\x06\x68\x06\x03\x87\x7f\x20\x02\x01\
+\0\x01\x01\x04\x02\0\x09\x02\0\0\0\0\0\0\0\0\x03\xa0\x01\x01\x05\x0c\x0a\x15\
+\x06\x03\xdc\x7e\x2e\x05\x12\x06\x03\xa5\x01\x20\x25\x05\x02\x06\x3c\x03\xd6\
+\x7e\x20\x03\xaa\x01\x20\x05\x12\x3c\x05\x02\x3c\x05\0\x03\xd6\x7e\x20\x05\x02\
+\x03\xaa\x01\x20\x05\x36\x06\x3d\x05\x34\x06\x3c\x05\x30\x20\x05\x2c\x20\x05\
+\x09\x2e\x05\x07\x06\x76\x06\x03\xd3\x7e\x2e\x05\x0a\x06\x03\xb0\x01\x2e\x05\
+\x06\x3d\x05\x0a\x21\x05\x13\x3d\x05\x03\x06\x3c\x03\xcd\x7e\x20\x03\xb3\x01\
+\x20\x05\x13\x4a\x05\x03\x3c\x05\0\x03\xcd\x7e\x20\x05\x03\x03\xb3\x01\x3c\x05\
+\x05\x06\x03\x6e\x20\x06\x03\xdf\x7e\x2e\x05\x0b\x06\x03\x87\x01\x3c\x05\x0d\
+\x3e\x05\x18\x06\x20\x05\x06\x20\x05\x2b\x06\x25\x05\x19\x06\x3c\x05\x2b\x06\
+\x1f\x05\x19\x06\x2e\x06\x2d\x05\x2b\x3e\x05\x0b\x22\x06\x03\xf0\x7e\x3c\x05\
+\x07\x06\x03\x91\x01\x20\x06\x03\xef\x7e\x20\x05\x21\x06\x03\x8c\x01\x66\x05\
+\x13\x26\x3d\x3d\x05\x08\x3d\x06\x03\xeb\x7e\x20\x05\x12\x06\x03\x98\x01\x2e\
+\x05\x09\x3d\x05\x12\x21\x02\x04\0\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\xe0\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x69\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x81\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x0a\0\xbf\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xc7\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xd4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x0a\0\xda\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xe5\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xed\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x0a\0\xf2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x06\x01\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x0d\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\x0a\0\x12\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x16\x01\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x1f\x01\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\x0a\0\x2a\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\
+\x3a\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x49\x01\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x50\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x0a\0\x5c\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x75\x01\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x89\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x0a\0\xa3\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xba\x01\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xc0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x0a\0\xc8\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xd0\
+\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\xd8\x01\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x0a\0\xed\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\
+\0\xfc\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\0\x02\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x05\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x0a\0\x07\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x0b\x02\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x0f\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x0a\0\x14\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x1c\x02\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x0a\0\x2e\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x37\
+\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x3b\x02\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x0a\0\x3f\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\
+\0\x44\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x4a\x02\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x4e\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x0a\0\x60\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x63\x02\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x69\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\x0a\0\x70\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x75\x02\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x7b\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x0a\0\x86\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a\0\x91\
+\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x92\x01\0\0\0\0\x03\0\xa8\x01\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x8b\x01\0\0\0\0\x03\0\x50\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6e\x01\0\
+\0\0\0\x03\0\xd0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x58\x01\0\0\0\0\x03\0\xb8\x01\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x4a\x01\0\0\0\0\x03\0\x20\x01\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\x7c\x01\0\0\0\0\x05\0\xb0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5f\x01\0\0\0\
+\0\x05\0\xf0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x84\x01\0\0\0\0\x05\0\x60\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\x75\x01\0\0\0\0\x05\0\xa0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x67\x01\0\0\0\0\x05\0\x20\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x51\x01\0\0\0\0\x05\
+\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\x03\0\x05\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\
+\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x0d\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x03\0\x10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\
+\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\x18\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x42\x01\0\0\x11\0\x08\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\x53\0\0\
+\0\x11\0\x09\0\x30\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0\x2f\0\0\0\x11\0\x09\0\x48\0\
+\0\0\0\0\0\0\x18\0\0\0\0\0\0\0\x36\0\0\0\x11\0\x09\0\0\0\0\0\0\0\0\0\x18\0\0\0\
+\0\0\0\0\x07\x01\0\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x20\x02\0\0\0\0\0\0\x43\0\0\0\
+\x11\0\x09\0\x18\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0\x12\x01\0\0\x12\0\x05\0\0\0\0\
+\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\x2a\0\0\0\x11\0\x09\0\x60\0\0\0\0\0\0\0\x18\0\0\
+\0\0\0\0\0\x0f\0\0\0\x11\0\x07\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\xd5\0\0\0\
+\x11\0\x07\0\x04\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x01\0\0\0\
+\x52\0\0\0\x68\0\0\0\0\0\0\0\x01\0\0\0\x4e\0\0\0\x90\0\0\0\0\0\0\0\x01\0\0\0\
+\x52\0\0\0\xd0\0\0\0\0\0\0\0\x01\0\0\0\x52\0\0\0\x08\x01\0\0\0\0\0\0\x01\0\0\0\
+\x52\0\0\0\x58\x01\0\0\0\0\0\0\x01\0\0\0\x51\0\0\0\xe0\x01\0\0\0\0\0\0\x01\0\0\
+\0\x4c\0\0\0\x20\0\0\0\0\0\0\0\x01\0\0\0\x52\0\0\0\x60\0\0\0\0\0\0\0\x01\0\0\0\
+\x52\0\0\0\xa0\0\0\0\0\0\0\0\x01\0\0\0\x51\0\0\0\xd8\0\0\0\0\0\0\0\x01\0\0\0\
+\x4c\0\0\0\x30\x01\0\0\0\0\0\0\x01\0\0\0\x4b\0\0\0\x68\x01\0\0\0\0\0\0\x01\0\0\
+\0\x52\0\0\0\xb0\x01\0\0\0\0\0\0\x01\0\0\0\x52\0\0\0\x18\x02\0\0\0\0\0\0\x01\0\
+\0\0\x4e\0\0\0\xa8\x02\0\0\0\0\0\0\x01\0\0\0\x4a\0\0\0\x68\x03\0\0\0\0\0\0\x01\
+\0\0\0\x50\0\0\0\x08\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\xaf\0\0\0\0\0\0\0\x01\0\
+\0\0\x42\0\0\0\x08\x01\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x4f\x01\0\0\0\0\0\0\x01\
+\0\0\0\x42\0\0\0\x82\x01\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xca\x01\0\0\0\0\0\0\
+\x01\0\0\0\x43\0\0\0\x26\x02\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xcd\x02\0\0\0\0\0\
+\0\x01\0\0\0\x43\0\0\0\0\x03\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x33\x03\0\0\0\0\0\
+\0\x01\0\0\0\x43\0\0\0\x7b\x03\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xae\x03\0\0\0\0\
+\0\0\x01\0\0\0\x43\0\0\0\x4b\x04\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x7e\x04\0\0\0\
+\0\0\0\x01\0\0\0\x43\0\0\0\x06\0\0\0\0\0\0\0\x0a\0\0\0\x45\0\0\0\x0c\0\0\0\0\0\
+\0\0\x0a\0\0\0\x02\0\0\0\x12\0\0\0\0\0\0\0\x0a\0\0\0\x03\0\0\0\x16\0\0\0\0\0\0\
+\0\x0a\0\0\0\x48\0\0\0\x1a\0\0\0\0\0\0\0\x0a\0\0\0\x04\0\0\0\x26\0\0\0\0\0\0\0\
+\x0a\0\0\0\x46\0\0\0\x2b\0\0\0\0\0\0\0\x0a\0\0\0\x05\0\0\0\x37\0\0\0\0\0\0\0\
+\x01\0\0\0\x51\0\0\0\x4e\0\0\0\0\0\0\0\x0a\0\0\0\x07\0\0\0\x55\0\0\0\0\0\0\0\
+\x0a\0\0\0\x06\0\0\0\x5c\0\0\0\0\0\0\0\x0a\0\0\0\x08\0\0\0\x68\0\0\0\0\0\0\0\
+\x01\0\0\0\x52\0\0\0\x71\0\0\0\0\0\0\0\x0a\0\0\0\x09\0\0\0\x7d\0\0\0\0\0\0\0\
+\x01\0\0\0\x49\0\0\0\x92\0\0\0\0\0\0\0\x0a\0\0\0\x0a\0\0\0\x99\0\0\0\0\0\0\0\
+\x0a\0\0\0\x0b\0\0\0\xa0\0\0\0\0\0\0\0\x0a\0\0\0\x0c\0\0\0\xac\0\0\0\0\0\0\0\
+\x01\0\0\0\x4c\0\0\0\xb9\0\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\xc5\0\0\0\0\0\0\0\
+\x0a\0\0\0\x0f\0\0\0\xd1\0\0\0\0\0\0\0\x0a\0\0\0\x10\0\0\0\xef\0\0\0\0\0\0\0\
+\x0a\0\0\0\x0e\0\0\0\xf6\0\0\0\0\0\0\0\x0a\0\0\0\x11\0\0\0\x02\x01\0\0\0\0\0\0\
+\x01\0\0\0\x4e\0\0\0\x0f\x01\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x1b\x01\0\0\0\0\0\
+\0\x0a\0\0\0\x0f\0\0\0\x27\x01\0\0\0\0\0\0\x0a\0\0\0\x10\0\0\0\x56\x01\0\0\0\0\
+\0\0\x0a\0\0\0\x12\0\0\0\x62\x01\0\0\0\0\0\0\x01\0\0\0\x4a\0\0\0\x6f\x01\0\0\0\
+\0\0\0\x0a\0\0\0\x0d\0\0\0\x7b\x01\0\0\0\0\0\0\x0a\0\0\0\x0f\0\0\0\x87\x01\0\0\
+\0\0\0\0\x0a\0\0\0\x10\0\0\0\x94\x01\0\0\0\0\0\0\x0a\0\0\0\x13\0\0\0\xa0\x01\0\
+\0\0\0\0\0\x01\0\0\0\x4b\0\0\0\xad\x01\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\xb9\x01\
+\0\0\0\0\0\0\x0a\0\0\0\x0f\0\0\0\xc5\x01\0\0\0\0\0\0\x0a\0\0\0\x10\0\0\0\xe3\
+\x01\0\0\0\0\0\0\x0a\0\0\0\x14\0\0\0\xef\x01\0\0\0\0\0\0\x01\0\0\0\x50\0\0\0\
+\xfc\x01\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x08\x02\0\0\0\0\0\0\x0a\0\0\0\x0f\0\0\
+\0\x14\x02\0\0\0\0\0\0\x0a\0\0\0\x10\0\0\0\x21\x02\0\0\0\0\0\0\x0a\0\0\0\x15\0\
+\0\0\x36\x02\0\0\0\0\0\0\x0a\0\0\0\x16\0\0\0\x5d\x02\0\0\0\0\0\0\x0a\0\0\0\x17\
+\0\0\0\x8c\x02\0\0\0\0\0\0\x0a\0\0\0\x19\0\0\0\x93\x02\0\0\0\0\0\0\x0a\0\0\0\
+\x18\0\0\0\x9f\x02\0\0\0\0\0\0\x0a\0\0\0\x1d\0\0\0\xa8\x02\0\0\0\0\0\0\x0a\0\0\
+\0\x1a\0\0\0\xb5\x02\0\0\0\0\0\0\x0a\0\0\0\x1b\0\0\0\xc2\x02\0\0\0\0\0\0\x0a\0\
+\0\0\x1c\0\0\0\xd0\x02\0\0\0\0\0\0\x0a\0\0\0\x1e\0\0\0\xdc\x02\0\0\0\0\0\0\x0a\
+\0\0\0\x1f\0\0\0\xe7\x02\0\0\0\0\0\0\x0a\0\0\0\x20\0\0\0\xf2\x02\0\0\0\0\0\0\
+\x0a\0\0\0\x21\0\0\0\xfd\x02\0\0\0\0\0\0\x0a\0\0\0\x23\0\0\0\x09\x03\0\0\0\0\0\
+\0\x0a\0\0\0\x24\0\0\0\x16\x03\0\0\0\0\0\0\x0a\0\0\0\x25\0\0\0\x21\x03\0\0\0\0\
+\0\0\x0a\0\0\0\x26\0\0\0\x43\x03\0\0\0\0\0\0\x0a\0\0\0\x22\0\0\0\x4e\x03\0\0\0\
+\0\0\0\x0a\0\0\0\x07\0\0\0\x55\x03\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x63\x03\0\0\
+\0\0\0\0\x0a\0\0\0\x35\0\0\0\x6e\x03\0\0\0\0\0\0\x0a\0\0\0\x1f\0\0\0\x7d\x03\0\
+\0\0\0\0\0\x0a\0\0\0\x46\0\0\0\x92\x03\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x9b\x03\
+\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\xa4\x03\0\0\0\0\0\0\x0a\0\0\0\x46\0\0\0\xa9\
+\x03\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\xb3\x03\0\0\0\0\0\0\x0a\0\0\0\x46\0\0\0\
+\xc0\x03\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\xcc\x03\0\0\0\0\0\0\x0a\0\0\0\x27\0\0\
+\0\xd8\x03\0\0\0\0\0\0\x0a\0\0\0\x1f\0\0\0\xe3\x03\0\0\0\0\0\0\x0a\0\0\0\x28\0\
+\0\0\xee\x03\0\0\0\0\0\0\x0a\0\0\0\x29\0\0\0\xf9\x03\0\0\0\0\0\0\x0a\0\0\0\x2a\
+\0\0\0\x04\x04\0\0\0\0\0\0\x0a\0\0\0\x2b\0\0\0\x0f\x04\0\0\0\0\0\0\x0a\0\0\0\
+\x21\0\0\0\x1a\x04\0\0\0\0\0\0\x0a\0\0\0\x26\0\0\0\x25\x04\0\0\0\0\0\0\x0a\0\0\
+\0\x2c\0\0\0\x46\x04\0\0\0\0\0\0\x0a\0\0\0\x2d\0\0\0\x51\x04\0\0\0\0\0\0\x0a\0\
+\0\0\x19\0\0\0\x58\x04\0\0\0\0\0\0\x0a\0\0\0\x2e\0\0\0\x60\x04\0\0\0\0\0\0\x0a\
+\0\0\0\x2f\0\0\0\x6b\x04\0\0\0\0\0\0\x0a\0\0\0\x30\0\0\0\x76\x04\0\0\0\0\0\0\
+\x0a\0\0\0\x31\0\0\0\x81\x04\0\0\0\0\0\0\x0a\0\0\0\x32\0\0\0\x8c\x04\0\0\0\0\0\
+\0\x0a\0\0\0\x33\0\0\0\x98\x04\0\0\0\0\0\0\x0a\0\0\0\x33\0\0\0\xa4\x04\0\0\0\0\
+\0\0\x0a\0\0\0\x34\0\0\0\xb2\x04\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xc0\x04\0\0\0\
+\0\0\0\x0a\0\0\0\x36\0\0\0\xcb\x04\0\0\0\0\0\0\x0a\0\0\0\x1f\0\0\0\xda\x04\0\0\
+\0\0\0\0\x0a\0\0\0\x46\0\0\0\xef\x04\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\xfd\x04\0\
+\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x06\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x0f\x05\
+\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x18\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x25\
+\x05\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x35\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\
+\x43\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\0\x4c\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\0\
+\0\x55\x05\0\0\0\0\0\0\x0a\0\0\0\x46\0\0\0\x5a\x05\0\0\0\0\0\0\x0a\0\0\0\x44\0\
+\0\0\x63\x05\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x70\x05\0\0\0\0\0\0\x0a\0\0\0\x44\
+\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x08\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\
+\0\x10\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x18\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x30\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x38\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x40\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x48\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x60\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x68\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x70\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x78\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x80\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x88\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\xa0\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xa8\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\
+\xb0\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xb8\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\
+\xd0\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xd8\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\
+\xe0\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xe8\0\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\0\
+\x01\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x08\x01\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\
+\x10\x01\0\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x18\x01\0\0\0\0\0\0\x01\0\0\0\x43\0\0\
+\0\0\x03\0\0\0\0\0\0\0\0\0\0\x4c\0\0\0\x0c\x03\0\0\0\0\0\0\0\0\0\0\x4e\0\0\0\
+\x18\x03\0\0\0\0\0\0\0\0\0\0\x4a\0\0\0\x24\x03\0\0\0\0\0\0\0\0\0\0\x4b\0\0\0\
+\x30\x03\0\0\0\0\0\0\0\0\0\0\x50\0\0\0\x48\x03\0\0\0\0\0\0\x0a\0\0\0\x51\0\0\0\
+\x54\x03\0\0\0\0\0\0\x0a\0\0\0\x52\0\0\0\x6c\x03\0\0\0\0\0\0\0\0\0\0\x49\0\0\0\
+\x2c\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x50\0\
+\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x60\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x70\0\0\0\0\
+\0\0\0\0\0\0\0\x42\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x90\0\0\0\0\0\0\0\
+\0\0\0\0\x42\0\0\0\xa0\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xb0\0\0\0\0\0\0\0\0\0\0\
+\0\x42\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xd0\0\0\0\0\0\0\0\0\0\0\0\x42\
+\0\0\0\xe0\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xf0\0\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\
+\0\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x10\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x20\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x30\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x40\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x50\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x60\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x70\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x80\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\x90\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xa0\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xb0\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xc0\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xd0\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xe0\
+\x01\0\0\0\0\0\0\0\0\0\0\x42\0\0\0\xf8\x01\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x08\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x28\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x38\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x48\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x58\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x68\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x78\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x88\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x98\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xa8\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xb8\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xc8\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xd8\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xe8\
+\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xf8\x02\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x08\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x18\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x28\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x38\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x48\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x58\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x68\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x78\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x88\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x98\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xa8\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xb8\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xc8\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xd8\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xe8\
+\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xf8\x03\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x08\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x18\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x28\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x38\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x48\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x58\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x68\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x78\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x88\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x98\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xa8\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xb8\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xc8\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xd8\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\xe8\
+\x04\0\0\0\0\0\0\0\0\0\0\x43\0\0\0\x14\0\0\0\0\0\0\0\x0a\0\0\0\x47\0\0\0\x18\0\
+\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x2c\0\0\0\0\0\0\0\x0a\0\0\0\x47\0\0\0\x30\0\0\
+\0\0\0\0\0\x01\0\0\0\x43\0\0\0\xde\0\0\0\0\0\0\0\x01\0\0\0\x42\0\0\0\x87\x01\0\
+\0\0\0\0\0\x01\0\0\0\x43\0\0\0\x4d\x4f\x51\x52\x49\x4c\x4e\x4a\x4b\x50\0\x2e\
+\x64\x65\x62\x75\x67\x5f\x61\x62\x62\x72\x65\x76\0\x6e\x75\x6d\x5f\x63\x70\x75\
+\0\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\x2e\x65\x78\x74\0\x6d\
+\x69\x73\x73\x5f\x63\x6f\x75\x6e\x74\x73\0\x65\x76\x65\x6e\x74\x73\0\x2e\x6d\
+\x61\x70\x73\0\x66\x65\x6e\x74\x72\x79\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\0\
+\x61\x63\x63\x75\x6d\x5f\x72\x65\x61\x64\x69\x6e\x67\x73\0\x2e\x72\x65\x6c\x2e\
+\x64\x65\x62\x75\x67\x5f\x72\x61\x6e\x67\x65\x73\0\x2e\x64\x65\x62\x75\x67\x5f\
+\x73\x74\x72\0\x2e\x72\x65\x6c\x2e\x64\x65\x62\x75\x67\x5f\x69\x6e\x66\x6f\0\
+\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\x72\x73\x69\x67\0\x6c\x69\x63\x65\x6e\x73\
+\x65\0\x2e\x72\x65\x6c\x2e\x64\x65\x62\x75\x67\x5f\x6c\x69\x6e\x65\0\x2e\x72\
+\x65\x6c\x2e\x64\x65\x62\x75\x67\x5f\x66\x72\x61\x6d\x65\0\x2e\x72\x65\x6c\x2e\
+\x64\x65\x62\x75\x67\x5f\x6c\x6f\x63\0\x6e\x75\x6d\x5f\x6d\x65\x74\x72\x69\x63\
+\0\x70\x72\x6f\x66\x69\x6c\x65\x72\x2e\x62\x70\x66\x2e\x63\0\x2e\x73\x74\x72\
+\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x2e\x72\x6f\x64\x61\x74\x61\0\x66\
+\x65\x6e\x74\x72\x79\x5f\x58\x58\x58\0\x66\x65\x78\x69\x74\x5f\x58\x58\x58\0\
+\x2e\x72\x65\x6c\x66\x65\x6e\x74\x72\x79\x2f\x58\x58\x58\0\x2e\x72\x65\x6c\x66\
+\x65\x78\x69\x74\x2f\x58\x58\x58\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x49\
+\x43\x45\x4e\x53\x45\0\x4c\x42\x42\x30\x5f\x39\0\x4c\x42\x42\x31\x5f\x38\0\x4c\
+\x42\x42\x30\x5f\x37\0\x4c\x42\x42\x31\x5f\x31\x36\0\x4c\x42\x42\x31\x5f\x35\0\
+\x4c\x42\x42\x30\x5f\x35\0\x4c\x42\x42\x31\x5f\x34\0\x4c\x42\x42\x31\x5f\x31\
+\x34\0\x4c\x42\x42\x31\x5f\x32\0\x4c\x42\x42\x30\x5f\x32\0\x4c\x42\x42\x30\x5f\
+\x31\x31\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xef\0\0\0\
+\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb2\x3e\0\0\0\0\0\0\x9a\x01\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x17\0\0\0\x01\0\0\0\x06\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x20\x01\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x20\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x01\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\xc8\x2e\0\0\0\0\0\0\x70\0\0\0\0\0\0\0\x1b\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\
+\x10\0\0\0\0\0\0\0\x2f\x01\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x60\
+\x02\0\0\0\0\0\0\xa8\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\x2b\x01\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x2f\0\0\0\0\
+\0\0\xa0\0\0\0\0\0\0\0\x1b\0\0\0\x05\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\
+\xff\0\0\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\x06\0\0\0\0\0\0\x08\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9d\0\0\0\x01\
+\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x06\0\0\0\0\0\0\x04\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3d\0\0\0\x01\0\0\0\x03\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x74\0\0\0\x01\0\0\0\x30\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x90\x06\0\0\0\0\0\0\x9b\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\
+\0\0\0\0\x01\0\0\0\0\0\0\0\xca\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x2b\x09\0\0\0\0\0\0\xa9\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\xc6\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\x2f\0\0\0\
+\0\0\0\xe0\0\0\0\0\0\0\0\x1b\0\0\0\x0b\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\
+\0\x01\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\x0d\0\0\0\0\0\0\x92\
+\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x83\0\0\0\
+\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x66\x0f\0\0\0\0\0\0\x7e\x05\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x7f\0\0\0\x09\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb8\x30\0\0\0\0\0\0\xb0\x06\0\0\0\0\0\0\x1b\0\0\0\
+\x0e\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x66\0\0\0\x01\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\xe4\x14\0\0\0\0\0\0\x30\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x62\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\x68\x37\0\0\0\0\0\0\xa0\x01\0\0\0\0\0\0\x1b\0\0\0\x10\0\0\0\x08\0\0\0\
+\0\0\0\0\x10\0\0\0\0\0\0\0\x3d\x01\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\x14\x16\0\0\0\0\0\0\x3c\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\x39\x01\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\x39\0\
+\0\0\0\0\0\x80\0\0\0\0\0\0\0\x1b\0\0\0\x12\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\
+\0\0\0\x21\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x50\x1f\0\0\0\0\0\0\
+\xf8\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1d\0\0\
+\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x88\x39\0\0\0\0\0\0\xc0\x04\0\0\0\
+\0\0\0\x1b\0\0\0\x14\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\xb9\0\0\0\x01\0\
+\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\x24\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb5\0\0\0\x09\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\x48\x3e\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\x1b\0\0\0\x16\0\0\0\
+\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\xa9\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\0\x88\x24\0\0\0\0\0\0\x75\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\
+\0\0\0\0\0\0\0\0\0\0\0\xa5\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x88\
+\x3e\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x1b\0\0\0\x18\0\0\0\x08\0\0\0\0\0\0\0\x10\0\
+\0\0\0\0\0\0\x8f\0\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\xa8\
+\x3e\0\0\0\0\0\0\x0a\0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\
+\0\0\0\0\xf7\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x27\0\0\0\0\0\0\
+\xc8\x07\0\0\0\0\0\0\x01\0\0\0\x49\0\0\0\x08\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0";
+
+	return 0;
+err:
+	bpf_object__destroy_skeleton(s);
+	return -1;
+}
+
+#endif /* __PROFILER_BPF_SKEL_H__ */
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index b352ab041160..faed6229935f 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -13,9 +13,12 @@
 #include <net/if.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
 
 #include <linux/err.h>
 #include <linux/sizes.h>
+#include <linux/perf_event.h>
 
 #include <bpf/bpf.h>
 #include <bpf/btf.h>
@@ -24,6 +27,7 @@
 #include "cfg.h"
 #include "main.h"
 #include "xlated_dumper.h"
+#include "profiler.skel.h"
 
 enum dump_mode {
 	DUMP_JITED,
@@ -1537,6 +1541,384 @@ static int do_loadall(int argc, char **argv)
 	return load_with_options(argc, argv, false);
 }
 
+#define SAMPLE_PERIOD  0x7fffffffffffffffULL
+struct profile_metric {
+	const char *name;
+	struct perf_event_attr attr;
+	bool selected;
+	struct bpf_perf_event_value val;
+	u64 misses;
+} metrics[] = {
+	{
+		.name = "cycles",
+		.attr = {
+			.freq = 0,
+			.sample_period = SAMPLE_PERIOD,
+			.inherit = 0,
+			.type = PERF_TYPE_HARDWARE,
+			.read_format = 0,
+			.sample_type = 0,
+			.config = PERF_COUNT_HW_CPU_CYCLES,
+		},
+	},
+	{
+		.name = "instructions",
+		.attr = {
+			.freq = 0,
+			.sample_period = SAMPLE_PERIOD,
+			.inherit = 0,
+			.type = PERF_TYPE_HARDWARE,
+			.read_format = 0,
+			.sample_type = 0,
+			.config = PERF_COUNT_HW_INSTRUCTIONS,
+		},
+	},
+	{
+		.name = "l1d_loads",
+		.attr = {
+			.freq = 0,
+			.sample_period = SAMPLE_PERIOD,
+			.inherit = 0,
+			.type = PERF_TYPE_HW_CACHE,
+			.read_format = 0,
+			.sample_type = 0,
+			.config =
+				PERF_COUNT_HW_CACHE_L1D |
+				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
+				(PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16),
+		},
+	},
+	{
+		.name = "llc_misses",
+		.attr = {
+			.freq = 0,
+			.sample_period = SAMPLE_PERIOD,
+			.inherit = 0,
+			.type = PERF_TYPE_HW_CACHE,
+			.read_format = 0,
+			.sample_type = 0,
+			.config =
+				PERF_COUNT_HW_CACHE_LL |
+				(PERF_COUNT_HW_CACHE_OP_READ << 8) |
+				(PERF_COUNT_HW_CACHE_RESULT_MISS << 16),
+		},
+	},
+};
+u64 profile_total_count;
+
+#define MAX_NUM_PROFILE_METRICS 4
+
+static int profile_parse_metrics(int argc, char **argv)
+{
+	unsigned int metric_cnt;
+	int selected_cnt = 0;
+	unsigned int i;
+
+	metric_cnt = sizeof(metrics) / sizeof(struct profile_metric);
+
+	while (argc > 0) {
+		for (i = 0; i < metric_cnt; i++) {
+			if (strcmp(argv[0], metrics[i].name) == 0) {
+				if (!metrics[i].selected)
+					selected_cnt++;
+				metrics[i].selected = true;
+				break;
+			}
+		}
+		if (i == metric_cnt) {
+			p_err("unknown metric %s", argv[0]);
+			return -1;
+		}
+		NEXT_ARG();
+	}
+	if (selected_cnt > MAX_NUM_PROFILE_METRICS) {
+		p_err("too many (%d) metrics, please specify no more than %d metrics at at time",
+		      selected_cnt, MAX_NUM_PROFILE_METRICS);
+		return -1;
+	}
+	return selected_cnt;
+}
+
+static void profile_read_values(struct profiler_bpf *obj)
+{
+	u32 m, cpu, num_cpu = obj->rodata->num_cpu;
+	u64 counts[num_cpu];
+	int reading_map_fd, count_map_fd, miss_map_fd;
+	u32 key = 0;
+
+	reading_map_fd = bpf_map__fd(obj->maps.accum_readings);
+	count_map_fd = bpf_map__fd(obj->maps.counts);
+	miss_map_fd = bpf_map__fd(obj->maps.miss_counts);
+	if (reading_map_fd < 0 || count_map_fd < 0 || miss_map_fd < 0) {
+		p_err("failed to get fd for map");
+		return;
+	}
+
+	assert(bpf_map_lookup_elem(count_map_fd, &key, counts) == 0);
+	profile_total_count = 0;
+	for (cpu = 0; cpu < num_cpu; cpu++)
+		profile_total_count += counts[cpu];
+
+	for (m = 0; m < sizeof(metrics) / sizeof(metrics[0]); m++) {
+		struct bpf_perf_event_value values[obj->rodata->num_cpu];
+		u64 miss_counts[num_cpu];
+
+		if (!metrics[m].selected)
+			continue;
+
+		assert(bpf_map_lookup_elem(reading_map_fd, &key, values) == 0);
+		assert(bpf_map_lookup_elem(miss_map_fd, &key, miss_counts) == 0);
+		for (cpu = 0; cpu < num_cpu; cpu++) {
+			metrics[m].val.counter += values[cpu].counter;
+			metrics[m].val.enabled += values[cpu].enabled;
+			metrics[m].val.running += values[cpu].running;
+			metrics[m].misses += miss_counts[cpu];
+		}
+		key++;
+	}
+}
+
+static void profile_print_readings_json(unsigned long duration)
+{
+	u32 m;
+
+	jsonw_start_array(json_wtr);
+	for (m = 0; m < sizeof(metrics) / sizeof(metrics[0]); m++) {
+		if (!metrics[m].selected)
+			continue;
+		jsonw_start_object(json_wtr);
+		jsonw_lluint_field(json_wtr, "duration", duration);
+		jsonw_string_field(json_wtr, "metric", metrics[m].name);
+		jsonw_lluint_field(json_wtr, "run_cnt", profile_total_count);
+		jsonw_lluint_field(json_wtr, "miss_cnt", metrics[m].misses);
+		jsonw_lluint_field(json_wtr, "value", metrics[m].val.counter);
+		jsonw_lluint_field(json_wtr, "enabled", metrics[m].val.enabled);
+		jsonw_lluint_field(json_wtr, "running", metrics[m].val.running);
+
+		jsonw_end_object(json_wtr);
+	}
+	jsonw_end_array(json_wtr);
+}
+
+static void profile_print_readings_plain(unsigned long duration)
+{
+	u32 m;
+
+	for (m = 0; m < sizeof(metrics) / sizeof(metrics[0]); m++) {
+		if (!metrics[m].selected)
+			continue;
+		printf("%s: duration %lu run_cnt %lu miss_cnt %lu\n",
+		       metrics[m].name, duration, profile_total_count,
+		       metrics[m].misses);
+		printf("\tcounter %llu enabled %llu running %llu\n",
+		       metrics[m].val.counter, metrics[m].val.enabled,
+		       metrics[m].val.running);
+	}
+}
+
+static void profile_print_readings(unsigned long duration)
+{
+	if (json_output)
+		profile_print_readings_json(duration);
+	else
+		profile_print_readings_plain(duration);
+}
+
+static void profile_close_perf_events(struct profiler_bpf *obj)
+{
+	int map_fd, pmu_fd;
+	u32 i;
+
+	map_fd = bpf_map__fd(obj->maps.events);
+	if (map_fd < 0) {
+		p_err("failed to get fd for events map");
+		return;
+	}
+
+	for (i = 0; i < obj->rodata->num_cpu * obj->rodata->num_metric; i++) {
+		bpf_map_lookup_elem(map_fd, &i, &pmu_fd);
+		close(pmu_fd);
+	}
+}
+
+static int profile_open_perf_events(struct profiler_bpf *obj)
+{
+	int map_fd, pmu_fd, i, key = 0;
+	unsigned int cpu, m;
+
+	map_fd = bpf_map__fd(obj->maps.events);
+	if (map_fd < 0) {
+		p_err("failed to get fd for events map");
+		return -1;
+	}
+
+	for (m = 0; m < sizeof(metrics) / sizeof(metrics[0]); m++) {
+		if (!metrics[m].selected)
+			continue;
+		for (cpu = 0; cpu < obj->rodata->num_cpu; cpu++) {
+			pmu_fd = syscall(__NR_perf_event_open, &metrics[m].attr,
+					 -1/*pid*/, cpu, -1/*group_fd*/, 0);
+			if (pmu_fd < 0 ||
+			    bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY) ||
+			    ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
+				p_err("failed to create event %s on cpu %d",
+				      metrics[m].name, cpu);
+				goto err;
+			}
+			key++;
+		}
+	}
+	return 0;
+err:
+	for (i = key - 1; i >= 0; i--) {
+		bpf_map_lookup_elem(map_fd, &i, &pmu_fd);
+		close(pmu_fd);
+	}
+	return -1;
+}
+
+static char *profile_target_name(int tgt_fd)
+{
+	struct bpf_prog_info_linear *info_linear;
+	struct bpf_func_info *func_info;
+	const struct btf_type *t;
+	char *name = NULL;
+	struct btf *btf;
+
+	info_linear = bpf_program__get_prog_info_linear(
+		tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO);
+	if (IS_ERR_OR_NULL(info_linear)) {
+		p_err("failed to get info_linear for prog FD %d", tgt_fd);
+		return NULL;
+	}
+
+	if (info_linear->info.btf_id == 0 ||
+	    btf__get_from_id(info_linear->info.btf_id, &btf)) {
+		p_err("prog FD %d doesn't have valid btf", tgt_fd);
+		goto out;
+	}
+
+	func_info = (struct bpf_func_info *)(info_linear->info.func_info);
+	t = btf__type_by_id(btf, func_info[0].type_id);
+	if (!t) {
+		p_err("btf %d doesn't have type %d",
+		      info_linear->info.btf_id, func_info[0].type_id);
+		goto out;
+	}
+	name = strdup(btf__name_by_offset(btf, t->name_off));
+out:
+	free(info_linear);
+	return name;
+}
+
+static int do_profile(int argc, char **argv)
+{
+	DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts);
+	int num_metric, num_cpu, tgt_fd, err = -1, i;
+	char *new_section_names[2] = {};
+	char *endptr, *tgt_name = NULL;
+	struct bpf_program *prog;
+	struct profiler_bpf *obj;
+	unsigned long duration;
+	u32 name_len;
+
+	/* we at least need: <duration>, "id", <id>, <metric> */
+	if (argc < 4)
+		usage();
+
+	/* parse profiling duration */
+	duration = strtoul(*argv, &endptr, 0);
+	if (*endptr) {
+		p_err("can't parse %s as duration", *argv);
+		return -1;
+	}
+	NEXT_ARG();
+
+	/* parse target fd */
+	tgt_fd = prog_parse_fd(&argc, &argv);
+	if (tgt_fd < 0) {
+		p_err("failed to parse fd");
+		return -1;
+	}
+	opts.attach_prog_fd = tgt_fd;
+
+	num_metric = profile_parse_metrics(argc, argv);
+	if (num_metric <= 0)
+		goto out;
+
+	num_cpu = libbpf_num_possible_cpus();
+	if (num_cpu <= 0) {
+		p_err("failed to identify number of CPUs");
+		goto out;
+	}
+
+	obj = profiler_bpf__open_opts(&opts);
+	if (!obj) {
+		p_err("failed to open and/or load BPF object");
+		goto out;
+	}
+
+	obj->rodata->num_cpu = num_cpu;
+	obj->rodata->num_metric = num_metric;
+
+	/* adjust map sizes */
+	bpf_map__resize(obj->maps.events, num_metric * num_cpu);
+	bpf_map__resize(obj->maps.fentry_readings, num_metric);
+	bpf_map__resize(obj->maps.accum_readings, num_metric);
+	bpf_map__resize(obj->maps.counts, 1);
+	bpf_map__resize(obj->maps.miss_counts, num_metric);
+
+	/* change target name */
+	tgt_name = profile_target_name(tgt_fd);
+	if (!tgt_name) {
+		p_err("failed to load target function name");
+		return -1;
+	}
+
+	name_len = strlen(tgt_name) + strlen("fentry/") + 1;
+	new_section_names[0] = malloc(name_len);
+	new_section_names[1] = malloc(name_len);
+	if (!new_section_names[0] || !new_section_names[1]) {
+		p_err("mem alloc failed");
+		goto out;
+	}
+	snprintf(new_section_names[0], name_len, "fentry/%s", tgt_name);
+	snprintf(new_section_names[1], name_len, "fexit/%s", tgt_name);
+	i = 0;
+	bpf_object__for_each_program(prog, obj->obj)
+		if (bpf_program__overwrite_section_name(
+			    prog, new_section_names[i++]) == NULL)
+			goto out;
+
+	set_max_rlimit();
+	err = profiler_bpf__load(obj);
+	if (err) {
+		p_err("failed to load obj");
+		goto out;
+	}
+
+	profile_open_perf_events(obj);
+
+	err = profiler_bpf__attach(obj);
+	if (err) {
+		p_err("failed to attach obj");
+		goto out;
+	}
+	sleep(duration);
+
+	profile_close_perf_events(obj);
+	profiler_bpf__detach(obj);
+	profile_read_values(obj);
+	profiler_bpf__destroy(obj);
+	profile_print_readings(duration);
+out:
+	close(tgt_fd);
+	free(tgt_name);
+	free(new_section_names[0]);
+	free(new_section_names[1]);
+	return err;
+}
+
 static int do_help(int argc, char **argv)
 {
 	if (json_output) {
@@ -1560,6 +1942,7 @@ static int do_help(int argc, char **argv)
 		"                         [data_out FILE [data_size_out L]] \\\n"
 		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
 		"                         [repeat N]\n"
+		"       %s %s profile DURATION PROG METRICs\n"
 		"       %s %s tracelog\n"
 		"       %s %s help\n"
 		"\n"
@@ -1578,11 +1961,12 @@ static int do_help(int argc, char **argv)
 		"       ATTACH_TYPE := { msg_verdict | stream_verdict | stream_parser |\n"
 		"                        flow_dissector }\n"
 		"       " HELP_SPEC_OPTIONS "\n"
+		"       METRIC := { cycles | instructions | l1d_loads | llc_misses }\n"
 		"",
 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
-		bin_name, argv[-2]);
+		bin_name, argv[-2], bin_name, argv[-2]);
 
 	return 0;
 }
@@ -1599,6 +1983,7 @@ static const struct cmd cmds[] = {
 	{ "detach",	do_detach },
 	{ "tracelog",	do_tracelog },
 	{ "run",	do_run },
+	{ "profile",	do_profile },
 	{ 0 }
 };
 
diff --git a/tools/bpf/bpftool/skeleton/README b/tools/bpf/bpftool/skeleton/README
new file mode 100644
index 000000000000..ed6a1f3058c6
--- /dev/null
+++ b/tools/bpf/bpftool/skeleton/README
@@ -0,0 +1,3 @@
+To generate profiler.skel.h:
+1. clang -g -O2 -target bpf -I../../tools/lib -c profiler.bpf.c -o profiler.bpf.o
+2. bpftool gen skeleton profiler.bpf.o > ../profiler.skel.h
diff --git a/tools/bpf/bpftool/skeleton/profiler.bpf.c b/tools/bpf/bpftool/skeleton/profiler.bpf.c
new file mode 100644
index 000000000000..0328fac5ed78
--- /dev/null
+++ b/tools/bpf/bpftool/skeleton/profiler.bpf.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Facebook
+#include "profiler.h"
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+#define ___bpf_concat(a, b) a ## b
+#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
+#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
+#define ___bpf_narg(...) \
+	___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define ___bpf_empty(...) \
+	___bpf_nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
+
+#define ___bpf_ctx_cast0() ctx
+#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
+#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
+#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
+#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
+#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
+#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
+#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
+#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
+#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
+#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
+#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
+#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
+#define ___bpf_ctx_cast(args...) \
+	___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
+
+/*
+ * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
+ * similar kinds of BPF programs, that accept input arguments as a single
+ * pointer to untyped u64 array, where each u64 can actually be a typed
+ * pointer or integer of different size. Instead of requring user to write
+ * manual casts and work with array elements by index, BPF_PROG macro
+ * allows user to declare a list of named and typed input arguments in the
+ * same syntax as for normal C function. All the casting is hidden and
+ * performed transparently, while user code can just assume working with
+ * function arguments of specified type and name.
+ *
+ * Original raw context argument is preserved as well as 'ctx' argument.
+ * This is useful when using BPF helpers that expect original context
+ * as one of the parameters (e.g., for bpf_perf_event_output()).
+ */
+#define BPF_PROG(name, args...)						    \
+name(unsigned long long *ctx);						    \
+static __always_inline typeof(name(0))					    \
+____##name(unsigned long long *ctx, ##args);				    \
+typeof(name(0)) name(unsigned long long *ctx)				    \
+{									    \
+	_Pragma("GCC diagnostic push")					    \
+	_Pragma("GCC diagnostic ignored \"-Wint-conversion\"")		    \
+	return ____##name(___bpf_ctx_cast(args));			    \
+	_Pragma("GCC diagnostic pop")					    \
+}									    \
+static __always_inline typeof(name(0))					    \
+____##name(unsigned long long *ctx, ##args)
+
+/* map of perf event fds, num_cpu * num_metric entries */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(int));
+} events SEC(".maps");
+
+/* readings at fentry */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct bpf_perf_event_value));
+} fentry_readings SEC(".maps");
+
+/* accumulated readings */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(struct bpf_perf_event_value));
+} accum_readings SEC(".maps");
+
+/* sample counts, one per cpu */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(u64));
+} counts SEC(".maps");
+
+/* missed (perf event not active) counts, one per perf_event.  */
+struct {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__uint(key_size, sizeof(u32));
+	__uint(value_size, sizeof(u64));
+} miss_counts SEC(".maps");
+
+const volatile __u32 num_cpu = 0;
+const volatile __u32 num_metric = 0;
+#define MAX_NUM_MATRICS 4
+
+SEC("fentry/XXX")
+int BPF_PROG(fentry_XXX)
+{
+	struct bpf_perf_event_value *ptrs[MAX_NUM_MATRICS];
+	u32 key = bpf_get_smp_processor_id();
+	u32 i;
+
+	/* look up before reading, to reduce error */
+	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
+		u32 flag = i;
+
+		ptrs[i] = bpf_map_lookup_elem(&fentry_readings, &flag);
+		if (!ptrs[i])
+			return 0;
+	}
+
+	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
+		struct bpf_perf_event_value reading;
+		int err;
+
+		err = bpf_perf_event_read_value(&events, key, &reading,
+						sizeof(reading));
+		if (err)
+			return 0;
+		*(ptrs[i]) = reading;
+		key += num_cpu;
+	}
+
+	return 0;
+}
+
+static inline void
+fexit_update_maps(u32 id, struct bpf_perf_event_value *after)
+{
+	struct bpf_perf_event_value *before, diff, *accum;
+
+	before = bpf_map_lookup_elem(&fentry_readings, &id);
+	/* only account samples with a valid fentry_reading */
+	if (before && before->counter) {
+		struct bpf_perf_event_value *accum;
+
+		diff.counter = after->counter - before->counter;
+		diff.enabled = after->enabled - before->enabled;
+		diff.running = after->running - before->running;
+
+		accum = bpf_map_lookup_elem(&accum_readings, &id);
+		if (accum) {
+			accum->counter += diff.counter;
+			accum->enabled += diff.enabled;
+			accum->running += diff.running;
+			if (diff.enabled > diff.running) {
+				u64 *miss_count;
+
+				miss_count = bpf_map_lookup_elem(&miss_counts, &id);
+				if (miss_count)
+					*miss_count += 1;
+			}
+		}
+	}
+}
+
+SEC("fexit/XXX")
+int BPF_PROG(fexit_XXX)
+{
+	struct bpf_perf_event_value readings[MAX_NUM_MATRICS];
+	u32 cpu = bpf_get_smp_processor_id();
+	u32 i, one = 1, zero = 0;
+	int err;
+	u64 *count;
+
+	/* read all events before updating the maps, to reduce error */
+	for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++) {
+		err = bpf_perf_event_read_value(&events, cpu + i * num_cpu,
+						readings + i, sizeof(*readings));
+		if (err)
+			return 0;
+	}
+	count = bpf_map_lookup_elem(&counts, &zero);
+	if (count) {
+		*count += 1;
+		for (i = 0; i < num_metric && i < MAX_NUM_MATRICS; i++)
+			fexit_update_maps(i, &readings[i]);
+	}
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/bpf/bpftool/skeleton/profiler.h b/tools/bpf/bpftool/skeleton/profiler.h
new file mode 100644
index 000000000000..ae15cb0c4d43
--- /dev/null
+++ b/tools/bpf/bpftool/skeleton/profiler.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __PROFILER_H
+#define __PROFILER_H
+
+/* useful typedefs from vimlinux.h */
+
+typedef signed char __s8;
+typedef unsigned char __u8;
+typedef short int __s16;
+typedef short unsigned int __u16;
+typedef int __s32;
+typedef unsigned int __u32;
+typedef long long int __s64;
+typedef long long unsigned int __u64;
+
+typedef __s8 s8;
+typedef __u8 u8;
+typedef __s16 s16;
+typedef __u16 u16;
+typedef __s32 s32;
+typedef __u32 u32;
+typedef __s64 s64;
+typedef __u64 u64;
+
+enum {
+	false = 0,
+	true = 1,
+};
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#define __bitwise __bitwise__
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+#endif /* __PROFILER_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC bpf-next 4/4] bpftool: Documentation for bpftool prog profile
  2020-02-13 21:01 [RFC bpf-next 0/4] bpftool: introduce prog profile Song Liu
                   ` (2 preceding siblings ...)
  2020-02-13 21:01 ` [RFC bpf-next 3/4] bpftool: introduce "prog profile" command Song Liu
@ 2020-02-13 21:01 ` Song Liu
  3 siblings, 0 replies; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:01 UTC (permalink / raw)
  To: netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu

Add documentation for the new bpftool prog profile command.

Signed-off-by: Song Liu <songliubraving@fb.com>
---
 .../bpf/bpftool/Documentation/bpftool-prog.rst  | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index 64ddf8a4c518..22ff0df327a1 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -30,6 +30,7 @@ PROG COMMANDS
 |	**bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 |	**bpftool** **prog tracelog**
 |	**bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
+|	**bpftool** **prog profile** *DURATION* *PROG* *METRICs*
 |	**bpftool** **prog help**
 |
 |	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
@@ -47,6 +48,9 @@ PROG COMMANDS
 |       *ATTACH_TYPE* := {
 |		**msg_verdict** | **stream_verdict** | **stream_parser** | **flow_dissector**
 |	}
+|	*METRIC* := {
+|		**cycles** | **instructions** | **l1d_loads** | **llc_misses**
+|	}
 
 
 DESCRIPTION
@@ -188,6 +192,10 @@ DESCRIPTION
 		  not all of them can take the **ctx_in**/**ctx_out**
 		  arguments. bpftool does not perform checks on program types.
 
+	**bpftool prog profile** *DURATION* *PROG* *METRICs*
+		  Profile *METRICs* for bpf program *PROG* for *DURATION*
+		  seconds.
+
 	**bpftool prog help**
 		  Print short help message.
 
@@ -310,6 +318,15 @@ EXAMPLES
 
 **# rm /sys/fs/bpf/xdp1**
 
+|
+| **# bpftool prog profile 20 id 810 cycles instructions**
+
+::
+    cycles: duration 20 run_cnt 1368 miss_cnt 665
+            counter 503377 enabled 668202 running 351857
+    instructions: duration 20 run_cnt 1368 miss_cnt 707
+	    counter 398625 enabled 502330 running 272014
+
 SEE ALSO
 ========
 	**bpf**\ (2),
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-13 21:01 ` [RFC bpf-next 3/4] bpftool: introduce "prog profile" command Song Liu
@ 2020-02-13 21:50   ` Toke Høiland-Jørgensen
  2020-02-13 21:55     ` Song Liu
  2020-02-15  0:45   ` Alexei Starovoitov
  1 sibling, 1 reply; 10+ messages in thread
From: Toke Høiland-Jørgensen @ 2020-02-13 21:50 UTC (permalink / raw)
  To: Song Liu, netdev, bpf; +Cc: kernel-team, ast, daniel, Song Liu

Song Liu <songliubraving@fb.com> writes:

> With fentry/fexit programs, it is possible to profile BPF program with
> hardware counters. Introduce bpftool "prog profile", which measures key
> metrics of a BPF program.
>
> bpftool prog profile command creates per-cpu perf events. Then it attaches
> fentry/fexit programs to the target BPF program. The fentry program saves
> perf event value to a map. The fexit program reads the perf event again,
> and calculates the difference, which is the instructions/cycles used by
> the target program.
>
> Example input and output:
>
>   ./bpftool prog profile 20 id 810 cycles instructions
>   cycles: duration 20 run_cnt 1368 miss_cnt 665
>           counter 503377 enabled 668202 running 351857
>   instructions: duration 20 run_cnt 1368 miss_cnt 707
>           counter 398625 enabled 502330 running 272014
>
> This command measures cycles and instructions for BPF program with id
> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
> measured in 665 out of these runs, because of perf event multiplexing
> (some perf commands are running in the background). In these runs, the BPF
> program consumed 503377 cycles. The perf_event enabled and running time
> are 668202 and 351857 respectively.
>
> Note that, this approach measures cycles and instructions in very small
> increments. So the fentry/fexit programs introduce noticable errors to
> the measurement results.
>
> The fentry/fexit programs are generated with BPF skeleton. Currently,
> generation of the skeleton requires some manual steps.
>
> Signed-off-by: Song Liu <songliubraving@fb.com>
> ---
>  tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++
>  tools/bpf/bpftool/prog.c                  | 387 +++++++++-
>  tools/bpf/bpftool/skeleton/README         |   3 +
>  tools/bpf/bpftool/skeleton/profiler.bpf.c | 185 +++++
>  tools/bpf/bpftool/skeleton/profiler.h     |  47 ++
>  5 files changed, 1441 insertions(+), 1 deletion(-)
>  create mode 100644 tools/bpf/bpftool/profiler.skel.h
>  create mode 100644 tools/bpf/bpftool/skeleton/README
>  create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
>  create mode 100644 tools/bpf/bpftool/skeleton/profiler.h
>
> diff --git a/tools/bpf/bpftool/profiler.skel.h b/tools/bpf/bpftool/profiler.skel.h
> new file mode 100644
> index 000000000000..10e99989c03e
> --- /dev/null
> +++ b/tools/bpf/bpftool/profiler.skel.h
> @@ -0,0 +1,820 @@
> +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
> +
> +/* THIS FILE IS AUTOGENERATED! */
> +#ifndef __PROFILER_BPF_SKEL_H__
> +#define __PROFILER_BPF_SKEL_H__
> +
> +#include <stdlib.h>
> +#include <bpf/libbpf.h>
> +
> +struct profiler_bpf {
> +	struct bpf_object_skeleton *skeleton;
> +	struct bpf_object *obj;
> +	struct {
> +		struct bpf_map *events;
> +		struct bpf_map *fentry_readings;
> +		struct bpf_map *accum_readings;
> +		struct bpf_map *counts;
> +		struct bpf_map *miss_counts;
> +		struct bpf_map *rodata;
> +	} maps;
> +	struct {
> +		struct bpf_program *fentry_XXX;
> +		struct bpf_program *fexit_XXX;
> +	} progs;
> +	struct {
> +		struct bpf_link *fentry_XXX;
> +		struct bpf_link *fexit_XXX;
> +	} links;
> +	struct profiler_bpf__rodata {
> +		__u32 num_cpu;
> +		__u32 num_metric;
> +	} *rodata;
> +};
> +
> +static void
> +profiler_bpf__destroy(struct profiler_bpf *obj)
> +{
> +	if (!obj)
> +		return;
> +	if (obj->skeleton)
> +		bpf_object__destroy_skeleton(obj->skeleton);
> +	free(obj);
> +}
> +
> +static inline int
> +profiler_bpf__create_skeleton(struct profiler_bpf *obj);
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open_opts(const struct bpf_object_open_opts *opts)
> +{
> +	struct profiler_bpf *obj;
> +
> +	obj = (typeof(obj))calloc(1, sizeof(*obj));
> +	if (!obj)
> +		return NULL;
> +	if (profiler_bpf__create_skeleton(obj))
> +		goto err;
> +	if (bpf_object__open_skeleton(obj->skeleton, opts))
> +		goto err;
> +
> +	return obj;
> +err:
> +	profiler_bpf__destroy(obj);
> +	return NULL;
> +}
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open(void)
> +{
> +	return profiler_bpf__open_opts(NULL);
> +}
> +
> +static inline int
> +profiler_bpf__load(struct profiler_bpf *obj)
> +{
> +	return bpf_object__load_skeleton(obj->skeleton);
> +}
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open_and_load(void)
> +{
> +	struct profiler_bpf *obj;
> +
> +	obj = profiler_bpf__open();
> +	if (!obj)
> +		return NULL;
> +	if (profiler_bpf__load(obj)) {
> +		profiler_bpf__destroy(obj);
> +		return NULL;
> +	}
> +	return obj;
> +}
> +
> +static inline int
> +profiler_bpf__attach(struct profiler_bpf *obj)
> +{
> +	return bpf_object__attach_skeleton(obj->skeleton);
> +}
> +
> +static inline void
> +profiler_bpf__detach(struct profiler_bpf *obj)
> +{
> +	return bpf_object__detach_skeleton(obj->skeleton);
> +}
> +
> +static inline int
> +profiler_bpf__create_skeleton(struct profiler_bpf *obj)
> +{
> +	struct bpf_object_skeleton *s;
> +
> +	s = (typeof(s))calloc(1, sizeof(*s));
> +	if (!s)
> +		return -1;
> +	obj->skeleton = s;
> +
> +	s->sz = sizeof(*s);
> +	s->name = "profiler_bpf";
> +	s->obj = &obj->obj;
> +
> +	/* maps */
> +	s->map_cnt = 6;
> +	s->map_skel_sz = sizeof(*s->maps);
> +	s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
> +	if (!s->maps)
> +		goto err;
> +
> +	s->maps[0].name = "events";
> +	s->maps[0].map = &obj->maps.events;
> +
> +	s->maps[1].name = "fentry_readings";
> +	s->maps[1].map = &obj->maps.fentry_readings;
> +
> +	s->maps[2].name = "accum_readings";
> +	s->maps[2].map = &obj->maps.accum_readings;
> +
> +	s->maps[3].name = "counts";
> +	s->maps[3].map = &obj->maps.counts;
> +
> +	s->maps[4].name = "miss_counts";
> +	s->maps[4].map = &obj->maps.miss_counts;
> +
> +	s->maps[5].name = "profiler.rodata";
> +	s->maps[5].map = &obj->maps.rodata;
> +	s->maps[5].mmaped = (void **)&obj->rodata;
> +
> +	/* programs */
> +	s->prog_cnt = 2;
> +	s->prog_skel_sz = sizeof(*s->progs);
> +	s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
> +	if (!s->progs)
> +		goto err;
> +
> +	s->progs[0].name = "fentry_XXX";
> +	s->progs[0].prog = &obj->progs.fentry_XXX;
> +	s->progs[0].link = &obj->links.fentry_XXX;
> +
> +	s->progs[1].name = "fexit_XXX";
> +	s->progs[1].prog = &obj->progs.fexit_XXX;
> +	s->progs[1].link = &obj->links.fexit_XXX;
> +
> +	s->data_sz = 18256;
> +	s->data = (void *)"\
> +\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\

Holy binary blob, Batman! :)

What is this blob, exactly? The bytecode output of a precompiled
program?

-Toke


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-13 21:50   ` Toke Høiland-Jørgensen
@ 2020-02-13 21:55     ` Song Liu
  2020-02-13 22:06       ` Toke Høiland-Jørgensen
  0 siblings, 1 reply; 10+ messages in thread
From: Song Liu @ 2020-02-13 21:55 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: Networking, bpf, Kernel Team, ast, daniel



> On Feb 13, 2020, at 1:50 PM, Toke Høiland-Jørgensen <toke@redhat.com> wrote:
> 
> Song Liu <songliubraving@fb.com> writes:
> 
>> With fentry/fexit programs, it is possible to profile BPF program with
>> hardware counters. Introduce bpftool "prog profile", which measures key
>> metrics of a BPF program.
>> 
>> bpftool prog profile command creates per-cpu perf events. Then it attaches
>> fentry/fexit programs to the target BPF program. The fentry program saves
>> perf event value to a map. The fexit program reads the perf event again,
>> and calculates the difference, which is the instructions/cycles used by
>> the target program.
>> 
>> Example input and output:
>> 
>>  ./bpftool prog profile 20 id 810 cycles instructions
>>  cycles: duration 20 run_cnt 1368 miss_cnt 665
>>          counter 503377 enabled 668202 running 351857
>>  instructions: duration 20 run_cnt 1368 miss_cnt 707
>>          counter 398625 enabled 502330 running 272014
>> 
>> This command measures cycles and instructions for BPF program with id
>> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
>> measured in 665 out of these runs, because of perf event multiplexing
>> (some perf commands are running in the background). In these runs, the BPF
>> program consumed 503377 cycles. The perf_event enabled and running time
>> are 668202 and 351857 respectively.
>> 
>> Note that, this approach measures cycles and instructions in very small
>> increments. So the fentry/fexit programs introduce noticable errors to
>> the measurement results.
>> 
>> The fentry/fexit programs are generated with BPF skeleton. Currently,
>> generation of the skeleton requires some manual steps.
>> 
>> Signed-off-by: Song Liu <songliubraving@fb.com>
>> ---
>> tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++
>> tools/bpf/bpftool/prog.c                  | 387 +++++++++-
>> tools/bpf/bpftool/skeleton/README         |   3 +
>> tools/bpf/bpftool/skeleton/profiler.bpf.c | 185 +++++
>> tools/bpf/bpftool/skeleton/profiler.h     |  47 ++
>> 5 files changed, 1441 insertions(+), 1 deletion(-)
>> create mode 100644 tools/bpf/bpftool/profiler.skel.h
>> create mode 100644 tools/bpf/bpftool/skeleton/README
>> create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
>> create mode 100644 tools/bpf/bpftool/skeleton/profiler.h
>> 
>> diff --git a/tools/bpf/bpftool/profiler.skel.h b/tools/bpf/bpftool/profiler.skel.h
>> new file mode 100644
>> index 000000000000..10e99989c03e
>> --- /dev/null
>> +++ b/tools/bpf/bpftool/profiler.skel.h
>> @@ -0,0 +1,820 @@
>> +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
>> +
>> +/* THIS FILE IS AUTOGENERATED! */
>> +#ifndef __PROFILER_BPF_SKEL_H__
>> +#define __PROFILER_BPF_SKEL_H__
>> +
>> +#include <stdlib.h>
>> +#include <bpf/libbpf.h>
>> +
>> +struct profiler_bpf {
>> +	struct bpf_object_skeleton *skeleton;
>> +	struct bpf_object *obj;
>> +	struct {
>> +		struct bpf_map *events;
>> +		struct bpf_map *fentry_readings;
>> +		struct bpf_map *accum_readings;
>> +		struct bpf_map *counts;
>> +		struct bpf_map *miss_counts;
>> +		struct bpf_map *rodata;
>> +	} maps;
>> +	struct {
>> +		struct bpf_program *fentry_XXX;
>> +		struct bpf_program *fexit_XXX;
>> +	} progs;
>> +	struct {
>> +		struct bpf_link *fentry_XXX;
>> +		struct bpf_link *fexit_XXX;
>> +	} links;
>> +	struct profiler_bpf__rodata {
>> +		__u32 num_cpu;
>> +		__u32 num_metric;
>> +	} *rodata;
>> +};
>> +
>> +static void
>> +profiler_bpf__destroy(struct profiler_bpf *obj)
>> +{
>> +	if (!obj)
>> +		return;
>> +	if (obj->skeleton)
>> +		bpf_object__destroy_skeleton(obj->skeleton);
>> +	free(obj);
>> +}
>> +
>> +static inline int
>> +profiler_bpf__create_skeleton(struct profiler_bpf *obj);
>> +
>> +static inline struct profiler_bpf *
>> +profiler_bpf__open_opts(const struct bpf_object_open_opts *opts)
>> +{
>> +	struct profiler_bpf *obj;
>> +
>> +	obj = (typeof(obj))calloc(1, sizeof(*obj));
>> +	if (!obj)
>> +		return NULL;
>> +	if (profiler_bpf__create_skeleton(obj))
>> +		goto err;
>> +	if (bpf_object__open_skeleton(obj->skeleton, opts))
>> +		goto err;
>> +
>> +	return obj;
>> +err:
>> +	profiler_bpf__destroy(obj);
>> +	return NULL;
>> +}
>> +
>> +static inline struct profiler_bpf *
>> +profiler_bpf__open(void)
>> +{
>> +	return profiler_bpf__open_opts(NULL);
>> +}
>> +
>> +static inline int
>> +profiler_bpf__load(struct profiler_bpf *obj)
>> +{
>> +	return bpf_object__load_skeleton(obj->skeleton);
>> +}
>> +
>> +static inline struct profiler_bpf *
>> +profiler_bpf__open_and_load(void)
>> +{
>> +	struct profiler_bpf *obj;
>> +
>> +	obj = profiler_bpf__open();
>> +	if (!obj)
>> +		return NULL;
>> +	if (profiler_bpf__load(obj)) {
>> +		profiler_bpf__destroy(obj);
>> +		return NULL;
>> +	}
>> +	return obj;
>> +}
>> +
>> +static inline int
>> +profiler_bpf__attach(struct profiler_bpf *obj)
>> +{
>> +	return bpf_object__attach_skeleton(obj->skeleton);
>> +}
>> +
>> +static inline void
>> +profiler_bpf__detach(struct profiler_bpf *obj)
>> +{
>> +	return bpf_object__detach_skeleton(obj->skeleton);
>> +}
>> +
>> +static inline int
>> +profiler_bpf__create_skeleton(struct profiler_bpf *obj)
>> +{
>> +	struct bpf_object_skeleton *s;
>> +
>> +	s = (typeof(s))calloc(1, sizeof(*s));
>> +	if (!s)
>> +		return -1;
>> +	obj->skeleton = s;
>> +
>> +	s->sz = sizeof(*s);
>> +	s->name = "profiler_bpf";
>> +	s->obj = &obj->obj;
>> +
>> +	/* maps */
>> +	s->map_cnt = 6;
>> +	s->map_skel_sz = sizeof(*s->maps);
>> +	s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
>> +	if (!s->maps)
>> +		goto err;
>> +
>> +	s->maps[0].name = "events";
>> +	s->maps[0].map = &obj->maps.events;
>> +
>> +	s->maps[1].name = "fentry_readings";
>> +	s->maps[1].map = &obj->maps.fentry_readings;
>> +
>> +	s->maps[2].name = "accum_readings";
>> +	s->maps[2].map = &obj->maps.accum_readings;
>> +
>> +	s->maps[3].name = "counts";
>> +	s->maps[3].map = &obj->maps.counts;
>> +
>> +	s->maps[4].name = "miss_counts";
>> +	s->maps[4].map = &obj->maps.miss_counts;
>> +
>> +	s->maps[5].name = "profiler.rodata";
>> +	s->maps[5].map = &obj->maps.rodata;
>> +	s->maps[5].mmaped = (void **)&obj->rodata;
>> +
>> +	/* programs */
>> +	s->prog_cnt = 2;
>> +	s->prog_skel_sz = sizeof(*s->progs);
>> +	s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
>> +	if (!s->progs)
>> +		goto err;
>> +
>> +	s->progs[0].name = "fentry_XXX";
>> +	s->progs[0].prog = &obj->progs.fentry_XXX;
>> +	s->progs[0].link = &obj->links.fentry_XXX;
>> +
>> +	s->progs[1].name = "fexit_XXX";
>> +	s->progs[1].prog = &obj->progs.fexit_XXX;
>> +	s->progs[1].link = &obj->links.fexit_XXX;
>> +
>> +	s->data_sz = 18256;
>> +	s->data = (void *)"\
>> +\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
> 
> Holy binary blob, Batman! :)
> 
> What is this blob, exactly? The bytecode output of a precompiled
> program?

It is the skeleton compiled from profiler.bpf.c. Please refer to 
the README file for step to generate it. 

In long term, we should include the generation of this blob in the 
make process. But for RFC, I kept it simple for now. :)

Thanks,
Song



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-13 21:55     ` Song Liu
@ 2020-02-13 22:06       ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 10+ messages in thread
From: Toke Høiland-Jørgensen @ 2020-02-13 22:06 UTC (permalink / raw)
  To: Song Liu; +Cc: Networking, bpf, Kernel Team, ast, daniel

Song Liu <songliubraving@fb.com> writes:

>> On Feb 13, 2020, at 1:50 PM, Toke Høiland-Jørgensen <toke@redhat.com> wrote:
>> 
>> Song Liu <songliubraving@fb.com> writes:
>> 
>>> With fentry/fexit programs, it is possible to profile BPF program with
>>> hardware counters. Introduce bpftool "prog profile", which measures key
>>> metrics of a BPF program.
>>> 
>>> bpftool prog profile command creates per-cpu perf events. Then it attaches
>>> fentry/fexit programs to the target BPF program. The fentry program saves
>>> perf event value to a map. The fexit program reads the perf event again,
>>> and calculates the difference, which is the instructions/cycles used by
>>> the target program.
>>> 
>>> Example input and output:
>>> 
>>>  ./bpftool prog profile 20 id 810 cycles instructions
>>>  cycles: duration 20 run_cnt 1368 miss_cnt 665
>>>          counter 503377 enabled 668202 running 351857
>>>  instructions: duration 20 run_cnt 1368 miss_cnt 707
>>>          counter 398625 enabled 502330 running 272014
>>> 
>>> This command measures cycles and instructions for BPF program with id
>>> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
>>> measured in 665 out of these runs, because of perf event multiplexing
>>> (some perf commands are running in the background). In these runs, the BPF
>>> program consumed 503377 cycles. The perf_event enabled and running time
>>> are 668202 and 351857 respectively.
>>> 
>>> Note that, this approach measures cycles and instructions in very small
>>> increments. So the fentry/fexit programs introduce noticable errors to
>>> the measurement results.
>>> 
>>> The fentry/fexit programs are generated with BPF skeleton. Currently,
>>> generation of the skeleton requires some manual steps.
>>> 
>>> Signed-off-by: Song Liu <songliubraving@fb.com>
>>> ---
>>> tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++
>>> tools/bpf/bpftool/prog.c                  | 387 +++++++++-
>>> tools/bpf/bpftool/skeleton/README         |   3 +
>>> tools/bpf/bpftool/skeleton/profiler.bpf.c | 185 +++++
>>> tools/bpf/bpftool/skeleton/profiler.h     |  47 ++
>>> 5 files changed, 1441 insertions(+), 1 deletion(-)
>>> create mode 100644 tools/bpf/bpftool/profiler.skel.h
>>> create mode 100644 tools/bpf/bpftool/skeleton/README
>>> create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
>>> create mode 100644 tools/bpf/bpftool/skeleton/profiler.h
>>> 
>>> diff --git a/tools/bpf/bpftool/profiler.skel.h b/tools/bpf/bpftool/profiler.skel.h
>>> new file mode 100644
>>> index 000000000000..10e99989c03e
>>> --- /dev/null
>>> +++ b/tools/bpf/bpftool/profiler.skel.h
>>> @@ -0,0 +1,820 @@
>>> +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
>>> +
>>> +/* THIS FILE IS AUTOGENERATED! */
>>> +#ifndef __PROFILER_BPF_SKEL_H__
>>> +#define __PROFILER_BPF_SKEL_H__
>>> +
>>> +#include <stdlib.h>
>>> +#include <bpf/libbpf.h>
>>> +
>>> +struct profiler_bpf {
>>> +	struct bpf_object_skeleton *skeleton;
>>> +	struct bpf_object *obj;
>>> +	struct {
>>> +		struct bpf_map *events;
>>> +		struct bpf_map *fentry_readings;
>>> +		struct bpf_map *accum_readings;
>>> +		struct bpf_map *counts;
>>> +		struct bpf_map *miss_counts;
>>> +		struct bpf_map *rodata;
>>> +	} maps;
>>> +	struct {
>>> +		struct bpf_program *fentry_XXX;
>>> +		struct bpf_program *fexit_XXX;
>>> +	} progs;
>>> +	struct {
>>> +		struct bpf_link *fentry_XXX;
>>> +		struct bpf_link *fexit_XXX;
>>> +	} links;
>>> +	struct profiler_bpf__rodata {
>>> +		__u32 num_cpu;
>>> +		__u32 num_metric;
>>> +	} *rodata;
>>> +};
>>> +
>>> +static void
>>> +profiler_bpf__destroy(struct profiler_bpf *obj)
>>> +{
>>> +	if (!obj)
>>> +		return;
>>> +	if (obj->skeleton)
>>> +		bpf_object__destroy_skeleton(obj->skeleton);
>>> +	free(obj);
>>> +}
>>> +
>>> +static inline int
>>> +profiler_bpf__create_skeleton(struct profiler_bpf *obj);
>>> +
>>> +static inline struct profiler_bpf *
>>> +profiler_bpf__open_opts(const struct bpf_object_open_opts *opts)
>>> +{
>>> +	struct profiler_bpf *obj;
>>> +
>>> +	obj = (typeof(obj))calloc(1, sizeof(*obj));
>>> +	if (!obj)
>>> +		return NULL;
>>> +	if (profiler_bpf__create_skeleton(obj))
>>> +		goto err;
>>> +	if (bpf_object__open_skeleton(obj->skeleton, opts))
>>> +		goto err;
>>> +
>>> +	return obj;
>>> +err:
>>> +	profiler_bpf__destroy(obj);
>>> +	return NULL;
>>> +}
>>> +
>>> +static inline struct profiler_bpf *
>>> +profiler_bpf__open(void)
>>> +{
>>> +	return profiler_bpf__open_opts(NULL);
>>> +}
>>> +
>>> +static inline int
>>> +profiler_bpf__load(struct profiler_bpf *obj)
>>> +{
>>> +	return bpf_object__load_skeleton(obj->skeleton);
>>> +}
>>> +
>>> +static inline struct profiler_bpf *
>>> +profiler_bpf__open_and_load(void)
>>> +{
>>> +	struct profiler_bpf *obj;
>>> +
>>> +	obj = profiler_bpf__open();
>>> +	if (!obj)
>>> +		return NULL;
>>> +	if (profiler_bpf__load(obj)) {
>>> +		profiler_bpf__destroy(obj);
>>> +		return NULL;
>>> +	}
>>> +	return obj;
>>> +}
>>> +
>>> +static inline int
>>> +profiler_bpf__attach(struct profiler_bpf *obj)
>>> +{
>>> +	return bpf_object__attach_skeleton(obj->skeleton);
>>> +}
>>> +
>>> +static inline void
>>> +profiler_bpf__detach(struct profiler_bpf *obj)
>>> +{
>>> +	return bpf_object__detach_skeleton(obj->skeleton);
>>> +}
>>> +
>>> +static inline int
>>> +profiler_bpf__create_skeleton(struct profiler_bpf *obj)
>>> +{
>>> +	struct bpf_object_skeleton *s;
>>> +
>>> +	s = (typeof(s))calloc(1, sizeof(*s));
>>> +	if (!s)
>>> +		return -1;
>>> +	obj->skeleton = s;
>>> +
>>> +	s->sz = sizeof(*s);
>>> +	s->name = "profiler_bpf";
>>> +	s->obj = &obj->obj;
>>> +
>>> +	/* maps */
>>> +	s->map_cnt = 6;
>>> +	s->map_skel_sz = sizeof(*s->maps);
>>> +	s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
>>> +	if (!s->maps)
>>> +		goto err;
>>> +
>>> +	s->maps[0].name = "events";
>>> +	s->maps[0].map = &obj->maps.events;
>>> +
>>> +	s->maps[1].name = "fentry_readings";
>>> +	s->maps[1].map = &obj->maps.fentry_readings;
>>> +
>>> +	s->maps[2].name = "accum_readings";
>>> +	s->maps[2].map = &obj->maps.accum_readings;
>>> +
>>> +	s->maps[3].name = "counts";
>>> +	s->maps[3].map = &obj->maps.counts;
>>> +
>>> +	s->maps[4].name = "miss_counts";
>>> +	s->maps[4].map = &obj->maps.miss_counts;
>>> +
>>> +	s->maps[5].name = "profiler.rodata";
>>> +	s->maps[5].map = &obj->maps.rodata;
>>> +	s->maps[5].mmaped = (void **)&obj->rodata;
>>> +
>>> +	/* programs */
>>> +	s->prog_cnt = 2;
>>> +	s->prog_skel_sz = sizeof(*s->progs);
>>> +	s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
>>> +	if (!s->progs)
>>> +		goto err;
>>> +
>>> +	s->progs[0].name = "fentry_XXX";
>>> +	s->progs[0].prog = &obj->progs.fentry_XXX;
>>> +	s->progs[0].link = &obj->links.fentry_XXX;
>>> +
>>> +	s->progs[1].name = "fexit_XXX";
>>> +	s->progs[1].prog = &obj->progs.fexit_XXX;
>>> +	s->progs[1].link = &obj->links.fexit_XXX;
>>> +
>>> +	s->data_sz = 18256;
>>> +	s->data = (void *)"\
>>> +\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
>> 
>> Holy binary blob, Batman! :)
>> 
>> What is this blob, exactly? The bytecode output of a precompiled
>> program?
>
> It is the skeleton compiled from profiler.bpf.c. Please refer to 
> the README file for step to generate it.

Ah, right, seems I managed to skip over the second half of the patch so
missed that :)

> In long term, we should include the generation of this blob in the 
> make process. But for RFC, I kept it simple for now. :)

Yes, I agree; but fair enough.

-Toke


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-13 21:01 ` [RFC bpf-next 3/4] bpftool: introduce "prog profile" command Song Liu
  2020-02-13 21:50   ` Toke Høiland-Jørgensen
@ 2020-02-15  0:45   ` Alexei Starovoitov
  2020-02-17  2:51     ` Song Liu
  1 sibling, 1 reply; 10+ messages in thread
From: Alexei Starovoitov @ 2020-02-15  0:45 UTC (permalink / raw)
  To: Song Liu; +Cc: netdev, bpf, kernel-team, ast, daniel

On Thu, Feb 13, 2020 at 01:01:14PM -0800, Song Liu wrote:
> With fentry/fexit programs, it is possible to profile BPF program with
> hardware counters. Introduce bpftool "prog profile", which measures key
> metrics of a BPF program.
> 
> bpftool prog profile command creates per-cpu perf events. Then it attaches
> fentry/fexit programs to the target BPF program. The fentry program saves
> perf event value to a map. The fexit program reads the perf event again,
> and calculates the difference, which is the instructions/cycles used by
> the target program.
> 
> Example input and output:
> 
>   ./bpftool prog profile 20 id 810 cycles instructions
>   cycles: duration 20 run_cnt 1368 miss_cnt 665
>           counter 503377 enabled 668202 running 351857
>   instructions: duration 20 run_cnt 1368 miss_cnt 707
>           counter 398625 enabled 502330 running 272014
> 
> This command measures cycles and instructions for BPF program with id
> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
> measured in 665 out of these runs, because of perf event multiplexing
> (some perf commands are running in the background). In these runs, the BPF
> program consumed 503377 cycles. The perf_event enabled and running time
> are 668202 and 351857 respectively.

if (diff.enabled > diff.running) increment miss_cnt.
Why show this to users?
I think 'miss_cnt' the users will interpret as data is bogus,
but it only means that the counter was multiplexed.
The data is still accurate, no?
This condition will probably be hit fairly often, no?

>  tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++

I think bpftool needs to be build twice to avoid this.

Could you change the output format to be 'perf stat' like:
         55,766.51 msec task-clock                #    0.996 CPUs utilized
             4,891      context-switches          #    0.088 K/sec
                31      cpu-migrations            #    0.001 K/sec
         1,806,065      page-faults               #    0.032 M/sec
   166,819,295,451      cycles                    #    2.991 GHz                      (50.12%)
   251,115,795,764      instructions              #    1.51  insn per cycle           (50.10%)

Also printing 'duration' is unnecessary. The user specified it at the command
line and it doesn't need to be reported back to the user.
Can you also make it optional? Until users Ctrl-C's bpftool ?
So it may look like:
$ ./bpftool prog profile id 810 cycles instructions
             1,368      run_cnt
           503,377      cycles
           398,625      instructions         # 0.79 insn per cycle

Computing additional things like 'insn per cycle' do help humans to
pay attention the issue. Like <1 ipc is not great and the next step
would be to profile this program for cache misses.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
  2020-02-15  0:45   ` Alexei Starovoitov
@ 2020-02-17  2:51     ` Song Liu
  0 siblings, 0 replies; 10+ messages in thread
From: Song Liu @ 2020-02-17  2:51 UTC (permalink / raw)
  To: Alexei Starovoitov; +Cc: netdev, bpf, Kernel Team, ast, daniel



> On Feb 14, 2020, at 4:45 PM, Alexei Starovoitov <alexei.starovoitov@gmail.com> wrote:
> 
> On Thu, Feb 13, 2020 at 01:01:14PM -0800, Song Liu wrote:
>> With fentry/fexit programs, it is possible to profile BPF program with
>> hardware counters. Introduce bpftool "prog profile", which measures key
>> metrics of a BPF program.
>> 
>> bpftool prog profile command creates per-cpu perf events. Then it attaches
>> fentry/fexit programs to the target BPF program. The fentry program saves
>> perf event value to a map. The fexit program reads the perf event again,
>> and calculates the difference, which is the instructions/cycles used by
>> the target program.
>> 
>> Example input and output:
>> 
>>  ./bpftool prog profile 20 id 810 cycles instructions
>>  cycles: duration 20 run_cnt 1368 miss_cnt 665
>>          counter 503377 enabled 668202 running 351857
>>  instructions: duration 20 run_cnt 1368 miss_cnt 707
>>          counter 398625 enabled 502330 running 272014
>> 
>> This command measures cycles and instructions for BPF program with id
>> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
>> measured in 665 out of these runs, because of perf event multiplexing
>> (some perf commands are running in the background). In these runs, the BPF
>> program consumed 503377 cycles. The perf_event enabled and running time
>> are 668202 and 351857 respectively.
> 
> if (diff.enabled > diff.running) increment miss_cnt.
> Why show this to users?
> I think 'miss_cnt' the users will interpret as data is bogus,
> but it only means that the counter was multiplexed.
> The data is still accurate, no?

We (or the user) need to be careful to get all the math correct:

For # of run per second, we need total count;
For cycles per run, we need non-miss-count (total_count - miss_cnt). 

So miss_cnt is useful for some users. 

One thing tricky here is that different events in the same session may 
have different miss_cnt. I just realized that we can probably avoid most 
of such cases, and only take samples when all the counters are counting. 

> This condition will probably be hit fairly often, no?

This really depends on the system. Data center servers usually have a 
few perf_event running 24/7. We are more likely to hit multiplexing on
these systems. 

> 
>> tools/bpf/bpftool/profiler.skel.h         | 820 ++++++++++++++++++++++
> 
> I think bpftool needs to be build twice to avoid this.
> 
> Could you change the output format to be 'perf stat' like:
>         55,766.51 msec task-clock                #    0.996 CPUs utilized
>             4,891      context-switches          #    0.088 K/sec
>                31      cpu-migrations            #    0.001 K/sec
>         1,806,065      page-faults               #    0.032 M/sec
>   166,819,295,451      cycles                    #    2.991 GHz                      (50.12%)
>   251,115,795,764      instructions              #    1.51  insn per cycle           (50.10%)

Will try this in next version. 

> 
> Also printing 'duration' is unnecessary. The user specified it at the command
> line and it doesn't need to be reported back to the user.
> Can you also make it optional? Until users Ctrl-C's bpftool ?

Yes, I plan to add the Ctrl-C approach. Even with a duration, user can still 
hit Ctrl-C and get partial results. I guess, we should show the duration for 
when user Ctrl-C?

> So it may look like:
> $ ./bpftool prog profile id 810 cycles instructions
>             1,368      run_cnt
>           503,377      cycles
>           398,625      instructions         # 0.79 insn per cycle
> 
> Computing additional things like 'insn per cycle' do help humans to
> pay attention the issue. Like <1 ipc is not great and the next step
> would be to profile this program for cache misses.

Yes, instruction per cycle is useful. Let me add that. 


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2020-02-17  2:52 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-13 21:01 [RFC bpf-next 0/4] bpftool: introduce prog profile Song Liu
2020-02-13 21:01 ` [RFC bpf-next 1/4] bpf: allow bpf_perf_event_read_value in all BPF programs Song Liu
2020-02-13 21:01 ` [RFC bpf-next 2/4] libbpf: introduce bpf_program__overwrite_section_name() Song Liu
2020-02-13 21:01 ` [RFC bpf-next 3/4] bpftool: introduce "prog profile" command Song Liu
2020-02-13 21:50   ` Toke Høiland-Jørgensen
2020-02-13 21:55     ` Song Liu
2020-02-13 22:06       ` Toke Høiland-Jørgensen
2020-02-15  0:45   ` Alexei Starovoitov
2020-02-17  2:51     ` Song Liu
2020-02-13 21:01 ` [RFC bpf-next 4/4] bpftool: Documentation for bpftool prog profile Song Liu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).