summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/flamegraph.py
blob: b6af1dd5f816e47c8a904cc9aca7d39894c81348 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# flamegraph.py - create flame graphs from perf samples
# SPDX-License-Identifier: GPL-2.0
#
# Usage:
#
#     perf record -a -g -F 99 sleep 60
#     perf script report flamegraph
#
# Combined:
#
#     perf script flamegraph -a -F 99 sleep 60
#
# Written by Andreas Gerstmayr <agerstmayr@redhat.com>
# Flame Graphs invented by Brendan Gregg <bgregg@netflix.com>
# Works in tandem with d3-flame-graph by Martin Spier <mspier@netflix.com>
#
# pylint: disable=missing-module-docstring
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring

from __future__ import print_function
import sys
import os
import io
import argparse
import json
import subprocess

# pylint: disable=too-few-public-methods
class Node:
    def __init__(self, name, libtype):
        self.name = name
        # "root" | "kernel" | ""
        # "" indicates user space
        self.libtype = libtype
        self.value = 0
        self.children = []

    def to_json(self):
        return {
            "n": self.name,
            "l": self.libtype,
            "v": self.value,
            "c": self.children
        }


class FlameGraphCLI:
    def __init__(self, args):
        self.args = args
        self.stack = Node("all", "root")

        if self.args.format == "html" and \
                not os.path.isfile(self.args.template):
            print("Flame Graph template {} does not exist. Please install "
                  "the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) "
                  "package, specify an existing flame graph template "
                  "(--template PATH) or another output format "
                  "(--format FORMAT).".format(self.args.template),
                  file=sys.stderr)
            sys.exit(1)

    @staticmethod
    def get_libtype_from_dso(dso):
        """
        when kernel-debuginfo is installed,
        dso points to /usr/lib/debug/lib/modules/*/vmlinux
        """
        if dso and (dso == "[kernel.kallsyms]" or dso.endswith("/vmlinux")):
            return "kernel"

        return ""

    @staticmethod
    def find_or_create_node(node, name, libtype):
        for child in node.children:
            if child.name == name:
                return child

        child = Node(name, libtype)
        node.children.append(child)
        return child

    def process_event(self, event):
        pid = event.get("sample", {}).get("pid", 0)
        # event["dso"] sometimes contains /usr/lib/debug/lib/modules/*/vmlinux
        # for user-space processes; let's use pid for kernel or user-space distinction
        if pid == 0:
            comm = event["comm"]
            libtype = "kernel"
        else:
            comm = "{} ({})".format(event["comm"], pid)
            libtype = ""
        node = self.find_or_create_node(self.stack, comm, libtype)

        if "callchain" in event:
            for entry in reversed(event["callchain"]):
                name = entry.get("sym", {}).get("name", "[unknown]")
                libtype = self.get_libtype_from_dso(entry.get("dso"))
                node = self.find_or_create_node(node, name, libtype)
        else:
            name = event.get("symbol", "[unknown]")
            libtype = self.get_libtype_from_dso(event.get("dso"))
            node = self.find_or_create_node(node, name, libtype)
        node.value += 1

    def get_report_header(self):
        if self.args.input == "-":
            # when this script is invoked with "perf script flamegraph",
            # no perf.data is created and we cannot read the header of it
            return ""

        try:
            output = subprocess.check_output(["perf", "report", "--header-only"])
            return output.decode("utf-8")
        except Exception as err:  # pylint: disable=broad-except
            print("Error reading report header: {}".format(err), file=sys.stderr)
            return ""

    def trace_end(self):
        stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())

        if self.args.format == "html":
            report_header = self.get_report_header()
            options = {
                "colorscheme": self.args.colorscheme,
                "context": report_header
            }
            options_json = json.dumps(options)

            try:
                with io.open(self.args.template, encoding="utf-8") as template:
                    output_str = (
                        template.read()
                        .replace("/** @options_json **/", options_json)
                        .replace("/** @flamegraph_json **/", stacks_json)
                    )
            except IOError as err:
                print("Error reading template file: {}".format(err), file=sys.stderr)
                sys.exit(1)
            output_fn = self.args.output or "flamegraph.html"
        else:
            output_str = stacks_json
            output_fn = self.args.output or "stacks.json"

        if output_fn == "-":
            with io.open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
                out.write(output_str)
        else:
            print("dumping data to {}".format(output_fn))
            try:
                with io.open(output_fn, "w", encoding="utf-8") as out:
                    out.write(output_str)
            except IOError as err:
                print("Error writing output file: {}".format(err), file=sys.stderr)
                sys.exit(1)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Create flame graphs.")
    parser.add_argument("-f", "--format",
                        default="html", choices=["json", "html"],
                        help="output file format")
    parser.add_argument("-o", "--output",
                        help="output file name")
    parser.add_argument("--template",
                        default="/usr/share/d3-flame-graph/d3-flamegraph-base.html",
                        help="path to flame graph HTML template")
    parser.add_argument("--colorscheme",
                        default="blue-green",
                        help="flame graph color scheme",
                        choices=["blue-green", "orange"])
    parser.add_argument("-i", "--input",
                        help=argparse.SUPPRESS)

    cli_args = parser.parse_args()
    cli = FlameGraphCLI(cli_args)

    process_event = cli.process_event
    trace_end = cli.trace_end
<+f_JF킮:/f7hi`N)᳓w ]|B l﷏O =H;u/p=SUo7XxWt나rw6͐˻m_$jhYͰMsV-x-xkxwg5N >BirOl"p-pnkaM~=F{-s;z6ۧe88r;9뛮=& ݱ`0ux kI ~ϴgX jHj_[G`.AZ9J{ٞxd6n=&0&woqO#nP~k3S,yܣ4As 8" 7QtA|Jn>Iwwǔ<AtvsЃ1=]~=orPgRS`1#O|MKr@gvtNI(^k,&{;] Myno]\p kیFc0X 3`W$/L kDULqR+P |)xW}|:2<<2;_[<O|bn"9D:7:rg|P.IC0۷{$[ٶVC_JsƱ`zB޴GC P<O`jv/vѳs]`ԚМʕG[ ;O6*?>auGn~K^!: T=o͌Sy F4/]5pj1i#CBq*vo2H18'/=E"[/~@v+*R?T\/Wg&c"{Sgp"z{2p L"[&n9Ar{>\<1]BkvtvB$?@ti0VR~0 6a-a$?x5D*(2}DFY>bBcm#,5馪Pݺz*rS;Oͣb\qG~1IGc SI>0g?erdZf7a: ;b {zwj AWW‘kpbY~B ~Zs~ZYدF-ሽ}xmA'7yE:;mg>[tҾ YlC~QJ Т>-j)PNJY u?0"HGQVfD˫iEB枅Cicݣ$bHy `;e'Z<^ڐ,l}ĉnm:_' PwARC4FIpk 3]^8to\bdF,@St}K릪/W&1S ,_GRz,F_K׭PFLoۗ_Sl>Pa8PR^V ̨A?6׍e1yQҷDx`<,"0m!C0a[ DGeOt'uɁxs!*vcIYbKbt` .Krf@3릱&V9 cB1G|tgTާ;A @>^Gmbw~yJ{䲢KsF3g?_}#+$gxվq|̗n!E ;@^>1tlD7yg o;x,B$xn ̩BZO>JƨF'{hH}'' N 4b!ݷY4|yqsSR3B=dG _f%-pFxP+J{rK.m(_ N( '>V|oװ Ա?T :'q3Y'7܍:^ Y6/ߠ{_|ʱYt47;|XCho6ݕ~[<-ZEK//^Bc=^ǓN@>+wtMymO h-g#'RAH<(dvܞ$h`}Tދ{y[܈^ {SȀ2](kG|>\~x0^/i]}v7Ձ ,t|u)6Cs^xda>XDV1m;iJ: v,v VTgX pHcGvQqjD~Tk~ʖ F3XDpުII[O˪ρ!*aʃAAM (0[:7XEI/kU}mgkXKm3g5 '{uvɅ˅ӂ`BtaUPg:ϴwTW<^3}̡4ZEDEaU *l5{'ֿkfc8mʈdsLؿșX( $'~P WTx 8 'B-U a]NPұ C_y l:HJ^e;l *GIhNvȫ}WLex-F!m@|0~o \ g1/yݷdmΥa&Gu]ts5R1(qPpSɄ^1 a bwnbNj*?+`|%;{/;9hm\C p֣{>Gs| a[:aƜߐy@H(4(Ad`dA\^ X^G3ժʬ֯PჸtNLm^>ksU8ع ~Q åˍָX|ĵAtI=QkDPV]4=ڄ{*p\\Yn'CGe0|ns4pH %\E5U*XAyD AXeH-tt{f_ YY]t8m&AV71 31jX7"p{B L(G.Ǒ&<.@VL:KA~獏]K(k?4RuNZ \ݻ\Ѐ!7ҟLlwB!HC M`z`z:lڗ^u ٞvJqfÚWPqpvTV쟆@r|gW_?O%>9 [8n!ϟG?!R[bZc nZF5UQ ;YQȐ}Ho@*Y"1 zp.@[R.D=,yi<6h, A A$A1ۣ4JqMz:L֏.yٹYwT_9`<-LKϪxHP)8+r5tK~8!~ԣ•5`qghBa06 E="t/ghRpBw Ydg9\5-v|9ivo ")Q# kYc얬c Z%o!>Q6a?_=`2)^3}zWj ײ }P`!DuF}|soi%1b6cSLPh~λ=t>"B.Vjd|+٢[¢'䄀1uz j)@h_6|-xm\ 1@ FRgJ EDY\G a|vh:r<[!ԀmSD껫30M1|?oھ=TMFROniLh7pKBтrK ; M{澪3uce7z7/w&{5g}@󉛌9v"_9!te_ EH@`AB sY4m t2r!e+ w,ۤզϭ]oh::>A/PlW_9>D*AMP9Aq5_>2 3dDq`խ}u֛4Mח)Vl"r龃;X%CH_ۯh mYGԦ~y'nOoanB ]込gͽ?V{TbDL%R -gg;{1c󿓄A5 5X>?2<RH𿡦NE+dٳ01'3# LO~_\v-!:]٨ {6jW"񿇖#v_;9᜷ƣ>G)n༮N[W{F߀}. bA/:NX+g6YۀQzEx@J*5ӊ%Gֈ >ta~#%_ZavzTpyrÓ1,x` bI<Ġ .9RSF$m.F8LJ&u8_yȱ(9spEւ:rT HC%*)Qéɰ|&Z aT'wLbX-o%jӻRcӄ5"nϟ3b,,vtHKuZp32/~j@.:l1p E6У?|;$|GhףwhBjI&h1>Q,m[tڏHÍqkӱ8z$1m/~a0;ADg&&JT'`Io^6"=}J G3(a`L ET"#(>Ptz..hX $R2 "iX1B=ȇm B]$3`gMG--cnc. ozz=O p*Uk)[p`;_Zf ('2Xp8,1EP |̼Ǡe6Ekɇl2 xrMQ8(v<<^;#8VK=pw)˨Qj:p(J\ DYPhǢ gJ/NZ&+(mcE$|,txz#i/>gb|֧:0޹s&oEkO{\zxRIzi[-+2/iPbݏT-?gN?ow`|]eτ$! q昶UBFk4E(&suTK&$ܚދ>'9 (srNg:kH(ڬy  L%qAK7`XR\Gn&$ DԖ3<ҹm^ ߇,%8M0"ft9u[#m;/Ό}XE$t#tXa 1n 9*Y* w.b8(lyHB(Ѣр6mgC jcXoc $x#y6݆q(&PW0;=u hh}X|(aODF2*_ofz80yƀrr3gyþMis7e!lH{wtx*" @ gY: $H@0uH0Bg$8cuY@rEN8 24}1bцb㒒@V/L(3]d&dˎgޤ6/4@J:lRh}dvJB[ňi7Np_( k! j?6Ak(an FФˑhHCM'z3߇z~:voބf͏픣^}g yZ OюZ~;lCj;ʝYKOBt {WEhM7kDx6b爕^pUS&W#s@=@B#VٌeCY5{zvi J09!1|XX9X0)u0,zD3W>ڝ {:^"P;?^9iL{3GTNv O_tFԗ%7lsbM67Q{mXUAP% ξ=t12 }[IMAaF?a\wvB/(Gz:y>fO:x?r}7w`gޚ]CM"4sXO0Ո? .7 4f=?L;jV?-UHI B@=`$'[TMݕ{ uԳ_)#,A..w3_ _M^C"01B_M›$q3k.2xgN1YϞ\ @վskr:WB(y⏈,D{Bfͬ1J1B23h[Vp)"^ZaQW2e.ZwrI{GYnM #AOAfc_mde6h}\Q0q q+p~c |ZVKߝ7NƵ* n~.맢y#A/!zf4U?kve28,㲂Y-S?g#<iUuxY,Iv=J?v<!#1qEǏĨa V̓HG|8Q^uS/4}c>ZO(oM~~˄,-/'|&0:O4G5q7*e;1x6@$fv4׫\9[=vO٭FNMJby%AιD4m_ifk6a fp Ac^;_|\"AW1nu|G8bH\L[GZb7_Ftai$OS{`_%XKF^K~҉F M F[N WHN<zlf˝ϢM (GϥG yQ f.3g?kl炇NFc yYIPbssr\csä}t~ ??ڗy~@9&6JeEG>_4wGO\ bϴ.cm"nG` o5W 3*s\Y,f,YuSR-m`<#I(Sa7Ốt\Dzɪj ;#%mZOZPܺͲԅrW5Y%e eatamJۘA245^ 2#ɖ~}h͹gXo/XܴÃ`kZD'ѶPp8zfv46,kDY[M1rix!3WLj .1r'lǧOvjșW_r,  ` ޣN0cz Ka?þ{p9t݁E`p揶#ZqΚ,%CTu/p0_C&N-Agh!kLU6:9+c( *BADG=dgsH-~G(i>ioعB)ՋoP eX첰[`\r׷@!MX`H`1#m(@ĵVLYK1Yr|Co#R3y#K& |za*L7^֥6\\jZ*!/c=9q ~)RRlm_tA0Q,loتX 1 F.aaXTZ5̟5|vl Iw0@XTTC&,4B?&W:[k2׶Fh0 daO9e. .ܐ,ٽ9#VHd6H*p'`9K=;ComRYPґцNZXq=0ߏj_U%7K!=`:M &|Z{ EpBI5u*9p\a_ǝ\UՁe]qok+[iM#UCfpts2 [xh*dNc͙t:y)`MԄAyp' 1M(%g=/3$ a, xL<"Ӡ}-h9tܤpn 5[%wQܺd SsHzP[3 ,B9>l], ?Zp>eS=\T%;8Ss HN>iEfSgLM #ըl/S|~(X[F/gAᙃ$"(jo5Aߐm8J~|8JG~-T}..nL;XDWUon9k!IO3@Tz>1xsh|4`Y7%ox1_[zMxR Ec 5QK0RwA+scp {_mu/F$QglIu3zykcmQ%X&ƚCr{AH4MN0^V~ܺ=uEII|(aA[\Ġ 8l Vz&Ǩ=DHȲyHDa/ׁs8HR )F@-y57]qUۥFϣ=n W*ۈE7+eHY"i$ Z@6KXp@a(?K8&{,}t@rپl-U+k`]z,ڒ%fifX603*$w,漱31rb&-M "$(c܌2dc;cEa"w [}mIZ>9T;c.l{_uO'%qנgleȯ⺡Z@/Kŝ ֮A+`9\<)7کZtJu|z1,8$Pd};Gh{ B (?(邆pDt̞k&-XCsYÁa/!c.=?碰?4_րyC(50qYڂ@ߑTؼ:9& ~Iu &| %Çh#򑂬@7AWs)x8HR Xeq3:6ͥ kG_S7#1jISp"s! % 8:("?Grf8{׿(/ڌwl# 5|ev"Ͻk :nv3E] jxxоae~g/W:54)7gB7C])FsT#t<@}9*jf!dI򷭒$ șekr˛Y}ٯOo\s]0ڎQC&;挡SlN|>m^Gž:-(믡 s]ӭIDÃEIdNFAMǯz9OzG,%6ӆg q:0r#JSz9CHAo+"~XK/|8Lo\Cn-^#)QU,N! X KS<(eQYA H s'_RiJ:TЇrs[hVaU_d%p$Ţ~u.dzx``0d0kTIC'g ,qm%D()mZ)!F;|"C4ytp]B={8D!p 5 XfݫW[NR[10SjE ֣]YHٯc n-s aX<Pg&9-}ﲏ& ;:_+x Ι)ms7nm.ܵ<{l<# sU ;2Ֆu^M݇zg^N}nqw86kFbnMHec/ P4aUt.Ɠ8_?ӟCz!$@}N==ӳѩ]&la:\Yggzes$ߺ(r-<`(9bOӎtUPz"HNPO%0Fzo6Dvh- v Z?ņ` C< y9!0w&N{zr 삩T{Br'pRE A~7:"n"drbuE"3> cX+1oEGk]h (Xz&B=조spv8n4m&:7D O$D aԐ}-=i$FZjzX0Zdd$]'4wΘ^T1"_ȑ.UGe iV-}mўlb]D1N]~Rة%&!t1# Y '4Wa޳_NÊfmrP`@4~v:s 'JdB?uXOr $"<M΂,#N$OW$E)8boȋ[kn.Ubf~Mɴmu0[毌#l;`! Sbh rԿߠ< K!k;x6saf*$r}F f/6>.Z06xHlM{hdN* ߉6'.GE_NjO`.~3/_.v&O&d'̖vC!溌3b#6h9 <>[.p=,; rIӟ,+Rc(IdǵPN/ 7Ed!Y m]IZ:*;"#Sp06\=o)cCQMJ ̒4v*u?{yuPzOXLN>n0#t=zfnz{_@[krRf LX2n /$j :T. *bXӋIVy,vJ|O,a/[ͩ"n]ݏ?mEj l0t4j{"AK |k-S[ĄmA7wV/bɒЀr@$Y>P DA~E=d qNO\o$$W7%˛)H] s ,ſ쇂0Ω#{|uxf9u3o| ؑߋZkiL 2QCbň!kXB\P!,İ1W0?wR9bbTA LJAi8~}MD/?Mr8;orbзRn Tx)n~ ^ڰy=>"Ucl~ĩ,hC #℗)kYEp !%p?;}€K !ߣez-P`P Y63Zỏ7M:?ә[Nl `I90 Dۿc:[ҋϞu3͎ 1nK6I]ܚi2l˥(j_jXD}9'7L#}vhKq¤Y!/u)(s4vM)' \剴S XRͯsٿ[iW8g CkҴq\#_>ih]Į QC5Wi|=X%,,2cGϜB:*ݲ0 ə|Z|g닢x7?~w-3{.b94YyЧl`K{[>`7}DSf8Ux-G`!':_Q25j.Ff&ljLDc-۾ e=ƃD-ڹgm9,D(5?@$>T1ďq/ޖ?#R`$,B~k̐Z;3 Z@,>nH)ߘ֐! Yp(,|{b?AxUVXtE;wL܁RRrFb9@\sq5F S Zv ""QTbɤU`\5[i-bH'GI;y@  Ĺ`QY9e:2!w;#os}kPu!B#p.6 ط0 ٔ.}fAh\/.s H`Z9ׯ- <KļT=T~-~'~f؁I) DtO(~9=ޓFpK(#8lST@ ۂbPAXnC!jw #ܴE[\$ScEJG,[ 2PdC̈́P.o5Ci%TZ;7Ji1E<aCus* 8x-CcQie]QTd"xrP"=XpF+$E9z`G\"PP=X B䭰?@ cфAF G\]<' nnu$0xE pf)\hrP47Ɠ=7Z?:7VJmdJhZLTq_;Ua!>u -4iE=^*ZÇƂ`B ⱽu)|ҘGck09@ c0黈r2(vA/{U/ᓦʮEMXj6>^7;ŭ.AbA,CÇH.)(~Jx 0Yns 9Q018W yFPw*[N/FicQ{x񮵎}c$y>ΠA ҫ,H@<;ƐŮ]gy+_xMgD__#Yx~OJ|C':;zCDk6,J9ja^`F:̳g)K{{-Z283XU+abAmc>,7 ~sob,@ \JGJNR4BZ ȝ4MP&!s~F% Ϸ?׺W\Q|}28zqⷋ${ oxȞ+Rn0yՋC͡sug(SW PpF7l=#Oah,G\Df|XXgXړFMFø`at/HDN,78$lYM\ΆiQgʢHE8M%٪%* :*Z:_4b;OTkid?E`Be ӎ<9op6 Fc=x5EiΟս~#ڥ Px (y g:dy ty׫=LHdd "o#k\h.輗R+k#iPS{/|[g_O 3ֶSoпv\pzr`w?&Ư W`B~Gg^Q&*xUA?M <t6akV1m_x>@T<]M* KQ0v IX@635T2 `ZHAe'80(2!P E@Z=Nv :&ѴK0}y M YdͬD8"_zl^7K&gdf#& +ɽAXn%4fi  {Mߒ{ܛd8KS}\ ):FDFCR@R!O^ߦ_yr/XMQ=%A^Cz (6rFN_n+*$2!P0A%_?m>ސ;*Ut||rp|Wb;!8`75{LM P}t<Ħ!'d]j5 yO?9H76 x(-O7j*yHo]QT&Ȣþ3oO*VO[}0-W|?v?U(w1T[ӷSpnpPQn?PВU[4jn `K_Fj(, FX*(Q̀n<('TUD~Qx>7QQ~V Y&,]iijAʥge)t6 '~]_f5ae>l8h0)6^ KLorxMs9:T ؗ(򂃳(g"~xْ<#ppsٷ7:  @Y LW30[}\KW6Ujv`|@n±^v.Ku{ghX*Ұ]. X{wo+M?IǮG}yK]ze@_ EW"Ⱦkj-,2:7s]2M\iТa>Sr^PTn._h~5J~+/Sܝ8ޫ Y@g}Ƽ~{M7< [>3JT}Iq+yg ʝtel=v56G YV,tlxٔla"l9GD{F8AHg$`LQi~L_7<FՎ̰|`گ4@N1!~c1# QtREqQ+nRZ 7BQ{,3EWj_r;]0uf$႐g wv/h $`8 pK䊼J"RBl}@ 8" gK=L\;JPPڸےDOX%-(Qxd7TZ흔@(ӮDvZ;Y+Y^.׋utpϒG*Ǔf=#Hi Xg'~7t!H(\G`[U,t%wnVJ1)R =`ZaJڹB3ڄS28/WPӖ^5\s#tg53|y*q qSјG~Oo(2vzhYG#Kp-?w4B%{ۚ4bU'(齜$B!N@C݇ǜۖ8B7ÐKxà \<pE9;q.h!i>}C~lG&"㌪v Cxw|lB"L8} PmN9X8~%&pxQۿUפw4 -b2s L7#?gSϥ%F1F=j [z#S/:µe^y3VY3 c+3K2X07: _0k<8)$Q?Bh=pnsgE('YqfBB"VYԅ2bܞ '/ma;CF }v .3.Swl6MI1MaXL=]uc؁PPi~_4zv i3a鋑S0:h/~)wɉ::L_vܥsx.Yj%FkܝX #Ƴ߼ߴ:JnK@]8$E" (}w5 BYHn-{ilZb?*G> (R,,}oXt>V^'0a h-&a)K ,,LQ:6 7˺ޙҴϵW8Zf$-*6z2cbul~7vٚ3bF ^#6kty&}˛$#R{ pl) ϨG# \8%!gX?C߿[4|S.yVxq&s/jf 鰺 )THZjUcb9q!Dv o!ĸc`Yٮ,k*9PfB:AF#Pt3ӹ'DBY:LfV &ԺQbD25Hs&L!zx\po4nڌ͂ 0PWLg&m?ry"bR?;P,|4.t'3 OfTk 6#+ |5]02aZ IGl:r `ȇQၕ q]O4" gyj혐D?G,&K1AؙtWewoRz[R׽Z;9`P ^.Ƴi}gɋZ(} BH=;SlX#zD(T88"KF/9ŜUR\)5yB f `̹ &7JjU?PP Pro3+ύS'AXhBiP%C-p@wXt5Qj)  &Y[V^G0R$@Ya?#ǣ IۅXz"G;5t֮}Uk;7E@A?~dTV)K1Xo*gGt.)o36eCN e?`*4\j*,CLoHfCnfs6݂vk9ëpK# ϡO8 ?d.:+Ӝ㡧56B~![ MdNYð| Lf;˹3bBՊiaeʵ##( !Px\V==7x-.a@ӻy|GϋnL\i4 .?+#pa3]&!# by) >53g˱݌LksvsO+!oGt??ҧH-^"anq' ) )(vti8|&y7^0$p炂 ME!J{de9>e`^TVQ^C2ͧZMamK&6 px>W9@EV"9=~K2e{.7N7`SwNDX~ȏT^A{pe=X8 K̃?bh:Y$O2;R3K\q``}W'*nsb 8kG91֡iF >' pV]äDlfr\*H81K2|#,7Z*PT;ي,T.+.Y |:X5́A,9g}V+X`Oq7ޠg\F`g}HoL!a50`bURyrW(PDG:\@>*Z_v~jt@ݽcF%ݻ^x(Kʹ!Koƫ>l6=(2Xǘ}o:!y/hSbuTB}`c@]m.cxξ{VZ-9me?;R:y4hP͚\5bm7ݖOŘ02ܜY]A7=G `Z|oNPi*ֽ_֣쑨\0f0}C/s$HH(0QE@!ar6NnBѯ !lbqw@a&6-9fEEh^\QH dhѢ~2k8YǘI~|nŻ[+3?V}>A ;X(]A$vmܪ$-Ņ!uI)/0zz]X28Iys(OPja#|0!nv&_U1ȡ<*RslK-2w7+1zrjp^֙Nuzo/랺AOzQE:քbV0 ~Hg]fwPNN+j1QZ1"GlxaTTe%)+pj!z 8!̏R⟮?= c%ʼnua4gvğT8x'gU|jN]e w_:XKCpT.ҮO;ָ\&oRlC26Ew\A m(G)((/eH M B<; 536*81 =!װ$kw ³CjM,| V^SŅ JBaz]Zk\e2#Fh8:Kbm;?R"dFGPɼ$GvrJ vH_on ,QUOK&uQ\pE/q\PTS'j%]K ?qgu 6Ad<NS!sd(H!fC70F4\ZϺJ XM>,5׿7~Z4dK ;2*8$D;+'B;1i ]}i[HpC*1; z 8;catLm]oO~\Anښс@QiK gQ$`%gv, y%*6kz0oyF;yriHH$,2:cn5]Ld 9ss1 xb(~ݏ6ih݆Dq|"DrD.W9L]#b(Qz"ۑ߳7B‰T?p8*4Se{j! 93.rno׼_ Q25>|"NLʰnUM٧wNc۳{j 3.@-)*,;5{*sc./ʮ]Á ܡb|yNUt-s#2"0dEk)AھSo仗 |?^ M@sD{(KW53p2y8Y+[ر \4o5ze\:yψ+yiE0`v䁀Vo<>zN?{g 6g>_:g]Jٺl˝[Z~~QbIzB|7$i.`wm|t:w}SS?Hrg(q 6 W;OjbwAF(g_c0-T\$,/!.![9bb*fo2;vAD;;]rFơ 8 H"4)T(A>S>_(U $l9 >oȉiSf"|isEB: \䗉(#6ͅaK\su!}HmWvSt emp;)<*GPQm+sߪ _8z5lRN}n &q` 6Q\[[k:ٜ)Z^01)d}Mv$!FcXNy!泑K]}x#dyf="Zlrd le(gLgB3u]8܁{(6^q 0̲k\=ƒ}Uj\CR k$Af*<J!9n~i.‚ravnh#(r(`xf:/uHԫ nٓO-<-R<Hݖ.rgv:nۡ{k*P:%p-#kq+Ʒk}v{DpY{;{L0b*>e=d_!<鞇Lhu#Q-L >no-6c Gb N8}6;˿}HBl "{e/GM@ńZ8BrP4[8;Ai +k݊w{jnMJfJa9/ fru̹꥟-vUqZ-|.HGl/ʃ+~ s+!)|a<`" 34 R ,X%1=Lq!Cr-L/{a?>L A;7דM@RJ8m H#8q8:#۵G"HP8i K򡻨,9Rc;9ϝ=TKd&1Eu=LL7K1ߝQZf&gV@x5y;L/(Qyʼzr|;S1<=8s.&<İZVdLۑ i^hcQv:nZ HP5JJpq\ )lLjǐ{`&528Ŷ &h'cvr]ka Ȍ w۪lP Uv{|hmIb:W,頝 ReD7\1ϧgJ/t;i∮p˅ΐdL(wKOp_qORGw8QDȡa[9o5d`UY]˲KڈvR .uca\`6V%M..Sb]#+e\ 8`adel~WNxH"hXA8@ſ8r,@֞5ATچI~- 2[zq5"Lv[2R`6QHs1#'^pGq:F\,;^d2(f\ TCRxD8狫iKd|< [;bjL:tp-Df0 ` Aw]!Cut.ܻ=L旬Yn2'OBG|t58tI1%ϓ3#Pվ[q2܃/{e˖'jx#cp#J$b+XvƩa~BAbZNbOp |V^لT0}Ә[DS!잇ˎm[A O<\w`%kqXq\8o %{]uN* b|?u sJv8/IE&FwLu%V%œ0#Fd[ѻ;V! &szKwo<<5A24aOz˭nr'wQS,03ITX{g}*OANGƚ6mm(LfEpmۄ)kY@ D>Q~X+ dfo.w? \F ł3^ih#~}^߹hmFT0N-cuV2Ld%6 +AA*fp ;"z6e*G֐GH)b/udv`TN:Y\CA03{[þ5>E޻RkU|\PI6 1*2#mQx8ēÃ=OʃNe&J-pDf> 93>SrS׷n>`^? B, _q;]:z<yֶڌ HerPrهwZ^5)6 =Z4,>|أl$aCM]qzdz@E g,9w+-뀵E9JW0*&V2H: ;EKqq\bmϡH^{)xJbq -R۶M7Zqz=,ŷ4ُfKvҖ9=1; a *('L20=[%ɻc^%>w\$k47|]ѷ!~7~>"-ݢ$mK7𠋭z~/02NHtIA}hT#[DvnGסh9m&؏2!!1M. ø[#V`I4nfq)Ael܊Q_9Gx\hϞ|W3 yޥ>>pTFOl9̤Ao̎ΈSF ';z +|ڿ0oSguF f!: EZ0\onin{aPgDpyPԇy&;]\| <*UY^m<"OEb~60 ?3c! _4E5&w!qZjz}}p,6 A?{q ޳KO$8DRWי/;;^cL%CT½ a-%z`brg ;jqpGGdG}'`y8\Ԕȳc_.vI` jc$0FR>tM{зm K?s8#YPQA6?3甇 !ɾhw-t{*_@/TEf+byeQG.`B Jw .#Od$|m G9P ^ڽ#6~,0:PGT|6G,/ӬkΉ # [˻;Tjgj=:E˒i}_FcB#SLAH*!wi?cb=""!`P`U &Xp_D>H m` H ? B~jf&N>MIL Ẉ 1B2𥮅iੀ˵iN {Ni6o-\wh GK/VdEHSɒrTqǜj7#Z-@y+ /C4τ(yYC'ɰZ1~/T6Mb9"ؤmg`I{Б=r$]5u]IQt8[w[gn 'g9mY< 6WOluGB3Xyq=n(ۮ6| A[ydv_ۄˣ  0ynË԰ENec~8X]Ժ(}kiLKlAOSFx9l.Qa 7(l"z(u6?s\O[60%K1r(dJʚ m&1z Z90OYN 䁊7S 6B!9s2K=YRuٗ !X+kyAgmxWu%kn˺T T8J:kgQ?\HZA/X*zv t1m5~z鵶f<"Z8 ȹ[>F&lb zUKlH#(`Aϋ:,b^W]᭓h4"ȠqFaAӄ_tG(<ԋc0zLF?$@$ȞyJu/ql›1xurxʐA1p=aJvM?xQq*sjǻh8+^~#uZOkt!BGXK|}˲ZP(y:a0n'߳n(k[׾!,ChǷp:_ WAɦ#ߓlQb%aų` MY@/T 2w4iJ&ƺ\=dizow'W,lAu.X0&gQAbb<.e gb7%Fpb<`<U,wd Gkӆ[ʻȁX`o'm3;,1Y.3}j8Ǹz=5[ 9/S)4zmV^N0<8^y)#c'gcGX+<.yv,f \| ^(!"N Ksqb[A*Dm8G^`lnL>Ww.=(;ȃ{r*Gd=Q%U1U_Thr"ӷoM>sCG@q_\ ͽq(jk8ukМtA.⫏ Glk/O&lK`P,у > CvD+'$b2!TO!s掟`KO#4C!?)汒-WK9QͶ* `Ź_a}7x|!v?S6"DIetז戩[W}t9%I/+iZXȇgoMzdט˟FO }c'v~qϹ}}?˘'87:I;ޡ p& ~fSxKoQOW|~^xYcݲ e'2Oj.954 /ϛT&02tk,ߌQc,c8{!Raz>ٺCGZP4 u×3kl#Ji嶊bc1:b'ToْE77R[`zvK6jͫ'Q%ϳ5T?Mq¶1Vٸ\91C͵.$l[]燑RpUbGrlkepW? U=ro2]ncxOm #,23_6+ϐ2ruPj0]6w&<ܽ]{f\wO_(׮>{z-PS}K-L]/}ߍ^y/w30H(U2!QYyY /Xi%1dc3 QsH"cb Vٯ;F7yqi~Lҏ ߓrxriMX[d6  4^2"{ljtQyGfL[OhӦ^lcĥ|ǥ>Ph~p\ Zٗ7i=={(ˬUkgFR[(?= =w\CgDd=+p}n]9}{H-mw/C@x5PGsb9EaUЃ. i wtl^,*!w7k /R!DžsOU0zG;ݠymdoώjů/ ,_ׯl{_=)rrpH&x̵M*TXMK*|>ú7Y! >3U/q2į /5PC|АfY._"Ձ8ToȰ<_=x6Vȍ<ˤj/"۸FLCݜo2x{(6lafx ʺxcKRbT|v@k) ݛg ]R!a>!P>ϡ" zD{;H"H wbi;y '?[V 8bjKN\cd)'XҗJ<>E!o0Р胣)>X9)0\xq?:?+YOG,O i /qϲph B0W:Ǫ$d7k/ON3G+:O~8ppgA c5dRGYHːEInw]^*/ՖU.C͗%@iw+.Ko, I  y2>`";< V u ~SnaR9?N;R<#_ig3eIi# bq^|IͶb !Dg@p!qM|ZmRpjН^ ƖX¡-iqq/DߑN:?_mH@'i;\v[Pw<6`5*\Y!W"B}QO .-h<*ޡQ;.0."#㳳+3@=?YBPi(MgJO#"x/6:9($Z'XԚGe=ACCu|H~ĂbHD($|% LU&`?o8x Hh 9? 4I:~)_7k PKran2`DU!?>p_ǝ{]})l' ӪTy ervzS:@3 ~᫏ v5J_>uIG,t@@*5Zo[ >$h^Y$A@uo]^'uȈwWJzXIzH~@]ѸkQ%92&hP삈6mιW=OР@9/聍!u6,}.t{']1Mn6<"&5 %"YVc+'n0 gA TeB{laP,/Ƹ. -%i&:_ЎU5 c`D ڬ83ͯP$q1NZ=n2ޡnڂpcc"nAhXMAey76  V!F\6N:r G389S\&\q!i%Y۳Njϧ.wa~;a:l9il.]ǒϵ nƩQ`{"Iˮ8~qKr\Սу{" pȒB:-rM):PRΠhDxuEn>Vټ`hF(D"@}N$IlN!fK~AGs%zRY S> NJym1p-{E B <8}k8f338ovnƤmQnΠ -@(5|8Y糈lvaRa islr:VzUfvQ~j"͜gv <ǣj,pPW;l3dNw.^rkFQ [BKڊ!P?ghص>Zr5)7Z%yh"w)8d(Pq8(Wlrx YxrxoHgDȥlڑ p܌GVIz* 9[ gF3!-sAĸ~LL  㬈O+0 "-hS-_e_fv G u3 .cd0ur,9MJǙn)@0dB+v$\9(ۂd%P{M4G+t٢6`6 !K+ 8 ( R1A`{@cFaM> R_/Be(E A|ň?]D0~f Ц(֢`+JļA^/`&{C̼g+ Җ0 2qʴ>OUp6 0/Cz蟌NSe{_&u⓽>L<yͯN74ve\LO\p(F|۾d\uE79ps?j880>zqǍ۬\cפYf=< 4ÌNyR۷>.$Rr ../B_(MCrQ":_Ds*X!/c"{zV{K`n^kWAߢcV>yUpXMXEGDÎ`gW "^7a͝%K[%wr+}0UϗO2| \ןEq' w[7Nۍ&@$OyM1bPOj{l/&]m#A _x{hLU*)Q4嬶FWQ n eTI)rAC\ <%+Byk~H҈2FCԘc:4hc8_ w׌]cOTxQaޔR4HL C+˅֧VێϠZ5aJ_雤r2ޞ_`i)^;Vz+&;mWnbu?E*m$970gO* >E4)n{W9T f#Oes^ȷ4XȎ~G2GFNc![DP$CiF4qx|RM-+CTv\ :<ƕt鵰C&!w0!rlW 4qB %b5f0 U%Ʊcc=.z/-H ©F-hI¼3܃VyxB;~8=bc-$͑{:rs|9F5y,,e`ۜ]5 9H#K ˸_N٭lp@wKY?&+@+w1CSN"}l{Zr%R l.BTɚpa  k(8VN~˽>.cJ 3ŘPW剣_CyW/?` *qf{{}JFU(vTsB6q+Z%%A!*Ym ⅘LBdaWx[XmL\ePSR YFȳ*Bzv53bi7E9flŝvy\̎zzx[7y~f~:,΋uZm 7~No{iǽci7/“)?WmSlli{wge*zQ[($tyS3,Ž{.3Gߏ7@Fyکz!>p. #>+lz?IW !$"d=Kz‰p''C 4J{ߥ :#ףtAM`U 8wA:E}o( -`G{ZmszԇM_lOOq:xH/ޥ(T!@I<P(x, e"t Jr.x|-7u],~Q k׊wtdE@s27S~.B+?%sk|p$#'_ͲF^ %|E(. `̍ &/ Τ@ yEӚ#㇜zG_mθ!7KuuLTB?X)y}O|vu_ ]2Fa@ ^Qp10҃b @} Cpg}MTiH}87D-Ӻ?2k )ZȣJRb ТdpSv{`4 $D_.;j@{"Q$sQ[呞IxIa݂Z ;i͇!׆:#hL!\A᭰7PW6δq$qex48oL ?E8q?&H %~_:-_ݢ~'ՇqƱ+57>:ӻ.?Z߷>_}u"?n"+bTo_.cSݿ>8 $*wbQېV= y6R'6B&SXK;k2X3գownJ6}'$qܳllĉ7tl0ǟCNG=]o<(Ks>.Ū⮄i@O<u,}?at哌g`THm/6\ K!92bZV> kU=9utsbNMs7opf냝rcd^ï6zvhC31CHL/:vbe<^/UG;ZHgz:UŦKFXZ[V0vrlDw:Xi>iafAx4 $r!AEIʁ599x㹽oo\3xuo/2'WmmU{xIGH7qcT3 B9ѮbZ7H'KE)Kb spfaSm]cF2v:zX>5uȾSt ,6tOL8 sZssjV溶$'A>H$@+cJܫvLm\J`r ֯j[H%d"dj:ziOD%k5ZOgޑ@B( `A$h8X #I [ 8n} 7\I.}ڎs?cf;w/oNYn 3#NTLb/.pچͻLweݍ vمF)%/ =^݋h:ØF8(齡8;}SHFym@+VCOn+/,v϶N4 @j[8Dp셐{5l׷=S^~:\>ed+/Š49Jgӓ}`\bqCÚfZB5XˍngMum^ԡHD (z%1!Ю* Td@jH|4mE5f]sbyƅeG[}s8ǫr 7uoHx*2>Iw_H{ E^rGx-[9 }\%}?5wnzS|^:t4ͷˤra٧D_絯UŢ)p=f֨}/PmU94-D08`PuTh/2R`JV%̻;mz+@1C#1;Q-UW8Y[ 0mēztЫ^<9a(߿1a%5E쬛g 5Í>걻aį/p䟚3u׆R׏5x jmn.mlFjiz'WbL(HdB^9ϓI1>+ѱſeoq ML۲[N3){3ͯG+_3Zh=[.ʋR˅xx[bp+,87V;:qJ">CP .T4)ϓ'5<]-CI%B&D0KQ@M(&CLq~Y"W鐁eٻ>O|63<;Gdl!VY_ߝfO#^y#.zqH?\ j]#*;x":iAdc!8tDI ]OA;`)m)ި~^^q۱斧N\xq1RUkh歉kt\ )hHS݈(\Q9̑%CeS-w^yl#gf:`FfOo@=<;;o\ۙ>Wv |_䭴ҤhQ` ޠ:J-5ge\@ ag'D d:Ҁ#ſ'p  Fb:n=Xuo*j>DBȁG % A:~xzHm0ф>w tE ;[wI'WA1i%q\}$ ޠ @UT$nWy#ob[739q$ yޭw-D$%{eE$D E  MFo÷CUDI D?DGI۸d/ܺ4,Eg]%"p_'ڈQ",:y^!^H=}C#d{ʨ[PHV{UF3{g77wiRfgH4ĎHC!pDpM 6Ae@L%zzxE6kŭA(4>uoEMhw$ gMxMb@Hҁm1[D=ي,藧tѬ&6N}DrNVZJ&J&J@*(j(j(Bh(h(*)jvݾ^Jb??3WL KA9ս@"*A ;!glWӐ|oBzL/vQ=n]8^ѨuTX$jBgw GG|y@U ;ě@zK#׾هS CvML=yEo˰ͻ& `Id& (h""(_=]DHDLDA -1E+!K,E)ԑHD!QQ !HP!HPS!+ )ED#@XDDQ۶6Q՘rMŁ.X9zzJ+bKI0:45j`_0$HNm!9!i:|m2iA\;.:r$SRd1ݩڔ@0n@k(x w%ٶPF{J`DR{uW7l jcLݩarO;x{$w3LSaQ95&~@Dj+0N GL䈃K o-vMv ev[.)tĢ{_g_ǜXKJzZUJ2X؎\OW>'V!@ 0憧G&(!FM3D >w g ZJB0[z mX@("k^G>){62V j=$jMEyc~M<ܷܙXT8ިC_~P='_26NYq_9j);8bgߌI RX%m[HY췣{"nTk MϰCu HB "'3 ;B0==U#o1gOOyP=o@'T;;HODÏ_˺d@q;iRjzr!3@ ^~7$@}^7@Q =Ƀ3Qx .:1ɇUjPΈ&I%&k}}Ustl/ < %wIR,,g6@3m.7"PQ'Щ)@'^Z8? SU g  E#.+ LQUUUUU~W̪ʌTPAPPc|szPV|1) L5UUUBUUTUR!IUSZQjPhj H!)bJ* "`۸: VS"9)C@Q1(L%4~^x oyc`aoy!9$<*s(2zmÄ;2iPF6B_{qH GtY7N157!?Kۿ*v)YV3Ȱ^ޫQ=-$ FO9hzfڹoA<+0)2@!ݿlq2}`{PA*]–tdRFC.?M{,j2i"M# 0pp u:#j) 9[@t=>O7&=00su6\/v'9~erH'g9 !@mz?p+lΈjW""H(EStcnz?#X0@+tV(4*! eFc*z-W>"( 728 `CZʁY_Oë*f,;&H}< 5N_V:C+l_2 [%'{~?|cbFYVml֯cY;huiXemyJѷ}fi ~oE`m˛q5G]#-?.@9JbGd':[_~#?P}&*@ "E@YTg?A?-Ǧy|NJ yPƺA8@jzU3 Rƴ#]@@?}?KՏV`A xDD^ը4h/B1X8mO`v`af`=#kT"NhpQ(ѱ悠8k61tb?tRHrνxLެ>)+y÷-