summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorYafang Shao <laoar.shao@gmail.com>2023-07-09 02:56:30 +0000
committerAlexei Starovoitov <ast@kernel.org>2023-07-11 20:07:51 -0700
commit88d6160737fa7bec9addb9c479b8166e88bc5ff5 (patch)
treefbdb473f6f59f6c119912118c395ca183dd24e65 /tools
parent62b57e3ddd64002f5b3f6fb2ea50b79a2994cfec (diff)
downloadlinux-88d6160737fa7bec9addb9c479b8166e88bc5ff5.tar.gz
linux-88d6160737fa7bec9addb9c479b8166e88bc5ff5.tar.bz2
linux-88d6160737fa7bec9addb9c479b8166e88bc5ff5.zip
bpftool: Show perf link info
Enhance bpftool to display comprehensive information about exposed perf_event links, covering uprobe, kprobe, tracepoint, and generic perf event. The resulting output will include the following details: $ tools/bpf/bpftool/bpftool link show 3: perf_event prog 14 event software:cpu-clock bpf_cookie 0 pids perf_event(19483) 4: perf_event prog 14 event hw-cache:LLC-load-misses bpf_cookie 0 pids perf_event(19483) 5: perf_event prog 14 event hardware:cpu-cycles bpf_cookie 0 pids perf_event(19483) 6: perf_event prog 19 tracepoint sched_switch bpf_cookie 0 pids tracepoint(20947) 7: perf_event prog 26 uprobe /home/dev/waken/bpf/uprobe/a.out+0x1338 bpf_cookie 0 pids uprobe(21973) 8: perf_event prog 27 uretprobe /home/dev/waken/bpf/uprobe/a.out+0x1338 bpf_cookie 0 pids uprobe(21973) 10: perf_event prog 43 kprobe ffffffffb70a9660 kernel_clone bpf_cookie 0 pids kprobe(35275) 11: perf_event prog 41 kretprobe ffffffffb70a9660 kernel_clone bpf_cookie 0 pids kprobe(35275) $ tools/bpf/bpftool/bpftool link show -j [{"id":3,"type":"perf_event","prog_id":14,"event_type":"software","event_config":"cpu-clock","bpf_cookie":0,"pids":[{"pid":19483,"comm":"perf_event"}]},{"id":4,"type":"perf_event","prog_id":14,"event_type":"hw-cache","event_config":"LLC-load-misses","bpf_cookie":0,"pids":[{"pid":19483,"comm":"perf_event"}]},{"id":5,"type":"perf_event","prog_id":14,"event_type":"hardware","event_config":"cpu-cycles","bpf_cookie":0,"pids":[{"pid":19483,"comm":"perf_event"}]},{"id":6,"type":"perf_event","prog_id":19,"tracepoint":"sched_switch","bpf_cookie":0,"pids":[{"pid":20947,"comm":"tracepoint"}]},{"id":7,"type":"perf_event","prog_id":26,"retprobe":false,"file":"/home/dev/waken/bpf/uprobe/a.out","offset":4920,"bpf_cookie":0,"pids":[{"pid":21973,"comm":"uprobe"}]},{"id":8,"type":"perf_event","prog_id":27,"retprobe":true,"file":"/home/dev/waken/bpf/uprobe/a.out","offset":4920,"bpf_cookie":0,"pids":[{"pid":21973,"comm":"uprobe"}]},{"id":10,"type":"perf_event","prog_id":43,"retprobe":false,"addr":18446744072485508704,"func":"kernel_clone","offset":0,"bpf_cookie":0,"pids":[{"pid":35275,"comm":"kprobe"}]},{"id":11,"type":"perf_event","prog_id":41,"retprobe":true,"addr":18446744072485508704,"func":"kernel_clone","offset":0,"bpf_cookie":0,"pids":[{"pid":35275,"comm":"kprobe"}]}] For generic perf events, the displayed information in bpftool is limited to the type and configuration, while other attributes such as sample_period, sample_freq, etc., are not included. The kernel function address won't be exposed if it is not permitted by kptr_restrict. The result as follows when kptr_restrict is 2. $ tools/bpf/bpftool/bpftool link show 3: perf_event prog 14 event software:cpu-clock 4: perf_event prog 14 event hw-cache:LLC-load-misses 5: perf_event prog 14 event hardware:cpu-cycles 6: perf_event prog 19 tracepoint sched_switch 7: perf_event prog 26 uprobe /home/dev/waken/bpf/uprobe/a.out+0x1338 8: perf_event prog 27 uretprobe /home/dev/waken/bpf/uprobe/a.out+0x1338 10: perf_event prog 43 kprobe kernel_clone 11: perf_event prog 41 kretprobe kernel_clone Signed-off-by: Yafang Shao <laoar.shao@gmail.com> Reviewed-by: Quentin Monnet <quentin@isovalent.com> Acked-by: Jiri Olsa <jolsa@kernel.org> Link: https://lore.kernel.org/r/20230709025630.3735-11-laoar.shao@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/bpf/bpftool/link.c247
1 files changed, 246 insertions, 1 deletions
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index 8e4d9176a6e8..65a168df63bc 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -17,6 +17,8 @@
#include "main.h"
#include "xlated_dumper.h"
+#define PERF_HW_CACHE_LEN 128
+
static struct hashmap *link_table;
static struct dump_data dd;
@@ -279,6 +281,110 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
jsonw_end_array(json_wtr);
}
+static void
+show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+ jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_KRETPROBE);
+ jsonw_uint_field(wtr, "addr", info->perf_event.kprobe.addr);
+ jsonw_string_field(wtr, "func",
+ u64_to_ptr(info->perf_event.kprobe.func_name));
+ jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset);
+}
+
+static void
+show_perf_event_uprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+ jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_URETPROBE);
+ jsonw_string_field(wtr, "file",
+ u64_to_ptr(info->perf_event.uprobe.file_name));
+ jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset);
+}
+
+static void
+show_perf_event_tracepoint_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+ jsonw_string_field(wtr, "tracepoint",
+ u64_to_ptr(info->perf_event.tracepoint.tp_name));
+}
+
+static char *perf_config_hw_cache_str(__u64 config)
+{
+ const char *hw_cache, *result, *op;
+ char *str = malloc(PERF_HW_CACHE_LEN);
+
+ if (!str) {
+ p_err("mem alloc failed");
+ return NULL;
+ }
+
+ hw_cache = perf_event_name(evsel__hw_cache, config & 0xff);
+ if (hw_cache)
+ snprintf(str, PERF_HW_CACHE_LEN, "%s-", hw_cache);
+ else
+ snprintf(str, PERF_HW_CACHE_LEN, "%lld-", config & 0xff);
+
+ op = perf_event_name(evsel__hw_cache_op, (config >> 8) & 0xff);
+ if (op)
+ snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
+ "%s-", op);
+ else
+ snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
+ "%lld-", (config >> 8) & 0xff);
+
+ result = perf_event_name(evsel__hw_cache_result, config >> 16);
+ if (result)
+ snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
+ "%s", result);
+ else
+ snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
+ "%lld", config >> 16);
+ return str;
+}
+
+static const char *perf_config_str(__u32 type, __u64 config)
+{
+ const char *perf_config;
+
+ switch (type) {
+ case PERF_TYPE_HARDWARE:
+ perf_config = perf_event_name(event_symbols_hw, config);
+ break;
+ case PERF_TYPE_SOFTWARE:
+ perf_config = perf_event_name(event_symbols_sw, config);
+ break;
+ case PERF_TYPE_HW_CACHE:
+ perf_config = perf_config_hw_cache_str(config);
+ break;
+ default:
+ perf_config = NULL;
+ break;
+ }
+ return perf_config;
+}
+
+static void
+show_perf_event_event_json(struct bpf_link_info *info, json_writer_t *wtr)
+{
+ __u64 config = info->perf_event.event.config;
+ __u32 type = info->perf_event.event.type;
+ const char *perf_type, *perf_config;
+
+ perf_type = perf_event_name(perf_type_name, type);
+ if (perf_type)
+ jsonw_string_field(wtr, "event_type", perf_type);
+ else
+ jsonw_uint_field(wtr, "event_type", type);
+
+ perf_config = perf_config_str(type, config);
+ if (perf_config)
+ jsonw_string_field(wtr, "event_config", perf_config);
+ else
+ jsonw_uint_field(wtr, "event_config", config);
+
+ if (type == PERF_TYPE_HW_CACHE && perf_config)
+ free((void *)perf_config);
+}
+
static int show_link_close_json(int fd, struct bpf_link_info *info)
{
struct bpf_prog_info prog_info;
@@ -334,6 +440,26 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_KPROBE_MULTI:
show_kprobe_multi_json(info, json_wtr);
break;
+ case BPF_LINK_TYPE_PERF_EVENT:
+ switch (info->perf_event.type) {
+ case BPF_PERF_EVENT_EVENT:
+ show_perf_event_event_json(info, json_wtr);
+ break;
+ case BPF_PERF_EVENT_TRACEPOINT:
+ show_perf_event_tracepoint_json(info, json_wtr);
+ break;
+ case BPF_PERF_EVENT_KPROBE:
+ case BPF_PERF_EVENT_KRETPROBE:
+ show_perf_event_kprobe_json(info, json_wtr);
+ break;
+ case BPF_PERF_EVENT_UPROBE:
+ case BPF_PERF_EVENT_URETPROBE:
+ show_perf_event_uprobe_json(info, json_wtr);
+ break;
+ default:
+ break;
+ }
+ break;
default:
break;
}
@@ -505,6 +631,75 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
}
}
+static void show_perf_event_kprobe_plain(struct bpf_link_info *info)
+{
+ const char *buf;
+
+ buf = u64_to_ptr(info->perf_event.kprobe.func_name);
+ if (buf[0] == '\0' && !info->perf_event.kprobe.addr)
+ return;
+
+ if (info->perf_event.type == BPF_PERF_EVENT_KRETPROBE)
+ printf("\n\tkretprobe ");
+ else
+ printf("\n\tkprobe ");
+ if (info->perf_event.kprobe.addr)
+ printf("%llx ", info->perf_event.kprobe.addr);
+ printf("%s", buf);
+ if (info->perf_event.kprobe.offset)
+ printf("+%#x", info->perf_event.kprobe.offset);
+ printf(" ");
+}
+
+static void show_perf_event_uprobe_plain(struct bpf_link_info *info)
+{
+ const char *buf;
+
+ buf = u64_to_ptr(info->perf_event.uprobe.file_name);
+ if (buf[0] == '\0')
+ return;
+
+ if (info->perf_event.type == BPF_PERF_EVENT_URETPROBE)
+ printf("\n\turetprobe ");
+ else
+ printf("\n\tuprobe ");
+ printf("%s+%#x ", buf, info->perf_event.uprobe.offset);
+}
+
+static void show_perf_event_tracepoint_plain(struct bpf_link_info *info)
+{
+ const char *buf;
+
+ buf = u64_to_ptr(info->perf_event.tracepoint.tp_name);
+ if (buf[0] == '\0')
+ return;
+
+ printf("\n\ttracepoint %s ", buf);
+}
+
+static void show_perf_event_event_plain(struct bpf_link_info *info)
+{
+ __u64 config = info->perf_event.event.config;
+ __u32 type = info->perf_event.event.type;
+ const char *perf_type, *perf_config;
+
+ printf("\n\tevent ");
+ perf_type = perf_event_name(perf_type_name, type);
+ if (perf_type)
+ printf("%s:", perf_type);
+ else
+ printf("%u :", type);
+
+ perf_config = perf_config_str(type, config);
+ if (perf_config)
+ printf("%s ", perf_config);
+ else
+ printf("%llu ", config);
+
+ if (type == PERF_TYPE_HW_CACHE && perf_config)
+ free((void *)perf_config);
+}
+
static int show_link_close_plain(int fd, struct bpf_link_info *info)
{
struct bpf_prog_info prog_info;
@@ -553,6 +748,26 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_KPROBE_MULTI:
show_kprobe_multi_plain(info);
break;
+ case BPF_LINK_TYPE_PERF_EVENT:
+ switch (info->perf_event.type) {
+ case BPF_PERF_EVENT_EVENT:
+ show_perf_event_event_plain(info);
+ break;
+ case BPF_PERF_EVENT_TRACEPOINT:
+ show_perf_event_tracepoint_plain(info);
+ break;
+ case BPF_PERF_EVENT_KPROBE:
+ case BPF_PERF_EVENT_KRETPROBE:
+ show_perf_event_kprobe_plain(info);
+ break;
+ case BPF_PERF_EVENT_UPROBE:
+ case BPF_PERF_EVENT_URETPROBE:
+ show_perf_event_uprobe_plain(info);
+ break;
+ default:
+ break;
+ }
+ break;
default:
break;
}
@@ -575,11 +790,12 @@ static int do_show_link(int fd)
struct bpf_link_info info;
__u32 len = sizeof(info);
__u64 *addrs = NULL;
- char buf[256];
+ char buf[PATH_MAX];
int count;
int err;
memset(&info, 0, sizeof(info));
+ buf[0] = '\0';
again:
err = bpf_link_get_info_by_fd(fd, &info, &len);
if (err) {
@@ -614,6 +830,35 @@ again:
goto again;
}
}
+ if (info.type == BPF_LINK_TYPE_PERF_EVENT) {
+ switch (info.perf_event.type) {
+ case BPF_PERF_EVENT_TRACEPOINT:
+ if (!info.perf_event.tracepoint.tp_name) {
+ info.perf_event.tracepoint.tp_name = ptr_to_u64(&buf);
+ info.perf_event.tracepoint.name_len = sizeof(buf);
+ goto again;
+ }
+ break;
+ case BPF_PERF_EVENT_KPROBE:
+ case BPF_PERF_EVENT_KRETPROBE:
+ if (!info.perf_event.kprobe.func_name) {
+ info.perf_event.kprobe.func_name = ptr_to_u64(&buf);
+ info.perf_event.kprobe.name_len = sizeof(buf);
+ goto again;
+ }
+ break;
+ case BPF_PERF_EVENT_UPROBE:
+ case BPF_PERF_EVENT_URETPROBE:
+ if (!info.perf_event.uprobe.file_name) {
+ info.perf_event.uprobe.file_name = ptr_to_u64(&buf);
+ info.perf_event.uprobe.name_len = sizeof(buf);
+ goto again;
+ }
+ break;
+ default:
+ break;
+ }
+ }
if (json_output)
show_link_close_json(fd, &info);
}d 5sV]P٧:{Nc ~B%J Cq(1$dB+V yCTW}+e Ĵ! yw;*o:8Z)m(B*4|pb7H-JK sqm>oxؐTg pױZ(-MlxE$b9JFxscH{ifCra9~m xcZ%{ snu^yWC0+rpB(v- <"9He(d' j{dZ%imi Rg6C4 $sÍ\(| o+6䊵wƣ4u&ls|A^y+5KAD]`Ҕ'\7JGLX y9a9yi֖L}t@W11C(Җ/LY\ "]xO)<޾^Ub/Ѐ"F!7M`y4JT[r/(~%WwɅ #;7W&tfTl1vy_C9Ōݎe.X5~w^ۻyt}xxc4^Ss3p aAQ&`dpBֳyDh}w mˤ[W-PP&zxgk Ǹ9匀1!g8 4MЀE0i( '0THO$<:T(tTnr S6wJ @${vf "e'L,YjHR}4au+ZXI?9qTW~h{asʊwiv}ǧm/"xRy06CI~5 $33('5FFBRBY-X^M^|'jmC _ݔHzwkS! gnz S`dlEYeE2~$Sjw2j>0O6'mZ)R4 "dӝwH"_Y>n=GJ` =}<ݥ=Yk3ᕸ#i<Ϫ"I7ޑӼ7,B4b'ΨGLEJq8?#h~R٠Y:ޔ6g}wXN.)2(D6F[x.4C)a9 - 'wߛ2>|2Rm%6%LȼOpP(vKkvd(PPX] cȜMv*53+>41/T}Zn>T<j)Yt DSdDas&_.n9l'z8ْ 2zǪ(K55ϣFgn3j@ W1IA)3gf:&?2yJ#ٳU(½3m6W; [OiܬD#f!*>jmL4Mjepӥ}"BS"6c^Y]1הM[]cTVPO: JyxՄGc*l#byb]*, ۯ^!ӡRG# ( fVHo-Yg=sP:TV3| zJz{WзH򥒨5 $.!LR"GF#GӾ^X3EƤGpdJ'e͡{Ӭ$fBGS3b^xꐧ6 Yr)r&d Mm0!Bihb`jNjD?f~7m63} KrUҒuk:ӛ[TkPwA bk12hgy6B l9L+9#7GIuH4M>f1)F]㌧8'vydad~_ Xd#6`j@=nch}3V "]Ze8c,KƖl[gaAOuXEQAҒ~ #uCgz -%J?,GsaTX(*m!xD/ y:kK[- )CtBcW+=!lo;RUlFJ/Ӳs"LrOfXߍC1t(b0.L,;ny^rs_M,=fpJ\~)((WhATi$p|T]'9{ FوM@ҀsI> KWOZj3wOuS%eݎ-s2q'z4cϐ&]3Yԣ,ɗk ;/{-2Q^ 1h333krG&#kDb0DDg-y"c_C` ySi~J C\8E2SJlhkdOi%;dF?FM'2%zp8Đ)`12[Oy dp-Mo,?u,ZU > ҊFN e:"@Nj %+<}">u&lq ]P[_M3m)M#ޣu w N^#ോ#Z[@FSw 3sK8 ,7*.f=LCڅuR#u="Bh((x T_{I| PӨ6ipO1Wq1|H)u*nl*Ӑh"BcZCO|ėq5jc r‡hs#e<,Q.('屧pb^ZfUZY)LGxGDzwQBi5cbVlX>CP#P ~nC_G|4َË:q6L8%s;Ig 8ɺb~Y6D$C3rឋ|(٤~]?*Kd6@r=hhmOxMu3PI`*ϐ[TeV D~|750奓c:6}cqYE"xT@Zy{e\4`ZL(Rͭ )jGI7(Vx84N(* Ld#ݠs NiPvoh<#fW>;nl㩝b({Ï]=~>[ hE *EC00V'j#0@!Η__BlxS^R6Z7B[, ,k~7iI_eh!Gf|߯Laa즛 _^AhQaFrhZ|~͗),5?M7 ۭQ[pxi5YtQKsJAF'\& QSh) (y{/.U_ 0LV ӻzpXm~d['fʪ BI־Uc㽾%,;NKoݺH)!.$H[bnϛo r tS rTVDRsyuFm, MG~$[g3 -yDiY6ʉth#?䆯ԇD2a), ց΁:VvouҔ2GمIɨyC{#Rl>ing!-hhILU|&$Ug# Blajq$M̀(\UB=\_ñ7C!v> (z<^D;<Ar`aK)Y{B)E:tRU5aPLj3!KV&Gg0D\6GKZU3}xh䤘0gK]k6kW3G}|ND&D}h[ >: s"$T ?~z 蚱Z ٔ-MIg[Ɖo;aXL+򊽽#_>:;S~XeSm,Z.ڢCP{o ꠂC;ʉp+(nFQ`EWLU ~CFfQr O,a4n/0+(4]"ޕzD>k!cUNaL%V"xf%'A+/it(hm3w8:h!vա?4,fƃ9e{, ڎRxq ‹şJcŒBZq <#s[r|A zuceP&ԡ܈Xh#V#}~4E Oq1{ET ;:TljHVV0>񍲑ۖoGߑ&7w'1Qo@C&V'tuyT ߍ-h-(&n7fHc!cC JrHy:f+::M[2qGP8oޫޯbOm9LC&|ljE.U e}\ \Z;]n؈!.ok lnIwcAx2O~ev0Ì(ث1:㖨û\FP^Q1ϓa,;|໯<#R!o" RJ#xK7ZlR&-0pUfS)xXW-ip6h'GIuU"_4~K̥3'‘I㓩g_D=IOu]U#9YbNje+w!R_Et1<p~i 5C7^$o?lɁM8 6.K{9(8e AKha.cCHSjxω=_HVbGgb$z63Š53"0&©)`Kx[y$~{ \R`k.?*ǒ"#5sX2)Ŭ.e9Ns_U Ǥ]H0ϙ@TGc:_ͣ8d qgg%\.ocL D@џD[բ~#W/k0zT.H_]È?;ZնѠ^ %0AxxNiw^n$Ϗȑq(t66Av398"\msaJ8wE7IU%WUs8`4+}G@/_G D槕}a~u/O HگX R냀;z*QQŜtI hG$R}S˘1g?:~+[.6_< NɡiȩNA^b6=~l! EC%| GDg&{oP;* zxDzq4$SsYl5@T-zknTYc742D !/\꣒+($_a07͠(|-[)~}T J%rLatc1Bs8~ @[F&Qȥ6m^nan,]tߊʓ)?L]3ҢR{CЯd-I#Tb&e riN4܈B%lP9bo(Y%El/9 sE}`R4f(t2|\*z9SO~\a Ԍ&O(uA>\ցD$*8m@S, ^c%R^|3 \G93^Q/wOKإ{ 7vzAF 9E% Z@nXE~0hY `||Y4.JEhgMߢtLRj\Hg@q=e*YImkm6<^oa@͝?"n=~jDu!jPwm# Wt(}.󂓱9c&NߏeiŹiR< Zo$K$2͹F$:2qsdF*^axg0dtd^ dp2k#zրox|Q{\| _rV"᤭єtY=gȽg|#C͛{dɸl |Ї'IKN'Zi]v1aool5G9;&)\.z ]|=n״3lb l ,U,8,*W 1.v43f/ ¬SR,c[gﰷR#:5:Ll1;\≬Aը?22v͉qV~^ ʞ|dU$) vTޟNG_A;(Pk أF~N P8oL0s[ݚc= X&d1kYGCl:7:](G/*ۈ[Af0[рtmMܫKӎ(.m}8Ÿg<8=Ƥ  /vSPO,TcU ܹF.P1 LpY\TANseqvpV_ӫGvsa$#NU6a9kel"Rl(zm=+GϚsU1٬dF=,AQf tjҁE9+VkXk:wۺ!e?=U0ikW) S6EҖ'?Rhm.?lh.!G|1"7<ۮW-dPOK9Egh\뼞ӣAC8dF\5+ERAYX|DJkv*/yW<2>a6>v 0楆 S%"q<oě4Y3u &:1 *T [}N\S԰ 5c h#HJ+5dIa=f1O u̹ 쿣{>zhӷ5d׏*AR_J"-,YP؝l_^j!d:z/dPƼg*P{WXw2Av˛Dp`ťw I֗׋E`hLbGI~9 Fhք#/ Q+Np 9c GnvbOzu0RU{EH/* VÉ4qbtqv#'w+|0rmwcD{#uP;n{/w{yiWaqg_$8! ݸrZCI95xXiJ8a_>wfdгE ;oym1ΈktkiKI_Ŧ"u@ǀկ=Xyұ|yeEQjH]{W,giI0a]÷'X:j!f-'[mj5 hvXy2 12a9UeDD9"H';!;w}p"ݒVEIT& 6,3"vrݥZ#k(pTٞgL2cbf>ˌM~^CL־K ^Oil#L+cxX|K4SC15K+Oi! wi` ᢪي&c# gFbk3>nn}1UnGkDɴQ1b13^$fp!ٷַ \gx~Dx~aF؏f]AO~8|#D>Q{$mN z -$4dw!q `}WjMllG\tkRD \}+ nH>կ^h*PᮌPh^vWHL( ).S4~eY2((;eˡNplls]8۔EVq':/u)XAI~v1<hK]~EBWh 6`DZuMr v[P𾕤v<_Z}Ŏűv:.)xeiW:1:'q{ X4%x sa|#x;Aqʊ#¥6f  q jd?]X|5p3ek 4#fdHS;i-X9HQvJ9jd bMƸ$8&W g@rRN+ط}9}pv٥6w_|rZBIBΝ;+]&`7ăh?Cu1fi,BI$Bꧾ0? rVkI.4wR܆k,56> Gq$!ĥL3a1(<~?ߜsov_|$u#bY3XRD+3It,kƄ!133 8+Bibp[ç/,BXzQ\|͗X41 A۞bԤݚJWĴe<|?r/*E!򏕓h32T l0Tzhk ɂ 8K aZ#r%yH'jsG-fJ:n:H;g%Ud}!Kfؽ$I1BG晗:'g[o \ge@َq2g>uoW_xmSȟxQJ|!rK26"*}N1Bm ?CR9/Bƒc^vk_q ,Iu c"vlmX a! RjR ,/2: u~3vyC7l/h{=k[fG9HzS?q _3n|~v )Ew1P(& @ԵmrƬ|FM~Ý꼼;}׎–= N jr639̴r#cNAkn!sVa\mZ$|NKI <%]xWb=[~oS>M2 z_A)[ *VI*d[Gzwj Z+p̭R 5ڏ7*Gv{!E~H_k,9iSq6VWygrmB6izٽLd5X lprjǞc0bws*ڣH'!}WZLpgaϋgY-0@<)~oB)jhizzsL-hv̒,]A}Yn0\p+Pߎjr&_n } >r;߮Q́+%2Xq|w_u-t& OcMSkJr@@-yºמGN]8牊\9i^E$( fq`5kdC\K!Y k\4 336gkb# }A)x6̺N5^ߧH-k" 'p.1= `BӫqdZ'qt ~7kINwڗ-2{DH@r[rwkˁRUd g"P[ΝajV2N 4Hq:o}m$yMpr.e IwQdX 0TI MfAR7T>KCB6,H:hX6vL.Gv^]D͹u^ m} uZbvD:8!^ڸQ8,W_H~R@iYXDC%y3QD@ŖӾ cyPV-FŐƛܬj=qc;B\( :g)QPQeKټoO7u5/K8ɋƎ;"كt8dpƤʽaFEc?}Rҗ5.k扜GQBm =c+OUI>fSCjS&M.8E\ x^in;'q0p+׾[r_{c@9T8fgKS\w9; KRTQqb_\(Qq|hQ 7(ąYG rE4i_鲌ʔ44* !jO |{>GhW0v `(>" vP}֊U >'wwK|3ygT)[]c9uwd 2nG7yT_G1rg`Ytk3}vnrkX#T2xHxP=L!҆=vJIbSJzՆ/ĝˏE.g z5_8#]jJwX97cs s<1$TѹuƵon$ZZÕ9;lr7b 5Q5ZkVk<,,8|+mQ$@+1ktץIuG~0gnb\ÃƣS]DUA,ٝΖfNƖ!"st+"$lb|ч?.\"D{uF`W.>᝴ȑpTHtĻnqϛ(1.l@\A5&tu*W_^[G>qz~\&:+y,csm N'w\*LnD~-#E+ݙ9㖂M~9OVvQ|A 3D6fl;:C6Э}_!ziB՚zf'lMZo˞ WByG+6eͫUTf %*,~ƥD}.վxk&D4xzv9t E4E_ĉ]jqK=qRM|kr͇%ޝqJѣ&SK+ _S8T:NLtO)/R^rOZ}V-YG9|?8-18;_ɡr XF4mF kyݝ52kxy\d%CK@La 8!uܾe`˒ߑnnv.Ф5bTɝ,~F.]ϳ^2(+]$jYW|b=ՙbZź)ncM8`ye3$ÅZ GgN 73Qp8Fks/y63i7 (N>gZmpB{rk)s.t(qpmD5>&v3h0:,ў?::^M Z3FC;{TӠyݦ[ŜsE-bMLCjMFg%F1 ɯwRP)G~ zhF*]`K-E-[j3Wl4⣷zeNv;3u{}LsF0<z/F;NIkg&xs,:dտXD}})>-#zi㏋j>64o`kN? I!wK0,s齊֑jUn}.gq~T^錜аDM2RFݶ z!xX[#ooYXjFdM&n<^}_-QCw.Dd#oluΧ"a碼Jڏs8|VMòbPL w]@îGhǍ;[ \58"Q~@w]cAf"|x 9B&aT,kTxzT-XV+\jVZS$">Ud`9~DR胥['zn-Olb5(P粕c3ư@Vx7ɥaЋKJBFDa }kYA\gV,gk) {QĝP{蹫t{/0D;ˏ?B<{:fS*dC*o+.䠛 I2 Al7pIB~k &C?Fbg WMCCd |/J&W˩P[,ix#t^.bDS Gs|S]VsgBeCȵ[4n9Llo:{E!-)؇iGԇ(W1"-ӗe Mueiir͡- XgfƻQwC5Ӳs NE;uV:q%B]kaۦdǥQ(Xp[V`r' ;i5uÂm 51tЧ~1Aa1NkDΈiaIq FӪM .ȀYLcrψ%HtR|>'?Jp\-wD<]Z#?JF% Ҧm]=ۆprNګ2Q>"#;ɸE bj&ʒ1[v2aL6z m/ ,+eU`(N4/;ߛL3rw Nsgg٭1+Ϳtzx9z8i>1\4Jkk֑N}"!>p1FD"n, >k*f1Zl^u+L מ5!w,2P&L{"N[5jvw뻏VAQr?_,ODY1'mQLtN @lqo7leJ0WӄUvAfQU i;pX?F 7kHM>S<币TY[wH89&0 |s*X[ nPN %@e{S 2}T1TN3|4tF Tpi% xJw ƄuBYVat_~+A8cNa`wSQ+N 8!|P6YƂN:l@_e,G+2!g(8Mra]F. Qҥ8k < 2 SҬ&nMk (gAUa|R$?'̲/oj2;HƜeY~X}~oլnjܞT&wydtuAp 74#jdPGbFI.9 j=ͭ@`ke~(D[(gnG86?J곲QpLZ:*rH挆9Dou{J#'+4WwR}+Gho{h%}5a#bSb`#ʞ~C&u0mJhs4H[-I:r+m)A{؁f$ R^RUGBxްVl9a1U ,r8X2ȂMw(Hî q4ZZDa,˾:;ɖ f1z%$uo1J kvEE/ɬLcLƑL5?/sz#-7=Ciٚ \OMFRzɧ'dcn]XHW3 21R(ēNt[kBJt6C^|}kRFoO>)RcWNڵ;Πyzizz_Y7@3W',9y靃nqL>MWs970W$f!`'}ͥr~OnܷkdJ0hcHlO'ݤw&\]\rƚSfEv:"?e$gCFrGWg}T}#Bw~qzgJv^[zmk\ -kxŋ,G WAHE $V4xl3|E^~&#u3]ԒП68W}2a ]G %xU]ձF]y@av 7[H !?P~ws4gנGZl?h a41!]DhɤyCM62h&h V#%~{iD U'гI4gkt?=&O)j[V4a-4%*˻ï2>,u;K’g=ʢft[uoA&+E#%Y_z7`P"}}VEgkcJFJc~"`IY4CS "7ս59_yúޖ \8ƫ]Ķz0󶌄%|=iEבv˅Ֆ[2|{}9}:UIKwv:[ߵ -{$?G-G~ U-&礽[2Ty)ל]x1xީL=C t,$f1fBfHwgÚUޛшwO[ƾ#(Tp{@k}9`P9jNu|Tc+pLǻG?'#35a_?|5PHqcă;Tݕaۇ۫0?}MI=wZY͉ @|F H*r<6U(z!Sق"R`M*/l𝳂OdZŠqR:#lC2_MVoOG1'dr\ YR-B 6Ii 2g.@,LA~ %~ 1"O;cg'.AJPAu~bél ;s3,fU2%zY("?)(zH8K)B)Q K`G~ LO14TK@ "0S"4%<9T4dCSK^Fh StA?mlL]]SX_ؚ~o'z7|t['?dfMR}\z;=Z}{~%R|_o(+ x Ѫ\ksoٜ&6G;N:5 .Kbd1?*Wv}u(mszڶMK:c>䳲>DbYj*e >pXb'пQ( 4 ,QTK,8O7 H(Ok::?&{BdHeP!_z~elwbv߫nv:oꕜC~V~_԰rN//*;n/Cal9z&<}Zm .Pcף FF$?O@|Es0˕ XkLW|0vygeڗS]N5pזCZd8˷\G=ݢ.HPC<0LMFh3ʲK\<p6yTVbHV5;[>ǟm=~aNݺi~{ܳw_?gȆ?4BK QTH-OO^p6S}#6n'AzpOقq7|>c(n񠹓Aq`<1Zw+ԋ /#/tlϞ'YcDl9rZ/,7.d 0~yiۿcܖKd3 gɫm<][v|e@)8.\&E7peE"‘{x,y/=?Wmitp?~|=ۗl|Fmפz\Y#Osm.釋_kݣ2|fڥ|Mc0^z+Pv }ûuy^smz} o\>ORfM#]|/پX>Fb%[}t +/lāӂ̏Pu > @<vpTRi,+kU^wIV&kC̬hQtf| =uҜR?Ql-1i|RQ"5Ԏ@P[cO}~2Zg8hⳀW alyJ=Q1wkd/u~;8!wC4 퓃rqdZHX~[ϲW܉ewS5;x8s24jи}TDqk{ϳ r  axu4%A烘<5ʽh ڟ8':>*I91p#g! u7[_ͪO7N,n=U0+ݿTFʌ⡞z-c\>=`P9Kk.AsnpsŮvϏUЄ o e[,O9$giI#*+?kzi.?+Z9ׅŻͱGS֪`Zn XqpJ(!۠LX_H~m%Duӏ5P~gF/=Ӯy'J\8f`uV`1`Ok~4?ia6ӒeۣTә?n/+)H?g2o&:غENh/zcsH@ IeiioP"Ӓ-@%vqFn١ӊ%NӄrA.u`-حHs9,#y0a 0([j ȣ3ue,*wΦ 𓉩?od)HgY[` Fb31ey`@!QE@ B$Rh6AܒBjʊz7d0K""X-GK2X3Lwy j~a˶9mRz'Wa+KZi<$CF!'{P 奝όCvqAx"1f1$kŻswPnQ5&\8*bM3[|[ug  %3">VyVZmr t?=ǧSj䕼=;2 #۠Cɥ-ҝ5R;gVp>Qv/љW @DmjZW&'h7`4e|Zq[G.&_Эt< dMzF, gZ8Y51 ]Zog2JQ UU'*2$uVty3-S-Jj5֧@3j{=lf}3_̋ 7\D<݌mWߥѶGK#n쐛ݗb35t==z7ܭ;N>h+BYHQjPd{L׭'POkiCE ,%fkRGam~+,SȈ0uP7ZȎaA)g2#s?_?O~l\!^3vV?ΐ*J}J;Ӆ_ϵ<3,QLg+9PPi})B[h; r3Uu Q'u,a:ݿ}׍fcBfھJcޔ8KƎ_cTx@* 2]L z: QGcdoy511ꆆ֎iwP Oyk&/7~ܫ >Fm#!WαF*Tw5(^` I7^(ѝ} :!7fh4Z+EoXhW_Q93=VUI۝' ٻBULaNc[3TjZRf[WԀ$Qʨ;S[H8R/]ϙUcP0f".`!Ug, 928 ivMeZ2Rxc?4%ox/t3t@0V={ePbmc%JjZa?PArWm#lեmk=5|lӳ]~<߾45B3ҥc5 YU/~;f%zr"s<\G[YHlR`:cm&n"r7ݥszĈ(am m&e (`bO$E VEB*$|EK&њ^S.'*1jWVRoH(dU e(l8=Y5Xٹ{A,XMwu4KCG@$PD!oewkN0tt(eRJ)M C*NJeΎ'TإqV>)"1LSXP`$$fԨ̶,FӫT~1uPO9p:Jc{ComG3i(Yԩ+:lRj7X7k­%O\ZVB=RQq$97T~Mҏ /Ք-U|U4+NwK1Q4!+cSɽ3ю ʹHeb#_bD55xX*l* \į"RbIV&gWei¯ e:!} ({2msnjtZ4Ws &fd䄉?DE o+]"H1w1XA涼-D{U"BBA8.B xXWs9TD,"~G3|0}gγPжO)he=t-TEEsxC &^/t׾x%WF Nv'6p.l/NGOz{q1G/~QC6+:Cfaug52ˮ)W]ZTG42FA-Y J~^{1,Q#b|˶dX%VYȳ~\z*dToiAnsa;LO/wMs"FYg>}]1,0Y87[F",0i䚙[}*y8'PӿIСEHPsXшvZV0t.6+<\X & ANH&Erg,*>nGz3B^7%q7sU UwZ3dlU)"~D`R#(xrIpTeJ<93oA!{;-agr6L4%-{9%{KzJU?Iy֖ ?DgW~6wXmf.+\\ 6B1Hucڅ߷>_sz׶_ hQG~~+MԚ.1%heLtFbFH[^aʲ.9Yn(%eҦUv&PC+3J); 4Х/-S8ʁ̦")h6!n@`zhQ9U ?œ3!?b0fS/.|,sRTs="$eָ1>'Kw66&8Z\o1/Ps좎1<U&8I/}Dz: qWL>QÈ(t+h2%AybC)F;` )/Mշpr}oXq2ODZ35;KvS.*j*aTBrU1u qAxz~K\>{z]/NiU]dyTw\*%< /.&q{{JvfG"c34, p"6B[ԋk 'qmK߀yL= ѳFC`TMuʬ&ԘzHJ1a~^M) &aړN˔רQJlޞ2nk7m|YE6 HiUKy*ֳWvϸfi5;Vl!ЅQ  ZdWa#B^oz٭ăv%تղ]wzicӷM4jRG V"l5Lm |2CfB9~>ܳu`)(X;Z3g+sH4xK.wYB4t;2NVYHɤmb#g `qC=6b"؂%;B}CaRf>Xs7ӑi՛. Mt CmmaB%ƤK n0 SGf  z Zrź4-rAA͐DA 7L#BKgt*碋ꯞa b0+B)%]w?񳅏YO8-o[>\enNn}YH2B!V\mdh;`!ݏAPr@7B*T~[oƌCU|n# (a柢-* S4-[32e$9ٝؑJ Rq"Z_]$bG6J5C$A$veqh0$$M2=cT7<&6HHZBqmQ=Dy4~טZYb @d蟹I`s}9 m/~}jZ?>1;ϯ]D}~D?&~Zbn#`5=Q ]9z#VgWq{=ŕM)!7h4(u-~ ?ۂq&ugf)TZGj:v,+4O@;m_H! K@6~}k;r0ͤorHI%G]EJH6#]% pm$< X 6R )QAub OIX?-ע s DD %Ų4F.m݂eƢʬiAw6u\#8P;Ue285r 8$T"k4ЏOFoXEQ14 }a.W*[+U% ".e˒\-Bs,a~:3u4ʦ+l?9Sup"8JRtJ2C')AêhDž6*H;9C$?CGpGBA%@틢JZ7XiHֶʜ9%q[?qӚ&#R.ҋ7xIطz<0 KVޜGY]8zeyIx&n{4UN޻mZ]ډ ֌(}1-A%QEUQ2URTQCTQCTQIE@PPR5M5E5M5EQE-RUSm1|ò#-~[%DÅI3uBQ`h6 # NK%t"K=Waaք-YfIjNx LJEKSwv lIFouU-<{ $I%5<wyo7xMU RB4$MTз+JUq 4!d$OyVIUI!^C" RҬ QEB03TDUUSTPC@MHH4"% H (]s۰d"90̠3(1 r#l3+%c2V3+ Y-Y vƀ ,ګ \ \ _,ݬL3+3lʲ* \ )c23ɷh F⑸n)#)Xfa>OlZښ."^42@v6iM̞{ CR1w"{4o\@sl|e6Bp=>ԱԷ붣W_53Ƃ1ssf.pK;."9gZ:}o;FYǁ%UOs H!.2mrq_B(#wgs"7& s=:un.87mo՝0vWE {$ތF ~v$%A8-y݋ZP5fWp(HHME*< q+v8ʶPM I͊YF] $X!+}Zڦ *RhL!0`b՘OkoB[>i(3ژhG۴r3Ls  %E0ZГ+ 拫+ǮXFM]Y@HhZgŴ(իWJBμwa0-|DT= EI[3ϛHA-D4yq*QP ,otuؒڛ97;:V )VQ=j2@ݓ,'ZFWq+3a PđRƒUpD6j= ٓMih= $6i*@)% Qʑ(HƠ2hCBD5"BbK*4Uv]b*Rh DɆj^\X\($<#9E4)y*E:^Ο_g߼0SJ$*۲#})pJbQ8Lh(Ah+s׻ PhZwqaܗ&$!py̟v35-1hXZ! Fd9_Dz?F[\N |Ywjo};P&*G 2rٰka}7ԞPP")2[!R)E:`'UvQB:IWj g*xt>4ќ0 j Ҙ™:K1$%,BVYK:o6[N6ݷ]f3[Ȑ>~dC[j?^I#r5A=?yYX9\hW,  j"fXB2dJ aR օ(ē~Kְ-ICb^;"LXRjaI"S3\ƗN|16RB؀iyD k500Ů1 _BLjvl,ptc( a% Vߎ16 `>ԆyG;.glfǵhoЏ<ӧƅi#c1~Fct kYmtA((Gؖ-sYqBREl e/iEXX )3Zn# S«_ZI qܢh ET:1f_n#G_ˎ20h˒h* H Sk?&v1 6lKYqB}_`<)G+-4pJ D# YX>sI7*:lgp4BtR3"Dw걖<#iZzѺЩ2cCɉjųAZ( θIҎ DB*?gsb{2vuv.5YM4~mrUsl\simqopzѝ?ۮXOPEm:4G׋L.8Sd\:t[nЧ]oQ#u=fq5rǜ/zW??$'!`9@HERV` %@oHR;_|-!/ o P-:AO!i?E'\ 7\Dɽ:PQT ~NsB])Q !6u JF*A?Q$ł-X:Q;:1@}qU|g5Tu`TSrcsk$ qƠHf9`'z9W'$09 rZX'9d[p*J}qJ"D]ױޠ+y%{]T82830ءЋBl7N ؙD? z錫mJ^B]cY)]?"{avo9|uk1SiqôJśn372g9Jg;a՘sIlkW|Kk>Bx_yßqᄉ>Y03WYۼMu7ef}8mY+)D#$<_rf`cc 辆Sr70sq59 84MŗWGV(/Ea"P=m'F_, 43 f Ÿ"ؤ 4A j9:!N'E;w0km8{Lh%Isg- -+rNHGq&3 U̠TIf3r KNŕ_{+U^:&If.EqVM^$KO?͟|=}"< +(‰" uH{~~{- h/FB 0B8phb'\g5x'Ə~؊`P(HH Jc1|A#~%? JR>O*݅%ٔXlQ=NYONɞ%~3ZXH9XbNe|@WQbwm ٞ#g뭤@ԡrzƺDg xcDyRb05D2O$cxw>(Tcn]@]_Cο,7e326%*27GZ X47\~WoƓ="eKd^9! D->i'DnLcNY1\wbݨ4!CH*[/""#+|-[\S,k%=MG~jiچ(v&a-]gYPp%CoOB[`{FEoz}X?=}}6 (@A#è]o:ҋ9:m8Feήjs6S4 08'-3¹ (@4 UY^P;? 쾸es' s2*QTjq`xigmQ%11!5KƏWQ+#1SXoakA߰&1 Ir61+dZ;m}~q\UhYЪdgVX>W+[8 '29MoF%:JGԭnwnݶ <'gpěLCB1 TbΩƿozTG3 b'F;&]I\i m "p{eJ[;oya"O%M-׭Ӹԉ`ZUo:?_YzG-Kqڟ:uZYKnBYl9!# 8h8!!W?%,Z030z, <)|i'K0Mcb-c )1B`p0sWs>kE0HB˟3dsGt)rȐB[#l)L D1߇4l2j&v~.FQH%KѥDO~C1e[?N罯|:~roD/6Njr[ه0\^v wG%a0^_?],oxx_]D~;SZ 3:-ދXVA")M3w[x]0Q!$ףҐQVMrf`^G_t?k}gma[+)+ɰѠ?|gξ  =f3bA4⭒(Q '~McL$6&}"B&pMߙ=`>&N }yHi /洄03YOO<`3R1hYuGMCVq3O7d|޲+ݮ{u=7gO}O4!1!M}2 &.B-j18f_ksƕ (8P)ɽQy=FC}PGAζ^}`FmWY4n7Jh`v} ,ϫt/lV@VNsme|ߣPp,rө'Y19S1iijKz-/-DR)1 g %&ܙ0ZV 7vS3귪>Ϯ~,Q4r +H3zhp#CHz4mnqU6ٶIz66N!b͑-#@J83ș$ht h3ai_ճ>0&@f/TV6~ fHrt<ҿV(]y k(r!&"o)?ץYEb+sz4+AycUlXY@^37,z!'fa_xnj~kwAVB?쬜A9MGi.eKwR}Qx*N7٨I^f.7#5rsQHvՐ#U{z A1UF5~s6njh"0C*?t]m"닅\M9hν˻ Jtk КPsެՂ5\`zXmcw^u qWilUocb\Ĺ5C+}Gƞ`k'&-1ϳgE9UBvg%,s&P5OAkPl cdE9!&K1 2wC$ E\la0-T1-W#H_:?f6Zx[0kgJwld~N/>Pwv2y&iE=^_^F!L"<꟪I-6?K[攼)ѻroXZ',Y..P&Qu2U/n`TJNyihx]1 #@跌|uv:MTȈ0&RBI/i (K<, }!?lT=3k6[k }Blw@ƳL,RH)6iU (f;s sqaEN4 YE<%]pC (BGvF3`69o4)wvJ;CFyc6u77w3>"BJ_}h?U8c ľHT@) OeW~MH=ms[D