pax_global_header00006660000000000000000000000064135161752700014521gustar00rootroot0000000000000052 comment=138c70106835ee0f05879e7f2f46bca8dae7ca99 trace-cmd-2.8.3/000077500000000000000000000000001351617527000133725ustar00rootroot00000000000000trace-cmd-2.8.3/COPYING000066400000000000000000000004141351617527000144240ustar00rootroot00000000000000There are two main licenses that the tools in this directory are covered under. For the applications themselves, they are covered under GPL-2.0 (see LICENSES/GPL-2.0). As for the exported headers and libraries, they are covered under LPGL-2.1 (see LICENSES/LGPL-2.1). trace-cmd-2.8.3/COPYING.LIB000066400000000000000000000636511351617527000150450ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. ^L Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. ^L GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. ^L Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. ^L 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. ^L 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. ^L 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. ^L 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ^L How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! trace-cmd-2.8.3/DCO000066400000000000000000000041041351617527000137210ustar00rootroot00000000000000 (Copied from the Linux Kernel's Documentation/process/submitting-patches.rst) Sign your work - the Developer's Certificate of Origin ------------------------------------------------------ The sign-off is a simple line at the end of the explanation for the patch, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below: Developer's Certificate of Origin 1.1 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. then you just add a line saying:: Signed-off-by: Random J Developer using your real name (sorry, no pseudonyms or anonymous contributions.) Some people also put extra tags at the end. They'll just be ignored for now, but you can do this to mark internal company procedures or just point out some special detail about the sign-off. trace-cmd-2.8.3/Documentation/000077500000000000000000000000001351617527000162035ustar00rootroot00000000000000trace-cmd-2.8.3/Documentation/HTML/000077500000000000000000000000001351617527000167475ustar00rootroot00000000000000trace-cmd-2.8.3/Documentation/HTML/images/000077500000000000000000000000001351617527000202145ustar00rootroot00000000000000trace-cmd-2.8.3/Documentation/HTML/images/kshark-cursor-1.png000066400000000000000000001672661351617527000237000ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxw|?~)$$@^B 4"#b+ Ҕ@BTAz!$$+r\Klnvv=w{)!J,xWB3 L5BEURZ_~=2M UXXؾ}{B\H#PJ!RB}ԊZ1NN:u/73??]Vt:ZmUe JDNZD晬 G& ;ytԋiJB !y,RJ  /P! Q*JkTTARh4S^Cjuv4+n‘Wxȧ)Y񱩩^>>> ga9B ə?MMU^Wp:\ ??h4BT۵kWnnnzzz&Md[Kr%>!5xXsOYI.H @_'Ż g-rGk={VժٳVk+wMdx8Rʲfj<"ٮ ~8p)1T*+7 !Dto* }Ӳ'.%BLFrͦ"yRCWɺGΕ7t4OXr'YHP_g;FB*lp)V? !nݺO-30 LJLxA,cxY?)s,c1)f߾˘y:7RMQ*Ti.A\zfC}S3d*)OS3S3 nDx˷,s,SKwY(:80IT7~_Gt(^:, ,c~=ÖxyxS@jT<ϱ)-13\XOrztIp#%CqB޻Yy,qKkzP*SW3  N2R1C׷'!$Nc`q/#+2f13;Ngxe_\~W{Ӆ A@?P(Um/8cL,!^/\Ma` ].5z=<{&?^HgI!?dkAng9w]\QZŲJK9z^a͟hG\ (1FQP';<馽g07}GcKpxD=5.4u=K׼ >XsoeնWo.x,_/FAu>~Z\V1uA^RRoԷSӑft$mz{Bxύ{fxhbng|=9;߳?Xl*XF>v'KOd)@0eJ30֍Q0@U*Mm?Pp @n暳uܽx*Jկs3*([/XhӅ;=0(}Ξ=[U\{9 MɎIOy'=MKE}]Nz06n2uCӝ jJ~@fe^@Uݻw_|iDH]u;utYۅ^@nw׬YJѣV=jԍ7ʓ" `Anݲ ڭ[pHؼys) 7o~hOc3TN%;f&6ǥ{/ n#GL6% yirG%$$^__ߒ(,yƍ^~۹/{-[6[mо}w~dɨQ2vRԔJttY>-%g9'fNy/:yTR.E?(EA*bbE'N\vN0~|l9bs>i׮݀%_|Q-.+rJǽ9Nӽ=툈K)ȯdX(ϝ (֏_wKH ?+sF'%%_,W_)K2O):0RDzлw 榦N8Ik͛Ns>nYةSkךUWw6wgTTG}ػ)1իWwԩ h+xݐ>t_6.z#*}Ng C}`m0mZ DAVfUͪxOKH[B⋷ݭP C8pR? 0g^{nz߰xȅR1̌[cF}믿>|Tb7fGƍΎv4]w^`ŋn&%n߱豣j[PT-HY A! ,^!ձYE|RB(!DrVJTAm{xxmU̸y3?~ I7~pժ6n,ąRYHV;f脄 KLJz_~#o޼%Jd/^Ea͚KĿ]!w:lx`PpNO<%jm%EɲqƆ *JJ,n۶UVoVJJXذaCBihhX?}twwiӦ:*QvofeeI5\|/_U[bEtj\B,dg!vuԹ^@`nX [K !:ǟ~"O:rjպo{H%Bt;~=H LVBvRGfU(]t % 2[={ > !nVܹӒ%_|Xbwy8['ajٴisXXtƌ{?y3)g3fΰr쥋~>"Bޝ1sII ϟM6=Zӽ}woq_*^:s7o޴ͶS8 'd !>|: ů ߩc͛wiv??j߮ݵǎNl>urlq7 _? 6~/K?>qN7icǎۮ2}4%'y-8p`w_1B:F_<(yyySL0111˖-N4i޼y'N,f;KnܸѢE#Gڮb_.]B[l}֭[|_BBRoTXnѣG͞чξ~KBG/lA'q 3$$xܛo֯ңG|޽:w  znzնx~ &N)iރz'"YI%߬BKN^|EBȋ/D*$+fˎnT!dىWN_~%ooVۤ$߲i Μ>`٦ݎnd1Le˖^zbNuss3 uk6nx޽M4vZddv[#hf*BNm/ yv w=wSN ޽{[׽RL&SpH}-%JLJڻg?ؠ~5kV^E,yfY+?٫0 C݈~7opև?!aCR/O?X$$ߍUT6k?م9@m&!߹su<UT*/^IInNuTѣf}xEr-s넫@IJJ IDAT(.]^0_ZU[nnnCO=O榦N:ujFFF^^ޢEڶm+Ο?vaѸ~z,,Yb08)ۑ(77wvJ-ݘo[H)|3&Ohޙ4wgɺ/6,=W-Dyt`VjH#J %@پcLj/23ƌ-^fdM \ga'OM߻cƊ%nT̹-3|J-Z}4h?.\8ݺwv\k٪3>ۯ?_٦MntٳgAAA7ݻGYn݊+jժW_Ĉ5ڵksuRh#ǏKנX1mS}*jǏ3f^}oi4>n]Ν;s̖Zxg ;i]9/J6m}}|g!/ u+wbt̃N/f +f#DA!hQݭ`9v+XٶmK8|۶mf_!^0ѶW}m;~f̜1~ۯre|@v%YJvJtlAߵ*f/ rz? 2@yïȭ27Vu؂I"aV~gV޲5|Pp @n,$% Fs+c#YG*YH`e sT +8 7d`rC 7@n8 7 P~P(l ) R1iێZ!GJK~k:0>cc b޼ykعsܹsԩSnݟ~Iޯ_?N׬YGO?ǧ^zqqqk TO ä\rԨQݺu믿6mt„ G_tiTTTZfYVlyeo j@u2k,!CYfyzz2$))I\w^{M8ի%m^yf[ zJeXRJ) P]LwdQ0)"##?1cT&10֬YSPPܫWVPX({kVp7 ¬nayZn鱟Ν;l-;o  ܐ ܐ ,30ܑ@U~`Ź#"T;M ȆU#;"@P(+yj6~ζׁ f`8x 20"Y(?T9 @n'>p @n ͛kڈ}۷ooٲNܹs;w6iDVܹS,۷^۷oNNB), nM6zvΝ;צMN7hР|N}||26{0+:B\||={f͚5bp999Æ {饗±cǮXpQQQb9s6mݴi9s8)!>`7<BN>_>ܪ_qeggGFFFGG[8#G: eTm0v a:r8`r_~o!dCZZlY[Bd2رM6Vnnn6m/fybaF]F)zjƍXiZZZvnݺ5`V BJiaaa&M, v"k\ @R 0v 7v]d<5ֆs}JRT"OKŋVΝ;rH j׮M]TS0 C)eF:)!cРAIIIvj2|cǎQJ[li;˧-[\reaaW_}e tNj20ɪn~Xp?Ji^^ޔ)S&L`Yl2I͛WXX=qDt$ܸqE AK?_ҥ !$99y֭[|_BB9&&򤃓P`hF10 FN.]yoߖJZ`J5Kz#`h4QvjOk׮'xBz㓕UdM10*qۡvEvPP10\CԩS322-ZԶm[pW޿``T3,,lÆ Fqaaab,Yb08)ۑ(77wvj%7J˗g̘1ydpSN5,nډ O=իWB@AAȑ#-ZPT-Z8~X333O>ZO>!%N:-³۩IJURB_e,W|kՔݚ.c`PcU=B G"B=3/ b־2v\2uJXY)C ߬{5͜y @~0lc`rC 7d`rYc`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rSWtMc7Bμr#㟊@QB JH:U}epBHy G쨜wԣ4.D&jB\Q9! 6vu>2_bԊbv-"=-v[/~OZ[. iN!K*ɸ @n @nV l˖-ZtM4Y~XP( Zn۶'BB훓Sd`;w={U sAi0*****͙3iӦM63gNɢJ-\pժU=zjaaa~RNub?}twwiӦǗ d*ݳgOGKiڴi1[KIIiذ!!$444%%aTu'BPT֭sIV['W-U4l[c ݓ%cPr d-e 2…u85WVDbbe ܥ۸[=[;-ݺNXm5\FeҎˡgږX6m>lw㸋/vU,߽{j7oBBBB@UW lƌǏ?z(˲ cǎuR_~ёVd_04TM% =n8^z9rԠ+VXUj׮͝;4TM%l̘1cƌ*֬[n\\|}}P=rC 7d`rC 7d`rs=[b敊$^!$Vt.P=2΋TS7i%E=xUXU%;+:2~q%٥\*ѸZªٽT~87)#8 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rSWt%pEBHm)T)#KOB L[:f[r8 BJ:;Zt>~S#BnMfhDQVJ~`񁴁IKѢRvOۧڼꊳO5l튟UMB;^WYUq!(.Zz5Ơ hVd >|vi233ByRZdeJ%CHPxJyxJ vD5 La[^y^V (|a {wQ y޲ܮQ>Buyذa8 P-eoj$;;{ذa]tb"BHEnȑ#jw=( |FO9ҺuEG/^R eE ?'`P߅(w!jn"#MfB){*gefVt PYPׁ|F)Ym˫@ֱs7hؠu6*U%N-g[Q(TpyBx8qcp;"bꯝ,U(w߾AAK3 qeh.047*: WteW5 =*)!&_OξvȦ|7kyPXI?P$%%͛7T+J@f8 |YH(8 |YHBBxy_,PTJ[ըbf088 20(_,˦\t1''cDJw`\vb*'20JeXBh4:]ev!|ݻuw aeٛ TU 4$`\jb*U%@Y` ՆLhpSwy^N-RTn٢UfxۨQXERh4&MԚGԫWO`\jb=*dDRխWcmڶŮ:920aRSS:د_dPpBSB[ߌIJ7ܿo@a ơ4;'AЊ 7/e9Jr!ibr"­Iߠ~캡J#33̱#Gz]2tgRj$U*U&cG5o#J*=GuڣjJR3&>rRAz~q;*M BzzZݺueN?҂CB2J z؏~K IDATFݖv݌l~MXG 78q Lׁ<_! H 9NnnbZu!P~ !uԩ@MlE|u3 sGvRs @TQPB*B sgN=}$/Sv9:\X1;;={3W!UReX('N}Al2}Q䀁2|׺3.n֬322ՑVs  8_?_?zu̙5h ysaEkߕ+Ayۿvȑ5 <={$Rݺ ZG֭33380bĈŕ,!777771E}UdJ2L,TB<;?N$;d H@ď[cF(/c/jNO>[D]:w^`ŋn&%n߱豣Lq$$$4qGDD\rʕ+Y8C(2=D* 1cF'$$H%ⱱVgXrڵ=zo0w~bݻwܱS'OkE(*lÇn]nXhlժխ۴KINN8hPz|B/Qf㥵lYbEtj\B,OLJz_~#o޼eeSc_?/X8Iw^hqXǚ5k믿J>j֭jYfǏWK.}j5ƨjGzjJRV^=j(Vd]GԳgON'MNBBB.]ܺv횘(yʼn%Z-(>nذ]GSJnbbbRICBHBB33Bޝ1s)ɷ,S{ⅅ>vltR׮]lg̘ٻ7{1cba7| _hчg4*^z%enjʲ,˲\sww:uE-R9uTw\z8iҤnݺK3iҤ~:;;O>&Mj,lٲw}W|*/Ȳl޸qil;Z2a„5k߿ĉ\8d=3qu ׯ 0{v( />(ٶZv.],mٱ}GaaaNNnƍ !ӧo/Sor A|U*UӦM~Xgޏ?hŢ'ON7q„/X"8p >>~w!$Jb۟;F ;vfZ3ì^F*|ꩧ~:/77755ǻ8G9rcfs^^حRewΡnݺXUnΤI;S,&&%3yXޥKk֘UlOSo0/_QN~ k׮gΜ.(:tk" fsllĉē'N5Nu4Qݻw˥veʯL&+u*5b٠-))I l6ϝ;X3YT7nyk&U\7 h-Zt)|{f:+/Ly7l{\WQwUii$W5`("U Uv.bm(((7Ei+*jVR\Qk D2g;tdn|?19ysy83c„U`G ]ϟܘZmh_ƍ`5̊)8qB,B(ٓ0v{FEO&Iz^ߨ 4z^~rW b, EO(.. AƐA|\?`3Җv39r]x-?o)nn_M69&03I* o|}}ƌCp0ǰaNKH}=_5 YY& TGz`7.nSwm߿Tp yX/],;^Vwc9xQ#癗}KH .0wB/ޏ^Kbii:I7scxHC(JMt1&9׷>W*oĈ7k߷`\(47[[=΋dt7TGJLcܭ&N݈9*O-;WFsIOУ$榦˗/ya6^@oE$RN4loSË>Ó'hoouhCnD_؀$c F~@]Q?7τ~diiiȑ'OϋUCCs Kcc+ja{N>r#ԍ.^3F0vNg^jQ,Y7 eΓ]^{}x~-UlKo5pP F`{Щ`'1 #-hq~RhGm0+ƾfmA&I[`@BW!^@,,, lll>m'ITXY?p<CVE ^ {g@ D里޽WUkIoM O!k`Z-ZG .#,--I$I#Ꞓ$IVBy(CH@ wEE'H۟>z$Am Ç aρ @ ӧ'z+{[^3׭R3{^y.  ]2>  IӟРehe*01tWIktuzanBLO;|cݸiǏ{q@^_Ν;eO,&5_WH"f_=mW^;v\n! ˃_zyaΓ].]L F́>c"#_yztoLcbM|0TN9֯9Ov6K4 f(CaE:W[>/󞴪z/Q~ZGRk`w޵zWחö9QXXHwrrݽ;ZUJ ?|Ŋ+?]ferkU)CR$I 6dȐ~))9w~r]=sp\Jj^b*[x\21V$x[GyFFPV]pa͚էN#"6qބk ,?C?ҥK/ [!CQa$I{\]]  $Irˢ۷oӅ:jڐ Cg3V$udal쮑1 {yP #h!Cn6|]\h W~QljoY򪽽}}HnSl 0%5%933l.1`ę`}y]v6#"qaa!V]=m0W^2'5|h$Ri:4Җvly&fGhI2tبzSʫ֞2{wSV~sGtw~[^^iS$gY(- ,JER MnnPM$ 5j]9~0 j$4ӧ7*~ݳ'1r'@!|ޯٽ+vUn7o6eʼA? W*ZZ237n4.aӟݻ;k̈ GF֬]75oO?Qjߟ3gO6{v.KR[n: gFAS0Nl'[pݺpWGScǎk7)]w~aԟ{?p|{[YY08BG^^^k0~֪vvmذa bDZ!wSO>pNN~~d2J,9rS>ض6 _ x\ .12PPj'ToQZYY~([ .9Ju?+H7~ѣG:8cj#nVV6E"3 t:H"-Oz=׳:m+((رcf7`ׇ _6=>wܞQ('1 ?LVkh2tЇZ[[c0l0zW3$v&I(#Fyذa>>}|/\q۷34W\75wjD4ao~޽Μٹ3ҿH|O?8q"7Pbt/C 0?Ԇ%Q9;mmm}رbHԉr;/36>_0fhVM?=AL̃@t/w ]u_-]qK??3gI$W\aK.iڻw^d7􌋋W*J2..KQs1؝&趷Wtk?^m4MKKW_~5b 4hoZ 5.a_ii...ܣvL #t\#"YZ.Yx3gP|||mln@ ݣ^t)b%?q'4EI#-Ѧ=hTwumJea0Lь=؟L;qM0=>z$$&hR_9|&Jo 8pԨQ˖-ZCoPۆ`RTqw-8u^^ZY {cǎ踝0wm`b;Lf'|{ص+vIIǏO"\Dg9^~nu.n us=#|O`<7a ]t#"V,_nggѶy! t`)XN噏;A=+F[ZZILJ@S@0ysĸ hiiɭӥBTFs=|܁YAmD K߫DOjfs+e{zKӧAS@}C  @ 0+#y@ ϴvK? lE/@ GScCM|@ D*0@ s*0@ s*0 666D_ɓ=B(?:th̘1tQثmrL&~~~@h;tMRѱcDŽB![XXOayy@ XhJ0.!@cUTT܊%((H() !=PF 5C&C>dhL D/':::++ x(#QnڴI*JHNǭXPP|ta]N>=k,HyG ӡ;sٳ}XXXuu5aeeef Bŋ< BT* _9FT#>WlڇaGnܸVҦLBqFJJJdd${5իWS5kԴ:88iӲg;;q}@K,ܹ+E)h:=F˨ϝ;wb//*rnb'Ѭ#7S% '}è!AptpD>>>TA?<oڴiϟ|>}H$eqʔ)/^0^QQ=<}zeGGGDDjj*ekǏ'ٳ`g4|>vΜ9ZV ${n'''Bp ml? B_`P(fdd8:: ۆF;>]>&%%=y򤰰ٙ4߬V)Niƪ㱽b Ckh7Eh%%%ZVROٌ+X,~ vCFVXq:e衻 ΗQh r huڃL1u}„ EEEHƌezӔO8z9!OC쉻PCW`Am :݅d;y!q%w׭[ IR(Rk4oQ?j5 TH]B>Ǐ4.GEE566T4{{{9IK,1ȑ#222d2P0 O,;ۨ 56h 6A < xJꚖFL&Q(5œ'Oh999eeeiLJh4n;>ǑMNNNyDX[[LM<1''ǔ655566Ξ=ðǏwyaihP?MBhwvv{U*FdKlM=z488xݺuIII BPWTņ vNkjjO҄j*8*X6Tٵ_~9qDP8|s|666|C.˗===mmm===^ 4͛͛G-BB=Q %lF{bbbbz8)77W*J$Dj??#GRPHr ve;wtI|CƔ虞p<|UI=O= MJOOOXuVѬ3 '4d#tp߱cP(?goooggMu v9r$/^X$=iaD"H6nn-Ǔ$p mP?ÄizF*|ڵk%@  =AGu 4t&챃3͛7ϟ/D"oݺeof̙|>>ρA5 )k T`kn]PJ${C ^M~H߿=UagΜ=w\yi::%ԛ' ƍ0`@KKK&?]а{رð=)@Ylll,,,Bajj/DoU`⹅S0Ejtf0"h*T!\-z !7@ U`@ U`@ U`@ U`@ U`@ U`A{Ϝ'O:::h rС1c0Ea׾a2L 5mhHRGǎ – PP(:: K=Gaaϟ>}Xx<77" *(vP]6p2!{{{ ET* 4=7謬,W^~$jM6IRTI8ĂF(촟 {*vYfD";d(ݙ3gfϞ-{f͚% Bŋ y BT믿r!1R:u*--ƍ~~~1BTzydd9s(/OWf D"QEEH$JHH0:;]4//r͚5˗/Bh֯_cǎ۷oGGGGDD!tP!F6=Q vφv=@C,<<>d{BsyѤIc֔ݻ[{taO\N>g_JCk믗.]7nŋ1 [lٲe****** bŊ+W|ᇆ4 {j>?M0 3rJ%8ނ ح1cFmmmHHܽ{$ɪ*WWWsn \͐c]ZZjKJJOrDa0Fa 5&M+[gΜٳgR[`ϟ:u˗VMpONNJ&L8}={ƍ*///Xsήd=~xƍvvv8QdԨQ-a6j(PF{hxdddCC]… ...`7$$dΝZR =rAl&8=yD. V\NYYpaBBFٽ{wW2RdrA}eT'5 .H v2: }L !s8BDJJJpp0O^|'%%JZx 8{)SHU9shZ@@dKKݻ 144L 9\bG~BP( ۷oKwvɓBgggٺu5kH}JߦbpBW((hHSvʕSD۷ B DeT'5b R$ ЬZoڃBa0dApJ%wyPJ%eK,2^v)%I~=wΝc;sBaB4]55œ'Oh999eeeiLJh4n;>ǑMNNNyDX[[LM<1''ǔ655566Ξ=ðǏwyaihP?MBhwvv{U*1yLw ͻG[.))iРA@( *j߾}bؐ׮]tMMMIPCЀ@ A=\ۆjX04f ͛͛G!z/rĉBpE|իW&tP!!rj gQh.ZLL X'Y+J%Ibb"Quuȑ#)IccchhH$rqq9q 2;w$>!Þ2pp<|UI=O= MJOOOXuVѬ3 '4d#tp߱cP(?goooggMu v9r$/^X$=iaD"H6nn-Ǔ$p mP?ÄizF*|ڵk%@  =AGu 4t&챃3͛7ϟ/D"oݺeof̙|>>ρA5 )k T`kn]PJ${C ^M_XXx03gƞ;wB4璦!?Y0nZZZr7ҏ577߻w`ǎqObcccaa! SSS/}F}A z C -':(CnvFDMe7*0bEYA:@ ܠ @ ܠ @ ܠ @ ܠ @ ܠ @ ܠ 0H}s{ N<舦7-[Ftt^:4fF+'^2Ntg}˖-@lCg2L !0lllR)cǎ BF a` zxxӧ@UQQsss+**– PP(8PCPC݃0jڼֹC>rooo@h"J KtttVVٳgnjӕ  6qnGnڴI*JH{9sf>,,cƌ9{lw9 ҳ ԩSiii7n㏁cs̡KNN?]-bҼ5k,_Z~;n߾ "B$%$$p0ZAbC}6dgR 6e|AEEL&۳g/,=4iҳaVXX8yP__`yfJJÇjjjkHsk׮_/]4nܸŋst;y'O#XzzT*4hPHHÇvGW_} v?~y/ҁ )))#G61bBc5rH ,,,0 e˖ 4(,,C5dQhء>Cv jY&33 8O}4iҤb 9sx<`@O?ؤ9::%%%ۏ? w5jTLLL'e!s9sMύYfؐ$9rHh@h :͕+Wk`bxUUU@B_`6kP!5SN]Č=LJI]v3fܽ{n}'i:0Mw 2r>}ӓyuԩS[lOAٴbŊ( öm۶rJW_yyyKC Ydɽ{0 űnG}U`Rٹi(uKo;d> H$%%X.qVVV  r I2CݧtyGm8p GXݸqq;;;Jt:$u:B 8.HFhѢola'Y0CFoxdddCC]… ...`7$$dΝZR =rAl&8=yD. V\NYYpaBBFٽ{wW2Rdrxqǧ? 4& BE$)))A,Xnŋ?~ gN2sNJ$ݥ`CPMCֹ uzn-O>|#ؕH$.]S3l| sj$ɖ''ݻw;99) жnݺf$RSS HJJjB\2<7Q09+0$JeRRܹsBF>ѿe#bbb ŃV^(ΙZ6==V {H^d Z7ۙ>}-[,X)@>MDqrruCrrr񢢢NXqwwOtOʥ~cbȥ7x?CSX[[L rʈJnClccccccgϞaX]]]\\\FFF||ǏѣG׭[4h trr?8@֭[|?g3&!!*JF5;]Hz#X.B$^l4qP&/8qP(?~UU˗/{zzzzz^zh655͛7ϛ7Ca-܃;jԔ cLL X&Y+J%Ibb"Quuȑ#)IccchhH$rqq9q n}///Xsή"aOuHnt1D~~Kuu$ $Atx;viӦݹs?noooooAƔW/Du zjn-Cbȥjt T3//.;;>vmmm=<<::v 6l H$ƍP񡡡tC͛7BaLL P||<(JrڵR0Q2$dCEBZ vMӭ?[Iv߽@<˗wnlؘlo&<}<.efd kK?~|޽;v`ֿ=E H~zyddd}!x<77" lii  AAA C5D5=Q)a6b؟[ ,ZHR鹁9iz.SIMMbT_t 5? BT* _1f)sZvӦMRT*FFFt: nݺ B;;FO>=k,HdKQKɧnu?aaa7nPiiiSLaʔHjڵkW밅L YCH$AHR O7oެV7ogqj49P&cW鄇АB6mZvv'Oo }8X[[K풴544j;wNVTcǎM4޳]$)))((2HOJJRliiٵkΝ;I厎%%%mmm A/R_xU٣/xdWg~wovcHFRD"]=7zWW_~͘16$$ҁ 9 OArooo>ooRV[RR2}t tqq{.IUUUB!ÄhF9w.8 1i$u ={-en|>ԩ/_f[a7q<99Y*N0{챳7nܷ~ bΝ;QBxj$IZ͍'NL:9;;>|… S &Mt] ycǎO TNS񴴴e˖QS]Nz}SSÇ=\XWWGsww$Ix bذaǏ90[[p J111юahNP[[kccCΝ;}5/]~ӧc'8;;0lٲ% ''~8< իWϞ=r+VO׿o>c >쳙3gZ*%%+d=eb믿 :rƋ@dd꒒.\8|9s`v1@PN>_߿^^^^^NQKN6ŋJ~m;@EORZ*>>~ᔰzԨQ͝;ȑ#߿:.++ yƍL8QPL4i׮]b Fd2YCC]7eժUYYY`;<<oSbB IRPtzhА2ʕ+SND۷o @xOΝ;$.\loo202@B_1}@pΟ?50>  g̘8شo7n\t)gS4;͊+l0Z-j`͛1==,lĖJw^__߮Ct ,33ã!g$ B*677S Oev|>XVՔf'4Ax<+΅2ќ KJJZJ>ŌX,~ vCFVXq:e3g.${eff{+WbŘ0Ǐ?~<2*0ӠGeff2BjڰTSR 8dq2Ez\.+A@H—_~9qDP8*`/_z*ljj7oϟ7odrb )F)jԔ cLLX,Y$+J%Ibb"Quuȑ#)IccchhH$rqq9q nl Ν;]yaOuHnt1C۶SN-,,/RcN5k߿|~z6.z"Ò =B73gvvv}N%쉁aD"H6nHRK֓'O֮]+HA``۷%D/,@vŭ$I׃ӧO#""/_A0M `#hnnw^AA;0oacc#L/ѧ1(7`cm0?C !B fxW]ٌن4C *0Gρ!@@ U]H@ sB"@v@ DW]H@ =D ݈^_l[~С1c0hСC> `N`ϬwH&e2@ +//Bߡk`VcDŽB!gÃO>77K=[hx<[QQ   B!(vsaJsy.O|܇ }`ѢE* M D\&gbccJhnoots4N>=k,HD}z̙ٳg {{0w={v̘1lybbbAAW[nB.""B ĵInڴI*JHNסeeef Bŋ}^HA A5Mcbs@bbb'ZA1R:u*--ƍ~~~1B_;Og>22rΜ9_~+yyyk֬/PzX_~ǎoߎ„HTQQ!8BE( ;gCFMlc(&) ***d2ٞ={@ 9xC'sӟ}+**(]_tҸq/^RaaɓznU<<<_?>:*BO\RRR>|XVVVVVVSSw5_lٲe****** RSS mi:]{; |~!afM*J$%zWW_~͘16$$ҁ ArooνRa[zKKKZmIIӁݻ$IVUUrj#$l~F;(8 1i$u ={- S^|mdT:a„ӧOٳnܸq~-P;wJFcj$jv47{N81uT| .O 4iݻwjz& IIIdEEE$+9 bdiii˖-v?FF;Μ9S.s跶rj&jH(Q x<޴iΟ?>Rbʔ)/^BRn:;;;zarh{EE.//Ps___\&gΜiOtrʕ+uuujiw90 /8 ~FqU'sN|nWWt/c|ڵm۶ׯ_~V`III+V`V+++A^r$IL!ƍ K=Xݸqdt:I:spH$FZhQMM 7Q}fhs㑑 t .ݐ;wjZJ!44ȑ#AM8\.Z[[r9ee… f](zLS&8V<ȝrE@"\xQݿ?""$i^^ʕ+jP RԩS'OF[3qN;\pdU` "99y޼yF; Œ GGGP߾}u5kpt( ǻ!CP}jBHLLkO>|#]x 8{)SpʕД`4>8gVkeP]]=ydpwvv6't:+Jb`f{]d2Fġ[jUVVCHT*ΝۉrzˈU@@juvvv@@v* 5T__vm;DsS`T`nqq;XD"QKK QFQ0@&8k4$>}JߦbBABtFAc˜+WL:U$m߾](!DEE~'Ν .xzzzLVYY Ub(ai@zCBty///NGU`|>A(Θ1qꄂ'&&T{RZJv@@蘞JKKyƹH,Sbц|c4 IeDHHȞ={JBHHHXp>u~Ŋ[l`f{Zeffzxx0uIX T*mnn1e#.ki޲cZ4;q 6m#ns͍¨?DժT*#X8Gq`71dnGLSV\\Lϣlݻ{.$ .]ʡ04 z1"GeffRgx jaaa&vpuu%lA,nZnq)SS*0:] &1EcƌU;U`]& k4Ot@^#=:.6tcfIÇ}㞞VVV`5$0,**QRwA6+@h9"+==-##C&q9¢T*SSSǎvmPء>v 4ך S'O޸qcCCC}}ƍLߏN٢.H k4;wd2>Ӂu5i5cozƨUUw6<<|%ITTTyy55`ƪ $$5$$ʕ+dˆHHI-b>>ԍ{Ԗܰñv,W\]])iՌ6ai@ONZO&l߾{"ܹ7Rg_[ZZ-[&b,x.5[lKH&LnnnTyIRjzٲe .!TPP0eQ >@.+WRlT/))JR4**Otp9ώ*e@V`7JibЃ)luŊ .d X, ڷ\|y@@Q<رUXrrWZH1 IJ 7+**9~zJSbb"_GGG""++?X۩LnfbF^Bf$\+[P(a[m vBc[t6aa^û XBx37@W@W@W@W@W@WpLCKLC8:t(ñv cuV*R)#""^J qoV"0z]h ퟭ6,ƍ?BlrssBa@@@nn.)ljj9sD"9sJb CukaXXuX^O{ IDAT^&̙hHPУI?u.6* Bg2oݺȑ# ={vҤID"DDD?K+t3m ș3gf͚%H ELL̵k}CݝA{z>qĤI:7sϟ?Xզ=rJK+ɓ'6 D+wҥUUU:.;;Ãbc% éS )\zUZUk"kFw!6~ƎҒ駟Bs )x!ti`@bbbꢣnťǎjZرc\BV?Ν;G78}Vh4~ȑ#Q';:ydPPХK7sHSXtW{3֛ڵKP8;;ܽ{j1oZ/hll.|hINN4hBl DA0??b YkaXXu#!fQ߷\\\}||HC#G#%oߞ:uP(ݿ?::Z,g [%99;777))ĉ,qͰǷiD$2%%%ѽ{>y$)=7o߮P(ߓzj?a!zXVn?޽3;;2$<<0V+R&NhѢR3x.C\?~?ׯ_~V^}1 -ZvZ ֬YxbK!c:99988888899 B.!O`޼yAh4˗ӭcC^zL&^ߧOa>#X&L &LPVVfU6<l0~!6"L:h4bPSS͛}||T*KVVV5NVVVr ~bsMOO'/^|rF$ xzz BիW1RGx{{ vγLNN6 III<ZT=zۙ3g}RT#:j ̦ J=j[pBRu ]H^ϱÇe\# b% lǎ׮]c^M4ٳ*0+ρdfffeeÇۗܭA f֮][__hRSSI!6Vb8++K:tH,]vt4R#"Q)))ؿa0gQ.ulٹ{`0`0lٲITǫT;w~*VMOO7v]aOB֭{뭷HVOWtI$&O׻ӻwׯۧDPO}7nlhh߸q)SIHHHKK۲e$Ɏ;jFٹsL&b tRyy95A}YCC:6t&a۶mnnn#GR3 ۷o&-W A 0KFw^Ѹ{#F/Y$))iɒ%ZǺϾ}L&ݻ\dɪU._luDmg As-Wt%g[V%49x/,H˱>*(( quu r ٲ!22R$EFFRKX!!rkm3(VUUnĚLB!˷nJ4h%J~~~X_BCCe2ن %S=e...>>>ԍ{Ԗܰñv,9b&Չ&Om]z\a:6mJ<.c*kkk͛G[5`0|r\.\eЖ-[bcc~aIIITTT*JQQQ\BX!Bhƌ| ݐZ^lBG ~ĉ"wޡ#HK.R?p[FF%Ď!TPP0eXu!7 2.)))  %%%<Ǝ;.]J YWRR&H&UNN;j ́nT5,x"\|y@@vi#7r5{NNN*.^x3v~cccEEő#G֯_ODN'C T`ϯ<T] T`] T`] T`] T`] T`] T`C#CLC8:t(ñv ]5[rrrJX,z* 3 BA?J$F !]ݎaGE"Ѹq~GRUnnn@@P( %MMM3gΔH$3gTT>ގq~ʔDGG;::ɓܰpLqF;Fپ}B>|OJN$ Dc ;$Fu.uy~ٽ{gvv6%tqq2dHxx8aV2qE .pq~zcǎhѢkf͚ŋ]BgnP(b ) ͛GFY|;:6tիd} Vq;",\76 fl„ "h„ eee< [i_lb>I ))iѢEt@ `7;{lXXm0O~eRϙ3 ~.u@ xxx3cU\\&ܨBd2!L&H$b Y(Wر>vҬK s1116l0TC @ HHHhii 999ٳg'&&͛7'!JT޽[ٳ? rFfΜb0jnn>~Q(!=[DWhl`\.?d}+0RG@ҒKoذAх\0v Hy>l0~ѣGs D" 0uT(BMMM>>>7oQT<.SXYY9j(__J.!Obܹŋ/_H]БTWWOOOPzj2&\ւoooaw66dw%LNN&/\rjY_WKۛ󥋿fXXXfræ J1f쀗,Y_˗/"r9vݺu'Oƺa.ꤤ_.djڴi{jӦM#m(رK7I8뚛ݼ WWW@@~aBIҦ&za^jmmoS2T*3 RF._(J?SDB ;Jj!V~1?\CڵkǏG>}6uy~t---Q%ݘƮl&2Ć*bB"><-ٶmZVTgbr!TXX8rH@2QJ"!f[V\oӅ+WR3f #ñ# `a;/RWodˮӃkjjXR)F*-)t:P( tS^O?%رD:!j~~~7oD pŊm'\K7I=HN:e45 uH&1)Lv=l.t+xB T^^ĉm;r*z޶<ħ"==^[x1[ <?X0Wc91A?4tPLW^ &333Aff--H$TZX!O.Bg޷o-MBGjB.$XÇ22;"QrqqimmE$ c%j*0+ρdfffeeÇۗee@"DEEc;9x/WWא+W-"##E"Qdd$db )F)ag5ʣnǓ܈! B.oݺ:TYY1hР}J~~~Ge``WѷBCCe2ن %S=e...>>>ԍ{Ԗܰ0Daǂ#]l"YhlX_yWWlqjlڴI*\|躌7onr\rJ^ll-[B[lW%%%QQQRT*FEEr ~b3f|7tCjzٲe %l'D"77wyz"yzz.]Knnn;"Q:|={Oa0 VRR. KJJx Qk`dvן',Fub(P_Gn;2kx=U.]fƊ#G_z=hFOϽЁ@-: @@@@@@<$=@7bCed~;OlvOgV*R)#""^JDBoBCP}3[W_4Ȏ1c]F }ٞC5eVc9y)Sbweii3$ۊ+z=AF?T( "..d2q yX,ӧOg+dpΝ zxx)S'm:lgN4I"H$scuӁe֭Ga^;lgg&T`ǏOMM-.._Jvzx\\ԩS)/wɓ'?ݻo߾*;o/3~ҥK.\H zׯ_ƍu֭X&&&JҢ"T#"hƺ5JŽ([}oFQQRܶm=|M6zP|W_}lٲk׮]xqذa#4Hɒ%K .ߟw={ٳUUU;v򐒒ҽ{w[ ͛v钒5k|7vDnF {_` ϟ?|.u,>.u:zxxء}>0>]| Wk4Tns/555juuuLL +0aB^^;m` c 7N7n)y&BߟG5duXذc}Ū BVj^1`^ 9Ņ`Ĉ'O$%UUUSLquu"PSS3k,HXPPV۷oW(Çm6lذdPLaÆd=~kknBYYٸqRi|||#ѣB믿&iMJJ^^^6/3gMduuu!??23F?lJuNGnz2ۃHիW$/_]SSC?e2YUU=CfرmҢB t:N3q6.i:&g]naÆ?wϟƍǏ Ǐq)\p5kB|ɢEJcǎ%Og.?W_}^_WKۛ󥋿fXXXfGGGGGG[+$pY3`0L>!R#DI$kd+WH)> IDATt!ãqb777@F & !d2D"kH ϙ3{a ;gQ.uR{nVg6B/@G~Qs1116l0TC @ HHHhii 999ٳg'&&͛7'!N6zm 9sfJJ`HJJz*0\~yt+VBimnn>~QH!6=l_]Z-fsZZkZZZss\.X,>m1O>Yt)BH xzz BիWT*wܩjU*֭[DddNj4b11YjϏN ̙Fi?BP]]]ff̙3Lv޽6%󕕕F"MVVVIGpFdhHɼy>l0~ѣGGFF&''HRyf2y<$Փg͚cH $%%tivRtĆuɒ%_~%|,r[.BB𫯾jii ]V^uiӦٳGfdd_Q?ejkk׭[7ydcl\a6ʥ~@T駟J$``T`n^^^PPx@IҦ&z.3s@ V6.URٝQؐ2 ΨRZFG[v?ӧOSBi6/֮R,)) ~gV`6H,YN3+WFL>],{{{ڵ\_>B .IJhѢ?ܦbrQWWT*ׯ_sRZZ:ydPߌ:lHcٶmZVTgWǂ%ۧFKd2Ū/q>}Z Oa[L&1$ZZZB]WXJR(qߟ#c5qO^OOJvD"2jvib"tXr3(:I^^&שSFFd2L&V2J ')G3B>B"==^[x1)qZaXb͚5!!!1c|Gk׮ei{={՛GA5k]Hm|᧡^ir l֬Y50QQܠH$o uYuh!-:t(uŪD"V` 33ӪW`FGU`VOII߿>ҷo_r<&?[HDXXu:rydr֮][__hRSSI!6Vb8++K:tH,]vt4R#"Q)))lFŽmK ׭[[o5_ܰ1 {6 [lQ**Ν;.U|}}ӍF]lע9eSƎa;>>>3LwfX;K\\ܹs?SNB6/#3{l x㍯ˋHj;Gtt{w8Z[[8rHJW^M}ɓ7nP__q)Sp I֦_/ J/v,A̚5aӦMǎ{w `GQ"HvءV5Ν;e2-)%ۧ3 "rĈ{5w1b)IHHHKK۲e{ػwdڻwo&x>yKFxx8cd U*o!BCC7U[$**WWאHHI-b#JFG9؍_ڟ! #BC}s0{prrWt›+**9~z~xhy^*0hw!ƱcǼx݊$]F3:Ų`|CeX!`-YrrrJX,z*)tzK ~oH$Bu[ydggDq㏤܀PK fΜ)HfΜRxXCXa%a3%"![Xnݗ_~IS掮QtT`Czհ0X--mȑ9VSS3k,HXPPV8qP(4iҭ[BVoގ9. d2Yhhhyy9ch ))I.{yyiPt! Z!jw!Q9zh``P(ϝ;d6ɣaȑ7od4ꍝN7Ң'@ HMM]`z^,[ۛt:N3qDŀK Bi%RرcȣX=z醄BرcϜ9ãn;AAAEEEիWۤĉsrr-\Q2;V]V{nnnt9"˗kjj}677SWcvK;XRRҢE`7;{,U( ӧ_|!T*yXu; ~b!zTsssZ BɄ2L"G5$r̙SUUm?t[2"{_.C btt͛zMx d:s̹s"##ɨ2&&&͛7Ϟ=@BBBssGAQ@T޽[ٳ>ȶ?ϛL۷oX!4}#GG8xbl3,\Ğqpi D L1cƜ;w*0J}H̝;7==ܮ5jyQH$FUwܩjU*֭[J $'' Yf4o޼Ç ~qwێDNj4mh]"y{{K$3fܸql) }||V^Rxn;V}˗/oZc"O.]Ҏ wivRvK,/˗geerGUBj:))_ 6mڞ={ZmFFƴiHa*0.C֭MJΝ;b6-R~:tb8}^B̙3&:D"D"yW;),,9r` wcbbmۦVU*Ubb" K{*ӧboo]v $@V#j5ue$)?fWWWM#bCD"֭[4͎;ƏO2+WFQ¶Ď>dzf-Z?tK, 'b[䮫Ӄq"T*Bhll0b+0ZfgJ$˰ZjֻlC$:N(b ;gw⋙\L&w"+**"CVlA1nC)l'^{\BM>=;;;//ij:a֓ wy'==“B8NfϞo>jW"P՘^1zh *0:AluiСdNgṹFQP4"6 I?```/>[TKرQq}l{=n.LII߿'>ҷo_r%Ī?̬]^ѤBlbqVV^?tX,&vtiiiJG5DB>]HQ} [ u8J2>>^Rݹswe75jTzzhܵkAZv֭[```kQ*FXN׭[[osFuŽ%..iܹ|ɩSH+oߞl2fl3Sk=]vΝ a׮]VUUťxҥhJ"HvءV5Ν;e2]xW^pBRw^ɴw^___ZZZ`0l߾ݖrLkhh`;f[?{、$ggg6ƨUw5p[|)Q ϲMgp_~Y"DEEfǪ $$5$$ʕ+dˆHHIa߳=4莝Ӡ#"M/{ZRB F;x>7-`Кc=D}&|ݛM_{햪Myx3=7|f#Vq9t=F$ԭ-}Kg4s Ǝ'c}e;>g**kɮlhomrl;t}Ow9Si0 atT2tTV<ĿWx_Sj\~Kw\]k~u9qQwʽ{d_/I sSub @0bA!Z?/ֵקo5h~ U{CĦI!AJ}ABo?U4<}^,M!y LF^yYcdv\aԪ {oNyȾX%"hLvر>޲aw[5-Ȇ(69> F*(\3tv@AOx4? o4ZL.+2zO`߿+҃Fv΂P Uj \#\56٧'|_ f {/5X,ł,`00XE1^{ :|u}N?n"|AI=tn=6Xacłط[Rggn5h d ҚH!6Vq)={}Akv϶,w so;ܥ/|qpҙp醿x}0M!z7[{,4d_MBS\}ކKNsa&bȗ {o3=(F,m6P*(7%=?x~0Mn y^2+99E?LF3*' G 8s$hܬ3'*l},kTҗ6,U-Atsr#:zdc pN:95v`hIxe /rKFŽS5P@~Hs }3n3Iol\agl{KЎUa[/U![ALwaw^(mCqjujnXSe,;vvlL5Yze'Z-k-l)~._(c8@AFu9ZgJG~̨w ïx3m=y<ӻXit&/q_ xY|:~kCͰӉ+[Bt 6 N/Vo#[d&b_ͿNld_]Iȫ"iz5M:ТICv}㹧{>Qg0cB7\\jn)(+C_(('^*i!yAL+w.675c*]vf()]Xq } نVfs1~(W>ޒa+R9kC]$"9V$OMS߿|2!Ռw‰C"v}_jFAlMFǫMӿ$ړg/5fo7Uo(% *g& ױAw ïxs>ץV{VS֏Arwh_ DyM0Ͱ -.6ft)~IGKs &sn5M]닽]&' \/xց }ݦ~i<* zw̰=^[ճdAN3ڔՌ6 ;!^hB=;%L?ܹӼq&3)հb@9睧r.$׉n#Ta2kw\uyᤡs/U ߞ$%flĕ-dVW:u.d\1ݝ%o5#*?\ZߠP:<_\KWma/{"Bo)~.kud_K러ed.ZTN0>7{?*V*_ ֿM~~`2XK,| w^@x|WrcoFHf 7Ͷ!\.uڸH޽mޛ#Xa]^9vD]M?o̖{hBjVII>޲aZ}B$2\5xЙ[o:2DQA/l"S="_paæFԫ _g{*0@!hj6rGVv 7/u*jj00flĕ-dhgn}IyD!Ԡ1Z;^Swg w2 wZi_v1#H4ۛ {ͻOh\$^A?h8awq/ZxοϞ={k _A8U ?a/tv0;T_xN `oEjGf3|Ab̏ȑ#ׯ'7xعς*kyо GGٳ*0%8oKo"888t=z73[rowg@@ҽ[KϏ߬ڵݘk`vĉWEb#%CA jܼyՉ?q*06#xIxٜf3w朜(xI>ʬ6͑w"X$ۧY4:**g4ZUQ .@ꭦij1 ‚=bA׼j1>穫O@qG\-V-VKۋ  j fbj--gvqG*067ws2[Z-% hַjfbhLQo4c;m@<3ZGYjj%/jDvk`mCWyЪ5ZCJ ]ݬv@6~gz>Ak07Z Ѭomֵj fNYv6k Dq3,َ(MJ7޼F<AEuU_C)!"%a$݅5ِC?ȘjWb:~as_! &G3=eys򒩔NJ۱\_j1wS@kn ^(N>x*X~ILT^V`КVsݶ<[03vqp +,M|; @lK߻1U_0VCSGs?N8\a+ ,saʯ lh#gW<~tmjP({DZ,4M0:*sl敫f^+J"rP\m^,RJt'v;o' Rp*|Iff~UIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-advance-1.png000066400000000000000000000521711351617527000252330ustar00rootroot00000000000000PNG  IHDRc7sBITOtEXtSoftwaregnome-screenshot> IDATxwx- H!!=BB h(!X܋{A@Xt/ы" (HT%7ݙòdSwIg̙sޙw쉌" 8NF).ܮf,o&ARk~:_]\\U*4\UUUϞ= !JBHm&)""!DրR!kvBΟ?_Y^rm'''RiooT* ,kԏLr>g,Kwb(L ͲF\EIA2L]* !U ABxx^0) !2ˈB.sP)Po Tߩ8N.۫T*wQyAAd -hZRhŷJBH 8A  T2:Fs:ArBPk*bjktWXc-a#VtLUEiIII۶mU* vvv*J̗eY8N̔ BT*|ibFqpph4[%!D /rԚ*Vp&Q\|rui[p0煪=8+nnVe0mTR|8MjjNNJ_wJմQmڴ+&"gg*y0,j5ye53*4S~(-UFiyFөk۞| .n6=z>Tz[Z록yy.nnn*z+c Ԛ*V9%5ޙP:d2YUuVIq %9h5O1 >Oở,XetR&#.s p{G$ˆFyEsGp 0:GQ^e<"]6٧mlv]d2Yaܒ߷JӰ|7?g{<_Rc vV5ߟˣCzQUkۧRJyA8vOX-/|oG^/ G٧-!$ߙt^FA Vk9FLJwrx!mrRVP_1Q'c45bjJN鴚;wܾ}N|GTHxBp:FjyNVkCXu,cP-^)VbPGwͯwJ*:{YzGj5I=y_.kF9'eZ O_-(TkTJyth*"\d?d!1a\E~P *gy~|^}|NωkqyN'yX(G"H5)mx.T<ie tlF[b|2cM/e9T(UM)/_qJtlbलs8CL<|&6t{Vs8^uw?6=/:u%[A_M/,,$׶Y VzwhrN<Btl)YFs:A9^t,!9\qo6': s?SsJN`4զ&ܻs:jяe>*e62YFNhj^|VSXXd9Q!N`4r_#ܸY^cF8XTZ9!j گc䄐ߞYXag{Bi1.+Ƞ{ {s!A쑎eWuߟ\;y{ q4nX+t,T*۶mkgggPSNV.S‱ݡ @&fJLq<2 ҀA9NK]oLvsvxlXL Vups/,%"F|rznϮ** _)Y,ú8u8gj!N$S{k≛ ,Uj][NA~ok]׀v92L'k"=cL,!wsL|0X8h t-B=2JYGn@!=(=?Ȥ޽}LirsBč%Oar'rL.S/ e4շonӦ\.gYNPO;b? Z x^ OR:H@fnIɃ$hlLߡSW;׸'{5fAىRC8#-/*m%q$~oqEThO]LryNE/JӉßBR]7=k jGz>]ܝӗYm IMw3R<~zoEӆ+u;~MIϗ0ƨ ':}pжvK܃WZu.S o{YFұd2BTu37$Y sJnZRd\.OT/Zx1i8oX777;;xx0)Erquqn|%KsJj~#jnkk*BJc#ϙBl8T*۴iRyi;򼮦BR,hMNupp-2%HnyBiP987&0CNGsu5`wl:&u$}SêF!_X^|S*nW3W"80V+^gMh4ȔRk~aiz͍u t:^;s~5i*c\knZ^^ħ~RSsWd_+Hr_L]-ҫ~T9BJK(%3%(MlvXWYirә0ZsZSP2%Ah:o k+V\5C#gJ6m*Оf] Xf'՞)۴u&888N2eI%Rr@UwM5*,_9M5^cCLjk}MY)+f… LAddҿEY"lӸ;窼s[EnƆ,օW1;pZRĬx-[򂂂1<<{<}&uv!uvibnv&Y.7lҵ Va𶬬II?܊VX)(8dͫӪ` s ѢsJutt4o1~;=y+g\OSoWݮ(똛]*VaRN|xAAAAA-鵹պ&,X\BԿOK&K)ś-Uh?iUR$ٔ][ +5eh㾊)V4Ste+A?S>[vP+=LkW7d^;?ab,7;dJ:dJR x)h)h2%.?D04Ȕ4C8gpN @L @L @L @L @hpN @L @7.9% 2% }9% 2% 2% 2% 2% 2% ~%@sJdJ@sJdJ< @sJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJҲҒfnm9% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2% 2%.'M\Y5W5MֱdFW^L}Ɯ;w^Vv.--vSĩ^~&(8KC{s/k{쓍 [^Sm]tя ^uLW1pРN~|X*z8>O ξ)zxz}N~ ȨmJroEvQ-\KXX؟I0LqqqTT4r U`;;@+Ly_uuݻǎ7n,w5{θfee.[t֫Ǐ/ũ۶mnPMZ,c&Zr;{_JK˗/qFǎkԆ!www< hyiJR*w\gϞ3dAΞ3[o`:bĈo6 ,0蚅]vyyy&'Id2YppSk˧&%%nU`;;@+'#|}"'-,5U[˯L?zhL߾Ǐ1b4?@d2YIqQrJKAx4of\镟kooq\RAZGvuM?1YfJijAY||;eefԡD@v鵹S;$_uuu RZJkyyydܜ2GDv |cᄏ(338Q֢_a\ʶ3gnڷ_ׯr +/:u񒥄wmnCXw0Z<O/Zw%KBZ!D&d2)BJUooo$2""(0p _]PП.ZB{JաCz___Zmd0:orSΚ?0AuX2___z;w|Nڷo?⋇;g%S8ٖO-!5rzצM&W_EDv8sF4t/)//{饗)SlޜԩS)Lrttaqk۟zjLդM6ҝ;{^w7n0.D$zcbb6'&jfMR!J/VZTӐoJaڴi02Sw-d{ҵkeWteȑW8` :5%EEEїovZ9dƏwO::֬>:!ɸ'‘#G9RͤiS0@|`޼yOǏwrr291TsmG1c}I\!!,JΚ[U+~;j*V =tKo){oĉ/tz͸n?Μy1B‚^t<lʵW5)S?zO`;;@+[P&+<)oZUl~&Mٖ-r`R<_O66^o)4Kvboj)qNiӤ.Y%[\)ـ RH?ߺUSdz;Rkð4Zw\'zlgG}7dMK~EÀmܒ8[ |A #dJdJdJdJdJdJdJdJdJdJdJdJdJd?pi o֞) #Pꫡٳ;{l񵇧WdJ֬(8$kx^aHh.]9rD;4(##`EEEEώǷOߘs0t IDAT{7wFvi9Me@e˖͉'~ھ|2ZiIqqQab H2,|5iʕSNq+W^…R{#F̝7`o͞3nYY˖.bovssn< InԤWMom^].))P(~ػˋrرw]qL&+).˵8}ouR4WW׊97P]]-QZ 9sWϞ#G>O?lyy9Pߏ?=͛:U/5CƦ6? X8OXXϿR&Bc@|Ի/qx+WXRXj奋:Uֻ/˦68LƧ_l޽?4BȔf<''gQ;xI5A!]:tH,19yFFA::ĉG'BJJK;B{LOO'J/,lw^O/KfUr.]XΥebҲajxEA=ƍxdy%V£&6pPoȔfٳc.XqsΛT|0^otttrJ wfUUU\b{`vctxLyƘdr_=.'={ΐ!2 8{lEUWWڽ{ qrQϿթS'B_[BL& 2ujT滻KokAG}bm۶Ǐ_ oܸڽ[VQbkI=" n`z51vM#errN~)WܤjKcJUqPs6~/8v}{8o?ЁkcG]07v6mHu%v򻖒Z^^=J,yGw#F G5R&ywɹRXgP2gܮ];oׯ_sʕW._uǷSVfq0xrJKAx4o֤AiÇڵqqq6~҈AnZ%bGw`PpV3d| ˵p!#`4;.Yu豕V}ͷ !2̸ލ^zg6_5owܹPba;kea1 !b"LvǧcnnnPPPNNOǎBɎo? ۷o?⋇;g%S%jC8A!(0Ǻ ql<ȍ[s46id||}}a`!\}5`[a<&&F0ճ֭0q"!䅉}#fJGGG㮙S^1&QƎ7+UYM6&_M3&˯q%6쉍7lڴyaBI׮]-_QRRҥKQ#G._b&%XAk;L|VqhAn4yg[8iO `er\L>c]Ȕd|Mcf4Vd MҚZñhurB<z SՐ4o35{8qB,!$??̙M:9Oޕ믿8/fzuŊ997X>ך )dJC!-?E!.ZɦrBѱI:wB5d-豈8²7k棏(Ȉj=}NJ,K֮]͛6NH(//wss;x`?a)-ًrٗM+5-_,YV,d|,ZVfƴSgϾ;Zܹs,ssܹsG"dJX>}||!>>>lXt+sֿ#rvv9sŋŷ] _pMh%)̒!Fsyo޾}{ ba``7~hv|M`PP "!S֙ɡ} rs+ߢ"E<~w{˖n|k-_@Mn]X8Alf; % S SB8BC R$[4O7Ms߾dWBcv Vv<7DMs}[?ے0zBcB.]_3Z 2e1yNNQwffe=dwG'ggߔ]ЮCj @WK~7|0T+ wޯ_lc捍1D2,G[LҟI!v7X,$45dJ2UO>Ozʟ?NZj5ڐCX`}|n^\lYjjjH9>e0B:@VZ9dVIpFiBq:#`XLP[:a1j^Rՠy˲y)Wjb5rŵkx`J"V q3F l:ͣUVΔ<]xaȐ!A! ºe3O8P*!V A3VW`Xv4VutX9S8.LJ nKKRuU(UgNСBJX Ԧ9.rՠyOB?Ͱ +,nX7Zllvm5h}J Ђ8ɘ~e2Y]gT* OfXKStۥcAgsJۆL|ʬ 0`BA08aW*:gu߸ zTVVG9zhv,\BkDv+OҒbK I N ^FYCJMl5MXѮ]ӧo̮ݻnE:ݚMQytCZZR,k}O>cƌ6l8[UUZ&5U*Zn۶bNǷSFz&sX^n!+#=յ""{nM "2>s8JUq,V3>0Mspp0dj]TӘ'OΘ12GIɏ#ᅣCZڻw%KH}  A! $_6looOW,tfVa-!DՈ 2L,_ȲeΝhyOԒ[N>]'|2yZEZLv e˖fff9r/>|855Fǎ fW2Zc?}\|DFFZ>ɽ](Ζ3-E%կiLԷoOOϝ;utt ˍln#>ptSSN2eON* :K^zeʼ7L4q{ÆO4ͺu5ݯ_4~bZV!#F 㳲L^ao ]j;w>}x`;wo&B6.9s\2eҥK9"6lѢEj:''gƳ h:̘>}˲.\3wΌӥ qӦ vpe4ySѫ5Bm 2 \jڵs̖f482^GKS%#G((((((9rXњ5G'$<$eiӦN?`'z=MVZe5k;vۻ׬Y#~G'Oj_zFggg̍^ruuݷo_RRRT#ݻG%%%۷Օ2ɰ>裏yzzׯ ?x;aa]7xvA^z޽HA3nF%$_˯̙={xiV =tŒZ FK82Ojjdj Pǂ Gnnnmڴf482^6]8: O<{S_zaʯj~*1O=M^{6V銿"6Qdw&+]T9Y9sY3#uK\+[$ptk}ʦ oq:m= ;"b&>Kz؃NP[qxx~GNYG96)kkd9u--,".]4I]];R(++N5DAh؂ErM}l&e1f nN8:̱LI~lkİAtۥi!83X?S*.]XxB!bXa-M}lG3LVRR!ݽL&nX7Zllvm5hrTݻG>}c<2Zx^().vv A3glgvl5hr+;Dv~\Kl B=AŠͳRcW`Xv4VutX9Sd2*88W]r\irpp$V ~3Ŧ l Vzkߧ$P*klmaiZ`OdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJdJ, 2,#L.WT:V˔55IEGef櫭h͔ _1! 8mɔ,äwPq/EE~>޳]aLVN2RǍ7* v*Wo.SZdNrww7@uK-7uj=u/+}nBk$uoReJO5dk50y~ڴin48֦M&B/LJVLv[u  ކF==\PPs=WQQA)wر^zoٲSEkΝ֡FR,dn2P;w_DhsҒbZՄ<8j(k݄Ԉ3^ڇ~|ڵ?RoΜ9˖-;w͛7:U1 78L*&&f߾}.h,ujp)h/;o1Νlڴ{#^퍗o߾} ۷;ܧz_+ڵkv#9rD,)**zC ]BIDATl>ߜkNzg !، rvkIϟgq?0o<6\eBCC)nٲW^:uoRvvQEEEÆ ԑ/ӦMstt:u/B),[\\ܽ{wŋi䯿 <///֭[YYX,""xEaÆ%&&Qil/,<-a2P;wpB M) N4o͞3nYY˖.R_=^R\Dgs]zuzz={.^(Ο?O>XpҥK}d<̍77|S,\pȑ#SRRz\.o5Z=z@狟~~~BL& zWS˧Jnݺ.5wss9rŚ۷o9r+ȋ/NMM2dȄ jJY+YxtbPdڵkg4-_y]q%^}5ȋud2,唖gexZgѢEÇ?'rAFA>H!!! TTT˗/_|ysј2ƍ? zgyFj i=*..4hА!C`rRBBBJ:RReɬYv /߿_l-eoYeـJd۶m;|p~Ξ=+•+WzmTIPP08))),˲,}5sm 4;}&NصkYf[[k*_Џ.]\I&0LPPP!M Y,HKV_twߕrF!%Ay6m,駟֬Yk׮ 6׸8:B=*&ŋ;tPZ:q\=޾};ӧO߽{ݻwNY4ש>a2dO)|||rsssrr|||(]v]xť}'O׿uQR}zju)mӏqux!A+G%RksD 9MXKH k0&UVVf4 EcvݛC`򼼼^z;wB?;ballMX;dȐe˖UTTϜ9S,ӧϖ-[ILLK_qa4::?τ !߿먨([Mr=>}xiӦiZ6$Eo߾5Wl,̺u.\_GGGϞ=ٳtOLLO B)teժU%%%aaaqqqWׯs=gTIϞ=SSS-]hҤI[l&MDccٳgώޱcѣ/\n:E %}oҏ'O/L1l2Piii={ Arԩ b~fMeeeDd O=i>剞gy&88__zXlٲ=88ʕ+}Y鹏%K>|[nR:9xȑ#s4::Z.72n8\n|N9y\lu͟?ĉNHHxŻ\m0Iveˎ?޹s'O.]ԸرcǏ:n z뭷gϞM)|gEEE xGN:u S%G.WXXXXX8|pJ+ . 9~7|3vX( %}oҏ:JdΞ=`B!_I)KM:})ȝ۷vnv=z4[__""bӦMbphh(--eYa}JN6dJB133~wΝwy͛)))qqqC+ϟ!$??ڵkRl6+ABjp ן>}z_ToߎW*O#ݕr\NgggqqZ>p@R@Hu0vY999Y,$nXr9޳gϺ:;;F#!D& =&8vs8iv;{wRddd{{ۚ5;vnYk $$߽ە1'^RR{r8QQFQԫA(J, I~\S@Hsy^׫T*JUZZŋOIf}K] mmFſanSW56~G61(N|/X___UU^[X,޽{cpp*n'&NkҬL:555<<\\\<مǔģcǎ577=vZBp? oŋ>Heel"p܌Ve۶m--_K_' FQTJG /Hvv6qeeAV+6{BȹsrsA8q!d&O~Ѻ:秧mz?߸qj@r>lٲKJJjjjIMݫT* E1/|E =r"gOoOss _}a~bJp:[Z96$O)*,(*,#էlgeׇ|}u΀.tJi###FQO766={d2ݸq#++KVX^ x\śNjA _1>55!BZnn],**l./////{+FJ&''cP%e "%?Ѐ )Ҍyyqr(,*~ |h~\P) DfYVhONޣT*V&K^Ei~\BEӴZr˲IIIR-h4c6lb!nbb,<|=Ey() tyEQ,˲,7iwr䆍2 )4 c߿;ꆂV-ޔ??mٕ~ M4}BdDbP_S*BQ#ݼy5++1Bd)km6tJ) yܙJ06###fl䱴{%P(_?x5di(IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-del-adv.png000066400000000000000000000523421351617527000250100ustar00rootroot00000000000000PNG  IHDRc7sBITOtEXtSoftwaregnome-screenshot> IDATxwx-B) Ej P*^^iJQF6\T" ]i )oRvgvf~ ˖ɦ|?333oY !D*00YPYPFUt'NNNvvv ]EEEn!rBHAQJ8B8=|G.4b;z.]*/),++JR.աi"m|ΘٗzrŐ3ejS&J%DſBJ+h#q,Ѳ,r,g+S.J WT w׺ZmɌԕ.g;JFKdߺ|JW3Sll6 jkoo,0Z-F]I+YѲFm)3%ѴFZ kYLڭm#2nB^, he/H4 ܺ[x !~]m.Q~𬼢6]?MuRPLs}Oi3V܉e؏2 q|^ZyI6=);|NrFj^^Q9!HPDdLhmrF--Ͳ h2?|YXUt07-,tmٲ%_,K>Q)5h9eX҄eRt^3 DZ.\ ' r:wG{L],OWڅ{{ƄIv'@uҪCl@?/'[9AJaiU[nJ,9Ha7W{[ٿBJ{Hp,[XZĭo }^j@kYrʅtRFѤ䬒¢*R}pT05O-T*R+ȯ<}~jJ2 hZ3dϣL4)RW+huu-H~J(J$^7 hJJQ8RUth˟M+}hjfFakiRW)!֨گwWG%iR6#;_e y2!i#-MtZ9u sXg8cXҔ\.oѢmUQ|-]^VRc !>|C>>ϔGeaXaY>p2Ή3w쐨p9EibWD!D́rVVvK2 ]]ZWr>!Di#wsXѩ5č8R+56%3X JԔ2˱ M)R{95JC U 9H[z{88eRrs擔z\lfZ.w=v7 CXV8N [-dr07ds|,2] [{ڽfiiJKkZcYT*+`gg?1L ML\}]KIO/Itg6e'r(JᯁӦjdJhZt0sݴLƲZZ-Mkn^:~Sh銊 ת2SKJJZ-ԏ%zа}LyzφFoX@ߏ^_>[d_'wLhfΜ{QTB ,(!Ju3%-mB3%!4&4&3%~SdJL l)\ݗnWsj3Cʊ:QWѴgfaX3C GBmHHȤǏ]|"-+WSmE}yU4Stjƺ,Gx"O()Quʕ)S yYɊqt*_&kvf[c]\ i }oh" !UUj\BWkKHJKTŧ ЭWbBdur 辺ϪFb1co8cN?SDx2 ݰVŜo4MB=T*UFFƾ}YEQ/\8̿8.F^sT0S vi p9V6,4?`Ϟ=mKQIRͳB}' m+gGͽOP/^|Y|-ZRxMʦݹsGJ/]B.F㸉o\Ys^x"JfeJVAAfϙeQQѨQ9o߾\oB=.;;piW yfPPիE ###oٳg/]ŋwޭTD< 8L# ,;a„s{ Zٿ߾}{}v0++kԨQ~~~)ڵܹѣK}믿,]FGM)** )**"DEE' 3i˗CCC>>>tp޼y{sk!K 80==](ܴiS۴iCtNqܹ3l08÷->>~РA}:ujvvv&M:uHa۶m\7;M;vtrrZhQ ]mL_RRҡCbrqqqxxxII1 7)Spk{NY͛ǎ_oݺҥKO^+sYjUjj޽{^Λ7gϞ,4}ʕ%K|'|ݾ}O>yw &%%bzt钔IJ޽{)ڻwoEEJ #of;Mwk:hѢ;wϟ߾}ŋdRLJJY`Pzر,ݚ{o߾ݷo.rϞ=ƍ߿rrHfҕg__ߜ–-[ $00pʔ)B2@/D/...۶mkn۶-66+-JNN߿qSYY)A]u 8L'~mׄ‚‚oMig;tP^^^XXXTTTEEn}GC 7oޙ3g jj8Vx?$rWZZ] Xn^Kh iJ`` EQ5`AAA~o$CB@r:B%%%Be3ڵkꫯ8poJu4qM"E-m޼y[l9rHtt 8{aT^a𓒒vJ4M]vy󦩾WyjzJvf̘ajopg@BJvڇ~(JRiC}K"˿_|qԩ_r=˲ pܹsĉ5kڵkݺuVdq9zjVk]vݶm˲SNݽ{ݻ;v(3iJ45***<<|㸀(@y;6m:ر>99Y.2 WVV,+9sd?|ɓ'¨ 64->o.]ZZZ#\^ٳM(>v7n!_:u7p?vҭ[urԩ_|ŢE&Ohd^z7W^Be_+W]v5kօ 㫪tMo+WĬZ*::^0g@/oڴ믿. f͚յk۷>ʕ+_~yu 8௾ZW_IIIaaak׮81y䢢wfNѶmzۧOwލkӦk뢢EFF|ajj,Y"rYzoݺR8STo[ҥKfӔ'N^F1c7|Oݰa;C$CK, 0`@jjѶ occc ! 6 8pu1c3իW\ͧVh8 - Ν;;vlǏoѢEsHdLj2LPqqq,fU0iD"ӧO?4zh[[1c#Y9\.%!dРA۷o嗕Jȑ#,JX hZ|ɰa6m4eʔcǎxZazS6fnbt1fZ=a„[8PT,ΒORiΝ_^$TUUٳU4}Ka{ڄ M,JJ&XWV[PP0zґ#GN8q…r| BP4Z{L=٣Fܹs׮]kcc3cƌ~8GGG'''u IDAT;;;  2_9s׮][VV6sׯ_r(4 ( dJD2xS>S ҥ˺utrܹo)ȌT*㏇ BY`s=w٤ѣG7r{5vyrRDFGGo߾ĉ/B߾}I";Sd2 Q* &888W*;DT*Y;@b)<ʔC8L F7S+>SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @hiqQa#:@ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%X/e_?Fݷ]n4Zǜ_M{5Pc;@4z SZŏzonMF_={E^xIR-** UTznW k߹k~^SKF݃8wO|ׯ…B3;x~6fȝ;wB7w ۯ_ZZZojS(c_ӛ̵_u(*((ԩej4rGv9dG*++w}{fy~tFF%Kf̜7l֭-[ suuի&,҉7VX?ӊ ˖-}v>}3vݕMu4''5X`9sť$rP?kޯ_Yg ԔCΙ;v7)o?^d-[6:I H&N$|X?5!!A*w_!ֳ4sBg2S2m[9ٞn9ec"{;vСCJ0_$IaA~bRҋ/tUm B=rJ%0Z zqh4GݵkKbbb6h5mHkV  HO+׭&#=FHtg^/?_R ggҠ^}LJtvv.)))+.~Z,>>|:|&Np9g3Ї@ewfW_ذa}⍄#Glop⒒7x/8qƍ_O4IQvvv=?ϟ#:~mۨQ#o$lذ4U +ۆ"0J5~P"k%,M( 7%EQE-5vwMܣGBڵkt°a˖/ۧϸq/U;5)&vI|FaC|~ر{~}!!!sQt͚Gx6Y06vhnnnnnnlPjFM4w>s 9=?;vY榚js-*9'%˗B~8YpnʩCð˅qGd ;~?JOOgF"dg)Ja<[y [y*8;;GtʼF_?J~2_ ݻu34^ BJطoŋ )WT|Ɍ3-ZyNF:qBMRIdu+8.)FQaAQa& !K/޼+V<2I'oX~e>^B(rpp(je5Zx E߰!**Ro.Z\RRo\&?O># <2E͚6`@BHvNٳ⋏?W^87jvQkC|ޤO׬yw#:j.Ou>޽{vUz4 5LـpB  SA `+[ЀX{}x3~ 'V ޘzLJܿF{{<%%%"~嗎:l_өN֭Cƪ\6>3X8OhhSjDȔOz7W|ʐԔ+VzwV\qꕌ;5QQK =!>񉎊ڷz_ $gffKիkaiii}kp̙3Ç +,*"t-55-[;4<`}&%pqq鎓.nt6~I7ӃCLz{7OٳO?O<@t7v3kcʋ`jw2:|LJtvv.))؉,xscǎE5vءCZnZ{g޽P(hmP0{v۵O֮2*2~}ZS>m2llľa^|׮rTn;wРAi4#GڵK111UAZ$|ŏt3h|(  2s!P#`4;,^q+WDbXplt~s//Zoݾ۲aؙ3fرs]tY]ǔ'v8BD"1eޭ333[)lGW_8a‘G̞mTJjժHt`r`c̅ԣ8 z) Osj4>>>>00dt޽׮(y~) ^v}o^}BȫeΝ3pvւa׌)DƎ%TfÆ7F#:?oNG ~ 64HPЮ]˖ ]|y>}ƍ{ɜ=zܾ}8ݗ&Nqc||ד&Mc}zo6j 6_ KlrSx>ߒgNВ{!6hPȔ&{6}0XΝ;WO׬)//8|Ĉgc W,_vkݻɤ^KydRiNf}NOvMSF~~ٳc?vGDZS~~vfʕǎ~?2p7H9SRCSR͝#R(غ廼=zv(//tBͤ9sWħ F˫})6vhnnnnnnlPWΝr{~=vX{{{%1[s@?k-QF_O МǎgYvĉlL80M8MR4ܧ0TʧIa,dJK2&Nuv+2LiIk}8~!W STk4o#ɾ}?jϜ9s/=''3m[Q?qݻ2~ |cl˗/̼l3j6<)M5+uB6իWnQŸ~v;?lӦl͗_|n^'6hr3" GH9%d:i Iq/4:;o>Zh)WAϟ\sC>ؿRSF>~k׮}ͷ#Gl@3LYZ z;lg+/ajzFgcZ{?3Ν¼k|Ү}CZ:8mXXGYbW^֮ÇMcEm|뗖kf?MyT*n@sLYzެY"2 Sg͚ݿ3w2;k,&ES7VX1id-MU_ Kki~| #25%yСs՛ďϞ,WF_d=G3eayw7n$h(jI+W,ʮђj#Toة󍄿,]fh>m|oLJtvv.)))+.!#';KT2 KxʫuaA9]PӐGZjsiiixDGa^~h뗔x;maƍ,O~|׿ۡCϘѧw^xV pV֭}\"1riR$d2Ǚ_A|֬2W>4I駟BoqL&@Wsw뾢(J(;Vq¼QQzg_3駟iRbllw"M@CC4.Zܹs>m|V8y򔟟oYre}5ը5!Yl$Hy{sҥ#Š bblwƄ[5'zK>h)$EZW1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%@!,hpbQL&m˄Šb-{KMXVlx4Mgge%%PTnHRg'v( b6Rdv+j8aL2LA~իW,,KiNz3gdr?!"akBz lYVѬ gJ-$\>`Р`˶IJ EHh;\qܹVZ Evv.泞Y~U~~nuhWRiebe{YlAh&GS,+ɸ&?FFH$5Q.3 q,hXta) }lG8KSX7dS\\\\\lV>lSpt,q \.j8-QKG111cǎl%4 Y }lrs(*,04418:L1yNk<$&&v gM-k׮"Z{k[Q4bxh7cנGy]}-*,5ZJӥK[nݺuK.111هZ|W޽պ˖-;|H.za)U*UpHۢV UTۧgȋ/S=V^֮=(joXqy999u%99Y(_FEEEGGS*r\~~~~TT۹Ƣ#qmܸQ&d77Fd^aŋy{{۷/ܱcGmmm7o޻woȐ!۷??Bi&˅mF|vݪzaccӣG+W~v,\_~҄BB5by --o~m|.\d8pLǯ\._d޽{-ZѺuYb 7hwЁa\VrÏ#ɥpt˔h\]]mݺe˖a͚=fF<]IDATK,1s0/o$\boX[[ۼ#FY,oڴi...0`iӄ޽{4-̌_z%sz-AR[oeoo_e4_{|ɔ)S~ .ӧO饗?Ӊ' ߾}ҥKEiZ7h%11O>{'&& oԔCΙ;/vF6B?uMμ9sť$}U L/!(>zVMKK[vkfS5(ztk.|} &>LmĦ9FI!?L,NN8mTa7c/]v㸧v۹cGxx_?˒H ysJ%0Z ڹc̷Y(Rݻw]\\T*U@@?Ν; ƍW1"ok>baX+2 cggGQ!w=w\Ϟ=G9h ~ D"k* JբE 6ŕԁO[[[mUUUHLBGZjsiiixDǬ̻3+ۅgx$ %g~j_V [fx`=~ugkkkj+TX!!gϞ6m%I.]GF7 G.7wJo߾ŋ=pA;B8KJWYTB*MZ~)JѨ)JCh|pD" ut9sfw500 %7o:u*˲_}0,2%9_,]$==ѣ~7ɷ[n7B! ^KDD9paFvap6apFn!zX$ͩ XݚIztww߹s}HHHPP _ndS񉆣Ô}rĉ7=i$d -.))~7E浳s箉F.|[Z˵Qz{G}5B'eddhv9u4rԩvܩNyLͥG7ڽ{vqqqppԩ0Ǒэzѡ}ۘƛOU~]7GF([RFz}l|f኿7^e{+]T9i>};tK]\KK$pt}ʆrmr:e߾ 3;$b>Kz҃R&}۷)0*2%bm63-]FS ϼK }lfiMn/SG)V)9֠=ja1_uۥAYp J7&XE$ ?CbXjaҰ'S,)RiyyLt~+]1 + n3,!Кޱ|gL)xx4_3 H ˣfX.fΔ2cNgHwOTbX r]إkW¢׌Y!+]g=[ G::,)2i;^vi!ɜ]\":Frg5qVjz lYVѬ gJD"Qh 5ߣK*mvĢamX^b&j8aBdr\aVXf6 8\SL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) S[lGQ4ESZ мIRBT*EX,SVVUuR~~0j4[2̳gϞ:wfmɔ4Eegg={gxH$i4g,޽q)7?_?VLPԟѵ[74ʫ?O>T=AArR bϾ2 `q2LqdJ7wڶ c>^:,*,ps(*,ЫWĈ=ѣWL"??cZ{y%Ά ;vi'),(B=)fQaYkaA>Y)}YS^^q'ɫ7),k7[|'<O3dJ1˔2eMl&@`F&TLH^^,vAzZWko7dJMt>'OM:h s hLUjDa mg9zy,d2V^qGWVTfL)HX (3 eYD$Ti4]k91Ȕb) SAL @ 2%dJ1Ȕb) SAL @ 2%dJ1Ȕb) SAL 4qwfJKa, zbyJA,§ !w9.&vd5ao*ދBz9{w)TЙ{1 u,a~‹?3/MN @:%o?1y(˔7S~PTd)߸N)SAN z^"J' MS4-mCu8n짟.ʟOQT|\&)#+K]y-tJPx޽ر#sz(8{}4CgddR d+tJPywp``gYٺu%Z톍hF{HII6,X d+5˙t6=cjq~Pԅy^!0 wy-SA: , z,LUA)R.Saݮt"J(,,4 999W\Q:PBj˳ZJ'vɋ/NMMݾ}OB H7oΊAa6o<44$$ܹs'77۝5<<$]ϷfV}eiaa233ș3gW^mȶmt:0bd||`0L~@LUO)rƍ\l٤^ݻ۷oOMM~dQ%%%~8cYիÇ:<33qp:͵>AZUUUk׮ICF!$X,gHd;esss___r'FZ[[?ݸ1~Xx`,>/>S^AVuk!bby?88=8HO?Tܖ 9}vB>ȱ?]pȾk~mwU ?_]Cm#TG}WoB*/\h5LmQVA$!a߸|}.+BKTNš+U);}}.+: (psCAS,}N Bu(jvvU4i~( Z&h|l"99YT"grr"!aF @)僤w)JtF³6^Tn (H )@E(JK[_P0iJBtј_/^ :%Fak֬ o JC1QZ>( tJPY,#(<Mvxr)SAN gDT@#<NkoQ7r\ɔ-%ht4Mk4+jv9 ANjqBȋB3N( * cƌF%^g0-s JQIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-event-adv-list.png000066400000000000000000000503021351617527000263300ustar00rootroot00000000000000PNG  IHDRtKesBITOtEXtSoftwaregnome-screenshot> IDATxy|T?3K2Y[@4B #@&UYԶR*myhb$A *6>ʢJ aӈт$! Yf#a0Yfə՗əs9|{T* @'7o<!l6OOYk E{>M]H:JSSӨQ!jBHMtE=!D !DEBxO+jd_|Xw!((HVk4Zml6{dnq[:߳Cm6Ssz׻t'kl6yMRwj5k2y! `0?ՄNyAD̋&C~rl0񲫨7j]o❜ M KCzv<2, @V}3f3PML;k׮475?ՄA9ADb2fF뭎P,<ɿjW`.Q$E__zbx059I஭]wt6ӯFPu]CCCCBBT*ȸqBhjn !/yQDsfz'~%bQxlliYw-) 8]SuE &D:5n'|AvhB9/Zsךߔ_~B04V 'Z+x,hnnҥmh qFqpCH'(g6[y&bm?'H-7[VQ$ ?U7QNR)`Є3OyBx>((HY$3wv$]nSQ[ѫ--ϟ8&s+}LF3s&ɢj1E}`O#_\!(gpXB!G_i"u#دȟl8RYicF fyM3[btSݪAEur58YS]iU{twzwDrѴK7 vTDQ9V4hlnl4pxhhyfCC?VyAn3@1 QɗUA' soOl;mjj=p}(3`0uVwyl24eEPzn姖 :? ?੉lQ@ he44 s`449XϙEQ4x#`ɜh' 6nͦg e==zwy" mN:~*E^`xBgߔ1yo]H 56NA]=CbhK.7ByҩJ{N'?\AhOz2[Õ.-*ߟ>Yߓ≙#/5hk@OQ _&l8( R2v@`` C J|8׹(-)r!~(XBDA(S䇁a=sMVoo>XZvi߱n66' \?9YQJd"Wϐg&Y6k7Z!.]}ǁ9?sHd}zc#76oՇOKA$( AJ6pxҔҟgNw3+ rWܙ%NvZP.]juKc+/ ןkׂuD,o+!UݨIԡfhh&CP(*?jDnϨ:sG W*ks_?{>ޭݻ;Ym Ylhݺt 9pˉ1=#-M Ɩ&SKۋ*`&P$ϙjuppZo p-~~~fdh}7 DT4`X=-p۝NW޺.u*y m7n[[kA[njNn3 naEzږ?EAT(4_oh6|Cyd2F#!5 h={ܼySjU?~9"gh/7y7e+!d2{ ;٭hHwNnEk hT4`ǝ+Zpƛr[!KHW`˩cƛ m@pO t-_xƍ_~y~oܸqʕ{ghu}}KH?0m߁>cBҵKHWQo ~]=".Bv^7=/Ic޽'1c֭[>'m3{=C{lWE$Ҿ[QGFG#~CPe]|^R?ee?)&LpV@f6 !zVTTر:ъd}Mr>CQ;xdEoO}V(JZEQzҥ>dŊswf3:=k۹kּCY l˗555/_|;[O```eee !Eo9kcI*ZH]BINN~%r/fqd}wuK/eKVy~P---׮^c\0`V@{ؽF+?j׊h8\N ؁@Ev;+ڶt<7q#烲Oj Mpd~?ܥTJe{z<1nDHpOi/ʮSyA~<4x)@/FBTJ嘑 6s'rɾ=`J5qI>*g:֧E^|EjՖ/~xW̩O>yw>)u?~iiibJKK۾}!dϞ=}:N<yw)5kָ]vѣ'(ʫW 8uKB Ҫo={;\:"0â9|v񍍍.g┓:u|A x688u߿1cZ<^ [Je0t0 :jI^8jI]4:h_V49C oo 84oVz$OWFt1ZCQwsɰ Gy\BSB|4CVfpaEs],Ύ} &6qȝY>ڠdy˦u6aX;ǹ΀lf$^;Z~~~dddRRRyy[)))ᩩ.\/^|'j-;t:xzc&Lph4ɓ'X5nܸg&$$dggKG.++vvҥ)StQ:f͚tyi4痔XURR2|N?gsƌ>lbbK `NpOBm]reeec47כLw}7..ꭸ81ckךf˞&MΞ5kt : T?Jsљhڻweˬ?p>b رcVwRSS333gΜYUU垙@p,gt߲2`]vY-ү_;w>}@³6:=\Xpt,oh:qg7hT4`* |4$^'RI裏rsskjj \{͝˖-۰a-gVN*+WFQY䣹& =EMՊطo_VK,__>66o߾RgV;jԨ"b}ڵٳg0 !!ĉ9t FRf-knYYپ}.^(u*((Xd mΞ:uٳg.]i@r:ݖm/L_߿W^y^ʰ=~xAAecMuO͝;711Qgy&66[nRm/H_ssÆ ..RHvxR=@Ev;PHv N*7;HvR)H*78F.//OJJqIbÝ裏+sGEs*/J_̛7O̟?=y$2|k3lGF~zzj=Fx9#,_|QQQ?#Lb01O?^f{&Tn`or)\yR=@Ev;P.hH7C*7TnV|;r^^OIIٱc!Dծ^zذa111w~7G;_vm HHH8qmR൘JtRzzzff)Shd:y+2228;qīGΞ:uٳg.])R൘J1cƳ>(,\0(((99d2-\088899[\\hѢ>Sϟwr)ZLr\RjT*k^xԩj3+,^˵ͩܓ&MΞ5kօ ?a„K.=skܩ3gt$^kjjtӦM8q.`*ӧONnw}ݳg 6[ Tn`w hT4`* ؁Tn`Rr\o~x?r; `'iڕ)))ᩩRPyyyRRRddd~~d_SY Jv5nܸg&$$dggƜG}/c*qvII5?sq޼yQeyǹ4mBh#ݧOv:S܎ŭ_d2cƌGaaa{inn~wl׃,oZ*ZZZt.fUܓ8k֬K.Ylʕ~~~=.\#H)ŋ-8pSO=EBRRR"|nЁ\u!5kָ!(-//hժU۶m+(( V|PT(ݧO\ͅ:iQ[t{r/^R~_留k׮5͎ =zک@q/766'6::zԩ/믿N;ayglwuǂ^׼+Js̙3gUc~viby@z-駟Va [r@{xWuhO*7wU4r@{xY'@{;PhT4`wldyBEYގWpċX8d#7O=;g`  ,o^c:VFNծ_>66o߾FV2jԨVMvWXͪ2%%%<<<55… pIIIwd,T" IDATK.gffN2T]]-h;!l߾}4zWvt%KjJjVYYYƍ;{lBBeF)--aaacǎ-[((!!_~m쮰ASWW7h rh"~WK,QR.nݺUV-X6*˛H?Yܕ+WVVVʾk;=lNvܲ3gX. ]йzE`Ig͚EIJ!;h 2RS3fiA1..W4fyfffΜ9jܹ?-!;zNN3<ۭ[7S?p>b iAwe˖99yQB6WAOM{Z/,\mGv#nBB{رc hϹgy۞HzmBE,o/{: g* ؁@Evp@Pѐ!S` gHvw;t.,T4+RLm@zEC*]ѱ܎'oH嶷+:*ۙ;rC*թw<@zEC*ĝܲo wrN{R].xRYv8ܭTn/^,T4rK N  ؁@Ev;X׉TnXh1%>)pF* kw*c1 G=mSZvÆ ٽ{oo>}q9iRU5 m5%r{.mq|y0d2fddpwĉW_}5//ϪZ\0nk{ޖlsmew=aVR=-^4Z\ˆq[.;UY9^nqWC*<rK+]Y\1 S]n n̓NݰadZ~>aܖ!ײSe:ewI_hMvƲe>㨨z???>aܖ!ײSe:ewe0Mh3rwfwxTn3@SuZVP 8;qQpN90튧m Lb! sVw%hT4`* ½NrBEC*7P,u2,!=;  <|y0;???222))\jڮLII OMMRzl&; rmvYYYӟE7=|y0[єN<9''Gj7nٳgMve/Sۊ -[u#G|g7o!m7!#RAIYO]]vnӐ.F[+y'Lp5iV/^*7 :#//hժU۶m+(( LӧO2k[d!]dWXSSSWWGijjݻwk =pTn[\\l8Ϙ1cMve/Sܜze˖=FѲe7gzE`*hڻwed;8p`^bNCvdj;ޮzj=ؔ)Sze7g!3-@{Kfr;SI,|;c*!BEC*wyڀN  ؁@Ev;X׉TnXhHX}QnnnMM亂 Lbkٲe6l@Έʕ+F,+Rm'&b߾}e`5OJקر;e|!ۊUʶItRzzzff)So5!Vss<y;?tNe/׏֭[jժ Vrhڜm5 my\=h?_hHvLv&M=k,sgzEC*cs9s̪*g H < Hf srR1,T4r7 X8PhT4`*{HTTnY'R;RbAB*7t^^ѐm;1"jJc6xW4r[J嶚XTnr[dǒmp){,lʢUVm۶2O>86r[MCvd[5%ٱ\gHnٱd<+R[Ev,F@*gt҇oR}!/`sT6C*70T6 g* ؁@Evp@Pѐ  gH,1]_p*xC{zEC*#GL6###z=!$>>$ZQH <+RĔ PXXh2 jkkH;TWWWUU,YrA8o@*7R-M0ٳIIIN:u*}(!!_~cI`{:Tn_?F[n]DDĪU,X@JQU9j%hKmܸQ o>rHVfffnnnYYٙ3g=|!ۖN{ !sٴiStt4}h4F@zGzEC*-NT*!iiiJR:Fygbccufܹsqg<ܞo,|;csT6C*70T6 g* ؁@Evp@Pѐqڃ  B6::I7"=^zYOڈ y:?933ETnqΝ;'wIl@b[;uRܹsɒ%(jW4Or;ȶ91Ώ+!99y>|x۶mjCCZTnrˎ+hhhZd;9uRA={.Y~zz^ '+uYQQQ׮]566⚚w;V4; ^Θʭj ~ۋ~pVyL8qsh4?x`` Q:\VgLRmvد~ uT*5M``x[ q555ӦM͛+Ġ B _ JuuO`~,rӱh6cm^YYzBȑ#GI^xQhoi8ǃ2 BѣG=t,HXmv'++kܸqgϞMHH&Ĕ PXXh2 jkk(;AuΚ5kҥrrʕ+i N1.\hyӖ={,ZX{IIڵk5W^ݪq-߿W^y%%%֭s5j!$$$$,,l˖-/޺ukll3Wc4Mnmt7 qG颱<+Wr^Nܶe936t?==2gΜM6EGG;x_h^=wD:lܶe80xÇX6t:RFIKKS*1ho' BȦsU7~<}t_2hm^X++߼CЫ3}N ջbǎ˗/'lTn:c*7 s&SݿNP|0 @ g* ؁@Evp@Pѐ  gHqMXhЁpP W4Rׯ_۷o_bKZmj-{^zذa111w~7`<+ceees?i!Vk2hOyĉW_}5//xަ⪥yBkkN%ssmU\\,5FDDYv 3 (rjicddd-RAEQ 7q-%$$rVHmll]ZvCpZ!_}Ւ%K1cܖ?(P(% G)T{6E|1Mٳwqܳm۲Y^јLrssu:.ۖ-Tn|·#1>Hu0TnX8PhT4`*{HTn/۰p։TnXhW4ReE";[FVT^^܎ ^XJl%kk4ɓ'8 RHvN6I0h #xTnuG^^^eeeQQѪUmVPP@ZݧORffo!Cm&x!u-Ndz-mB\\U|-Ndzm4ݻl2[m P.x?rC_NW XhHN  ؁@Ev;X׉TnXhHNrs8}3Xh68~zEc&ȑ#ӦM#H^O?,W`W4fRcbbJKKA(,,4LMMM]a wc֬YK.W\I?^PPgs…G?>m)..޳gϢEEmIIڵk5W^ݪq-߿W^y^6SJHHHXXXYYٖ-[/^uÇe[6N׏֭[jժ Vrӣ/\KeeegΜFtLmܸQ o>r6 3R*N{ !sٴiStt9ZN+K:NTBҔJt&6'W Љ :r=_NPѐ  g* ؁@EvpN}IJet1:M <7_~VQfMlOE۶3ҟrR g Ȗ\/'ցm BEظ7=ͩS_zz.G^`Ӗ(ۆW>ĉPYQzƛAAQ##EB>?~TFE|3?|TbO1ZJv58c{x#;U^rン0F?H}|FOH JO''`]T]{# !Ǐ-?~C !?4nBhކNӊvDI޽'MR*>S_TRN9}5o[5n^u7DQ,/x<"rh *-ET'`SM?7_߼ٰgД_>~ɯO~yfq+CFX BȰ#ju~i˗[69tfCmva*jШV|fCs#|Y}}]=yFєJ圧~kl{e0Ϙct7/WW|٘"TnJEn 遁m={*(CW/<GOGyF!aa>_zJ/ƙsByhݨ?V]w9CosOط_2LM%Ǐ޽:{A֎ګg" &DD.;wGPUjuScc ;9F´x8qչ=={ؿK[gF~/nk1_~~xǏ74BAql;tJHHۼy3mxO<ѿCzkСwߞ=?]vٳ p X^^oo#GL6###z=!$>>VjF***Z,){,fzd)));vp~x_h/~ O:EF]VVfydd6OղlڮGv{ !.]JOO̜2e{۰pg=f͚t?99yʕVpBF#̟?ٳgѢE"3dڵfW4$$$,,l˖-/޺ukllJ+TTT<ߞ(Zn/!dƌ/bbbb~nݺUV-X6zARH[DQ羙<`f*7bBEv;Phu"(*Rb@P(_hH6q ܖ;NÐʍTVmR! ` mF,_hHvLv,rO{·#1>Hvv8O EXhHn-/@`BEv;P.hrl^ <8~xA6kHKK۾}Em su۷9|mhVǷukSaքg"3rǏȘ0aBssF&6ajtCYAAAe֬YK.W\)Ϙ1_LLLZ͛hϟ\7p?:tw$}/}SXNjcǎ:th˩A*7hT4`* ؁Tn`R-(/O/N*UXsvʍo &FRA&hZvÆ ٽ{oo>ڵkg0`@BB‰'֯_۷o_:#d2|ƍرcɒ%N]]ݻ>u$T4vbijjڹs͛466FZյkטA#!СCQQQ<0`su$]C^cEDDlذAzKV;wJ-˗/ ۱cmx5Z-/_NNN 6lرcǤ iT8 |ȿ|0**jر;v^c=Ǎwwo۶y愄A:#ONH6F /`XQQz?ecǎmhh0ʹ瞛={7xyz@Eq9sB̙i&}…/ѣG7BrssAEr]j׮]Ç JJJ?ueʔ).\-?/)**;wn.]~_f69!rʁ !J{!LF`2 !Fm4 qq{uȈһn2b$%%Yϛ7oڵL&w#mB拢H_( seEQdztgwW{8F/!bDQlٚ~fO?u6z``x>`tFz[a;E}d:F188h4+1ˉ=472['Nͭ;wѪ\Pb-[5{,ϙ3gl/1Zsο/ҧH;vl݃;jzW,xOxbC˖-5k!CuNV7xý{ҕ^͛;$9ɓmmVNn ~ϱcGB=Qg^nn^n`X_WK_=𹎚[+t6#-b!׿z׿ziZm~}/rٞ=zl?Vh۠WD"y!ۣGOJ۫ 憹̪];G7ڼN^uvF-if.!mWuzT4j(z΋a8ޭ׆*x/RؤRyvR(w~zM޽==kzP,'R)U#GF~1zߥT*<=#z͵_t!@E/T)'lȑ_ugrJֽ#zvq*x)Bj;RTʾ T4j*_!M`* ؁@Ev;C_f@,+6Wyn>sU7>'E 6_~xg_u{FR)_S[ M-M z˄BȹNk4R騢ܶIj˿{5Rq= @V+w1?nc)IIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-events-sched.png000066400000000000000000000461641351617527000260710ustar00rootroot00000000000000PNG  IHDRtKesBITOtEXtSoftwaregnome-screenshot> IDATxyxU?KY;@ "Ab B$#@й^1wF$lqA _^6a ư͉0FG ,iZ~)Ng%߯gSuNg߭ (J͟?_A|l6OOY?ݻw tQFBԄ sQ$Q !?U 7Γ_~ec]MCCCPPZh4jڪly 7t;ԎUDrЮmڔ.(JBQ[WKTB̢HDQ  /(_! Q*JS9?3'bK햖JF# (B-hTF&xE"x`&dxNEQ %!rAT)U'.Vy7Z,Kl_ughߊަԁLMnݺG!l6fiESTUZMv|`0 O5!D^Q4ֽSl0񲛸YowXW}Alj{,^n~RM9hh93?>tuƍ&""'HLfl4TjR'>[sCmڽ9{Q$ܰZAѿz$59I;ϯvmg\55fX} Pp ֭[ &phEAMϙ5&N*K$Ģx-)+9wE &D{>f;4!O. m,U?KS(ƖjZW ϶~. #uv9/] 5q_TI=7zX/նѤ ?BD1(ORf ١ !͓O]em10ܥ[fpv/o޾jꛥ[~*i߼e..uą+u꿽\%?c~w\j,?w35FT*A_q^G^t?ٛsfZ֭@˵Z4ϙn/B_\V4/Mxl2HgdVZE&!DP* AM:[K  ĢQ&Q9۵z>i Q$ou& a54}DP|QWФV~_řWo &лVE֗o 6!Ӧ.W\r>_ מ)QB!g$MFdy^P(JRyJ&CsCCCppR4*%E8(hJ~@RO4M- p(ϙEz9)sMFWx"g6JSDZL[uaRM\l6 '˞7vS˹ 2^~/# yכ.JѿWjOGM3_nl$pz[ ' sO!te?7 xO.]S*O~EbkAZTj?;Uſ"O̜8w%A? _3u`zlF3P*jc;h0NL?eŎB$w/^B(RA~.yKBZ|wѲE؜8)G pfɯ/΢("O7"K{zd?7ɲoj!TT߸zNwO}Gpω"O5CFt ol6~摒>HQ* *%F;dDyҟ?+=~P(w{Z:c\s_ܸԭ[7ZX/I~/}7zD,o+!J.VݪW&CcFęM`P(Uj艺:wQuW.{pHRyrm}rsڛ޳gO_X}Ylh=t 9rȪU9-M Ɩ&SKۋ*`&P$ϙjuppZo p-~~~fdh}5 DT4`X-pםNWd]3r{z)wtkE/%=3yǣ;άhGi[L/-P(i0Zlև d2FBϙj0Pрm{}]U]yLfw_z;U3755-M qGxbܡG--<zWE;F_qxWU-Z藊HڮO~V ϸvXջO_7y5b2V4;@cNos4`*-IJ\6rvl{\w]EeQoݺ5`ࠚgMMAnݺvړOw|n!7CO[!Bw .{٫wd>@p9ZHrC}ݓOW23 !O>={}3gܹX?fyqҬԆ=z6!.Zkѣu?o[1gs{HHoMKK_EqdTԞ={1?҇B޸~- }Jfe>ůbcSSS&L஽l6BW[[[QQo>G+&Iv2CCG(P$:wZT*(Rb򊊊?_:&@zSWW󟟷~BȂդĕ+WZyU7lAB2_ye>=kS.$h/-߸~kf͛/g22{̖Vh?hc---7_{衇?]6x֮EE9\^]>Gt9hT4`* !_vm?7y'?.nY~9)9r ~؁媳s|^t:R&N ؁paEs],{}c`„ mMB7tkqmkPGGeӺo.ncWsY\{g@63E\~ӝE-???"""))V/UVV㏴ʕ+SL4hNSRRbgddL0?h&OcRVVָqΟ?-5=򳳫WΙ3'33sԩ.-t:wTcǎ_ѣAAA.h&==twx錌 ???˞3g|]:Upu{~} mcnn5k*++? מm޼d2{qqqV/I36oܸl6[4iRvvٳL47>lʕV/9rdСǏ_zxԩ0;fͪrS [w3e#!d~*Tk׮5գ7GsM@{NEq:Y.l޼966vRgN7jԨbb}ƍzj gΜq1M/6EEEW\:WUU,[sgggO6˗/tѴs{nK^ ˇZbEEERӧ ,KJJ|._=@*7÷R.ۼ|Gv;PhV4r!N*Nf>TnvR\~EMק۷֭[7|·~;222**vqSO=5x3gVT*իW̙9uTb2իWgddpw̙_=//=mڴ/_<33Sڎk1=s^x!11QjYpaPPPrrdZpappprrrUU}dC yg.]Z൘J]fMeeB3T*e^xj3+ά^˵ͩܓ&MΞ={?L &_z_tXKNMM̜5k3oFSSSLL'NK1=cƌ3fe{e{q JyR#@Ev;PHv N*w9F7C*7vR[MӮLII MMM._o)eJ6S܎ӴƍwlژO9x{YqLr;N.--MOOh4ߥF'G o3ƶҴ hw;<_dLr;yf{36=Zjzjڃ677oذv;Zr;ȑC?~|մqժUfYꙛ#駟Zva,.HIIIII .ڎl7xr; r@G  ؁@Ev; @*7r; `*;???"""))˴6ĉӧO'^Oٍ:nͱ p.@1ʭh&OC[lAػwdڻwoSSSmm!C򢢢+WtA^R|혘RZZi&8j(BHHHV-//߱cǒ%Kv;bBA)))9xŋ !zׂ]:y8yU-uVA޽{ȑ%]]]]]]myFr%0-k$>`Μ9sn۶-**m.Tng\&q(ʴ4:CR)!qKc'-%kSIgs…?R[RRx!C<3'T!R̷ UgRQX~*++׮]k׮roHJ%"maw \x˗'qɒ%*g}:D7nh6+=zh)۵SN_ nlltOmTTԴi^z7|6:u*,,΀-0UVFFFZK3oܹsΝk8pXX"eø>L¾;yHБTnhHN@Ev;Ph>ou!pFozw *ͳYz>%%e߾}t N'h;Nۼysll.mdԨQmUeeeJJJhhhjj?( tVw|y0իs̜:u*Zv8BHyyyQQޕn`ٲemUVVָqΟ?`Fh&Oxa[I| !bvMNN*)),iZ "q:6FDDh%pF*[w*c1 g=S:nݺuÇ.,,|###tŋFkbmrm/jJ\lۀoEI 7kzE`*d8̙3z^^U7{ &6aV!"-b|.rw¸p9r{$[ڠ ni2 V ]vrM{V TnyL6(VY\ݎn^rqTn'nٲd2m޼^0nːk٩ڲ\Ev$xW4Ϧr;cʕ#mr-;U[nSpQ evC*w`6駞 Kf;4; ^ʭ Ő!C} e7s ܮx$*R;gA~gX@Ev;P,D*7P,T4rU'2ҳ3P#p ^+S#""._,5ZfmWVVJ)=s~9Զd{W-]Xx<+S5MYYɓsrrFˬqƝ?>!!!;;d!_2npʕ;wY۲l&; m%7oBp\uz,['!..N3f{s~nu322֭[r^xh4Z,_hL6}ʕ+e;9rdСǏ_zNCvdj;ޯgyfO>ԩS(,oB*g[9MvСrXxwTn/9AC670Tv W* ؁@Evp@Pѐݹ>ܛ7o:9.$:ݹV\e)Xhй]6j(O=|!vb(0n_vVz}JJʾ}Z?. VRe`5OBիW̙9uT{ rHF*jZvر}mt.{ܾ~iӦk.X6)Hg+Tnilc377w͚5&@zEC*cs5iҤٳg#<+RYfUUU9eW@*gT@*7 ߎXx#ݐ a!ݼynU'@Ev;P,D*7P,T4rwrx:ݹ ]  :RTnۉVSK#|!ۊU*Քdǒmr#jn˖SKrp#*++׮]k׮Tw|ڝm5 mlӔdrqh\u" dǒm_hHnٱd<ܞEHO | srR1,T4r7 XPhT4`*{HTnXD*gI:,Pp +R8qb^ !.]&FU\\,XgjA^ѐm%::L{L{6552v*((Xl劶qTnr[0a Ξ=;m4RqqqBB-ǒvt.{ܾ~iӦk.X6)гkw*4dKVLL֭[A8n#G/eff斗q^ѐm+&&>3g!dܹ۶m/Ơ ((}^ѐm+&&FTBҔJtsvy%&&xR=tRYv8Tn`  sh:(T4`* ؁`^'Rb!#kXd&߈ *3pA^<-;4ŋX.\Lv[Ǖ;OTn6b;b?&qewDv83<OW`98vr\ɽp5rT*٭Ρ1ٲW4Or;Ȏݲedڼy98vr\|8GzET*+W߿?22vd;9r>s#f￿O?s.RYcESu芩:NP 2wX!.>S[]O"q|  ^5.` * ؁@Evp@Pѐ  WCz[&Mvf\T"_hޓMǢ۴Uܶm+R{eeeJJJhhhjj?H9q !z>""BB/]$;p*^Ѽ'Ecimdee7 ل貲2Ak2T[[;dٱ$ X3g^|?fx邂` j4<==ʍ N  ؁@Ev;X׉TnXhHNrw&ti,T4D8.+cܛ7o0`svj=׭[7|·~;222**xW4RˋOZ툃͚L&ړD9s_sj>}M}MK\b&''geeHaaauuuݴZ`E8NG#""wH(kZ{ײqqqBB-7`F٭I˲;jOw=h"~˖-SR.X*wppP(%G)VGvE|1Miڃ677oذqn>gzEc2Ύi5}۲YH,|;c}TN&XhHN  ؁@Ev;X׉TnXhH9 WH@zEc)[v_$mtIII/_v@x_h,rDv5MYYɓsrr8lr;ަleZ F{,숼kڵ15̷z3Æ 6egۑ]Bd'[vgk/Y[څ8zEc)[vgk/Yh4FFF~g+Wt` C*b Xx#SM  @p @;PhT4` :   @pՉTo`  _h̤r8qb^ !.]ݚlkL*wtttYY {5L{mjj2d$}t!,ٳg/_NNN^f m<}tAAeυ j4<==''''''00ufpi/[uSM&ӟLN'i-_BEcb>}z}}=TnNQ>L9~_u(r[Nqqz}JJ ?>+GR[ ம*((qaaaNw(^rEc/n,[B^:gΜ̩S8x tٳ/_'''Y6>}ϲ… 5?N[JJJgϞ_~yϞ=VE2v;JMM̜5kVUUUk1v 566Bjjjf'cMMM xpTnz*7SsFyEEE:=;C/хSy RiNKKkii;wn`` kqN0@BEc>8J5 {=ztg PјG~SN?{-XGJRZ,n޼If+VPՉAAA y`UWWO2塇>|_ n۶MŐݻ4k ۠ ={v||E444,ZoꫯL&gEPѼB4iRFF?]PP}ɖOO9éJ2///%%eȐ!={$L:eee4$,T4S_z%ZN7{Go^\\#`hz~ʔ)]=*HPh4^z`͊&?…K+zzy*MYgˎn9al˗/v0v*w}WTTD?/gbTn)MgˎnF)++lذx(]7777999++Dj jiqKDQ!5׋X__jy„ ϟOLL,((HJJ:{iK ܬV;vG}$mAvteV+M)<<ޔӄ!jz=zȘ9s۷.]]" Ζ]P{ XHE#DFFر#((… j'pf.MYgˎj<ܼau)9sX(v*!$22rϞ=!!!A]"[b-;znnnvvvLL>lʕ.JAVRuօs'9^kW^Zw?ݻ0A}-} !O~?n4; iաrXx3A8ABEc>ԝqhT4`* ½NSMXh rbS c^*$ r@P(_hH>:yTnKc0RGpS G,_hHn>:ӹ{,Tn #Xx#1<>%a!pJN  ؁@Ev;vu{&L8z_}z#--mN׶}\0̵޽;--Ok@G?E \~mEM6_ &8Q ];?~|FFƄ ]7l6 &NGT@u;vlG r(g^|?f}̙/rbb2|Fرcm>&ڊrFk0kҁjrP(6o޼i&nӧOI[;tЊ+***xW( r*w|||>}>Ç!5+33377~EAO|w6nܸh"7nh6Ơ h̠)x[BHjkk+**۷j*Tn` R4.w |4`* ؁@Ev;8u{#mhcIpd:V] 9 ؁@EvxW9'鉸BR)U*GJP{+WʾWm-7ߕJe=>|~~~}p@Z^J7n=Մ BT*Oȵfs%%*jРP8*x)}MĉC{z.q*'O?0(ت3kx+QU{kР[ 8l q{ RDBZyAWq@Zs4`*WDQ,Zëc9\8GrZ]\\x_רaÆәPlݺuĈ[nD ofAAرc¶l"VjuhhUVG۷6VTTh"Ǎƍ{wEܾ}{BBBxxWzܹs !fh4~/ecEEźu^yڲ`c644f/>Snz뭷ϟ]p |֭[ΝK;wmۤ K'OtHAwy'++mwO?tĈAAAIIIRҥKu6uRyu׿ =u ^l6sS6uڵ#Gϙ3T*+*.w}h4L&#!h4F!.nt߾}w6Dz :~@|pѣGϟq &{vJyEQ i]Q/\8O&puds4rQ(;̙svo۝;wяi  w#K6҅7;?idѢE-/L&ӥK Ѹb b£}wFĉssskkk͛gnG?T4ji%mǎ>lYfW1Zt?һH.;gϞQQQ5=o~?1{wʔ>:\r짆 {GaE{뭷?ޯ=!!i˺uܾxÒ<9vݎh0& w;u$!D#Nutl68Ņ/vڠ3̴ii!oҖK`˖ 8T庽{Zmr]AE&Dp#CW*w > sUH^ekko{^ \;-K\:1;:t@n*hDQq  [?fChཔJeccJCwy^p8 BEoPٳf~<=o[Y8 N@E/RFSwR\KĚ7|Yﯱ T4RJgy?WT=z|p}   T4R B8w3.B }hTj p@&|;/|{Ϟ=5JR( >¹-M z~"45B.Vr^(JG]_v74UWWfe8VJ_S?1O^90IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-events.png000066400000000000000000000471721351617527000250050ustar00rootroot00000000000000PNG  IHDRtKesBITOtEXtSoftwaregnome-screenshot> IDATx{\Tu? Uoe( `** +l E۷,a7i7!L4۲E/ھ}Ly˷\ܨ E%ļ0r9<3afG||δ9g^"j-Z"T]o`jhጜ)1ZCO8uDPP7CUǎKB7\'H!HDniAy㫯jihhh4Ƣq.OrS&b9PvVgjJj!͜$I$BD(()W4BJE*Vy{?N֖VAj71EO$IT*Fm]FF1O !$$I7ϙxQxI$ITԄK6iw;μXecvxr75kjjz-I7k8V4///Vк_`0 ‹ J$qd255s|]EPY`{,uFyk93 -xxiKѫW/(vZ```Ks3SCEEQ"&Nߜ8C1P[rl{n$R5h̽I"yIzhA}nn{|ZC]3gĪ{>߿``.ܸqCR5?5^8AE @EEh_ڠ6M]uD~Js4/ }{y3 _IVx]Q0A(ms&<17wU7k}QG z+4նqG #JFi髗ozB$I MFcKS_OQƆzA55 (rg o9( q6ߖ$Q9y7Q%rCQDFy>^&^d7Q +}Ip !( ,)SU/5YޯPrz7Fϸ/|sQ.뛿:4!y'gO7F~c5!$d``@2g2J}WE1]Wqڽ|4^U\o"\QgϿ*|Ը)i?^?/ <'(j/߷oUMj^ɽ55}GF%"!t3D$Q%!(^773[I(Ik[/?DDoeQ{ Z0i\DxQ+&,X M1??8e41+_mj[ZZ~^Ӿ~+4mKuwF_oykM9Bn8sWVⵖC_=wxkzz+V4dML !7:E7& /VrPxQeo,IMyビ?]NlLIqhswUv+7ZNƙQ6!}r*d` ?%.gJy ϙ4M޽}|||ZkjQ[[KJo|q6~h( (J YjIDALBJVT$қt䗷HEQYs&IxەzBu<$I77EQ8GZ>rRNpi;k5^^?T5e8yCIh}Ʈ(ib9mBuKО*IE#З™(JR*js&CKCCC^j5q>>>^^^.$Iߟ!EEY$>]uw_}Yb{/Mrk$I4o!H(W1IEwh?6U~CeebIEQ|hgI%I;Q$Isl†Oh%\vZ>;~-OD$8kh&Fij1~ퟗ DQ"*Qj22eB|y|jxsgN^лwoFTk׮гMx6???[*B#Un\ih0yd~ *Kխs1ʿt̨!}||j kgՓӧՏ-Yj4  ;V"hZro~`lm66u{K_$sW^>~jA~Eom8dhQ\G "*0o~f_d]˽3r{zTteöWݲ֜7v=ZaA~ږ.KK(Tj 4~!Jo2LF"\[k *_|Vun(Ed2O??3f܌y6.dh>P^v !dԩ/.Оֽ4w~?+on]-z[[[]rw?~r%,,/hr!v|%8hT4`* ء\>zy4wד9t\[_v;~;پ6:xyyM`z~{ٱc÷o3h|tС:t"[nOJJ$Qy䑳g.[gѳ>۸qg}&UgRRҶm>m6{~{}W=O ؁pbEs4kuvdG g\:存h7"Ͽ~:bz,~֝+Wܼy3-gT+Wg39' {Ru:$I t|(66vРArgN7vؒb}ڵy慅%&&:u3H*7جI^k+**%sUUUaaiuӤ[pCNhK kll\vm'SrF_raÆF%IRii;3.t3Remejƀꫯʍ/_g87ջ9[p‰'*>Ñˍk֬inn5k֤IkTNNNNNu;!d ,K. ۼysfTn`RuTn ;PhT4`RHv N*7Tn` 0M,b/\YPP81m<;//oڴieee;󺣢u Gݽ{k: /ߟ.嗋-]xqGfcYYYsill\l1s87U⊊L&oݙs9RͭZjϞ=QQQ?#Lb01 6tl2 Yx muTn ;PhT4`+R!N*Nf=;r8^grSz>55>N~#F3fϞ=zTTTtt{ڵy慅%&&:u6-R/_=c b2N>zꬬ,O:/tmnn̙3WX-VֶZ@\\!.:thjw짟8.)))$$d׮]W^6lXBBBssymZonb0$Iy|YڛvR[[_/_\M9\vg*w~~u*++z|YW$VWWWWWӟYg+p[έhݜ=y܌~ɞ/_v跦=ʝ=wܪv;Y9&&f֬Y&MrtnL={ٳɭۼys[ lsr@g  ؁@Ev; @*7TγoTn`Rr]YY& ]p!%%%22|W˔b7RmiL0<111776M6/ nTni$EDDƈX__j]bA1S[NzR322׮]kg*wWiT*AhZIJKKn٘J->>d2ƍqqqrSݻeƍA7b-ۆ:ѣWVڳgOTTqrܘš Lnk9,,l[8p.˷ RSSSSSO>i,opHv ^;H@> ؁@Ev ؁Tn`RHv T*wAAAdddJJʅ hu_|1k,B^ϓ6u:]QQQllA:pmJ-++2eJ^^mN3fLYY(&vȐ!튊_tc<| :dO-YYYϟ߸q+> h"__ŋЖ/ޢcǎ%jڊ>cccGR!GٻwO?M- /ЫW;t'u,;888++kΜ9˖-3N111[lEʶmFMWj323T*O[cbb}LBnMW!q1m4۷rJڢV j|mN*7!$77WN֦. ӧLH6wTn`;8:A*7thT4`* ^: ^Ru:]\V]\>jaH;uqEKJJ:tЀ:$JnwQr'>mtCBBt:7>|w߽w/)q_p!%%%22\:Xr?sjEEEqqqii)mɉ0ӧO^zŊE1;//oڴieeen<'zjŖoy!D_s]_s]43ڬe?qIII!!!vza(n?uԜ#GȍuuuݴZ`$yNG###;Sʍ$kZV*BHmm_|r5*)ݓ/[᯽ڒ%Kho///Ih(g>{lUU~s9^'-j+VT{$ IDATz>sژi&lo{}'~ɉO\@=3g g^}UXPPpp;^z%BȮ]&M "!dժU{쉊j pg%GSϟ?|ܹӼܯ_?Bɓ'Ԡa.kSÏ9r}/]4((Hc8p[U:y<Ϗ1III?x`` qTnA4w߽ɓ'-\w(j½*k<{4??  E #JzRマ?x֬Y~~~(jW}8'[קر t:z8NWTT;hРFw2vؒdkn1д~I 222%%… 2SS:wB u=|gwqGaaa``1c;&IRgy_|9333;;{ƌjC.!慄ӝTUU._ܡ)Yǚ[*''g„ 剉a$eeeSL˳}ݩY>z^!C|'AAA)))_~wܡ孽,oV;~۷/[j>$&&<SR57NSWW!ww.78[wrwNfy:իEEE}/ 5j/VRYgyKTZZJOa:坟nݺJŵÑ[Gϯ%V5,T4ҹ,~_<|0- ΄ gϞmrrrAAA}}˗['OA??F1 h4)OI1|V~ƍ'o(7?g`ey/]<ĉ#F5jT߾}}||'f͚昘YfM46:坖=wܪ N8%G{cccvNI1|V:tѣGW^-oh4۷rJ;'$*B#Un=sl,ziگWO>ߔ @W#75nx;VZE΀k}>wmw/$]~RӅSrc6˻]nw)t @@Ev;P,Tnt,TJ嶡cO#pUܮ9+|Pu <Tn(jBȥKOb~րTΤr;yWةhI6&UTTTNr۞+AH嶈3TnTn{&ocnʭL1Z1ܝ:F:mEiӦvChMu [qةhsQǏ)5Rr+NWRS.RY8R9RTn7<^9XhHvS =SwCEv;Phu"(*'r&x:H7v`N%x:*"OOY@Y?[ӭ_~Ĉcƌٳg믿-C;wNޡhrM¸-Bۊ5:[qG Ȱ$l߾}oV͵cmذa'O8ҥKٴqW6Lr7Vvڦ;wƅ ~GF133sݺua͚53l׮]ܼk׮0]v7N@ΝkCVsԘS'NX*ʢb/Vo{ еʰeyb*ö2444X"Z1|tũ*>JHp6r+sa*Cŝ<0LUqm%wN6T4⁩vݼyd***jbyȵToOŗX% ;x`*=V\駟FEE{{{+Q 6V5MrQ 1팧m Lb!sVԝPhT4`*{HTnXd2Cv&2*"OOv87fETȔ .ȍYەiiirJx\62Y0''?l2bMnp12l%L喻effʍY[f`xWϟx\֬wqܜ9s=zĉt$[ $HvTn[DDhN}8 Rlׯ׮]gkmڪh,$7S322׮]kg*weeeIIkGS؅bznP<.Fk;~z]]!ns;y\*->>^C||gܸqmMqVvҒ~+W>cFѼy7kSшrƨ}\RCAAzիW57i(LmǕIII<3rssgyR]tRY8R'r 1,T4rw; 3=* ؁@Evp@Pѐݵ>ׯ9y=L` WHZ+Wܼy3x"*"rwؕ+WƎYt N W<)^~kSѾ{﫯z衇估B`*@:m1 RlthJc9uv:Tn{Y%ةhNH\ܮ rp` srwR1,T4rw; 3=* ؁@Evp@Pѐ  WHv-1]_,T4EH68fEC*wSYfBz}dd^'$$$?^N;vlIIbO Sѐ%cƌ)++Ed27772v*,,\|qTnrKNN.//8qbaaaJJJii̙3骒%Zmr3rFS̙ظl2;Sz^{mɒ%ѡTnzVBϪ:éP<.Fk111[lE1++m۶=ϯ}0RR.;&&w$̟?֭th 0}.NE#HTZNIOOW9Z^^?l… 'N;BHv <| HfH6H0rcXhH0w@0ugz8T4`* ؁`^'Rb!%kXd&߈$*"OL)@'Y?[q\ys;1]9lGU<<:WayV*vb=vL*pg R?[q\xxxCCN`;&qDq8;ِʭUvd;Gcm@psT4iܶccc7ol2lvL*"3\F<*v@ʕ+?Өzooom縊q@*8wOwr{ؑt:J5dȐ7B B>W䉩nm =  n5. 1ugz8T4`* ؁`^'Rb!(:{N*]m4مp**"OL=%x"6+'rӱh6cm^YYO?BYfBz}dd^'$$$?^q,l VةhMǢEܶuL0<111772f̘2QM&Sqqqsssmm!Cǒ?(@*rKmKz9EDD'Μ9Ӟ n6ѸvlٲEŬ,m6zh'*T4D*?؋mxyƍ111nff&!d[n1v*T N8m:ѣW111j:==Vs4v  ,)T'Xh=0 @SwCEv;Phu"(*Rb]8. rw TGcTX-}3/Ngq 5~#F3fϞ=zTTTttxNEC*7!b'-nM&IC"O:/6L*wSS#ݞ>@@*2R{%/T*z?`0>=8 # ۊVݻwoKKƍmX; V111V<|B*7xrp` srw>BEC*7PL ؁@Ev;X׉TnXhHv9t:  MRz 6+ZLV<lu:]AAAdddJJʅ l;b V}іoZ677עaÆ+nBEC*7PL ؁@Ev;X׉TnXhHNrwr8}3Xh.?{جh=0/5k!DGFFzBHBBfC,T~tꫯz!/E=ܫZQQQ\\\ZZJsrr***b8;}իWXA[rssgΜY^^bŊlژ7mڴ2ߍEiꪪ˗+v07f̘2QM&Sqqqsssmm!C&[BYYYϟ߸q+>nbŊ{gԩ֭'N(,,6Ow[x1m9r޽{~ib{_nڴwׯwh\sK/tEA3u-jڊ>cccGIutos4rBbbbl"bVV۶m=zth܄ļoݺ5::-aV !jZ>GSܛ HσTnlc}T.'XhH;á;PhT4` :   .pL* H Mc8Smj+&h9Ojm; гl=Rb˷ .y짟8.)))$$d׮]W^6lXBBBssymNs1<<μV5 $"8[qM2%//vOTnHV|D oyJmM [r[g+R=|HE#= "8[qtVwޖ7o+O)>>ޞCSHH6V=???777&&FcTTԾ}V\iࡐ>< A*7 G > ,T ֘3=* ؁@Evp@Pѐ  WH= TnNg1;yb!;S9v'bHF*Chcʐ݁c1ybrBʭ8; ܊ؘ|ء;]M¬aέh3 U5DD59zMFgΜd\jПf0ΝGաEEEN ܙs+ZU\\dgϟ?ߢQ~vٳgϖ[rss;6+pgNh~ _:ljաggn nνw<_hT4`*r;[P]ݵDoD{'r; `6ɓ(tdxbxX> IDATKOt:N7vؒ;@b6;!!oO۷7.,,L1;'''..ӱꪪ˗9=Gr*wvvvnn}5F|Æ (ҏ~aI}QGWa}zYYY<ϟ:u_~ܙ3gX";;[ުb.](In˓O>;uT$/?ct#GO[l_x^zuxPp Wrgdddee566]֞Rz|Yږ$t[x"vR'Gg\PPP__vn ܜs+Z'S?p‰':ƚ5kcbbf͚5i$G nTlr~6ol+<࡜X:GP*74,r@O^;H@> ؁@Ev ؁Tn`RUON*731_̚5###z=!$!!д49qH1z[קy…ȂoV=0=f̘2QM&Sqqqsssmm!C!999&L(//OLL͕޾|rfffvv3!yyyӦM+++kmE{w't#U+|!RW ЅC 0СCn%%%ڵիÆ KHHhnn6mrrryyĉ SRRJKKgΜIWKTWWAZmSSV?~喈U}}VoB"##7t:G{h"~˗/WS.Kʚ3gNcceI厉ٲe(Rm6zhۛXDo[&*ޚ0 r$Iw[3>{@Wa';&&w$̟?֭tU|||QQdzǍ&OA?khڽ{lܸQnT'tvRcbbjuzz:WVh:tѣGW^mc'iiisέZj՞={8;ĘJ1UTGU8{ؖYҴ_]k <̓^S `>BH^xqǎV"H .8AuTn ;PhT4`* ء/w40"jbrEDgNSjە+Z[>Gv;Ph /D\=nRyyl+؆KʾWm-7jupP#B¼i*)Q_VZz:999tHg\U^8z䈗+$$TmuxA  /gfICDz.;^ǎ 8?E l;$F퍐0Wϣ[ RW_qJxAs4p_(zyyI=SpB!FDIih\u[$}.4^n撗 hгO= &q}OLLG^Ϝ9Rqh|yƋ/_g-K,?~|CCq'7oލ7,Zه`* [l?>!d[n۟|I??gyرc !(Jotj#G HIIۗ-[ֻw3fOȑ#ӟ|||>%%% .ݻgv^'5x]]r%%%Z};3b2Fd$_h4 0? "AFyEmڴidtwRr$JE_y[IΞ-/&T;hI>033Ï4 Ûo7I?Ql 9k֬}7?unr SO='O4Lϟ_hh+h4K1хoFyդIkkk.\hmg8T4pke%>o^ܹs> c0sεk"F0~>}+::'s ?s,yb;Oq}iʕ+32 6<8Y=zn -׿t]æNݔ)S Npo(~϶ǎB&!c_Ͽ9/jq!?]57t 2'=}Nz.EBCC mYܟ-|yA|fm}{;4߶۠[%":y!o~^^^o~Ĉ07Yu۷b{mӭsQ7n譇PlW3sЩDN;OnM$\ٹ1 {sa  ܗZnjjAwAxAڅJӧOMnS>z}TJ5 /PMyF vZ9(J5ׯ111>> ܔK}QGuiC}^^^}=j@~~ ܔJxt: ϥV5>~~k ܚ;@cixAlA7MyhT4`* ؁|z `(ļ o*wƁm>!jĈ~7[o{>}|}}T*/i,Cc z&67BUݰWV۪htn;$ɮW_>}\ {4Z<裮@ f~fIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-list-adv-irq.png000066400000000000000000000504651351617527000260140ustar00rootroot00000000000000PNG  IHDRtKesBITOtEXtSoftwaregnome-screenshot> IDATxy|SU?MVmB)j R-@qd):>2ED J8Sxf`X2XiE@갔"Bd#א܄tI|ޯy=9s;| (JΝ T\o^gk4͂Ĭ {> :v#h+ Æ # !Lgj !H!(BZQ8Ov|W57ꂂjFQV}f[S(߿[0"ʖo֦uvAT*jR`E"H NxADFQ(RATJE8Qo5550@Scpy hh4hhp 3h6-<ףKO?,x^Ee_FQ* ~,I~ܤG"vq"}ÑoΡ !7@~tsHg+ !{t W&ʯ}}!DN_Ի촃*w%׮ !o8/2b|eϙxNRk?+n4osF]Q@ @n?KE3 ω "ϙ !+۫Y,( 9ѻGB:~x{Ϝ:y: J+R_9PT(jS,:MfS( AEA9!fM}TĽFd4Mŏz̹JB~_w.& yǛ.JѣKWoԙ9ŷ%E WnBxBpt8E}]-AvD9B}Oא⒟j Cz釋WX\urfׅ*.*p&~(XBDA(s_c߰MPm>X\reo7&衂 \^{YQFd"OאgY6.^i&BHYeՕ=w8-ME";n 2:6C}+>wX""!DTTZ&͑<{خ~P(lϿxZȝ=StUUA:tPM|y/:uDnM*np`44pf)/+(JZ5|ߐvϨ8 g W*gJ?{>^^9~;w{/۾JPрa;u r˗s4z-9ǚM {Uj?`?MZI83`???c mAkkkkf(j@@-h.Z|;JRuɺ{gvS(vNJ_UKz>glGwrYv +Ӷt^ZJQP`Hڿ˭A9d23{`0ۺvr-;>T8M g283?|Jҫ`oYzvط {-/~h~n@vn`Q]C<wSC]BL&<݊t:v+_T8Gv;^т;X7ߒ BZ[Nߪmo1+}vh,͛7{} 7zyիW]Co2DQ3{*J.(5J 䔕+rύCܝ_puoY>!d4jRbe˗/_и|oo;{&… y0&f.$h!-W]J0aKr[/24{2m+ܹ}$ѦkWxOWUP5~F+?j׌p^ ؁@Ev;+ڶly8od-+/'ef=_N;P\u흀=:TƏ?-GfZg+F`0dge8_ѴZmeeW]u<BhhW=j ]05`݊v!:tE裏h$ˊviG>x`n?GA[jx-'lU*`p`0>h+)cǍ_88cǍw\Uƍo@;St|k! ؁@Ev.k@Žζ10f̘&!Zõϸ675Y޲i͍F7W&lke78͌plϢTZZjRyyyrrrxxxJJʥKh˗x޽{kZz>99)F^==ztZZژ1c\xYYYV/edd5ܹs RKJJ,?;rʬY'M@kvС5kz)..NzuĈ1>>~ڵfٲq233g̘!}2h4FEE}g˖-z)77?|+cǎEDDXHIIIOO>}zEEEڄkmrF?ݗ>l$g׮]VkΝNjs\xق]G7 C ΐ Y;@> ؁@Ev0 B\xIO_Xm3{ٲe6l{#Iza\= x2|4OQj(S4˅Sj VPP@[l㹫fΜ٧O'NnІIIՒ{^|Y\QQdbϝ9ys䤧hж{mŋ}^2 ǏYsٳg…N-x W4OH!$==~GFhmEԩS=zhY@1- m4F[o%5s3&77ʕ+/g m9s$&&>ÑvIj~7t:ݔ)SƎNa-Ȱm'$]~m7pyEknZ^fMNҦMv֭W_}UѼk 8nͰmY*wTTԖ-[Ο?V'Nh2?8F >|&>h۸ũQQQ}QHHZMOvܹd|ݻwGEEY0sS;v(>|8>>O^uuu;vT*(VqDDD߿.YeѩQQQB""" ϗ;nj'0;R#q :tpс?|HHH~OͳѲޚu<ϫx`޽ƍ 3gN@yVEs8J7|]v<(j@5Ŷ@ ֭Jdsrrk^y߿ǎBCCW^}Ͱ#G~Zaaa!5,Z-vx׵h"TWW>}zɒ%JRT`blKjZ( Ro޼` 4;;;;;;{ذatEzARY.YEԩS=zZ\{&R:t`F;vlvvԩS#""-qeffΘ1ҥKlv̘1W\ioMgTn[VT(K.]t^MIIIOO>}zEE]o644t)S;TN:uTrgr׮]7l`o;3yt*-=ޑ-QcǍoմ]uc/ ؁@EvxS*7cޑ-QTcǍPᢊj|1 )`CP)1oJvi)))RPiiiRRRdddnnd),o^ iF:w\BBBff&mʚ8qbqq_ nСCk֬9x`PPKZ`APPЄ L&ӂ 'L ͛7Ơ7/ΝKp…}}ꩧ@ONj4mBh#{Yvޑ&֯_o2>#FÇKRϰ={466A7T=p@>b ڸ|ݻwGEEfgvvvffNp YTn{}ٵkZd`fy'@*7:A*7hT4`*r; @*7Rsss###JKKim#GLBzOeZccc{]Vb*[?>++&q o2K'nݻ.?R{|R|mNG[֭[G F +))ٲeˢEn;x`BA),,ܳg… !ZxqppK's4t:ƍA?}CҗhveeeeeW`*[׎-I:?5k!dٛ6m/!q1m4>e˖$nNT*SSS JtmNU IDAT*7!$33SJ֦d( ܹL$n[f|'C*7TnIg ZhT4`* ؁{HZOl-hguՉTnh6hR Eڸ=ݺu;xx'E]O6oL/_O[J=}݁>{lwiiiRRRdddnn+& m:eX-Z[o:u6fdd >S0|+V0'%Y}1cFNNΒ%KJJJV\I?gs=R[XXp¾}>S'T!EEER̷ mUgRQKKK[fM;-]`ժU۶m#wV|PT(ݣG\ͅ:iQiETgyfk׮5͎ו >|ک@q/׷Omttɓ#""^z饷z6;v,""΀-0˗޽;**,o IԴܾ/Xhy@`BEv;Phu"(*RdJ `ܮӞ#ޅ@zEC*Cцwh_hHvp(6m@ !E[r;3ysTnyH喸:h=_hH喴g*{lzEC*=Se'#` *xd,|;c}Tri*/*R˥SU'@Ev;P,D*7P,T4oLf&x:H7` N%x;*p^ܕʭjW^=hРݻw;QQQ{:Ѕ .HBMUȵo);sme%%;<͍&n0--'NK.fo!&*^%\lۀoELw.TnrK-MjV!ײaܖNUvVi/*aՐ-ύe7~W!w nTei/q8@Tn'nذd2_^0nːk٩ڲ\Ev$W4r;cٲe~iTTTmml0nːk٩ڲ\EvLo.Rl_駟ڵsp?{,|grZVP(/~KOvӶ&Pѐ9+V;T4`* ؁`^'Rb!(:L| nO@^ܘʝTZZ*5Zfm'''H)=s~9Զd|W_}Xx;+S5Mqq㳲Fˬ파QF;w.!!!33d!_2npٲe[n=r_|yfr@fyRݒ-uׯhAH}l&; m%ׯ?cƌfknrp5.]Z^^^PPjժm۶frѣ c-vnӐ/F[~zMM !{/NrKd;I}FaonӐ/{wݯƴիW/[3-+SFcTTg}l2߿ÇWXaonӐ/ڎ+##㩧=z?>i$C=2x5rpFStTnp soL4dscXhHn1O@ p @;PhT4` :   mO>ξ~&pՉTlٲ 6H7bAۺza= TnۉسgO}9X͓;vAp_hHb-;yB\2k֬I&9kC*7RxV 9r`m^*-]tݺuV?>mlV*7=VZũVӐ/6gvvʕ+[MTnd`kܸq3f@<7W4r;&;Y)))ӧOpfTnSTn>G*w!BEC*wyZN  ؁@Ev;X׉TnXhHn[M,\u"m!  R{zEC*DTn)Ɏ%^ѐm*jJc6 Rm57e)Ɏ%RRYK.-///((XjնmH3S{цiq*4dKYSepՉTfK-|!Ydǒmp r>| !Hn1rcXhHn1O@ p @;PhT4` :   @pՉTnu5XvBEy(x_hHrȑ)SBz}dd^'_xQV6lXAAbOzEC*bAM&S~~~CCCuuu߾}iʊ%KXh HF*1cƜ;w.111///))ԩS'O/$$$r,i7/@۲hK.]n]DDĪUϟOMJYU8j%hKmܸQ4o>tPRzzzvvvIIٳg9|!ۖNg͚E={MKF1((h4FJW4rtJ255T*sg}666SN+Ι3'11wx5·#1Hn1rcXhHn1O@ p @;PhT4` :   n_{U'3F$@+PјSVTnq.\ mwIl@b;W4wr;ȶ91Ώ+#996HnTnquuuVl'Ǖ;WC*s-f_O?t׮] g+`7rkZBѷoB F^>卩w]O"q|  5.` * ؁@Evp@Pѐ  WCۆz[&Mf\T۞W4Icшm*n۶<999<<<%%ҥK#GL2###z=!$>>ŋcYxPsRX4v۶`OFFƨQΝ;I)..!??d2744TWWWv,x ƌ3rrr| &\6?~|xŊQ)TBHjjRG{;9('@*7x{,Tn1Z9MP|0 @ W* ؁@Evp@Pѐ  WHnqMj,T4hC8Tٓ9jV;bY˞W4hPLLݻy睨{:}X*wII޽{OZ툃͚L&ړD8q7XtpM9}k!7^%} -=a„B1""Ʋ[XX`E8VK###nW[[+bmmmXXȎkٿ !!W^wAj}}֤ekOw}JE>}%K˩1,-+ z?*jvGzʮ՚p_hLrSaaa{ill|lYW4&SLNw״˖n>{!RYv8mMPѐ  W* ؁@Evp@Pѐsh7,\u"(*Tn}VQFFF&%%:w rȔ)S!z>22RB/^(5 :,wL*wLLLqq &)??o߾[5pg5f̘N0aʕyyy~~~=,Xh{yі={,\X]V̛7oҾ}^2e3ulٲeѢE[n2jLhЊvDQG'S*9WtS'i8y>}Fvꉢ/K5!_{`Ԑkָ]CTET҈&1$-jRrrNA6g֭=v&w]ooN׷n=O]5IJLB V{PO[t/eCn׫0HR լqoݪ+xa],ܥ89O9RowM{n i3b.Og-X2G!D.[R;EB~5ij``` &,;nnJΟEq`ಋ%/vꨢ99O9!$,Lߧk4׮]ST8_}T(#SϿp==X|ۣ7L&SccC񣴱{{ΝQv~و~ !K.5jEZP_߬yFѨ0^x/%ku-fO׮mjl!$U_;-?q5lr玏> m|0G  ȎMP 8islNzyRO?;Wʕ.w~eTnєJlS0|WD?]PѢcܶy#:T*骰>+Da<[_톝{hT4`* ½=:T{@`/|şh4: 쬌T4Vۂ1oZ[ pW,\u<B<ZՂn p _h|IBBBxxx\\͛i˗x޽{[|33g'!!ĉ4)))22277ޠG2e !DGFFzBH||ŋiVj VPP XS\U/듓w4^-Z[o:u6fdd >l6 V\I?gy ,h4=ؼyhKaa={.\H,2c֮]h͛zjACBBJJJlٲhѢ[|M&|`]NΚ52{M6EGGӗFcPPh|뭷lW t钽Z*%7.33sƌ6|EGGO<9""⥗^ǎp0ěoРL2vXڸ|ݻwGEE9.:NTBRSSJtvv9s$&&ڛ嫲ۑ_*%%%==}fXH~+y}[KfMR  ; cǍ_88{jqsJlqYmPƍBwQAU'@Ev;P,D*7P,T4_HFp63XTnp  -ܔep͍LJJ*--m|^Ѽ(*8[vt{4Mqq㳲}˦Š>5EΞ0aBFFFaaQSSc-,,`0qVaIE6,,|ƌsܹļSNM:<@@*>{O##ޝ1|O*+++**,Y䠮;p˩陙?BX~udB?'mm߾}zYY AXN厏 ݵkwv#֬쒒gϊFrk׮}t_vlf4FU3xh!߹s砠!C8薛ϯYFQZVVֳ>+~ x*7q={[^viO>t_ }iAvk.,rkڈ#G8Y/r*7k @> ؁@Ev ؁Tn`g} SñmlrTnzRFs9V]zAbbbv;DEEEGGݻv9sf>}N8!~؞={nI'egg&ɓ+VHKK8ĉoҥK髙'O>w\NNNzzVII޽{/_ +mς 4̈́ L&sW ٳpBrgŋ.hc+jPTRж(NѣZ(gވTK.5w1c^r^pݸٜ9sƛoРL2vX ;t6>r׮]7l`)<XN_r*7:A*7hT4`*r;3V x;vRLH*#GLBzEBHyyyrrrxxxJJ8$듓wA)--MJJ͵:K mTbAM&S~~~CCCuuu߾} !F:w\BBBffU+Wf͚>i$BHVVĉx{ޭ -H*wHHHXXXIIɖ-[-ZuӠui4y 6LZ*z{ڴi/rbb"hڵtիW;^wkz@ blu:ƍA}C:^*z;;;{ʕVQ(քe"9 ;:?5k!dٛ6m/ŭ_d2}#FqeffΘ1~6|pi-OXX؞={}Q6VItJ255T*s+VpӧWTT,_|QQQfYꐝ,?\v'!dSaŅ4널` Es3K V^9< QnoBBu.++۱c R%Hv Ng ZhT4`* ؁@Ev?qȡ@3 "._F<JmMl|Es4`* ؁ +s~~~}p@  <׫N:9f̘T*gZfpaJ;\i8 ά3|8vl߾=y@G{lępg<(ެٻwwϣ]ջl qs RDBZyAWq@ hT4`:9YjO/: p |ˁjuAA'N׿ iKh[6n8x7{"ncǎ%K555w>|gԖPӉmaΝ7oS__OjuFFFǎcbbΟ?rС(i`ӧυ p> oV^^ȑ#"""6l VjuxxΝ;˗ر6=FVӖ~i„ AAA :v옴#硢GEE9rǎR{@@k_|qQm۶?7oޜЯ7=z̙jl4KeeeW~WhGYWWg6i /0s̛7osu.8>dƍg&̞={ӦMR ^z饣G:n$dgg ロnw]v <8((())\jW;t0iҤK.і?R9stW{p}ږ-oW;w,dȑ#;wVӓ׿~_'##ief̘9`N:>|pt-W{V0'خWDANcǎ%H[{8>ѣϥKosښj`6{‚jndZjTM g~3yVlܫg}[۵K-m7hD^v{vURAlfժҥKW-ަGAEySo;l43N̎Vim6= *x4Qpep@CEφ*x.RY_ߠRy]R(:w|ݻ{*GޥKWBf8 IDATxwxgۥC !P$t ^(HPD@Q"EQP.B5RFB%m&rǁ'wޙ}3;3.qe1@n$ mLsRPn<+jt111P\IdiBjM #SI\p ZMI/0a;Z@ Iqðrhu4B!` d!H p (pNd=!`0,ZV*@0q[g"BkvH$4!0P," 634P$dBp0 \A ~iR`x77YVu}ճCYѫjA Ӗ899)JJR*-hieY  Jg⢛h3!` b!,W+tbEEko-(/u󱒖2HWYMgbGgwRm7p;;; 6Lch4YFl#zȢ"gggN fiҘ_f ?ܩÿ+˵>^V#׊Lؗ?+58+?MT|aBG4-1ypW~FqvvS(5*++0Lsa"Ű Mh)><#V24i(J{[P_GG5A1]y3JboҔ xز kr<8Āz !kuJkF2|?%%^ّRŴrTlitzCc! ""ʨg0 >pe$ լ ;N]RRI)J!` Bo 4MO9T`J@5Y4K(p +%^Ԟ4.t umԓlJv婛iI1>O;"NgɈ8?о DHBVzҙ苃 ^.*@Y5ʌ7o<!j I$Fp2Q_UUUYYR AܸؼȲ, &,C14 YaMZleh!CS|6z T_K EӨ^m|E&A./&&@|wHCT Yu{4E鯴e2@`!Ϧ,aYn1фaaҶe!B5ᇥ8&սRݩ\f;B~~i5BL!YdB v~Cie Ԭ^Ro n?B2BA qEm1Ae,Cs8ng) vnꀶ72J?zpF1geQ>̒j!TBN`ZV*RsKEAh P ˕OD@g;h Uv*LhIkr=5Aq3 ?,MSV|2fVC"R* 72KJs ޺GahsZqҬ" pi)UeEYP4ba  YDWd2RtY'0o yE{1=_lK\AȲ,@CS ʨI@Fi5e(YlH3D]ʨg(9bTҚ@V0:bhZX׿^ q}*@&PLVJr@u,CSA899T*A ]]YV^^݀.NxߥK yX!,˲"2#mȲ±"TW\P)0.kY#{GdyVZ>)!| Bȭ K*H w;W̷K\TSD/_|;/_yP(eDkA"^U}j(nBє T$l5W#,7W 7"iʈa˲BiVyEoG` kLϽ{3:wx3GL]ʄ4E2ɲ,a8c8^e` 4+JGGGiVT [ 8zQ( ]Ȥ B"2,Cs[6]j C.qr 7sIyXBژSH aoe?o,jO;2 N桳<ۣ`u_$"B}) .@U*0_{a K*i+&d@)UORR}[x;yP{ڳŒP_{I16ߏZ90 E& aJʫxiF!iVXP+>W!*cKo[\ 7׸!c BH]dPJȲR.gsN٧WTvJzd|+LCS M2 p #[s+ki+4yBpEfhn*vG$DZ]p+^a *%Q'fuR2zw;}._ !2TM7 B52u]o0k3n`dل z{8Z?a{_+*3Uv5CWUti;W^4N876?B>/i4y ?tm#!%B )QiZ!@+A+ZO^'W؇/p<"c"75 #'wR'24<0{2 +HI9rɉ Cv٢|k2n+*rpuuuppFMQ8+n ܴܲ.SFݕ?Ҥ4 Pu"4R&E_DQ{z^ZJdFR8YzoSˋuussST.O6Ƞ$ :ʠ$TJRmYLpWkb~P=5RςQUBT#T}5 `saEa= RBU b($,C[J5uZ{UՃg $^(h^l2 EQa&o]8z/2N]hjEE0 F"W6CVx篚E2O$/-z̜9S8?BPZR\_I22Be c%EB#[N :q9m1өwL+G{hV\u5[35 @ɸzE3Bf $I")!0 ԄJ/+1\gd00xⶱ`,pETUe?L/--xjBZlo`O(M80̒'ޙye q1 .bfC-(>I'-:bdiiɞ=*+Do i):uXH1;ro/w1u&HmB|~g$8ඡ ߚt BauU:Lc}4LᎦ1acV}򙓳+6oл)]Is)S$ bh.DkSsgNMZrmS*U7oߦ/4H7Qvp,4I~=z EEGGGGG/Z`0׉ڗ@{F|CR)vzw&B@Qd|\쬌@ thݥcRsnj&/4ЛI }{&ĿgNvMfr, L 趡-vy-:$>9npPYiIdXPYi W|lfF_Vߺ5}AjUد6cINf.=ǬZ<2,ht5Zscfhzk+V ~n۶{,[|˗.* S+F {k/X.]{zyW./{~ҥk`P0ז._%\{̏wNvZ:&|̢>h;Y;w%@Z}@Z}9@'L .yw]8iyM*%GxM_UTT $\ߍwi2g@Zk,iGV_~_KGJhΩc!,).o8x s~~Y tjL2Ϗ}Cኤ/A̜r [l|kl)f;:l~~WIoM#JT >X2>\R~.25>f3 Ns 8t>Âw5dG'g_?vϻw޽{mah{[p?Q;ҿw|x/?MZo]p56{H kFݦOXLa[,5}^p]yh<?>8uHS\fR!0!`Q0yg?YvϻdݗK Nkog`.}ǘwlgYkϻ""OxDӽ,*NW:Nq?a5Zs̑XV*//{ Â{Ay_W4yΟ!IcevO}}گ7}ִYm/h0,ywA(^zu‚w;qiooݐ!|:UTj+ ~۲бw~3慗@c^xy;"#%:}vҢ,K1WQ@Quk>پu3El9>5Zs,"5/$ʶmϝM,^x„ FL0asgOvc^IM ^xi5FIck7l;w.|LJ}tlrtVa~泖`?',1#Z-L|'\+x^&ra?XU?ݵcGߒ׾CB9!0b.G>dDvjhfN\7'$tqqq6$9$ܪ#Ԕ6yJ[{:tسWyoLR]]%ܩٴ>%dzyO&,\J5f"x22 n+lӫqojtFkvu\Ǒ:n܋!- zf;99yQA[8h5ps4^c6TxCM}ʫ_; ;;;_t>f@:uj[oʄoTS6|СMIdx4Q8!Rck?d!Cju.ݾKxz}ƨ^|+2*Ce˖ |gό ^h\G txX??1W_|i{;v_ ݽ[`7NYNj{K5k^nk,i~wҒ6m|wx3Oqܸ .ܽ{%KNyb .HL?r þڸq[sfVC<;7ӧ7mܨR則##>t%IRS&OVS~;"24AH.I'N8piӁ &q8;;}E~;FFDH?Hj^zqbcb~:Vj(nX9xYf߷o۶===ȑ֭[<}VOISOsacag̘|ܜ:4ס nM8ؘm۷,9cM3vؾDU`ŊU'O:iP{ͩ(}`ܸϜ!IR_7 t͆7̝;Go{qys!N0̝;w, (ёWoܴ/[:4{{wS%]06&믷+W_ye߶oojBfΜB1q/}߲hF)+b_[Gݺk.>{w7W7GG(тW>}t}׷ k*8dyK^yըnnxH>pG[gS=zp~fMuuuDdax"4KP_V&(>x<㠃i('#P ell2GB#[푭PV(c{d+=RI$)]0'W S HZ!E_-ePЧNֲRnO'ٻw?l~BwN2sҞ:vlu°8Is/,Mۻt,*y&=pp&Lxĵ7:eek;wF;g 7IfsX.lmUzڵÆ?VՉrIޚٱtW9j$E,ypzʁ# =vӧ9/1k,8byFcD,ypzʁY)Z\">em3tŕ}t͛7I uUeuG,_o..ZKVմmنYD5~߿UP4aYxzejڈȨἪO{yjeY_? san9YkϢŋϞ=˼xz I>s&ã!CIDAT1qSa[&*Eˀ7vs{ȫUpsg}||Omi-5aorRzszַ`| `Æ ߿o_PP u=8D#OZeJz}bEfVC?^j6mX=%Wz MP 3yS=qVA|_srrg-K\ruz5+em ETHS]z 9CNhhxCuֲ%)WXS]&PD4_oou LcV.,/1/l"2x A ')ZR6KC$E'D;RVaЧۮzÊ}P(=VWG<wz@ \\' ma] %$$VVV>*Md ell2G|^x֍GvVصTF?\4^ -喑PV(c{d+=|,òVz \ICi~+i:?/捿 m]]\ۆj=@CꤙlqQѕ+{*4MgguPf;> MvO~+DTnVA(Ϟ>c`-CSW'jf7-+4͈R^!l>AYxq!u" elU B P\ϑ#G&N4tGif}DEE;vcG"I.X0e>}ZYU8qx߾}M'MW_ӛ(__?0 pa0H>zϏ;䓄y V ej!Ō3f͚}.^8{3f3;,?Afgg[]AM?s̙3g={)SIttt Irٲe@%$T v:$~%%%N8ѼlShXi4T2|I |睅Æ C,X6p?hU}iz >h4N6=### `DN_xaBhԩ?^{}ڵJ-`u3fl&;))I*VDu}8n܋ ꬩQ[Q·'Λ8OjCƌ=fhH0 _b.E {lyaaYwD#:Z#[&vh|yyYe>VXg, iRZhVl+c!Xi^(#w$o8WW~(, %JB sss+))zb>stJK=0Ll \}Lr^8.M⢋wIRg;>4 ?2*+Zm߭}Q(nnQyCC3[!aJҢE ac8RًRFVJ~?pylPV(c{d+= elNqB §{|w2v<vo/Z7yNRjB0IeI2O) ҂AW H-gVU8KY!W5II8?5]~~[q~&o3#E^Wmkd??e>t.IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-sync-graph-1.png000066400000000000000000002013031351617527000256760ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxyXTUsgFYrA4)[ķTDRÅzK}Sq){52@EQfi 0~^g 8~?OOϝs=gfg!HM:t@hK4F4LSBoN hgg'JaZ۷/BH*/eW"X!XE  ?TK$\.HL$$ d\wN%UeY9gF]YV%Y]j='!wkE.[XYG.~Tcl`+OBN.Zo̯oz4sjl4:l80LwdOj~T*R:"4Ln|GWWVZ) .ghF!dKOO(**رJk>5Z-N (%iaX#ESH1.f`4EtU$0"EakkbROr}<h0^z ^6X_<}ԝ BaUR.Dx/'J/~/V[YYIQTcqrvvV* K,ͰFj=EQ ޚwp8)4t4ow#4q@,cƆgl"˲,,ؐ>lfaWT휎^e"ahZQWVV3 ck˪Zeigb *gM4M#$ ѨyR]v5|Up!.Cku:\}`F)h>O$i(2m h6.7/fU v}utvqj +e " h#tiE3{]$շ\*]T{\+aaY!aQy 8~Ȥ[\;Z;Wa8|$Иg<[D͂+}=:y)BݞkJ=Ƽ6+M#zwpE)%)nn`B$"woH t5JoCd]Btst\ J"Yq%G3[ahEA/#9Oireۨz#iг Gy)KVvE{jT"Q`u^S죏ka֠?b~9ڬ4=[!-]X-Zᡶ2&BzuU8]Ziɵ[Ye]B9Eȡ-4XzI|gH^{l{JTNS:\j;G^*S+=lxQnB'Fu)OTٳVٳgMJznA5 MS cXE,K~6icڠvwVBH$"ޑq럜rPI9ؠ{OE j_WS9w.BHCowyŠfX֠q)$bU#J+j4_N}䝼poenVZ ϼ!'qEѠh1,5-A{Bl'vR=O=o+yQ.*)7 lW_/:fvid)XrKueUFo.* 燍OXRN>0`n?+gԃii34Ų Ͱ4E";iehٸ|ny\+d !&aMR ˲Ee&VjΠiaX"K+eSGwW]TwViYW[P!MN!U*WQ!K_J ϼ!4RdX>ڬ4MD!Ҡ7yi!zW`dY.RBMW CSQ"(JL&S{GSd\VDqq?a>s$IYQh4 2NZjС8ѣGBC pw1 0,is+^&4˲ MS1ɨ[_}sO?K3tAQo:U+XaX_ğ,K3 M t0sEz#եkV~)r+%\60+4+t)4qtxrk". ge\d;te~?7 G TϓDD4bhx=xҫ}6hy#wY~eU];x(e#iWqrQ'{th[X* yb6jpgVO=TП7)Rx~=7B^ǣ ˒FtndVi ڹW24UQYӓ=KbAvv@ òl6ŕe nw*8=˲+)W)CW/;vorRmN)SKeKȣH#E(d$7(ac0L'ZAӴ`hR;C% UH?s?>`Sgah4Vn>c_ذؾ̿wڃao\~%,K?14ʾU5wëeeX2^:)dZk;)eXQ"Qob5:s~G)e}1pöc7 ˫zu8wR-:R"}ӑ+ Lb)ӦI힌Z>d;a}uzvN\RFF|mtƅϔPx_7|)@vFy6aE")WnFq_:g3Mx!AnuH{Pc_:}b{R)HtՕK3;υSw>U\ldooWˬV<;gϞ;8rP Mm=܄c(ż[wd"RzݿΪKCsvvd|g~f`8f׮]:N H$\ڣG4Ç-[k`bA/j4]QWciߏX"9Hv -Ϡs7kdyc:P4EJ$T*Ska(]ueeeT*%IҨ U(x304MuuV%IRpV$JM(D۔rqVnwN|J,%2#NyPU(.n4%OG\7Jˡ/2Y1eXd2D~aih4￾)Y^30|"ã.D۔R\l ˽y.U@ CFNGQIy *wȚG]FlEEEQx~S'GG>2;sϨuhgnrhf͚ſp&!JKjfP/nE" (yznA,yvz=8۴` 6?w=9paKJ{qL-X4:x_ `YdhޯO^=>'iBS͸ݳuOK*dk{EuO3,Q϶&!an}fOgTz?,WԐya^.<"ƅ;(V}o߾u)X3!``[WTy_3K7ʷ۪c{EFq-f9q.5wLY4Cqqoܠ󌕲 ˚g5ܛ*j7B(j7NWHEa]'@w')WA\Y&d߹s$=;;;W||{QwσuևóͲekaKcө~k`)WQa2[䓹Ga'N̞3'qKU BEpI9Y%ϛB;;,~K%E-e;G;ΓF6eY$SR~Сy׆k5|2R (˧O֫wE}y9,lU:x=WFOO{Wޏ(lF==kp#I1K1?@m:xFOѓu DSL+,Y}/W}-~>h.LR^z?ϟ>+UqP4b.R vTIu ?qIIIa÷߾ 8.''bĉ-XVF.[R㨾ԷQ@3Z5z!Vr ޹5Vl&u7Z`µөg ̗\][{x 믿b/Wv3f/2%%zL][仕`Lom<$$DPȀJKJK;x [2B(##ȑ#g(W7w6^C8 gHp7-jZZJKrsN0sl5"bҥi>n J۵kqƣGvܙyH֭[esrr$ˢE6lP( k..5kD". t.eܹ7o5yf``ܹs8ncŝ*yxrJJJ O?16C.P\᏿E/Z12pr~2 嗫TďbbBh1B[Fr]tٶm e%e%99s?dڴ ݼeKz }@ VUHL6aB="tgБa99wq"OFW7/\վ_@.{N"_ I2//oqqq8%&&&99Yfdd:ujС?8dȐ; 6mZPPV5_9{lHH0nܸq9nDz,M)))f(1cZO"eeeRNXKPu\2ϼB,77wȑǏ7MxOhlC#:)ݳgO\\9s𛢾8r_~A/r ź}>.eÆo{tsoe+-+:rΝw/>Rni4~IۧjB2^M߯?YZ q@ F R-?fd@1>}z//]-5ˉ>Ù_9YлM|0r?0MC t:BiZ*9s?~|#F@;vl/_&G۶mڵ`Y{{|KKKo JTݻwGnݢi \3AjٹۻRKqppǕL̷s)c%_*,UةSEEFFZfM\RCӹ1᏿` }7ǽ=܉/jjjR)IJ`01 /=mιg;:$xX>6eSΞ'M8w\7WW &?OHp86"x|S'MJ3-#4Ѩw7Vؑ+RygرoIL~?o"^cA~sLM֊F>%qusWiii˖-Cލ-?f N9yr3fI"qs888=*77#Gf|kxJJ,..vvvN) X,>׬Y?򃃃[ncǎ={tRY+zmR1_">BQQQVj֕$jJ2 RV T, IDATZp;w0˲xV7SO!CxGO/r@6^6\.}x7޲P؈3zf~EnR*% Ϻ|T "Uys?jua,-4P8a/K+Ke}QZxbFh/^f%sLLD"1cʕ+wލ-[rO?J+WFS ,xӫ)zJ  aT+,c.((.5s>++!ꫯ~!!!u?y &ԽS6㓓y |G7͜9k%yws;~K| R)xQ|޽)S\ܧDGgddYjQPL/ ={SǎRY[+TML4l0hv >|Һ;Vׯ*,,\n2dB4iRrr2N vqqQ*{Ro9r䈝bXXXXRR_|Ѻuk''_~dŊι&W3zJJKKꫯjz̭[nΝ*Jp,3gv޽֝ V7nÇζ^ ~hrdb?Yuz^8;gSʚKz왔خm[dawtĤ$NWVZRVV>'.{J~$)S|G=zhz _pXYYٛoyĉ۷T*n>0#y˖ŋ,;bf&m۾NaN\ܡC<xg<9w܌cLO"޽/WdBrc'O{(w>1fm:T27Abxƍ'Nl`|e'cT|?ķ2y4gxJM<k޿J/s W%+u*՜pxuk`4k`O7Hjh\Ç5u30ݿ٦!;~[@K"'YI,S{6~6xhֶ|'p&ۉ]Lz Zjf`r'R'{INNN2(ՐV^-Eޫ]+7G@eUwKt Iդü;o=ֳyZ)^Gul z@3dgc7 ǮIm$ l)nJe ZI{U޹X:MR6e#U.rĠMy)⛅5:*(۸_d^,"l[=htuvõ P<~}::OE΃E CQG9usb\*zB@G}DS.fWuUjteZ ݾI |d/ h'[-8&)#|p~}[I͵oLG_\*zk"= Rћ=S31Ky>v^Q0:2)h/ 6:fEFvv/xU*T^>T_niBjCE "_7"U09rtt8U7_IܟKYRwUvq!Nh#}:8{'XygitpDB#i)%KU墾~NWƄ&5#pegiB}u:#F^*L ?xeM ,0y@k5F|Vյ|Y)Zř[/vqUeYԣmp[gokhsO;+Ni"?xBa8}'WJud/!Gu)ًYU%T@p}:8k5+Yź[,v9>4]}{ϕ,uUomRwI=8[!*..XjtX#hI&y˲A4y=U3plf0RUOE!tN#J>}]͖W_I F@ 3*4vf"֦g!fڞ+U{XUp7K"bmLRAߜ` [rK5s)G.:9H{ЪSϕXoXPFUԐnr5dAQxn6)Փ Mqջ/~E"8o~`9a%&C&'7I5B2,^ᥒ$}N?y'~NoT~q^AsFŰ}zdddzzÇ]VI5ō;YB(XWNoY>u$YIC_ܬ(\+V|Ϸkrx֭/#h7rdKrȡk6u.C t:BiZ"pRaRJ1>0~u |^TZl2{lGE+.4<&_}СA$2NV+P BH,^^^ Qj|\.o+a@} &zy|Jӽ&g3Y35xBWaaaff{7eʔ>I$r;;;5I ̥rE땕cƌ^dD"6l=ART*6uxǏߧOu-X`̙)))GnժUH͛G #>>>8e֭R]v7n/))0`O?d=h~{f͚?u4ͬY.\pYi~4ѱXh30wKi:%%e֬Y8%&&&99Yfdd:u+;r[ #F̘1wÆ ի[jիW'Nth2-*ݻ?[n4ͭM<9..nŭÇ/Z_ <6H?jԨ;;;#>ӱcfdd\r_op hZjVh4ܪؚ5kRSS###̫VZp!wgKcdsڵH$D 駟w|Ph2{ >++}!T*  111ϟ1c?ϭx!^}U^rС[X,V*]r<$$dРA! $gY͜9{Ze+V,;g.C~ 痜/>|Ç[ɗ r+[eXԢG-_:uZXh4:::''ׯ8;Ν;ڵ4@sğ:<- `h;wdz:x Nܾ}{nr /pyk׮;J$??]vBV$ 6Zn]۶mE"{ VEp9SLr[`N|@\>j(F??* `xu l McϞ=?ooߞVx㍷z 'N|~] g`B h bn`Y6??/t1 c%gTTԪUZF*_fMjj|||dd$yժU .slY`0V^%͛7OVN0}VV>6 J`0,X#Hf̘rݻws鯾_|[Z lݺu;wTTFKLLL=zI3gvߓbŊιNX~ P(&M/;nܸŋ><;;J6!r,VnK?ySһz{Ƕ}vaL+@|^TZl2Xܴ䫐O'l f`30[ ``k0{3&>30[k3k׮aܹܒAk֬D\ A>>>\ܹso޼UtRggg4BCCry@@@fffyyJ*--gKKK]]]Mq͙ԌŁ!n J۵kq'4\xJ4Xll믿^VV&J7n8w0!˲4M̚5ˣP(ƌ%$~8OhT*UDDDRR>RLYeY|j322N:ՈCTʱ?^͛'o۶mR]EE˲j&4UUU\X"ˣVqA{{{.Qӱ,KQ>{ooo$FŋY eٳgwoXd-MIxq 0lGTrQQQVjeY BH,dY6??%^]ٳߎ;v(֚Bk֬IMM|nh3A]`0$$$Xc0J`X`?=!!`0^_K[aaaSչ&L k׮9slqLLD"1cʕ+w]x|5W_}RjL&̳bŊp___~^WTiii_}U]JLL=z4N /(((((M̙3wwoȐ! bҤIuJ9{+?ySһt۷o7n\cf4׭[m۶ӧOױތXgV㾒H[K~/jZNKK[lBHQ5CNlԱI=R,dx~_AN"1-˷ Rk~=!IζSD1=(u&\.R=`LABϔ1^ BOۻup#d˖?B]:by? BsRfL^{m `>Bh>F%>׼GQY ׋ ϽʴP|E&ce[g֯N/*d%RFD+(K>㿴pI th[J 0S{@s30[ ``kOݻw;vq"D"ٳ'Fϲzv駟nذj߾}ǎN,[SShQ1[bņ ^|ELr<**ƍ4p`}hv1x!C,56mo&y@j{!t* Pz?~\˲E]|yРA8ɩ;;;70PA׮]ѣ\: IDATܹsu ֬Y+ |||ӹs:88޼ypҥiii͙s@B\R⳥b0o.'''((H"p)** 0)b)*ͣ‘B[n Jڵ۸qcݟ,p1=mڴL$o߾=yd+CCC/^h4ŋ~bcc_2TOqƹsYi:%%e֬Y\BQVV6f̘X.$ɼř7g^OlllDDJHJJg#""T*̛6mZPPV~r鑑&!!I&LXJfp$,⃘dV}Roxh`+ׯ_UXXnݺ* 33sΜ9 ?䧯\޽s2lС\ٳg+ʈ9~8WNo("IrÆ xzg)='d2.'NP(^yJf냀 &L0$[kݺ++ H$3f\rֻA{W_T*ZͿrǷbŊp___~^WTiii_}U]gݺu;wTT9SxxxAAAAAAxx}7GGm]` 9sfqH2dB4i|x!ySm۶m۶߾v7%!j.\(D"Q_i - |qy9 ``k0=3k_qOl3Q ꨅLhFZ [ںuk@@T*m׮ƍS#EBCCry@@@fffyyJ*--gKKK]]]Mq-]#--ͼ08-dƉINNjNBosN>=22R$$$DEET$|6111""BRd㊓$~8Vഴ_%߿?--mĉ VllQ>ce7lذw^l EDD[oՐ0 jik`k֬IMMe|j0 BgϞ~~~;vرc_```6LP bgPKH$3f\r8>++$gXXؼyjunn pblllBBڵkgΜi% ~+a`-m֯_!C(I&%')3gvd'~bbbUUwppѣqbxxxAAAAAAxxl5}`@ :::::$˗/_nk.DDrZp%KL2eY Xe&MzWBsB**"""))iĈJ%X!Dd^^o7vXдiӂ>,pӧGFF>|8**ڵk0/"UDDyQYi< B(\/yl'Z:j={BG!F߃:}ܫAw|8jB(jy8=j;~#'Zq{-L<;x$|V4˸B߃Gߓ{5#.H;w|oq2?*\|k1>qB~;3_:=bsi [Ɩѽ ^dSqץSgVWN]s}ġzoB{:pSaȦ\ᆝ/?ڻu*L rS?7&ݽ#.͞ݷwKg!4jqθlqc7‘~Xp!ōʦ\(N^7ou8gٔ+lXEь# у'#ǧ+ģh~je?~O!H<?\0zm"d%㨸RBe2&Cd2\\tW" ?:{wJq@`K򙯽}O *ZWKq;~ qp([)W*hߍ6l4~͛pUg\bOpppS+1yUpwƙC7PE bcc-E͟?_TFDD?{ (IHQ[kժHUTRT\EZD-(Z[g몫JkY`I/M.!@>w>w}坻ϩ/D_A\p>kٰ֬aRSSU+OyƬ̘VcnzQkk5kL}]+% [n 6n8rN:}Gtp'NhӦ|4rܑ#GYw}?ѽƖ 8GM4Ublƈq@5 e5za̙3gTh5k֬Y]7$$ZC~zjŋU+^nݒ_m۶III 0~1f8 ֭վ} ݻw}6_[hըV\SZZzW7$ݻ3f̘1hTQmܸ1&&ͭiӦBp޽Θ1CKQ5*?cnH$qqq 1;8CP׽oR֭[k׎ 7oޘm M5B.YdΜ97oޔJ^^^"H$z{{:UFرcCCCgΜټysooir\Wo^Tlʔ)?wk۷?ydh^!jM})V|?D#]i$}R*!!ƦI&|>Mԙ-UTT`ٳgڵڵk5DI ɴhCLY-AvuرcZ!WZQ ,իܹs/\L^fI֭͛7;p;vLW[h($(99yUc֭gϞ]f;}>V_R-aFx`޼ygϞƙ -΁{999BnݺQcǎ-//9sfZZ[rFGGϞ=sN5װ+0fΜ9sLfʫ+P]P PMgOLLU lܸql!4 6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`l3Pس<ԫ)Ԙb3bNhfR*$?7? ,#go8 ?O)@G=ee#Ӷ튊N<F0 EO=Y`Bԓ #B;pt@VYYQYIg8}@IZHUHPAݒJ9/_>~gQQsG[jmkgg7i҄@iO,U`RSn\Jvz$b~57iА|>@ʼ7o<γr8]gT_H׮^p,,xA`Ba`9|p8:F׹ /^53cGʑnp8kn662RTTV>wO>:I&Vֶ&nСǠ J+M~N>Pwd2YYٻc M5Yt@ͱQrʰZ[[7oт`eqT˕KvQ"$ -,Σ2dQ\R$Vp8(-VS|>Dj W۷$I}ʪ3G'MFVwd2a[79`zTVHRF|U]X|NY uFVYY#43s|}}|)@ۃCO"$*zzz-Z&HFYHR&#e<=n>}c@ hӦil0 O"7jp]{&*)a31{F2RFDYٻ}5n޽"3mۮ.&6HյFFa B5>,4n$I޾뚵>../_{0QhM:y28xE^^6lAW.mgڶ] oEf#Iɓ>ήaR>%rϞ=d)rf>>/\J5vĉ~>ȑ& {{yĉE*xȐ!9TP}X*++|afrGHȚ [ _L<%+u_Uc999999ueETEz:jb` U6k֬{Zn~t̬٫kٳ6E>L}z: /T ݰi)S&X7{Ο"Ϟ=6_5mێ yfqs͜:߹sN$d2R&#++e2Zn˗ƍkkjjشi?Wׄ#T\ (iv11;mמ aÇБO 6ml)ZDmٹiӦ|~T<3+kאsx?Ba-M˦mm޼E rMB66?#@ì(%$$ܺu`۶mӧO700 &}lڴIvp8Ύ;&MԴiS5҃bddꚙI LMM\ɓ'A2鈪ILHHk֬P( Մ@'2#3k;Y AoPH'ImT/4fW^ NA8E=_'<׹CǏ{t_*JR W猍,X@V__5y롿~D"=8.,,[JҬofԟRT,8p`…)NܹscccE"ѕ+Wn߾]N @c|"#{d&T =:thвIc#jA#r3GGGdcxw  - ܶmg?:yh AL۶p8۶n"/\X:$33t7sξ{ ͝y*?xTխƎϟwUWak*q  ]Ӽysjƍ;v4mTE  5H֭[qwϛAõN&HgVVTb[l}sͶ]zƍ͚5[hϧ˖-k֬٨Q&M~aҥ sO}ŋFsN-@DFȬԊ]A lْ妥_"8H|>uJ%6y+WkذlVQ60W`'3ھ=jǷTE$?cǎZ˱*ǩ|wywѶmaÆ 6li{*0AP5PQR0:vz-wwj寧WYYp;UY!J+*s~EBOHHطoرc|"3eeeqqqͣdovƌUQ(===DLoBE$|zz}y7 IDAT,$I酙3gxxK$?Vnzxq͚5{ݳ'FX?2k HTXc{ڷe쫀Gޣ;uӺ̬ /lظȑuԞ_F*H7o.H#"蠓SlND#6zzz~68''/h9j" 2Ujt_bqIIIb1TЯok/^r]T5? 숍K$(_433;{5T3)-}Ro+-3F"bNGΛO]7ѣbXͺ~[n_%: `llEԼX, P3g[?Vk@E{ UGoR;.nϴS 6uw[(ЀÙ15:x>ifMԩ6#O:%ZJlۮ}?k询 IR&dOO>}XOOr?ai)w~%< 0ȍ7n03]턊oв v7pe /\ݸqZkzҥ] TLJIEEEuΎw[>}Ç;<|ӭ[Vڲe˵kڵk߲e+*y .t+W6oLoD~—_~٧ڵk'NmڄЬٳ!Cڶmso^q5m ,Od TbέWZGfzן0av4a֗s/owgWwoKEEE#F"}uS##amv|\MDHkg̪͚1}:ujU>)×BmWTTuɰ0߉oڶ@ [mde$IRJd1lܯyMH $d9 }lmbrd U]Xh]}ZW'TϖzDUm+z {Jus(++lh_D}WĄ1vK YVVC$)<\'z_Yj!en *{$I~P3[˜@HH$uFpj"սP..*25m>;@UWmDKw: Za1W,Б2I2I,*,{r@}S m‚XY)E>>P232:~uF6#ȡ&taU=ǴA;{fJ==v} cX|TVV>KM9xt狀VبxΝ]w@c}f5d2DNq9׹m:C&#ܻ{cӦ(mQX]|p8:³:Zam:[۰ s?v=imb;[[SfFH@i::TtKOOϠi@`ffVQQI(,A&͚(XzKkcV'ycr8Դc/|+90P'U H$ BZA$Iޖ~כ72(Co$Ps}:wd2$ebZk߾sWgz*0d2t? :IٺTrw5pgaJk7hyH444n8ʪU`lcRik({V+;pÔ4נo4K.}Mx$sM5nm@!t앟/L2UV1&y^|Ԧ[7QA-h٪S_;wA;j 4َRRRFcGmL:wG 4FͷV9jU?[ۘu{wk)ib}xƳ3GPuMGĿ{[Zy蘚χ?#9=홚fρeffi܏?z{{kQȧq̙ЯwݻwNNΆ 'ODT$;gU^WOݽ-Q -ZI|[*L7iߎ-[޻womC4ϳrkzK!CSmH"ܿ<B|tRp P+}i>mrssH‡$ɇ$uyMo~uhޡ;wZlٳ[lUZ I$e붭۶4i҄|G$IiӦӦMMKKo1HK6lhfI :}LDzN|KٳghcڶGO[n)levLMM6mگ_㉉6 hѲ|/-Z=whjwBk4FJL<ޣGmto>r?zX^m;~*R M4qvvfۮ]XKI+L<Ʃۢe7p-3gP/^|2ثG-ZvzO^M6MмE~~>g~~~' ^aa +?f$ɖZS tGVV!raRQ!V{;?̨y6fhQIĉ&$xbQIwd:(yee˩ -?~| #We\7~²/^HE%Ap]Ki/A eee6,p{u~|޻Gȑ/7vl]'_]TMEƎ=~}a0 q!Jly0\`_JסC'NP7%$uuW[jM311S={R駾&&&&iS0.X#e,\~u1wgeL&s̼ L WӲ =qh<$KE9=ʃ[ͶdgW7wzyVQ3Zpllٶo߾T:RmԤ;?[6yf͚UVV~Ɣ^ncڶ sʨ;w=z#"<\~iUUk[n]RRbmcWY4FZ|ӻpwwwz-P|Y )(?ߗ7f!%j^ģ'[SĄj|Eo>[[[&Gƍ{$I;8?~[׮'\qäf;nݺ֎@JB쎘+*++e2))hР#E"H$ڸ1ý1n`ĸvmXQQ˗9_ΙYIo޼Y^^^\\p$cǎTԲD"166KĎXX]L:Mqs8OfMȅ 赼VZ9jzXU?(%w \uO<Ʃ8\]]%MK,޼yo-\Xɓ$9;v5&,@S32tٙ>ุ}y_U|… 'N(**.(Jג|&>>> .277(#/f@ĠAlޤN1Q2viS۷obEF>"֯ I&={޵j0sW^5$9{,%ܪ5ۻ'N'wadܯݻ髿[6oY__Q-WTM`6mjee5{Sѽv;˪S5u' g2jvLF]Quqq11iݼqnjw'{ժԂǚrYj#̙u61`xX=?=Kխ wzwuCǏuVF3;RsUw]'020Mh>b$ĘTI{TwVW)upldkk[x~ 0%BJ$eJ eXZId#$AܾcwuUnB RzSx~ 0ꕖWTHLV)#e2\R!6-U`ag6]cA c=zt FN5 C6 XAP}2dVX4U+h| : mYH: 64=~?u>(I }'L`ii9a„fWgNwgΜ0`s*WQmڴI }իWSSSW^vij YݡzCLY-A9;;gff$Bo\.bٳg===⥥Z|D"t 㨪j4Mfk :ŋ A 2$\]])2$+|hԟ;wի|GG .P#Ftԉޗׯ_O8ݻʽ(rmfkk۽{~a˖-vvv? ##C([.f`>PWwh"==} <o&++ʖMƠnVw Ξ=;p@ jrSJYYCAAO>vEDD@ Ptu|֭<ߴՇO?pޟs.)oÉĔ}w.sA%''^Z8577'SN, ePPPttMw|󣢢 㨪jƎlmm;w}\R$ѥK={^$}ξxʊ QFY[[S1JKK###۷o>(00kڴiSL A mڴڵת6njf``ƍӦMWW׳g {WW6mP 222O>>|+-[6~԰ŋ+X^$,,,R)IRԬ_NjD[5J>N&Ir+++^(*FUP3vvZ___U1Z{y]\\/֬Ycii&H߽{4Ԑϐ}ŋnnnI@ (..߂}jAqU\nyy9IB$ɒ(AQSsF@$$)9oo˗_z默qhx3.yg߿RYYd???$SSSnj{K-xj/H| sS2U?w֭[ª*={BUw/Ym*7ngHHȩSS_׮]O߼ycnn^PPK|\.>B5APV+@9w-#O˥^gggsևK˖-̙LGv޽cǎSSS*r &ѯGUP+wDD۶m/^T+g&Nhll痖F{u~٩ !.]4f̘jyr```HHHzzzJJ )Bg' ʫahhHÑ_&PENaΙFo͚5;v7on.f6СC2:hoof#Yo >ԩSZ</44֭[At㙙u,,,CMjj SN۷O*C]`'ئZ :{]U]k{-ܦMѿ7od]vߟ E)r8 ]-s}pџ?qᇤ?}Gllw$Ҙ.]P&&&ʟ"ǎTqTQD"QTTTnݪZGy#Glllf$I]?WLJJ4hIҠ:g.bqdd$tqq{xxxIIInn󕷣Jv%Hbcc5_JӃ|W*sFs{>pT*ݻwMa jWPPVZu%*8m4uttܷoߤIjnnn7n|])5hZ YXX8n87nJ` IDAT"NU꺻#Gro?G]S{A:u4hРS-G1#`\d<?[["c$I///1N&ηg4iiiN|eP:ԧON:̙3TPûqhx3S>OEEE|>?55USٮJ޾}K_]֭[@@@^^)7JX,^x͒%K+hA;5TT~x'Nwg4:XSSN ?[YYY[[T0>>K.vvvqqq6,,O>>z*7}t@G|zT]C87 3F];DT&2RFddB&##mZ޾끯'eee"0*0x":lC6T`lC6T`lg!je3J$eJ ed&$IV?QAA,Ð@knrgΜٴiϭW^Fٳg7oޜ٭[S?|ܹ/_\rذaAϞ=ݻNNN;wlݺ cG_ɸ|e˖L('NXxqffjF=I5gI wݛ4mpԷw`$66͛7ug1neddxyy ˆ]RmfffVVVIϟ>x ׯ3 ++ \\\222l׮]{N>O;]yK-ŋʛUnP9'_PPеkײ2 nܸM7^~P(2d}c޿۷ok%~aРA<O9=Ax~u'T`5dfffiipݻcPtt?ϟ?~TTͥ:uUTՑmΝMKsV֭[w*D2wuq\+++;qĤIݟ={vZԥǏ0\1=ztjjjhh(}qڵϞ=sww4i҉''SuPppӧOtKM>~xȐ!+W"˗/8p`jj[pp0A=zxL&KJJH$III޽+**$T ϟ?SBrrrW^Y |򦦦NNNgΜ!SL?}t+V"˖-?~|jjjXXŋKy*00pƍ1PӲ.`pjv?,  e7,)n:j(`||{~~>ѣWVV7zh*haa!JIJ<OMPMGyyyk׮Uh0{nOL.\HjccIG6mڙ3g) KJJH,))􂕕_fff[(//?}ԩSmmmjuE D"$E"QCyӸ\.=1B!TzxxDGG{zz>x~$2.[5]x{{/_ի/^tssԩ6s|}} tRZZJ7'}wBUY R6n8OOϐSNYTk[EhQ|pΟ$?yPq; (**JNN^z5ui je˖sINN#wޱcGBB) Ap\uvv6UTAm۶]xQD.]Ԩѣ_}T[/H$BOOJ[-q9嵒 dbbBG3ɑChmQQQ9|%Kok.Dww𒒒S۷o/Y1>>_buwJ{U٥A'F߾}H߾}頳3tttܷoߤIoƬ'O ?>}t]'АWaE%a8,+P0C uW!؆ m؆ m؆ m@g Gc/j_Z:@6!15QEvYwww>#vr &XZZN0;s̀x<_NܹsΝS-c#391ʸ|Vի #x/ɩ6m$>>%%% `Μ9Tq-Zv@52;""_驺Iy-:4rH5kFJw… 'OwwwgطgϞwܱ(G0`Ço߾ݾ}{j27^y͛7oNTs&SPmԺ;wΝ;ɓ'ѣj8 p;{ljYQ>}իWSSSW^V5ujv?, RR@Vsvv$I2##ŅNbٳg===cyeDrҥV.(tr5IOd2^e{̓ԩSprr:|pͶPOơ(++#IՕ 5 oDWLvv#:uDݺuùsׯ'N]*! ׭[j4]6zh$Ba~~>Itw\.… tj?lذ$U(uBGFF6M޾}KMfU3ryy95ܞڵkT,cGiiiiiit={tҥk׮Oo *GIP(VeH`;;;lc_~n"I ??n{w#ɣIDQQQrrի)Viw2vrss ԩSnn. =YZZEGG+$?V|>QQQjU vyڴi/_tgϞ 2dt޽{֭o7}tC(zyyM6-""޽{jhzzzׯ_O1lٲǧ-^X6ks` B*$)Jy<;D[5J>0VG޷o_YYY\\YUjvkJZwd::|WlS CIN/֬YciiIL@ D$IDwqbf\.$Ɋ 333*heeELB$ɒ5q}E777 Y.ۯ_'N[`]90:%PXAC߿2TVVfee-YϏ$1c=zRgxO$yꕶZ:S{ϟ?_d_|aUVVRsIOU.[ZZ*TԤ#޽#K>jR~~snFx|uY~ B,{v޽cǎSSS-~rS\.N[l9gΜd:55¢ vQ[-U˩=}}} +WjSpߩ'w}}}X--ZsѢEAAA[CV166&$ȑ#>>> TkLMM`'Nٳ{|U7s*1ԫ:x`bbbثyxxƾ~Νu]HHHAAAiiiLLL.] Xcǎ|52;DnݺU+s嵒 dbbR!Ъ gZҥK:̤y())$I]?"u1{}Tw^?Tˠv1&#=<<$77wTi׮]$66Vͺ4iA'O>|uX,666ő <888xĉԥ޹\?XVVwɯKԷo_ G?~o[nU~iӦ^zё˗/X,oDDDaaaAAADDĈ#w8$[`ƌRp˖-K)B)O5wd[m۶ǍSRRqd޽MF/䃚OiӦ{gŕ=BLf079!dW"A" 1؊IPA$"AVqC# Q(n$°2f7[/bx'tnֻ˻VW]JR&}w1 q7++lNU ]\\vݭ*lWW },kرc>;dr力i ii P- 4Ѩ"4Rȑ#ӦMtvv...hyJaggG4 -A濶Ãh߉6H{XXXPKT;Kr\B lhh{'>Al6 AGGͮ>,|~RRv݃z/--4iOIIw7pp-8qbXXXKK A)ٳgcT*ׯ_| %J,K(~<4!Ж4BhBGg۶m4^D"P(Φݶm8дzyy1L///!!!i?bƏ`p~AAH$D#\f@@*lii ¡ if`u7}djb+B x3gΟ?c|}}^8m`$ɯ:]\Ґ'Ob[?@ ^m LJJzѱ`!p"F zgB  z27@ A30@ cf`@ @ !@4C 06hxayUb`zU1OegV\\b<==/^hӧO9N``L&nwдb6s- ,++srrb2NNNeee4BPG {ZT@ 0D]oP[nyxxŋwvv!<a40Ʊ<.5>X9sϏfDv->## #"" a*jƍ@ DFF̠ˋ~N… ,p8 ((ۘV&NaXyy9s8t#uဪ zf`R4//&,, <~dr\nbbp$--gŊ@nݺhB];t"%sΥQ _ @ JxxEb]Z45*%%''g͚5o߮4iRpp0?$7o޼|q!%%ѣG{4gUVUUU]vO?7UVa|˗WUUUUU`:Rgbaa{ !G*._,$$D*;hZYPPУG0 ,--i  ?.H8TXӧ HZPjkk%ɘ1c$ə3gp8*ˋ%$$ Acϝ;dR?޽[ L<@Df]\\^ w𰲲_NzYh  Fff8]@W FG4R[B[[۲dHdccBu~)777//cǎiw 3<<\$QoLN IDAT- qvv|2MPuj*''u!x㍱c.YI!!!cǎ}7׮]{) JJJ6olnnnnne˖"@S-8p &NX\\+M Y;r䈫_WZP(|M+W))t:P!#m܇a8O0Z{dj j52G$''Z lWWW BDžB!.h;q[YY-^&B=<%Z}ytPbzzzrssc !00055UT&''ȋZ3AAAqqq*l;JJJOK.=qRJ...@pDB}vl@j_t5xdddkkvcrݥl6h@/tuٳgW\/|~EE[bqaaf ix\\\gggdd$MHW'S .((P*?3h\CqB!\4ꫯ@'2L;;-[d2 f͚o߾L, ܹsU*6đF<UWzhZ;K47L}޽ӧ#hS C[θq?UVݺIw0;ðʯ\蟁!˓͛gHF^Ɵ:Ғ'C?77';;ߟ&Z.@ ZPЬ,Cԡi-**j֬Y6m:|\h@cP]r1rr \5r>}JmB ٳg 7)x}N&dٳ...Tzg`]]]Pk?mixvwR|{xxDEE[l_]ށSoH`x~X^ zzzȖhoo/CCK& CuXyP555effXzu,h t(Ϟ= i9#`5g`<5g`P666ΝSTw/{ ƳgZ~ݝɓ'iZVKҔ???RwϞ=NNNo*B?SkUWWvZrJARZZ N...UUU@x-WWW݌n,4Kg^/===`J XpCȗ) VW괇C[}f`zjkkLKKDCysHqwwOOOÇ7lf ʼn'l6MP!Mryjj*hk[KEddg}W_;wnvF2h A555111K,p;;Chބ1HGGǴ4&bq||L&{_|**==~w0 S*cƌQ*;w$ ӧ{zz[2K~3fLMM UP(͕JݻIѣW^AmC5b`h{ +::\kᅲH$k׮MNN633hҒ8uTjR-[|}}Ʀz{{66p8dސbXtieeZnooߵkP(W666Rlp8{坝xA>dЬ;vp8qccUXk/ 9д\Vr力i ii 4""KpA9rȴi,--kZ`Q/ C:(S[[pX0{XXXpokk[t)upp Fjhhpwwxqqq4Cǃ)A>|X $6I&PV~ضm˥DB0;;[aiiZVVƞ={M6`w.4$\f@@=dZZZMB.\.~L!,,.fϞM֞R\~=6lufnPPP D"Qnn޺whh2oH1>}ۛb ?죏>:~8emmr\\WWG:0Cff`u7}dj rr@  6JNN믛ҫ\Ґ'Ob7@ @DD\Mf`a8Fh@ HUH@ cf`@ @ !@4C 06h@ al 0fc$'ퟰ+k1=2h?p8!=<}400dvW\\b<==/^bfܺu ʜLSYYMP!4<^U( QkZ޺uÃf/^偒?B;4Fpb???6-BBB裏8P(P(T7 @ Ko3./F9sp8s%̀*rTدzA__!=$&&rܪ*.8Ri^^^MMMXXx@=a%%%iii>>>+VuDDDD BawڥEJ"##ΝKnMhy`/ZJ,ڵ 2 A𒒓f͚۷oWTTL4)88PPWWכ7o^|yܸq䐒ѣ={`3˗/_^UUUUUVE=3'O|0N@C*._,$$D* l{YPPУG0 ,--i  ?.Hɾ^\\\X,^T 0@ommERgggxxH$Z |e {ժUNNN֭BK#Ǐcǎ]dISS.CBBƎo]ԩSl޼|˖-EEE4CN;Zq &L8W %giiijjjbbbbbbjjd2AG7G-h>  J2 ڵkAbbd2j5Ajb#8O0ZcWrrUvuuP(q\(-ꂶ#|ŋi"xKÃ^6 /]V߿_;/&P,8p'77Woy:FSSSJerr򀍼(5Rf8رd@t'N(JT .LLLT(۷oԯv_~AW*8GFFj7&i]ZZfVii) BWw={v[[[WTT>x@,h֐f8uvvFFF҄zp2ՀR?Ϙ15TJ_(!Eͯ*,, 8q"ɴ۲eL&#b֬YdIII} qBAijg ZYYC y<8/d2l@R۟={Ғj\ j- ~T*4z=|žR~˖-;vvv 2M)$%PuRWooohh~___SSӆ $ Al66==AiNW]uww%/s ,ʼڵK.dą GOu1 ע 2w!h~ 333]]]?~LJX,Voo/A===dKh]pe2b5^Z2L tttnMZkjj?zyY.hϞ= i9#`5g`<5g`P666ΝSTw/{ ƳgZ~ݝɓ'iZVKҔ???RwϞ=NNNo*B?SkUWWvZrJARZZ N...UUU@x-WWW݌n,4Kg Q*`[PWi"(Qff`tiܹ6swwOOO.**3--M$ajjÇǍGd ĉl6&Z SSS'Nدȵ z`БP455Ͼs HZQSSd  ;tZ>p.M-КqttLKK_A,d~@hooR | 0R9fRsNR@/8>}oo/$1cP% \T޽=}ɡ===T#w(А۩-]\\u[6P ?{Dvd333---SNK*nٲ|77!!-!!Oolzq7++Koc M )g\ٹo>W9_A(644lذa BOOɓ'_ti޽@}܄jΝ999ǏMJJ*:mmm_NAojav1{i _lll\nxx8}UZ2((O> Bhyܘl'O45_^^r鳱})S̜9cccD"wҥ֭[,YbggoZzڼy)SFM߲_fI˖- qp8_~%^M;hH }}}3f̠2۶ms玍k͚5>\by{ww7ذpwwommݿ?{>E"QRRRLLPwf̘8R̰iӦ/Ru={7FðnȎA?@  ̑WZFiI6_{uSSSz W+.4ɓ@ gfB30}Ydgᰉ@ ^R @ ͷ+r'>@ K!@4C 06h@ al @ ؠ@ A30 c@ X _ƫKչzf` h`<}400dvW\\b<==/^bfܺu ʜLSYYMP!ԑAjQu;hy@֭[l6{ŝ@-C}2݋d8s挟D!!!?Z}GG(FDD( T*ƍ@ O4iZ>< uDoS$-[p‚ 8@  } ;~/ k`~ D.[UUr۝T*˫ 0$--gŊ@nݺhB#LߠڥEJ"##ΝKB{- 6(-1Lī7@999k֬}vEEŤI uuuy˗Ǎ9%%ѣG{S4-SSS_{5%ً/x<dddZڵk~ѫJzz+監qaqxpppsAqT*KKK=<<4䝝\.l{xxHRJu9OOOhiAhFsGG_Uo{ !wCZ+,,tvvf2G jy@d2{zzqrrBAr_#Z3~~~dwpONN666eee@ X,gg+Wacc;NjɆ .Z[[ ppphhh ~q|@1^P(@1TUU[\]]st ]fkkcrᎨ/ ̜͆93%%ϟ?ooo?z8: p={ʕ+I!Y$̐@݅x\\yL&s̙.\ Xڎg͚d2g͚U__OD&L(**wg&dikm3n\oUn0믿;?֟|fÇ-,,0 |<2 w^]YYYf{ e_|Ejj*Mu4Aѥuҥ1cL>>,,jzEԦMfϞ]WWnڴdW&$$QgggUBF4k`___M0 TAjer<99y޼yTa^^X, uEK#T 2D]C֢f͚iӦ˅Fy4ڵk\.op8@HS^rr \ҭAk>} qBAijg ZYYI!ǓdAd2l@Zww{zxxܸq<\s$kjjڰaD"!6 fڦ2R@{JE]p25ZZjhi[cX&Lp8~ :___&I߬ݻ6lXl6 }}}FiSיNjKT\M]CIze:rԩS 2!xk`;ׯ߆w&8pK8رcW\YYYIJqsss ~z``D"!?C邶#l%ϝ;/;h7|~.:`;#h ԩS/^ę IDAT0c P/G2К111n0SSS 7n]ڇ2eʑ#G?zgϞO4ƈqX̙3z#FbXї.]0￿sNee%]&Y__2SXXK]ꯣ[n԰XCAk ȑ#$""ʕ+]5jɌ駟t|;Q\cߨ/׭[PSS3Tƌi ǎ0dd ԉ'w4===7##C,δ4Hϧ|Bq 6M-Tuؠhk[K HSS>쫯:w܀d jjjbbb,Y$QvvvR* C f~%J,d~@hooRwʔ) .0lѢEGP433#ᅲH$k׮MNN־~Ғ8uTRP(R-[ mmm ~~~ %/4t|ע222h\k‚ ۷mVTT矿_tieeZnooߵkK;h`L>=++KRÆ *M%kyRu&}>Ӂߜ>}ۛb ?3GP߿Bm'l۶‚K^^H$ Խ۶mrUˋdzyyi2␐ 4 n* 7@ bXy3gΟ?c|}}ɌI{[+(-:9fkN~?@W+.4ɓ}@ ğ abbd2^t,a%%%/:a5ϫ"PP4C M@ +@ @ !@4C 06h@ al @ ؠ0Ɠ_׫ ^H7_܎QBl9rd#|]aЀB ٳgmrr LF}3"={h[аNsO>x `{Hj :/ 2rEEE͚5kӦMϟ'\.ӧfa"?ђBZchB M3ft5@*~NϟUXXCoٲe`Swkkk0Tx<4k3PuRQooohh~___SSӆ $ Al66== ujժ͛7kwhKѤ擼ͼ?ȇj>0`1cH$:(p>a8رcW\YYYIJqsss ~z``D"qM4]va;S~~_1kksw}fpgggnT_a)u x r;"""**p `HSN!1P"7|~. &&&- 7n;` cBڄ ðĻw.֭[4//ooÇ% x?sUUՀoiccv>ﬥ4G5L}}9sF]XXT,+::ҥKY[[wܩ$&d2wZ&9 077C>+ɼR_<<<;{1>o{zzzoooFFX,nwQQQmmmiii"SSS>?n8%W('N`4BPG\:q~EUPP[o+jNa.++KReffjT*njT*wI L>=33SR;882X]]7i$r5F3/ ;;ChΧ)Z"##MMM?쳯ܹs@蘖Nb8>>^&=|/B{{{*$!)ZhPU60s ,>Lh3fLMM UP(͕JݻIѣW^Am_GP޽۩-]\\Ed6P ?{Dvd333---SNK*nٲ|w7!!-!!Oolzn7)S;w?>777))&Zط~:UHGBʡZ;vT:wF#&&X$d2slݺuɒ%vvvo&)\t!O?Y[[_p!..NRooo k :/ؓ'O|#`ccrɩF|||yy9˥?)o߾{ʔ)3gز2H4oC Uj.ODDD```ss][l75QQQapp8_~ۇM {BBB__ߌ3m۶ݹsF/8@ա~YÇ+V lXXX߿=u"()))&&NNN3fqȡk1\}8HHH())>uT|||b10>cVV#VsrrO:ewnnnwﶵ5/īL zpҢl&V\tiHGGGSSɓ'ccc1 ="C011p88*/Z'^C !^q02W[Q @ @ ̙Ȍ @ dn@ cf`@ @ !@4C 06h@ al @I/xU@ h3cPg<}400dvW\\b<==/^bfܺu ʜLSYYMP!uPAB UF8N2h[,nnnؠk&W/6… ,p8 ((ۺZT7 @I͇\r„ l6ٳ`9>< eMğ=30鑑s5NL 11VUUqv'JjjjS1 +))IKKYb[.66>&&&""&Z 4<] UɯzA___O'ftΜ9ؠkLW6fddZڵk~)))=*/////onn޳gB󵵵_LJ'IsMMM>p?3]$d0Rtfff!!!RtݥXXX5jԨQ 33Ga&&&YZZD B 9/4'O|_" mmmʒE"?Oب𰲲.*dffف$cB:u* ZYIŻwΙ3bܻwݽ{@ ]#&V P,qq|„ WZB!Bl VW8󭬬/^LK]ZP׎DT\v X+ p߱cGwwwii)ޱcGWWWiiF bV;_~Eu["l߾]Pl۶mѢE`JJJO7oޜ1c| !00055UT&''k_tIV߿?""T* :K.=qRJ...@pB3į\.}}}]]]|>6 sĂӃrj38w\JfuoBŋ\/.!A ˬqBrٳ]ϯ'O8AVXA|ٕ+WlmmI)))J2%%E" J?L@㸸H ikm3n\oUn0믿 fee30 ryrry¼} :VVV5)x*5f_X\[[w^7n;tf`#wnذaٲeڐ 4f!ڴpX;2!:7ZjkS{xxDEEq…O>6-xhٯT LUH\~9>bq5778ncǎ]reee%)ڿ́끁fffƍ4tAa~ŬկFw߭YwvvF/R 0P7 #""jjj T?u %b|7rIII@.k=Aܸqt~Lrȑϟ^ٳg&M1b`#QF1~IW&Y__kkkL&Assٳg]~ɁahKaX[[ۑ#G$IDD+W$Nm sss5vͼ͛7ۇ8;;?y$;;癙[.22G/455mll 3b蟁[Fffddvٙ&055ƍ#[|Bq 6M-Tu婩'NWZv$(`|DZ]VVJFT3FTܹwzL>=33SR;882X]]GhEaggw!Z}] CKddg}W_;w~L,d~@hooOVEN2%77w…-Zѣ666R40ΑҥK+++ju{{]B!Kc}}}tO***JL&C]?Bð o۶?wu8F~&ѣG^:""0D6o޼ov`P(͕Jݻ2mll\nxx89玏///ror۷o2eJ@@̙3066L$O~L2jԨc6QFiQK8G&ߺuݻw322t\fͻ4c ɮ6-ϟe8gϞ cXz['8H48Η_~k. NuM5 IDAT|׿替@loo0/|~QQ%m۶ݹsFoÁ aG77_pğZS\\|)#sss۽{|!P`0/J@ 6JNN믃{ihZqӥ!MMM'O0lG@  ĄP=_2#@ A3002eP"^ t"#AЃf`c!a=@ ؠ@ A30@ cf`@ @ !@4C4Pb#yxUS:~?jAzf`eeeNNNL&ɩ81 ]qq+x"b6s- &ӧ'00P&ABB@@7R}HxI'xy>6T7 @IM])-[vpΜ9fE"QHHx:㏀uuu}  M߼#4zf`֭0NL|.qJyyy555aaa+W’j+V!4\.&&&˺vikȹsҨC# ޏaBUrJJʣG˛٣%9*55^Mŋy<hrrr֬Ys튊I&SC744͛/_7nx6y S>/=30 LLL0 4FD/)))F5j{,((ѣG@MT*]|YHHT*B 9A?~\"дu33KKˇu9֟|Iui]ti̘1'OX T?,\P,h4غu۷GbBd2A2VVV0 BQTTxb@6}Qp\\N\.73xf͚iӦϓB.Sj3Ai={h@+@b}޽7n v5<Α RT`[Rl\j͛5vnq5ajq2Lf^^^`}}}MMM6lH$Al[[t.KvphiRIH>Ғz(Y[[S3xs5)$%PuRZ[W+[eխ30 訬%=k`ׯ_ 433=k]J [ V^jmU|"jU^ъU@˺=BZ|"Z)P! ds:+&@ rof26{LRM@9ru늋ѣGϜ9BD( !Fjkk9MBG޺uٴB'Qz*nq fi{w*{&*_4/۷''' }mذR6$"}E~~~z>22Reee줇yLnnnrYYYų ǥguuJKKy<ɓ')1k L&eddHҨ[n^^^xb6 /q&ׯ#&Nxܹb׿oT㡣i#Fe˖Rvb*++M?t4"+L&s@Mgeeg|4d$ooo"e8LySRR:::RSSb1M"BHLL1+sRϟ rvvKyqJEEE!!!N󋎎6>`GLII1݃X,omm}ƍ7233CCC:ۿNzIV{qU1hY111,kժU_~e~~>9sfRRRw~Q6F__ZZZDDBhŊN4iQAGGGr2yLvjnndvz7U=yߍ.Z'>bccSSSkkkk1V3jnnnNHHƍUΝJMg;e2V IɖӈSiiijFs28o޼p={(nIYl0uVPP ***(Y8ϲvuO┙P$Dbr***BΝ;M'td2ٚ5kvv6H$III }ʂ(UVV&H>5]A%hYӦMrDѣG!!!&Lo6-|m|>̨xb"yLf֭"H$EGG]IݞɠY4^_SOqL >>>6mjll η .$ ˤ9J68]IHHݞ233;FU(7o4̳q>s,N,ӞR V^2DCh|eM¥ ^gXEn߼ޚryuu <3C~=0-U,$A ֠`k5l F WWW8`^f \X[KKKxx{xxxkk͝3g{7^J/]$|~HHȃ`^^ެYf͚G-e"WQ,e)Nwm6f& l;[m?pߒlZ̶/>m[?,j[?ںq6nܰq}u#׮]OY'|bŊo [޽{Ç޽{]]AAAfffiiM/G]x1))$$$dڵDp˖-qqq;v숊ɖ2HYBouʴYd }{8q"44X7Q;S܀2X֣1 t:NjjuGGJRJe{{{;V7=2T$JY!s(((裏?ÂkWw &m/OOOGGwyN0!`0B\.&[ eE3gR)6O>ϒr9aiJJܹs?($G||=Ÿ%?Rx<>PiZǵZmp8W^)))1Z%%%^^^ˋܒ2ۮ^iEG$yxx\&C*/H$歷޺sb+sIRoo﨨B#""ݫVwMɅ5k֜={V¨(oooTz9Rً,M[቉fFep8dÈ Rtj:!!aŊ8+ HaXjj*MMMmooDzp8Dׯ_j?"KmaXaa?n7dUN>ڵk"E"aߴiロV)k 8<<<ȳjuNNʕ+===7mdnn7{`@P8P(C6 PHlii!b,88СCݻ'p8vccc,Xm۶+WC._|900vpC兆d2ooﶶ6r3oP(3úLI$lJES_7zk(F`Ƞ쁝9}qm-+#=nf!LrՊٳg{yyY{48ǏB69rݺudѣgΜqqq!"EEEᎎRݻ4ҼӊBG޺uٴB'fjwm޼okk#xqG1 bp{.q!rDE_(@eddHNK >f۷oONN۰aQQQm~oQUUp#Fn@UYYix]M[{ffK/_Me˖ӷƔ5Cۏ?J\#-駟t<0KKKw[&M+NIIHMM֮.66V&%%%y{{cǒ[,Z}Y>O-e"BHLL1+sRϟ rvv)Ug\&ÞXTTr)??h-O~ȑΔ='Olܸ޸q#::/33344(11,M'NzIV{qU]8a̘1̘1 A??Њ+N:5i$beVLL Zj՗_~O+BI|7]pAɩ0f.]w߅O8jO81m4"gRInSg|lvpppdddTTabyjFsWd|>>>{,$1;uTY9,,ㅅrkWwɓ'/_]"@8ܺu+((dK4HGye)͛g8Uqõ{CCCRRy9YKEEEppP(ܹs,L&[f@ &$))/XhOI$ww\Ǡeq8;w "XUUh"77ŋWWW>Os\ʈU322Mrrss`||<1oxzzH$ڷoi5~IHHFϜ90pΝKGW&ӿ|\.wΜ9yyy4yfffz{{{yy;v0n5CFWׁ)+WDEDD?:0+mk{"0_\N۸q͛ G`j5 )I1۾0Lz^tO^t B +\^]]}…8Jx<^MMի;Ɍv{=0fp6ԡ#m `0:::70 gjЇаco߼>;ufh?TQn_OhZ0g;B$(.Xpd|Lql1&M½yӓ'dѣXFtW~ɍ;Ţ4#H_gV#;6ӸQ Mmj0`;63i F0ї<,k K\PFIJ0`6G).](|t線N!dfN0j2.šK=\lQ2c|^ {`'B悬.yˋG[b{wvvvpp`X _ 3c`kJL&Gu([[[L&6sߙƺ{uuu΍uU䯃ć fL0(|@fIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter-task-menu.png000066400000000000000000002434561351617527000254100ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx𹒻K$$ ! **D# b OzA * "T)!$$r6?Z.ޯnv&{!rC>LBҴiLճBYw *,2"i՝oӦ !DIIe.B(%J)!>ykD9P1?QvznnRTJ(˲e7( 'ycd8j6P(9Z\.dYY[%!$'PJ)!@8A*P!D&#rQe'* Ji6y\V nA4 R*Gg{xgYeY=\P(VITuz1]I)`xP^wXcQJ)d29!\+ޙ-}zފ9^1?19nnnNNNRJ%yP13Y0،e3 ò,q ^dr\TNcxlZyvVV *AY{_V#͵IݿFQ*kX^9V J?bV{Annn~~gxyݻWzuV6?_LWBr P°<%es7\OܬZ>R‶t܉Qz?WR?2ثoVQ;ɇvBrz.SVJCA5]vN*j-|X^8u=zVwSzQp<:*)S~^dY=+$8T˫ZjFl܂BH||lNIIiРJ|Z1]IxT(Oa8hX}tMD0ahaM۫oViA.9m}N8l9(t0J n|{݋ٹZzٸTs'r2R}d2Fb~AN[ۆm=UN;ɏjulRfuL&{?v%-1Иv>&n?ӯMnuBN%M7Jӄ껷mrcX#rgOH`u!t O vv_b^^/r=O (< ^k=qqxNT*qھR!b =Xh))R˗/g˗/RbgfBX FYis]C|]|HW ƿYOѷmծwFЮnB654=GoGYc_.sNjc$dac'yZ]qqRij".$P_s5YZ`R*m8x6FCBȯ粃jd2vٸۄL=!&Y0T?CoHoo'EoXA+ؖ^IWb~MOQxlF)b)RΝro^J^ʐaXJr= ˞F`,xR(ϱArœ]2x <NJ٤xqN|o'fǧ\K)}GZ5\W׷Fo}i&3GBBp8DejԴIq;!0si]I!)]ϣ]66!D'p%W0~+VJ|c7Vr޹|:/35#[ P(01G\< :wl J)b6R$2,2j}fےf<8FOX&ޅx0rnmܸ@(!c䄐b?1Gye*ṧr,Cetq#P{:^zio&5$U+Fmw~WrJ"#!VVʞ)cXYuFG b9!D+j` BlmBo A9Q*nnn*J)Hzw<>P"M39=E%" XF9˖OT9vvzV!Drwyeti<R2 Ki d2f_9%-[J2:noed2Ji<JU^ZnćY׽Zsyqi`\W7%rDZN*YUT BO)A[-QJ8Vϱdݏݧ NOuAxq4lĔ>y+Ajf^xs7>J|b猛.-jhHX={3>EL7;bn<ψF8m>wyu|8Vϑvmlˏ56!MڧcxM2ArqH_yρۛϝs8P&ڳna>_ݼ|T777RY(gS79{TwwwjU:;/^S|^\142BȎ w2>|eĻFd2BtR5k+ 8zUGV tj+$Fƾ}Lmp;CyT*\~9.kւu~=,mJK|J:t1i􄩦[W ${-((jzG2JRVݻE+չKjnՎ;6gbx LآS߂\}A>SoiޏBrvuR; N}QݚF*u|?SP`m}XR2tϒ prrrrXetZ[5! aaa=j,˚E()Q`gfs JTi\)O*tUGs,?f[7lI6aҫI~T^?R*&J[T2\@R)\-0oU!CrٹVZ':Tyyyg&%ǖ*JH8a 8cYG]/>Xkvkvv6qlq^>qU+[/me+@GPz9 !i|3OM\n)+J8"0G]Ț|Sأ?eDj׺+QլkOLoolib/^[/)5)Ɇ9$K5ߖ&Jq) lٺS-ٱ3FU9]pg[o׮SS_(If_mߺu{1ZTT z]jש۷_v槟~Թs@:m>cǎb4m!7gܠz_ôP>a<\o>p-N>ߏ,\0jwU]!)8<0jV)Ezѽ{fgg'%%?Jmn޽{VEj0}ׯk֮Сig>6]P{_~)aևpX^]}EmѣVϵ/˾>{?4'yǎ n?v\δj׃՟^>5kvR_iGEE5iDRjȑ#b={7oV;tp%1q޽ 4P*{333{hzꕕe%lC2fwҥPZݿ\M۷oh4[+U$X"00P.Kw?ع ~m> _ڪ4О}'ݻo>A"#䰭"eʞSҒhJk !񇗷nO^>~]u;f3ڭ-[l~Iv}k`i 3lGgk>tPNN̙3~m1qϞ=QQQYYY z뭷đ#GX"??bY5j٨QYfYI4!>a{#F5jTfffXXXDD~…իW<0٪Lsn۶mtqf3ݝ5knٺն=(;>@T]HJ5l; w_?}ݻ/&JKyy,\^MFGGK?!o Ю}sK֬Y"OMR_f[n)1q!!!...GNLL ! 11::zԩ...SL{h)lCvZxxkxx/"&3&***##zq9/_`[xYb ;vR\BL7dgx ׮}Z~kmv1洝Z !?Z6$f!)X32^$-!?OCP eϒ~|k ~mXۡCZje6]Ru~E#7)fq%Ϟ ;qawoM޽v}aiYz/ɓcpOM\n̙3DcAIJJ7񑑑QQQ˖-N0aΜ9Ǐr )wmِ֬!CLlذaޱr2v#Ftؑ`TaUr<_~Ǐ5;b;;C?Ν;˖.-)-=tڵvQ~XN^xAJ?tp}_y6gt,^z)wp[qËp#^`܅2CDE~7 !ofbٌs̱fKn@BvLwc7P@:ׯ]uwwip0ay___:6d#:CӉo-[vcǎ)j:;;Y֨QCY~Ç7haaaYlD XfϞ}y[F~, r劋V0lT&I}}f~6{F>5$+ NP>t[ukIa?GzR#(7zyѦMJݸI+ح0AD/o;>Hlb|HB'$4(b#34/BYY_ IDAT]vÇ-B[W.kZA$Yp3??yDnXZa#PExydee۷O;ȫQtquzYf C={͜줤1cuBɓrrr,XвeK1qܹFބ-[l޼Yگ_ŋk%KJنDkժRJ]6mڴI&⇱GaUr?22RFFF6o޼H5q„O>vye'N$8j/xGcԏ ExȈ#~P\1~T#hQ=4k4%AӦM quuef޷Z)֧W_}믋 '(!;B,"e05P@o={d.ᇷ~Kĕ6wkftm۶>iasO#nxfqlwE:UP#;rhݺ'N?-EFa.]I-zQ/J>>344UV'Oܾ/!dÆ +VVڪU"##Ŝ7oyٳ$mH/&&FZdĀB۷رc feGVeٳ<~77|a+O'ORnovILfӧ7iTZ\s@ 'Jm%-YÇ"ʌO? FȻo=iu^C`8f3IiUBdT'd;![ .޽[z2w&&DAAM6v}|ZEvܬ݅, ?bM|S_;eBq2a.$|(*7s"vE#Xd,T>wm*Uw8"0G{zJ9 @6Ձ{/f~2Bզuޛ"ٺq)g]hُ|F!+X'%Μ9ճg J)%c9J)£9}I2(<ؾ}k4~ݺu5+ymP 0~„3gL41&&?)4504 !DP[PJ d60.@7a„˗/?vl7WN"r ϲ VjJO>$xzzٳG֋ iݺuyyyݺu0`iu.JXTe~<<tPNN̙3~m1qϞ=QQQYYY z뭷đ#GX"??bY5j٨QYfYI4!>a{#F5jTfffXXXDD~…իW<0٪Ls퉥@id۶mWy0TXn*P(ݻw=:11QL $SNuqq2eJttD ڵkᮮ򋘘2f̘ 4ۓuH<,_|!!!;PUg//'NmZtk&޾}{nݲ<==1111Q<I'Yrwwy_\jXPƍ7m4bĈ͛7߿ r:u)lOXWN#W^=v[oUN͛7wT Ҝ>hܸq鑑QQQ˖-N0aΜ9ǏKCbݻw5k6d"6lX|wll\.';vĈ;v$$$$UnXv8@i맦?~ԨQ6"q2!b?]C'["ח.DAAZ.]I&޸qSN*qƧO&;wQF3f̰ ??9rD&9r5k$ +3PTBoZx===o'B{ѣG-]4<<2j(qQ{K muwYdɌ3.]z퐐1TB6lꫯ!ׯ_5jZ279,,LK._~n"ܸqcĈ...b@Vhmڴ9p۷Ν{h)3Rhg*@СCcbbN8`BH7lؠ r3Zڼyx]Jb"7Ѻue˖9R.9rٲeSrr@UC8꥗^9s&!dƍ7ntvvLΝ;m%ڰaŋ߿oKmڴ12bA #ѣG5 &>TBF ܷn5U|ݰa#Gi߾x;6QNa׾,f[bŊ+l T gt "0!<0GCh0]`@Iݳm>ZϏ ^ P Zk`%UԻPiV 6RO(jH ;f+F~K9 ӭfl*zM˺ P>x+7zg߁Gh PRx,$@y ъܹ3$$DV7h`bL&dJe˖gϞ ~Cfff^4M^} ޽{5k:tɓ&Ji~~~xxxxx͚5QF55kVz P!?5k^xJi&íj:<<֭[6=uT)SDGG\"k׮2 ٨Q#kKLL $%&& >Qd2BѴi 6إB#5@TՖhΒeڙ"UXF)JUMWUED)8ʕ+:uSשFEj׮}=BH|||ڵq شiƎ{iecccGi%s޽#""rsssss#""Œ2oZvɒ%}-N*"D` 5jkXXXnݬd^reRRrr+2DDDܼy͛g.N*6lذaÌ)9k֬o>+Uyyy=zHTX8"0GCh>k◶e{˺ Pĕur/lp&ںn߾ 8"0GCh !p4D` 8"0GCh !p4D` 8"0GCh !p4D`,TT4JR*<%T'9P ]@ oIR(s^8wvI"8"0GCh !pE`;w Q 4ؼy(d2RlٲٳgDÂFoR^"{~k֬;tɓ'MpkRTfEϟf͚^xARoڴpZu떽{P!vji+05G*3|+L&S(M6ݰa]*Ċ矽{6d:=--LJQ^T&E 9mڴcǞ>}eؑ#GZܻw܈w(B6hРQFuJ+W&%%'''X(֤h 6l0DJԬYs߾}VRPeaM|GCh>T)I)I). 8>s*J)%c9tvjӲ#rPBK3 ЖF) @(zQ٪*F+$PB[71?%>x"JСC^~Y ²~ر֋ (+Wؽg3g ?;E\._reΝWZݱcE兄XYHL.\sNF@%'ˇRկǙK7L&+(((4sfFPdNJs1f~횓̤/D`E֧_k׮޾u8(StkŘo/ɓ/u/ԩK̙iBձK?ӭ ]HGCh.$!^|3n|p\˺ /.$%X ɔNʞ=z =H~p̩ulסJom鰎@i;z/)^ۻN5IRrʧ}n%CAܙ3gΜrRN/ ?{w~)f50R9WTy¯;L5TPPJ|J~u  KOO]B3+ t  wRS @c=׮f+L|JT<Ǎ3} ˺`T(_p(R8Ӿ}cnj90Ơ g S0qcǍ?]t1L/n-޿=Q[zQz؍aѥKK3v8c$OGqǟ>}vwwGPQ"#XzQ<{14SN?(c`!Jm6qw)*+=ėʚ(S0 xxwU;u:tHiX1"0zz'NQ&(vqb$|24!YYD:t b1gxmhႎ: 4(33w!*ْ(aPF Ա㢅 !U|< hԩS*< ٽϫ^ #SNhbKj E^^\&3L`w *7YȲSxRW_ FLb JOFzWYwJ4,$@dˊ>jD`:?yX-Z*`Gլ}!!`-yʕ֣Z4lݸ^ۅ]=rBBpczz왲8T2k׮U&9){rlI AYm@YwTg!qd'$d޾|joKĸG/'/?Pxydee۷oΜ9BG z ]H(#B@Y]H(#Bc.$9'>c#2B!7\J naā]H(AbY6)1+YYYeݗrD.WwoҴiݠ '''Ӂ(Q0,2kerZ]ڮ|OKMݻwlǯӪXɓ n@B.VPÓNPi8(܍sԇ<;Q_??K g B5n8//O^ .뾔/NNN 5Q(bNUB鄁 uvq-BQVv:lS7TtXIJJ㏓{ԖdhmOaK,wı#^^|qLJ32 *~SbYNPbHXe|p^G||֩S7Th sԩ~_iݦ3D)5U A  IDAT?~K  /bNѡ tW*.>mڸ..̛7oe[zxxx{{GGG߿c7YYY}iժՍ7nܸѪU>}uV9+.MEX(rrr _vԩXrrϷ-^@yaqZ6X)E6"CĔ۷owyk:ugGLLMM}c`?v;;wT{_(A@y^yu OLLk׮]Jq111Jrҥ#FP*b2GJ5tеk* BvڡCT*+e- T|||׮]j48;vtvvԩS\\T*̙o>1T*K)SJwմiSFYA*;,TOcjܹ3f3fRf?Ȍ{ojשB׮wޕ*4=7J)>5V%N<˯B32jשAiݦyw߾v;k\m۶8tP&,F` l߾#8ii׭[;sL1;NOog|2mAo͛;wG^mBtV?}n/v:/cRA0CBHllR !L֡}ĄYz/zL;sN:O6{u´l5eF?p4sgEQ{e[oŲaۗIJ,˲6/\\\&O,=%'OR&Lܹsnn48&Lx饗233{1a)'˲˖-OķRAez֭[LbڐQqBȸq֭[{رgvgǁg۶m={LHH ܹ_4yL,u}tӵ'MύR ̦Zn}5A0{~ʮ_>!dԏ.]p_X*^OQ(5Zdr᯾g`ѹs6oڨVǏhb1'L$#Hн{,m8?iġC?֭[_~| 0fǐ񵫫ԩ֮UTL2EV?ɘmPƏijc)Fz=2<^;xgvСx5Tz'OܺuéSvܩhN$Ϙ1C 8pСDGGO>=66҃O>]vc 11qРA{쉈Pͳ2{b!-Dύ6MUVΝ;;3w~ۆ4o.vw~ŗm۴ׯﲥKm(+#0Ӌ&}buc&0)׮^[Kc[ oo~ow[j%F`fJB*0-t͛t޽Hd<+ NgQKWe9[nnBMvڼyӠArEvye'N$wquzYfmʀZ+ѨmFFF3://SNu!//7ް,"1I^5~&zKK,5,۩SǕ+WtWtIİB񅋋s||%WW^?{lb셕'NF;wίZ ,T Çu62Af[:vX ֳm62rÇB>|֭-[>NXP:lzm!%߷SpplFE=z<˯һOoIKQRAAq^AͿqL&KX#mz[#u`v ,ԩSXN,y啗?)S 8me_3ꬭ{#4eذagϞX{{={vذa,ucǎ+W#_}5$''_paԨ'990B66>I@ŘC$a1V`Ǐ>}o㏗޽3q2XâEm  4{km߫`ً9:N40 88ð 1Ǎ?bJ9"D ð#V5<;%Z6s3g^-R؝3"S]|TRaiwذN:y?YBќ8~lK_׷rGK,666yw.agϜ~יؗ?{!C_@K;`yL>fMDd0u+̌c׿ϟ?'JV{oe~A4aoӱX&733Di;mI2jUk{{?KHx IP8lӧO XXċ{71c?/*]]o8#|@;Je祸+Dá:?PEȼڰ3!h/0w!>&z-y8o0ϷxpC(ĕ% t1&5 z; E﹃@tS 54ngha{n(cJLzsA**FAS7cAlp;{7,,:8@u M>b%lA"嶵8ZX <@u+"DaF@ i#}Vm-Ù8a"_ \jz@ 6<}?LDxG8pfǫ? 䕣D@ |[PDсl9R(~[P@{ @ XѵMtr`te;f_~[7\>k_0й@/p_  `b'EL>ơ{E-/}0'Kӯ0\~ϟ?Gqѹs50O4OXWW ]D?jbN?~|ᯏ> ġ{e^yaN.ׯ DrEʮM?CHiiiy>wZA10[ !7]2KT Nd&}ci G4+{/O,V\ҥ# yok`=>|# zƌ,myyytGG.\5+f߾K-u.}flk6JED&x~Ȃ!_7oLٹsƌNziD=՛ V{eW`f%Q9x2 O'O8Mb ttt,_ _y\~dכ]W_w{W^}mϞ/|+f&3'qߑ3egAAZ8<=|,"W^}mhKټm-y(??xsFX1nk׮Q^Ulaa1xɓ' lbʫ[y׬%҂}6n4r.D4La+7q' }kN?q}sЃɞ`cРA...vfff% KIlx׶myyy@㩾ӆ W_r&CjZ"v -yuuu`Βohh0aE0r.q׆)^GXxɼ+++M;%Zbo>4uƺ7/@!o?cǞT&ÆhQQ$VVVdRƨ`jK[AXX[[[I;v$G] D^T{m˖]vɁAk׭x"& WțffF}m`/|4k5Qv #_VNMyoq&Q}wukv)Ŕ&L|(痒СaNvz ~IKͳ y7yIpرnn # >|l}Y͞?|p_,7DkʔO"Vyt95}s^u ^}xn oo ð#V5<8+rrq'Ν;nˑor#xmp %];w:;;[ yא׆ ^k{{#,oț1 GRZfff@$S[[{ҲOxQo6|׆ z۰arDZ){Cu~$GG?Y2%׭ 2e/`:9ȭj@ \,%A9d~sԨw?\]ӵ\\]8 J_~q{n; ,Bo`5tذaMMM8KN_Eсwt!>MO&l`*3#}ܹ==Nbfkk{̋^9kS F6eHsL";`6F/GdyذaӦN`qe +v2eVE ,zӰگyee.lw(pi?߲~gǏ&OA &=ա?tM,g%6,͈L0iW'O2D,DNty5/W|okZcO;B?!^؟2kLKk׸\58XlMT׵ZGV"Jҫ{yy%$$* B-39q v/]J1`իWjuSSӱF!Co`[:T.a_k41$~`n[lpQkڴi6~67`z0c=J#D}xq=Ć h2F%}&9y=_ qׇa_~_0XӴF.."^^:3z~ y…~۪"dOjuDGGS0`[|\(;{ üv$')hm:rؑ#Gnذb3~۶;w4h{33@Cw56m?q.ajmk>_VJ0B5v>{&v$';V#~R~P#GIl3-9=o[ǩwwtt;Ç߆uwc6cƌ7 LwNaJllt鲔_O+ s`}|xP_7:{{s=Ǜ#^0`0{%N:2rҥ666u\ +C09~N{H`K☛HNJ D({677g/yOtbs='սދ^wE\zxsxI+0mS'5qs/*(H ftp/\ ^* @ V`@ ;+~ @}N[PW`A u]@ n@ @ L Z!pvE9-Ç[[[S뢈ǣg/_QQ<==|Ģ"@h"R"BmN<) ; tn'Fگ777VPPĦ`P,YDhC\PaP"bRwgGE~ MsA@j$ #=ݴVORT*t哓O>MqիWg͚% BߵkטDPg ȕ+Wϟ/ Rihh诿JiA ]WAgvܹ???999K.bDD… e2َ;XDM ݻwANthV^ØH &%%DbH"B/Sa" 5E/ u;D4$)Dd׮]O>zիWv^v̘1%~G}Qqqqqq%K,Y$B$-[fT@222-[V\\|֭@'5 SXoqr pJH$"+&??x!:;::93Ы<oԩW\a&Б wl;q_~[\t ޽iӦ 0XJJ =+cƌ)))L&ۿJ:pg65ͼynݺ0S,cURRN Qt:u:mH$++EUUU})tSL%A{gT uuu? ݺuV% 8qBzxxЛWp8 --- !!9??heIIIjz̨~TJr@SSS5MJJJ$ȓs9;;"9Kfs&-4H$k׮t=222dXGp8<'''uVRMPv޽ ŋO:hɓ'35ġ|0ռZV 8}vGGG\RǏ;;;?~$B~ff&^|yDD%WYBpsssttܰa Sunܸ1n8n@?mhAI2 ,''G&Q\Q()))snrwwD[l ,"fDDDnn.% {@Y0BT im6&&ߟ=&b* u;á fbŋqU@$555-XYY#r Wp8j66Q],IA.O,P tT L$)  E$47(Y4'SҒ-ټy3֭[we!p?ᴴ#ЕJ%0n{{;<駆!:"q"|X\cB!˓,X$BdrwN8Qрݷ~ęCx{{oʨꅅSL4G,'W`555CjRqŋ b+~~D+>ڊJ"Jv4ԭ<="z#Lb* u;ÁåKZR$brJ,?{ zFɭP yT]o8&!񩬬̜={鵠C~ސ2f9{!kkkb[:I2p8نiQ($Z \AEcB.XСC.ti:JEy '}p@;@mkkq}̚fIܑ#G͛JeZZ-8^ZZt3@c%rssj'[[[322d2j  Ejj*ӏS(ag0SI{ΐ!C?~ =hh 5-- #L'˟* IqttZ[[4qDjBܹp Z[[^Mk e.h=ztB0(((rK6V~a_7n򲴴}6(YD"9馠%A{g8qqq3N;RT"$''?~7zhB D...gΜ7Atyyy|~LBOsp|||B!I@s.4&4iÓ&Mtwwˣ;2,#̶mD"}|:!.^TFfD"HMD˜u,((H$D0P?"?;FnHPZJ* _=sL>occx LAvvvᵵLsrrlmmmll#(Q:uꔭ, ] aUÃ?NXxw.@ ^ B=m0vUn^aK+++O>@ az?OGcD'@+0CB"@C 05h@ aj @ Ԡ@ A+0@ SV`#}//j$;/GE?ѯѳϗdϯyyy|>bQQ@ XhRd6$:э@UPP , r9mHoa:44( 5-Rsa'Nr&LpP̙ciiIXSS3|>~ z+*gΝRtرϟ߱c7| TTTx{{[8 !D~3gθx<''#GN)))д40^r% :X\WW㸋Kyy9)&477fVVAz{xxӓI-ߺukܸq555CP^],WUUuL:u׮]zKMH鈴6)O2t3@R988׃<oԩW\!DӦM`a8yk׮ܺu jYwn^E0ݻ6m0pw3f\.W(FGGٳ(b㭭'N PɦT*}{IA_{Yb@ Xrejj*9s毿:p`|gaF夁_߿dȐ!]鉎NLL,//?uԝ;wM'N? 4- +zƍ_;::N:jO:H~H2 ?}v???moo'Mg) lmmڪP( Ǐ[YYa6w\kkkp*BQ*+VHLL9r$%22rڴi^||͛7qi0;vlĈnfffMM Yûix߻h3222!!$guu#Νsss1b(PQQN:thIIɬY6l"]6((,66v͚57޽;a<)^ɑdBHII;w.ؽu떻H$ڲeP(dYlwnYjٔ( 8pJ_dqx:qNYDXbE{DDDnn.8*H(ɦKv50JEL^x|'D&+++?0Npj5mmmmX,8~wƐa!D"BBwܼy3֭[weBdO'"ii`^Aەdeee{s1rguŊӦMaWVVFEE8^VV6o<@0nܸtpkZP^ &ʲe֯_ CE& RRRd2Yll]g<P6)l޼̙3ו+WfϞѣG+^SL| p8bV,X,CPXT3YSSC?Vѿx̙3E,ĎgyJસJ"Jv4zcEC{]988\tI*JX,|gϞ4A(~3vXGeO܅+0+++333gϞ|rNд4>>>>~Μ9L"])eT_hrz-hi0͟?a۶mgϞ׿o2nfm8{呑͝;?0 SfΝd t4iᑐҢץ^e =ztB0(((uttܻw/:TӉ#])~ݸq۠dCCC``  llldYhN>Jf ;$%] {\\PӾdggKRDLzѣ >,,L$9s G5$ ᔕBvQ^>c 䔖cpbLcj󫯾:xC: 7@z؇DFF1))I$D$Fze==h1 wɅ%:U 4s?zѳKOOJC })w5zhfQF,$$dС!!!.\`6۷qFTjffnkNYZZ>2dȒ%K YDhCzk׮/٫DI<<==}ԩ`z#++8h@qM N:5==]t͟ޥbڴib8>>}1.gee# of~~~?0￟7oaVVVVTT` r333@ٳ|>9''c8/{'^xn^Bܝ1cƨT* ~3fm&ON5,O罼|>=J.>Sɞ19{Ya {X,>~xkk'9T?XYYyzz޹sK}2 gϞ+V+Wɓ'`򵴴| ԐT*}{=Zvٲe[np8A1R%YPT'O\`L&{ALL g}6s_u@֯_PVVcbb (֭2eJYY1 7nܯq)V{ԩFp_aXtttbbbyySdGuuuUU՞={6mD7K/!zAS^^aG %:d֬Y6lڵkbcc׬YέS###Qv+**YJ~U9/0 ad:P(RRRΝ voݺ.l" YDMo߾,ooozp89(PpJDpt|>Edi6&&ߟ=D"'b/I0{ICJ^^^Z6$r9r +++p\Z}EIN׿`ɟB$)  fp8Db D" 655L&+++޳gϝ;wO͛g̘nݺ˗/!/^aiibzw mmmJ%QH9bYl޼̙3*naa)S(' Z'Du;7fe`xM6[[ݻ/qXX8`~ݾ};88xȐ!!!!ĵ=VUUq8! x5kta?~U(RI.E6^%q033NzqΝ;$a4bÒ?e„ X|y[[[nn=8ĔX nٲ/D+Vܼysyyyii)9;`>z(66wyWȘWTTBׯ-_~Ĉ!!!,%W^M?YtzODG ,555;;;77wȑyzRfkkK8|,"ԦѣG[[[=*Hs=4~ \Z} @Doo֌ L"20Bjggj9w)d /Djj۷92a„(NNNZ6==nA&'O\?STTԄ rrro߾M}Ox!N~!gIn-uP IDATH_~1 ŦՍ)~ݸq۠dCCC``  llldYhN>Jަb)ٳgiii>>>bG ,ZHTP׿KRc, rՍh x<[AAP9VPaKL'zM~RT*FGGt:ɧO8iիf BڵkL"OEh?"vh7,o^dIIIJJKK>>ZҥK,޲tҐެfաDI)hITUU9;;_reΜ9@9s&Ǜ5koF1qD0wwwq2ԟ9sXZZ# Z0a… Xܦe򡛂0ٺu|8\wYtkDDL<ڵkL 1Q\\ <==q_tMp߸qe(.8cƌ3g2P? \8y<ԩS\McXJJ =3fLII Pd2U*Ձ|>dW4NGo'GܯcccCzzCG"XYY-Z=f޼ynG)%C{g} %$$djz۶m .$IIIjz}cWDeF4n8&%%4uCKKK^^S'v8NBBBsss眝 yrŖ|@绸aaa'Nh4PtBCCnݪj)z{{?HMa2 LFJ|ۄݻw666@\xS4w}G?mA~wZ@ qq펎rť.?vvvL"OEhΎ9::nذ*B8vƍqƱtt#(BHII;w.ؽu떻H$ڲeP(dYlXb޽W`8-J8RXeژcGDD患Rl Z:wFfkk{1BD`655b r8frB\.%,7r!IP]8tp8Z%J2P#VVV&tD"QSSYx񢇇ECo^cb҃(Jza&#v?aS,b8yreseC\cB!˓,X$B4\dq2***$$IN N2ӭ233===kjjj5.^H0H6rT*mllD8z.$N_|>qJE4.$!@kk+cq;)hIν{fΜSwwR 2 hG%SI"^Zu0*Ϟ=ciX,.]jJ%{R,:1Y[[6)@8p 1iQ(j4V7.Ż,.;vlAA%á=2=ݵ[ܑ#G͛J%A8|,"&aN>|?%0oo֌ L F_ 77WV8qB x BaBHMMzH {5 )hIHssիSRRFlٲV Ü333Zmzz ޽{ZmffQ@G%z!8:::tH߿rFAK2֒dqqqrɓ'+W4kZZhfС&11غ_0tw;W?>>>>>~Μ9$$$ddd$&&>s A(޽[P(/B,3P? ٩MJJ8q"WfΝ߰\| ɏ=:~xPTQQAr࣑\*m8>m4uZihh fLƍ^^^^^^of*4D a'%oMAKBEh dʕ'vw޽j*=zD<_YY K,oܸd78U @ B3\.B\LbiWk 0nׇD"3g@-yѣRT"$''N9z m۶M$g9РxmmŋAu h4k֬H$$** ܿ KLLq<111,,_]ʂD"H$ zp:˵ i[[[,Bt503{X8A0t:ݾ}Ο?۾ el=m0vUn^aK+++O>a\P(LMMm_0CKL^v[Kz37@ A+0@ SV`@ @ L Z!@C 05h@ z79<.t˹'Ǻ(nig/_QQ<==|Ģ"@h"R"Bmx<77c, rՍ*B⒀ ;ɓBe1E) YKTTԡCݜh, @#AKB3 ϏYb2B|0_Yj~T*JWCINN>}4űNW^5kP( ~~~׮]c~.B[@{d2\2nY̽dɒJ6yd WUU3SNjiiڲe )H 5ͥKRܰaڵkU*ڵk?3߳i/PڐBÎݻwW\I(L%"SChf͚UXX{u:Qt('QJB3 gȄ;f^۽K(xRBCCBBBsnnjsYJRΞ=kgg$B4\n ō*@#p˗/Tz3RDdE]RpWTnnn,"ԦOaaVt钯qqqqyV[.PқаL>:44( 5- ϯ^\2gΜZ VVVΜ9͚5~#Ș8q"<[nEd(`9AиQ2 z W$]۷Lhp8)))ƍtP֮]{Ip֭[bwEEa CƇhoo5ĉ\g謕Z9\wYtkDD'OvSCLxxx"OOOǗ.]i&7nܸl2KdǺ(Θ1#??5??̙L"OE&'.Bh}7u+Wt 4m>|8c 7cƌۃIR8aXJJ =DhƌSRRL~Ju>"Bmp8¦xm͛7@Y|9ca0X)J@1Bٽ{./>uF84@1>jqwZ@ qq펎rť.?vvv899=~IiO;;;aS"x[[ۍ7ƍM(д ܵkh=00>ĉDǏLt#(BHII;w.ؽu떻H$ڲeP(d68RbʄW`8-Ce ژcGDD患Rl Z:54[[cǎH$I,477( r9ZDAF414) +ژ.V?DCQjʊ֙,Ӂ֪dϟ?'# ԦM3Ň˫C&Szp8RI/d'lbh@ !+8+/_p8/_6N;vP(r<)) "OE+++BBBD|aa)S,--"GPiCnܪrٳ?>zzz[effzzzj)\x~a"mVU*ݦ^z.$N2.$!@kk+cq;ˊb ZRouI˽{fΜSwwwbRfZ+LѠ D=z1j1u)^-={hbG LM68ϛ7///ŋ~ iH׉|,+օ =;dz9Nvv!vZ lj *v.$K8!Bhǎ[PPjJ%Q#(T XGGK||<ٶmkGGٯV`z~ ;rHBܼys}}R0Ҙ>Ejĉx{{fddd2c 4 -Td 5a "55!%$0ςȦ%YCO!477^:%%eԨQ˖-kmm0933Sզ@:::ݻWfffK'i9ct2QВ}7w 2֒dqqqrɓ'+Wt-J|]v|G$sxb,ܹsgZZڪUtk :su=<<db4P__?g jjj222?9 A(޽[P(/B,3P? ٩MJJ8q"WfΝ4 &?00*4D a'%oMAK2Ugi+W߿ݽ{Up$~ee%ԓR///XqFt@24rNTVV# O첗?Fun\\{T]__&\\\Μ9L^+<<|߾}hZZի?XO◗Đ A@@@~ҡ7dm&}hPqvŠކh45kH$D߅%&&8ԯeeeAAA"H$=xIimDم_VAE|NNMVV!B{zʦMMVVVgϞYYYZ[[_gΜX=jxp qct}?}A \.EFF.]T*/DWigO;4x9{gؒӧOb6=E z.kff& SSS{577OJJB/ Z!%}>uO]]i4Z(h@ }^\aJЛ@ L Z!@C 05h@ aj @ Ԡ@ A+0㰿ɱtno wqQX\6d@\D2 |)ͬWqQLPq^ K%S3qGxAQYY?۽\.K̹9<ýwf0:,L& CBB033O ={ B iɝH![; 3Wvv7f)D: %D[S\4`gѢEv"ߦŵJ@j"+ B AV۷'O,.\Vi9s&22R$GGGߺud0Z#(IOO/**;w ϟ6mZAAL&[~=iɝX\PP Z; 3ׂ VXQRR| D"h?NX2]sΉ'R8j"_ׯ_̌o) 0/4ViJ`dΞ=ƍ.\0`,ofJJJlllAAA^^ޤIyrSrrrn8pر4;۷ϛ7֭[/^;?a„w.RRRnmmM۷$I|||вyUsΝ XrJot6p: tx<^RRT*2dHvv6VUUEFF \(,--mhϜ93i$ ?z3g@aCC'|@@.#G?ޘw'm)W.]yjA ;^_rbcciprr*,,LT*w!XH-u:A: 3$y@j"56#'JO^QQLFRLqפnjUꨜ:u_~DEEYFV^zڴi8xB"""""!!AVYw`Th`YV̼*X%p'"""99Y$%%؁%%%555effW#Jϟ?߿pB(j[vmcc#G<<<0&&f߾}&'' Nb644HR^bkk(J OOO21cx7ց!q.7oU*r<11vdx{{WVVy >rlܸq m2dҥs΅.|ے%Kr9A qwwwa$ܓg$ r, E\\Uܜ Κ5k2cƌh4SN959K;tLF !))߆o|||bW_}%XH,\`dBפIvءT*m5-/_>qDf`dΟ??##nJ4})$9l޼$%b&% 颱W\.Z6X2oJc. fxlٲq}O&bj5S>)H$ًV&|7W\!'NWH<oȑ'- ȐȵBKZqqqXXP(tuuݲe  2zݻ-z ]x1> TC.qrdNT*ٳnj,Dח/Z(** 744|#ɒd|vRu` *5jm@$ sDJ]ׁUUU17jPH8q^dDor`d9/@*JRw! *糄G&)$m>\v-88Ç>>>EEEPh1eib%H:zSSS|YfAD"av`ѣG4k; ~A5?_l4dȐ'OjZBA?U^^ں Jbt`T[i-j*J\ʛ{Yv&G| |yD j5-,Bd>;)H5%;;^ؠAQ-X O'''޽;##cpٲe555 ^ A-_c"mΑ-[TL伄BaFFZ޷oP(d)4j@CCCrr32BZ2icVN,XdeeRZv˖-ȱnnn[njņbV{%pm׮]:.--]a833w}wҥ'OB//͛7t:2lʕrśBwwwY6lǎSNL6m޽C 4FYnuHPPŋ###ߐy<ޱcǔJw}GK13j:''gɒ%ԏЈzN]~`-odu!럻fO?EEE}'III ÇDM6544(oV"j|"#!IfbiiYTTԢZ߿Fٰa)3f_][[K]vmSSS6 50Z CDSL!xH2m"5S[[.`Us^/_f)dq'4]UИ3gNZZvӦM#|f_",]n:? JiCHŁ"$>[v*3b[[[L(w^HH 5̨jjjbbbbCΝ;\>>RXX( N(MwﶷJ222<==ݻ5x`TqFXI|agc HǏ'wRرc>ρ쳼ac_X[[;;;ϝ;~੸xʔ)bX,O2k>iBrK$- ۓիW ;:::88l۶0o<{{{jTWWϘ1dQL`VRQ{{"#; t۷o#(IOO/**;w.cϟ6mZAAL&[~=9o޼[n]xq3f`vz 0 B,S%&+++00J R"D,++#ˋɑsx۠MV{ɠ hY@s#^UU5vhRi&xQQQq̙j(,//&L{."%%eAܾ}; @"Ƿ -, 7Z!+;w +WdM<o펎NNNnСC>>>|>}޽PXQQjkkKN%%%I!CdggCaUUUdd@ ͅR.Eˣ@:xÆ ;~8RmPX&B3KܰaѣGׯ_0xemwhr>?z3g@aVV֨Q|>-]A<~8(((==fK ܳg|%%%%ƍƍ+))#pZ靹U*|V9`0ݻ7 J\]]Z,3xcB[9^^^χBfFyy)-((XٳHx<^͓jo綔+.޼~ RuuuW^/MMMMMMv`IIIxdU*g L;1S5I]v-88Ç>>>EEEPh1eib%H:zSSS|YfAD"av`ѣG4k;0_!CN'9dBaFFZ޷oP(d)4j@CCCrr32BZiO)Ҟ%N,XdeeRZv˖-ȱnnn[nj*0+k.NFDH.d\\\ٻᄏtғ'OB͛ᇪXd+W<3gZXFciih֭[G*;vR5[e9vbiiYTTDk4 6Bss󀀀Yf-\?{줤ٳg+J.t֭+WSN=ydII `ذa;wjiiiÆ cV;wm^j:''gɒ%OЈzNvbRnڢ2sqfnjbNHH>|8`ĉVYjUhhQ,OQQQ|IRREk`^*LRZZJjZ[[S#u8S^[[.+ib,\_|%ZSHL;u+5w\#3gNZZvӦM#| AEEEdҥ]v/J&ҨCRqqq``H$b>]=={1'33 ݻbcc~=&&&F,{zzݹs˓H+Wiؽ{T*MLL$PYYYJ7nd,fo'WTw鎎۶mlmmF[7mD}VH{쁯w ?R\\?f̘bI|HzjƖ0<?cǂ|kd ptttttܱcGGfD 3f\yܹs]O?JRtѢE$2$;FuG]3XIEY; N0Cm߾=33ѣO; 1`8~UN>ct5Oǹiuuu\bS`:kkkHc`0OLL|ڱ`:IBO'a 4:=AzK.\9w`Cb^;&]£P7 `Pk *yO@܁a0S3Nd;p=.乣*Oq]6::ɓ'O؇sDN#ݻw/^|e˖}7wN0A ܻw*[[[|٭\YAVdq$cƌH$V??4崴4'''gg6 6l`oor1(!,<{7ntpppuuNJJrtt2d/?~% }}}/]Ĵ 9??pzС> Yr]```YYiI{\S؅,87xc̘1vyupppll h3p1G8rŋ߿%K>|l2_~9k,jHA0Yhkkkfffbbbbbbff qr"syX@fڧ]GIRRRll,x<''B(diiiJrǎEN֐|xEQuƦ@=W<DFkl LG<O*M>3&,,,//nilʭԩS|>$**j͚5jzӦM#=yHHHPk֬!вal25 FDDD$''k4$50N7jԨ?\KJJjjjtwwo]ȑ#ZR' IDATvڦ,PdeeyzzB}i4___e&Q:nʔ)gΜ?уKcԩب^53-x "..*4fի(1cƁ4ͩSVK[ojB!Annnk֬qss,!Sx=ݻgL9;;|77%K"Âܜ2ͮ5q3Qx S.hG~_}H$b"C02Z&McRm۶I&D2˗O83Ȁ[R)Mj)͛7;::㏤D,ÅZ__/H566RuBDZir7544NgΜ tdDDss3Kj5huh#"˜e&Q~~~2Ǥ|r9u m53-x l^6% 2!HGL~ jriwi.Y~\o'w!KHz|ѢEQQQƄ2999F 3z.$`֭?ؿO>իWÇ={Ν;#Gtpp`"w}@EE-œ3W/_r K,S@W_O_ ̓u 1wMcS׏97+ի1,  +J$;vKKKA=0BR@ \RYYYYY <Q555MMM'Eb,NrG޽lLMMIfffȄpqKJJb||8wܬYӷoٳgO38 Y055_5y2 ,sNQQ3z~iKNN޽{wFFƀHejjj A (**Z|{"46=lٲERdr \ Œ Zo>P-RȒdgggdWRQMq46e466.X ))*11166VR<<g,$+?СCE"є)SJKKIMkkkxjg K <<[ )\_|%ZSHL;u+5wM92gΜ4M͛GDYYYpp0ϟ0aByy93 %ҥK3r[LMdQ!@HrJՆ xBwmC<ʕ+c:555111bСCHLΝ}vu , zbI"ܹs=~d$qLKbJ^Z,3]̥A 3f-:bh>ST*J-ZĬ[ uWSLbxʔ)o6&D]3fmm=.\cՕfff gBa~~~``P(>}B`"#q>""B$EDDo-ܧ󽽽YE ) C~HDe1fM,Zh׮] Jmb:~jS>ycǎG$Gȅyɓ'D" jcgΜDѷn"7 Ρ1]~1y &b2]Ʋd:@A'9~6e0@[9y> srrӋΝCMVPP ֯_"DG:NBBX,.(( ZOa+V())Y|… YE  vkv9qD2T;5o߾_p Y HMcyx.cE%$$׮]w#œ={ߍ7.\0`)))yyy&MzwMݺu뤰hw^( %񒒒R!C*22R Baii)eQ xaÆ?~@|j,jnذѣׯwpp. ?~N3%ȃٳ>qqƕP ّGR VZEs\* VUn0jkk%yyyUUU-NRپ}ÇsǏ㏽ϟd͌9, 1sYYYHeZX4@5O῝R\xՂvnK]z/455555ځ%%%RxNNNP"ҔJ;9ܘ#|NGN:<8N988hMIR;;ӧWTT0cc]фT4SlNׯ_'%QQQk֬QիW6m=..ɓ'mDDDBBZ^fswjTh0ڿ#""5MRRsRu:.\ZTxk׮mll]z3f8pF9uȑ#Y#ANS$Dɓ'%c,,==]& sRRo ⯾J$Yq;0&McRm۶I&D2˗O83Ȁ[R)Mj49yfGGb\ND}}D"BHihh B.?wN ڿe˖7??}4)T5V hnnW#P"f/Z_'|}}u8xu;2'Ң&srrFECxxx``e:±K,iA 7mxvc:PE]C"(4ցQƲ\4Jk};0 SSS|YfAD"av`ѣG4k;0_!CNV&\R.?x`Μ9PNvkZXXܻwh4fݺu(Bxcǎ)]UfIo'EEETZ߿Fٰa)4775k… gNJJ={RRHnݺrʛo9uԓ'O sNV6l0`sN&eVsrr,YB WtZph*x*j֭-*3w|}}m&.YBNs6mjhhhllLIIqrrF:XJx)~:tH$2eJii)imm OLLGܩ ΃rssmmm/_-R)$Ci3gNZZvӦM#|i_",]n:? 5*JiCHŁ"$>`;˞={Fakk㓙 aPSS#===gܹI|ߕ+W4A޽^*&&&v Lh򬬬Kҍ7Oj]鷓իWbtGGGm۶Qcx~~~#ȭ6m;w.,Bܳg|sN1c1cODիWذx7wd= 5;,>S&T8p;Z,>26#ATWWϘ1r2yIRPBf nEV Bm߾=33ѣO; 1`8~UN>c 7f>8xK^x᝘uuu\b߉yN611DO; 1 jDf)dy.-I ρ9 =}V9pA6}(mT5;a#mrkn׺Jˀ"# tX-) tysOh ;^ǿY^o,ZXZ>q% (VV,r|{Au@X̚k a ß0z~^:yтyBhd W!Z0}_D8NBpkfuPpcoW4h6." ^}҃&[; Yt7#-*\E٣ݗ|_uWNn]PR}m(k^tf/"D7&H^ j~u%Zj~oX0Si) }=C+i773%/N f&HةUhx/HMVB·l+6gjͶ,^c57Oݨ tS{gC͆Jceslcn4fljһXvju -/G[oS.ͷn /re}Nmډ;v_p.Z_{yy 2d}5քa!dddmۯR~.,|im73ӛMzXf_F9[t7kP P)(hPרG*oWV8,H!gwH?OW44iMH!r88 /nNa @ShhŏX kWNp@WPal25ҌUw{:Yk&ު3k7]* qWu>=(;ffsYoY&&9,x)W ʳh`=X4PM,>}b}mxҨj>_|/˟0~騛d_znɾM41!s8M&JmoK5goU<`_pnH1^Wi[q IDAT@Bdo{B^pOB5L\S'= U}'[گ 6̧=>>lj/jU X3P[cT;0 G:Mf'.? tCnW6&IO89Eȅ&mfOoԶvx`F̕`@ﲊu͆s[CMd,S@VϺx{Ce}Ġ#% b>i&Hkn~sНGu* YMA)H:ij6X @Ziv ݳOgڷѮV$)US2M(sruy+EK-_ӧn>fo !]/w77RR7TT7m8Xm#k?jPN/ywO_zU߯nxPU1!Poȉ>)lmN[wnژ?gI4ħW'- 6<$ H6IK Ӭg75JR垡^oVÍH]lnf&CܓvxkAF}Ps3wi' jhBn4Ԫ :W|1S59FbQ=7jO3Z̄ ;43Cys:̲G~Q@@J&+ eG:fj|mȍNa' gp’+S u^{wkf/6=Pf)Z_^'_~Yj/}7GafO1ލWVHDuf{v74uz5fnf7ٗGBjEXX6Vgz'M TҚ:fy[^=̨EG𺛛 u~l9wkwieN_Y wi^q;pȦXwFmεE^C ÑBͽX51}릦uuE#}G9]l%7=XFFǝؿbZ?6Yҭ{w333!.^x'ff]]]yyWXwcSIP]y/yb0O ^777=:==}ڴi=z h3aw;q)*,053sau)x r ޸vvqQ޽GӇjGV]\^zzԭ7ɳΫ_׾ݮ܊WR*ƈz_n{G'YfJӑ3c{v܁aKb7!`09^4 7"`5a„ԙ3gsܸq={҄Q͑-@L|{׮\&)Sq?쭰)n^R-_+|eU]x~|H(uk]m@++cQ) cOѣgk]##g,UBɌO_ ߉T`0%W< )`pZڵ+00GB-gAW/7* 1~*+^Zy5=S3`0('!...PAR_:r䈻{z {t񼻧@ z9 ķ'[XX +Wγ5F T԰[y1~|~QYwUoVC.]L',?kȚ~1sE‚|ܾGG^lɓb1@b0g:-[[XXDGG?| 7nhcc &,--?"DwaQ[jۭfd*rJaU" mV ]]֥B!vѢREX"r+g7Md2z>O=}ϛdI2g}_tcEEE+V1b|PTTԍ:hƍ癁,BPC&{A=z4(({-<=q`Ӵi ?>'VWW8qիcǎ[?$ ijzc&jddcΟ?Ŭ,tsj gu=K=0rǏBR`ZVV*n*Xk׮rT >o00 =tZX8p@Vx2H???c̰GwM>h޽8u\-((pssqX,nmm`eeR@8j .%B BPP=|*NU^椹'""bΜ96m*.. .xxxtvvvuu*h666G o!jPJ;M_Fz˗nr_Fz*a---׮]۲e eKKKswwohh`njB&,(("$ ݻw qvv6yE+@hPդ&[!@Ch|>{dMF슻&@Pd7Alll zJ"7I$Z9%H>|bĘQcԽI޽{iiiWZ$hVTT؀O NoU`&B&%%eff߷qEDD455T  "##/_"d6nG5gJJFIMMEJ(hlP-Th,)ʤI&A==ԮkN8?ba#Fnt#Gt999`0\P(߿zjf?&&qppHKK)))[;4'1\x}%<<|͛ pݺuvJNNYԘ0UVZ*11122rŊ:25 ؅}q0wl߾ð)S۷o5ۻuI<^X=d+VvppHMM>Y؍7~$>>[X1 6lMHVKаCve#G NQch5_˗谰={tuu&۷o߸qԩSǎ elbm۶ݮ9hA4' k#G,T 3Q7ma?sr_`Sʔ|)B/neon@@ 8ѱnݺ+Wvwݦ^XUĥOsMkT`轐No@1<oqqq}Y~=i2'VUV <~#ϒO1 1r8qUG1ڏVtQèQ;9 R8.(jw/mm#GNd#X?{9Eh .egeMWF~j5U`a JP/GW_BV`Q lU޺y]ERP!oxs}qtJϮ3c^~E*!XA//OPsIRΞa9z@<9P@ @뉓<Βs@r_]iSh7Z@ ]C y:U~_0nz5z^nt'Jd1jf~m!Cں9a]B m@ "2sbAyѣGͤ5TV 1b_zi? ШգGB>^}Ύso64Q =zAD[O@W!@ T!@5C D ē`Ŋ*X^^\. @.ϟ?^^^B0$$DRy1@HP(уY@U~~ wqqg*^a?~\$,]qф;E}q' _WrDoܸQ&dp=&.u Ə?Uc;sLrrree~EEEYYYUUUk֬Yr%]vҥr|Ν,Bhs C7'+**bq\\\7z0 Om믿FFF[[{ðׯgddQ]qԄ/@׸_1fV޽ֹ]C9::>}ڬu=K=FPTb*tyyy^^^`j j qct] jjj{whC ˫Hz{{x2sc {CCoQ__LjB Gp `555uڴi<0uԳgI]]ݼy,--Ig/^,\]]KKKVMpߵkL&owimm=y(xzzJ$e@M?{s)WWW>p .uvv:nڴiwޥ 8'$$HR|1)@'?\{JTTTف77 \^^ hh܅=NÇg̘эLe^|/*oebrڵ-[ 4hРA\+0Pq8qbee%!9R*$$p|@`zr0@BUee5֤&[cCFateee`+sZW5άR?~LUp႓X BhhhvvN+**rssc`6q|ǎyyyBpǎmmmyyy%Ki۷?55T*x`׭[GD@@ɓ'֬UVAՠ@b[[ۙ3gI!4SwMKK3O@B> {h ^/ѕIʒOGR`ZVV*nJ*6466FFF#Cj:==ߟ[!+f׮]JR>+FZFsWY1XJʊ ^}r:lT`iii MZ.BhsF F?]$C`J h4 j5iUH!Xa늻qVQ7zJEnH$rJ"<|1T+Oi2U5޽{iii. rss {=5&DUZ A%%,YrnL^| iN̜c"&J%%{~~{5w =zW_Ec555AAA493o W^R|$FI,,,bŊÇc邂+KKKf[ObSd?K.U*v  @ zzѿ9x`HHa!!!@xO>dwZ@Tn޼/޸qdݺuÇ_~sHyll+KJJ֮]K?s;---))_6y> Bjʕ˖-svv0!ٳg}Çܾ}SC@P@ 8<(** XxqQQUYYYvv7ˊ vѣG+7SpvvRr|&{7lPZZayP)ʾ"A`P3^&CxA@<>|w!˝Ь, \]]wڥ *jxAAAj0777;DNk.'֭[6lXf '|بP(bccmmm;B*#G$o2cPOmii 6'33&Wݻw?~^Vb lBWWנ poϞ=}ѣ !))رc/RKKСC HuI ϝ;wʕdAմ{W:%,k֬4iRx<̙3}}}aa_ЎU,g,\5zݻ/]߾<9C ޽{'OܶmaC@믿~P@ ]cA  9!d?(OI>`3QrP[^^@X^^% CBBT*ð}9887jmm D 4V...|>%??[ja?~\$,]41axtBsE -쏸8O3 ^qFL& 1^̲ ̙3ɕ~!eeeUUUYfʕ@vڥKVTTr7P!7|qnŸ8X\QQ!mn.!@c駟n۶_$C*(O} ;aׯ_*CbjBwP40$@ <()))))۽{1'e.oVTb*tyyy^^^`j j |ܹٛxA555,hC ˫Hz{{x2!ߡ@oSP A= ;U8N6QNzY 7o%vCCŋkii) vd~Ν;'OOOODe2jA椹>>>'N 8dKZj⁅{7|~Bf⛹7cƌ/rǣy7sL>Oiq<&&F,Ϟ=j֜z0'Q*|򉵵5uyЦ99"hs?ϟ5kֹs瀰[,GGGUTTrwww V\e 6oFk[oZ%$$P8qbee%r]Em."""$ە+W >o00 TCOHgmmMjB56!ǥRUHHH]]7fu:]@@@YYJ֕1MaةǏS:;;/\Vz=銊ܘ&Mp߱cG{{{^^P(ܱcG[[[^^ieɒ%qqqZvOcFM?h@sҬ߾}鞞;BW`cZ8VYePcccdd1f׮]JR>+cԻX[[ZPPN gbqkk++++qVKDGGul.H|S(O]T椹3f?~*ah2+0X U"BZ A#;;y#qJE\pWDDĩSlc@5qW*A(J2W 믿N]H$PuHyQQ̙3iH #bW}W74407iZW.Bjsgggzgb_B0c%4 Aj4*$@ѰNJ ;XW4;aU`M666z^R$ SJ"<|1T+Oi2UIg:;;߻wP2I˱c&* 4`(**JLL 7oKPMjF .@+Cm~~>mA+0hЫAdff8*0w'%%eff;FDD455Tdz AUUU˗/gB{yy9rD9rD*{+gJJFIMM67 999Z6;;[(x  5aJ2))iҤIPia1~Bʘ&t0#Fnt#Gt999C\P(߿zjf?&&qppHKK)))[;Rk. = a/^XҀ&-GGG2D雗X# nRmȐ!VZn]UUU74322 CFFIW>{TG@k1c^9jooA~>bbbbbb͛aXCCÎ;RSSccc=z=o9rv:\':rȔ)SD"ѢEȻ?{{{R3 bҥ͛R . .lii13VSPoBCL!4ԭefWPMaأ)e$33S&IxrSmmqHISSShhX,vrr:u3O_hNrpo i"''ɩd@p]N|3QIz{S5S"{'WVX3Uxɓ':LJ<>ޘo9v3g΄=~]|?H$O)}^H>I[DG ;wlllFg[>|8{aÆIҒjaÆ;v,,,O>9|0( %$$$XZZ[_M<~֭[F e;"DA@<XP`n]zSSU`CD}}=(+*eee7n/+**ػ=zI[ƪCsUUZ'O6/aÆR ; OT{"C k.NG}9s6mRWWLWT˗/?pKOO RCmS nݺaÆ5kְ}' "66,ēU`v}^zIד”JfΜK [bxNkPP޷gϞn{JJJ:vK/2tPy9z]bs]r%YB5mll^}C,0ēbH;@ 'J.?͛`/ /'@ҥK'Lp-^{n;;;ޚ+\|93 0U`k)_ ? @ } :@rP(-//\www@}y ,// !!!*EmN}L&3w"(00PP\C*??Ņ绸x =hǏD*KW4M 0n8R{~ I@;7nd2,<<]DXRRoD"H{Ec,;ԐǟC1Q9s&99?¢5k֬\׮]tҊ \N*6'>|s'+**bq\\:h>m۶믑BPC%\Pa0~~~TehWLo&##uuu 40$@ ޽ðĞWXbŊ>>0rOJJ2!sill8qb7BUTT;}G7nǎ f7Lpq_qMRb1uyyy^^^`j j ڜ|⟳׍a 2 jjjm=C 7V^^^EEEzۛ[! |<4 oF}}}pp0 9w܂9kEVSSSM SN={,͛7Ғаxb@ZZZʴl]d2~sNkkɓ'@S"DEEuoh 5Csc2OL*@H$555T9-ܹC&avv)#;;,9s4F1 `[[|YΝ;PCPpųg& }M0aĉOf Ç{zz5 ؤjǍ/#=K7VQ~/#=ðk׮mٲeРA~ ,!!!,,'NXYY $r|c@ `BJJJ |` `0P =9 XUVVZ[[8nmmMjB56!ǥRUHHH]]7fu:]@@@YYJ֕1MH!Hܮ\B Y6q<< >Zd u-IMT!hdggczP($B DX[[>njkkihFR6uttفU!(8&&&6VsssFFN@̙3gϞ=jZPǓ@֧XVV\.}:*ʄ ղ2WWWXuVH"6|G{5?X 02Z8VYePcccdd1f׮]JR>+c|>߾}+IoU`mmmjAA8wEŭșC;y:g6q\A]&K$BABx*0T 4'͝<&ĤO2$ KuR 4QAV`A0 wܩT* E\\Ўaaa9mSQQ̙3i 5q0 J%9_qooo'LM@v띝ۆ {= n߾  RRR;;;KKKswwohh`nj](((`W i L&kiia 1*$3VTդW!糸GfW4 R4UQ7zJEnH$rJ"<|1T+Oi2UIdTɬ*0 O*H$t@j7.W!܆Bh7A1Ӗ}Vi`^- ĝIII999cǎ%MMM**99@DUUUdd$aL'NЍ[<==SRR4Mjj\.Ff4VB0''Gfgg BoBc0LT&%%M4 !-veLȑ#ȑ#R/#Fnt#Gt999 ˣ W^$iiiz>%%{~~Z@s<è'!|* SHnoow^^]4'&&)&&f޼yD(vޭT*U*՞={$1M(V;f?a0222OnV|X v`hnn7thڢ/4U:ё#GL"-ZD?9SmNٳnܼpB@pE3 c`ƪÃ} -Tb)$݃ P(.]*<==o޼ aH$WLL&JMƍ#%MMMbԩSL&&O]TWW?wC9}vñ.&ĤONj zUUD"ټys/ny9G}{_ჽf53U<~zT*J7l.to߾h"X,-Ztc&0++k„ j ۶mDf" swkkF<Ν uT`5߹ӡbxxl|k15bN.z9GI9Rn}[A}ܭvrq%׮\~WFmL DaixYOrG]]]mmˌSD߼~U׫.]C yz7B>>f #..N,8po[\\\ddd -T5z#4]~=##Ϗ 튩7ddd%%%^zݟ~ibbbO+V0>y&T尟 /{5d'Nhn+cSSSywy죏>z7_ܹ?'55nXs~ ޸0coWTb*tyyy^^^`j j ˫Hz{{ݻw qvv6y +踠޲ f744&+ܹs Lc XMMM6mǣ*L:ٳ@RWW7o`0a0!Pc[cC`q\*ZYY1}c]i]Dʕ+P:hC?~LUp႓X BhhhvvN+**rssc`6q|ǎyyyBpǎmmmyyy%Ki۷w{h5I<1L%KP7R=00&B]N+h IChmD:.111((%$$t$e˖8qB3f ⤦@ ͟?_ B Z[[onooP(hA힐駱aB조D֘&yR6ќ²i&pOHHhooupp bcI5z ̙gZP( P ,++K.>JeBB‚ jYYX,޺uH$bB8p@V% ` @t\fU` 566FFF1c}ڵ999`T*S2ޞ"VFY1XJʊ9gsfǵZ-AeD"YBU`ЩJKhN;yLIhdjI$ 4J$f; u:=p:/ & A'1s45 RIƭh̙T*%  jQ\\xqq11Bg;wT* "..|C0aaa9my!(x{{;aj[6l{q퀀PhggBwXZZ{CCsV@AA"THm.?Z$r`D+̽ 4h4|>=2ƾ02L^ ƦP׫T*ꑔvH$>d1lb(SZLUj5dTɬLfIg=pdP+06??XܨA}Lz3v-33LcPo'dilO*H$t@j7.W!!:5ߍY-&j- WK{3[Ȥ̜cǒˆ&J SZ@Mu{{{4Rh2Mcf hra0222O$Zv̘1:n׮]#[FF)tss׋II= 󋉉innnjj7oa ;vHMM}oOcÄCH$ڽ{RTT{H$4@?8C 9blC^v`0477:/**/V\I4U<#GL"-ZD?9Sm^ZZaiiA͈ . .lii13VqA Y 1{аSR]A5 ҥK͛7C67;N)ɤRi||]]]j\:s-^X$d[navΝzK$Y[[[NR5<Μ9s}by3|PYYVg̘k֬h4'NYfoݺEȽ9w>LVg_mn.!@c%Jt:]aaL&c*2"[cv횟իI999]]]:d ^|I?4'_Q'铠@_;v~Ǐy<^ g'N<}ZVէO4i1Mt%$$V!sv֗.]Z\\VU*ǧMFٳJekk_~E$oAZFѨUJRP4475?~o&*0J%NV|Z&BV7玓ݻw qvv6y +//"^_XX-hLlhhx7냃IMhWP͹sLjjjiȩ6M:ٳ@RWW7o|bĘQcT$ lc2OL*@dV`TV`&ݤ38vkbAhkko,n l>v=薙xff&1246'qR$t: jАBƬӖbj5Rhvȑ _{+0LJJ;v,)hjjRT&L ATUUEFF._EhVsxzzh4T\nnss+PjB!PXaT*&Mz `XW49h9"JY̝#FB7t#GtXR윜L\.V(_z5f888}AKhNb篹@$Odj9:: 4ݫHl7 ݚ5/}2'#FFF`Ș>}:h1ct]vj n \/&% /&&)&&f޼y544ر#55566ѣG\L? g"hJRRٳG"ӄ:5d ~爱] z5 nƿ/Xr%Uj~kii0uEǏ}e9F"y#GL2E$-Zzjs{s477/\P ,\=!0cUZZaiiA>*d1 P(.]*<==o޼2dDGG;MffL&JZ__qƑPXt) f%ruux'>t_@/4'n87dTIމ_]] TH$7o&I0&[輀FXH=mD"ѬYȸeeeM0:==܉/qacceb1\N~zT*J7l.Js mP?ÄC۷-Z$bEܹcLvay!(  hw}#}py;wnnn.hNjBρbxժ0H$(:s`΍XEY&@ z`طo_nn~g?9Os{_,|ή.#lժ%KbFtC2?˖{>n߹]U QݻwanEW!@  wqqx$Lngg'Sacƌqvu_Ù[K@  P@yY@ } @ }`ݻ~[@ }ݴ@Cv !/ kL/\iСXZn{@ npml tSa/X4 .\a{m;rN I%,Ma iRz 2MӦiT&=qVIV(@b)@Z,؉_}ȱ`}?O'#|uկA~7qu7ZLDb!9_e!{?r{>4 ,r1J٢q4 8'"I*]x歞Z6m 獺b~cɲV-9veʖFUgIIDAT,0|222rܹaQ\1aQŢ݃8ׯE"w~]c+w\ozK, 0Hnձc?9rdA3^iߘ_\ S&5$0(mSxP: ~v#o=|:0FǕttv1 ; K1ʶ|jŠ`CD}}'_z\Q'UQ챳m <7bpWk600D)J~hq1|GCCC=o"DԺ?۾#o0b)<(n0Jn Ç}g׮DNuX Gut:mXs!a m ʂ`3::}[&IR4!jX+0*s!(Qۻw޽T5sn ! :·b.$TBQJY5 7ubK/\p{>6Iʼn]mHFj 1ƍ3wc3VKsK˽'3|QX+c+]xVyU,QrSaIDݻK+FBKy[2+<Ocə0uy.K/4T[t$WZmD‹?^"L69ku5[;\$ Wfxou}~X^PWozvneTM$ OOt=<]UCQ%4I%p<-H`osO* x驯u-)IE5ˢ,V[eYR2Z(~qp<[@Ύ;UAJ( ($Brn1e1&ID!f$gn΄p}hE"D)MQ5%EILn!(&ODFA`q#RjF/MxcX%jKS*zI]h("[M>t3iU#",o}kGX[EÖo0>C4M`ztQH3_8:;;].ב_~pX,IAY6_T=)=1/BY"JƗhڿPH$bXDQDXwU67>c>poA)V,ˢ9Lx$<-Yf|IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-filter.png000066400000000000000000002001331351617527000234670ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxyX0" R* kH`Fj7l{SII ԛK\j悚" o3 8<==,sfx3!H@_Pa\kH#ɴtH!h aBdw'''T zR]]]BPt1!IJ!IJ,B}ЌDL4c1o+ZRH$r\"!IEb4AGfyX“\iDAh5!TSG,bYEa042 !DHD PHōXjz=M"H.KRĻ0B`YeY @c(i$Iia/D"X,JU*UJh0t BHo)eK3l44Q&#CS,˲,C"@$Ē13$"enhoF+Lu5*J*,+ ^x-y$l2D$EQ X.M!$Ɍ3\yJ9;;+ OIB ˰,I&.m4hט/՚Jch jvF]Mq+ Di4t4E⩼X"${5juuuiii9s?/((pqqiRmڴt BaXaH4oW%X%i55yrgQa;0ˢˬehl# >]R5=f+Ma`W^mNjZ^$Ri;[BFGީړYT*Jj5N}Tc#$SX?7ݽM6 B,V|ٲKKKtl5:KB͒40)XQcXx30"{$W6E )͆ƊF=\ %wGho}'͌f˰O\zgܬ(etSk*K+*< P(FJ܅f 2 _~KPX_?ĥ\]]U*Ck`44K3ɠ(򞁢(ov?8)4h_tɚ~YXD#36 u@2A~mRҺZVð l{zxU3xnq67TtWmWonj/Aw/ߩ?4YcmD""3*Pg 2*F榒s\DA0#Â={>B t`Cd]BWԆpwGt %Snԙ3gV3gΘ F(0Ag2h=dY$l C4[3;#!$=v !T^$5&7pʺ jOQ2ѹeݟq3ꩇ5e XtݻBU\ZrC_yW8s:zX ϲZãA4c6>fYkZ,"B{x?s%zO"{2ϼOUwl$Ž.*2"\lW_Ͽpg>l`$I<+z"L&RfөzK :g8|0B?2d~ȟq]b C3 K ܊GeY)3hj []Hxs#>C3tq`=u WŲ 2&;˲4ФxK :WZQc0Q;UYnˆUU;Ə\wӗos SER8uRK*qqβFOgwTh{/4X4|l Jqtn!$*i"MrDVʹAoYe I\J˫q,ː&Jo˫j $.4XPuoBݹ׽ "MFdij())4tZY$$)b1ޕϲ,-b1LR0SOQ7zR8pW!.eV'*QO,P Mw[ `Y5J"ښͲ Uի2O'l«6}w41{][x{:EA<ǫG yk7 $B{][x{uvptι'Fxݥo7?n3x:z\UJ xC 4biT]C d.VJ%7^#xz0{,SY]C3 jY'(#]dbVgyeumPn*D &a`\3BJc^ K*ؗ"w؟O\5N3\*r)Rx~8Bh`g^ ˒&t]];z04U]sOo {o"w,5(eN\j*58T=q4z0%(eW6OYT)CW.ɿzorZJPJe1C:K~C&4R2 !I$1C::99oP h4]OiX~'~q4MK$~=4]>vbe)XQeL޼?&y~ö#?ݢbع W_ kjս4kzpYaX)ޠ:IvOg]眽SvVϛD,Z7b)=]I힜 :!bP;FZ~)N\g[׃M}LKIF o7?۵ !Կ7ϵ%ܨHD0 +O;6&{x{6h]JuwuFS7.(+ST*DW3?y돓eeʶm*JZf 遯 9sĉs{C4O)B?8Rx*,h0(҄6BH,HeA#|{nҧ!=6|6TB D3lmhךǓԀ50v><@pkK_,WE) wP,Xܿ{4LȬ*cXs--7tZMY{m4Bnidڼe3FYU.coeeeo3!Cie2=vT0P0"Ox~esV\Zk!“WzΜ6Q4}6l8|6}鱗-G}s^NC{a_ol_ՈbUV3n5jT Ek.[JҎ;nܸݺuc:hq`قPDb2 6DGG(]vqaZO$q)Afggs)frvv媵X vvv5k&8V);W,óS^^>hРoxJKKG!?᏿ϟ8˗Xa/_T?EEݾ"mۻwmvk*+++ g}ɔ)S3Ze6|yzXVrlܸ7',/?wrdAms:vg(B7>$I޹sgݺu8%...==]g"ٿKq}>ֆF>5qh4YYY/Fލ-oʪU8ĉSeę3GUXxC?LL4m۶-/AYVVʝR(!X^jUfff@@@RRcǎ={Y+k[߾}M*KDܟPll+t:Vw%2F: IDATzQ,~YpŊͻuc̲lQQ?ө'DXXϤɓ|_x.}=?}۷  wKֆ}UF1x/SS& BBSɉ16lذwj֭[WTT]RRvZ P(&LT*U߾}m]~C,"##Җ.]նm>dٲeQQQ~~~fW3 ZZfM}^kܹSV Vc5mڴ={ֻ_0< c.X`ذak6Ï'55'<<|тmФFrJNjժW^y2u/-)޽;?d29;;Lϗ,Q6͛ߏX]]]TTSlGh#BmgGtyo$UVa>/َϚLS~K㉱q?A8;g[3+79/;--"+'Eʊʪ$Iq^Z$'A#_VYYo;v,44tj=[fȥl޲e,N,[&{m۶wY9;;LL_ڵOnj1+&ABB\5J×,Y2$Ƴ_O5kVΑ#f2ÓHzbyr>}̆!$ɺt2q *}d3M}@+c ren bƍǏ߂1 qٳ}B?ķ1y83h:30ޯ=}Ӏ'a$y_`?U"G cW,0Uʑk` f`,bBFƵL8yhec⤦nuv4Cm*$@sX|:_3W=UP5->|xذamڴqrr i7(s۳Ú"*V<x3071c [SShѢ 64V/**h4˗/hʛ٣vi)k`o7n\.߳gÇu0 `4nܸAĢE\]]۵kukre6lvqqQ(!!!v§XjH$RRf͚Uk`=Erssg͚e(qۺuk``T*رƍS.tUUUꊊpww*--1b\. <~lYx#oa}yW'СC۷o3|7aaa]tA$yΝu%&&SL tK/gΜϞ=Xi:##c\BQYYk%$$p1 cY$!!!::R*;R!u:]NNɓ'у5'.AZOFGGShڔXk1ي ZktXxBe)\ͅf!6oJc޶mwfee}AAA3gdYAAA4M3 ӣG˗/,,RE.TUU%XS]]>{X[[ !fFaYV(J.2z,T*Xޔ2eƌ:u}lL<I&… ,:;;sc;f;[ek>6F|% ~V^fV)`ܹ͛sa?kY^^^QQQg>rHjj*B(<m\۷C1>YJƮXBiZmL1[" jժ̀ݻ;vbY"ظ1Ykt,&rѣ9b07oӓ/^駟(l2,O͝;>ξwEQW\VhTTFqܹѸr^xXRhh(W=5s$I|||rr2QRgO񄄄իWO6 DFFΞ=[7FY[QОܿXx[wڵpBZ { rkfCk3)S̝;o޽۶mnnn*o߾6_СCNNNaiiiK.j۶|}vYlYTT?`0ꬬ5kkXZvΝ;jY\5`0B1a„6mZϞ=oaVITTTqqqqqqTTNIMM =zR/~A{#x L&wߥ(ׯ9s&>kmO=,f>Nm8RxS[L޼)y~hرMW?IܫWkKEͩ_67 ^P(hH$ܱT*ׯU*U]]WQ۲Zk5Yl*&???++k/ ͈Hhc= Qj}8p'n߾Gr?wNܵkW.]$]pbUUUDDBh46B]gD?)XL1jw9p,Oܹsr|ԨQZoQ(CZƞ={~皚9s8q홙7x뭷pĉ׮][WWf͚X8HlCӴwB_4K1+ey cy&L4iRUUUdd ӧO_~ر\`xuZܰ*Ho. ͛Қ)9۶mkVhjJ~|aǎfZ ݻh4޽O>8SN,^~s60`@vvB[0O;9p OY"{Wu'ۯ S0>Qiseee&MjP=n MkD~l;/j.Ew9p,O!IJ˗N6 =%B--֣~ h` qlڴ[P 68өecǎ⠠ nGEEK/$^z%nH0Q!F)zyYu.>>?N H}t~eeeȰ >0Pֹ$;w[.11TBBBttteeT*NjSRRBtFFqJ\\\zzN9y$W_NJJzwmd@sQTf=:c BGq?~B9rmԼ{n|ݡCĉ'&&&Θ1ڵk7n26죏>.Z30o8cb,.al䌍]bNj\V HJJ2Xb޼ynݲ 50BCCSRRFʕ+ٳgk4qƙQ*yyyh4T*8w\.C\\D"ONN޽{7ꫯ.]4"" ڵkwܩVM&Z[[>zh"ӦMٳ''Ųeˢp) &ˎ;vÆ Ϸ @e)QX{7˦nޔm߾C8(x%זߋh󳲲/^^s̍`5_x2 /LjZ Lc^ګW/ggYfqKJAZO$q)Afggs)frvv*\hkv*--1b\. <~xUUZg+**ݫ̲q͙ՌŁ!n(J;vq&.>>$IL& .fV30˲3f߿ԩS(#Gcb7ǹs͛'šq|T*wb NjY]PP beNePյ޽{رcǎ!jժ̀G6cBBBV^m4SRR1*h4Ν;b4W\ /Vddٳ5MaaqpbBBBJJիMf# R$I|||rrݻ˱g`k֬TF& YlYTT?`0ꬬ5kVjjjmmOxxѣqbTTTqqqqqqTTlMֳgOaaa b„ E 2(ʽxe7c⤦nucǎmL&ڵkmv);Xxj5+@b'k4ŋ#$-7tڵ-[Z:8 і` 4ΞA6?3Cc)Ɯ.Z:%ϯ ,^C?~=!Yg=_tHM= g0 1ABϔ1] BO;ytp#dVBh=\ោ!tuF`-Yf|f= `>7#yJ= ЩвGQ~ iЋ ϽN-9e>?\A|-3/*d%?P#} 8v'8Y.w/-n֩Ccrweٺ&Ui lٲe6lxd2ׯ7v1ZjӀ؅ ¬5L6m h&y@j{!w* P=zT˲E]t)$$m۶>f^ګW/ggYfq4AZO$q)Afggs)frvv*\hkv,'77788RlEE{UU,+(( H$\wJKKG!?.خ"֢2* !u@TڱcǍ7dӀ?)S?~$ɛ7oN8F#F,X@j DF "KHHx+++R)?gϞe?dY錌ӧsy Eeek%$yΝu%&&Z6gYOBBBtt4ZNKKgSSSj,2eJhhN~rԩ111Z6%%Yf̊XJf˨p$,⃸tN} Ҁo`I&9;;GFFȼnݺ"oooook>v?>sLBGӓ===ݻu&Ɇ1c._؛3gJ.((0kKGrSE Z >|O>d\}?^P9e|E &N8cƌk׮hlܸqƍ3KVk/5J~WÇ7... * X,씝ǎ,(kٜwe\^("m BhժU7oܽ{wRR_a`!!!W6)))FJe4ΝOOII1+W|iKP.1!!!%%eӦMdĥDFFΞ=[r_vmJ0 R$I|||rrݻmw{f͚LZhW-[O7 j:++k͚5%XڵkwܩVs⨨(1XZ~CŰZѣGh#X2*lMֳgOҀ ń ૯iQ˗/LCO% IDATk'];"MиƎm8 kh,[w!pH-6cj xZ9 l‹whN-wp=_`<6i1 xZ[k}`4VsOB+͉鬯~wT%@h%k`ᅢoI3niMz?!`JxhOnZ}rUu֐ێH!>x Vpsgnj{=tOM~ެ0͏$?mB&jTma4 Ưvw\jR.ʝ{ F+4+zsjyX:xI^>\!9L7#xe }sZ Q:hA]EW񑓳 Lׂk`\>ZT78еڔW?G^2\*|!"Wl9 d {nxowlpp;:RS ef`FոC+:K;s߹MN?- 4#<"J`6Lg `# {Թs9GB$MT(|q=~^}Tc|{Hl'qIX~.ۑ?*QhwgBj"~Q f3߲0QT:>T"#wR$ xdd'>wP1RH}q[5xO"&V~+|>F<[g]9oׅpG f3߲0iDһw~1\" >VpIhZp *YM Zl9+s.W%NZDŦ\Po6910:R%RܻgQb_k2&a#p50Ф50 FDhh];njHe!HN0 &H$xغoH" !)`4H f`?M4M bOFզߟ$ a~h ;v`0azԔ=K2vjدT~iDvc"(88[8`\QQS]w-,0Zf^رc[i uuu WII{I&~m۶DrSNAAArmn۷oo8[_^4i… %K/T* JR_QQQTTT޽wvZL6mڴ e۴i? jۃ>}9s֮]jO~3gΘLp<=k/ߵ7o1|}K}Yn]6ms1^#Mm} ? "())iԨQ:uruuE}cƌɹ|믿đS+d3gJ$\wz|뭷^xgg疎p<}]ۺuk``T*رƍSAX^É8%77788y֬Y*Z]QQVTTWUU %bѢEڵ‰qJii#ry``5cVZT5[Fec@,bJh/#""ϟot C贴9s RSSj`YIwnj2eJhhe23uԘ^zղ]3E2*"lF\. oeͶ:C!^<%.^ĢOCYcm1jԾ={FBڷ!gĨ>dϡ  CBzXPhWOcZ?Uۑcm8griRpJ>+/f6mǐQ^)Ҥn= .>o1>qBvO+z9Ϥgm/i[.MC&&! }㜊s//Θ̵?뿲6w4#gޯ/9?!ܰ=w8k'y)_a[>Źc3&a )z4st Fs3!d؃;peq/⋹SW,_Y}3Ue~ĕ,ע(s8$ABÉ) (}~{\ɲOpYv{N[.`~}6A8e38* ~~g /.eu"<!.H~9Ϥg˝R" o~8[A_j~)Sa/!./}6+q9@ 7\ۓ̺[5_fs3 Wz-!W- ?W-J8'NLLL1cƵk5w֭L&:th~~>N'p/о}ƏP(FyukYJfвZ lժUIII1116rƮXBiZ B{߱cǎ;mU(!X%Z^cYeYfO,Fef˨вZ ,..N"'''޽'*ʼ}z= ФT۶m[DDs-bÇmٲoQo...d$ñ~Tի͛7=<}P( "ٳgd;BiE-,\s̉S]MQVJJJIѣGBB4ٳge CQAKW!ܼqkY`A'gf?|y9 4}TSR^G4+:dddotѣ# ,UHh:rW!*$4\ $H1zz>`JL*J,B.C<ի?~/((hXt~mlB!D&*RnݼJvF$b744lٲ%k4Cʜo>|*s8HWvuD s8 rp:u<عUCP+lT`ڵ8O.\OO)N~!aRsSQY_>BбCCî[:u10DRb6\./)yZ بOG|fiiX>,]^^^?iNe2Q5#$_ :eޅ_XTP^^ QuNl|^MݥjjCPslT`633k# I$9N׮8۷nN8 XYh2n;:`zTVIRN4DUWۀW"_: Zak#4y^^>n45á?'J|V==ڶ#H|tH.$)_r%%޿ ##c@5 P h߾}C H&Vp8=?.)*b30͚zFrRNDIG{˽E3б>n[OeZpÆ #~вVh&'6$ܹ>(DwۥeeAx~:`4Z/Ĭ\*''G:tw6P.vhڡY.|ظ"$H9IɧEn}OyD yy!.N®|4#.oNn`ѳgܟy„ K]aÆQlXf`Pſ\ABbAUVVjPVtrǺu= (|9y 4RƲcʨ:@.lժU>}k)_g _~bч$ꚉݼyf;.vB4:\|yĈ N)((6lX>}} 6LZQQ!TOcI,:_8p@CGB[h1y򤴴4:7֣gjy`g.L;t߾};qyv޽WWINr9)Jr-իw05m٢ӧvuIP͡i{ݡAG0ԙ'A۷YV6/_{g-Z Cgd #0?|KqSiaa;D6]YYY_z^A˨(保>}*11`׮]SN500 '}lѢĉp8'NآE }%*##ťe˖trөF`SSS.Ca``@ww}GNӧmllZj%:T$D&@72#3qcyˋ/ IьE󑙦G ܼ<.ܼ< O__dߩG}u'{j+0Lv y&2rʕ++FJK}n`K.͛6_B Iҧ-[׉'^&>']]TsHDZZ? %KR4uyEymݺzz;$%%9::/]uȟ..K-rd2jY|+*+khhի#ooɓ' r-8cc ߱_pqM>z$H:4??ߟ^<##cK,Ow,//Jǎ[h@J 3gNddD"駟ܹS\&FFL9zl2-~T=6[O-hydV]_~I!ϝ='Ο;BHDv|2K爠0ĴCGciikN凸kץS޽{pt˖-Ιj/_<A %p /;1Qϟ7oϽ{={fڟspP& 9$bÆ[oJܿE,^e˖^^#ϘQ <вeKs{e/j#Ke2zyu؎;ܹcoo_-4yׯ_?v|uɓ'[jxbPH/_UVcƌ8qv._lٲ4W_}d;;QFEEE 72GfuGپT=zҍZ<2֦M.zUVe2@ha$I{Ν,Z(;^GJk?D>|)SݧU1&A]9f>aZ:wZ*++9NYY2* +*u~P ff?}#?jSҿXRRrСsRgkr}O^DdRzE&*2It#0s̈77WLֹG+v߼ySzzիWׯ_C5J}bj@Ȭ; Qcc ѣ>}̙CmN:bf2u2lsHhgg%GFҍ IDAT fx$u31mTZTTtibLc899ٻfdһwgd̛?jwpp)/"B2%Nj\RRjG3:ŒC͞[\,qttbɸqSDQITz̙ss9sF*j.Q;ܹK޽899Q `llAԺT*ݸq#p ͛ϙ9sMW^VBJ%@-72%'556C2 /L9zXW`p8ӧO۰~C=4Ӷ[~ݔ)_ۤ ݸ%dsŋbqBBɎwusgy `}+'Ir\.?IC*-~IzzzT{-?uԐ!z_#}1 9T-֭[<.>Ng \pѢ.|_89:ͭ? ۶mSqe˺Y{$U* zgAAAnne=[vǏջg^?o۵kDرƍ;iӖj ~),,ވٳghGu 0ʺ];B o߾Æ С~{I^j uGoiT,z-]# 9{}O>^#?ͣ?GI 3S3?u|ܧD&8xokl5m2ׯ rڙ98ʩKpz"JϞ9=k_j]bcAAcḃ~C#?ȸ9$j}{4XdQ,X.eVkJʂ7]qC}odpT 222bbbߴCG===H":F$DݬR; .+)ldwBj$!o$ILjAo&'6==={תŝ`Bl72hj[H4M^%%%F_*&&&U]uXh>h*~t$R'kFoϒT I %$AD6mfBN \"4tFpj"5CԴC|4OF>6DUWmrҐO=t: ZaW*0*9I2I,Ͽw﮻GCL{[mA~^ :VVw搨}b*ŋq: Zav{wb_-0s8.Cw)y͐==ܜ5\zzHTݢۀ'ϟ?wf:ZaCz\͛= ر%4Lãr\&W}NϞNdߡ~Jܜܻۧ_-Z"QuE) up:uUj߅gP$xJDa?z- [2":X\7 q\zό075߯@(T}Lh:[Br9I$AVW$Iwo޼p˰%O2T`#f?\2h}7+ʊ \.IR.Je===333 \Q90T`WT$5→7s`߳wt.gR)5UM2:S:u PWHlٲewp +WQXQDU[P]ltpn`Hk7"ܹK-{tIY['PH};yGY3gha=z~qѸƭ?hӶ wэr8jJi-7GۛqU-qZQCզ:5O9k0Cхlp՝$+W:uw@{\jρoիla\tiÆQQ ںuۤIKRita9srOꟚ{W,>hӖ^&I]N2=gDmڴ_v9L8u\iR$ܻbL&{1mTB2W:R|{Nli?ID4#Io ll^e!Zhعs?hӶgwM۽{Mz//5R $;wܵP(FD&I-Z)[p|Ц֭x]D~{)$dyBK_}٨험W_xhs6 M[Q>hʕ+ZAk֮534ށkO~;wO~&~^999q=sl=ǮNirԂ}]`-$%JyV`S6ml[ė.]|L>M7n׿zںn7ͥkyҐgƤ$٦m;jŋqW c G%ECuf9n5ƍ+)*0a™ӧ^ IQwd:|$%%9:82IT*=zh׮]֩pCL&KydiӧWTTldԣ?l' ʤ-\-U搏?^h1rG\WJ\.#y-5T`zA$Y,)?'NF:a[x#{GgτugFQ+5۶3!XYY޵kРAT#uLQS;mg_Z)޴CQaA?▔PFE999wKyrYww͛W^ŋJz U۶I?۵kWTTdieך\I#~-_āxypuu{ 6BNir;[ZZ& ߾fC8E'=:u [ $ӳwp8z䈵u{R~$ɞz?G\9А*֩sg))ڵ+,,a0#˩+VUVVrR.Wx+W-((Ȉ &_F-thF-Bj"njS߻W%:H lmmoܸ:DuܜJ]ڶmkSSS rss۵k)mSHL$Ν;rv<}7_朹{w2Lh!r\j u{T4q6nؐC\ܦM}}oJA$?wΒK޽+_@}Ȑ!۶J$Dm[kuc8tq-((ի~~J8)!!ӝ;wڍOjY&K!!ڏKHTiFZL:uuqqqt/kV;N TwJ>_ݽ{7`iS6HHyV`SqR9::ݻO*-[$,lǞ/ZT$9s###f͜Q݀wҐguޞ>8885׿U]ȸ  ruZגb$^^^eee-N077QlXG^A 2dGvmvqFAUo]gfffVTWOȖ->244׷Q 3gRZZ_k׮?_$;Kq (++R > O42W޽? ;wvȑeeecƎr MƎfZhѵkW_YSLgAi:S7EU'ն[f߳G.SWTLLڵnmܣG \f-5n Z/za˖?9;wիWyGh>ǦϞ;:2yctQ~hɤQTT^^8 `Mu6x PK? >(Yr"'UjhTC#.ں1`>$cG#d%Iry%IrRZ^Id-$Aܹs[;*0]>jhTC:;`>$UTI\^)'rLV!-0ߓU`"q76<\S$Ө;!iQ߈ :U`*0)W`gO~ qO}Z'Q8N v PỐ@xYYY 4ΝVu(Wqۻ?ҥK...`С7oޤccc]]]BWRRxGGG>x10^vvvt{RRP(2eJqqM#kc܀C?MMr2x vƌ^Ҽ\>n8:iqqqF fzed˖- ,//6F9cXOT~E"QrrH$ھ}{YsWBͳR<+_=ADAAG֮]O\eggѥK5g<"00p޽JsLiiٳgO411ظwGBsAPZZ?@o<+++33s޽k׮ZVXaggFwYrرc={a".AdlڴiܹKHHGW^=|?VTS\]pÇfff.]oBj궦:aΜ9sē'O+NѦmƌO>MLLT}H"߿1uV///Ƈx*4ˮ]1fKK: PW{ 4$I (5J$;w3Fɓԟcǎ=rHIIɡCƎK52fUC 9rԋGGGСCnnnT,֯_oaaQTrw^vٙ:wED[ڵ+}Bq.\.$Ɋ eX,.**"I3JiPǘ΍&oݺu+V~:r!CTVVrWWה1NWW׷oR-gLkt 7npss+//ρ ݻ[XX 6L饤hΜ9+WTÃzYՒ@ dԲL&|Z;u-s/w}bǷi1=rgffrևMK6m=zDR(<[v!e˖Ap8e~UuPǘ΍&oDaaaA888^|9>>^,[ZZ2ƈqYbȑ#tKFFFRRRJJ@ 8v옺/^:b/mۖo΁|H3>_]GΝ͙&1U_EYZZZGA6z֭+..ÿs) Ν+++;{P(8E"ѣZ+rww?uTiiSHLII 6mA/\0djA122RwA*KP!""BO7o.**Ξ7ovTT2,22R^4i@Q͞jVu+00|Wk֬ƀ]vEDD,X@jT'LIIܹsw+J0Eu۷OpݺuQQQ\=… widdn,9rdHHH~~~^^^HHȨQU|Mu]%Kؤ-]{b]fС{NLL?uԾ}R1P ؝ SN,ӧOwssc|V^=mڴk׎nܼysBBH$lݺ7n'|.U r劭m;8 =ey~n4z;vH$Zh];88,jT'̊+L2dȐ>l̘1+WT\_mxTTT.tD2#IJ+\.r9Y&IŖmZ޹sI111ԗ~DИx>h֬Y=ݻ;wL5FFFZ[[M"˩Ge2ـ222Bo߾ ο A+Vpŋ/_N("9S߻ |}}7nŋsgbf̘DՇ$oqqhh!x* nFT`z3V\~ԩK.\jU```jj%KVZU'Q-s̩vzUVV ǏWK.CCC:ԣG }}} kw &lذaڵiii,\Df4`cAؽ{\.'Ig\.\haa1eʔ/^a``o߾իWZj߾}IAAAWBa=s|r77O>DY*,zF\.uj!333h6m=zD>^,[ZZ2ƈ$ɇRȜ¼>*۬8;;#G-III)))رc:^xȑ#i*$s`D"##KKK\]]{uGDDR_s sΕ={V(jq D" ѣG"W~ԩSNYYYѫ$4m4 9W_}f͚Ӏf2]}4-ƹk׮ hXM͛͛GDIIܹsw+J722ze.Znݾ} ׭[K鴖/ SJ̒%K|}}mll좢{8C8x֭[ ۷/gZnlٲUVⰰ0 262D=-[TjgҥK̙qFX{nG}4{qiqjW^cǎ믿Ahhh L5όӠ ppp011166Y-֭[-[֯_?333C+V2eʐ!C^rL>}[iii9ꫯmۦr֬Yx#Q\h>O4zAj ***:hvroczmآ嗻w3}VAAAFFFLL &r 'P~@|WT?XSP@z tB P P P_K;PǏ[ZZ*M {TSSbcc]]]BWRR;1 w%@0tЛ7oRr劣#wttr XXXmaa]TTq U+::ܜnOJJrww SL)..ְfh$_یӀ1Qy~n4F2lٲeց Q[-kg-?[XXƍ&Ln8z8**.""ɓ'^^^gf'b'OLII?ȘŋP۷oD"h"_u!^ϟ?zc233EM4)99uǎ֬2fHKF5Qy~n4Fw~uBBBBBBff={4ӽ{mqlwqqy;wW9s$''߿رorrrrrYf͚U 8u ^`qݞ={N:PRnhhH؝q ]qzM^ RbHQrN*ƺ++=>>^&CI|⅃FƁM{}׮]Sj%%%$I8::jX6 *߿?S\_~qqqTKffQtB͛ &߻wOu.\.w׮]ֽ{|;lllzS+xM,oڴ6;8 Ø΍&i&X v)sPӰAyH-ddd >1?Um}r!!!"à ?OnܸA5xbС"($$D5'NNNrvvNNN\\\H[v-Ik֬3gG72ɸqֲ痖Reeeԫ^",\F1N\.?qℛIiiiT7oL366466v ϻj>mmmwߪ۠dLw)JΘ:$߿ߧO7ohNzzz|OOTٴiS׮]꿣r%'=CQA66V(n۶ݻw('Nܾ}{YY֭[k3c\T|U6xĉ)M9oooPӰA g֭eee[l4iը:oݽ{T*ݽ{CӧΝ;Rixx8SNpT*矝HwqzM@@ѣe2P($Ip[n0`@QQR`֬Q5Nug-===WRRRTTFΝh"CbHJ;w˗/ Du{jfQ= ;w|KHO1cwƄ(Zf;c$=uj@DT?JΝ;ǏM6,W`'OtuuU" $IDs1c(6*jرG)))9tرc24rHu1fCϏ~C+ܿ"h2m"r޽v횳3u>+& еkWŦtڸj.[VVFdEE2],SEEE5Q/D3*0EU>_UM)T)'HOYqH$),,QR{*H0?cu9H$^bmrB*$N|u.{uY#cg-={6n8PاOHLLnݔNZeet3fj2};vH$EEE۷oWgR~TwqߓUuW$YZZᡸ#JΘ9s\q8RdSe\.* y>}Դ.6Jmڴ{ݢx{{<|jr^""33jhT7A:tXdoVzx &RyfZZ5k֭[֭KKKKII!IjSBÇP#+.Alْ LR{^2JbsuFsPU'O(c8jAU5t/ f8΋/sSAȳg|>L}8?"'f7ecnΟ?#gmm9Z}}}>aÆ~Z0w6mڴm_OW9K =55uĈ/^l۶-Ҿ3ŋGV @5u> |@Xxx_}933:Zn]^^^qqqDD-Ș+Pxܹg B-224**UC#@DޣGjEԩSNW#I2%%%((hڴi׬q$A;JRcccTJ7:88DDD(~yuEEESݎj*8p@&EFFjߋ%(rȐQF͛m۶EEE}Q5Nug-WZE}cÆ !M>ѣGG 0`c>LbϞ=x߾}bXCNTw-{1&>>> .ܹs}(Nihbԯ_GdÇׯZ ! W!*^')FׇSN/PE*W2dH.] B|*??`5 ڨ9&M$~wzMGT6yfr믿 z򥗗9ݒ7}tHdooE!T(%Ju9--6g2])0hk'n2YF8駟^tH",XZ1W>\ ̜90ӳg-,,>ٳg_|H$D_|=;c|q*@ x31< U̪={=<<={n*Uf?|d]A<ZO)//t˗/Uݜ򸸸ׯ_TPߪ%?/Z6gކ-ZPw3}VAAAFFFLLLpp0A)@,,,:]A燅5t,*0hT?Vk2( <'(lXb ocPen`*0`*0`*04rW㖖J/OFv.UQ]tE  :͛Miu1-B+))jr劣#wttr XXXmaa]TTq*Qu$wwwP8eʔbҫH{MX8 Ϙgԫ&995x7v3fxꕆ˗/1B$яō5J(Κ5˗---Ubbbnæ?& h>hd2ٲeˬs #FJLL԰MէC\>n8z5Ɓ^-) 5(UT`'OLII?>k亩%ƴ|wO<={6ոxԠq"(99Y$m߾]Ck-zu:ѣG;I/ZhҤIɮ;vP8zh5gӀ`J>cuJԤyX,x(|ի/^L`?ݻ={:u.]4h ݻknwqqy;wXmoݯ_NHHHHHܳgO&'''''Ϛ5k֬Ythnhhy 5W}ewJCͥ9q=s&/G㯅KXwww ? 4`{SbHD-dqСT}zz:I/^ppp8i~kה|~II I%%%r_-k3**<Oq~Q-Fҥ =7o&L |=QTp]vY[[;v찱ٳO 777XiӦ(iaw|9's}uY<{EDLLP(JoL&?v$awq<::ŋCCC׮]svv߯j'Oh4r\*2M08W^^z{{)+k֬IJJRIF|hD"u:ݣGH\jՙ3gfP RNNNh3c_]pt .vɨ EZZA Ξ;w.A|ڵkӦM,Ay />|pBHNN8s7lpiFsҥ 1<<<22zXZs\rV5Z^& ]J9& 5mi;\\FRLMM͘%dFKqqL&رc@,PGGGll ^/.ח [n ={AqQS ^K=JuA G>}:fEqZM }.  I @/8 Lŋ7/_ʵk<<<^|)͠JfЗP/zƾ^rSQdz'?ە B񒓓{zzT*աC/^L=500 5Ckll\jϟ;wnVV8&.\w` 1c! 4 IҌ2"(((%%ET*5kPvB A[Zl#0/,Zw,\主OxDOOϬlL"_Tfdd̚5kX*))())DT3$bcc?3J<};3,,L2'4ԩS5Mbb"%dff?"ɧa IDAT œ'O+8.FqttjYYYd8IGh++O?t׮]/^bTTTZZZff͛Y1104k@LLÇ7|3&&&;;,Yr!Rۛ nm>uT``_:e;:jZ.ܹ|@V[[[k44T*w&;z{{ݻ˨ ˗/쌏_bŰR>, ]i A[Zl#>\ri`9VRR{bFiFR4,z>+ԍV]]]~~~<Ϗ:Y 1Eh@{)kx|x3}}}7oD|>ժU#\\!,YBDqqD"ͥwttlذA НW*7ọ4-I^bQh4[D"Hm6겚@ ojj24&t9 9ό*>L \s`@v%$9@6QQQ7nZˍ7w6z󋌌tuu|6ϝyw7zʊ͟nnmm=s̾}0 {c\D QXYY%%%gɯhaXii8v_wPP@ ^%LJL ۃĄU`5[b~@ Ҡ @ Ҡ @ Ҡ @ Ҡ @ Ҡ @ Ҡ 0~Z0*qUTT4sL=F(" 翌d}j`Rw1֮/(++swwx-z*e2񩭭bEE+uuubOOO@@A EЀ}P4ΦձN*g_ܦ04; |Vݾ}X,:}rr3g3[ZlAA\~ݐ*r~ˬY+W^ X/0րf4 F2?f:o޼"@nH[lh4[W` /_>Z&ǂ1]`&raӦMϤ?>33/-[wب( &%% :@"B a_g)fSN?~###׮][WW'RRRFGG\HKM |hM >vIOOiUUUUUU{{CwttۏZWWWWWbH  V@nݺ~zӇրfQYY{666~w7o42%%ƍ7nPTiii僑 ?9=Z&_mllobVVX,2eJPPӧOhccaؤI0 \. 2eJHH\.gF]vb!100pԩ.\'IĉHՠޢq81WVVѣG+Wr۔ϟ|T ]8NzzD";wnEEEjjٳG^^^ӧO0igsCZZX,3g?z0jþaa~UϷ5kVYY%r83fx{{cީGyKnٲTnܸaȐ!Ο?ckkkkk;w;wð vNw$Ic#mmm&M4i$+++.kH )뫫7l؀aJ[NS_ w:#Ӂ:yɒ%<oɒ%Fwttx{{Нk- 8pÛoIDzz׿ݶm[yy9{m?aICѬZ֭[$Id2rƒ%m8noo___TjjjXXخH$8K$%t$It:"B 8.Onݺv2{# R۷(;F$ɪ*///={xtt/ ] ߿_R Oh.]`vCLx<؝W\j|>I===tppP(,.PlkksrrdGGǶ6C"OH駟Hl@@@FF}7jڨ$LVx/:_An?ݻ[SW3_An6a555w'V*R-Xf2AQOT~tX&Q{ر\___ Uj#66v܃FC[={w8z(yzpR+++R-(I>өՌR]p{ }. KPQzi >4ΨFzbc_hCCcL^ p#;T*fcC=3 1$E˗q|)-(JBfC"OCΓ$YSS3|FΘ1C$ $ɡn۶?۶m 4]./\PH @'aJ$IR)XZpΝF9+0yfpORZg-oqƚJ9z#GN8amm ۷oL2%00Ν;@qѣG8"2a[޽{wX>p8W߿_zyT*/^j*pO@$J$I޹s+f ?`feeEߦfpstb#th82c.SN`=&xO>;#O)S:bj4Sj4DJtqq̤I&)'O|Wq]蘓jLEMsɘq6=7 D $ F.*L:R\6㻺:;;WXaسg?nȦ@ġCJJ:|P(4$Bݼy0oo{vuuѭCC\SS麺RRR1 HJJ?jZ.`.uðiy Zm~~yػGDDFDDXwpp8~Ng3""[ڨ[,Y$11'11LJ}a*Ss{ ZFuuuUWWfY 1Eh@{)kx9qݐRܼyX,G ~aҥ<O"|}`2k֬M6utt^\\lgg'Hrss):#$WX]Γ$yĉ3fЕFooo.ҝ8tЦM(u^^^AMŋ{9&ӦM3f̘1c͛F*$PojE kq:@ (:ގ?]/F3Ξ[oXӍ!ݭgΜٷoao@ ^qJKKۅ T!-/^X1 @ z @)藹@ , @ , @ , @ , @ , @ , L=E^rJ'b͜9Sϱ=,51HơaNxE]z2Օ庺VTT' B"B ]賆z9::Pzmm_nJDLg'y@|hM =T5g!- jZvbX,GGGt:gΜsljٲeAsuC"O-fV@\zj bqPP/sX ЍqL0C>k2G {(ˋ6mڴqF ?>33/-[wب( &%% :@"B aS^N*((8~x{{;GFF]N& w"si鹁2TӪC谷-144444.$$$$$Đ*>6dggݺuw@; ͈hBCzz m@SL z)mll0 4ia@塡SL ,"(k.X <466N:500… Ý[4 QO͟?(=Zr%˥ގ<<00Kқ7o20p8tD2w܊T;;ٳg㏠O7igstZZZBa||3wb9s~гHT&38wT*xNNN#1X8ӡ{xtt/ ] ߿_R Oh.]`Cpʕ+Z-'I ťmmmNNNGGǶ6C"OH駟H{h֜>(<< ,OPc<'Ir̙6WKҺ:][[ q/,,4UE "IV7NP!I\fǩP(B.#69s***:#pIcGXʥhU`&T*322טNJiggČiӦQ-|~iiZ>y$gVV@vvL&c-^///HD5#I!66>DLgʔ)mmmЧ4ԩS5Mbb"%dff?$ œ'O+8.FqttjYYYd8pqN%X0

]v]x4uGԩS+Jaf}]]]+V0ٳg ىϟ?71S СCJRR>|X(~BE n޼H޻woWW:4t555:+%%E"ПHJJ?jZѤOtp8z({K41l޼yZ6??޼yʥa17}`8-K' w())y¢P]]akkAhYDCL=^ bڵ<޽{TKpQjL'b:qqq8xQXX(E"Qrr2T[[ tvv ggg2M0}) oF.AВ1lznNccAqqq8Lt #Gم&х&nnneeeLaX8 L9'IcÆ QCL4֭[E"H$ڶmHdbbbppy\lll߿ɐ*$'8qnHTn޼Y,ӣ ?tR'H >0px5k֦M::: u/..H$ zQ:}ݱcG4Iۛz{{7662ps~΁M]NTy@ >]/F3Ξ[oYYYwӍ!ݭgΜٷo@ 1 DyW1 @ z @)*$@ A@ A@ A@ A@ A@ Abܘ *@ Ɲ/_23g+1Ẍ~u"htXmll&eOOO@@A b͕xE]z!F( IDAT2Օ庺VTTx 84A5ԋ^^^|>ݺu*Edw 4 (ΖmA?Kwc)\ti̙L=99̙3zͦ'655}'AH$(ZmEvZbX,t#03i1~3T`N*((8~x{{D~¤$@PWW'ڜ\./..nhhشiƍxz/[lٷocccXPCޡz k׮d))),ٿ3M |hHi⌰O<A]zU(Af(++srrbbDD?|ƍiӦQ{HHOOiUUUUUU{{CF>&`,fb>g4j|{}oݠ1 =%KTVV$IXƨF)//U*@ ^^^r\^xqѢE,޲LAS5/'IՕE?,={͍:::~6Ѡ4DАq~ˍk׮Iҡ!F?LLgϞ^㹹UWWTH4{슊 PΞ?>X333CCCoSNV|(2quu}^G0$r܁O7\.>r xZ \~J믿H$1Ri]]خuww'IyѢE ..Όq,f*BuwntnM]uww޽{ɓ'O6R# R۷;ru:I:Y"X {{zRSSv}}=HDBzkh LC8Dӧ[C^dǎ# U`\.7///77s$SS,_$T*SSS?cX\\,ɨ=vXnn/,S舍]|!9 nrss{!Ed1d Tǘŋ7/_6o^ )`Ƶk<<<^|)͠L>z#Kq\V$988H`W\tT {zzLH*UVsfeesr|…U ̥1cB3AU`$IR@ P*$I*1L;O2#Wsg4Z8իNxѿ:J8mooq2F~7PѣG9r kkkܾ}; `ʔ)wae LCoۺuݻw^/Ο?ի ,H$,hgϞ#G䈈0|hH%'2pqqrP(oB$I޹sܟDaVVV$IO(t" JBc(?~|L8e^3f:u555G,ce˖fPVVV---̥drCbcc#570}F c1#Wsb*))())D <==e2XTTvvv@(,,,--6mՒ痖'O|o"@Tfdd̚5kX2$I644~gmee駟ڵŋ;d̐]&phnDEEeffn޼LP(|XO|7cbbȮCSSHFPr|ΝjZѤQmRiBBB__޽{˗wuuuvvǯX8yqNg},Sf'T0gdj&YB*kxF3gcpfcИ@@D@@Bkseee<oѢEW^byyL&>>>@puur,BE!@c.$$+WV^MX, _@3g^ti|1;ꠢy)#Ye˖A1G!'''9sr[XĦO> DV͞.F*%::zʕeuIJJuuu ))iↆM6x0󙙙>>>_~%lٲo߾FEEx 0ں@3שS ?N鑑k׮d))),"a /_>@Rha`i0OVUUUUU:tP˼͛7/?ӻᄏa ònݺ~zܹsD"44444.$$$$$3pb|GG=c,bDD?|ƍiӦ_n ܾnA'˗...L]q$[ZZ\\\,cTє{yy*J m///\j/^h"oYgLjdɒJ=$">{?|qPP).ٳnnn\.o5c = % L\ 0/^,]@IKKsSRR$ɻ˳gV^ܪq{1ioo_b-=88>o޼ .jxjjH$={vEEWg>2hmm]t)]lÇY6WWgϞѕE xjLj" ,~:0}"yyyvvvΝώ~W\%Klܸ%22Ґ!Rlֺ$qݻw$k׮0|{{{=$ɗ/_vuu}@_,X`4nJ믿H$CEdR^^pB.4LQO=/^\^^>00000P^^tRCB#us܁Vb\\{p---,O߿ԫ34;_t;7wgl úkjjv=yɓ'TUUU1 t$It:g`-JMMvzgJ$%[CS`q\$M>}ݺu,=d""&&F(J۷oQ&;v)j4UVݺu OdOO svڴi5 @jA  Szdz"2D?8>03-ڜfhXh-q5kuppعs'XxÇ+dp h4F+tFJf s"""=2qIJ255?2}}};ߟ-:::bcc/_n=hryyy}}};[n ={P YZZ D2aLLŋ˗/1D/ zU`z8`j$4>}:uDRYY)JI&#^1`V`\BC f&J%IJ2- }}}o .\ȜOPC/$I˗/8< DDDx{{+n۶?21cƒN%C"???//gς,#0C $(((%%ET*5kP Mjhhu۶mHOlll\jϟ;wnVV8O ME(8S-]Ð?8T* MrBFG*$Ijs9993<PZ*$```P{P˜CUVV?뉆.KkkkNNG}>q&4.=*8_pťR2B᯿BP={ŋZJcV`&zj ɰX`З3gR"ڞ3gNEEt,H,,,q޸믿+bԥɧOrZA <%s:LNVZb1f<  h4`[VP#uj.R@ͫWM} dB6ӧO{xx;yh陕500-\LLLggJ̴bFFFaaaiiiӨ|>TVX,634Rh֑`ҥK3g/j۷ob8::|rժUT\zj bqPP/3g^tiL5hzHjٲeAsul8F*-[۷QQQ A]]@ HJJksraӦM1 ;|fff}}ϗ_~ DhBE!P =uԩǏSzddڵkd2YJJ H rau,АB`t eeeNNNOVUUUUU:t}FFƛoI= u떯tܹ1{ZWWWWW HfccaؤI0 Gf!CCCL"\zz m@SL z)z F]vb!100pԩ.\`$I8q"00l7Ν;'Jy`uuܹs)֩.PlذҥK ,bOO KKh@8NdzzXCg@W\j|!Jsee quu}1Z-AF 52qD"O?nFG`*x @˶6'''IDAT8::῞ȌRW}OWaXwwwMMݻ#csss}}}N,_$T*SSS?cX\\,ɨpX!C˗/74zqܼ\OOO ޺uM ٳڇ" ""ѣ`0&&f|˗gCC*0=p_`w}GWj5ImL>:(S^LAOO]JL G(dX0+3fP/@T$T*OwjI9).\7A[GP]˗/8N?@[Br+V$Gݽ{;b*0$ RXX؎;zm>sj(S*0茆F&.WLLٳgG`*IPPPJJRT(IIIk֬1=Ѫ\}v@@)Sܹ3'F=zaX{{;1ooܸR=zȑ'NX[[@,S`0o֭[޽;,zq8իW a^ܼ`D"bT*/^jժaǞ={9"#""F2DCC:.={6Kw#F\.:666RW͘.={رctS'Or{׿N ,kľ,ر ͸eOв*<<ğ|~iiZ>y$ϷK陕500-\LLLggJ̴bFFFaaaiiiӨBPCR1k֬ay˫d``D$QHlhhӧO{xx;rCh++O?t׮]/^P4 %ct2zX΢Evرzj 2,..NP%4 @߳g7!(˗/쌏_b!NՕBkjj?QZ1Poetc@'СCJRR>|X((}t` Y]]akkA'4x~~~cm#߿zg{{{Iz Y 1E=nh/BvZy=%qpp_[$Iz{{}򭨨߷uss+++3o 4 -34.YF](mmmz gϞeiINƆjSXX(E"Qrr2%2 ƃ.]r-[jȓa!RܼyX,'^cc7nll;F6G| b;;;DK獍^^^AűcÆ =88L<11188%4 $I޸q7nhn*D"ѶmIKt1ᇥKxn:JeFǚ2wwwhѢW\&|Z VTTr\WW׊ P(XD!}P.$$:緀MMM| A$**JVcjo.bqtt4up˗/WZ@Ǔ'O6nhooWXQYY9P ӧr֛pCGٹ;Eu?n-2jWjsxWa¿2 Hvں:L2ΏFraӦM7n333}||K nٲe߾}ߏbRR@ III,"FYx{b:uTAA{wŕ.@$@wӍl-. v}xbeyh2'.IT1:I`bq!%QDZ6TW7ݬ*GuսnU%8ksrrΝq|vYcP^O ]t)o4CEܹsʕcRCwǎ/^xL&Ϭm(**rrr'ȰtҠs>|GMڌtЁE$Ad2F`ZFhjuwww Cd!x̸P(h4cFcCdz/^_^^ˆT*DB-TTT gN6 FEE=yc\WWm#ZXu3g9sdt#$۷o*?^ ٳgҤI^^^+L8ds533S$^zղMO?4(((44ԩS uuuRtƍ}QÀCذGmmi$IAA GѣB022СCT[mvI*:<_zϟ??o>>MMM6* 0a’%K={f-z o޼IY;H|[xx-A)=}4ݶFLV\\KE֬YY[[Kovylذb4g B`0ܸqcƍ˗/7L׮]+,,Wׯ_VUU*Ia>!uhliڵծ}٣!uڲ2zvhuÆ TuxٿFDDTUU$YVVf0jugg/cT͛g͚E-D)S_^T"߯jJΝ;]khhCeddS'1YT*Uvv֭[ǎk;jժUK,),,v횵ռ,X ">AkOi!JmR[ZYR9 g2=A%zhgΜIKK=%%%񭭭tD$Q3^gQZkG|hDeQ*NkͰvYR藤R)T*miiQ&*50v!/u1/))={˩:<_zWZ1NHHXfM^^R2~/G%''\tV^^N ]GB2,X >ߗZ89{={7f*Gd0;v?9s0$IFEE M6EGG$1?y$<22O} l;DDD4<}Tt<|8+Og!Ç}ѻ;Sq/<\RvL8pdX|Nw߉b*{nVgϞ$A֊(J($$ġ3JII)--jj,++KLLpWWWkzMoݺFGGڵBԴb r,7QdddII`ؽ{[XŲ,c~'O믍FwKe1ؿrrrx<;׿ٳTnev/k'N/B/>tPXXcCQ݋7TE\r۶mg>I&NWQQn:Ž\^XX8gkA c07?ndɒJ^P(dzckAPfffGGǦM;{7ڡ7{l},'''/_|ժU>Dq8Eڵ+##?p8!Non^W_}e4)SmٲEVؤᠸ8,,,1)iڵg ٲesϛ٘'OܻL_]psjW^MLLILLqfGGǂ D"т #֠,V b"(99ݻnm$qHAA5-38p 000 O>_jhh5k7$ITTѣG-DXmmm_gֺ?6WSSky%>1ؿ<8eǏSA;uxٿVWWb :;;bqMM RQmr31ԂWHHֆ̙3#W^PgXtu6ZcSN͚5K$gffҳGݡcTOOO}7AAA3SNn4lkk[tD"a 5+UJ{PVVVsScWs 챬ѝ ,ly;]9kA$&I$M&SiiiF\$IkvvvJ/\nV"{.BT"0gya |7+jmF&;iq W_UpaҪ=yh2XW>o}4Wa a񂘘˗.8N <3:6V0^`*36b/?(8 0 l,ׁ=~Y4 ^8)"x +p8?%yj9Zbf`MM.][gg.8/}z^Xe'Nu+%g:+hK.]zY}/03W*SIR10U5j!ӏ.^jc$#fdK LJ {I`LI!!d;FfF'~b;n/ͯgިGNWSSacLُ;vby\nr%>#J4$~ID`s``93 ..V sf@{!̓ .nٲ X,30R#.:~=B^yp/$ÁQ̜1c Ƹy{:\{!EV\cFfs?$ CCxh)?B<|8=Hdg_kEW=lȐttv\rџO xFQ Q47c&IFj4$F\QEL0z`k6s[ Q&_ ʼn;™GKmcovZ 8Kmy:"X\M|g޻)$_Iy!2\DuZd`8Y)zy81brzKP7`xzZ!IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-graph-info-line.png000066400000000000000000000046541351617527000251730ustar00rootroot00000000000000PNG  IHDR#U1sRGBbKGD pHYs  tIME"B) ,IDATxPuYn)t~+Hm2IRfDkRoR䇥I 6BҮ@% &:;lnƹT~9s"r记@?y>|}=\ ?HCC}bDDDDDD" FDDDDDѧVfKS_W2}f(?׺̾ձV_\J7DXU41Mfn?'L`]{^4iL4.)m;q}'[MfYNƾ n+C@NOiii)F={x@('L`_- GJd*""m$iV:M}}=ѤwIY"V\&r ݿ@wg`p9Ν;Œ?l]|r@&0O34 :PQa奴4,۲_^M^Z>k_t` (0Y9[<<9BuMf, Cb(..nSqÜO`@h#GŲom#dNqq1\,Y.+M cc dPd$Nyk2rT,]wƍ_3rv; = >]͡'DZu۶N#lN` 9p (?O{ݞӧ)))ჿ]l>$FƎ"77 78xiKngӦDEE93~tMd^+ \|q`YJ* 3ZY,Zvسo O 22.)pAƌ]땜ZCJ>_3bٳX,9def\RXn-/ۺTWW@Zj*3ftQ <۴$C# 5r$C !v(>_1o6ܹ{?;gGCwӟHW0-/poDGGȑ~ ĉ >Õ9e49R]EPP$u9FZi0ڔSn6 ԩS\wu455q 7p̙vwT'ʄKwxRӺO޽s߽2u&N\)wQ癉`t˖6Yڽ)//7 <;ՎF3szڝ;Ӵn6<.= qoکO f4k?ifm-gٰCJJJعs9R]UnK\FsF;ʷ,uD|帝$ W]_x}=vJxxxu:t({xg0,,bl6oV `gd]{Odp1111턄`YjW{$.˗3}tg[m οK7י~xgm`V|29 !i,<wٸqnvxf3>\ٟ=.EDD`| 7lgqrsr8y$C iӘ7㽝ęɡC馛8tYYYu6Q^J%Km` 3;fs,RSS:l8}x~ɻ?<:аp^X֬t722ҙ={Gɍnq=6xbTVm,Kjy9ͯUL&cu=a~~>m%L ??~39m?DJj 9yϫ_x9#X/o`w~}mDD<z#"""""#zmZ"""""t( FDDDDDG(Q$DDDDD?Iڪ}IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-graph-plot-area.png000066400000000000000000000121321351617527000251650ustar00rootroot00000000000000PNG  IHDRpsRGBbKGD pHYs  tIME9$>iWIDATx{T՝_# c⃇Q1K Ѩ@ɚTlbP1"l(+ZCDH"8!\Q?he H⊏<AbnviD1OT{=4kj.||yω&D'K< G%xr6RJ"OǯC*ɕB ;@#4ws\zѽ{8cѢE1eʔѣGy}bȑѵk8餓bʕ{5~֭[uQC}d |ڵ+^y啘9sf|ߌ/aÆO~]w]a|;1f̘غukL>=ƍW{q'M55N^>J|ŷGhrرcG;w.yݥKؽ{wDDr!RO1Ì191vjkk#"⠃j8bӦMR^k>;⦛nظqc\|zxO{7m{:+򕯴o.+Wg$JE/Jů8XhчsOƭv- < t#b38|DO.S>au=Gh@xVOu[׽< t`۷oĠA+-\0k׮qǚ5k m-O<1:wuuuhѢBoÇ>|x477WՖu.\+K~s=ı:u*i_fM 80v u֪,o֭;p.fլrYcT[ګ-kEPwMuy/d[֜ڲ˚o)K.I֭K۷oO ,H={,=:]6+}s+~iҥiΝGMݺu+]uUiĉ%M81]}UeKD=U阬~֮͛]v]v'3f?iƌiҤI{|nv[[GY5\԰)oY.kbVK)'|2q/.ٞ5RJMSDs_* 8Dnݚ>Kw}7=iРA?>H)K/N8s8/^\sΩ%RKKK:vޝիU UeVN"&fW_}5r)iÆ /9.kNYmY76|DH={Lk׮8)S.gMݻwOw^r\.]Ү]RJ)ڵ+TՖu.;O:544u}V?KԧI'~̙3SKKK5kVyWsn?x7vm~x㳟l_رcG̝;7 mFiӦ?qUeKޖ-[bԩ1hРՕihhؾ}{466F}}}1)xcq5T}nϏ/ѣG:R۫YŲ؛V}Z'u]u)jՖ#ϭԫWT[[=K/Rm۶RJ+VHOtP߿|t9礚t9甼ÒV͹VNQ[?9eܜ;TSS ~r\ӧO_fRJN˖-c;t.We^}mfշuRAM51+e1kNYmYeͷ\StH yω&X#@x@x@x@x@x@x9""M΍;#^1ˏcoxdǧ)yoΎ/LWe,75԰1q1uMyw#^+˷W~q /^eѼh`zTLq׹OCqmq _՛n!%>s=#"}q; aqˬ1j^*/xVǭ_|}ϸe֊µ,ߧ]zU?樘?⵸ypsʿs׫7_y:cZTZCcnnTg%WgrfzJs{n{ofƂۼWvݳ^Ys>?뾩晪^D'WgV7N~ŵecđm}ϥRhe[{Pƾ8V{jؘASx}zFac3<]\m\t<΋@" < < < < _\S4v+1v܄R"pwPxx@x@x@x@x@x@JtXy?FDק/,4L{uZpzºbWű/dm7n9C6z?1$""Fu?""В}ƪ|>}aL{xsv/[En`[͍2Ż⒮oX=V?qhtrs tEaFƈg{qҹ1[.װlI n}&}u]\tn4,[KF6+,z7F\tnmٱ]0V+  ws_y.+9>GDD㏓ω6뗭_sѧ¶/l]Gݴ-ddC98tt ۶084.곪dȺ뼋*[b^//]qN^;!~eo#"v9xDԮYw~p-{k_jݳzڞo7:""ƯZ[Ҿc٫ -ѱddC=аlIDDa={?1$6>Zqb66>{ɿ.q~ޕ:ˊCGwӅscިⱍ_ykNWnݳzŮ gò%Mg}ߟS=W k]ޞߧ˫`ZyMl:{u<"n>fN<8ykA;(Wfy+vBgxsR9/^}=F+ .8~*^ߋ/bZ?qhz]Pߊ5ӿwl:{uzIJۈ+]՗9O~=ys<3.έӕK׭jM-jx1^gƥtĐ+w~aְx||ŵ|]=iHsYp̖/Ҫ)g\ o %תx|3s9Js/+kqq+J,R-ߣ+k9_ϊK < < < < mrMqgqsDDJɊls"GhC@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@x@xx@x@x@x@x@x@x@x@x@x@x@x@j/KBN5IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-graph-plot-title.png000066400000000000000000000021701351617527000253770ustar00rootroot00000000000000PNG  IHDR4^tsRGBbKGD pHYs  tIME7IDATxݻKci_֘)DJ Z !( `0X iB1ZX[(. ho .h$|*y/'作sCٴ[h69[OE,1Bod @#OlۀCL9@ t?XJ @ʔ[pF'VF @ @ @4??&n566*J=999Nhcc㛺T$eYB!) 0 +HBZ]]JXLXUȈ~٬~FFFi?2I6Ֆt:UffVWWg;;;ffm/mnnv777J&t:IRmmt^XC%%% ~ۛBssr13ikkKmmmd2ϏUQQ1|>%I{{{| ippP뺽u~~sqE"'vwwkllLWWWWa639 VZZjJ^\ffGGGc<Z&yɉAs\ 4MUk(*>;ݞj-..ZYYJ @ @ @ @MxXf3槁FGGH$.˥|v+i{{mObe46>\F3pTg4VUUIʝX)WƢ500 ǣH$μNNN@^W^Wȹm?~\5TF{ q @ @ @ @ @ @ @ @ @ @ @ @ @ @Oo~fWF%IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-list-adjust.png000066400000000000000000002343611351617527000244570ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwxS$PZvB٫,W Y ((2DE {2eBirqޯ'99sp͍T* 8E!$1My#/wHP)27›zzz TPPжm[B˝K!PJ!RB֌ZpbP9RB(EN!7pK[QvVF D rXZ{oV:NVu]F^xʫ.ohʶE 77`9sfܹ7otZ+__*U X"IT$y!99g%(}_{~fd mon?GOꔒڪPVG޽SwsR(th]ۏG۰g ?^_ʰ_Ug{o2 nINrqq)x쐔,Cvo?wmINRNSp9@nnnaa!!$!!N;vdgg4li5 !HyJQ9 =|bo,N$J$JLK5tYf^3 w]}?'H{l'N&RBI3`+YL\v^6>U/ğ9)~ B 3 g}*_t2 _E~KPV^*4}Zyyyyxx0NDQ)1B?_{ey(9m%DXIMoK]ayk7nu1C2K(rԴVQw% xY*Ze\JwQl#ITU wS#QvL]#vA;r<:7VSP(fOKJ75ZUJŵ,mk4 ԩBr8FJeov&nP]Coo^n|I0T*z5 Rb)5հ !%usyrD)P20hy=J`4rss5 m_VaH$IXd+Vs΅sαkНhN(";AO%I9jT%QUt oʩ+f~{:UXَ$qkR޿,>V)y^ؼS{N'iկ?)*lI%JxAܹ[gJ֬j0p@%i-sYUqURGSf\6 %p[Y]Z>w2FýiћU]NK4 %z~܎Q+3 f hV㧿sth웑glk5lB޶FUM1$̾3+ tjժ1Js`R9lu fǎrLV̙p?-{AJ%QB$IT*U(R*Ϫɽ7վqowtko7F5wcDRoIz^.u.*QG$* |Z&1!gC%e'\O)dN0Pn~Υ{[{{4jvCbY r !.*E'};~1HVPѯ[ 67HNU^.Y.#!$_oЪՔD1мQI`Lb0=pFRE!*$Q9Zh4”K' |~nfVVAIB|c>pg~>nK<ϳ,+8j+tVO=B.]A$Q(3^fiJ$ϙ K_ ](S9'4[FR*iD%vMT$t)Is)9NhZ'>9L3%r56>9=>9}qm7 8uӷY&W!+Ⱥo ǃWofj{7qrjWR.Ÿf1~rR{_}wW+ )$>zhj"Ǯ>m?zOy~oȽq 2Y{ZMi۴֑kKlPR!IT4O!rđu nf •s';V sf4Y?o\8VjU777v Ove̙3ǎ_}=9t:M'f&w3g o4 ϱf( Jv4;0!a bcvypc^⟍\ѷ;(I5hJWmiVZrA^^^fOrssmVXXF XdjVӧO-֪K.U<߿?**c[+,5pQ]4.ZWIO#_ S=[]~w-,|$AxZ3 $IBa~NNN AoSN8d` E1<<<777??_G U5S璉E<9D׾87m4 Q惚^3 O9s&e8#*rǴݔ;rߑ!8n'MAG+`̺rڸdMԗ^Vv.]N0jrgϞsNNN;nJ<͛7 g)5iaǎW^m4W\٩S'`ޟ:MR>䓽{rtSj:-,+Wvرy_jfywvμTϧk:g8ݿPF|`Z }azL4ye5nh44moωGJAt0zbfgu;9CbUPJHH"U"+_%򒜔UP!p6\p6 ,!>\xrg`M5/8 L30 e܏Z5H/V5XA~4J|X^BHfcȥ/>8W! @z8yҖ0EL20PU߽GӧOlk[liҤFiժվ}X֭[CBBZmNΞ= mְaCZm6VٻwoN׻wשּׁ,;V",]nݺJRPXg+}5Ϟ=j k'fXȪ~Kn5۵kG\4_m11m11N ! B8ZR/BZXZdBȡC||8`SvнG={ZX{k׭s,p1V9[#G0qje=wə1cưaX֭[lْ5dȐ_~5jҥK,`3g  9sBmذ믿EӬvE)o=:333<<<22NV#:;wΝ;w7#Sd.!d$B$IZ`ᣊB}y)!fXobފ+׭_J.><Ɗ hFw9a _3O7oB>~ , qG_ ءc'NʭVX"_uR?@ׯ_NAT@V?6oNJJbu%3OcSLqss!OBޟ:mΙ3aDU\\܁Rg N{7mdŋ_xzƍ#Fj NY$%%,^zrf@.\ؿ{&M?n,5nk׮u떝F@.\ l' 3+bʔ/2)>s2 5Xdad  `ّL?HOKKj3fX i&LgoL7͂%ܼq!B^&DA0B c&\ y+g})E{C"|ѣ:bkDN3g4jؐlͭeqUQtyUFz /o7 kgQ3B&M3-[9s\r5k֔k[pڵU/ZHShuDGG萐ve ŋSN0aOn IDATF@SO8||B» ܹ3V/Yb͔)UQ"wݿlfXsجYӔ;6mjZq;qs?N}?s!RAE.ne^^|CH^"GyO? 2K2Fn_`0lذ['pw)?*b+즇,XKW5bg`}ouσsoեkWvꢅ Cxn>}Xo.VZ!d͚5K.RˣY+Wx{{_rev 4{]vy{{^okzWծGQ>BPT~3fȑjV#:I'׮SwЗfϞ6mZHjͧjehG6kٯP,f|Ze`AݺXƹLގ!}xyY($d#!M (E5B4$[n?nݺիW괩cƼ?nzȲ:待c@At0zbfSkJoGxv~b8oȇJJHHa7B@W/W27Wvz Z^d,R^^+̋\zOp ِ8ۃPB1 @7Μ9f JUǧmVVh>Fvoא * J:xEiF(5Zm' $IJEiZCa&8|JPDEEy{{o۶mժU^Y>}hƍ9rX=g^^^5jԈ)YoD KRZ-g4i *+㒒-[6|pAnݺ_O<}:v#F.^8""X=h 8c A%$$Ot_z={xx <͛ ΁@e!*|RLϳIRz.,܉P1.^8???ܸqc _|>m]HxVu֐V۩Sgϲm۶5lPVm۶fff[;++NՁ&gS[.]Zn]R)={644T0 77NsB//L%nKkve32v[*>kub6nܨVóZhO1ʔ޽;''gƌÆ c[nݲeKVV֐!C^~eV8jԨK,Y$""Μ9388833388x̙v DY G6l_Ɗ(G \.3g+RpmkvElwfߗBeQY[+O:_:T.B}:8M#7uѫiE.zYH᧟~ 5^ڵh4ܹUV~׮]^zAv DLvUڪ]v;v0+T҂ 9$IAAANr$*U^}͔ծwf9/3mΝ;mڴuV~VóZhO[1^{MJ_wnGzoݷ︱h{_|u+Q[zWkn"dee={O?U*Je`,e9Yٳg+s缽 !rM(i4;V"xzz0 !!Ve+N7yd77ƍ;v,[`r$vݿPnku_3e+;`J"ﳖ`xꩧ=>kZ j}VeUJiNNĉǎkZe˖?~|TTTAAAddqd Vrƍf͚9~j+Q4h:nܸѣG5k,Y7..N^/vϠTdZǑ}n BEg-1cƼ;w&$&&5g>Us`LaaV.^I&oߖK4^5{jr F׻ ظ~aڪA OˮvѾ}{feeyyyedd )VdW;^}-T.seVc'<}ډOd4iRZZZNN[l ̙r׬YSnݺµk/ZHShu &;;{ܹZ*Vf 룣CBBjҋ/N:u„ ݴiS߾}UV0|w[[jW#;{eP)T}X_)1FY-Ve9I>zuUVh,yyyÇ7kLR5kLOk4~ZgB;YʡEbUVVVxxFi޼-T(kꫯ4kN)mݺ={bmmkrxV[-T|YִB}Z90'f^?dcE~mwK8oB*THdZg^O^gs}s(QIDJ5p$QӒjUǏ]?{DVVVBBBLLLTT\x< p'@90gCl2=s`Ά ِ820gCl !p6d`Ά ِ820gCl !p6d`Ά ِ8pBP~;xzū !}]A۟9!ڦM?hE{֛^9iگm,9gRkpF묛u]aYoo߯O|MK4/:=v !I#[@UI#.t1W,N75;#-9k\WBȬMkլtlY`X'{bl։\DzZĴVO_N{wtx,oű\fa[(V*tɛ#Xl!ZN$l4ȅŊdEkXΌ]/$=x@Uk/N| G:wt}?5[Y0 :qac7rY% X`u̶y rRlŲӿPMWwq#^,W[i$~-8l@Emfӷ=>o>207mX"//o?DDDDDD8̙3333gΜx]1BΛ7oŊݺu#}wjڈ{bcc6y%K8 @ v֧EGG;[RRRݺu !KJJr< ߅,; Jjڴ5kʤCX1 믿~D)aaajժiiiujjYZjݼyPVbPi#:u1c9|\\ܨQTӧOdddnnnnnndddx(ѢEz/ׯ_Ib 2$22r=zSyٲewYtY+Wx{{_re%r*}`#G9rY!Բfcbbtkt~ِ820gClMAt0zbs7.zkFwTv !ow [ד\\=~HٯjUǏ]?{DVVVBBBLLLTT)_%zԬ>؉2Ά ِ820gCl !p6d`Ά ِ820gCl !p6d`Ά ِ820gCl !p6d`Ά ِ820gSw<`&hˤ//ޚݨ׵Լy!ժ|QŲ ![>5`:#Nu9Aɂ>a3ZgBf/-sG&p}7uOl*C{ <~ZGw!1v@ɖIC/%!d>}AWj &v ]Ugh5ok`4ymZpظ)^{qEi߅.MBdutghx)J<?KZ^7i~DEg+~˿>^}`ӎT-71#vza~널u3G%Z?;WLlx=!$Rrf,rfK'-,dF=OYBZښ. k-IּyQ.aKZY !kV\ _N *7_V2ukyv=.^*ZO=7V,yK_s-MG2>>v `y6hK$LKF6jT `=L^.Rd BȦ3;q޸ܜ ,8c)_T꙳f7erժ^JijWVVVBBBLLLTTY\IO)_-X 33O>ʒd 堬Bc/!wNaA$\JW!UH||}_޹sWG4i_ .G$諐<ϗG`8DQEBiyR(*Rz>QU@^'4h]v%[^^Ujh '}`hWXxDT*U;tڲ%P9#9.99С}ֲ oxoC_ß8ġ43+NzGUv,T*5&4DQd E׮]A5 Ի?#"Iҭ WvRsFf丣0M۶NZYR5lHT=װ#?:$T*kqȡNaJ]VDD*.G&͘ j%a޽{o޼yZNۯsm۵sRծS2 8tC JRJʝի;a,+XVFFRTn( *el oFݳGC_c~)wnˇnhd_ȰCPxxV%.q}`(J|^_mwtuuc%j IVZy򸱜X; F:t`7nѣGk׮j=T9;#GSSX~ssr CQPB%*BO IY';!m113fd? IDAT|fܙh(/V.U߽GӧO?Q{PRwRNa j~y)v+ ( g۽gO]b?sjk6o0hѢEzz Vޱ%%dgggggۗq"ʘؚXvΝ;6m$7߬V{s}/puu ǘ(vO*ڟ9suZc[#G0qң599999QlYTjݦZ-INk٪5_^_};v8ud|B3>$ٹsܹs,3!~O[=R8"..ח=>)3YYY}mժ˗/_ܪU}V P!!))O>SL=ztA^VunP^]4iž| PܹӾ=ؼ ]ь9"..N.aƚ}nVrڵݺת]g||XajjC бӉ'iE($*IT%Q,_Ch5.aa7mMXEhK_ꄐT/'!d5n466Vne̒_йS'FS^eKgЧo͛Zv%[pQM6۹s 6jܸo&Wp0*>ljqǎSՋ/~7j5̖Q >|ʕʕ+h촵5Q ݻwju5,,,>>ꨨ(VBQrCnݺ;vKl5n޼iӦ:.(((::ԓTN,!$99^ mҤɒ%K4ĉR+sVYCfvd&6޳gVvnݻ߸qC(קUg?2,|g_SB|~ڴ=9|??&r !qqqOOK%?ujosO> … aa-˧NֳS7w6uT96[o-¹?G\ܨzy9rdLLLfffzxl~mҤI{R9i$7z8~.]ʓ3~gy&33駟?~\'tVy7ׯ`ٲe&MΟPA.?AeyLl:mkgOZ ̖ڴisEI㸟TPPݠABȔ)-^eR⭝;~=}Ge >~**88x_{|xo58qbwjqc.\8p 66vw !#J趭[l}:hK»啓'O^zO?=灨>#6&B"#gׇYrM쀈7,AǎfV?n\bqQe lѢEǏԩSzxe˖NV+2f C <6:xdVJׯ}4o߾]؈?vmŋK:+ 30˓&L\tS=zpW^};=RJ/^8_f1 ڼ`(,߿ֲU+Y[s(F0-q!!!ǎٳgW((T*`uP[8!yAxGB~J͛׮nȐ! B1M^7n;[#I#""< f9Q r +8 ŽהRP~1z+Vw~m֬Y}ݷFhVʀ>|G[+!>|.^tϧSS+rL̎$96Xvm׮[' 6o޲eˏ-[d͉OHس{UV>18www~\ءCUV9nUra^f;c8bmWW9zh4l޲YNN94ӥKeKwǏ'Ny>>! XyΝWZe丯WLsg^_di u=yyv^^nXXӧr_|G}_#bFq˖-ƍgǍehڵ˗_.6myٲaɒ]ɝv^&$$!ٳg^iJѣ̉ߊ5!eő7qUVU( cPQxnݺ|23sbȷd_]v߾꫄^}u-[LyOREDY_f͚ꫯmUK.KPPlBɢ Cxn>}XM~ţ~&T$I._h4$I ._P(Xy)c~-B[ݻO>O`uΡ>|8Vm;wOnl];C̾(_?A9ҰƦMކݟ{xƺ{ Lv)Um?־IB644̛A/^xTMOgoB:K''Ho!xX-SS24a8ի # FP]]}…7?{EQDC􆘎5(!mi`OB(PD<]i# /\8-h87$P ntttmgD|C# bc;Q- 2XnϿ4!C't Q| 5a0`_T_FsS;=07#P;1˓xRU5CHz*pF&-MDGo,uu. TςN>^BOOoc~p2UHBaff>nlB!1ܫM0B֣3ZWRP(׵޹=yaނ)?O>OݐAGD&I7 ]=?0b޷zzz# O?mQ CWA@ttt ptz{{2lo#0P= 1oQ7'~@ ^UW߿p8==Md[s`r9@ 7opPEQAQEQED˛ } Q@H@ BOyyӧ[57hoW( E2 [=z4)/ V`@ OP(,ƍ{d}.j`eP93S([o}Р`e5*0 -;9 b0nKsSt}anC4g@;?=#?~'0;#[߾}V՚TӤ )++s!ftAC?9QzNCNEöo4jiZsI`م QEf$ٺm[VVfs@ }[qecf8|ݻw]]?~0v>GkrV":W+Vh% cOBJM=Is>q@AQo u|gO0Ylj91a۷o;|w{w^//)QrEqqq666CeXIIDQtذa_|($Ys@E<,w> k9QQ{M,6'77kټFM2HۧBB/##aÆٝD#V>̜wؾcL54ҸN2-,=zU[[3JȃN9laС։ 񩩩ZsI 8+%0E>bϞ41|2e{qF:::MRԜYM_V_~m`k08z330h(1[QFj y槦c,E=z.5.ZSϟ=e2bQX؇FA[[`KEe2YFF)QԦSAD.(&&+Gy x >{*ܻ'*`uR7o\y˖WbBAbŢƆé7jhAzɜٳC6luaZt 1/~;wZ§O_uf?mULUO:㞯5⬔`E K[bum_B,j1F;wti[[;?|H9mMKO쳹:L>L(DmwvrzTky?,> pP( ]] |H7[ ' +rbjE%b)S>x}i[ yΈ陛%O>b qUs@E|uHW/~;::{_~hA1ZZZ0-LH;eeOqvvT2?nf{UUUGGރZ#F k9rH$23u{M-[n,m@ \͡ .( :刃mm2YW/ւKJ*f%&TS矆X_~)\r覎Mѣ<gӳQ0qٳgǏj78 cƔ9ќi'a ɇBlP(BA~.GGGG Y塤+W־zimm]/^Ο?_}FIEWlظrɓ'k-ꎎEbXo_思#+Meg'OW{BTx*Ԙ1c0_-rL]A%'dIJuIZՊ;w]rrqqٱxXyPcD5J۷C6ljŊ>q gbST}wP&6mw(~С߯Wva0X(ԁ mӈM; G^s5rʢE^ uuǎ5A݄艛T* ^;a쉊 ٰq8::dP6JBQrLL &N=zm[:YQ{ٕakҤIXΝ;W*.pr'J??6leK{i/(u˪`RT[xB|wmt#}l";Lf|Lt/?qCIZ׽|=>a޴1hH]jkk[;ο!8C dȐ?{ 6 h(܁ޠ\!MOOol 0g)U(*(*PY[rM:g}>>w7臃+^<}a<%0HO!w(PBѡ@ T*oW(PgR Z67Xa쇃.i΀v~ ^@ 2Ё@ Qd? @?|:GQ.^#B  >^H?gC @zXA hXA ho!!NY_{@ SU۫kllb^^^"Ў&9ŝL+X,[II &˳e0yyyUhNp*t+++ccc\^RRb-[&H(ԉB0jmi`=4(SNOLL$"XrӧO;Q(xЮ\2o<eaa[SS}?rMx< mkk~HOh\9γmp8'&&y={͹̲kcK.%%%=zmժUp!!!UhgjYٳǎ <88xɒ%`$44t(z8k͜„ ?#֠ɉ޽;EL`?8w67=/^1c:y͛7;::2lڴiO7 IDATj!7v/Bu"d?_~155uppx&rOnmm=s q2@Ѣ"I&i 4qZ~;P(v؁IlbeeUYYlݺݽ|׮]# rw^zu{{ݻwv܉mofϞ ҭ$isrM 444::<=ztNNܹs,K ~邂̈19E7+W?țbC>#U$000::z{ussnpA ȕMUHc0mmm(1>(5;⸸ تѣG[ZZ1!0 ;w*Pb0iiiNNN޽{3fp8;wdj>|X=hMMMիW+|V466{055g)&ϰ*4M*(N\չ\H$BQT$u9C)z8w67=aaa[l~:&y󦣣cGGBeeef@ S[[ ^zI35~67n899s`L&sܸql6O?U:lݺNkkk`` vXu&)˱e\N=-R Tj*;FѰBFu>\ >߿>|СCN222$ƿJj!AFaÆ~S>+iEx{{WTT`iӦTVV|jbڵkr;#/UVVbPGGGIE`3|ނ [o qסC)z8w67=;w|̙3'MTTTߞwފ)S`>`ߧ̎=F(}zfff0ZRn988( l\.0^zb޽ Ȗ-[Ο?mx͛ LLo?7_r DfT`ϟ?Ǫ׸ހNNiÇ]]].00bY?7U5!7v/Bu"d?_~155uppx&rOnmm=s ;(Y8.]YYyy܍1 Pq~&srf999sY[[X, Sh֭vfmO>]PPPTT7vʛ3r?I,:t裏>R+HGMaN@,>Ϟ=?W\uV<+++''*m۶Њ 6l۶G a"33Nd2>||7b贴WWWRwt-ڵk׎;*++pY (0z8C FHHHBBB@Q455uڵf@_n:6lٲ*A rof۶m2qҐ!Cÿv\bƏ?y~!55U͛>jHdi46  i4v>߿>|СCN222$ƿ Fڰao)tE apڴivvv|>B8.sCq8.O RqLϐs\. <>ƺfo\.ѣd;vLŋ=Ok@AS999%''6VWW'H,,,0abbdz,+;;[*9sbQx  aws%-gg第֬,ssseee_}:pt] 44TOOo۷ovZqL~gFHHH|||RRRPPE32 22R$=|͚5^:!!!<>M4&LUUUNNN\.w 1 _jܽ{7urr"ʕRdPu[={63g_ yh(`|'7nUUU3gp8QQQQ/--ŖKJJP߱c۷o Z'%/ &n  Fkk+&JQ/׭[~b('O:99(ZYYE>22o Twuummmmmm͝={ALOO7nCbU`@455k C{&OKQRՕ`VTT{nSSSsu-+.-yH HCCCqq;tuuuuu5ߢO7ѣGJzvl|%hkkCQdRh4e˄B!Jd-6r߿ #GZZZ={l'xHBCC_~MlqMkkkluҥwx3gd|{{{ F۷o_sssnn.ڷo_SSSnn.ne111Rt޽(bsrM@R'/^Lܤr^^^dPu+0oo{J={,Y󶷡h 2,!!ۛΝ;'Od(VXqyL?١Oq%5|\bPmllܻwH$Rr mׄd?U g7liiDرz`S1Ϟ=CQ+11Q&L>S!Gj?A⚛srrPջ ۷]V؁Ԁҧ*Cd<==yzzܽ{D" E\eff r7Y(*,X@*ѣ---iii@CsU0JZ #==9-- p޽3fp8;wlz4 _z=6ϊq8Fb4yuNVhREv2rH2JRgXQ6b. UX,F #\q8̙Fr64 X,UD,r%|(J$~:F~:1]g7,kLرcNkƍ+WDUDtbH5C)> FknnF\UwE[[[]\\Q;0t[nSR01ĩ2&H՞Ç:uꔑQ] >߿cu}///}}}oo`BSAB!F2 ȨQ6loug%-:h"oo L8mڴJ;;;>OѲ;yw吐ʲ2E1 44T!UUUcB'd0 ``ѽ}4OD0~vS8vسg>y򤸸Nx|#5_m K.jWTT̙3gȐ!/^1b^i]xѣiЩ2y}]t5XbbdzG.aaauuu$))cbR3gX,&trrJNNnmmMIIB! X8~Ny윕ښenn7CQ,<,SSSqڬqg1adddedd|GD*dxXL222p=v-<#ΝU___WW5o<A^|o߾W^~&n qs۶m~\k׮zK>>>mmmحXZZ;vȑ#xK:>pX,H$r1!w QrYoouSC)tJ@u! SNȐG:ujE(B*]%^'WP\ Lf.\E,Vwqtt411qttod2 B Cd!u4Z"hɒ%L&[tSUKr]@IsǏx汱777ccc\RWWp/^H6AVQ y;w@.#Yc,j>;+++1!0\jC“'O;񫫫1!9o{f?<&|>?-- fٳ֮X=d 6oܸ2httc@o& &n Yrrr0Cb8((]fϞd2|_T^^f'/\p8 >~Xw`4}Bx*|9s8ʕ+cX555^fffdyll æ$|f|~HHT*U \i&BCC;:9sl6VTTD'ywPP(<<纛rҥGZ _>"""<<<$$p8RC!TwѫD:{lFFƱcDŽB!.^dIii@ ؿnhh gsY9s%!\.#x*|7|~RAzzzPPq &X¥ӧ嵵ƍ:88<|֭[Gg/^ tJϯWUAAbbСC [jN7Muʥu-tAe2Ynn3/lqTE"p8l9??_._vm̙ɓ'(VUUP4&śqf͚uU%!hiiAQ֖IPj{=l5%%eڴit:`ԩW\$Bp޼y&&&ŗ/_.ZdΘ1Ν;d+d&Mt &X*'''.{d1 \xqƌ ɓ7o޴diӦ=yDހ C'NSRhh4ZRR;oR)R quٹ훚c˗/L(UBڊ;bX'ܸqۊ_;;"L(׭[}IRKKK咒N涶Ξ=[Uݡ{M<˗hRkczwru-+.-yH HCCCqq;tuuuuuW`ƍ{Q}YQ%... [~|>o`0Pmkkc2B!fnnnjjl2PH*Yfq\{{cB@pȑG=AQ\qv9DEkb7oZ[[cK.ݽ{\.9sF&ۓMUh4ھ}sssY,־}rssq+/J{NF|`;oEEEmmmO> AQ… իW& MMM.]>}:.TE؁ Fkkko޼*0H驶*NII `֭2>X!`K]%V`fS}+0T9f/[ZZ]kk+P囪hlll a䮮^O5(*x<^CCԢT7M8ڵkr\"\g)LUT%ZfFN.#^$S]]ga](zxx\zuʕDZA>:55UiL.&&&j L&Öe2?vrrZnvxkIj6iҤ<VZKil6T*ž4 [jmTZ.TN:Ddaaѓw daILL<~xvvѣ,+;;[*9sbaB''֔@@!X,NLL?~|u?Hm۶a?#JFFF2,>>ocooݙ@TtqqٵkW}}Z-59@ΝU___WW5o޼Nl,\.WUtlU!`KE)z 9)S VUUu)û0,J>`W(ܹhbbhU__d2===)%YH H$Zd trrt:R那%n=22GI9?cccM555nnnƸLJX[[_xlv;w@.#Yc,'N裏LLLf̘ݗ.]19j É/UOAAA,Fbn~~~tt4A7k,<%233-,,|~ZZ޾vŊX, x䳨0-QsμyT G&mذ|ƍe5 .\p8… ?~OP<9@CZ{7nst BX/'FQ';iCBBGA% ߿k}gppm-};v-5 ' 6LOOZ[_644TWW_p!""A!"8t:]OO/&&_]]Z~!݇}Kߎ}V`d #17C ~  [ @ m`@ m`@ m`@ m`@ m`@ m`D =Pwv8Ýu 333%Ǻ)t?tOШ;wmWCwpqpp`23g,((b`¼<[[[akk lH$ nUneeellKJJY,ֲe$ Y]Y]Nxt`!F|mU3rMx< mkkn{%Ǻ,,,,3gfnnnEEE@?B?~|rƍElt?g:T57N?6ҩS8qQR488{e2Y7+_5##cܹ=e7L&???33lڵk/]#77UVaGDDTTT`˜SZZpbbb(@C@Tm"k={6##رcB/YT ߿_I744tx"= 0 Pq<7 @z<@?| loŋBPxƍ)_iiiii*!O77N$%%% ޽{_~%&'v۩UhqݻwڵܹswUrMMMnݺuD")T`^ڼyCz !!{1c0arr2_t/01 :::`|???}}}__| !P}vyQ^^m``}b{EO:퍭v'=W4:jiiE 4mڴWbOΟ?`_S^zbYN'$$ɓ'YXXL8~FxKֆh[[ɤ h4sssSSe˖ B VZl6;,,߿ #G=z rӃ!F }5AGG͛7եK޽[. |||Μ9#&*4m߾}͹,k߾}MMMŋHҽ{v'i >0Νʍ7l555]ti鸐-JDT桒?Ԏ]h朜+++|$44(TՉjqq1c+V?^&vvvԆ0Lp?\.gX(666ZZZݻR$QMaMMӱsUMM*!Og婩ի"tϞ=$Ǐg0۶mbJE;wL-w?=>8իWۛh4(؃)~ FÎv2rH$rF)Rq}-*0*y=vҀF577&H$ƪ:QZ/rU"1(ׯhׯkjK.ݿX,D111/V%yEM&ɰձc*С(_mܸqʕDauuƍUJgZ4< 0(bCՂ۶mS-_Q]<}tPP6'H֌>|qq1.9|CN:eddI߿奯LHў>} P(hBU5jԆ ~NE-Z]ӦM+((X,v횇;!!!aaaeee(bBR E|#;BG$:::((hذa)B`0v(d2v\_>44TL QZ:p-[?3|>466~ \]].@U)o2 H$III011٣G[XlTz [[[SRRhC,'&&v3JZYYYYYYx3Eÿ+\xyGGGCCCl顯) d2Ytt4.IJJ"I DFFDϟYYE-VVVr<99Ys-``w9ΚFgȽ!>DѾKcW>wܨy!}DGGzk=k>pX,H$r@?BA޽[UU_ ˮ]ց)..nkk߿?;3 111ӦMS.Jd2r049#SNȐG:u*z```\\\```KK uKKcǎ9r͛7߻wU̬Yܨ{B?Moʚ2e ^paUUZƝ;wMLL===LgCCYH H$Zd trrt:B'[לHlby믿N/Ǐ_vmmm*L > #BQΝ;Σ(zԩc%... ťB_8pڵkq!9˝lvddڤuuueXSL9~8Ok׎;vرAAA=uR*rbjtPLC A<==mmmڑ5 ' 6 ہo}P]]}…A@8}`V`|  @ ނ@ |37@ @ @ @ @ @ ~dh9<@8Ýu 333%Ǻ)(([T`tqkhɜ&9ŝL+X,[II &˳e0yyyˋf{yyD" !АڝܪJ+==8;;Xe˖I$DsyRu `;&wI&AU&AJ@Q,t"*?u= /"\^ҭKUV/ ॊ\n93y2:<ɓ guLjF_ FټyX,Z޽{Ϝ9byye ڵkDP̘1cH r势?Ab8((14Xn8acρ=0?RC\^PPP[[nݺk555_|7lب( j@"B aMN:wرJ dj o82`ɇ@@G*0IKK{YyyyyyyKKˁ۷N>}ꐐ}"OgXXؐV]]}-O>a4Xa*1YXXg/bffX,4iRPPгgπhaaa؄ 0 \. 4iRHH\.gF۷b!. `.\j >RP8Çmmm!ꥹs疕˗/rǑ/^|TzMf&HfϞ]ZZjmmmcc?>twwRDh@0FP`DDcbY{ЫYHTu8wT*x1X8<~͝>}3)O:>2ŋoٲP8S.fc}~/fbB-bH/b._8}RX\5Z ڷoRT()))>>I;wZN:Ug)IOI=ڴiN6m\._`NB#ba2ԧӏlŐьyHr,L\ST...ϟ?Gdoo/Ç$I666::: 'E_6{{{{̡ʖ.]:@ lll.^hBrJ(,&][ Y)و}6Z<䧩~axx8tY' .}Yt'7IӦMAtTZ]] \\\v~~>jHHRTTy$?cǎQB&RGK-D"8bCϚ5T\%+${zzat뫥H LT;bbbbں222_TT4e%/**RT'O@tssʒd,"I^XXWXX(f$I~gC p&M }IVO>^P<}/d2(vvvh49e<^ckk{1VaXxDGG}۷ox"h鎈ɓ'W*͞0WBBB{{{[[[BB7aϟ?OJJJNN~qAP*]]] DPð7o666P<<ĉtCJrb%hKxz( 4<<<\G]]Aj͏9 `O 'IrTVy@ ?m/Fɹ33mfߘwy'!MMMgΜ@ 0=o1a+A @ UUH@ SV`@ @ L Z!@C 05h@ aj 1f "1g`` 44?~|ڴi: hb>t7]CD#ɪnXNNx~#OPbhѢWD&|OOϪ* :99q\''Ro"=z1緪ݝ^EdwZCS -geᤧhgmm?~;1.]4m4w3go6>"B"DEET*C,h6o,bqttVш:x3#Y:u*//رc---j@2ryAAAmmu֮] gddxzz~@ܰaC\\܃bccXPCaPjLo>K>42@yFܧOꈠ^* h3CD}3ӧ...?קLBM!--ٳg---ш:x3#1ܲݠ1 y%KX.>Npppx!I1VKJJu.@rFxEXe A)tA{A$"Ϟ=rFo"hJY<Ə?(Jjzܹ>4?񜝝+**xjjH$)-- Ksr8MIq<###44ST|>A 899=\D\n__'؛@.p+WW?k׀T*+DBS*VWW*$-Z$G#"hwP9l?yYwn޸w', :::*++w1qĉ'#"&&F(J۷o5rZ-IZ"ӧ輔kjj@)H$%[}!0 8.V^Ο^dG>>,޲5=h [Ν; `Y 1&&f[l|qC7t|hJ Lfm㏮2 A:wJ"IիWtq^#r力VV`<oA| bd8++Wٳggffsr|Y ̩:uB1AHV`@T$T*GufxDP?"4%"gs# q8ɓ'HRcGh33O?t/^M )ct*yTCk#**jׯgiD&+O~,v'M<^bbb<#111YYY---dֶ~8#T*\m6JennVOJIII===vYyyy%$$%$$x{{';jsssuƾ`HDP~BEh _pf P(y<۽{yfohooxm'bժU@ԹY\puutuunz Y 1E=OXzAѵ=r}L4d8TǏϛ7ٹx+eoʘ)0L bqdW>>>AAA6ӡ-88X 888={:؈8 +qƝ$I]1kɒ%X[[K$l}kk5k%R~zXLjƍE"H$ڴi"YWWNΝRDP?"4%"gs# AK{O>Nn6ěƫW֮] e ~ ]633s1__H''vd@l;3fۿ{7t̙8 o4p83351qtaXQQX0 LZ! '_mĸįB dn@ SV`@ @ L Z!@C 05h@ aj 1fӧt!cԀ#44tAE#ǧM6`fxԝ~~~A)6W\\-ZtU d2>YUUR'''.TZZ-TO =]]Gɱ*www>zꮮ.1$J4ДB` .]6mǰ>5͛bX,jZ^pۛ[[[_rߟ Xt}xڴi.])'ð?yyy^^^Ch`CS -:L#,ۏƐٳ򖖖k~߸qc̙k֬0,+++,,֭[>>>| hloo90  X2:}tz'4XĈ~SL݌1|seAb}N+nz>|Hdcci꒒wwwK mwww\h.^h"oYB1}l;G,YRVV#r^${{{XϟO< 2%}={ٙ}7F 54Д+D˥K>}(ųf'HfΜty?svvaŤҒyq3g΅ 5q<55U$ؔx/dFSSҥK\e=z899=466.ZH $$$PcR̟?ڵk@rrrO~9zt\.w…W\%K]122R!?Riuu5خrqq!Irڵ;v Iraaax=$Ɂo (?͛Rꫯ$ =|hLYD&%%% ,rLCe,a%%%}}}}}}%%%K.*4P mJ ӅxBB@ hlldIxiVVVԻ3fm/[O^v֝7ݭ'/; ð;vL8qĉ˙qj$IjZg`.O^SSRjj*Ԁ=S"P- iqHdeezꖖud""&&F(J۷oQ&=z)+W޺u jxDk׮iǏGEE18^CRo ɓ'rRzzzJJJ|~RRRwwwIIh|IZ-˥R޽[?#Ց >=W1| سgJJLL dw@V)k֬9}Zt}¬O)..իٳgS-+++oDniϗ/_h|>I{챵U(xo߾n:j@ӓ'Oh,mdxxxdd˗//Ed"nܸT@knn7,4Ж8Ϙ1n۶ T\x{{{ ޽{a/==]VKKKSiii, !YuuuEGG$IW`Gay1 $IRb XPP ɨѣ>>>,޲5=hr999==='[n9;; ;wR YTT^DØŋoٲF3)NyF+0p?~KWT*I^os8ʊ:R⠽NRVV&JI&#^1`x <@ P*$I*JʴP(]\`f7%B A$I_\|qQ/""Ã2ѦM>sj(CV`SNeaBOdsYpeo۷OT*?X߰2/M6F:b]]ʕ+|ٳ333yJh)BqjIqe4}anP(bqGGǠ^!cr$IJEÇx.7}eJH]cUVVF?#`8.MMMÇ:xY)] .8::655Q UP(矙 P(Y\xQtuu1W`zjӓ!1|o{ӦMKYJKKbڂ^ #I2??|zz77Jg.M>{̐4L3D򴴴+Wz{{SGaS1 Z U*}j`P Eq+0UfC־2zW!oOvuuӟ44nnn}}}YYY2lĴuuueddX[[1===??hʔ)TK>_TTRNcƌ!yݽP$QH:wPVh33O?t/^4z )cy*UGEmݺ|smPd2Y||Bx_~i]GGnj jɓjurr2Kx5&33aHֶ^ʑ#G4M^^%JRp=WɓkkkRj[3+!!-!!ðϟ'%%eee%''x+̳ښXRrmWlmm;jsss5j͛Gԧ`?1  IDATx<___+,,| Xj׏d,Ipuutuunz Y 1E=*KPx<77{Q-9-3TjD^Ǐ7osqqCeoʘ)tBp4B(**rpphnnYo gϲ$YChnnbHw^Jd63+&P̍RwSg \LWA$&&ZXXu͚5@Ȍ]PP`mm-Huf1DVoܸQ$DM6 F `s.]$7>dCJb1Oh$I8qbԩ:@E&`|KKK0Fѡ2]ZƸZ!719j^o ]F 05h@ aj @ Ԡ@ A+0@ SV`@ ci*xyd}l #X,tpAE/JKK\Siii|2N???  h+..vqqx-z*KJJd2"4Po"=EGɱ*www>zꮮ.>X,62.eM>42#¥KM6Vh47ob8::z \Jו+W AAAi.]4RNS.72b(//_lAAxzz^v  AV`6l{AlllTTi|2@P]]-RRRFۜ\./((]nxr;aϟȨ/M [5uOK^N;vXKK GFFVWWd}Ԁ˗/r@%Rhԑ`~xHKK{YyyyyyyKKˁۧԟYYYaaaշnOnooܹQ{Z]]]]] ei +0 &La)<2 \:iҤ\>,,,133S,O4)((ٳg@&-TArrro.ɓ'\pE$yĉ8wT*xF3hJM\ɯ mmm˖-;s a'--M"̞=455~_xR͛78h/&?^|9˥ Ν[VVlbx֬Y=WP&/]4,,iÆ ( ?ңק7CccP(LHHDKPYpvv~:bbb0 ۱cGxx8KKhB,Xu4330a„ ̸\firˮw60ojjj$ p\VKVx&8ӧOg%555,, lCV_LC8D"++իWx'A111BP*޾}2,77ѣ'PP^^>x{(]j?~ePz@M)K%LO<唒SRR󓒒KJJ@'Oj\.J $((hFGfVwwY҇ jꩍ={T@v ᑑ/_iiij:55חrH$ZyUEEٳ)KOO֩.PYjҥKbgg={lmm KKhBHLKKKLLdぴ/_\|}.)J2A_prrz 5 A92pD"э7m 38в(vvvF#2egݹyꪟ0 訬ܱc82 ѣ>>>LG _$T*SSSWXA d25UiPkkkll>Ӌdgg֭[`Ν> G 111/޲e˗gSCSV`:8>oT*$_zEp8uPA{1tL*ZZZGf63+&P@ o̝BP'C:u*O!J%IJR Ptr|:7:=LB,\|qwz{{$޽{;t@HV`P¶n MWG6mSCF492p|}}cbbΞ=>S7qLۧT* EJJl:"3K#*۷&MpΝ>6r8c ZZZp7wygڵrȑC8q(Be ið?7n{|pqܹW^mhh?D"a1 S*/^\raΝ{PftMT8'&&&%%ѣG033367Hs$Ӑ^LQQQ111 TGf!ye zb88LWdĉaÆhKLRsYZ2yţGZ[[mllXSo6rԱjtٳG_^8q"ݵk׿/}a#e`֭n~~0aZ;"""AV`|>HRcO>?iHnmff駟n߾ŋj́9e̔^%Co_ qYh֭[F,dxB/h囹ɓ'df jÇ5MffÐ<чm}}=]صkβlΜ9yyy&77wΜ9ciڼyER[N<';jssspI*tIIIYYY/^`i MwnWBBB{{{[[[BB>~ apppeeVmoo߷oY~ܹZFo{=䭷rss t@'JBpPE߼ ,W!+**\]]---]]]!<׷cAZ:gI= z Y 1E=nh/BݻGp8kP$I/R?~|޼yƍ6N)cZ 51ӈ# eYԙmmmٳ,-I===-,,6bX$ݻ sÇK.r˖-kjjɐtET_^, ÃzxxqqqA,\$;;gbb@ :www ]ںf` ONNfi MIׯ_q솠7D"Hi&pEj~ҥ<O"oKNNnd9:qԩSuPqtqq)--# g]]ݪU@ XjU}}>C,U `O 'G!ua```ڵ#5 AEe 9tР7!%(9wfͬp{ 7o\$8̙3qqq5"b3q\~!Ɩv18h@ t7͘5% gv܂2?$ @ɛzgbaA+PH@ aj @ Ԡ@ A+0@ SV`@ @ L Z!z1sMɯ/"qq>##dVUUW^e@Mqq [hիWXRR"|gUUKKK\Sii);;;S(,"i:@_+''‚ҡc⡦Wn-h)56 jf_ӪpႷ7Ϸ A7e}}G}DD"RTi4͛7bX YXrNO>]v|wYYpR1T 席PSxMdX]]-ght jkk׭[vZ ?>##/ ꒒wwwK mwww\h.^h" :88<|$FGGGj0uA{-YLGN:K% 8::?1+?85w\Co0gΜ .Ғ<ٹiYf}wH$3g? 466 ݻweҡֆ!444,ZH Ǜx>gϞuvvrvvv| Ӛ*lllJKK-iʕ+>>>$I|R(|$I)֩jotwwW}}}`[RҕJ@rqq'#ߺukϟ?݅BaKK1g…iiiMMMRPPԡd\.b[[̙3kccp܅ ^r2 <<<YDj?kN޽ʊzegݹyꪟ0 訬ܱcDL(;y!odp8ADGG:/9rd`믿_~ezz:>}jaaaӧOYD}bԩS׿>~p˝;wtJvɓg͚e'/  ~ʷ'O|;vʖ-[.[n񩫫۵ku aۻw͛7v ^tְE/ |hJTuV//I& '1!:::99TBUՂiݵkPa2Ξ=Oh4O S9سg'؞9s&Ǜ7owRauNNN__Rs}s̨$PxOJXX֭[6@/_dz///.atb91uuww9{,8uʕ?ŋmmm^^^\._`Αq! YDP@㮮!z1RYQ-]Ԕ]T*}8|)3TK#4eё ::B`AgFŋzI(꼟 Ÿ>t+\1ˀ:eUzFdSSÇ?p80|~vvJ lc>7wssꫯ藫(G0PC @8(Xye~~~r1lTOŽFĴuuueddX[[1===??~2T'O| effeeed2jT*g̘1$uzD"tҡӧ]]]? I]WՓ'OVɔ蘑AZL&W(O>/0 Ç5Mff(e`N3׆;vLZ0ő%::O?ݾ}ŋhBڝ3gѣG?c 6ƊSN|W̫խ)))sΥJ%˷m!啐֖OT:"_7njB1>aO0߿=11ܹs{=}34a#͛gii\\\ DZ/nMM StҫsgbÙ1cƺuZ[[IZ1/YʌZ޸qH$D6mWР"eœAE?zzzxiӦSgLa>EƥNǐ ʪuڴi)kkkDMҸ8 .\hOlmm]f@ Љh\h0R&؃}<)$1^"#8wP񍥽%(9wfͬ333.7o\$8̙3qqq@ gX߷G 5>MZh @Ѡ1&@ @ L Z!@C 05h@ aj @ Ԡb0S;#U$8100:^ 4,n80he4ŝ~~~A)tPLRqq [hիWXRR"|gUUKKK\Sii)PjCޢYXXPzUU;_zuWW ZCS -:!,\tiڴice-oFyfX,Z}w IDATY~ bqPP1 pႷ7Ϸ ?mڴK.g ˗-[FA׮]Æ4v4)))Z oC.򂂂u֭]ϟȨ/aÆFEEx _E_{ԩSyyyǎkiijLo> 0X)Hڋ7gϞ8p@_ˬ[n|'_7nܘ9s5k@c{{sΙ(DhhhhhhuuuuuuHHHHH6\塡&M #إ4 l/@œ&M z-,,0 0aa,BE$''gbx zhٲe<ӓ:s˗s7̭[t΁uuuEFFZ[[ 444,Y-YgҥaaaNNN6lDP<|9>5!=<|u `cǎpЄ,XZ`Kw*?ϟ?uVsssssm۶eԥǏwj*#a'Npss;|gMMMԩSo߾͞L}1h|w<i}O,--&L0a333.;|Y1ܲݠ1 #a8NjZ$Z-ǃv11w qǧO^SSRjjjXXخH$8K$%[}!0 8.V^Ο^ABTzm dޣGR@EhtCy(]j?~5q'2&RJ֙7ǟȫW***fϞM)_zz:Nue͚5OV՗.]?>;;;mmmckkP(XZBFdZZZbb"Kwh<}sq'''OD}uցmFC[*G0p 8Ht ppt'lnn]ss,l?yYwn޸w', :::*++wN(+VłLFMѣG{{{}||Xe j566K{rsrrzzz܀x-ggg@sNj 0&&f[l|P@'Д8ϟ?o+*$W^ѷ9hPVV&J---qD L NPHl\M:J$IR)x?M9Ւʰ\._`7:}*.A 6˗q- {7I~ݻwYCW $Iju ѣM6}Ag ѐuww9{loo/LE1 ڷoRT())), ̘8?~ðGdKڵk+++)ȑ#:qℹ9Pn߾7iҤ;wxa7nx!Ӌ a<ܹs^0|D"B3;w:tH ݻ7""q 2$qs9111)))=ѷ_P[[K`o8L_Jĉ (qÆ P]bbff;\.FdbccŋGذt1lrco]]uՌ`ĉ\.w׮]׿e˖[gϞĶ6 ,s>,lݺwK|rp'Ř[fff___VVL&q.ĴuuueddX[[1===??hʔ)TK>_TTRNcƌ!yݽP$QHXDhtFmff駟n߾ŋF39e̔^%Co_ qYh֭[FPjɓjurr2K3{{Çk4L :::fdd|Wm}}=]صkβlΜ9yyy&77wΜ9ciڼyER[N<';jssspI*KtIIIYYY/^`i MwnWBBB{{{[[[BB>+++Zm{{}ǶSN|W&M;w֡難{I*2y뭷ãhf10 8pR:xP(ԷA7#UHYv___r>dإ UV566Q'W$IVTTZZZR7ZA,"t֠ E`` ssswՒ9r>&SFg8oǏ7osqqPW@@ߔ1S -iđ2QTTܬS|X,݋3.>R>\t)]lYSS===-,,LxC.'''R~zXL/:.QWWD*7؈#b… @,yXwC]]mҵ$xHD">^cPj_u"8n5ZR]6| G cvfLy_|2y;L^D"9~80߲ sHccu@yxx81pMƂ8~EܹFeH$nJ\dLk`` ϗH$MCiǜdr$#bGgϞurrǧ*z`aXHHHEE[ Å**сUַTMB(ɤR@  *))!<ã#[F!c kT9U(Me%`/))k׮UT~\~}CVݶmX,qqqϏ+ɓ_n$\O2bQQѢEBP( }6R:+o,t`ZM3aB0,%%e򼼼 6DEE˗/gff}駄p {ȖQ<1=V$..nҥƦgᲿ0N6mZzBJ"%8uXIOOyQQQQQQ}}#G4 zҥAxxP( EDDDDDR:+o,UH\9z興\>'NH,,UK.y{{|WW׼gha3&,,'ɯ ͋-p d̙iiiӧOF(|2,,L x{{㏄_+:O>]t)ǣ65kիW9>,g̘׿#+F!?((#ù90{{8::~555TFusbtz ٵkWtt4Jڴisg4 L: 111r劯/g ?k֬7oVUUyzzJ$B8$3yR2j(AlllqÇzX!P*++# jFeeHapPmiia{W/cR_TWWs4߼ys\\ٯ&q}JkOc19۷;Rr5uYǫ$ɫfar`mmC,4͘1c4Mrr2@b A/jnnnFe†KEEԩS===N={67q_sZmggh>̝O5rrr_|ɡgM@..._}UOOω'Hy󢣣ccc8|.^811911qɒ%l䦱1%%e֬Yˊ rƱ{<-cxA3fhrPxRR=f*$PF󃃃[[[;ܙ3g}PR]]Mс{|}}|}}>>5͖-[D"H$ںu+q]q_SNݰaCcc#!ONN&Y;vyY'''0 0xȤiS^^aa!!!l7UH+oxh|*tlRboo?A9`"""^%Iؐ]0m#G8 hmmpBBB )0챶_BNNPo:m8y3$Ab}85Pd:0!97b]cks/$+t`:0K @`i @t`ՔdRT  fz* CCC;\AAy&!d,Kaa(,,ȖQw=?T9U(M\ iX???@vZJEPqɓ'u+Zv۶mbX,qԧ\s Ms'hѢEBP(ݾ}a9ʕ+-0̼~:0j^|9334((O?5W*!%%0BaXJJ`yyyeee6l"eټysBBBee޽{ccc9e2B({gL-)[t:ӦMWV(RСC CJzzϋ9bK,0"22222RP(X+WܼyB0{N?Z( l*gT* øsnjjjpvwwLPF#ԲrV{5l9g3qXt:ww~ɴ ̇#Ë/zyyx9s|צynPcx<^WW]]]p0JC)00bxƌW\9tD"6mw}GBQ>~<oѢEOaԌڵk;w\~=R% G 6M0lL&;w.ǣG{P(풒a2j8 OOOr c=a;ިrgȂ d2YwwwwwL& dKt2H;uJGH֭[wyFsu;{͜9X0j֦R9`su`& yرgٙB@AQӧף(jƍ*..&%Ȗc@[lyQ9ӭJkVXAJMo޽;++ ðԘ:#CQ4&&;vlDDB06U6L222rss'L`<ȼy󲳳srrR`onnVT΄, ??_V;wN pd(d DT*322NjTt?wy5:0zq/++ۻw'|BHΌ o߾}ʕ4͘1c4Mrr2_~j{ff&]f1...zSN:ujĥ~jF9|0w>l> lhhHJJINN~%f@@={8O4|W==='N ͋-++xĖ%K QAYf6L{'t ; cƌӡ B. 9T*U*ѣG٢0n…yGGGNNΔ)Sp\d<'8gZZZ|~pppkk`;s/ CBBV޽{<ȖQ.$kzV8]3<=@C>}z^^^V0N]fooB<u&XFh^nnnuuuzS+E"Qjj*JHnԐ+kkk a]]]PPĉ-0\.ONNJPMHH ϯl׭[a1n,H?ζ+9|2¨N '999<SXX]lٲE$D[ɨI)&ێ;{^tv;pQORb )// 0 ð @ܸqH$+V0:0+oxh|?*tlRboo?h,W$-MĆ҅ig9XwWxDkkkmm La5^?rrr:@003Xf?F_w8H, t`+ž?0/PO6z0 @`i4ЁX, t`a 4$1{ΤAN<gEFF5zO|XPAAy`g(mmmB044}1V@&IR@TRRB =<SBy愄ʽ{rd(d+ރ`{nEJ.]jdTM6^ZPHCB:@ uϟ?/*****?r7_{i֭[az% a8P( EDDDDDr d,ݕ+W?n޼YPV111>>>?s΄ >o9hͶ=.h42ϏͭjwwweJ0Zڵkr A/PgTt?Y32/zyyx9s|Pcy<^WW]]] bMMMϟ'$3f̸rʡC$ɴiӾ;¤aʕ|>޽{n+:K,qppVEQE?M EѴ4H4}B؆ب x-z G†РIbb"aT}&MDyzz޾}ÜǛ??c}2T*?sDB87gUU?a'e2ܹsyy<^ww7V㭁>%jnj$:ǻ|T(yAZ[[wemmGЁŝ2eJii)[- q[ "Gȏ\ii)ِH$&clCBQT$M4iڵd***OLp E"۷{zz>}k u0_*8qɓ;gϞrR)ARRRGGL&sss#ϝ;hrVt֬Yo>V'uiHW#butt\|Օ#+6B;^;a z(k40|߾}**..[dfxoo{fΜiOF7mԤ'7ghhhFFFIKK޽K|n/|>QKjZ@x[[]\\Nvܹa6sǵZP(5q";6 /^yYWWJH̙SWW斱tB0''g̙B>q|GjooOMM 'SNx...;vhoo qRl2F c g@^^T*%?˗/?ydWWǗ/_Α-566ݻwl15Fcǎ/͒!/X/qㆱN'zӀ@0l:0=PoZxoo/uޞP4iyL'Z0*z7q4 0{ё-`J%JBU*Uёts\>w\'''.@y󋏏x"qg9C~g8Nmnܸ(@fn`8v"3V(8KA<%5k:tHTZ-c|~jjj[[J:rȂ p///_b@ 9sfvv6yu@}}}[n 3Wfƍ*..UP3ӧOGQ2A8vXVVٳgɃBCCGCl9Xw}w˖-=2*gRvڊ+HY3ݻwgeeaca}֬Y7oެH$pHf0'>>>%%ɓ'dԨQP8ÇD1 cee'*++# jFeeHapPmiia{W/cR_TWWs4߼ys\\i&۾};3Fe@@<.//x6ОKRYYh"[[ۋ/[&88 i1ziEEEƍ{뭷bbb0Eј~{ر A''ozۙ> |={8R2S:fJlT¼y󲳳srrR`c@FFFnnn~~ HM@Vϝ;'8erZTfddL:ըVϟ?~<)1KzFgccܹڵk&0Nzq/++ۻw'|BHo&33B}+W}!J?g&uww̤ڦhƌh9c:_~Vvss3*6\\\***N9uٳ9lj;;;Fsa|| سgG嫯9q)7o^tttlllYYŋ'&&477'&&.YA/_|MXX6zhsA***f͚eي{'t / cƌӡW B. 9T*U*ѣG٢0n…yGGGNNΔ)SH}Z-wq hllLII1dUHƓgΜyBaHHuy󃃃[[[;cjс{|}}|}} ӳgvpp*((0ϰq2{{{%`:3Pм!:}9<<077/rhC 8q"+E"Qjj*)􍚚r%~mm-[&F!˓EB󫪪"ill\nazy^^D"9~8`ɈRܸqX,4gyyP('|||)h4[lD"h֭5pTzFs\hي|Y'''0 0tȤY^^aa!!!lKٹqFH$VXA# .$b I(O:uÆ iEt`-8'WNMJrp"""Rfkْ@ΐ|@,ْ5fK @`i4ЁXWj,8 Ô!YÝuɓ'%6@!~:0SoBde4y>>>|>͛P&IR@TRRB =<.d^CcAEoJa&olEOIim۶bXcSSS/\¢E BPtm6!c }P(H$jMӄ@T'0~:0˩ONZxde4lyrRHn444\{yyݻweee544+7؏CEUU?ae(vvv2eʥK,!(DӧœYfۣp@QbxƌW\9tD"6mw}G(0Ng|,Y@$>9Ҧ۲`ۮGQt߾}&M"_/I… 7mD!{zz޾}- .))q<**j׮]8ܹsz)Qp2[& 4\H@;&j'GJAsS#?bEc Z\\k.kk?vݨLѬX8KR 0,y">SL)--{+--< J$E% zzzpB@(DI&]#ClVEEE> ̄o&,,966֭[zɯZ*%%EVV'u1m-[Fiz1RTqqqT!_DB׭[wyFsuOOO@t|>QKjZ@x[[]\\9RՕ8&ϙ3MȘB ,8zhWWW{{{jjDUb(:uTcz)Cցmڴ)??DXȥT*Җ-[FIR|'Ovuu?~|Ш-Pcc޽{/^̖c5bbb;F46~3acÆ bCAAZfLё8(;I&I;iV/]vZXacs3a؁aT*qW*v`Dv (J jJ(vttPuTCF'G3 ڨWz{{g~l9X3¶QUTte6'z/OGGG,c :ds89q7n0$Uk֬9tRlooOIIYj1OÅWX!fΜM^ikv֭aaaܚ:0_qFb՞Jz׽7.**;v,++ٳvvvG {!!DQӧף(!d Ȼᄏe˖G3JT^vmŊLA2֖Cb32p`̘1Xh܌5 _pCF'l3}WUUQ3GrO` öǎkO+ښiccS]]8dCxJbuuu(::zܸqoVLL .dp!#NNN|MMMMqq1):{H V^㛛U*Uff3!ϟ0a)js!7o^vvvwwwNNT*2"P*SN5*s}}}ǏOJ Oo >غu;wt̙_jlT>#wٺu|xdddpjpqqꫯzzzN8b&=z __333wi41ch4dcmMa3fLYYia/^ܜdArrr_|igC GQ**ѣlB< rVr;faBƔYf0 8B`3<Ι3g}PR]]͘q-޽{< 4[ZZ|~pppkk+#]] F+NOxEff&}%~UUJp .^H233_x1|LPCKyyP(xɭ!4/lW:'m0n3N~GTWW4qDR377W,DTt[r -`f1G/㍍֭# DGlٲE$D[ÓqONNg!!!aRQQ&dp!1|…TNaÆF6MBZXe}KN'Epؐ]0m#G8 hmmpBBB`yl:}ۛA:0DIDAT`P:0K @`i4kaoo7ܼ9:.**ONj1% nͽȬ,#Mq5g_<{d~΁F0y./ mmmB044}|7oBL&JAPPPII !,,,x2 [U(M q0O ]VRB:[x,'Ol^pA?|Vm6X,⨏cL:|ׯ_HbT9~$qEEE- B0((ˁsVXAz裏BD"UT/vl۷}eݲmsM[>ygc>[>O3c c RRR0 S( v8\WVVa/_,-- O ͛*++ˑ-1_ k$..nҥƦh8 oڴi B*:trْ@ \]]: K8eʔA 󢢢#Gi>~zsWWK. jÓHBP("""""" ##cĈ˘Ǐ߹sg„ DhjuwwwWWWWWgggGGG57~fQA|xboIjjjpvwwLPF#* 0bO.kk׮sd1@'372/zyyx9s|Pcy<^WW]]]sgQ755?>|X,Ϙ1ʕ+H$ӦM+W|//{~/YZyEQ{65ED" 9b^gb600-Zɓ' GCCUR]]aXbb"۷ɄI  .rwwߴi)LLL0, -PSSsgg'7o93z`˜QB KJJ|||pڵk;w\~sB1sLh/<==9HT*?sDB&Ǜ??a dsxҨCf(n߾X\ CӀRĉ]]]'O4olٳ\NJ:;;e2@ HJJdnnnBxxs4\.6ЊΚ5kۧj}}}n" jD˗/rdņ^zvAZ}իWs'l BP6,,,--Mddd׭[wyFsuj7`d4GQ+HHazzFIOO 9sO?477 9%)ѕ|>Q̥KjZ@x[[]\\/9qjBFGGoڴVoo{fΜ1L6!Ht]կ>Pf]]+!3gN]][1:*)J77WR=/Xѣ]]]|>(;0ƽ{.^Xq\T-[*˓JN]|ɓ'?|rl9bbb;F46==s!3_`_|q  C^߿aݻɣ*t`z(R%j^궽=0i$M aX[[UrUooo$iHW3<+:!aWx[[#[&FA0 S*8+J2#@ (B2:9F@/nnnvvvVT0c@8S[7n(J?Ϛ5Kp؁999;<\>w\8L6!`??/g8<%lsΚ5k:T*SRRVZ斱t~۷y.//_b@ 9sfvv6a3gדX?%wfQ[lyO>EEQ7n\TTTqq1)9vXVVٳgɃBCCGCl9@ZRvڊ+HQM`YYY p> ?k֬7oVUUyzzJ$B8$3yR2j(Alll88CbQLC8*++# oL3<+CBoCQ_5~b9edmm8jR$$$,\pرƦG\-r5uYǫ$y<]WWGqƑ#Gr777|Xؼys\\2yyy۷owrssŒM>iEEEƍ{뭷bbb0I{œ'ONNN|MMMMqq1mg߂/nmmE-88 ;0RAܸ1̛7/;;;''G*vfJL322rss'L@j |Z}9@-1i[?wM ..?޹skja8^VVwO> Lfi0f+Wo1"J?g&uww̤Khƌh9c:_~Vvss3*6\\\***;vL՞:uz{{snjSVVo6sFN:sԩٳgs]l_PC{G;%:/NLLliiinnNLL\d III999/_$jjh HEEŬY< `Ϟ=zZh4wB:͋ww}2}T.X0uԤ5AΫWmPqhii ̙3^jс{|}}|}}ƌp'''gSzժ0FU 76 brJtA0AᕘCUww7y }}7@ΐ|aC;׻[˭׿F2[p9h> ׿J*z{{poxPSSa` ]8`4Q۷d 666vvv^^~M#W~X*$ 0{R[wUm2*e}2ߠ* n+++~|͑~oa9n#Gc`xE;8޺u#Fd\Voؽ;w> qg\O*u^]A"48sȑ}<͛XlY"w`MM y@d98 666CwFNhX__m/[ =_y#9iT!8 q~ҥoӟncmAaXW8QJ>cG>s`8vyJ l oQ5afqjmiW$uЁi Tpi@m~~K|:rd0 qI<ZD$q_pLoCm~?QOU#"Cӫhm'>{Z{_p p * - Wj HkkLveժU˖-H?W!b ԧ{! A4 *0Mx$#2H72v`w8'5ZvԨ_F+ XKF7j?L6-) 2hBlllT*/5Jqp/$=p/$ Pb ,@hq܌? ߁ont^H^w^Hx0Y}抨߁qsiӦ0\#;㸞  :|\ny抨cG=<.)Ν[`8;CHVxjt|s=w`A 11WC!jݝta6ַ+!/qxU_ oyx-E{[+eMUg77c+֎?>b ٢w9\w`z:/(T4{ztON#V{;oU;ϗ:6ƣUsĽV*MOn_mZ+jҙμJMwF|'x׿HMC;gҶF'ЁAz]MjӖQ#GXX[ukk[]zF',]+V1: ھ6}:4=:GZ#ҡizt^VkUH#7S=nʪߗuoO/~鬬. NLoWuV].MoxKө%ƌPvtMgtN]y FmkcmեEAuoGwooHUw@`"NPT>s|o#F X[#_5~?ϴ ":0?N÷Fھ9e滇 =qd1=ES*<90akoF#WPXj Ow0Z*d~F 0ѣl|4­|!2iۛ;?Ɏ:0y]#ݻ5G>:v`NӽHEd\gD~Ǐ?j(++ zXe}PX?tw*? HwAF Lj3,M㳚~|Ր__Z[=IGKIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-list-enable-filter-1.png000066400000000000000000002432721351617527000260350ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx𹒻K$$ ! **D# b OzA * "T)!$$r6?Z.ޯnv&{!rC>LBҴiLճBYw *,2"i՝oӦ !DIIe.B(%J)!>ykD9P1?QvznnRTJ(˲e7( 'ycd8j6P(9Z\.dYY[%!$'PJ)!@8A*P!D&#rQe'* Ji6y\V nA4 R*Gg{xgYeY=\P(VITuz1]I)`xP^wXcQJ)d29!\+ޙ-}zފ9^1?19nnnNNNRJ%yP13Y0،e3 ò,q ^dr\TNcxlZyvVV *AY{_V#͵IݿFQ*kX^9V J?bV{Annn~~gxyݻWzuV6?_LWBr P°<%es7\OܬZ>R‶t܉Qz?WR?2ثoVQ;ɇvBrz.SVJCA5]vN*j-|X^8u=zVwSzQp<:*)S~^dY=+$8T˫ZjFl܂BH||lNIIiРJ|Z1]IxT(Oa8hX}tMD0ahaM۫oViA.9m}N8l9(t0J n|{݋ٹZzٸTs'r2R}d2Fb~AN[ۆm=UN;ɏjulRfuL&{?v%-1Иv>&n?ӯMnuBN%M7Jӄ껷mrcX#rgOH`u!t O vv_b^^/r=O (< ^k=qqxNT*qھR!b =Xh))R˗/g˗/RbgfBX FYis]C|]|HW ƿYOѷmծwFЮnB654=GoGYc_.sNjc$dac'yZ]qqRij".$P_s5YZ`R*m8x6FCBȯ粃jd2vٸۄL=!&Y0T?CoHoo'EoXA+ؖ^IWb~MOQxlF)b)RΝro^J^ʐaXJr= ˞F`,xR(ϱArœ]2x <NJ٤xqN|o'fǧ\K)}GZ5\W׷Fo}i&3GBBp8DejԴIq;!0si]I!)]ϣ]66!D'p%W0~+VJ|c7Vr޹|:/35#[ P(01G\< :wl J)b6R$2,2j}fےf<8FOX&ޅx0rnmܸ@(!c䄐b?1Gye*ṧr,Cetq#P{:^zio&5$U+Fmw~WrJ"#!VVʞ)cXYuFG b9!D+j` BlmBo A9Q*nnn*J)Hzw<>P"M39=E%" XF9˖OT9vvzV!Drwyeti<R2 Ki d2f_9%-[J2:noed2Ji<JU^ZnćY׽Zsyqi`\W7%rDZN*YUT BO)A[-QJ8Vϱdݏݧ NOuAxq4lĔ>y+Ajf^xs7>J|b猛.-jhHX={3>EL7;bn<ψF8m>wyu|8Vϑvmlˏ56!MڧcxM2ArqH_yρۛϝs8P&ڳna>_ݼ|T777RY(gS79{TwwwjU:;/^S|^\142BȎ w2>|eĻFd2BtR5k+ 8zUGV tj+$Fƾ}Lmp;CyT*\~9.kւu~=,mJK|J:t1i􄩦[W ${-((jzG2JRVݻE+չKjnՎ;6gbx LآS߂\}A>SoiޏBrvuR; N}QݚF*u|?SP`m}XR2tϒ prrrrXetZ[5! aaa=j,˚E()Q`gfs JTi\)O*tUGs,?f[7lI6aҫI~T^?R*&J[T2\@R)\-0oU!CrٹVZ':Tyyyg&%ǖ*JH8a 8cYG]/>Xkvkvv6qlq^>qU+[/me+@GPz9 !i|3OM\n)+J8"0G]Ț|Sأ?eDj׺+QլkOLoolib/^[/)5)Ɇ9$K5ߖ&Jq) lٺS-ٱ3FU9]pg[o׮SS_(If_mߺu{1ZTT z]jש۷_v槟~Թs@:m>cǎb4m!7gܠz_ôP>a<\o>p-N>ߏ,\0jwU]!)8<0jV)Ezѽ{fgg'%%?Jmn޽{VEj0}ׯk֮Сig>6]P{_~)aևpX^]}EmѣVϵ/˾>{?4'yǎ n?v\δj׃՟^>5kvR_iGEE5iDRjȑ#b={7oV;tp%1q޽ 4P*{333{hzꕕe%lC2fwҥPZݿ\M۷oh4[+U$X"00P.Kw?ع ~m> _ڪ4О}'ݻo>A"#䰭"eʞSҒhJk !񇗷nO^>~]u;f3ڭ-[l~Iv}k`i 3lGgk>tPNN̙3~m1qϞ=QQQYYY z뭷đ#GX"??bY5j٨QYfYI4!>a{#F5jTfffXXXDD~…իW<0٪Lsn۶mtqf3ݝ5knٺն=(;>@T]HJ5l; w_?}ݻ/&JKyy,\^MFGGK?!o Ю}sK֬Y"OMR_f[n)1q!!!...GNLL ! 11::zԩ...SL{h)lCvZxxkxx/"&3&***##zq9/_`[xYb ;vR\BL7dgx ׮}Z~kmv1洝Z !?Z6$f!)X32^$-!?OCP eϒ~|k ~mXۡCZje6]Ru~E#7)fq%Ϟ ;qawoM޽v}aiYz/ɓcpOM\n̙3DcAIJJ7񑑑QQQ˖-N0aΜ9Ǐr )wmِ֬!CLlذaޱr2v#Ftؑ`TaUr<_~Ǐ5;b;;C?Ν;˖.-)-=tڵvQ~XN^xAJ?tp}_y6gt,^z)wp[qËp#^`܅2CDE~7 !ofbٌs̱fKn@BvLwc7P@:ׯ]uwwip0ay___:6d#:CӉo-[vcǎ)j:;;Y֨QCY~Ç7haaaYlD XfϞ}y[F~, r劋V0lT&I}}f~6{F>5$+ NP>t[ukIa?GzR#(7zyѦMJݸI+ح0AD/o;>Hlb|HB'$4(b#34/BYY_ IDAT]vÇ-B[W.kZA$Yp3??yDnXZa#PExydee۷O;ȫQtquzYf C={͜줤1cuBɓrrr,XвeK1qܹFބ-[l޼Yگ_ŋk%KJنDkժRJ]6mڴI&⇱GaUr?22RFFF6o޼H5q„O>vye'N$8j/xGcԏ ExȈ#~P\1~T#hQ=4k4%AӦM quuef޷Z)֧W_}믋 '(!;B,"e05P@o={d.ᇷ~Kĕ6wkftm۶>iasO#nxfqlwE:UP#;rhݺ'N?-EFa.]I-zQ/J>>344UV'Oܾ/!dÆ +VVڪU"##Ŝ7oyٳ$mH/&&FZdĀB۷رc feGVeٳ<~77|a+O'ORnovILfӧ7iTZ\s@ 'Jm%-YÇ"ʌO? FȻo=iu^C`8f3IiUBdT'd;![ .޽[z2w&&DAAM6v}|ZEvܬ݅, ?bM|S_;eBq2a.$|(*7s"vE#Xd,T>wm*Uw8"0G{zJ9 @6Ձ{/f~2Bզuޛ"ٺq)g]hُ|F!+X'%Μ9ճg J)%c9J)£9}I2(<ؾ}k4~ݺu5+ymP 0~„3gL41&&?)4504 !DP[PJ d60.@7a„˗/?vl7WN"r ϲ VjJO>$xzzٳG֋ iݺuyyyݺu0`iu.JXTe~<<tPNN̙3~m1qϞ=QQQYYY z뭷đ#GX"??bY5j٨QYfYI4!>a{#F5jTfffXXXDD~…իW<0٪Ls퉥@id۶mWy0TXn*P(ݻw=:11QL $SNuqq2eJttD ڵkᮮ򋘘2f̘ 4ۓuH<,_|!!!;PUg//'NmZtk&޾}{nݲ<==1111Q<I'Yrwwy_\jXPƍ7m4bĈ͛7߿ r:u)lOXWN#W^=v[oUN͛7wT Ҝ>hܸq鑑QQQ˖-N0aΜ9ǏKCbݻw5k6d"6lX|wll\.';vĈ;v$$$$UnXv8@i맦?~ԨQ6"q2!b?]C'["ח.DAAZ.]I&޸qSN*qƧO&;wQF3f̰ ??9rD&9r5k$ +3PTBoZx===o'B{ѣG-]4<<2j(qQ{K muwYdɌ3.]z퐐1TB6lꫯ!ׯ_5jZ279,,LK._~n"ܸqcĈ...b@Vhmڴ9p۷Ν{h)3Rhg*@СCcbbN8`BH7lؠ r3Zڼyx]Jb"7Ѻue˖9R.9rٲeSrr@UC8꥗^9s&!dƍ7ntvvLΝ;m%ڰaŋ߿oKmڴ12bA #ѣG5 &>TBF ܷn5U|ݰa#Gi߾x;6QNa׾,f[bŊ+l T gt "0!<0GCh0]`@Iݳm>ZϏ ^ P Zk`%UԻPiV 6RO(jH ;f+F~K9 ӭfl*zM˺ P>x+7zg߁Gh PRx,$@y ъܹ3$$DV7h`bL&dJe˖gϞ ~Cfff^4M^} ޽{5k:tɓ&Ji~~~xxxxx͚5QF55kVz P!?5k^xJi&íj:<<֭[6=uT)SDGG\"k׮2 ٨Q#kKLL $%&& >Qd2BѴi 6إB#5@TՖhΒeڙ"UXF)JUMWUED)8ʕ+:uSשFEj׮}=BH|||ڵq شiƎ{iecccGi%s޽#""rsssss#""Œ2oZvɒ%}-N*"D` 5jkXXXnݬd^reRRrr+2DDDܼy͛g.N*6lذaÌ)9k֬o>+Uyyy=zHTX8"0GCh>k◶e{˺ Pĕur/lp&ںn߾ 8"0GCh !p4D` 8"0GCh !p4D` 8"0GCh !p4D`,TT4JR*<%T'9P ]@ oIR(s^8wvI"8"0GCh !pE`;w Q 4ؼy(d2RlٲٳgDÂFoR^"{~k֬;tɓ'MpkRTfEϟf͚^xARoڴpZu떽{P!vji+05G*3|+L&S(M6ݰa]*Ċ矽{6d:=--LJQ^T&E 9mڴcǞ>}eؑ#GZܻw܈w(B6hРQFuJ+W&%%'''X(֤h 6l0DJԬYs߾}VRPeaM|GCh>T)I)I). 8>s*J)%c9tvjӲ#rPBK3 ЖF) @(zQ٪*F+$PB[71?%>x"JСC^~Y ²~ر֋ (+Wؽg3g ?;E\._reΝWZݱcE兄XYHL.\sNF@%'ˇRկǙK7L&+(((4sfFPdNJs1f~횓̤/D`E֧_k׮޾u8(StkŘo/ɓ/u/ԩK̙iBձK?ӭ ]HGCh.$!^|3n|p\˺ /.$%X ɔNʞ=z =H~p̩ulסJom鰎@i;z/)^ۻN5IRrʧ}n%CAܙ3gΜrRN/ ?{w~)f50R9WTy¯;L5TPPJ|J~u  KOO]B3+ t  wRS @c=׮f+L|JT<Ǎ3} ˺`T(_p(R8Ӿ}cnj90Ơ g S0qcǍ?]t1L/n-޿=Q[zQz؍aѥKK3v8c$OGqǟ>}vwwGPQ"#XzQ<{14SN?(c`!Jm6qw)*+=ėʚ(S0 xxwU;u:tHiX1"0zz'NQ&(vqb$|24!YYD:t b1gxmhႎ: 4(33w!*ْ(aPF Ա㢅 !U|< hԩS*< ٽϫ^ #SNhbKj E^^\&3L`w *7YȲSxRW_ FLb JOFzWYwJ4,$@dˊ>jD`:?yX-Z*`Gլ}!!`-yʕ֣Z4lݸ^ۅ]=rBBpczz왲8T2k׮U&9){rlI AYm@YwTg!qd'$d޾|joKĸG/'/?Pxydee۷oΜ9BG z ]H(#B@Y]H(#Bc.$9'>c#2B!7\J naā]H(AbY6)1+YYYeݗrD.WwoҴiݠ '''Ӂ(Q0,2kerZ]ڮ|OKMݻwlǯӪXɓ n@B.VPÓNPi8(܍sԇ<;Q_??K g B5n8//O^ .뾔/NNN 5Q(bNUB鄁 uvq-BQVv:lS7TtXIJJ㏓{ԖdhmOaK,wı#^^|qLJ32 *~SbYNPbHXe|p^G||֩S7Th sԩ~_iݦ3D)5U A  IDAT?~K  /bNѡ tW*.>mڸ..̛7oe[zxxx{{GGG߿c7YYY}iժՍ7nܸѪU>}uV9+.MEX(rrr _vԩXrrϷ-^@yaqZ6X)E6"CĔ۷owyk:ugGLLMM}c`?v;;wT{_(A@y^yu OLLk׮]Jq111Jrҥ#FP*b2GJ5tеk* BvڡCT*+e- T|||׮]j48;vtvvԩS\\T*̙o>1T*K)SJwմiSFYA*;,TOcjܹ3f3fRf?Ȍ{ojשB׮wޕ*4=7J)>5V%N<˯B32jשAiݦyw߾v;k\m۶8tP&,F` l߾#8ii׭[;sL1;NOog|2mAo͛;wG^mBtV?}n/v:/cRA0CBHllR !L֡}ĄYz/zL;sN:O6{u´l5eF?p4sgEQ{e[oŲaۗIJ,˲6/\\\&O,=%'OR&Lܹsnn48&Lx饗233{1a)'˲˖-OķRAez֭[LbڐQqBȸq֭[{رgvgǁg۶m={LHH ܹ_4yL,u}tӵ'MύR ̦Zn}5A0{~ʮ_>!dԏ.]p_X*^OQ(5Zdr᯾g`ѹs6oڨVǏhb1'L$#Hн{,m8?iġC?֭[_~| 0fǐ񵫫ԩ֮UTL2EV?ɘmPƏijc)Fz=2<^;xgvСx5Tz'OܺuéSvܩhN$Ϙ1C 8pСDGGO>=66҃O>]vc 11qРA{쉈Pͳ2{b!-Dύ6MUVΝ;;3w~ۆ4o.vw~ŗm۴ׯﲥKm(+#0Ӌ&}buc&0)׮^[Kc[ oo~ow[j%F`fJB*0-t͛t޽Hd<+ NgQKWe9[nnBMvڼyӠArEvye'N$wquzYfmʀZ+ѨmFFF3://SNu!//7ް,"1I^5~&zKK,5,۩SǕ+WtWtIİB񅋋s||%WW^?{lb셕'NF;wίZ ,T Çu62Af[:vX ֳm62rÇB>|֭-[>NXP:lzm!%߷SpplFE=z<˯һOoIKQRAAq^AͿqL&KX#mz[#u`v ,ԩSXN,y啗?)S 8me_3ꬭ{#4eذagϞX{{={vذa,ucǎ+W#_}5$''_paԨ'990B66>I@ŘC$a1V`Ǐ>}o㏗޽3q2XâEm  4{km߫`ً9:N40 88ð 1Ǎ?bJ9"D ð#V5<;%Z6s3g^-R؝3"S]|TRaiwذN:y?YBќ8~lK_׷rGK,666yw.agϜ~יؗ?{!C_@K;`yL>fMDd0u+̌c׿ϟ?'JV{oe~A4aoӱX&733Di;mI2jUk{{?KHx IP8lӧO XXċ{71c?/*]]o8#|@;Je祸+Dá:?PEȼڰ3!h/0w!>&z-y8o0ϷxpC(ĕ% t1&5 z; E﹃@tS 54ngha{n(cJLzsA**FAS7cAlp;{7,,:8@u M>b%lA"嶵8ZX <@u+"DaF@ i#}Vm-Ù8a"_ \jz@ 6<}?LDxG8pfǫ? 䕣D@ |[PDсl9R(~[P@{ @ XѵMtr`te;f_~[7\>k_0й@/p_  `b'EL>ơ{E-/}0'Kӯ0\~ϟ?Gqѹs50O4OXWW ]D?jbN?~|ᯏ> ġ{e^yaN.ׯ DrEʮM?CHiiiy>wZA10[ !7]2KT Nd&}ci G4+{/O,V\ҥ# yok`=>|# zƌ,myyytGG.\5+f߾K-u.}flk6JED&x~Ȃ!_7oLٹsƌNziD=՛ V{eW`f%Q9x2 O'O8Mb ttt,_ _y\~dכ]W_w{W^}mϞ/|+f&3'qߑ3egAAZ8<=|,"W^}mhKټm-y(??xsFX1nk׮Q^Ulaa1xɓ' lbʫ[y׬%҂}6n4r.D4La+7q' }kN?q}sЃɞ`cРA...vfff% KIlx׶myyy@㩾ӆ W_r&CjZ"v -yuuu`Βohh0aE0r.q׆)^GXxɼ+++M;%Zbo>4uƺ7/@!o?cǞT&ÆhQQ$VVVdRƨ`jK[AXX[[[I;v$G] D^T{m˖]vɁAk׭x"& WțffF}m`/|4k5Qv #_VNMyoq&Q}wukv)Ŕ&L|(痒СaNvz ~IKͳ y7yIpرnn # >|l}Y͞?|p_,7DkʔO"Vyt95}s^u ^}xn oo ð#V5<8+rrq'Ν;nˑor#xmp %];w:;;[ yא׆ ^k{{#,oț1 GRZfff@$S[[{ҲOxQo6|׆ z۰arDZ){Cu~$GG?Y2%׭ 2e/`:9ȭj@ \,%A9d~sԨw?\]ӵ\\]8 J_~q{n; ,Bo`5tذaMMM8KN_Eсwt!>MO&l`*3#}ܹ==Nbfkk{̋^9kS F6eHsL";`6F/GdyذaӦN`qe +v2eVE ,zӰگyee.lw(pi?߲~gǏ&OA &=ա?tM,g%6,͈L0iW'O2D,DNty5/W|okZcO;B?!^؟2kLKk׸\58XlMT׵ZGV"Jҫ{yy%$$* B-39q v/]J1`իWjuSSӱF!Co`[:T.a_k41$~`n[lpQkڴi6~67`z0c=J#D}xq=Ć h2F%}&9y=_ qׇa_~_0XӴF.."^^:3z~ y…~۪"dOjuDGGS0`[|\(;{ üv$')hm:rؑ#Gnذb3~۶;w4h{33@Cw56m?q.ajmk>_VJ0B5v>{&v$';V#~R~P#GIl3-9=o[ǩwwtt;Ç߆uwc6cƌ7 LwNaJllt鲔_O+ s`}|xP_7:{{s=Ǜ#^0`0{%N:2rҥ666u\ +C09~N{H`K☛HNJ D({677g/yOtbs='սދ^wE\zxsxI+0mS'5qs/*(H ftp/\ ^* @ V`@ ;+~ @}N[PW`A u]@ n@ @ L Z!pvE9-Ç[[[S뢈ǣg/_QQ<==|Ģ"@h"R"BmN<) ; tn'Fگ777VPPĦ`P,YDhC\PaP"bRwgGE~ MsA@j$ #=ݴVORT*t哓O>MqիWg͚% BߵkטDPg ȕ+Wϟ/ Rihh诿JiA ]WAgvܹ???999K.bDD… e2َ;XDM ݻwANthV^ØH &%%DbH"B/Sa" 5E/ u;D4$)Dd׮]O>zիWv^v̘1%~G}Qqqqqq%K,Y$B$-[fT@222-[V\\|֭@'5 SXoqr pJH$"+&??x!:;::93Ы<oԩW\a&Б wl;q_~[\t ޽iӦ 0XJJ =+cƌ)))L&ۿJ:pg65ͼynݺ0S,cURRN Qt:u:mH$++EUUU})tSL%A{gT uuu? ݺuV% 8qBzxxЛWp8 --- !!9??heIIIjz̨~TJr@SSS5MJJJ$ȓs9;;"9Kfs&-4H$k׮t=222dXGp8<'''uVRMPv޽ ŋO:hɓ'35ġ|0ռZV 8}vGGG\RǏ;;;?~$B~ff&^|yDD%WYBpsssttܰa Sunܸ1n8n@?mhAI2 ,''G&Q\Q()))snrwwD[l ,"fDDDnn.% {@Y0BT im6&&ߟ=&b* u;á fbŋqU@$555-XYY#r Wp8j66Q],IA.O,P tT L$)  E$47(Y4'SҒ-ټy3֭[we!p?ᴴ#ЕJ%0n{{;<駆!:"q"|X\cB!˓,X$BdrwN8Qрݷ~ęCx{{oʨꅅSL4G,'W`555CjRqŋ b+~~D+>ڊJ"Jv4ԭ<="z#Lb* u;ÁåKZR$brJ,?{ zFɭP yT]o8&!񩬬̜={鵠C~ސ2f9{!kkkb[:I2p8نiQ($Z \AEcB.XСC.ti:JEy '}p@;@mkkq}̚fIܑ#G͛JeZZ-8^ZZt3@c%rssj'[[[322d2j  Ejj*ӏS(ag0SI{ΐ!C?~ =hh 5-- #L'˟* IqttZ[[4qDjBܹp Z[[^Mk e.h=ztB0(((rK6V~a_7n򲴴}6(YD"9馠%A{g8qqq3N;RT"$''?~7zhB D...gΜ7Atyyy|~LBOsp|||B!I@s.4&4iÓ&Mtwwˣ;2,#̶mD"}|:!.^TFfD"HMD˜u,((H$D0P?"?;FnHPZJ* _=sL>occx LAvvvᵵLsrrlmmmll#(Q:uꔭ, ] aUÃ?NXxw.@ ^ B=m0vUn^aK+++O>@ az?OGcD'@+0CB"@C 05h@ aj @ Ԡ@ A+0@ SV`#}//j$;/GE?ѯѳϗdϯyyy|>bQQ@ XhRd6$:э@UPP , r9mHoa:44( 5-Rsa'Nr&LpP̙ciiIXSS3|>~ z+*gΝRtرϟ߱c7| TTTx{{[8 !D~3gθx<''#GN)))д40^r% :X\WW㸋Kyy9)&477fVVAz{xxӓI-ߺukܸq555CP^],WUUuL:u׮]zKMH鈴6)O2t3@R988׃<oԩW\!DӦM`a8yk׮ܺu jYwn^E0ݻ6m0pw3f\.W(FGGٳ(b㭭'N PɦT*}{IA_{Yb@ Xrejj*9s毿:p`|gaF夁_߿dȐ!]鉎NLL,//?uԝ;wM'N? 4- +zƍ_;::N:jO:H~H2 ?}v???moo'Mg) lmmڪP( Ǐ[YYa6w\kkkp*BQ*+VHLL9r$%22rڴi^||͛7qi0;vlĈnfffMM Yûix߻h3222!!$guu#Νsss1b(PQQN:thIIɬY6l"]6((,66v͚57޽;a<)^ɑdBHII;w.ؽu떻H$ڲeP(dYlwnYjٔ( 8pJ_dqx:qNYDXbE{DDDnn.8*H(ɦKv50JEL^x|'D&+++?0Npj5mmmmX,8~wƐa!D"BBwܼy3֭[weBdO'"ii`^Aەdeee{s1rguŊӦMaWVVFEE8^VV6o<@0nܸtpkZP^ &ʲe֯_ CE& RRRd2Yll]g<P6)l޼̙3ו+WfϞѣG+^SL| p8bV,X,CPXT3YSSC?Vѿx̙3E,ĎgyJસJ"Jv4zcEC{]988\tI*JX,|gϞ4A(~3vXGeO܅+0+++333gϞ|rNд4>>>>~Μ9L"])eT_hrz-hi0͟?a۶mgϞ׿o2nfm8{呑͝;?0 SfΝd t4iᑐҢץ^e =ztB0(((uttܻw/:TӉ#])~ݸq۠dCCC``  llldYhN>Jf ;$%] {\\PӾdggKRDLzѣ >,,L$9s G5$ ᔕBvQ^>c 䔖cpbLcj󫯾:xC: 7@z؇DFF1))I$D$Fze==h1 wɅ%:U 4s?zѳKOOJC })w5zhfQF,$$dС!!!.\`6۷qFTjffnkNYZZ>2dȒ%K YDhCzk׮/٫DI<<==}ԩ`z#++8h@qM N:5==]t͟ޥbڴib8>>}1.gee# of~~~?0￟7oaVVVVTT` r333@ٳ|>9''c8/{'^xn^Bܝ1cƨT* ~3fm&ON5,O罼|>=J.>Sɞ19{Ya {X,>~xkk'9T?XYYyzz޹sK}2 gϞ+V+Wɓ'`򵴴| ԐT*}{=Zvٲe[np8A1R%YPT'O\`L&{ALL g}6s_u@֯_PVVcbb (֭2eJYY1 7nܯq)V{ԩFp_aXtttbbbyySdGuuuUU՞={6mD7K/!zAS^^aG %:d֬Y6lڵkbcc׬YέS###Qv+**YJ~U9/0 ad:P(RRRΝ voݺ.l" YDMo߾,ooozp89(PpJDpt|>Edi6&&ߟ=D"'b/I0{ICJ^^^Z6$r9r +++p\Z}EIN׿`ɟB$)  fp8Db D" 655L&+++޳gϝ;wO͛g̘nݺ˗/!/^aiibzw mmmJ%QH9bYl޼̙3*naa)S(' Z'Du;7fe`xM6[[ݻ/qXX8`~ݾ};88xȐ!!!!ĵ=VUUq8! x5kta?~U(RI.E6^%q033NzqΝ;$a4bÒ?e„ X|y[[[nn=8ĔX nٲ/D+Vܼysyyyii)9;`>z(66wyWȘWTTBׯ-_~Ĉ!!!,%W^M?YtzODG ,555;;;77wȑyzRfkkK8|,"ԦѣG[[[=*Hs=4~ \Z} @Doo֌ L"20Bjggj9w)d /Djj۷92a„(NNNZ6==nA&'O\?STTԄ rrro߾M}Ox!N~!gIn-uP IDATH_~1 ŦՍ)~ݸq۠dCCC``  llldYhN>Jަb)ٳgiii>>>bG ,ZHTP׿KRc, rՍh x<[AAP9VPaKL'zM~RT*FGGt:ɧO8iիf BڵkL"OEh?"vh7,o^dIIIJJKK>>ZҥK,޲tҐެfաDI)hITUU9;;_reΜ9@9s&Ǜ5koF1qD0wwwq2ԟ9sXZZ# Z0a… Xܦe򡛂0ٺu|8\wYtkDDL<ڵkL 1Q\\ <==q_tMp߸qe(.8cƌ3g2P? \8y<ԩS\McXJJ =3fLII Pd2U*Ձ|>dW4NGo'GܯcccCzzCG"XYY-Z=f޼ynG)%C{g} %$$djz۶m .$IIIjz}cWDeF4n8&%%4uCKKK^^S'v8NBBBsss眝 yrŖ|@绸aaa'Nh4PtBCCnݪj)z{{?HMa2 LFJ|ۄݻw666@\xS4w}G?mA~wZ@ qq펎rť.?vvvL"OEhΎ9::nذ*B8vƍqƱtt#(BHII;w.ؽu떻H$ڲeP(dYlXb޽W`8-J8RXeژcGDD患Rl Z:wFfkk{1BD`655b r8frB\.%,7r!IP]8tp8Z%J2P#VVV&tD"QSSYx񢇇ECo^cb҃(Jza&#v?aS,b8yreseC\cB!˓,X$B4\dq2***$$IN N2ӭ233===kjjj5.^H0H6rT*mllD8z.$N_|>qJE4.$!@kk+cq;)hIν{fΜSwwR 2 hG%SI"^Zu0*Ϟ=ciX,.]jJ%{R,:1Y[[6)@8p 1iQ(j4V7.Ż,.;vlAA%á=2=ݵ[ܑ#G͛J%A8|,"&aN>|?%0oo֌ L F_ 77WV8qB x BaBHMMzH {5 )hIHssիSRRFlٲV Ü333Zmzz ޽{ZmffQ@G%z!8:::tH߿rFAK2֒dqqqrɓ'+W4kZZhfС&11غ_0tw;W?>>>>>~Μ9$$$ddd$&&>s A(޽[P(/B,3P? ٩MJJ8q"WfΝ߰\| ɏ=:~xPTQQAr࣑\*m8>m4uZihh fLƍ^^^^^^of*4D a'%oMAKBEh dʕ'vw޽j*=zD<_YY K,oܸd78U @ B3\.B\LbiWk 0nׇD"3g@-yѣRT"$''N9z m۶M$g9РxmmŋAu h4k֬H$$** ܿ KLLq<111,,_]ʂD"H$ zp:˵ i[[[,Bt503{X8A0t:ݾ}Ο?۾ el=m0vUn^aK+++O>a\P(LMMm_0CKL^v[Kz37@ A+0@ SV`@ @ L Z!@C 05h@ z79<.t˹'Ǻ(nig/_QQ<==|Ģ"@h"R"Bmx<77c, rՍ*B⒀ ;ɓBe1E) YKTTԡCݜh, @#AKB3 ϏYb2B|0_Yj~T*JWCINN>}4űNW^5kP( ~~~׮]c~.B[@{d2\2nY̽dɒJ6yd WUU3SNjiiڲe )H 5ͥKRܰaڵkU*ڵk?3߳i/PڐBÎݻwW\I(L%"SChf͚UXX{u:Qt('QJB3 gȄ;f^۽K(xRBCCBBBsnnjsYJRΞ=kgg$B4\n ō*@#p˗/Tz3RDdE]RpWTnnn,"ԦOaaVt钯qqqqyV[.PқаL>:44( 5- ϯ^\2gΜZ VVVΜ9͚5~#Ș8q"<[nEd(`9AиQ2 z W$]۷Lhp8)))ƍtP֮]{Ip֭[bwEEa CƇhoo5ĉ\g謕Z9\wYtkDD'OvSCLxxx"OOOǗ.]i&7nܸl2KdǺ(Θ1#??5??̙L"OE&'.Bh}7u+Wt 4m>|8c 7cƌۃIR8aXJJ =DhƌSRRL~Ju>"Bmp8¦xm͛7@Y|9ca0X)J@1Bٽ{./>uF84@1>jqwZ@ qq펎rť.?vvv899=~IiO;;;aS"x[[ۍ7ƍM(д ܵkh=00>ĉDǏLt#(BHII;w.ؽu떻H$ڲeP(d68RbʄW`8-Ce ژcGDD患Rl Z:54[[cǎH$I,477( r9ZDAF414) +ژ.V?DCQjʊ֙,Ӂ֪dϟ?'# ԦM3Ň˫C&Szp8RI/d'lbh@ !+8+/_p8/_6N;vP(r<)) "OE+++BBBD|aa)S,--"GPiCnܪrٳ?>zzz[effzzzj)\x~a"mVU*ݦ^z.$N2.$!@kk+cq;ˊb ZRouI˽{fΜSwwwbRfZ+LѠ D=z1j1u)^-={hbG LM68ϛ7///ŋ~ iH׉|,+օ =;dz9Nvv!vZ lj *v.$K8!Bhǎ[PPjJ%Q#(T XGGK||<ٶmkGGٯV`z~ ;rHBܼys}}R0Ҙ>Ejĉx{{fddd2c 4 -Td 5a "55!%$0ςȦ%YCO!477^:%%eԨQ˖-kmm0933Sզ@:::ݻWfffK'i9ct2QВ}7w 2֒dqqqrɓ'+Wt-J|]v|G$sxb,ܹsgZZڪUtk :su=<<db4P__?g jjj222?9 A(޽[P(/B,3P? ٩MJJ8q"WfΝ4 &?00*4D a'%oMAK2Ugi+W߿ݽ{Up$~ee%ԓR///XqFt@24rNTVV# O첗?Fun\\{T]__&\\\Μ9L^+<<|߾}hZZի?XO◗Đ A@@@~ҡ7dm&}hPqvŠކh45kH$D߅%&&8ԯeeeAAA"H$=xIimDم_VAE|NNMVV!B{zʦMMVVVgϞYYYZ[[_gΜX=jxp qct}?}A \.EFF.]T*/DWigO;4x9{gؒӧOb6=E z.kff& SSS{577OJJB/ Z!%}>uO]]i4Z(h@ }^\aJЛ@ L Z!@C 05h@ aj @ Ԡ@ A+0㰿ɱtno wqQ[ֶڪYJdXD "j[ܪR!XkWj!bNUx *30+3q&եgrr9{nrI2Xff\.DAAAwŒ oooPy s玟H$1cJbBmB5SWW&[ۼ@3WVV@ b*:Ⓚǒ̃bbM0Yxݻʹ6Ac&J@ h޻woʔ)bnѢEZIܹsb622ݻ. 1bĞ={:J9yĉE"Y+h^pabX,]xc'T4dvV`ǎ۲eKnnnPP'|J2---//o޼y~).X`999r|,BM&w$INND"IHHhm_ .\reAAAllEX 0 S2o޼k׮ &8j"qqq_1 uVFFF\\\L!0VTҠ@t%Њ3g۷/]4p@PP蜜ؕԽ{w8qo(gǎϿ{˗ 6sLrH|qNNNNNάYf͚1*dOƾef Ø^ R$ Y233@ P8j!&f8^TTm" IDATrJ^?}?K,]84Nyyydd$ 5]cJqqqmBB˴RiJNaaD"g&c{{C=z :#Gxzz }aYYĉx[dh>۟dJ+2]qqq666y~ܹs@gّ-Sf/^dNn^]U ڞ|?nȹs߮޸q믿677777S(єD :477HryjjZ޹sP(dBmk@ 0 8 64o  ΎЄF#'lllf̘QVVFLN SLq$jUȜ9sF ܺuDDDYFծ^z鄋pBq<,,,!!AծYP2yTS%p',,,))I)W` )##ť :LvEEaddd\\^'x<ڵk; QQQtJ Mb644d2јlmm(L&c4w777"c*JL+0"$.+ %i7oVc&-ã77SNqq544MK:˗ϛ7ppp˖-q$ťg'$C!Im V`**&&,onnr劓؜={ S `̙tgΜ=z4kyr9%膆B1yd)HX"dBׯpZ~ܹSVo߾u-K*++ccc'L@ 2O6]=Ǒ-[߿H$pJFhF i+ 0544kjjq\|X" `ɒ%gϞ%Z-mllS>!JًV.~w~~~ׯ_'NW<o$,@3BIZ~~~HHH$rrrںu+ 4F/׿xttҥKY)dGq&˵vh̙3vX0XRRx∈###ׯ_P__|$4dJ"KJ1c(7$g JS' !!w ,%%ۻKՊD"ԩS&&y<8-~ BpWV Iwh4%<"<LkrGyzz! `}Z1ib%HV`-gTJ_?KǏSq\-Z믿q|̘1K,Ybxi^Rȫ'Ox8k4cF֢Z&nϵeghr˗>}ݷlb0Ν;]\\fi;rȝ;wN6 ðӧ۷t:nݺu&K. _zx'NP=-+S({쭬LHH5jabxӦM *jRP*I2KK˼մZt:݆ رcۚڵkZ m^XQNЏ?8bXρQlv hll|üyN*H$ԩSݻ"$vDҢ=[zD"i`ZZۉ Ϸ%WHee̙3Mu j޵qi ;vddd?~iǂ@<ux28{M^~ԬڒÇ\ðn) |333XcA ՠxޡ? U&tA+0x @ A+0@ @ C j @ A+0@ D&.xc'Ѷȟ>l?]PYha)E"QPPН;w0##[(?ܹ'f̘RXuuuaaab8,,hg4WYYY#++%ZO4퀃bbM0Yxݻʹ6Ac&J@t=-NN:Q&{ghC$]̔)SN8>?~<<<A'{L"-Zj4ϝ;.mmm###޽K2L!!!C&]wz/1 9'ON8Q$Ϛ5 =<(-;e˖ܠO>JeZZZ^^޼y󈗥/X`999r|,BhD#HZۍv6o-.@sp•+W.Z%Z#s4ݼys׮]&L +CMqdE8믿bv֭B` c&SA+/NHHqh4]vɒ%:1̙}K. 8bfrrrtttNNNvvvppOJJJ޽{'PNH6nѣ .\plӦM9;v̟?ݻ/_6l̙3ٛ}g˾{+1\RI$DeffM@VqW,Bhs77bNjY^N \)J^ihY@q ixw### M)aܹs'N’@@0~.GqwT*kJ6X& 4oJVw %I||<0x;v:ѣm9rS ۷&NhmmMt) L蘕Bʕ+@XTTĥh~y$<oȑ'O*PLKG(fɚ6l>|ׯ_ogg7lذmw(r@o;w333nj#(qɓ'iii\b?|޽{?# )((dqqq666Ѿ̂UVQAӧFZ-yCL}IvvSEEET*>F^7O?uww_`53z/$)r+''|s玷7>{W]U ڞ|?nȹs߮޸q믿677777S(x<СCsssD.;w B!@ 0 8 &Gټ л@@Unndzc,Sx}Z׫T*I e/onnq\@-AvBó(y0 Jrƍ!!!'N$nڴݻm>wC~W@"V`\$bMZ j,%J{nj 8r ɔ%g}F Z$~RRҞ={HWXQ]]R Bx^^^ll~"6ݺuFINN}[ \DtV{H-TȔj RNyJ,!wvظpBB1xhFakJJ^ߺumz}JJJ#yJ:Jc.8;;޽`0Rvq2T caa,_@e*ry|||}}ÇΝ ...Dao޽W0 t:nݺuD+Bx'N]Uf o'yyydV0`N۰a!֭ٳ-ZD֟3gB3gZ&RHw޽~;mڴӧO`6r]vԑ#G4wuu]vQvq/cVT*-[F 7n pY񲲲4ͶmZTwmVMʄQFa6a„UVTWWZjĉLYg)޽{KaFYK?#bԩSM>Nt!yMMMhhP( %r[ S蹺r劏ϵkX YхDxд?Mql5s禦6m?>ē%%%HpJ˗/ﲛ8б`tMh!|???XLe]=񴊽{[֞@XZZdee~=:**J"ry7>>ܦq|Ϟ=2,11CVCgff6L&mܸ#d-v^Z"ݥm߾dz&Aݴii[!ݻ|޵kVJ~~رcرcY 'bիX3Map7nQP'N B;;><֪:dooooos >FL=xee̙3As.GzX0o<.Nd2LxbpGFcc#񹣮XAYͽݻv6㝸C :cǎǏ?XtUV={iǂjI'鵵%%%^r%a:%:fffb8))iǂ@ @ HLL|ڱ : &܈d1܄ FǍvhx.>$⅁K3l" hM&фLVl2dIzB@<:ČNsz37@ DWV`@ ] Z!@t5h@ ՠ@ DWV`@ ] Z!Nn] Cн{2X;]Fxia)E"QPPН;w0##[(?ܹ'f̘RXh[7bqXXX}}}یph<<<GVVKP!{M;bd1Ee,^xfZZZLLL, @Z PMh(8ɯ1xtMl &]Kiz/i f Ə/bqPPŋ8 EN=zv8~>vC9ay3Yrssj-[F +++h4C·~{MMM۷ooX eٲe_~Z/꫶ \d2RN>mkk-Tubiqƍ&L;w.!a2Qet+Jo޼f0ZeA2LL@dJhg`<ɉxm0J=pA~)phu ̏O[*~}-\ۺum޽###=z7n 'OdBR{֬YJCl4WVVVafmm-Tض\Aɓ/~eo]e~XtWXwu ð?^(e> YPPgcc,_{x@+ ҸWcJUVorjjСC2223ذaO8$:GqOƍvvvNNNYYY _~ O<D^^^W^[CodɒCG/x??b8 4&߱Zoرc$=>pBJO===/]䈉cǎ-]t XlѣG1 ^ba_ٳ!8Nbf@ `B.z}>eB!W{%///TsNBbiia=ĥP#8_~Ly.*++ꪫ8v)Nrʣo߾m NM "hB8s~~>q |8w6Rճg޸qcll&1}\]];$mKKGaii׶^^^rۚO0aժU555իV8q"ak׮MNN^nݓ'Of bxӦM *jRIʄQF1 hZt:݆ ػ߅p1bX,:ujQQܜ.djNkjjBCCBahhhmmmk.]r劏ϵkX YхDxд?Mqׄv#sMMM%67m4|NjKJJ8#J/_e7q3iV5Fn҆bq||<vjÆ >=aҁ~H$nnnGZCo5o޼;v[lYp!KR׷=~h$qMOKbJɬ^Z"Џ]Aʙ3g-:>sL&/^ EEE[uEEE1SJ$D2u{1 qrB|yUVV2 hoN=z:.Xܻ߽kg-cǎǏ?Xg>?|@é$ N`4&hM&\o6pK=/]}֖>|xʕu{@ 733D y%}ċ )7y@`8˩ !o|oavر-[}'@T*͛駟 O#ׯ_"6:NBBD"ɑH$ mZwa…+W,((]hKP!a v n޼k׮ &کɅݺu+###..FzVT<2FXxqBBFqڵK,$GЉ9goo۷o_ti`C5srr}bWRRR;)gJ7?W_-\0''4KPͮcٲoފb3aLoWT,t~~~`S jjڜ܊q/**rwwom ~~~JRן>}ߟ%Z.PQ^JOwy<22Єj&w\]]ϝ;7qJ ,)) Ǐ>=99yԨQ|>{JҸ񏂒 9$>>#Gxzz }aYYĉx___ vrrhJeǎC=z({=nܸO?}@Hѣ/^Ȓ& ̄*SĢIدvmO~nܹod jkkoܸ_s])hr<oС@"SSSjΝ;B!ڜG`q`0ukggMhL];x2fƌeeeit!!!`/8T5[ř3g[IDDĚ5kZիONxaaa Zv͚5ݩSdi5qZ|’t:BxI&]x`0܌FZhoÇgeeQ`0JƍCBB&NHݴiݻw}ܹJMMꫯD,3(m۹#I,jDժYLKJSS`7#&v 2,:(t:R;j“III{IOO8p !\bEuuJ09yyy~!;[nh4r Z.DtV{H-TȔj R^N=TK6Q-ظpBB1xhFakJJ^ߺumz}JJJJ:Jkb{nJQ;KLL||ӧO-[ {[\___ùs QحۻwRYYZZtu -N8V{vV%vXZZ%Zv:nÆ [ng^hYΜ9 bΜ9jlK!ݽ{iN>]PPaȑ#wڥSSSGvEŽZR\l[2nܸa0jjjyXg1h4۶mkQ>F黸d qmڴ199yСC]50ʺ\G!NZTTDh|pj$7 q&44T(;]r劏ϵkX YхDxд?MS#sMMM%67m4|Nj'KJJ|||R˻3/]YIׄVIB')X[oY[[{zzfddaiiiPPꨨ(DF<{TXXI|xp={dDYE9l0LqFͲkvzjDBvfooogg}vrl<ۛbwӦM#RH{w?vX@0v|I|Xzj+++LSxǍGTĉB>O8tCܹS(8^YY9sLМ˱K/ϟ/D"QHHx %&fV`e5nw8o"aǎǏڱ #&ɓV:{ӎ.(Țy0{`oxf֖>|xʕM|s 733IIIO;`Abbӎ^-ΚV`C"^;&!@0V`3:#-|@ @ DWC MZS@ g;mR:5 @ Ά3@ D'CT@ B"N'avx֯Ƌ⹦0~ a5 E4՗z<ӝ~X0^S<_gJu#*NҔ}؎ 4Zh _7Wvk4zhBHMv`y,f1EHXaZs9lfk\DfF ,R mEmlĿƑh|Dw+,3.?4MPM^_o}J/?р]ffؿޑaI&y7 .|G*?vh眰7Q5κzMOd?mԓ_ðG_=.zFp ziFMu1xðVW8wW2 ͡B TY۩,sqsOh Ǎ6o-)Z)-_4Yff:(CMqd|`S啫^&z9t1z ̀Xprd4h%up֯y|~ةނ;%;Oa}aނ1^?}l^ݼ-VfPޮ(h473sHq4#\*-k0κ_AC,1@3e8Xx+{`׀=SF7/>݁skT:n2x^u%Zg0^ɯ"6 ck'z'VZۼ@檶IcɄ^ۨg*:h6jߙ˞&w#al[$w+7~<{hqǿc7_gW0%>kz~6Q9,`?^<(#&Ҡ/FnO<E|lcIMDa;|7 :Ts-j$=σm[ဗzE˒ _7~ qܲEl00_ x  0l&8/(IJswhj]雏/3in?Y_`0̺YiF nf;`R>B2zsG+'>l5_aI4iNxߛCZ`CLR~)_YNoSٿ_`^ۨ7p܄&d

w{~80{@XYs^(W_ۃEm$}תt8רt`Ӫm%Zr~oid!4Z.@}9̬Q㑋ew0#ʯAb&HةQ^(_]fJcIV [4x@ZIیaD7%ugnW =ϷW:r6& ]UK%pdխw {)xSE4dm0 nUqk=%s3lOwlEI5 mxi`s>izo GH^0!yr~eOz-0;np;n#| 'a Ow+ǎ|A.JuKPFߴJfEGqtq0/!a_yגW77^yH9ٿaD:nP>pg; OL/ozma;|Wnedwa;Sspߜy abmN:G7:F^bnܪ%/&&T|`+f IDAT?͚a4].N`w+0a`_ gJԵa8@sxctBش7=̈i/x9nCx/ 1HðuQ#0 ;@}}̰A]FeA7O]{7|0hra`6Th`!9 uM>,uܫQm-=W}74.}hYMKyG;KO[C5d@!PX5bPS5DŽ>WV;!Uk5a8o6 nN*Hp`0 F ?*/6lL@ 7rW'J 3g/}_ϡWv}I1]c$Ln#yQvMTc :u‡*&xFaOx49PtMzW7̭Ǟ))XLjN^xN'/x>8߾Fے[Z?tgw nVOaVD*lh~7y \,q'ݬZLfSzT7Dhhh+@t<8szi𫽈oPJJ07{11 vыe仐y{/A + >a+w\С=ju4cX]^yѕ?/AP!w_y;6kF>RmOhJ8GjarIIv/^U?gOgKflfӍ[q8 jñ04l-~2U8~)juYeej]$̡G7_' K̉'[ 8|tָ71 ˺RN=p$K4=-+ 0{7sBalYAYͽݻv.uñ06xMuio & ,Egvp.28{M^~ԬڒÇ\CxN< ǰ:S~@t1fffy >x:!,H \huH^633#ov~rJss#-Zy蝥/R &S @ Y|⬷<篿:cƌM6>|Gׯ_OHHʟBp_Щ33W^yeРA&̬;F>}8::޻GyǏ;gϞ2… +VزeKppp~zq!‘B677grmffu M)~zl=xȫb0رckTT?k׮LQ_~_okh%Kw!Ćx$BF ]HG5M^FcM&ayAAh433B33W^y755mH4!H #LR5=r)!:;7]Ht@ ]D^0 #QQQj% bð#F9sФ$/qZya-i@ :o߾:nd t:뽼(M,YR[[[VVIٻ|O>رc99Tn6ciiY\\%~?C DcFEmڵ&M~WrVۿÇ'%%QlݺUR1cQ$''^z/'|rюrf͛R?z `痗?(Y~k@?,b=)E"QPPН;w0##[(?ܹ'f̘RX怃ŭx:A]]]XXX, oV4WYYY#++%ZO4JYLq$ mmm[ hAG Z/ M @ +cǎmٲ%777((O>BR7o޼O?,X0}\~z!9a7oܵk}V Hrrr$IBBB, hZpʕ+ bcc-Z-TȔ򿀆M;Ɛa)$&&fҤI\ӇF Z/@\0D l֭{|nܸ|@aiiqI!'O~ P*q޽g͚T*`U@͕7Y ZvnX8_> 2F5jԩS&M'ODDDD"//WҽЛ7999eee) {{{GG_~(ǷGOhA$ѣ^^^B5--ŋzsII EP,YBL#GK4)0@ /*\߶m[@@bqLLw}$R~h4 z}ttt\\k_>kkBkgnΜ9"hܹChudkko{3g2;ȗ_~dgg蘐M*""⫯ڼyP(d*j ,,,(Bt=PLOO߼yAQT*鿦[lYhK(,,,88СCmmm_W_{2Sݻ/^hѢGAs`oo3.K pqqy,T=Q7nܸ` ê7mD kt85?]mѧW/7!Њa---k׮]ti;V{U"a:ҨK%.4CtYߟyӯB"06mjjo߾xh? ðkz~pe]4[(aT;T$n ^ @ c?W0\ ~鲓yX-8 @0*"O6:D^d5YB/h@ +7ޢpXa@ |z3abK7'w(P7|cP ,+o?4C _kii?}OXvOY&چTc֬nVnOl^h!۴27?ޱZ/ki0Wxub ÄCeiP.ݯjU6akx/M#9ocJ1@`h@ 2 a8afffFʻpkDLkaٗ2c@JabXm7Kk^l?;ZZ?V'"~;f-7WK*ݮc p60 5\&8}Icekp@ѓ*&1OmqJA].4g* +C@@#0@íV`WKKi޽cעyvx&֫8os KbPi)'Zi/SD)Em^dSFR^E&ɏw?1o2tCK/Z Z@ ц  ZZZ O0oO9yj AҷM{&&mBa 6Zп^E)UI>G~PkkoKE –V-KSÍP}>ݞ=C _\4553^ӦMKIN lYo hw3s V( ?]F7/8ð[eMd^&SyM3ןB[D/mVT@w^5y֨ǬY{Z U.DbcfjB8+7u9G0G t҃/HF z/<$^jTbfoVbfѻ#X5񃂂>yYk #Hx!N5 4 ?j_D2t  ZH9ʪUσ>˚2b=)+lmma@0cx:t?ؙ}3;sL\\\qq'N())YrҥKp/**H$gBcV'hƾ}AQQ@ طo_;, XYf> Yv-Pab ;a[jj7)5C >j_k޻sM`O>=~x[[g}`֤u.5}bۢo$BWcx5trr:}tu.@,>>^,[ZZ<}N{իW!Caii  ((rɒ%bh0 ̂0x /Vа?|Æ nlld.n&u\lvbbرcMbט1cɓ'3fp8ϟx<77ׯkavvvQQQÆ ?~4QlbQΝ;:R鴵  ZZZ66HBaFFR$FP!8abwO<1zjp>wB *66v<oŊ111 jB;bEF  eX Skj.eeeϟ'WUUUVVn۶ H6nXVVFٴioiiΝ;p"7o ]lYKKwnٲ޽{\^!(]fT}X~}DDDYY٩Snݺ2xlɓ<˚Dj w܉:R8xǻc bbbͿX3x޻O_ ~f}LLz1Aatvxt'~zzzbbbVV֠A\.ONNtҩS0 yU?r]Bhq@]]Ç]F 喕>~ظ0 h(9s~痞~ٓ'O Ɗ5kָΚ5 0X|}>ٔᚺZg8lviii߾}]v=~[n 0*]VP#ElvyyEkk+!\.(nkk{֭d2;;Wk5ckώ;~ѣGO2ðWnڴCAѕ#w ]="aoV}>n>l5g{kǏ{emmc'oaf7ěQwj"+N\u'>a;! vfbb{MM }Jxa^^!BhqRp_qggG8^^^blvCi=V\.WT8P(MMh_h2e8"{{|F#ˉ]B& ={PJɵHRǥR)/3 ]fx}yqbb~l2 9sfvvv^^g}ƠF# 233xdrD"ٹsgKKW_}Х!Sk<}><|Rn]﷋ 0R0 kll}mEzBĤeee <n߾^. 68^RRh"d#F0{E3>>^T&$$H$c Xx,J +JثH`^쀚2\SW ҲKV[YYꈈBƴD" J+VۡыcbbF7TM?Gk,ЇBع_֭[pڵ/MTr0TѫW;v|Fzwl9y{ r"^3:T<3fL':%0́QC^xxzz` IDATѣ|_yy9fǎD.N*b(Bihh={6˝={vccō X]v͛ BNJv^6CjB[g8aaaBB&E"Qdd$ښ gg~^92OOOPn3Q=4 ?jv8=رc͸q㲳 o@@^5 DS-0OZ_;Ya|k<2wyw:~;dȐ5rӬv0 KHHW'R;@ / UUU~~~jlˆȑ#\ҳ,^8..D^ Yxm{{{ iӦd2͌ Xlnn@رc}׸nݺSNu+e ;4C NFO{c8P(?쳞$66655,{{ƍvqpcEpŋ744L2,KٳϟJ6m;w.!~/᪪*_etKR7F`?SbAgϞM4B$c&&&w~w rԩ]v 8pdʜ= 8uuu jhhXXXdddZ{[&߿?rȾ}nذI{700yyoԔ1}Q__O711qW^Za]){?0݇ B/(L4Mjjd՞SpO&[EW qAF` ~Z æLqʀ8p@V߿x+8rJ a0.-ZtQ60ZV8`Tqw޺uZV*FDD5 2; iQ"-kߡDP@#0@tC Yr%')>>^.hСCΝׯ_^^ޡCpƌ3fAAAnƍ@-S8p`cc9]9҄ԩS.]J PC xc . iQYr#; hM|B'l6M^MM=aU%k⿙h4fff=˛B'bnZ?''G"x<B vwwr'N|2zyyx r!8a)))XðiΜ9|>Μ9Rm4V577[Za櫯bxZVf/gv̙bbႂ'N\rҥ@zI$3ſoc} "@o>c XYf> Yv-P!"#=h1 RSSONVkBP740x7899/'::ӧW\rJee%j]Ʋ!Qr@@ꜜ///(  +Z|ԩyyy< >xyyK Xyyyh4'2xJEwCfUUU&Tz:vʫY,VBB˜1cl6Y޻pTVV~G666555srƍvzuXի@Ù0a¥Klĉ ,,ҭ(_܊vaa;K.ݶm (LB|e|>@" 322Jeff&1H ߿O?ںߺuAduu5a666o&c|rb b꭮&@+'|ok4`0 )tiBPGR?hQUUUeeellmۀdƍeeeDM6ܹ!C/xe˖\~=<<|ǎ`-[ݻgfffT^(]*М4!TWWϛ7oڵ10gKZ:7n?~|iiۦM!=Qao~ JKKw_!)// #<ݴiӋ---WTTb:ws΅wU0N8!HBL7n7n@ رcgBs8dOOO=ccG8j\ J|}}=P(}}}ehڐӧ}YYY`H$M҄GbxY1@F$%&(g8RT8BP*8.J{|BX]*М4X>ɓd s@>&@555 BBHIԞ́!^eWPH??{!pdnH8h4`[x<}EuEc8^SSCߥR_ 򼽽 ;8.fz*$N=V\.,@P(McB+(JXageJ&u0y}~~F.PH Ϟ=c^DWZ^CW% 4' ￯7,… ...?&$mh8'jOF`0 5jTnn.AG`CB8b(uUȘ,EZm߾^.ŁV8,ZA-啞T*E"xJ2!!A"[XMƊeeeTL-T+d#F@=@)][ZZzcZR%..D" J+VۡыcbbF7TM?%U9PW;'nڴiܹz&-'''"@ݻ,TT ;voF9r?# jjjݛhaLDG~~~&;vlRR8]-.Jϟr===޽K 롡a\.wX]cu57o2x 2TDANKަjBPqڟ4X,"##]>>>ք>00P 8;;*EdwCz|9ixmc#\*+**f4i="'&S90Ktv…|ĉÆ KNN&^^^|>~'>Z_D"Hn:pE2000""񈈈@I`;k Ȋ@ VdE$̩M+"ѥ}4ZF`d(* gpqqqפ$O۷o[XXL6M&1hfddbsss|cǎӧDf@ vz9 nܸvy;;pŋ744L2eΝ gϞ=T*nׯ_zi/!z4C D7A=x޾o߾6l >{lҤI"ʕ+V:~x޽1 +++srr277DEE؀߿?rHPp ۷o```vv67|r{ejjJ<ٳ?<##oh@ DŽ}+|}}@ <|Lv@J.{SFGGO4I&yyyʥnjkku͜9R^D"ё#G ѣGHtq jll @ @ ˗/ZO>]pg>}KKK޽ۿ;w]O>_~KyDDw}ʕիWSbcbb];qT_QҥK.\aXee%sY !*0hWdnܸyΝ={1ׯ޺t...%%% B"9R_nݵk0 ;rȧ~ Fx2 =쉀F`nƍwZ~B8eʔ7666VVVrE=f annnwwwh]є5 {[x3UVJ҈QFa?& !9xɓ'ha||\.gǏ!7\;tйs뗗w!h]111lll477+#%MLLLMMNtRb8մwpp2dŋ;ֱ zfSޡ@m)M!C{.޳gv)4W J[[_aÆݻwO>>@r'Nxe ,,,x , Bhq6 Xll3̙̙#J-n,&@cp\]]sss 4쀓'O|+)&a)))ք2z+!@z|9ixE ^E 111= DFF~~]reڴi|>\zðK.͝; {aYf|;;kתT*fg5D̙3qqq>>>'N())YrҥKp/**H$u}ZO֯_?c co>@PTT$glqc65k޽Ç!!!c8PoBhE)\Pa0~KMM>}:Yjwߥ~72n~Bs@[KJJzDG>|x; -Ydɒ%%$$ݸqO?0lw_ Nfg5D,>>^,[ZZ<} VVV.\`BpomFAAAPP%K -n,&@cemm P!C6l8|pcc#sqfJJ֭[bqg=fe68vX1bט1cɓ'3fp8Čy<Ћh;;;ܨaÆ۟?(>rGv)Ԕ`v1ww>}hZJ5` ߿dɒzkV:{,YhIIISL!l>~X  J233 B^jee%1jbccS]]mlv *66v<oŊ7[]MV$}O>zƤ]X,ʬŔ.Onj_陲-ݶmlܸѱ(i&__ҝ;wW!myfhheZZZ_cw˖-33{%tUz@sҨ'(бwɓlBMB`ƍǏ_ZZi&B`fMyf !~2(SRtPo BBBBP(ܵkÇf[[[1СCaÆ(JLv!.ٙ<~r:3'NH$uuudL&7n7N ر3˗/OJJ j8kZ. J|}}=P(}}}ehڐӧ}YYY`H$Mp8)))ɞ,)L!dX/^ 幹ذX,q@D`kkB@X,JxKK y(. R)RM)]*М4 PDj B ԕ2 $f;A!3MMM3 33P!]Bff&2HQbq}񀀀d2To߾yZ[[u}g8Μ9988 f}]-&'ܺoa&'`xm۶K[G`555]*Qyyyކ)ťRX,nlld.p///wqqiAi=V\.WT8P(MMZTr8h2KEPP*y}~~F.PHN gϞ1TA/Rr-SyWj>:t7 8 hxI  6l!jlT*KBA  F`zBĤeee/0lr<..q$$$\.]S<<<~vLyzz+ʄD Fm4V</++KReffx<oB]0L&Č1!%U$?BMJOOW*" KKK]oLSVVVj:""iZB"H¤Riuu+vE蘘h /@@IhNb_c>W">vؤ$FHf;A:NNN3@ޢ@$jߨU4 \~Z@ ^Bl V511yύyk!D'CS Qbx]͉'? IDAT-ߊD޽{x$rmmӉ'ڤjQ>#XPr ϟ/8Hqomm=~_|AKKKoݺ֦^z񠠠DBqҥ_~Ь}={&L#c+"JA@!5kΝ;/^ܷo)S?/l}||p޽3f̰ywQ)رcpB[0 ={Ν;?~lTE+W1bmahM|B'.b@8O޾}{mmmRe^Fǎ۵kWsssO;kM|=s`999avv;˝8q"qˋ-X@.3sss]]]9knnhjj3gϟ3gT*5@]PoBhElPa}||^aإKΝbq@@{0 {Y|ڵkU*Ygv̙b? N8QRRrJp V^="D~!5kvÐkی} "@o>c XA*V^uv ~ӧ}]jj7|SYYI(7뀦;М4"^r={6f̘BjkkގAAAAAAEEEEEEK,Yd a EEE7nO1 [|;w~ Y 3ŖO>hkkkf!Caii ؟ی KK%K[XM .P!"@ 6>|8T3%%e֭b@N;2 NLL;v,k̘1yyy@ɓ3fp8bF<ZElvttCnnnTT԰aϟ?=zekk)-7yWj3,,ˋ|7=>|H$L8NLTSԂ!faϞ=BpDq>>rG`fjjp0 ;vSPժT`v%K[ Xjٳgɚ+w=C؟щ &X,5|b H$GQ(Gr Bhb;;;eggG hZǵZ-rz X,H$]`Aee%7zj̙3oܸSL۷o nnn7oޤ¨A XՑZ[[gggg5 44T j͍^ڻwosssNNۻw/rrrZ͛o>J_i=9I<ѫIּyȻ(>gzv6[A-PF 233jutt?yb?7͊ ''' qtt ,\]]"SL9tBJ ՂOjr­vQOjrao޶mX1NĬAB\|ҥSNavUVUWW'`Z|̙~_zzٳgOto[oZtu+0**‡@tSLy뭷rѣAP~tl; a ,x>3oݺuqɓ'<ظq͛7~K~eeefSco,11ݽKRx<0//!8U*8+ M8;;?zrcJ豂 -sV*="MtqqQ(!J;k>??_rbP(ljj" gϞ1TA/Rr-RqTj́1tUr05<ѫIP(Bh (9 f^g 4 h0T`X 2L QrssuM&¡CT*AYͭl4Fw mt?u5g|>_VJ ; )yhEP]Wm]Xo R999`ǵ,&&&---++kpr<..nذa񒒒{t!8RT<+:J2!!A"[XM .PPc&bbbF*.S4ӕJezzH$@ҲKV[YYꈈBj D" J+VۡыcbbF7TM?%Ut5@x +ZNNN@Mc&%%i4Uz]/P 5_@m;#BjjVMMM}DR 4HV8pPsrrqKMM%nnnz1!ӧOohh裏0 ٻwoBBBDD }Ld2\~!PK <"]Џ:o߾joggGAA͛pQa2|G~~~&'rqZڵk666ی a\.wٍ7] .P!CEt!4m)T*?>{.Ɔ1,,L(YȻbH$$vUTTX[[@@?Ы5C@lyzz v4BsǺGع PBӳ ^RR! nJ܀7:`YhFCMzg$`Xw&L vĉaÆ%'''Ncmm .zOSj/D"hݺu#"")Am'P?;(,--4Vzܡ3ܹs\.n>09ulPAcRզdggX[[[ۅ /^u^!yAZլ=E l>ӾJq8Ȟ!D'CS Qbxltzo¡D#0dǣjy~=B"@ :4C n @ A#0@ @ 4C n ɫAӧ4uQ;vlСhtP0&tzF`999avv;˝8q˗ˋ-X@.3 /n8MMMss̑J7huuup8 BЊ$1ɓ|>`aXJJ5 mz,_KNHHHRRR.4W_}%b922￧vA+WM|իWui2͜9B+2vZKΝbq@@{0 {Y|ڵkU*Y0Xٿϟ?sz Û,YR\\P(> \reeeR>|8N0!99999yǎ BË͛7lؠP(6lذec XDZ/ }`޾}{+V $L5݋!Ɇz,CsxaNtVx޽{###lv gÇ?}BP(O1b.MEEE͙3B+2vZ/^P(rɓ'njIe2YSSӞ={BCCɚěvjmmmiijFRJR!dR.l.fn=#0\.Zr8BBpuue^p=zxyy Xyyyh4'2xJEzO[`L<* ЄjN:5//OW 0fk]ޅ >!ܮ;w.7nܵkЋXQF={vvvv#G ===Bahh acyWj344T(zzzT= f;[A-b/z+tF Xp@0i$"n Ù0a¥K''N(q^P[QQ.,,twwq|ҥ۶mq|֭8!Oh3~vP8eʔRT*srruiQw J3)C+pLII6l`2\{[[[CC===qp8JRT`[[ehT*Rh~B.ɤMkj=}Z]UIuhE$ÇD"9rB8z(e^p8Vq\նx;7b;;;eggGhBzE,K$.X=jz̙7n{)ti۷ B777o24p}b_?;;;P Tnnn*EX,޽{srrx<޽{_xC2o޼}TZoKX@SF7yWjHywQR}Μ9$ԛD-mrZ0,_7V팺(::ZVGGGw\v|R111 :uJV`zEhr\ތ34 qiر_رcR)1LVTT899h8::VTT҄:/ɜt.Њ0wJ,+**9;;}uNVUU8>eʔC) TIZN8!H(2,**?_oܸ1n8@c> 4t 5@ѣG Err/ MVT[[2}tc`^:++ D})]'%%\t5p(?/^ 幹Ĭ@ hjj"[%ze aX* 6Q\(T*}F`cAIhN_{^M"B!JuLoBZ yܭ 4  A;(j 2[AAuM&ƅB! I/X/z>!Oh3~vP~pl߾}Н޴ieyhEPZ;jnnu@[[[u}g8Μ9988Ǔ/򴶶v,11ݽKR //>^\/=r5+. f2 iUHzERp#ݔ.MR 姂>??_rr Ϟ=c^DWZ^ñ 4' ￯7zD&}MuLouj@zG`TC8jԨ\]q# dгniii,+-- ~jfW\jU*eUHhHC+=jl:zkW(;jWK#oӟTV#I#0=BĤeee <n߾^. 68^RRh"Q 3>>^T&$$H$c Xx,J u 0L3b`V_uҥ啞T*E"C ҲKV[YYꈈBG~@I"I+VЋ111Q^a8T$fp5Q]!|% R H>vؤ$FHf^ 4kTڤwF3TVJ4hZ>p▚JbBREȧOP__GaVSSwބϟmP?;(drСCBP&yhEwu޾}[644:/((ؼyҥKɚJϟ?3}zcc#aMg??a2G~~~l68qӅ7ٳgsٳg766ÂQj=V׮]𰱱 nz 2TD1I7ՔJrwehᄅw&--M,DHbWEE5! ? z%vYY٫x'>X@/4' p{<ѫI܉_VVT/)) [n%IЛzZ0,_@&3 Xݻw &q;qİa쒓 N|>gkkk ]p@ υ@Q_~H$D֭#""p 8}BfB젰O ??ҤjuZqsy{{s\;;ŋԩSAqB:'|"- T2ρʆw~:wF4al6ۨZmJJJvvٳgΫcv…/vWޝ4ׁq7nnmmmkkZZZ-KKK7o^ rXڦ-?~޽0l@ `l>ӾJq8ȞѫW؃?pu1@t2;7!Z ͶN=z7_V~ah@ NxtQ-/(Jsssf@ hz]U-mB#0@ }^Ғ5k=z4ۛ]D 0?X|%Lvkk+]tРA.Ʊ漏g,@  py<.iik@W!@ 4C n  4Zmedk@ ݀Y@3ff^}͑uVz܂᡾'wN9?~TO{@ ^m>[q3Ruv_'M8Co'r2 caXo}!"^pgXCb^|iob8sf ^c\. EqL0$@ZZU%T-E&R+U@QQ E!` I0P,9}Xjw{Ƴg7AKX >缜%\JP\"e ,eÆpx֪#'0ι(|ńcׯ55_:_۪"p!/)ΝblI؊Ӄ3(W !n\/ĶnΗ\ YozpX0ر9r$Y( !nE^^vCDIDATOM ]H``iSP>,R~nݻЉkŒ !t" >|09/pTs<ŝ`iSP> O4k"xoh}}[#$0-64C--*`50(`ᇈgmpp-,$o Ϯn+9:j!a5O h4zwvo~MDkTth+SeYc3ZQ {[ӦtBU H xcfJ,xIR6.C-$Tj!*Oؿo}k'H8zKߚIZHBUy"k$o W.^le9B֎X;_`/8n{0`מ=V8c4??||:˗/YչzrϽ?2JdSoaղT&oQr>UrޖN&Jw&3ڕ۳Tz<>+/5T;WH,~ogyx2l$'N_;Ge*N`EM@_۾ڕt]5ēߟyDZ9o\;%MR"q5u92E4deo3Ok h2wzZ/iU+iz.9=ȾD43l W/z5INe8c$Υg3LfR:ˉ)/z^ 6PT&$ "]st]y %u[Swc G udFиwvT9M׸Tu5GqFLdMN9.TMWsOFDr5Y& ,$ D2SRR9IGJd_$dJmk1lc`&|SjcRJRTKt.jUDjsǰ$0ZGKRy" "JdDZKe9NlYo ]>͍㓡ZeUf%F\M͵_ޛ~-s6ޯ%28S "U5_ܛ y80`Ma[Khlit4V1]=i[Eכ/410\g|}2ޜj:ֹ}m `m '0o}SaƋGNpȲ,IRϒ1\؝tr!LOOQ:@D2b1CXwU6?: Bg6tA9N( OVg?D0IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-list-graph-follow-1.png000066400000000000000000002405341351617527000257230ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx}S R(K"+WQ|Wi7tAPE"%$! lvwym$˦}?ffw3gf%TJ!R$K!}1|Iw ʤLҨrNNN a=-[B䄐lBB)%/eʆ˗/?Jqvv*J.aD"$oh{D5(OKF*J$̬LᥜPJ(',s<"ȤBf-2R8N*T*BA .pO쩛i*x͛RrK7+!k/f+BzUwO,lͼ?w;z yS|22"1Bv̀LmVcS"+{xxTTIVd vOYYYuuX)OF# !,G<ճҲ,0% XF47+^d))1LqZPꛕzZwM|vqo+=:G %nGkkY9z٘Sii^DV&k<4Z+^t\N(ZpWi4leU~sJ4(SVòS-˲05G#~A <DzN3F&YsErdR BykMoy,գIjU,RJ,X-[ϋl&zħ7p;O#o49<뵚\P-camʕb犵Pqq!DZV>oTqFo߾mP~BNB)a>&?_<0=s,i9og߲ cYi<%O*:-\kު{F݈9{+2FYQJOWz82|T itrޡOV,TҥWe%OISמ< }kUϽU`B Xm=?v zWVJ$'Y&=5ݳu|*InY+!IJsLk4!:nV`XQj/Q#R *Ks.ԮnBMRʱzmS!t:ӧZV<<괚ܜR)Lۗx0(2q:| -qFPPy9zqPP(%|i&>!i9V2:cY2:Ns,Cye(贁t?w鄐Z7rrbrf~JmS;~=I(Ҥf&-/I<״VCW\wVɇضJ=<0:J!7NZă RߪNZi)5z#&sJN~jNu'Z\ڢnAWo=XGRF}?ҳxcT;4IOri_%aW[ Fzk\糱iٺV Q*:m^pONTe_ݏM'*UVcb>c`R12-u%1g6mt & 3yX"cZc)9r,CyN*=$eXF&ޠ mJpU-V6B2I})'O]dX֨6KM{TqV)d 33 S<+9+d҇b 1IYu| dnB(l0 >)-Yq^>ɳay*WvrwUr?-3T<ܜ9eڤTN'g.q',%2/fSSKZ;E=ܭw4{޸(HjTvMD"ygk϶Sp2:w.M߸I-*~^%FBQ7$e?zT|a2 ׊O r>+2$$+݂eRB`/o#ӳrl?9qHo߾(oOxu]jz۸OWNjz+B̈́L}iV;>)M! $JXz8>j4wb&>/g;7 !"pSkA2mmwN)}S3`qy+a) W6/Ivv͗<ݾq-k&go$8E稜\jgRկCprѳeT.W$A D:zt:{? 8N.cpeypKG7<{w<ϱ.-Cp9_5O>3[ьǩ? E +{>ֺe)Sr>__ۯ:)jt؟RrK)aːugN-߫2?,|{BJzm5Oߋy\̟&t_ˤ+vبkܴWe1<&_۽M!-ud!NtsUV8pm#Whͧ=G2:++erڹ"K:e gX526qfK^i|u?o w/itVaY(Oy*H J\_SgoV"CRɩZj':TժUrssŗ/M -Uϳz>??eYݾ|qmq-2yyy y9ffee,+/-(ʕ_K_8?PA{rle_|axAEP:BR f(O/oTj ɍ^'^58"0GUHj>)OџlTk]بc`| )-MؽQ}ڵk6c믨f(m 7$90$1ī]Ir^߲u'=sږ.8XU5JIII`ЇklߡWS&||U]nk+BJ!C>|hҫw5{#;t^ewQͲKΝ;/v7v33N3q~~۶n9wIΟ;[Yo"9鱏nGxxxvR0̎;ԩciot뾽K.i;w0̗Snۺe˖))),2dHqKT^l6 gY'DV:}Ob"Є-͘񝋋KZd[hѪ.[NR=:eጙZh+$dEhpwFTGZ+\V J_ׯ#ᯮ_ٳbcc=h?^] 9xSą]Λ?/+++11qqVj+rNNNqqqVj0M6ׯtk֮m۶ig2U& w}wbn}Wk׮mӦMa(8+ ~x4/[[7vwLs￟ĉΜ9y n^L8i…jmiu 5?͝cFBvMsG999o߮VŗVݽ/:vo~e%cT#h6#w>} z !Gln*DWDV^L9s指ɓ'ͮN^>~t9r ~;wyۺ_37b*` ,=-5>ЏNblv5Ξ>}~($ٳ'"""33s Grʼ+V 3gά_~FFFgΜi%lC >|ȑ!!!aaab+W~ǁf6[iN1ۼyl!#?E bcr:L $FFFN, AZz[=z/Ķ !9@HZ>c*CInķ-&۰Ç56 !lRl۶͒%7oRnJ^>3 J7x*#0^}@)Sv}#.6sNSN1ϰn,o<#|9e̟7o_Om۞- |f-5ڷo߸8Ӿ5h`ӦMf͏="tZFB$:uťUV2͛۷֔ၘ<˖-MtoW= m #h)6ABHXجE,ha W !d6!/;B> "P)1!iΝkc͖ݔ} NR$;{umQj γ޺p6$gB\)S._~1|E=zŋz%!zpElllխ$Zjfvݽ{WtڵYfM6}'|휜YۆUYə9tP+ դI(3D"8j ļsÂF}n ӂ8GRq`YǬ'LŜ9~܌Ŝ9}>Wg0 "DBȇ'bؘĶ׀!hs6o-ɓ3g΄~ק#G9s&99RjzذOuڵzu ˖ݔ}k}8@T豓vڭ]Niʉ IDATf Co9s?|ta['L81555;;{…͛7͛v'N9lْyf*j޽,Yh.]ګW/+fdee͟??88l)nݚ2eʄ dmftv3J;vٳjժV2kq_~9ҥ ƎHFb+'W.vȳ0RюAAƍ7j0Q׻olHϞ3f߿1,3O QBB iwX8`ȡk Rz&ȖM~8HR~0},VmZD.]/GRo}[+ -\ñck֬u RdguQԹdO6iwѳ֧>}zgÿOix)!dÆ +WTիÅaaawޭRݻwgϞm%lCBQQQ"AD"z5zh!+Sf _MjI5j8 sfϞ:ujrO ~-W7~xږ7-loȒYf~5wwswߙW{(6Bcr V祛=;lByU+t ! NVD+ɻwo% `ݻɇHV+={ڴOSN=OYv3}k#PI!;N?ψseO\"խ(ϛA<ߔ㟉UeO} 9 Ci.DV~e`gz@e^aTY`p4D`*$%rp @~r\lkWSSOfdU=z4iv̘1}QNNβeˌ~۾@ 5O<@?=zF`2f...tӧOwuuׯ_llȑ#Æ Sս{w^akvuu߿\\\k]pa„QQQa2LVBd28R(L iv ʷqݸqs97oޜ:+E@_gff:TIϞ=8PJBH*{֭"Zn]nn.]k9Ŭ *w}W{^OdFn˂f˞{k^@<|UHG3 A,O Rx W.]4MUHGCh Ph\=-iRوlyX"8Ҳ ͖/ݓ򔣔999o߮VŗV2i&-*irZjIR̦9D`yӧB={"""233 0h !qĈ+W[bEhh8sgddԯ_̙V6DϙG&_r8pafU}4m۶1228f([L|ٺugL&wݴiSggQF%$$j"'Ovvv4iRddD ֭[...GDD[/n6 +V,\iӦ` PKp8uѪe˖{ۻt钙YJ3g )5 @<ɚMԐqoիR4hiӦÇo޼ѣGN7xUVըQCHT6n2بFn޼yĉAըQcm۶R'@قC)/ƌc|rqΝ6vX4$<|qƃ6-aÆ+VxzzFGGKRBѣޮ];BH||QUYi&*i:uꤤ;vȑVrI;NLJmBɖKŔRė˖-kذǏRh(yyybڵk߿Rz޽:uXIԐ@( +;p믿NTU9 d(|PqUH:u Jm _*ό^Iz:i_{Ѳ^ܛ܈<;!ׯ_1cTPp fĉ .l޼8o޼kמ8qWe˖͛7 {^dFYti^$mH5`=޺ukʔ)&L /ԈVe) >}k4&MX eay˖-qyСzTnݺ=ylBf͚eS(W0~~~j7߼wht rss)ΝkܸL&kܸqTT3--[nJ[nV4d(v"HjԨ|r2Mc`WO#!!!JiӦٰa;wܾ}y999ERaÆ Z'|gSSSk֬iK֭3fѣGŧݚH$jzȑ֭#L6m]v-A *;v9;;?pΝO?E  "Ʉ|򉳳Yi4=zT^R7(fڵ\+VL> 8O(,KQ.!(Io:tG8 QXXhׯhB.cQsqww|sNJe Ο?OtR]\\MfK?>vD"9vǏ(0т {F#\6l'|'XJ۷嗄O>dԨQ˖- %9Rx6ѢE,]tڴi˖-~ӦMCm "ڰaCxx3!o9rJ33)$$Dׯ6LV޽{;w >Y le˖y޽)F`F "2dHTTԩS.\HiҤɆ t:A*}+288x¸/EZoE˗/1bT*1b #0B$,(pD"&][ӧO'lܸqƍNNNIܹsm<<>> f[rʕ+m h X rA`!p4D`P\{2|ٵ#(?yճ<<~6֖PV-BH@@@BB(4 D"5jaTh=b΢}0Kc4cX|kUU!Ξ=kve}B[jjnTzqqqիeV!")S= DGG1J=z䄅eݻ%K4ҥK{UM 6rH.]XɼjժD?????+We {n*U޽;{l*+UQPa 8}P$&%'&%8"0o)Paڵ?-WP,:>Pl _o<R^%lD`xt@ifF"f8"0bٿff2++СC֋ (Ç}m!8'NZ/ XVZF׮_O3$iӦ `"0bJWС?knERi!Vٸ4JVо}~B,=E~ JC .pYWG"XϯFPh ʥݾuK!73 @֭eYlJ铔'111={6][6fG.u㕒@k߾cԅqiLѮcG?_?ӵe nZVW! Ǐ3 8?\J Y+l=D"+ov^k@8 ^oݮu6JJ,:Yҽ0agʙĤ䯾J|ͥ .\8P)N/2]xGu)f50P*+p\mSI-'8GR+#T:ѵFZgϞy}n,XZZj58Y&9K_a8J 3LWzZZZ,39B(_^/X\ Z $&$ܺOfffIJn6jT3 @P(;1ݱ"0JzFK8JRBRm?<ǥ絮]֪hǟ*>sL.Y!;.vT&+'(pP))O8sL>Vd*U4iܴ~r܍ֽ{ځ%ݗEPԭP&WD;WZ5\eF;٥DN>d2jޭ[ jn(1z}bb™3{.H(!䧝;e.1LLS'Uӥ Ҍ̌5JT53Qv$Xy |yQ\ɓ=j֨S7itzszyE˖hOIduɤ ~8#|_e2Y?YE?Ӷ}"|r)IQU['xEjԬYo?8|kIiOyZҤkז.Nʒ IDAT sDFy>99miWiR)P>^u$'=OzNNXK$ke7blQsRIy )'g~ҶnI9hq%2Oy^~yr899 )r B TZ;Rjǖ^˧n^gX/ݩwdg )_ iwiv;fQPBf&+~׮\~U+1+<<^cr_QfY\mcFFFϞ= !1V ;c||]]d?K|[zvlglѬY'O~%{={ c6&UJҰcx4z!ͽ:gY$j [~۷{P:hGC'|1U*j6&YEK\Zn}^?'mڴrrLlӿ޴q]z]4?E bc%[DGG{zzFFFӧc7={ sΝ;w{Y ʲlIwpʎ /7g֗#ㆍkq|$9)E+[۷{P:YR*(::ZLƚ6 a!;u^s<<Ĕ oݦK_i S)Ǜ.zzxni'CO5k6 jMݧw5qB-Z\N`"##RYrբڵmT*kZRH}GO_=C2J\ZxI 5>x…kР.fW]v DEEe˖ >\.ӲhJ!C֮]+d2ڵk T*bcc;wRĝݮ];''r|ܹ~~~R!r\,(,תUb]v5jHV{'w,T|}w \Bzs|?'X{vh\V51%w͏;,,~Nz-$BV-_ܤn#yҺ >N,n`ӃKHOkڠVFz!b> m_7կ׹]Lo߾#0!RSS[vBWӾ~ 2ucccϛ7/^]mB8e_1:uJ7ѷ}w 1N0݇'OB2m 'i1,s ~1͛7۷og>eԮ]߈ܹӔSl4=Q, ~Y bfС`ar'NoԒJ'Ntv.qu!''G9ƍ{뭷222u6n81'0˗//bAat:֭['MdڐQqBȘ1c֭[sĉ/vg v1DfOs;ѧVCaMw[AѿE,<{r/&igF̣R}[!fL9Ys7{@ԃ'qO=|W;v:+Kc`قv-^u/yO󲲲jԡ|3؟~W1V%ׯlR!#3f̌1ҥK7mTTcnjYxxȱBJ$J=A10~/_|޽f8y9s`vBfΝ_vR҃[&OR%QQQ۠Rƍ۴YFб%K\xm۶E;}֭[ Ν۹sZm4Zݯ_!C9uhK76lؗ_~ٺuw}wv0W o'kDe7!x!!2vʵJjgW\*+_M"FSeOhհq"PB >>vc1 )Zm~ݻw޽??̓NXY_@+qM4ڵk/H8dZl:`c! ðK[AMvڼyӀ RE{5MxxرcS?W -ptGI$^',RzaɳeJ(.9r͚5ݺu>>Lϟ?/&&5k֦M6n /^Xz!d6!DVeo\+UQI)K1Q,.=e:tZ4kBy*H)530E(+\1l 5[8tԸ?G%I a~{Kgעe?zW$:^غuu։o9s?|[H|ş,2x.t]ĐN0t~E{qrʥK?aޮ]׬1L`FYbeܜ?Ӿ}WE0F"3նjkk ".P֢XwVhbR@TbWDjE^_jE-JYgf/Mn26㓜{'&$8z&kN8T*%11(..θAla!K6TT~嗸uv:CC˗GE; ̜;El͛|fjbkTCQvAЉv=R)3B JPJy(MR>)0BOw{Gj'A_t"M;n4h>|X_F lΈ%Ke=f=gϝ̚% ---V}Ν1s*~'FєT*4LZV&,'\!ؘd)S3s!4Ԕ ͽWٜZLL U_nٲjuM@3R* g͚T__?nܸ:뇿?~L`cm=ϟ>|8C[@%$$\~wF0Lѣ!>ɓ , ͇+P'N9so+ߙt ,d0sek"_vGO*ԶO5"nlꐏ: 6߄ Rlk^_DDQ$;Z nx}Ǯhj6vFFPo5v6oρe 9vqu?Y݉tTϝ;_pT"ٟd̘bCVdժG>|DCCZ^|4+sM%CRyw!|ۃ_Z[DaSS9oIȡ s :QXCC^9@zM뾊Rd]7-}e1 rYJell믿NxB n!y6Z>q)‰3uB*{[S+oTYYyݻw#]'ȷ100)w;o=J$ԠaoDO%122CWEi-EAzJM8@Q:@zH[[[12SΝ;tP1?y >v@)MsSȑoãs301@uĤ7>iEẊAaE ӦM^x+Ϟ={Ȑ!ڗ^{oimG Q 6Z#1vP4( (6561CT͟aKK-:Ѱ]O?zX~*߇̇vFvOOO yW<=P_C Q63l< kӱ!wP:pȷF{xc*kmՏ?#F׽5, uo! T>h}Cw;U; bwk싫@F/w! 4͸qB 3FRG124ӭS~g!<nI~ o| ]AC7~Ʀ$W^}ID/J%7042|=xk_𑻿122>bxoWQHń3~=͛}ѲeRRR?dذa_yW_%^@L>Hb``0h1711ikk߇  Ư:V Xb˿A:1004ظ݄ h[#? ::AEM4n۷o_nݹ,=z4% ߫譑#&q\V50 ^@ H;-Xq2|ׯ_~}BBO?4m׫nB^=v֏^^3|Ѡ("h EQմHnh`js@H@ ~ v ׿em=W^믷mfkk{MggW^y݄ ?eXMh4(Jl`ԨQ<|jss`@ H`_̞=b \Ei-h4KWVV~*+rtvSV{YaF`0Kӯ}allvAr2C߼\\\Os  \0dIߧ8e-bs; ENΩ[_!mE"є)Sy !h?>o(~R8ɮ?OPV=tĉvGYYG1^o 0Ν"\HlHZfO;:R^^?=֛o>}F~:1[ )ַF0 KþtDf/h"mݶ)iu#F1br20a(֬]ʕtk`O>1b[ofb[ݵ˴T{{{c/^mjJJCWZun-u[xa2-RID&pE~oܽ{71)i֬N"-RJk䓥KJj<6u}'ҳg&O45knݺn 60ބXNΩf=Ν;1XQaCrEфĤĤD'''ccc.&׿<~($ZhwxX^c޽1&cLscyyy‹/}k۶'ݾ}d#Gv۱M Bhn޽7661eclNccS/} ,o -TTTLHgee} Ԛ5k%`~g^PP %fAocʫOߟ3'lfQw;::uON}ҥ[Ug~5RtNT4ѣuH ZϷb Bk_<]]]}wӧM}w٥ο1l8oĈs:|ۚqܹ>#FaHZ!tg͟Snݨ!`95s̼ |MA|sDNIOOsf ȱkՏ=hjE1Qv\?|X_݈]zwC߰7lAq0 [\Kw#}}qͷFo|[܄ X2 ke`` ݹSTĉ^^{tҶ/WTT;l?. k_|}@ &s,+8\_/if-Ɠѣ_E͍aa?ݾe``y䈹y:[II@@@o(j=a©S[Y a_h 컣G?,/>|xssBχ8ȀBVc ߤ]Ѡ }`Ѭ_hjj<{ݻw⫰-9\lԩSiiwAKKׯDTHM #G"R__?|p)]⫆eE=g̘>cENܵs'Is5M:URqy|,`Ѝa#p\_UYYQQ{:M 宝[}1qD7`RSOA}A%88L9rӧ"|~'p8]ipƎUTqwf\?EXCdҶ0݅e˖Ux}DQtU6osJzuqMjsXD"Hbbb==Eh.{yzFFnjjϚOW$]h͛7 Esswǿ=z4&2d-TC*=Ѻ 7JRRGF6}tΝ/_[͘1c/a8P[(Uw ۴K{%%&6pj(*|Ӗ͛}`chhO8e||YG&þ;B'GGB.B^|y憆cͺ.B[Pn|ZY9fLU*zOئ' ˠ:$!Sr\\ưMlFmV?(~xĉ0˗j_ر_}! B0;>+9q .OBo@A~@;M޿_`wTFkCǏ߹1; ȬYo߁-0eav·FW_E\*11i„ ߤB9>~xn zwGFׂ IDAT׋F4a `KjzcXت+-,,:ڶם} W1s`O=;/b^E;2[.~eddgnni`<,p_@ &`cr;9`2@_0VI c7]ҝ~KE @;@ 7 dvV@ 6sv:vz_@(! )++d)))..ںu몫rynn&6mZFFFkkkFFΝ;,ʠ;$=0X BRy?m۶>L&}g_|ؑagڵUPSw:M~@{NB8&&&GyWLLLPPP}}}}}… MLL]i?(HLL K```aaaKKD"INN$yr]4qΟ?/d2uN4ŋ_vM&IӧO١əP_H~-.-y/3# A;vjpR)'JJe^^fe22ٙAAy>"CWaaJr1ӧOQ ;:L`0kkkgΜYSSkM55uE[MKKçiҤI𫫫}}}MMMqkkk,Xp\]]PX$sss/&$$XXXX[[_t SQQQ} LK<Ѫ%<==+**rR?z􈚄Z6t1 ֎H*,+::Ϙ1֐fO6븓ӧOԸܾ}#77RlEѕ+WرE۷Zgb" &. Qwmmm-pjR[[[`:?k֬<\.̙C;;5;:h4ǎGĚKLLēUYY&DdGp8 BqASwyF2 eaaklZZ ;bXBlɒ%TߨT*öLiRݣP D[nP ¾LJ|IRYXXFڄbĴqܘsN FASw|$E+??#Gd ???LѳHc``[B!IhNM] M| s*󛛛Cx6aX EѶ62\ `M, 8UIk#5O*m%0!]K$Iv`/eZ,p^vtݑ԰hH$&neeeX,c@o;'I7̞ I Hw-Z%TM< ~tzڒdPo]!CYYY999F… R4%%(Z^^'0 #Q('Or355U.D"!]x?~>TUU=f\ϝ;Gڄiɓ'I|`ѥ 5=tN8a5O*mO?ySC l߾5۵:YF&u2 XݻwxiӦqζ-`Oxj̘1nҥ|>_ATnڴI( ͛7cꂃcccQ &9sBav`;R&Ο??z&uC?gcaa|r90P9~uv50{\GcVutgZ}С܋/W}M\|9::ڵk=~' Bʳg޽@ KNNm_Xlv|||ohV`PT#({ѽ =47H` JXA H7G2|H_@o!!@  @ @ @ @ @ Z!]ϟ?okk SWh4+V J:#=:n8R+'.ez(VX+RD".]RR sss9oܸ KJJ\%KR)h900Cip\l69?? ;2!tvӧOx<|Ia4u}`/0=tKddỲzW7+Ϟ=K}@aOлiӍRwJessssspI\|חZZZ0~ܸqW^pBJJJYY~ ׭[rJLxRH ڌ|>?..sZ#ƍwȰ0L,Pv`_233}||@STM ?C@ŋvvv%77wʔ)]PWWgeee}?~͛7oVWW߿NСCׯܹcmmtRSL9|ǮU:kЖ}E\*|DTyyyal6[&(*ɜ@O>EQIo)8qyyyT+WL>RGZ {mm̙3kjjpM)&=`iiivvv&&&DI&]|TWWn.XḺQ{6aXIII666/^LHHtPQQ)Q1ӣ9҃~Μ9gΜaNW`jMN8;;%,kϞ=|>ڴiO(J$ 6XXX4a23~{_KK0HCxǎV`āa*++$"2ȑ#Afj5j#<_R#CWYYŲ5#% ̖,YR]]MvR=l+8&=:Ő^__OThoou떣#Rp'O*B777j&,+&&5//Ĵ,Z(..NPݻ?V` pVv(',fƌ<ԚxY(ڷoRܷo߂ p!QO5K.]̙3Jի1VHp'.-\#&]uz@a2Ԗ"[[[lU(޹s;Ҝ2>PT\.Ef{{{ۋbжo߾n:EJebb"ѲJxZCzzV`"tvH$Vݻw܉(wJi\~~~Gd~~~g;`Guuu>>>TǨa B>&=f@:dnnnص+onn&Z033#]<ÌSX,Bh[[qo.9,]TJX`X...O&Jt!P L"(*H.$*fIB@e,K*ju3TMu[*00Bj ,,,:u*0q9b<]s?x(^(z5u5cr|͚53f|>]'*cbv[ȃ~7}ȑ#7xcʕتݍ7v t.MB1rHR  z갰r推DGG766644DGG"R[[_aNZp   [Z}aG;3fڵK?:]He@'NϟO|@!fccc@@ hjjz1ώ6(t&p@!CGT!1԰;s1߳gvj!++\(㛪njK|s]PhOx'ccc'>H$ׯ777﮻XуƬFLh4+W&&&: ]BBB]hlΟ`c}JRFM>ֳgφ/ԀX+V?s[Pp\f#رcD:jv{ݹb%%%(l\  67̙-n@:_fFwkqii455رC*fWMLLx<^xx_I'rɓ':(ٿF8z͚5\.wڵgϞ3ASSS;#ssc_^{]RZ +)H;dN=fOɓ~SSSS]]_ر|Oy̙3?ӨQrssOvttr$5:kD E"Q}}=Q(H͛޻wՕܹ1lk``wL lZFQTVs8!CGuuu>>>TǨa B>&{ 0C_,_-((pssî]W|~ss3т~t 3Nmb mmme@ (~w8H;8+;/=f' IDATϯ]InݺޮhD"Qyy9P 0QE"_IWj{j`,ׯ_T50ceefΜIT͛7/[dYNjժ[p8* [VTs\[[TJ6ÉonnJ5kV]tKOOwwwn^-((3gfPJp8؅eLkv0rf3E&{Z=B&LpJ%JMt/^`ڄSb/cD.$s^T5*D---0a„  Iw!U*UHHHrr.d284I0N*B榓RĄJYHOYYY999+ R4%%(Z^^'06rsrr ɓ'\.&LMMiii"AHjA$Irr^C[gϞRkkB!qu W7>zј(r=@ }+W/hlΟ`c_[藿@ =!ϨJ@  @ n[DG]H@ }+0@ }+0@ }+0@ }+0@ }+0@ }+0=|G4͊+G7t !-X^^H$r%%%077ݝL>ƍˋ.YD*26B16f1asss``  B`G&Îqi2"iCСCcƌDpC(pRP111INN&"ܶ\lٟxx/_r!!!Gzqgϒ^ G>z?xaaa BKt'N*j˖-jCo޼x<}mFՀ5uc-؅ RRRʼ?SLXXX]^^n:.^T$%$$06B6nܸ{ǏGFFa¸8>_ZZK7d`_233}||@STM{No({Dݿ='!sgϞXb߸qC @5ŋ_|ƍKKKqC_s玵ҥK\͝2e U^WWgee,\f~QFҝ'N{͛7o޼Y]]5_bŊ+JKKKKKCBBBBBlw5ucǮU:kЖ} \*|DTyyyal6[&(*ɜ 6:y^?"CWaaJr1ӧOQprrb;:L`kkkgΜYSSkM5͞=:Xմ4;;;¤I._I}}}MMMqkkk,Xp\]]PX$sss/&$$XXXX[[_t SQQQzΨn~@ќ|9w+vpp8vح[۱Jӧ$5:kDeXBp„ (eRе"͔b |gn+ .quΜ9yyy ---g"ggZR=H'drw͞6mױxܾ}J$ 6XXXmɠ nnnrII{Ϛ5+//O.򼼼9s:ݻgkk[[[ 5k uؿ̌Ғ23ijj*..ޱcXbbUU,"VVVeeeD$>|X&9r0l2uDKjpC(++djEQZ(vbBْ%KQîT*ݻm%'""";Crxxx}}=Q֭[jPPPTTJOPTZ\UUUSLNaUUUt6D"qtt ]ck-HLL7oz=WWW>sN d ꎞϗ`wL ;zvTWWCuМlP($Mi|С֌ OOOm#CZZZՂ777>onn&Z033$]<ÌSX,Bh[[qo.b1bU`DJ଄Yf}׮]$nhooh4".QGL9Vę2bruOOOZW`ʊ͜94p8fTYfr5kf̘JC.kkk]l+,,:u*'90vX:H1h^(ډ ,(((!!A"⸸EVںu+PƮ ,==ݝtxH'dHG$E:]mdf'C-l6{xYLisrrg1{T7M0ʕ+*J*8+^x ]^i0B.$sbP-((Xl`ӏ(jyk`**$$$99YG#NNNJ[V*x葧 #....M~~>7n8L' w!y<dB @g(`GZz]{wU`ZONN5j.hhhJ)))EO>A Y('Or\L陚*D".H$=$JgN螗rDžBaG8d_)ʡC*X\䔒BMH$ڳgX,~ڵkvMRRSSuo0 jPg9QB anddo߾ʕ+0,,,)))%%e jT&*qRjq8"""--ZwSg޿D"iiiIKK?u… 7lؐ8df =(m۶a?#P(#GT*III3vب M]&OtttcccCCCttox<,R&pw10p TBbW?>qD7 \@!&P;])u;]C(**055jll p8MMM BBb4a'n%.SM5ŋs8OO~aܳgv閕enn. MUUUcƌ% |>ܹs.MOkN=;_^(r\#3Qaaall,tB|bΞ=OlKKK \nҥ|>D"Y~9Q&PEѢ"___(M6 BPyf>|8|>ϟ#:A<55fU`=hjE@ +W/^FѬ\])  uvvK\W?k=x`###wQpHSSSeeٳgwލ Ƞ9@  ##8X~A144 999ؼw閱76w3@ K3"e ۀen@ }+0@ }+0@ }+0@ }+0@ }+0@ }+0=|7-УG7Xb7һ ,//O$q\ooLpO~ LXRRr,Y"J=-p\l69??677x@X vdB0Ox*)&=A:0f̘N;`/0=tI1=pNE@iST[l177777WgϞ%9i͛7}񼽽o߾M' w( ׯ__`377 ߩ6;dnD]twpBJJJYY~ ׭[}AŋD6uGAz ƍwȰ0LKKK|~\\.,OSA_~%33LJ 4Ewԩowڿ='!@z:طoo޼y3YYYupŊ+V(-----  kx{{ZCIKK[jUii{>#LN4!t#VA[ BpTI_VlL&CQT&9;;3uQO9%˫PR]reӧ(VTT8991i&0쵵3gά 5@fϞ]PP ,jZZ QaҤI/_$վ۵ ,p8EEE^MX,VRRŋ,,,/])TTTxzz (=gT@L?hNXDP8a„|\g)5q&-50;RK:t营(333'D5jLX,ٳW\ ]rmpss+--ŖKJJQ]r;Pݾ}UH.p֬Yyyyr\.͙3N()--ŎdÆ BFill|X&9r0uQya82lg[XXl6[V(VX,P(433[dIuu57jؕJ{D$St@x<^DD@ pss~'bH\'*ߺu[ RTBppɓ'Jeaa jښrcbbZZZ^-ZP(d pVv('X\paʔ)#&-0I0;Fu饁b%&&:88QQQR4<<(3BZ-..K.=sRz sGT8vT*EQ~޽b. L/@?B?8==[^zuhh()WB\SSIƏfmۆń9mmmEEE `(o>R@j ,;;[$H$Vݻw(z>%ȑ#2,##vl쨮.22LJ5졡999VPH'ӤСC7tkiiW ܰkWU>L`ffp38 R((;(~w>N+Rg%Hsts%-2fǨ.4XVCRLgގO]CAQX\vb]vMW;- JHHH$b8..nѢEtBtΣ(Z\\lggT*ձcǒJI>t(7o^lQXYYy 5/,,:u*,GAEQDUKOOwwwnR(JEтA6uDGqq8\L&5;q\f΢@gN螓~? Ƅ \RR)I )@ .M:%O+0iL⬄w!IUWҡK0jj4f/}uYHMƍê[mbt1i!Ë$Bqv.E[|XЅG&:B E`s|Y8"a4 QPiy?999+++''gԨQ0""A*XZZrE###?alMrsrr ɓ'\.&LMMiii"ADLRIO>$uq\~qP02 IT:TTB''/D"ў={bg֮]KCmtJ{^~TJsBÐ hMZ21tεuss wOtttcccCCCtt/ 111iiiW,ۿD"JtB@! wޭXp!ь3vHbZ؈=IZWWgggG\P9RT&%%1CyA}ݟ(;(D-va""-T |4h֏PCIސD25S&B\A].{|3k}=3{vf7.%%E۷oܸqͻ50:Çǎ+ ͛WVVFXr\={CE=}n)qzxxXXXxxxOJ544|F#H=tWPKhxr|…|>Ν;1** >ϥZYYI$۷*++}|| E&D"'''Oһ7y7>-?R5iD`=#v/\mw]#`5-[D"Y@>jXWWxbΎkJ$Dn:^ppptt4aLX\\#+++r|_90dkkz꺺:iii666Ʉaf͂cvȑ#FiӦxiӦSw50|vn[aXw.@ $!!!...=]yh1755eor466VTT8q"""G@ HOO!Z!3qs Z!3Zo!/s#@4h@ Ӡ@ DOV`@ = Z!@4h@ Ӡ3..n9=) IDATxȑ#)uQDPxƳc)JO~~>.fddS^x󽼼EJ%s1Ph0cD[#f:,⒕MMMB0 @.Ў$AӎC0+%4<癝hA/+++++0Vn}'NP3Zt̙3BP(|2*rA pBUPPݻw> r4" ;tN>_XXbNNNZZZQQի-[!!! .,((J;v`>nJII5b=5 @ǵf͚{bLLH$*((D111,"#x M;`0ttӴ_hy_(kTvzK.]TUU_Ս5ĥK.]`ɒ%K,aqBj>>>˗/7(!˗//((~{gd9d =[:_A|`EqR)ȊZ7y<^kk+a...,"gmmӫt9z}wAy\^^^999ٳSNE''r ʜYDhG: M;4PWPKhx 8~x.K67nܙ3gpj֬YDص^M8Ν;ƌ?رz?nPVV)###{rA㇖5SNDQQQ.'66V"eee"Q&l ZpIWWWx!60ؔa pkcc3jԨSNHKKK'd39[ol2gg琐\$ͤI._nnn|www Ö-[qF 6lذ|rJHg8cƌ̶Looo&'T$((( ?&Δ: :;;:1Лy<ޔ)S.\2L(L#/F&ٛx; `hll۸qc>} ,66"QFT*ݷo_kk|>HVΝ{u S :3 =\ښxZ0VK*B;p8rѢEUUUig0%4< !uXXX}}=٠wrr7"##5 a|QZFބl۶%33S l۶933e111*j֭=YQդũ^INO>=qDB$W)ٜE 2Zp(S%^Émiiptt$둑J2,,,29l[[[ŋ?Vϝ;7i$84p3>jfϞhaMMM[nupp,!uQ8q"cee%*III+VPj%u8...ոbkk>s<'L1 koo͵g& N -00V=KKKJP Ell9sׯD/R(t!!!.HB_(o~~~hp___z`gttC fvv~트* ,--Nx;7p8* ðvkX,O r׽cA*TID"B0LPLBkRLgsc*Zg:6m1cg}vyh)p/ᴴ`L#+J1fGG?y SGtE aE9jcB!cbb,X$Bd ðǫj|sĈ$Ɯ: :::߿nݺ?dbݺuLsrr&OL)iXsF/00_%%%wT*JðlAHPzB=W|> ðV҈;>Tx<0tt 8vvvgϞh4J%))X#.M:%KW`,SZ*Y$Bgsvhꓟ~{ŊV)p/BgJYt1kȑYg[IF^jj*IMM'TEPH,T* *zr 6KRdHocƌʢ4tD,Pr1 kiia=$~\\\jjjzzСC qӦM2LT:aEEE|HIy.hAzzJ:z@ EOOτDT"2P(8v ]1YBÃN*++jjutt4!:;;ǓH*FEE򚚚UVЛ1))I$$$-?zg5?j=QŞYYPLMM 6={ttK nVM}}}7o 6o|رcByK.grsHɴb <S𰰰qnYD"9_]A-AG?QQQgvSSS$ۉ]>>>"ɂE"ɓ']Л<]ZZćSCԟb///PH{?? AZ: Z:Gt &XXXfddN{:SJf˖-"~;!-^o#:jzڵD"[aXtttpp0Ӹ.ϛ7O$Dy敔08"aΑ#G)>ʊ%h~Gooo>ommx ?ڮ^yZZurr2!BG%KǏٿ? WPo0;w@  zEԔɵW ^XQQqĉ@ @ A4@Z!3Zo!]H@ @ zC i @ A+0@ @ zC yF~9yͤqldz̙3f666K,\Op˒wyG(Z[[T*FO´Z-Bggܹs)Ijjj-[6j(@0k֬쮤P&ɜ2LCS`A 񅅅>>>~!.䤥^zٲep‚Tc~pވa8k͚5  ŘHTPP bbbXDhG@Wiܺu+%%חl uEviG5x7L{~Gw޽zѣ/^$peʕǕ+W]>|xҥK.UUU}L" qqqGxbkkoܸȑ#Fdh&ɜ2LCSU_7W*"L///|㵶b"B}td<.//Fs٩S⢓Syy9aeee,"#ÄvAAA%Ԓ޸sHǏrdqƝ9sWf͚eaaAX[[;|>ꚛKބܹj̘1?Î;GO?eeeb822+[9ahMCiiԩSE"QTTTɓ']]]y55uovbb"9PaaPZmNNή]Ν;k, :;;6oplىaؘ1c(b]9r$}Cw!RSS!7m$ɔJe|| cVTTP8 ".. z=4q tJuQ@ mmmREdJ5Е+J)Rn/][v/fffLWjjutt4!:;;ǓF*FEE򚚚UVЛ1))I$$$cдJkR8jG.KXX￿aÆg⢞-K= q߿`… :dggGiHMFhČ?PT*UNN͛d2͛g͚$P zJd9ORW^Ur{7c$ϟаe˖SN6lSΨavg߾}===WXZTT011 3g盘T*Ր!CjΝ;RRRZmJJʄ XD77m۶ )r >|xرBp޼yG\Þ={͡"'S82@WnnǍ7pˆ>"tD٠%tKJ(B1F\jee%HoN쪬177'L,N?rWzdNƥґ;G!=9r$qI/--:99\Bpʔ)0uuu/DuKw50|vn[aS^"@r;3P _d:1nLMMMٛ\z%'N;@ z^ۡ?4ß^tމV`o[@w!@ zt @ `r̹O?ہt @ w:։u`فSk;0 ?ʕm @ HNTN ~hC@ DOV`@ = Z!z '"tXffT*>>>NzE\-R*,"'`޽FchCf:,⒕MMMB0 @.Ў$Aӎs1PHlbwG@`xfP #b/=~xrX>}:>>?ŜիW?> YpaAAT*ݱcߧ8pʈ/*$n|t\k֬w^xxxhh(.ĈDH"B;M;֭[)))dc+%SwP]}Y@@OMo++++33]_>|8.0 00̙3,"޽{7l`eeebbnsrr.]jffdɒڑNi~6667:yqBB”)SM]yyeTTU``@ pssv.N2%!!] wKYYٴib͛v,r,--q3g>>>/Ν; ,--e2ٹ 0'MJJrppOtN:'Nggg{/(ÑdFjmmo3f l"ON5(^r[<<<|>=}_|+}ɓ֯_{ngg5[ZZ  .--=~8~2GuuuUUݻ7nHwK gȐ!C (,,9s+~y󊋋#""֮] qm۶ã,++Zz5哈 8t꫃>HKKJdQPΙ3߼~H$/B!Ғ C'ǸAe\~~~ommMNNEj1 j|>Ed騮.<<ח~ IDAT=!!!^DB'b$gRV^meefddT*hGbX.c& xaii.KRN:h"++իW^~"HP`P(zxnvCX,EHMMM(J===wuMbC477tiӦ3f|gϟ'Cvss`q}8YYY2FTfDH,3%싎צMN<6''gɔ|q":߻V}js$fW}se;NFXqTzQw/ݍyyy7nomgϞoȑ#C !/e_xtҤI,"'˝?>~==)Cƍfffĵ=p8,"SG_}ڵoߦFOw}G២J%%-0 `bbbg n޼_Q <=߰2nܸvvvX===}.8$_~~+o߾rJAhhMJKKȵ9cԩoK/D7 2cjjZVVF> ]~XY&,,~<8=#'}cg~8 :V`qqqC%M6d2RoccC,"ԧÇ>,HAzzJ:z@ EOOτDT"2P(lmmR^MОs'b$%qqq7n9tиq֭[G1))I$$$=HҨ(\^SSj*\rʺuƍ{ s ~.j삾tQv›8q"L8pqܸq_``…wAS'aaa Ξ=fffgZ=`Zl0`"ҷo9s|wd=%%EզL0WܶmBT04}zzzX"44=NJ5dZsN=8=sfΝ69za 'UÇ;V(Λ7r{!7tr|…|>Ν;^40e>\ 7n |>߿Ed.AO;y/5%SG{yyQzzzb3^L,N<^^^=bH h<]BaTTBE{p8bӳ˽y<̙3+**pP 9oll.h} &LpuuŨ(.aVVVdIAزeH$ ٳdDDD)SGnxs>XXXgeeęfcccmmLO"NB+{U %vp _p@ róL{{U>#BDG 34!qg|+Z@wl&>]+W~?666VTT8q"""_F D3f֭O;Oenv{C *=s)@'D!zh@ Ob@ '!oJbG+0@ b]H}@ ]@ DOV`@ = Z!@4h@ Ӡx3`HOgv389X[zJc)JO~~>.fddS^x󽼼EJ%Iw+++C  r 踲\\\x j۷o?q%0K.͜9S( B˗/38s E#X>}:>>?ŜիW/[ CBB.\XPP Jw"B}6{lC# D"QLL ֬Yq޽Ph"#@J4|xҥK.UUU}uuuF.qҥK.-(((((Xdɒ%KDhL!U`tD++++33]_>|8.0 00̙3,"'aG 4t999K.533[dINN &&& h"#@O?omlldoI_xqӦMw۷/3g>>>1MJJrpp{weTT@+MRVV6m4Xyf5jmFF}= viee5f̘Wu׮]YYY666vvv?3n@@v5gh0<={6ǣ\xrǏ&zerF1m4cADr/_f\$z8} 2dȐ?ԩS˗oڴ q+VC0~hԔ18P2yTAן8Z~8NlllKKKFF}u;g۶mͧO8q"!<(5ް%33S N233pGꜜ777g:AAAwttshL1<8NddR #LN(yyyָxǏsM4#:|>Oٳ5@ 0a֭r%.'Ngcee%S;ñx9>R=-0eð\{{{a<:߻VjWȥG/>˃}9U{+~e1//oƍ]V`iiiRrvP(s7_*KP"\r={HGϯ0Zp(ۿkkkrrK,CvTWWKt|D"ؓ]A-CGg666G!HOԦ&XlP(0 S kFäP(tZZZ0 koo?^?u8JV`DCKKK0y#JvvGz[LNTGTҍP6;:: b s8bPPЎ; \.Y`S%u24999'OT8tD=y?cV`III]*|HpEO\neehD:1 +++svv6ƒqP2@okk0F>hx<KxD94\A-CG?n~𡫫kQQ.2 lg%%Qi:kF't0 ŏ=bX,Ξ=hJ%{ЙR&c8w9_铂[AA:??pRSSqk( j5ZR៸"4NEpo=-@3?f̘,JCG+ě׮޹WD Ls`qqqC%M6d2R_ t Ê?pqW_}Ո mmmRt\ ==]R=zT D R P(qqq)i<Iy( j::=inn^fMll÷o߾|6ĉ4MBBÞ={4MRRA J:Jc}ppp8pVݷoe@-̈GG*FEE򚚚UV9>>hjzj:::ж+0`@QQqm;5ݼysCCL&ۼyY۶mKLL~qA(~ BT~7bIƩN]]]LLDiyJ5dZsNanXQ>>\ 7nD Y:Dxд_]A-"ttzjժ}_G}aXyy7Ǜ9sfEE4"XaÆ^EhRqqP(bz^0:G75( L& DNNN'OzSYYcnnNXZYYI$۷Nok\ O mٲE$rAðŋuvDGV]V"H$u1 fWyD"H4o޼&"w[~ߦBNt̀F EgGD>zxд؋^s1xgqE,dyj ߜ0b0u7! q(zJÁV{? )=;"=ۓH`2בk'].?}-l1C~,}-͗>53ƶo<*{ q/B{' ):#2CWn;T0tI-u<ftjɼF]nmɼw.`$b= Z 9-|9wɠaxV'W)qɾC3BEhGOf vߩh"e+%]dHN]yteR]T4}h}gcF-!zL` z}B"lاӥ'c{B O&~ێ Λ_Ѡ.̭v5>wA/IC^cа}|@ZiL5'BA/\[tՐc;yr>=!< sF{'vr:41}.pb37N\7:;/UT/ p牆جIXY* g-y{kaO\~uwO~]ԤR^PΉȽ@#ѧ뿆) 4_\D!~|2&{2Zc-|.֯+ԻNc [eJ5V xy@F/P:b NsmZ@ֵG <ʶWߞbC"EYىaS) Nh:;&jJjLÄO]y@)78?5GmoUwαڜ^m[S'lQ}&)@3 }sy}9wcoJpNv|D6W_"B}2f/;Ⳋ =دQ0A4_Ѡg@縠2 WA>l>yYC&q (LُNc>*ڝYllgjE|V᳝ ;O5+f9qT4k0Lz~(Jc=l~bcѺYӨ-yO*k4'A]+>\<}0y=1>}L"K_ ,Nmw+ܻ7KeZ;cttN]yO6:5q1J^gjgPH2?ӑs TlӍk% #vБ`_Fѽ3縠BEzG/&f#bgX&tUM}RqkŲwLpbR ;0c- ~h,^u>>&&4"(W10 \S6mܛEU J3Q7<, &4'6i3Ze^cMJF d>C/XycG.= vZ4_4bSA{\hY@Рȼ`c郥}!F?DPWPKLWOxo ݕo,Q>jl h;4ڎ}F*H%0vh}ʏA>ߐ~y#W{߇/<3U II; _ lL-og:V/셾sW9}FOLC#'*u M{ǣ:N4jM?&v@v}@ًO BKjdmdJ5vЀ~W8=)|)k6`v@Eh!=lh͋vZf~ʻCa@zǀFQpi&&^[pi'!vt܅63a䐯OI^~M❹YсM=ZJ41``Ygn԰Pr!W%C*C/ܗK톝O IDATnhAܘ1̀F R 0{sV`)i_ly jz~٥MsM8^i,}z攙`x8x!!d{aDgLCcJ Z;6uKFLXh;"& C.F2G_ML#f ðAyO=sߍr!CgSƾ<=nRaq/Y?/dzZfieƴoAJEmxj;1pہsD6~xֵgevuԿ0+0G. ⽿[tCb߲)=|rG fMέE"Pizq=nR)C[n WPӽ' ̀F R vt=lOW/v2D~L;ba;Z5%2w7U<}ܛu-)ȑ_x\|01V[svqAeՁxΕl`LS 9Htv44JvÈ `оqh\~ hKȯ=Q׷W 6-ܼ q_[wfn=?n\;s7{{@٣}gaq/B{ǓТj/ߴ{AEi*5[MXBGtD=_TLGnMCqRX7m-xʟ,Y+6 UJ+v_ēcqﹿFF evO; }LxrоUubEԔɵW ^XQQqĉKf@]*%_D ztfS'ޘ ` Z!z%˿C@ [LQ.΋tw?0=S!@4h@ PW`Oz*q @>>/_ ,@$nЋб;}t|||aaχ~999iiiEEEW^l.,\@*رEZOLLH$*((D11167hZfMDDĽ{CCCYЎ )nJII%C]iN"##=/n߾i+`8JZ2UZ+Wtww?\2tP˗//((~{G슋ׯ_}UwYv;{裏޽{ѣG/^93K.]tiAAAAA%K,Y Yۓq|Wo} c@TD"V333MڊaXkk n''r ʜ mn 啓hΞ=;uThY@MfmmӫK+-'^pa֬YuuuXQQfΜy}s\ JJJ<==bqddѽ?P2yTtԩ"(**0q8{،5ԩSFwuuŲ2E{??? bq}}=aNNNDwgܸqgΜ!L{$)===ʺ#mOzmU*lhh8t萧'\~޾btt֯_`, J24]O2ʮHKKK}xSLp.*?ښ쁘2&M|2S:_JrkW+_Jr"1//oƍ}ӧ+˗S1jԨB\Jkmmݿ?g>-uZ-aZֈF@y\&,2 ÑH$-FOZ;wdW[w:2Νxo&[T-[,\",, C0, &&FRmݺP2yTS%O@@@\\Zչmiiptt4nG"\|Y>x 44"##5 apm|'bppѣGjuNN..X@g* Dёhaa,H:;; ɉzڴi999,3Y1<](I1c7|*˷oN9g⸸TWWcP(Уk׮zKPgψ 6^֖988|rðʉ' qtt$ӁW`J2,,+V !Jp/^|qZ}ܹI&tM4XZZT*P(bcc̙o^~U$}BEdԇ_a Pd-S,CvTWWK̐t|D"ؓ]oIЕH||͑#GE$ᧉ&XLtLg\.G+02lL+hȕ^ @&Bs҂aX{{;~iӦ3f|gϟ'DHD6p8* g䖖[>!bًV*{zz޽͛y#;; Gp8&M:v; k,ϝ;W '$$o@qu?0lׯg H>|OX\"mmm+W6m~dq:::*?{ŕ-53o>gF؀F /iHT\AMoBQ3WAVT|,/l15A"F ~ܩU:]]S-j>kU]IIIfBo۶Miڴ4\|dxPmU`z))..8q"k/aFP(S1v-^T[[d2R)Kx9|UGc.}rq"D"Wq ]HnG(yܣ)`˔pMNAnݺٳ ,U1Bf [N+[t8Tuə>}EDPp+0Bx9˚ lժU֭ FM8qׯǛ<==,^gVQgϞ󫮮ٹ !d4_ }+ͮ`o9Ӳ'4X|9~}df M&+7!C/$,,O?_#.Μ9c03. d*..^f #,n޼iZmF71/\of͚|rZꫯե?(LzΝ; F'Q;1d.C f2j6oN 'Mj&yW*`񝠯_d3gΤDlNrm5#Hbbb:a!lE;k׮߸q[#v?,M)}adɒ\z533sٲeOY+** ڵk{ q_XH4fNTYY*ɸOkv8toPPP?>|83\r'B>R^^.JI*VVVMtuuU*\}}}?~L]ь=ZTرٖ~~Vn#Ҕ)SD}}s_]~ fn= ~啁7&,ljj>~ƍ)@<`L׾@K;> Y (7 MsG } ğ;FQTR422 $IXXŋ444T*Ν;W͛ccce2YllVut]l(!cUXX/ y% 93 G ;ѣ2^1%D ~8@%''w! b&5ae؝b=tcM2Ι3gӧz#ļw;#VZe2lid2WWwқ:::{:9,X,krrjxY(={6**J*/\+<"v*SNeeeGFF~XX\\WQQtRe+V3gNYYJڶm<--M.4GB1V+Wܸq7lذj*oBbGDa(֭[6mShJ&vIII9rw}GQ۷ RRR:g l b&5me1 ߏMJJJKKC^:"Nŋݹs~6lDIJׯϘ1wߥ7edd <~AxKvٳK.]t&33=gwe˖ݽ{իG7osBݎx'EQ^ r9Sb65Mhh(^!d0y澾>DUUU񼜜Hwh -..X,EEEaaa< Ց^[[;yOӚDS5c:,SN}Evv㝝B Q())) H+ӈ M6&Hw^wwQF?~СC ,3 1>WUUYyfVw޹Sh4eĜ ׯ;`JlGQO2壏>[b93a„+WĖ\a```YY^.-- RKK D uo~7JߓMQTSS͛7׭[$SՉLD"ѨQ˱DR }I$!X,Z!Hwh*//wssDnnn5nG"HT1bܹ555\߸a7ׯ_[Yk279:&ϟŷoߦ%fںudڲe˜9s.jlllZZdںukWz`Ej"NfddfZͿ鿵ۻ}u;Jʕ+Vɓ'VBDԖSN`aBB‘#Gfsqqq`` Ξ=[H귷_|זuZGUHRJEl ڵkcǎūJի@lSJu fYܸq# LJ.bbbv؁s;&&i0%%E'''cɬYjlDsNjYn[ӧOB:ܹsBuVg-Q߼ywssy;vl6?~„ Kd2 նFÜ<{,nSUx1njӛFKCj߅%j Γ6l-\~}CC^0h9BbÆ ><$$d׮]F1;;[R9([; qXI|tT-Qh+Et Rd=K4%\\RVk鉉F(Ųk.___b[//ݻw[,{rC\Ɵ B:pjeme"f/|3.99yڵk/++ Ji&V?/Y vW_}(52s捍111$&&+.6w[ڵk...7n(+#5%\+H,YKfff.[ !<8 IDATCIj'`Bv^Cf%Wi&p244T&qY?qС7x% ?~9|p rח~B'i&|!tAWWWRNa*pa5ѣJ;4˯iw]d˖-r]^^۞={D" L GF7on.<<<.]e6׿*JRH]"v'B/w5_xR!zjݻ} M:::Ξ=y ./@7P4'3Wǫ?7n(jP{ =d2YFFF_`|} b/>$}Y6.T`}@Odx37@o@o@o@o@o@o=z=[ C:4rHc] Sz;FQTR422 $IXXŋ444T*Ν;W͝E+bccZm8b baa!D!lvѣGe2cJ&qIJJ:p q?3I4 +'5tEHFҷ{/Rb]]]]]]===,:-tԩSe2L&r-!OBbοGE#{…!++k„ XtҚXPP0j(,|7ںgϞ?GHl.D֬Y' O>>@R,..6EEE<ĎFv͛7MdZb˔@Mb6NZ\\uVlljuleW֬fBDcǎ_zA'nCpϚ5+==_ٹF:y`0 'O%$)\H-׶"7s۩hz\.gJfF ūb`0 ?CPUU_8 qױ -..X,EEEaaa< Ց(^[[;yOӚDS5CNMMOIIITT~=B:""B,O:ѣGtǏ޽{!!! "%%΂LXĸ2 yAXX\.ߴinD{k'vD"ZV*Cscƌ9}mFbڸ8Dp5e?V|ѣGǏ'''c;))) "$$6NIWal#Ukj\D"є)S>#??+VF:a„+Weeex4((!G[!vDKLǺ( h4Fh4j4[B…\"8xX FofII 0{ޮj5=_5jTyy9T\o>D#$6 X,WPV+ЯcU^^&hMH$)#F̝;7f9::x+8˔pM[Cvϋ۷oӒYfmݺd2mٲeΜ9tx5666--d2mݺ/hؚ\M4[ ، ٬VGj:q˗/#i[Vx{{wnG$:uLJ2ug6lmmh4Rh4X!!!ȑ#f800h j9sfII˗cbbC$Sc\ߘ=Jܰt Fzs+7onnnX8o޼cǎ3/;"H, B護޲X,R!uV///VR?ǶD? D"Xf}vEn_]F.ݴi{bKА }}}O8A̅jҥ{[V\~=Jܰt vCd˖-r{N P]]ݼypsq1_JRLJJ _|`k\]VVVΜ9S.3g޻wϖp!w1gggKoV1yyynnn{졅>u l4޻E>@Vu޽Ok_ٹ~ ~46= ~啁7&,ljj>~ƍ)@<`LyO/@K>}Mxwq^tk*0>,z @@@@@@@ɱܸ^h@,8";FQTR422 $IXXŋ444T*Ν;W͉ 966V&jZG;!bBoBbG Î9zL&WyLuZS IIIW򒓓51Ltn݈#~f<ڇ;gΜ˧O롎޽{L&sss[jdYRR'\]]޽Ko舎ڲk?2ӧN*pيWn ԩSYYY呑~!UTT,]>+V̙3LRm۶GHlNH8iiirL.9Qaʕ7n VZ-QH숢1EݺukӦMc*MuQS)))G(}vAAAJJFb>3i+:쏵&%%!SSSW^C'ŋܹ? 6 bfvvvbbbYYg̘қ222Cn8y>lʕeeetĈQ"j/wReMz^.3%fYфUXl0BߟGHlӑ|}}>|sy2bRTT-X^Jυɓ'?}4>>$ꢦpjjj|||JJJ갰:""B,O:ѣGtǏwvvFݻw/$$DP؍o V4x& 1nL}AXX\.ߴiSM'Nއš(z8"HV+JOOB,H$׮]ª*!IWĀT$7ٳDT,Sscƌ9}mF ӈ~ K.|͒,h4'NŬp!~嗰?~СC ,3 1>WUUYyfVw޹Sh4eĜ ׯ;`J{5ɓ>zʔ)}ߊ+Ι &\r' 3""BYQ7{?o,+O6EQMMM7o\n LV'&&2DF*//Jk0'Hx:X,Z!8r777HFk5nG"HT1bܹ555\߸a7ׯ_[Yϟŷoߦ%fںudڲe˜9sޓjlllZZdںuk;(hؚ\Mɴ، ٬VnR*W\ZO>>tc111L)))z>99Kf͚VfsFFF'*;w V:5pb>}tΝ2X@Vx{{+D7oܰp޼yǎ3ϟ0aOs"adcdwyw(NV`yyy*joW_ ?sL#$6H_!aC1cƾ} Þ={f̘-mذaڴi\Ǹa_bE~~>ުT*YLSݥ),wwrVjnnV(X(ZZZ::!jݩGaEg̴_z7t~֯_z .B\TD& !ֆ0!FAiB͟~Ν hv+0uZĮw'NȊCLLLhhO8k֬tO< njjfn3oڈD"^l,`TVVFGGKұcڵy{{GϟJLLOyY];>koo+9CD"IOOonn>+J<{XNNNPPPmm-wdJ,s"""Yy:K܅D H5d@k:zh4b谋82ՍvuVDDijg***Vl-lE;miҙۯ+0PuuuNN-Z% [q YV`~EEEE3O<Y[BFhBdo];1c`ZwE{nz_dɱcrss?3,d*0V.V`L4 sh CNV oVĸMo9r$.;q~~~f+f]MUy?##Æ ׯohh-GUTTlذ;NHHȮ]FcvvJ>To29"Jy% m(NA (CLSݥi+W^{-===11h4R㓓cXvKl{nŒPA 7Ӻ):pjem"~ۨ%99yڵk/++jUT6mj?%KۛNlG}W?~f!Cf_|Ab*E$9s`0t, q]dȐ!Ld:tl޾};-4hPHHȢEVZ_xZ^x``Hw駟O>{좢S5nܸ[,q46 OcT\\fdX$$$ܼyj666.bHTXXh4wmWoms7 qSLt---٣FlD{\cՕRW_}d3gVUUњljH8111$&&Psµkׂ]\\oܸ-QWHG ;s+skYdInn.l2Ç'񫫫+** ڵk{ 񓮭Y$fI'\ d'(^:o`Ǐ###=!!!A.=x@ȓ~7mڄo w[SxLBQ̙3m 'U8v옻}&w?Bn޼ycQ.[LTJht1JDM~u l4޻E^Xֽ{>}}IGGٳg7o|…?ƫG{+oMXT]]}7R5y2,##} >X*gcC}X fxi> @]*0>'Np~ 'tn\5ЯSi4J%J###KKK ((H"]x KKKCCCRܹsz=؜(NsssllL&j6wXbBoBbG Î9zL&WyL Ѵ IIIW򒓓5w1< _{wd2۪UL&-͒8L]zSGGGtt4xq?vuuuuuMNNy;m}O<nٳQQQR}…οk^tiԩ2L&EFF^ryb[S:u*++<22?⼼Kүj_bŜ9sT*նmxDpryYY\.OKKs%jʕ7n VZ-Qh+,s![n߿ڴiLe)𐒒rȑ۷o8j!b&5me1O3vAAAw ӛXVVv3fwwb;v<{ҥK.]̴kŋ we˖ݽ{իG7o%x?>l… .lD(y!%Nl\rl6k4P* B`0yB}!BÖX[,mT IDAT0oyaz(;;{{(.$ƍiL΃rMn:qD@@X,>|0DEEDjZTzzzbammm\\D" vVUU IZn%%%3f@+zпp?6xd21ӛގÇ`ǎ[[[x6 ,++˥AAA3WM&T*(o;j҂VQk4h45MDDyv1+RRRFA|X,~7KJJP-_͍i&Lr ӆ:OO?^ewɦ(֭͛srrrrrZD֘GU^^%**77`0۷O"m "V+Bj2;9xeƪW777Z譭!p;DJrĈsέ l~:42%\S'v9X,}6-5k֭[M&Ӗ-[̙Cw=ش4ɴu~wjQXѰ5i2A8fYVݤT*\bZӔpM!%++믿%rOfB"BZߝ{V4x& 1nL!лIݴ~իW_pfH$2L6|!4bH  Nl%R*++CBBП~>+0~/XAJcǎݵk 1=JJJ?>B(11O?؜B kM"5J&M<Ƚ{M&ՌFŋ'M^]]4k,P||mt:VMKK={6y:Gw[^gJ'N// 3B_pU`999AAAM˧Ν* ;wIOB"XI$|m`0Кޅv1ӌM\S5ȭ["""={PQQ*0fCla+ hK4`~]!srrOh",Q(ʆP(W V`~WZnݺ`ĉW^~zVL_M i0n7 9]H[,… 322lq{97}t˗/O)V2l6cdb}C;7(pcƌ),,X,zJF? Γ6l-\~}CC^0h9BbÆ >ܖMڵh4fggT*G; [bRi~~d:rT*( N z ;KփLS5zb+W^{-===11h4R㓓cXvKl{nŒPA 73A^^^ZM/.uQؽ$''8[vmQQeee| R6mڤj%K`7؎;nܸ}͞=9s>|ӓW__jKd*..^f 7o޴Z۶msssx[HvO6m͍ 7oksׯήWHQT\\\cc-[N<`7|3k֬˗W_}.--mEdLNwܩP(h5bB؉(jȐ!vL&СCfiI56625SSS[[[%xR|'諯ze2̙3hMggg|jd6 ͉B466H$&G;pcuڵ`7nxKt\ߜ%Kҫ˖-C=|~~Q"88XP]n:?Aښ\M ؉PL}_VˡCx ,|qddE+OrCCCBB\.=q>x@ȓ~˥R)>,455IJV6mM_3e:PD3gDDDH$77 XW6fRT*IIIxعs+(݁CKK ^pvvXt)jTee̙3r\.9s{oYBz'vu˖-rnۼV޽{ N>׾ўcxw655UWW?~|ƍE qOpvv0`L&k_a%x, ^(@Vo 7 l ~ة4JJXXPP$H.^RtܹzGHl9zL&0ccce2YllV BX_XX-QHș=b1:5w ` b1'_wL &W_Љ>'O4'N>=uT\No={lTTT*uww_p!`X>cWWWWWةN:U^^baqqq^^^EEҥKWXbΜ9eee*j۶mg\Vػwe޽{ѣGϛ7رٳg.]tRMMMfffm//wRez^.3%fYфUXl0BߟGHl^[[;yOÇBUUU~~~ XCa*44bx3VGvO ;1DS51~~~n^?~3SaܸqgϞŒ(ڙڸ8Dp5n/&"hcƌ9}mFbBb7/ #OIGKω'bÇ/_ގ?|f7Q?䓣GǏONNF_D>^Em$JaFDDh4)sҠ:OO?^ewɦ(֭͛srrrrrZDzFU^^%**77`0۷O"fsttqNC,[Vjevsp#@ÌUyyH$rss5#HT*G1wܚoܰۊ0הpMK.qP!0"(99~e___bXh#G@n&"(55UHԖFC2{촴4ɴux dqsҡ[@T^rj>ydժUǏyyy-"&j9sfII˗cbbg~N#D"j8q˗j۷ok*ɲǎ+y׮]tR[X+H筷޲X,v+E_ЫX^^Jbu:Z~r.x+VrRىp~8b5cƌ} ={̘1[!;۰aôiӸqa)4/޽{79F*zܹs}UL.7773-1>.a&"d2!ژtsBjBZߝS׈9}Y~}xxի/\%/_nooPTD5"D RT @FG$$$jD"5jL&_zsrrO/w! Ν;7|5.D-++Af: ,a]X, .hg6lgǽ{BBB/_2%w!_J lpc1}ݺu ??AAA| VsL=~'|b0>O?8b&NxѸo߾`oBXiڭ[:/a[h~DP(=zDKT^|Yׯ[yf-/]ӧ/vmb5kVZZlNMMG'BbaX{Hub {Bؽ|555V/_|9$$dƌ?yԸpUR?ɓ&MbayرcYBӧ?b__iiiZ_2e B(??˫OMM;wnCCC}}9sӻ8W0+6jX&h4 zNj;S*}|諯ze2̙3hMggg/// )؜٤hllH$111"յkׂ]\\oܸ-QWHG ;s+sk!I&aӦM {1{o'arС7x% yJ1c*Xpuҥ{[V\bN ۷;w|wm5^jmm]lRJ90l6U*J2))i述*0b2CkK^i~}O~R\ݯiwCB-L~C[[۪U>N/ xag%^E Bf%~ƍ)M|!8;; &@@0Too'Op&p xʡ=7?9mZSSEQMM113<ė_~R[@%v~ v_zzބm90ef~;Kٳ^_cFѮZ{{ۀrfA?^bW}A6EQ|A_{k~&GDܽ[zÇ#"[T`FRIR,,(( H$aaa/^PT:w\^#$6wf`(ccce2YllV BX_XX-QعXÎ9zL&WyL $N8Pt#ׄ_GI9##J1r}O<ռ$..N&߽{ܮ-Ο??rH]9Y,?599j:ԜջGnצpa{(jȑE."?K;3wؾ-3c9ٻݻС7/ @g9 u=.o !;[7̘6Nc瘾gϓWQ0 W/_|{X}'`W`~:ir\@7FHo '?DQW^kG x#+ V%/#.W4jE;BE 8_aDÇwOF+ABwn?^7a]n!DRN</dqۃ!<ϪBwnc{ux׎(8ʱg!@7,~lQbNN֍*0ƅ8?jjl ƾ5:PT`i] H f?`*qѢ/33{#U`BxZS#{{{;ni]~0!(?SrٳgD f6?I,=}Rgɉ+Bp p zv`?E555k4gϞo1݈E ntcY9jBNY477KO!cQ"=zXeX~_F+|Q8m ]H fߏ=:5u+cŭ `%R+໐!]H QL Nq>!CrUMF#].$}|B/644υKģG{HGQ?|9@|X{{?yCBGv%BwEw?\.?:ǏWkgzz-7ŭJ)8#5ck Wjj?u5ڲGEK>۹([&ێCB rg9VF#)v2 p~ƽWmr@qW[YQZ$P:Sd7&7wVN!Ь.osJw)[)Y_LꝻ Zn+/}i_;_ٗ,ױ*7jUPMrmK`e&o|?]΍_E 0s{}vsuΜI`O\M`/c;r im]W_>l6};?8x5H(wIDATٶmYQ_ SwT1{NGDaODZ[{v)H`p}u3[vNOLL8x*<$o!Y?7IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-list-graph-follow-2.png000066400000000000000000002373471351617527000257340ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxyxL3Kf& I$! bRb颥EiK-U]4j_kN*RkXZk-D""k̖I2l̜{{nr{=##r0EFIHӦ2ZV e%tȰիWwvvvrrBX`*??M6%!$-CB(B~BpG鹹...JRV+J<,˖IHd2oLs ?ksr2R|kH>q'[Gi\MJZ`YG$ֵ L [) ztu:ay Z=i@XOmbF訤NNNyY_g^p`TRRu//jժi4rss !V߿?;;;%%A+%jt%!)SA s>=g/QB "0cYi65mY]#7we8xJ()|Ls3u/fj9eRϝHIOdF_()CwNVFr@e }KpgR5u떔Xk`@yetZc8ĩ9RLxcG'8ɚ/nx8*Xk}#Og',4z_)&J)R%⡂oTYfF_nXQFbNb)q>@<0xe:cKbH'OYs1z{:_yKq9 mxx:i{^(%  '4]誃wwq:VhVIR+*P*f uS]%P:omz T9$?mֱIU2aؕCc=KX߷\. _O6B׭F:t7%ߴ6+MB޶ ~y`}岞>!e\O1<}2D5mBy]{T<)p+x$F`z^}HIQ9NRiJR M_h&ctK._J._,KOa:c8V/SVic p,c s^Wm Mp3ҩOƞ'dԨо6"V 9TCt򽗂;4r3[GxVS;)^s#=N@_ ga:*Mk]r$r`RW0K礔nP+~/_Y}镜OS)N`8>FYis]C|]|HW ƿYOѷmծwFЮnB654=GoGYc_.sNjc$dac'yZ]qqRiFj".$PsQ)jl-9YNd<2$=G+h 'ϱ#96-Srƥg>HPS m׫[]R4 q#!I!agr T"òFYjEa4L⤐MʔjKήQ.t8BBN+\+%^>ñTr͌T[\>-ޅT(#E@;w6piy1Q)bTF`Jվ}{mI3mF`| NK,B XFN =CP.t"[BxeJJRi Rϱr3/$H &1mL,+FEO"a̖2 -/N8AvY|kIF`,1ANeD <ϱǤyIoviE^2:kXnbiX@9K)/<?t)s)9:kN$SJM|~,yo@_v_j0?25n#nf"Ө]34 Y?C(΂ONmkxyB?yA<ի;{i]GQ9)]xc]rgLnb4&Lf89,pNIsyR*γ[iy2LR2R1B׫!$ak`u/FVj?!ey^\X&pMI\q,溺reU*BgSJů}XB@ ɓRG1`88)*a#Gȑ#R oRFu܅LN+~IR'%ܣ8lJ*Khhב/,O]{[GZ[I2g@M_N^dbg+y|q~KS_(<}QW\HZfwSXF'?,V ؖ |S2A=Z)21 "< G*dd-v|n#_n1/\R*<#;Y}_O7:X3!$3)б-LHNwR*Ue=_MڂNGXɫv:xɶ,CNs7c[yZ}hCA>I9e<*б-FP\wS춇 ,/z~I9Y8T"8< pe).n /_"1Xˉαjg7I9HeL"28 L.W*vrvvD^ot? zJoō힘x?x]'o=HkO?zF=_}G/qZ>q>ե6^ kSgoƧfAM\ۛ菇ܻ.1S2Ou;9ݮ vq&iZ?w )Q\&T.7590\z{ћydM>빛ݹT* |?g^="^- Uxgŋ111s݋kC8F1|_F~2NBf]'z^ϱxѨL&W(NfzXoԪANͿ^M"oXk[g,I> F Ms9Jf-X{֬^{xxTCLj˫zΈ@gϞV9|$T*ju޽[hRtVرcs!{-:-3(N*gW'*߷խn4ykPg3և+*AxU*NNN*>)C', W('''ɉeYF5Uш"0<#V˲٨I\RR5 f&>PJƥ"KWud<±ke.j~̖jw/ʟKEȿ_ևR4QZU|-j*JTjO<a|s:εjղ%x?1j˓>3),?TPB1 SPPqꯟ? |ǂ\[9g{O~On_Gw[z.+[*>ILIO+ H}jr wpMi_Pp 8}B֬0)&U֕^p f-_b}{cKvoԨu?xEz) MѮ=LI60%Yo)4Qz @Tk`PJbr]Og[6:uj)OYYo"%n?U EReoQ~}]{[YdɧرcQg>6}mڴIMM]xaJ擄!Vq}e3g~;(8qfmXqɃGm_]&lituu-F~ aÆ.4tܹ֪Ƶzi١C|5֭[ [hQ1Z7w^C:jz츱Z=WFRϥK6}Ix5W132{vD||YhaԮW*vU<e[EΛ4~D+ gg{Yi^uۯ[Nׯ^CdtB{~嗿~[Ra{f͚Qp#Z0G[=zoR/V.z?<=Ӝo;v,A?ysM;Ӫek_W) ,W^oҴٯ6w7jJd.n3ARbYw ?6@9P;+++>>~޽s!V"dzϷAYIJL!pp 8B8q[&:#BʤU8a``o.m5˺ WfFzYwp8V#c=CFFQJk9ޅ/!$3RrU ܅̼l/*ŀ!('F`PxyxyԬ۵[J- դIJժU#Gwn޼ZСåK={4h@TٳGLիFիWVVD ޥKBCCjusss 7m۶MHoTebŊ@\.eҐsڶ}vZUYhOD^>{ٻAG rfA2deOiIHm˄?fb/o_?ݺwjwmͶugf;>@T5Ggk>tPNN̙3 "&޽;***++kРAo8jԨ+V/_<<<\L5kVF2335j4k,+f"'voȑG /\<0٪Lsnݺᅬy^l!#7o… q3-t*x-…A p"f+!!l5CP 2_J%$BFVĎkwӦMwm)CFzZFzڽ{3>tܸf3ݝիl޲Ŷ=(;>@T]HJ5|۱ w_?}ݻ/&JKyy,\^MFGGK?!o Ю}sKV^"OMR_f[l)1q׮]!!!...cƌILL ! 11::zԩ...SL{h)lCvZxxkxx/"&;6***##zq9/_`Ë%f2|ʅ vAR Zbn8Ȇ@ٻ]|Z=zmbK?8Ei;J}SՆ߬:$ QzFKOĶA%Bv'XTg)CYﳤ_{l].((~oiZmBtJաCŋm Ǖ<{f&J7x#0am,6mz/ދiӧg9/$O.B>6}7͝GIbcc?Jl&4͘1coniҥz۶moZ1b֭OLL?׃Ozrwwwss0`{Lָq7jM6ݿr:u)իǎsuumҤٳg-5dիW;uhm4ex Nx% 寋oC[,RN[z"zE-\hv׆2R]f̙R $%% 4w%3! BȞ'9^tlP.li۷kפI8plƌ'eV֭o߹#L̍_d;q(}jשe֥K)111ƏW'L8s&ƴ)S\]] 蟐`W&Nxk!$"ko ؙ|G?s5>222**jٲeۉ'Ι3'???""b„ 6Vn!1ݻ͚5:ti/_;66V.Bƍ7rȎ;B*7RNׯ:a„ѣG[j. o/?;w,[TL#XDҵkڵFA:_x);tWڜѱxm'ի֦-Onō(H{)}rRKlBw7$QnDRRbg3f̙3ƚ-)Kc GF~2NBf܍ oG@\v=;;;Ehb}ÜE&֮S }}} 4ېY@t:N']lٚ5k;'lgggV[F 1g>ܠA۷og%3 c={MY388ʕ+...ZèQ!OzԻft(ۧÔdBj1=RÇu]Py7*%=^~GϞǏmڴ4ڍ4]bݺ1 TAپ}3?iOAĆA/Ƈ!MNH!O3]! Q9>2?@ "䙟 [ IDATQe>|"%?n\~Vzj4hA O9'O̎v<UOVVV||޽{ſ|cǎk֮3Wx{Y0ԳgNJJ;v\Q['L<9---''g-[ΝkM ޼ysAAM[xV]dI߾}$mH=o޼VZ!ڵkӦMlk#ٰ*K9j###7on!&M'Ν;ϲl\|'9j˗[1uJ"<|T?~(?FҨH5kiӦ ø2 3o 端z_BpBvX!A2r( Է=ew!o2ӆ8},Nu's"0 s2*|b} 6mHG *r`wG[7ĉϷȘ>ܥ8sE=jW^ݧ֧jɓ۶=_="_~ŊժU[jUdd3""͛7oޜ={D HSP(}7ní٪̚={===׭[aÆ"5+/:Sԩ8x;uӿ={CZ(Wm\/P{Imh[~pmi{qKzֈma=端2O>OnDZn ERZ+v! FD+˻v% ~s]/8,0ޮݻWʴ/ozӦO7n<>~lvl";TnBCXQ݊ qGЊrr&)ǯo2Q8@z_ P> Jh웹q,Lo2BV;6Ȼ\rU| = I %ط:P xu^|ŋ̠P(jxyi2~f恽3j:i[6'KP-韏(d[İD 3zTB@)r,G)Tx3O9UUU۷аqcyAJ^/d5k֬WޡMcG2sȳBS`9M }f͛g #oشmmjJRV ͦP(͞^&>T$ҕ*L6gOOO__={̞=F5kܷo!%%wjqƧO.R|󍇇GZ[ aǎA%3@E0Lbbʕ+ q)S[Ǐo.]4<T::y{PQ`M|GزegBw2f̘D1100"m$&FGGO:eʔ)V6Tk׮/bbJJرc2227lO= U4| rTJʲ@U!AN8aiҥz۶mݺu?D$d&Zjݝ_|qժUb)C7޸qȑ#7mt}B^:tʕ+ԩ#XJN=2`]U;\zرcoV:u6mԡC[zPQ.PJsrr>GFFFEE-[L|;q9sGDDL0. )wm֬СCM_~ޱr2nܸ#Gvؑ`TaUrZeKjׯ:a„ѣGdɄmt5!ds:Zl\W޺TjZzt&Mi~⎘6ZhOJuiw-'7y4q k_} [vߜj;!$++ҥK_}\T l0y䴴 lRL;w5k;' ޼ysAAM~-^X.Yo߾V6$Ξ7o^VRzڵiӦ}%18ê,4+iZ622y2lذ͛77o?NKK[-]v)LhFvZBȌ3Fٽ{Tھ}{hh7nx]]]+aba wuqq3j{]nPJZ5kĹh˗/:uX'UqX*~7 ,ꫯ !M4ټyVݰaCfĜJ8M6`S?Z^~}ڴicƌ1A|"! HuxG*+\f„ }WZ͛'j*wwoذȺu-Z}MJeUfB.7ߜ>}BȻ;f̘KBF-.j/~vMnΝ;K,1cҥKo߾"3P Jh~~~_}!GVR;;;_tҥ{O֭[7n9E mM6}ܹso޼-E`F T6lXLL̉',X@i޼zA.UV6mK @}S&ZnlٲQFQF-[0srrJNN_ p']K3g$lذaÆ ΖIܱc֭[Zׯ_xmiM6ґ#GBF)a6f̘`B;UJh֭[f6lx"۷oGB&ԩ0חeYl+VXb@ጮW!TBP8\`_h @F (=wU냇1 P^x hPRE k`UQ}f%`c)+v߰cb䷔0Jmf.nJرݴ냇[a/ѻ=,4?"0G]H³g!;D`Vlǎ!!!jA6me2L&S*-[<{hX;233{hzꕕUT@E_^://СC'O6QJmm֬Y5lԨѬYkW^ /T7nUnݲSL2%::]]vaFX[bbb`` !$(((11nTtYB&)M_.)u,Gkv|Eu,_’4HT¾n*5?&J)qW\ԩ&NMM0*Rv{Bk׮]TXEM6nܸӧO,;j(+{f_~/jK,۷oqP1!4hPDDѣ]]]ºuf%ʕ+WXa!""͛7oޜ={vqP1mÇnH)5Yfͽ{ZѣEjr 8"0GChYť(.@'S] ;Cň.x`e?3 Ж vE܅p4D` 8"0GCh !p4D` 8"0GCh !p4D` 8"0GCh !p4eYwQRA)8)4Z( 8?T첈g`~ЖN*B¹[L1 8"0GCh -۱cGHHZnРMDL&ɔJe˖-Ϟ=+%4z+2Mٳ/Xzu^^ޡCN<)m燇XZ\'2+B6իW **88xƍ[juxx[Cʦؿ۵kWK[lԨ=zP[d2BhڴR!@%V,44?ݻ&]iii>>>T2)]iӦ7,Ǝ5J޽{GDDFDDD"AEDD=5,,[nV2\2))?99yŊF(&T5E6|Ç%]Tf͚{R* k8"0GChY JIJNIJN)vqD`gS PJ)ˉ5k7-[1"0,W%K8@ m\g" TxRA%bD`XJhOL%eݾ}3 @۷/++Kz}AEȡCeeecǭAP"+W }{f2w֋ (\jΝ;Z}ǎ-Z(bUg!3\rŊΝ: !"O;W\.6lhWg.T>2l<ϙ]@9)kNJ3Y~]zMf>L}ק_?ӭc&&OBlPֽϿS.1gN  W.]MVl˺ Ww! U{ǎΰg u@1s-.30vb=L&S:){U{*@ 3oױ]*Jン":;ܧ{alC8T&I)~ sgΜ9sI2;*@v}}J^cP 04JS @C)=B*ש6>V,==v:Op_swwGPQ"#XzQ<{14ٷԩS'L0 bFRxᇭ[N4]JJ:3񥲦/GT1p"14!]rUN Z-Vc9^/8?I'yzzms3]\*k)Iq( # BHVVÆ 85t;^$,Zc ]Hʭ$s/JX-T2FffA:uhBHf<n-Z8u"0ʭ$Bvņg!*%HԩS-ZX`>y6<0B' e,!gCM|{(}Rjǵ+@퓑U2@;< P)ٲ"kFFZXO;V7n E0%F5k_`*%*Xxr{a4[6WvߩscG\vЧ.;^C={`N5̚5[*ɜzki6U|ߤ} {6;PJ*ƳPy򸋋ky`2B w2o_>m }7%bܣ旓R};gR!f@#=.$rrw!.$rrw!1pʀ|L.qWyM0.$gt,xꕬK9"ݫ7iڴnP@ىO(ea52I.GkmW>ϧݻ iUt,ދIRQn !eF+W(Jn4i ޹p|jCӨM ³gf!7yK G^u_''(N1NժUKt@م:JBYfvB[ĩ*:GD`,$%%޽jd24Ja֏0%{O//Z 8KUYuu?ʩufd,P(1Pv$ N>PJA/^>>uũ*4GD`z9sTn*IBѠAC\qԟC̷< IDAT_#B( =+tB1>ݕJ% j TQ|`Nݺ|I #"0*))5kt@[fZPvFF\E*=@)W~J'OZ"ܪP8her 4Vmo"( ғIQFYw)JS70W.{Vw%oܜt&|5 J@BV ~/m[)+}JcR,K+}ӧ!СC6 Ud 3_?!C2o?tu޽3g~f)Tܑh(+fnyyxyԬ۵[4Z x4*7D);tܰU67o OKX۷V~/:|..sڶ}vmglѢEǏ2bObݧO񚍃)}Uhr< ,C<ϛ_?]Nnw͛̚ZGKդҨ٘gQmJA5MV'%~;o…qqlㆍvu8p`޼y .ݧϜ."66;::eӧOVnܸqƍVZӧq\Ywh*By3tи/SNeݝǒmY *oJ)⵱:$ܾ} ]kש;{7^>bbjjkܹB  KXbb*UNvAL4cz-}jB_4 իܸIhigXpŽ:TzAA+W_Ͽw{2Jzh 4mv 7hظq_U`cDY;wT*7Q*K.9rR>TaÆYFP(5k 6LRY)kivVرsNDR9g///{)R)_߿_JTRsΦMj4Rqq`*Ss΍1cرc2Aftf&޳gSNv{TQO))b,1vɗ_~QN BH6m={kߡ_Zoݺơ2a1cf۶OׇHOKK}v횙3g)޹tzz;iz#>>nܹ~Qu&J+aӧOba/=&[1$?~,=-ɴiwHLoxثW./RǴ3W^ԩiiӻw^|\׮/L>]cXFlp|޽AAA,˲,kcre҃Zr|..řz8qΝ;J3qė^z)33G'Nr,lٲO>D|+/dYVoٲeʔ) '?~ڵǎ;{l1:owvX|nڳgτBHLL̎;^|EK~g[Q޹ݷoOg<]{( /luW]a BNx% 寋2D/oBѨQK)Yqqq;wn jz-?=a$BH?D ݳ;ց,aN4lߺu˯:~o!B""vuu_:uz5*ʰS~1:jz !-Bm,%6ڿ?=HG۫Wb/^|:Jɓ[l1y8uԎ;4ԩS3fh46̴ӧZz0д{'kW_]nw $&&4hjz޼y2AF=3[:{3F,|v<|ېŎ/m6]t-#e|fz1?Zbݺ1 TJkWڭ%-N Jބw~7b;-[#0`i _rb!fg\gcbbw^d2 N3ۨ+B˲-7j i;wnڴqРAE|?@V9aj V /j@d2K56*=~M)G^zu3`Z|޼qqq_q "5*vP򁅪`Ĉ>cǎ{/00$m$E97[۶m6mG~sgTTԮZΙᇝk׮)>@ٺ)0 [)]vk׮3̚kĞ={fNvvvRRر"h-+СN9srrvFB.`v tiU[yĉ|2ܹ,OC1cǎk֮3Wަ 8p Z._xN^^nddرcr;u_rx "rQQQ&LoN01**J[)kitdRò:u\rN[|EΝJ +_8K]ruug&^X)>i҇rbs_4 b*aĈ>93Rf?ȌX:{K֎|!zmFFnxgB;#Fl޲eԩ+Q_G`_1-==vޟn-(YhѣGxWzm6i)^R* 7_u h7_dbz 9jW~Ж~DN`v̎:u*v+ډ9ﯼ3>suurF6(0Hds\gW3~dfWsvv Za…A&>dG?9VcWYCg3N|miu[G{Ҁ:k192/`@Jx4hnczs=1gK??!V[p)OTEg}#GYm<2,i#f~Gp\eܵY??!?f,e8_0xpC*ĕȧ t3&}uAw*ʾs"YXZXhd4Wľ ;::,T>< z;+N~n :t#:, <[ufF^;|(rˣǎV>%a 9rdCC?rg^zy l_|W-С11]A:;;mm'ts:;;u:ý ze+ma_?tv o_w2eȐ@σ;C7LOcjEg K^㌟0ҟ EznO,--G9~WFz~8@!: ~*03ȠA2X(ZYYw8*,A C1XP+G'~ @߫n,Q8|V50]G `W;/ZZZ❝8ޮoq;[UW~7'G!z#@ ;*(:uXىZ,4h_P@ ejgg'+{[Jk`7K]=UjVP{~~ h2о@ ,pfEA+W䀈I8/K?~ h2'7mct@@:;;?>o;(C^QӘ<ũҷ_#_zy Gp_^>eńܐj;`;FRQQ08/ 6,X=ņZM1GO 裏?.,,B0r𕊖榃6G/ao͙q{#Hu=zw߸Awޙ}o0Rk;;%JV"㏢,X`QdOur4<>q#3DYLyKA9d~u̘gȑ@lAe9"Hz_~ '\UUsN:?F.N<~`޼y=<Nbfoo%ԛPd>5ja #F 2;&!jaaA_qX(֘1c,1b3gXx۷S4Wޗw://`QoFcGUUտΟߵkG|cO>ӌ0-45`ߞ5L: }2q_9~~s? zWǼևF<5a`Kz~CddxXmys`1؀NG'++^E_qqq -JLLd׷qaD엠VRC$44th8889sFV3g΀JC sɒ%/^T*ԩSNNN8kr&''76ԃY?ݸ/[21 knn.--ݶm @R BDd2___rj5jݝE Ex<) ׷H]paƌ@z}+++w7CЎ ̺YfPSPM{PM#NNNLB fܹքuuu .x^M8NJJD"4iҹs璒&LwJHk׮v0rA9c0O *@mڵK$TVVTs= f;nhZA-cgQ&#U8NllP(9s&7А.]"1cP(mڴi׮]cS.岲2///¶mۆ֭[q&;BaBGGǺ:immrLPgϞ-d2ٜ9sv~СCc2>Φcǎ)-AssW`DQppp(//TzaZ}"$,WY4 IDATKzȱ*//p8vvv&8zbP!##mll.]ZSSCLV;lbII1$/GEE544:::^J|C)88x׮]{|'Nhڢ"OOOz&'..ɓ'2ŵd2ŋ'$$h4={3z rAS:LgybPjHŋ7QR=((腼LVP F ;ʈݫjݻh"bGGGhѢdVJ_l7|j{pyChx<;C|>---{qvvV(ǠvMHiP?)T*Lnݺuڵd￟aP|uuԩSA]\\0w'99ɓ'...8.f&^[[ $ǏrΟ| DsV`999RrvT*%%%BpE |I#G̀ 4l>&&ߟ=`X,M1ici Jzzz?bBlƆ9g8 h48"L6B1*0T t <1IH$BTW*_^i`Y /:h(J"nEEEӧOgR$D"cJlj/r8/z5!O0~vSecmmmW9s&---urrj,P烃JBHHH)B}C=p8O< M@avtt6mZ|9YXUUi&PWeddxyy7i4"G Bmn$?_R"@kkkq\V] u[[eq&tSLt 4TA4q .t:JE>R"ѣG,]Л0uJeV`,S*乆BR0'64աl7 Ԃf V`t 6iҤ+0[[[P B]L粳9Nvv61]dfo܅d !~zŋd ]@ 6Frߡ

8p-==]*BaxqP)%VL1ici CnjÆ j---MT{nB5kЛ%##C8pV}4T5sT#5uT u:]FF!4Z0ҬIj>Yz}VV믿$fԨQZ6%%P:u*[VV! I- mjjjll;w.auuuqqq?6ۮAiP?)O~ɓ YYY9TL^8LǪ͛@)00677Y: ` &En uiddRa~~Ǜ1c˗ח/]TR6[ZZAPPB0ufN h*((pwwr@,TȊ=hN*)&h1 _hz,111D{[[XHLLˣ>7۴awMDm޼Y"H$()?~ܹ|>>44𶶶}/kٳgV\ EEE999k׮ ˆ%KrT"LHH r\(&$$: 34qmذaΝwލB`BhG/CÎa?OVk2 Mۿ0>'Y=z^}xKK={ }h[n]v-AAAZ699lY !ZjUDDDCCY+TJ9;*yՒP}vb$P!怫pZq9rDVgffg;hG111ta[b1ElIMc2Z[[BOOOp트*& [ZZlll?(πqzhpooo'/E" bU`ЩJID3mڴSN%erB*Dt hT*qW*"(tH$2R : u&SFv#e,jM>r >}4ԜWp'*0/^p.^Hqm3gGuPH ظq〝 _|W_}5j(_ +--NNN/_w޴iXP?0t㞪D͛AAAC]hO?Daƍc֭OdT*#dSLtmvrdddtt{***?2h J'pO&o0ҒazJI45'~wVrB*Dt dPmРA.biiYYY e/I?,C7 %6lsrrl/ggg/Z9q'?r޽ o߾r2xОݻwz{Çgxccc T`ٹG&э**--ޞ8^QQ6}||8֖.J{fp4q\Fs >BL0LT?!%$0sȦ44C2>E6LB774_Rݻ Ń֬YCCob Nw[9S>+IS!qf̘e˖  cCɉ0gg;wPYYYz>++H8NAAA[[Ћ'xD~.j{ K*qAESь5Jզ{gժUmjjjll;w.auuuqqq?'O\h듓 _~^?|0;w̙3wa&rr\x?~ɓ ZYY9;;>>$$xm___@@y?>>xRn:DSw! nMӝ[?[{1x 痥`&<~<hjl 3y&NѝNۼyD"H$QQQzð;w.22Rsν[BМ8y0P={6--oʕ@XTTSQQvڰ0 Xd\.JIII,B)M6lذsλwDFFaBBP(B0!!EsLCð9++ߟ 5EׄgـA0>'GL>|!H˗/D"Bzzzxx\./)) x0STGHMM 6rt}:9vf_70*J(R>Q.|}}*U8jwww!M#?D7 %qt .̘1]]]߿xeeڑaB^WW7k֬`Bj u;';99YYYLry ;w5o]]… yfBƎa5TԑD"7n@îAIASLL{%5{_"|۶m?޽{D-[ܾ}{ǎ%F2&`7oܵkתUoܸ}vO?3gos]iEAYiRN> DEE߻wo駟F:uՕSԘ5&&믿|krrrvI(ӧb]FߤT*_رcǏnjZ`1RV^?zhBX]]mccaؼylmm1 ?tP[[Rܿ?<&{,C_X-,,׮bBlƆZ@xӛp8Fx{{;yh.  b]N+JSs'::zŋիR^^^R@BR[h\tGx<@ 5ke7mڴ|rec4Lxx-[(xE9F:.>hN8mmmREj ÔJejjR^K=K6Ť uOX:t(NG6LB774!T*ݽ{Bx5kvM ⒑8`|>eZw}VGž%**ߺu 0222%%%--mݺu,jt艪VWZwޘ+VhZ>x޿?E8xhR^dof- GTئعsO>-x7ۧT*[[[XB HXQwp'O ,$42d3g9z믿nmmᑟOȿPaR@OԵk:tlMKK۰aNyj qHII1{Asxvvv~amm-pʀj@ZƍbX,޴i#I7|ȥ'O[N>< IDAT,߽{g]Bt6T`wkh*D ~I{{{dddXXD"k_Ddg&L4xKKK&7^HhsssUUU^^ z'>@J(N6 _GG 3c!ϨJ (C [H]H@ s*0@ s*0@ s*0@ s*0@ s*0@ s*03|@ D:;;WXA=z֖r L&J|>ϯ 󽼼x<ތ3._ eee|>ҥ*E ՟ڰk@#BAA;uww/((– @P(XЎH@݃p)@@hB0СC...cǎBtBD*hRбJMM%bܶ_|z;w[o% ϟ;w.Ϸ e8okkK'&&Q^ G޹swvvv@7N:nD"DEEz_r孷ڵk,6黃jЎͱ ٳiii~~~+W¢kׂcd\.JXPP@}t6lعsݻwcbb"##0!!A(rP"v9^!CÎa?OVkB;ydVV֗_~YSS^Dٷ0 9@Ç?x"}eHD>Jw8 ݻÇW\rJMM;}LjbŊ+Vr\d;XHMM&χzIffQT`jaa'vE/ [ZZlll?.)πqzhpooo'/E"Bq\P :(={?ŋ@rUooNTZQQU”#S+Ly1t钏^'*0 f͚ELFx<^bbbKKJڷoٳMmmmW9s&80ѝ ۊON9D"z77JG,@T!8w NJJR* "!!aL6|xx-[21XhGP͞* ///Voaa!q Iґ|I}<\mVՄf@CrY#ΡdIꞛq?( iĉ.\t*$(YH#.M:%2@+0MDw!TUUeddr/,,\|9hBӏ,4~ABCCSSS4pssj`YΝ;>>>ׯG6M=S!M4rгe:U`ݼ ) k4&tw@!&ToF:>hN8mmmREj ÔJejjR^K2b҄{񶶶ǏbS8tP_iaÆixB斖FMT*ݽ{Bx5kvM ⒑8`|>eS>+IS 7=KTT￿u .adddJJJZZںuXLT03Btt)GGGo7ܷoRlmmMOOɟ?#h4Fj)))'xjp̙;vhjj21?66166vܹ&5 *j"&tw@0v`F)!ǏOx 9TH OwJ.'y<^```ss3# z[tSPM{ bɒ%<_55wqڟnD,'&&ƎKHCBBBӧ]Л@/<ݻ7gVLUs Gس=z_'_0Q&*4ȓ`gSRRG{Lg'O[N,84XDEEE2 oicoooggI/[L(W*֭H$d!tPMNjΝ44VqFX,7mDܕ3۷,X  B ܹd;GTS nMӝ[?[xOw&HD6FDDIG.Up3y&N㕕nnn,BhG {]]ݬYjkk M)&Խ7|&8$jzzYaʔ)ϟsZ[[n-\yxx{7p8)))dҤIΝKJJ0aw}*++}||D"Ѯ]̜Q=9CԜ|p8bxĉRz: MZzaw ͩs!{{{3gΐvV_!cp|Ͱ077 $vʹiӮ]r,yyy8m6ǷnNqX7gϖdmmmmmm2lΜ9LBP!\.wtt R~z;;;rLqرc>>>8IroƥKXircoTya1ajx{{{qq#0<=8ݻW&''RɑJJerryjIIP(ܾ}@ `oT|zKW@@#Gjufff@@zvT__Ow\U,Sɦ4|>tГ'O2337&A9qU@(-3r 7p8v2\$Bp3财 tV 47(YT'SҒ-CawSy !J+3vttsk:C18/r8/jIIIJRP$$$,^IyKKKZ-X7nęCxGGiӦ˗UUU6mZhS󢢢ӧSyaPT*qW*]* //:&FCq/,,_Mi$?P㵵8V .񁆥岸GC&=777~'P'0ĉ/\T*I$Q)H#.M:%2@+0iMDw!)0UW&0%=i f3.9{&[[[PiӍlm. Q$i4Th]ŋ*a Z>>>hkkKOOJ,B(TPNy#&=__Ǐ?~\,ơCVWWC7iaÆixB斖FT*ݽ{Bx5kvM ⒑8`|>~zg9i*П;$0 .=M 6km===5mjjjll;w.auuuqqq?ecS**j"I*0ƍ-":9s;ȽCCRZZ뛚 NNNL5ͨQZmJJ 0cꫯOdɔ)St:ÇL޼[\\ɏ?>yd@`JBP6O'y<^```ss3# z[tSPM{ bɒ%<_55wqsD,'&&ƎKHCBBBӧ]Л{޽{I|h1 ==.8fab50hL0h50KO &]τB!(G9;!_lhn#:ZvƍbX,oڴ q<>>>$$i\޾}{BP(.XΝ;LBP!W_}EHT[N" ݷ~;ggggρC׮][__<''.33BGxqqܹsŠ8Wƍ#Kn߾=sL.;s۷oS5A[tGV8ޓ@ DonLބbii mnn۹s'a@ Fnnn_P@ ,腫S -}@ ܠ @ ܠ @ ܠ @ ܠ @ ܠ @ ܠ `|)`Er9#УGRAG@&ɤR)+++|///7cƌ˗/aYY/_tJbBmN:%0 ?B.t\\.ݽ[ZZAPPB`B;"uv%,(P3~;+"_hzrT$MYqLc4[t:͛%D"yyyǺ,r[o%ߵkטP?B2~xrҥ  DomdiD,@CzvΞ=V^^rJ ,**ɩXvmXXFDD,YD.KҤ$!&a?sVVQ[[kB#׆ vyݘH LHH r\(&$$aiаc kB݃'0M+9^ػwÇ\rʕ};88pŊ+Vr<44444I*%nR@ryIII@@{d&d /]q1 cJ dVd`jj;jn֬Y7Z˘.uW˛[-=ADI@cB0)p 5yT@QCE }C 2v7^~] $\o=~Uԣ7g ZHRTjl6_x100>$ˋCvduдC3 uOp `5//o̙j0cƌ .%44 5**J$ܸq wV(ӦM;]NƀLjZ$RiZZڨ$YYYrյ*e0i]`Тe?3gBwwcǎ#2"p?x𠳳)SΞ=KSSS-T3qwVXn: f֬Y׮]c __ߚ\]]GĊ+lB͛W\I -Λ7`0 6'T$>}:8ohO>ɉ;[sװuĄA\t qubLL̮]ZF\h-x fΜi2oAJ#_~IHHLٚٳgJ:"x&aX~~_kk+sh͔ d^4l_)lZD ^OZ4A(rGjIWl`^xl6t:rL&Md2ӧO9`6a(qR#qr<Т哟w}wժUVC#QՔs81i`l-$v#">ZH$$h4/WPֻ-:r Dd"(ӦM4tDв$>f3$~vvvQQQiiĉI1))C8;;:Auuu}I{.hbqiih,))@ 5 yyyJCdK5aZ6;;)sgtt7n\ss3td?~d E///JeZZFy5k~Mo6sssz@,Qɿ&vRXXH=EQX-Z(cǎ7o|EfCfX]?npm}}}dnG m۶Ύm۶bښٳDw^V/DhPð7o666FGG͝;/M]lllUUbDReffΜ9h0adڽ{70xEE`8p%t 50L\>~o)H"""IK@vjs6c a-qݸq3<<\$wuuq1Ej6in.3]A-AGǟ4p`"B!wInjnn4itttJROO3g0`6}a4>-?RaO}}JH$'H ZZ`Т:G[>>>eee G)}vT<>{ږ.] [툉dOr\.OHH7bcc322ȈeT*J#""߿&BㄊA'Nvj׮]P(Yo DNNN_)%..yqqSAA)BG-KNrvvvvv>t1 &k`c AK? !@h gOOuʫ;)vYWWWSSӧSRR07@ ;@?O"Af`[t@ ޠ@ A30@ {f`@ @ !@4C XyA?;X+JX\]] IJ2???Hx VWWT*XdN!B} ( bCi;[(z{{WTT;22R"DFFj4ڑ\A8yD"!W9\YRmv0?&/M.\ ˖-/`y_"899F fg}P( Ebbba9Xp!-PǏWX1eXZYY9T ariô5uΝ;S[[QVŭX֭[xqMMRܵkQ^8?a f:ק>RF*fffrЎ0kð;w>|8$$j uŴvg#8XA˃M"^(׮]{޽qԩK.# @Yz?|'޳gϓ'O^zՖ{dgg+|DK.urrtR}}-[N81 8ӆikV8vˣwc>ۏa u:T**&\RUP "'ϗs3,NC u\*JV/ÇA466zyyqЎַ~ѣG111%Ԓ~pi)ռ3g 3.\P֨(Hs f/&8޽[PL6vrrr:u~ d2Yjj+jXzXA֚CCCC``T*MKKuiϞ=[(r:u*8#zkGG0 s̹|2ŶmۤRܹs9D05kֵkרA:::ȃ ;\yg 0 ꪪڲeK̛7\$11q߾}@d_`())!PͧBx7>_uI u\[zX,^fMvv6?~uToooWW!3Ȏ;ԩSE"[o_hZ Üp'*2Cdt:bf2\Z[[F#3AAA"OQO,Os\"\ @Sm5Wdql-GtFzE٬M2"ɞ>} [^F ÊzT]Q=#)??w]jPx,-[1{ 6$%%:_|'U$Գ+Iyy9(o[BҀQtvv\/EEEQQQn^^900FeOEVٳg…A xzzn۶ ۷{yy 1mڴ ڧNlQ`'O:88t2;;z2))C8;;:Auuu} j...gK,ƒX Ā\T*9DTcrEK;UE+neܸqlWM&M&SFF)zyyPzT*4Ǐ׬Ylbww|ٜ˿sǦÊyTI9rb6Ar5DqxILL;v~y/g9A˒g]A1cơC-Zaŋ;JkH=Q/2ߢ?䓬,̙3ghT՛6m^ ٶm[gggGGǶmBCCDD$|?L&F_/tEEEuvvn߾ٳ_-ui5*rC/Z*>>ð1cDGG,XkTTԘ1c0 3&L0Lwz8|b9|[o!Z 9q 6?oJ$pP,|g_G[qㆿ۷egggxxH$ 9:bl0NJ]H;Ӓ!=-- \(&K \.߹s'988xҤI+J===Ϝ9v a4>O}}JH$'8=zrpp)++"r%Ϻ[[[+.NG zzz@ pqqkkk#(;3L~\. T$ĪH[oE"ɓȫG0IqpuG(ӧmmm'O&/;;;;99P+9%%E"̙3Mlkk[tT*hX3-h* |@ @ `ޙ#JgG;X(?{zW^}uرMnxO˺N>@ aFb|hQ!s`~l a݅D 7@ ͨw/;SB30@ 61@10O`a?î_@ ?@ 70@Pߠ s`@ @ !F Ok eA \Tj D+WZR%Kt:ðO4i'kf: oooP]QQHDh8DhG irHKK P!v֯_x fffJҚT!B;(M;aw9|pHHiAV@ cϑA3ZDX ,77WP7.&&ɓ'@ܳgϤI_u GGG?>::… "7oެP(ƌ3nkҤIA988QV/_|ܸq˖-S"#@ٳ?n>VyYnn9s*Ç*1-- *::Z,޼ys}C]@8w\Lm۶4GGGJC /D]re…utt8::vtt`؈ԧ@ wss'&gϞDؿeHĉx؁*0L H$}E&}CE~!.03@B׾}V^-׬Y ǏǏ9D oؘi7+WLMMq#h8,i@ןSO_ n vؘa9q"F.|yng1 #`+vj,XVoݺ#Jn*H8DOPxނhVq|p -ڸ: €( - AE$q%''0c}ݺu`\.S]q[R3mɇ8BFhG2LhH\ptt@ z0gϞ]dBDx y^HRVKV9tp' O&Q*Je}}}@@}T*O?Dq뤤ymذҥKxJ___.0SQQH3$LIIyw?i@ffرԽ0kK7n|׊9,ׯ_YLF"N9Uzɯ=wSoN[2.***--8q")&%%utttgggR'.99>>U* r9>/K,ƒX Ā\T*9DTcj]\\xΝے K6o߾|ر3f$$$0~pww7͹LJ2--M<~x͚5@~zBBŒ3CBBn߾M狛ۑ#G,Kaa!mdCP<<}Rhd2x#66V*zzz9s**''ӧC itK}}JH$iii TLć 755V,wuuz ZV?z[oSVVĴ4p "B!w<(o.Jiùx񢗗Uq<%%E"̙3kmmmK.͹s888UTTpY\\TPP@ՙ#'bc AK?ܯA n"׷fBvZRD{ a:;º a!okүS͔o~#⮮ӧO`uDiӦرy@?£hoG30cK)%@]@<Ќ=quUv(=!@ [B@ @ !@4C 7h@ ao @8or|_94ҼC^=ztɴ(ڍa)v \Tj D+WZR%Kt:wBa0###%IddF@3WEEP(*B; 4퀓'OJ$r*BGǓ#Gʼn6y@PGh%@-V,yA`vkf>S( "11bpܹӴ-^zuD"_vM_.-hbevܹ?j...nŊ@\nŋkjjJ]8DO̔J555R433t\ׯOIIyArrr||::… "' 'NDGG: Z|q-[LVVkҤI30h"#@ٳ?nW_ꫯ6nxʕ}/DX 绹"++;0xYttX,y&34_ o`̙ Ë <AZZo1w\%ډB \{@$su8wƍ'L0a„M6={ð+W&%%ae˖UVQC"yhТرcnj3f̘c B6';hڅyF7.|yng1 #XZr%8SLR,,,D"O ^Rb@(Z, , jkkpwrr"-Ѳ rqɒ%---ؘi7L .uJuNsdkM|wBݻcѸ}ŋq&&&LѸcZ68@fhƶm2wYYYeeekq<==ܹsHCeа\,'垞 66d2j___g&111fsh̶l1؜&V8tD愶ZUUwrrҥKO:e2YfqwD$${fY,c777Ffp7778qwqq nnn6m#[ q9i:;sm'>=~ɡZn9Pٜmʩx׊l9aXWWWUUՖ-[]^3bRI;;hڬ [nHҭ[J$ի80tF02+,,СCz ,,#Z!@;jkkKNN aLuJKKV\NZ5&rrrO8A*R2 8PmZ-Ax.вqBF4ZXoo/A}}}sqH6$ZȆP'`D*vwwSJ___f̶l1La+u:Ә mOd̴uĄA\t K. ubLL̮]ZF\hRSSSBBBtt4=-@3VgϞMpu`f`~~~MFKAAA"ԧFQ(]]]HÇ hllAs\"`0IKhC`0Bȴ \A-ٚCGǟ;w=yǧl30jC4c-̣͒45cf`V3l+l2ӧAdYŋfNvp1iԀj???\TTxQQ%dF|り8!=-@3?mڴ ZCGdႼnϻU5?s;\ǜYy,;;tĉѡ R'.99>>1 ;uꔿ Fj@@@nn`S*` @%KKKFcIIX,*0Vv㐴"l͡IOO볲^;w\`0`ᑟo6sss===m8`6md¬4მۑ#G,Kaa!mG/ƍ#URLKKh4?^f V^^^999LǛL [.QA\󐐐m۶uvvvttl۶-44ðgϞ 3$޽{ZNۿL&cqikk̜9s&=-@3o4'L`2v= 50ڗ`poD"hll$-h6LA̝;y'"(<<|pWl-qݸqBE"4ԭe+%T'k֬),,$WݻvZ >|$ ϟ_&m޼n7_p׫@V BWT$--yaæk`iii t#66V*zzz9sIsssppIHˢ"B!w;bx^t lRyc hkk[t)hn#&&O?rdL ŵ4NNNk`c AK? <D aXM lիWϟ/H$IppkDhEh!˗|fe˖Yf1..`0M2s)((-((غu+)jdxB56mz_MۀfBEhGV_ M;AUUU!!!k֬!6WLKL&j5Aw܉X,6y@0PDVR C̦>!a?_F(!6};%===&&=::zΝ`)S={VgϺ8P&&ٍŋ_tIכHt:T**&\RUP "ԧJRfŋÇA466zyy|p2@bu\h9@jBo?z(&&ZBEk˗/)((H(Ο?_~!˛9s&8߿ RSSѧ Z68v4oJt444JҴ43:;; vpʒ宮H!Nݻw+iӦ?~׮]NNNSNoAkkkTTH$q34f?ɓ`ӧTLH:O0q׷44L3g:Z`ԊM;Sb //uF:k֬k׮uĆoMM X#bŊ[l!b+WD ly `(// bqB8=-@3 9s\|cPey y=x𠣣cԩ$ ln  ,++25SLR,,,D"gmmNNNOBpb0;  :.hlC`v\.wtt\dIKK 36fM&… oݺR\A-ٚs_>|wBݻcѸ}ŋq&&&LѸcA v:"##M&SVVnq<++}} ;8;wÃuH6-//Iyy'0-))1Lj b|?8ƢEQ{F§kPBpӧOxժUyV`b m ĥK:ud2}w} fG8`&f3AlAtwwرMp4D&B/E(mڴ *BO l'ƍӧO&hلٳNKJJV_̶}J%j,XVoݺ#Jn*H8DϰC0aϸlu֖ u֕rfOudkαy| RJ@d@qjj h4hF ҸknjVg`A]38F!mF6:qtt$Ol@[)gϞ~hO]凚~߿``@Tq [y8lNhҧL&&rBur%/]'A111vj&33̿"4N"GHMMM l"ͼZ={U l[U/_~w={6iҤz27F.TVV2/ DOHd0LVy.w! Fxֻ̎A(rGcFOsӭrΝ'O'e! b$+NM30hHOqnL&{)GwPjjj\]]]]]ɛzUZH$& ,F *.$GH8)BO O6l6t::"(RH$mp߾}ճp2;;tĉѡ R'.99>>bqiih,))Ŷ 77`0)J[ 4*0VG0ς͡;===ׯzwܹrJaG~~luss;plϷ? }6yd|MPPH$k`c AK? !#b9x`YYw, @ }}}+VP(;btv򳧧N{Wǎ)))<"#@ 3fD"~ޱ .رc333 f`QɋE:̧Wt:Z(h@ /qJ=AoF 7h@ ao @ ޠ@ A30@ {f`@ Ĉ&=`pxGoXyyR@,++DW\buuJK,t"'Ԓ?ݑ$22RV[(z{{WTTpD  (@Hɓ'% ኏%w$$$9r\-..NLL/VV V}Do4,/_%H ELL̽{M .z>|͢v.\*-[^.-իϗH$$88ڵkK>"|D FVf`Νɩ ㏁VVXu-^FTڵCZ'33S*HL[ 4q_>%%BEhG^N-w9|pHHꊧ%G VIMM-))?aݻwRSS h%@-* Z =Vի~ׯO872//oʕ555n ӟDn~Wx7|e955U&466GFfm l4 M@ggcr֭ӧƍ-Z48K(#wA.͛W^^n0 CyyyPPO ГLkE)55ё6BpΜ9/_VOCf֬Y׮]cڼ ;\yg 0 ꪪڲeK/K/eee\)SETCD"*BbaX|03@BWmm-NNN%4Z!0;q\.;::.Y3&i…n[i[R7ٔ:*}P({.DGGرh4n߾}d LѸc4Ba*?&)++ ,++}} ;rڵk_bLLLjjl&pOOO9wcccKJJL&ZEVyyyyyy===r|``<===z4w\Z; όBKڼy߯5Ν;iLf[GjL{ٳg;CjggD͛@...BmӦM fwwf% $ -tDwƍӧUV[(,]ԩS&ロ5kGdsJ%-hV`z-TuVD!B}rX30m\aaa-[Cv֖ u֕rfOuߒd(瑜gg'NT*nLFvCGFA30*lpVмQ+@&VkuKD__IJJ7oކ .]DRjh$~#O2 6w*}T~<{@ H$& FyɃOJ2Ԍ)N6v@g`'Oߣ230+OgggN8:::t:AA%''G"'[s ++_߹sʕ+ af977f9??yX1+vRXXHLPK;*11qر~͛/^D//VTi4ǏYdas1cơC-Zaŋ; 6LǛL j7FEEA{qo_mɐ<<B#66btvvڵ ,ohuA럿'dee7-33s̙I${jZN~LFA icsXduuuV͌F L&ݻIqܹ_~egg'7==תOhq 6w?oJ$Mh6LPKtvvD𮮮Ax 0u ۷osD 9:bdxɤY2] 5k{]v-A>$'Ӝd͛7 t_94jAT__R$ I|UnˡcG}뭷|||ʀuT\gj[I|agwyAPo&((H$999/ρshp===`A Łx]jݘcJpN'Jd*//WT`U(z z7mC 8^Nem$\T*Zm6/^-hY}<4oGbbbHK+>L<={6)ˋDyyytWvss'Wڵka{g/aNGUmnZ+**JJ233WRRR`Jj#G5gΜ!C,^R93tK.qĖlaPPPvv6>+++88C;J555G(=;~׳nqL  ,..nܹtBa>}rrrHRLJJjwBlsHd2B&ɓ6!Pc! =<<kk쎄BBpss1cFaa!7v ĉ333ɽt S5::gΜD7oޤ$SLY~^_nݴiӨ.bbb77WF4l-@&beLOttt||`&hRRR|}}WP(.]d2=ztRR8}իWFJM(nذرc~~~p̙0 AAApԩ|/[bdddRX>Nphj4TDBJMMlrJ~MBqe@Jo[TTT*9,WQVTTԖ-[܎\zZ!%SL3 """mۦjU*Ull,nKPTT?uj>{ׯ_'zxx·~СC̙3Chn ///H4hР˗T*X,&oa4%-FiŊ .7o ,99YTR r:..nffff``L&/R)k$XM0aZvΝ&Lc؎JKKWZ5vXc/^^BЧIѐh힞=%dT555tjJ # 7zqgiDh4!L^y\2""O>9{,%dUUUt5PѳQBJ(NZltSN(㵆|lVU{Æ c!***,,lʕG!m}}}/_^=zRYYIJ͸:Vm}Ν;'NH$۱c/vse˖͚5 !4wO?C5y[ ]+0Bb_lٔ)SCqW`Qt1bx)KJJػz=}T$N#7B"Vر5dV=X!`CD"{T؅,lIQƍŁVFo-lEmiRVk&J*Zk~_y\ήr'OxV`~>>>O6j~R0b6B:kBT{}MKKcd2oٲeĉƍnݺ5 }JJJHT*U16J_ZNJF? i7]{&KCj߅%q>)ƪjy?>>~޽ڵ+%\reyyZ&/`PrPnnUy!yhh;t:]BBRl l^WCJ"uӧO1`={Ƥp4#V{az>==}O09sM&SEEpU, t:7|S2{Dfb/--ݸq ;vڵk+**׮];n8[QL9;;yH( K}]RI߿OiFzsۼ"**J,GEEQWzuVرrJHHkHHիW9 9:b )a勵gِk$ ,HJJ6nݺh"Ѓ'ٞ rssCBBr+ v.lJ&6Mҝ;w¤R)I|Mn͆S'7x`WWRXPP0f̘={r_(//9sL&=w'YMڻwB0䩩 b˖-֬u dݺu2]rrΝ; BWW`ޭ[ҟAi#ٳTʝ;wF!FqRtݺu={&FyĉHX1{l9: p!OOOOOݻwך95"_PiioM6s`{N뼼.\H~`0| BP,[#u ۝EMM , 6C5adڵkWJJǟ/6Z'O\vٳg/@#PQ^FI=zۧ=w/w+++>_@"Ri||&J$>o_ C \Mah 0-x4ʼnN @@@@@@@n;utoK 8ݻ7ñ Ff ,55UTJ$1cdee”`X~yR&Hf̘V9.QaTUUEGGKhJU?#H-V+[†J&)>!dٲe~-S' =&&_p=|0ñz /^8zhT*Jnjs%[B]C@#zew}7''Gn߾}Сp…:.%%O>p;wh4;w/8?cVg3R(b؎j6ׯ;vĖ)!`0=:==!tƍhT' 4U΄:& Gl0z_xp3xa8)6l>}zYYYYYٔ)Sbcc]\\QاOGjZV{Q///[B޹,숚eZ*0 Z-FnD"VjBlsz/ ߿?dȐ+!gcn4O>-%lKJJFYTT4}tJk&v),,;wܸq_GGFFDѣG?|=!!a䲿{nhh\._z50qcd6s޽pLfz}{P(ܵk'yxG_P(S(>>>iiiC! lyfww}?~|ӦM?PRR2ydXxe?V?H=|pLL igr<44ql/XO5FVzuss#g F3gΐ!C/^СC/]d#[eggﳲBsB+V;w.%c FDDt:NiKЖK؉c lI"hΝfX\\='''(ʤ$V{nX!6 H$"L&zGM!gc! =<<(MH(* 773f}c`0L8133K70_֐ę3gD"͛7)ɔ)S֯_׭[7m42r3::zƍz~x`Ddk"VDGG 8d2 6,##Nqqq&%%׷}5:BpÆ 555ǎPT]]8=;w.44d2QFAf[Pѳ.1VF5qssز۪488XT>}C.JE_>^65;, jlcb)rlGlz!DWΞ=+ Ϟ=z OiӦjJqF ~rdX-[6e[BaaÆ҅5?W%&&wz:u}a+d7}.w!kXbNjf]B;"t"=*BL״5dܸq#22800077ڪ h-M*j̈́ZBgNLL*ZkflUWuO< Uvvy@<*Zm6kc+Ԗ2xԮ޽{3O B{ {cBTj0z +l]HH';{XFo߾iiiFQVS5?UY{}Z?|J[nV۶m嶄X? )--ݸqm lz}N ͛rq O0y߿T*4iaOHojސ+**bqTTTeee]>C cuʕWWאWrxrtRaNK6_;d,X ))ܺuEBv/B&* Bl2̙3+W_}5sL[jΝ;&Md2lҤIw޵%_흌… OVa6ɞ;w5?u L@V`ywo]طaB9׃ɴk׮Ǐ?o_ť ToRdoo˿5ÇA84TJzOxY x)WSn!]PWxxE[М/s47P47P47P47P47P47P o\j4^L ϗW,...8Z*TR)HƌE SRRbqxxIaVVVXXD"1cZbc;OUUUttT*VTum^W!--- @$qxb;ruvQ*Rɓe˖}frrrLLL]fVi$_D'9{}'N ?~|Mva޽{7ߔJK.4ϝ;7ydT>}۷oSVĉ:7̱ۗyѣGd2*\GTKvر۷3'ɹ .3g)\xiӲJM8؎qFL-6nXu,Y_Zjҥb؎( {ذqƍ={;5@M>^ A7oLIIYzu]|fVV{~W헵.[lƍ!ŲaÆO>:.ߺu~ڵ+ s̜0a[oE튏wttl"_<~g}dɒl*b(a5g ajL&K CjjjXX)Z-BHp9:⃿B2dH]>C KOO7Oc?J#GM>Ěj& Ν;7nܸRR)FC ޽*W^]k4V0qcdZ޽{2l͚5/49r$00P$߿7ՕP(S(>>>iiidb800ʕ+|ݯթP(0`ɓ' ˇ1Y͛{M6yxxx{{Oï7gaE"ϝ;G SSS &B=}4<<<99a'O>|}fϞMJ"""D"QDDD^^իܨUvZFwKXӑz=}c[֊~:XRe׮]}9z(mأ5jԜ9s xbRHСC/][UfDC i|ٙpܼuggAׯ_V`qqqsΥ;! 铓CJJeRRVݽ{X,b'"d2!L&rrr<<}իF#& 7lPSSs1???R8s R8uT>I헭oX222m)a,[U@845MjjD"![R`@l l6_r_~B|2y -**R*GiիWb+**j˖-dnGEE ^ZVĐ)S zT`۶mj**66qj`% (**BUWW:u`J8F=\(^~A ~C 3g :90RiBBB~RoI;J49ӬXrrR05游Ǔ2/JBls7 &޽[ܹs„ r QiiUƎvŋ9{,%dUUUt5PBf„rssP.͝~N "Qki=Z5>l0FV\y/wѣG!!!6BPVu0tܹ3qDDү_;v0b7X,>\l٬YBsO9]3=;b!WGCr;LXUVnA3ġik8W%&&wzD:u*22ќZy.w!!bV4zNDQae5kƍŁVFo-lEmiR}+0P~~~bb믿>oGĀc4 Ϗ݅V~c]X^a[mjdڵkWJJǟ/6Z'O\vٳg/@ q&zO_G''{{{_VVV>|/$¡E H MȃH$}޾ YQ^(֠^J 2pixV7M 9'f8K27@s@sü ds'jsaV`V6 &YA԰YhZY0Zx߸?j०zh߻C_(+U -SX6Ϯ((ѥӈ~]:v,,ӝVj4[m ͱBtUر O &kbR;Xor:гC;gjBvE-oBlGH)O>` ;K};OnrY't.Uo=&7DmoECO6 U}ƴ~jh,#%z=6?،1Իs-DkVق.]:4[ѓJŜj#K &ұ%G6!^z [q׬r{JM\vZg#j[[-Lw>Zk̉9CvNAV/T<*c#r~Jk+i9_!oOF[QAP}@G֯knWbcݡtJIgG~*Hx};L/ѿb-oB[a|+հa'k-|$>gk&'<*JkZ^=ZRҞ#?D|0VJ!62 UA_6_c3vdyŚWJb_r*3T)%v[}}sϏzbAgp6֦CR# "t$o۝*nNF rGBo' |$^mE`9//!*r`?`#rR3KZ8QXS5keXS3KB;rXiמ-V+BZ9L.-u'?+/k7scV^k0=e~dGR/`34l&o0-e~$CCR幼z [p}DmHaX4FJeA^!~wHٽC?a?#~{vnf@+B-GJZ8Yz;̵}k+B'?bs;{UzEg[֣-C?VB@z]=ݼr1evic&Dc0__eN ϴa.ڷuvUKJ4Eg\j;@+B*MsL;i: a^dZ ]yɴa.=;$~ߏ-8kvl·Fs`e5ޥA茖S׶ m!6%Iۖ* !T5i OqxKUՒ@vzH![[Cv4?R"e[Zkv*֚9?)|,A(;}rOс [ I2#x9&^^{ oOW۴Wt_MDOG. 'J  B}:V,<_ ޝמ~+]9Nѳꖎv?o!b[:N{)K=h*c?iUvv6AU-"v9yi]mޮCڈr0qV\.-KU=RnSq0q`/&HS<ԽÝ5ds"-4zKŜ ɚ.cu#Zk"6zLc-t":!r9 P+ڕ^@_H|:.>[kg~]v.5׷Qx)\ڻ='a>]?_DKG :OTi+6%ie*̧va>?[#h~kvK>nj;vz.*فna&Ojs[' un@Jzvr>{ɂ~[^r׬r_tlr ߻KuforѰٚ+le\:yXVsҍ g kVG{rzb=rA:FܧӦ$vj Ӭ@@k1Q<ܧ(-o2c:`c[nl=u%,jWmĭ{K+#2 ydY̪ٷWn)d#q!_ыlIͿj%l#ؒCm!CAPYcI3׵y`{ˎU-Zh̻ü:Xo9`#V_n=]6+vؽDmDmG rk[NׇO5e=k4 &'D٬ ` Kڿ3hp,@7zqgO&d%kV@ ׆'8fH= D b0ZzO1xIDAT=ֳ#i>*!2O׶.~\~\n?/KxlTe`OW Kp3=_֞'6ͫ46-VZ*#~C`_>9gOR0$bAjapi.j;u |R}dy thv+o.}hF{:`CW`Qcd.'k&yBt?etlNX)MFv!bE;TMoBvۜ&pSt`ƪ#nw3_#iWYc$5baihXG?vo2L '1x\5R<_tb-(bv_OuM2^2rLJ&;Ӹ3e.V rcޅlj;8k/F~1E)Ouww[kB;.ԧˑQX#ۗ3iF vF)B]R.3bmʡJcKm{[t]2r*T[8 qXRNo[N.VzuNRLһTMrقNx:kC9wtnZSVe>aa#iمj-(:>*!*jV"Hc%հ cdždvnT20䌜qv"гNQMX|݅|D3Os_&m!]u7rsFI~H}aХ"=֚2Wq3_EcX!d^.ֹy]zձ+ؽLmH\Ljb 9Bm _]cNZs0j`yOUz jxiOzv*mҍt@UY\ 9A|N YGO\-ܧ;l X LGE-[k`BPQ;gNJ\3'|B F|{=>xݭFoN$*WT~ ˴7TYq*ԧ =ci\S(ݿGn-pZIvA-͛T#wޡl_9;Ap$'&e-'vA2SUcG(󎟸9yݯA\&%YCoyiMkQdE臋Ev pDDanjGV8"ˆ擮ݩ.q0&IA=kVxfqĀs#e-쉿85ɘݗ^@ؘ# b¼Š.0M 4:v}sx5 ֽ'D6J#|<#aoNNM~[3߭?|_~Iw/)_LBm< :v]pe[8@J-w!Ył<99yڴi-Z7n3\ E> b1fzuщszO 4?p W 2AQQQz~֬Y~mXXX-J6qqqy.uLjz{{nݺu~ee?k׮Y, 41T`JR"3&+oIII ϟ'YYYaaadƌjCmN?Jz *::Z*FGGT_Bl\C*--- @$qxb;ruvF9LѤ ׵ ` lag yxVu֬YbJHHؿbb [QKK||<}嶧Y=z{;~ѣe2ɓƍH$nAAAÝ7}{LLTf+j;}1cƼ0===99977w…s!/6mZvvRܴiۜ 7nٳgرƍe2YvvL&۸qc=, Zdɗ_~jժKrxڊU_`Æa)$&&7> 5lN_/8fѣG#FXdIUUٳKJJBCC###ckEIII?f>\.j ?%KdggS vZh۷/_o7-[_xŋ[nmMb9mA jLF ԰0rS$iZV b9h|2cXӧOsx1FGB1lرƚIbZ r ni tqq+ 0ɓpܸq3%%%'OŁW\an" 7o޷oǏoڴ~"߿*W^]k_@u]/ 555ABp̘1~~~d/, kG D , >xf;Q?{ᘘ|1  o{QV#T)~eggﳲnx=;~׳nqL |*sRѡO>999DT&%%iݻwb!`8qbff&٤D& !d25Pc! =<<(MH(* 773f}cV٦kR\x]~L 111eeetŒOnN>}FR9s CzzzPP vPaF*H6lPSSJ2uԍ7׿@zag uZ/&dֽ{':uѣ:t(//l׮]JJNoMP\td2=zhҥ'>|ܛ9{,) X,VUTbհ`488XT>}@ƹsBCCM&U>}Hґ#G2-#b866JVoݺ5""ڥϟ?b8@ !+CU`%%%]kN#_ ,.$bE+XLj)ͺޅdwDD"{Tk0e&Jrwwp FQVSr98+˟n܅|%*5_ؖ-[?0C?.[?Vsz>ӏ?X~ǟ~i=, 6lط~v-V+J~z[E\#lͤCbR(j?vM>}ڵ[fZpaUUUQQ]ZM2eƍaÆ /щ~$Y$~ 6M!4ZzZKӥ.Z(!!!'':ᣏ>*,,4L;u18###44t„ vj4Ry̙G1K7ZG׏Q! !׿qFJE^5jB JIIiD7l0cƌ򲲲iӦ6qyAo6LFѠu:NVTUUee>y߿T*4i)MAoHa&GEEETTX,:a+X]r%$$5$$իb{ذ߳MIsĈ_8Xf\.gOB{uwwW(Ԯ1cٓϜ9S&9rIؽ{^'針5lN_py!} <500~J0a*u…v"n߾}ɒ%qї@͛Gm۶[Wi4E) D2qD90! `0| BP,[ ¨F^iuuuiIɓE>*xX[ , 6C8fҥK̙SoerPd|t}{?Ç|'>\\\d2СCQ߅_uU;pۓUUU.\4E,G4?c+++ /g@ kO?az&P4;;m_=tm=dȐ؍~߄m90bgg֭CƏ Z~xcgg7uZ~zNWbl9T`u˗jU˾}T``w;aZ{{?322wrf3V!T###{kRSSJD"3fLVV)LII ϟ'YYYaaadƌjCmB J*vXD4o vQ*RhbGol5hR`Qqqqo5k֣Gl5?wɓRo߾Mo^-Μ9ӻwo 9Ə>=&&d2թ9w//{6 ;A{>soj €Kmi͛ozk|bŽ$&ڹo׮ 3Iѩs]H T` 2Q.ԁ0yMQn@:\,4V q㌌ }99h~ cG6б}>o/x)*~rN{9###c 'cY]򈑑2!dX#+RkTm@v99 Qϟ{cFYtuX,|EÂڴ_` BA?oGxH={5WF0+0`yB_C+oj]&F!DR=?׌_x 0O3*ЭժY-,k`@]9t QoNX@#A/~׭_?gm"U` q?nݨ( i13oM"+B*mo?F^o>ry_oDE#B:QTXbo8L 674 ^k9믷 :uJS&` wE$=*!1wEBS.AUǧN:~ty#R]H 29A7]۟#> *mo?64⧪**jR!">tpK7-Z<YH(G#ӆ|Ƃ^\p{ÆD_WvC.HZ0>{!> s> @cA6Q""ֿyFBfֹsN:U:],$,$od-//ܹKcȬ|H,ܣc[ߎh:@V b1ǭBCGf%! K۷koe4˿=oxͱW 8:8(o&XNvAC4n,/^45lFoҜBU* <ůysҺe-7y BUUI=lӏ:ԡ•**k^vwD4fwڴv6VD aN.j.>{u O[7=ٛO߫n8 &]K'{Ui{7UĖ@ƴXN?Oj`vrkdNB%[:t75F߾mr4&@BK'^ v*tO*-:stJ\5R˃Uڷ D@aE9;ZJ~"Tc⟰k`u qu4zŊl  `1&lj vmFzb%Ld0[ &kjcG; jfb0Y fF ܅nmj4Z@ XH&3Y~Y`50:GJmN5`&wu&L ۶rz_ xwkmZFg&DD\3k g'nZ{Xk*0:pm5;HޭM[gGg' AvΎn=޹_UkF9Bӵ+EaV;RV^o[@pt۾3qBluu#~M޵Րө}?I,p 8pMy3 2xh4[ prճ :q7 wTkYuܥ10XV^ GkQ0dzz -Ǯ^AW JPZhtuQ8^GƒGv춛4M$94=wc8h3+v:3{>7MBqEډyu/0Z>}t%վ{}ZRXO 펢gIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-list-info-area.png000066400000000000000000000117331351617527000250220ustar00rootroot00000000000000PNG  IHDR%&4sRGBbKGD pHYs  tIMEu+[IDATx{\, bD. WZI/cr9$x*B!BHۘ1 #nB.Q[UT""P {N|N!DxdMoBBp-B h!KE÷![B, "8jy*"a` ŢJyβX-B!PRB * WT m@,I;X,Be%%<UU9]H,iNA1tP0T0m^.+#GHxx@b)fAhBݻTzpuh4)g0 ݺAUH xyP+++Fb޽;B d٢~" #Lm4!bYTT\ѣG0bHC$YL)XoPPVv ]e28ww˲e ϣF^O*joʡR0@b)qr9 =<&,ㅅ=CC-Oʃ7rB-0ի71arTب1 ct; 8vQ[)Hpyb!XGtwvCDj DOdхdNU/"7N*P6J)"Oy81ye.e}=RV,Q]"'J7^c(ep!O?3gδJix?c@D}z#(~ph45`ʔ8xy cƌECf_BpquCXٓ%@TUU'O@'iDUXb94Y i!-[;vPAgRUr7L: m󕪨@EEJwb&s?V 8NN[iqtⷰ}vq>>$DH!Cp%ƟyN0mg9cU99.~6ҼeYѽIYuU%*o\GFF:/^}Eoa1(XP 0q(//Ux^hGkHJZXt)pP{{ e-D\[C* c{aҥ:7ܹs48}$  .aȐXʈUu55j#֭[,ӿØI&ARaڴiAMM <==RRsչX,ܹsak2e%$$ ::uuu:eg}5556lt `{ /KRATbΝ7ouѷLxא:>|'OX" B]S5J/`ԨQxs"ڳ۵^ g?C Kbh40׾ygʚ\9ԩSd6X[[#אA;O+d;&La+A 233q6oތ3f4kXD"R{4ϲ&&=xδgΜ-[`ذ`Y.5իW yyyX|9oߎm2;l, hnw_gڳOvFDdee#;3kYr%q([{d!##6DDz,:w ezi %"=#CgÑٯ]GkUSP*y&tcek,^DGG!-v9 X`!N*JBYy9$&jGFF"=#J?l18 BP`ӦǷ%#V2331{+uQQQ8s4nݪ?O(Jdgg#>>A;$*>>P*<A|E76Y^TT$>p3iStw5رs'$% `ƌbvuQbI0}  δw׬Fξ}Z!RSqmbq34408Tփ8(wpDᥗ_šk`|:{Vxn:ͽY'~O7n,\&Λ=1q_#Xv$'#)) vUSa#GB. 21֑Q\\`]:ba=zNI8t]pa@̞=kɓϯ?57)ׯbbbٳ_EZZZ%!Bc/~(/;M浻~&,,XI ac۷_7:ﭺ:mڈyo$ g5ʫP(E/= ^|ק;0w۫J%>¬ٯ$;[#"[MrF4ƜHI#|};p{qXN A``N?pptX,i)U7pBTjMP,Rj !IJ(J:J,??MhhLr' ;{{A&M'|<0 xwo1PnߺׯBXYK '%tB!BF4 4j58qyJRZ$ ^^^KI ()!B!IV#4|$ļ\)=}XIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-open.png000066400000000000000000001623141351617527000231530ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx%PI ${(R^ł*p^ &^ڍ ("HB!63?e[*g9gsvgAa4t1zh"B#; LۦoǠUVTRPPi=:u$0 !.8&g !E!EB(z0 :ƉaϞ=rrrBCCfspplt:$6Eb0T9y&S|& ouo-Kc4 CfV,s*PEBK%Y`F0 !A* EQ$ࠠ v[吐EQE1 Q}/%It:$ɲ{Fd2U\YVAAAfnf!DCrI"IV^tvYr)(`B?0MFyȃFOO[*~Q?ǯJZ_r9*W(bQmj f`E5;t:gΜp8NeД`0fyj.Oͦ.2 !\"Ɋ(NIqج~l-+isH>w!geWU@[RBX*<1EHHl^i۬˩&sC[E5 +999yyy/\m3f8ydժUukYJk^ZnBȲeYR39~TBQ:/9kEiwhs}8-p€5El弿 [~#zٴG r-tmRAva6QNI{zj}Э[׭LMS˴e9N9%-+SSFDDTR%$$dt6 !T믳Ν;׸qcZ|V,pISRdYq$ӞpWFSp$Ӟ17+)]&+BV{nV]Zk3N97O u&)Bπwػ{zrm[ǿ>;\ZZ` _LM=T;fSU׿Vkvv*VM6=zV*<OKNrve"$Y%;פl]Ǹ΍ëY9?.+bxX]ruH{\6ܲn c⹼YVdEQEmU"+"ɰun9%xE&1{4Q`0erKS16b4SҩVBV!VH=q.ϻBthTs+9] _:x<[F_ )6\"ӹ}ZEl;oodt:%EZׯ~o})Gn޴Z9@?,ɊpmAHԮQfs86EZԩ|0)s*wu+i 2;6q__gs T8a.Ok~$Wpfu:6|,_xcBT 3e_=.I=ǣ76 [Emj'ӝdԤ$%fwVZC [q!.|WkԺ֞KN] eI\F:ұCIK^F >Er)VwjvM+ʝ{J=r溜],EdEr9,Cr{,I"K.ZMY.jбӗb1#,.YQs=:I˶j%|{HIr9 GeEr9/d8KNVNg%>v{&ʊr.P֪6[zUC,fWgxcB U5eE"[uD ӱtN{Id DjC٬F WBBGO|&M rڻ4l4|޺uwfOOTBL&.z_.q9իG;wE$IJx%UV֭uiW\\c`YYHYrW%̅FM8#ˊ0 !ul~dIr9H+ݺ!ZP?.YsjUkR7i]|X֭`GOPE(G3<1 !.ZmfU5e{~{ j(pmBIpt6[փP!{-jB~W ˒0͕+WX,sWF's2233Bh.L}7өfEOT"᳕G:U`oY !ԧzRg`+z,K86툗CZQY\NǤk[קР~_36y'NW?4ݻRYVd9(,KNҗ.eYܹlբADRjhGJjӤԴԴ?GnԥENi+rr:W ys0ET U6"g[zٴ6q$It/p:UV eﱴ\!%Q-T\Nኋ O9>]$8}Au`pNT+ҹ Yja";U+Wn !bv$IBQ UވZw L=ac !a$I5hw'%Ftج999aaaFtZ,ɤ^(ꫦRd2ܩ7Km޼٣5.K˥Jj߼yփSGng!NG".Y%rm)Q\+f+RZF[˿>c?9 vh`]=f6 j;tp8 6w>oyPv_oMMԬ1:j汇;h S'965q:l4P$#+ەosk}\zt-MF !.'`WQӳr~uv$ӷuml1)Vۥӳr[6^bw6$`g!DF#lӨfٴ i-mMF:xɥl^ͭ#[E{vXѵU:ƽ8b/'hr4l޶ILQeɕ}1lwiwkk2$0հÂW)ɲ( kNN% ' (G΅V7dI? ptĜJ-!Az5POYp9.Seh6իAJoP4v|b$jߺ7x*Ilv,˩h{~۽)/bWȲ,K.=->k馉: >۶a܋[6Ȳ|BՆZNEɒ ;^̻k;Q*ȊrԸsʕ,|gR\"\N)};ԱQ^FUK5/eWg^mGΜm$zϗ.n15^y|d|瓭%Ary:&Ԯ5ֿ[uʷ;L>jDJC=7}tcmS2;W7崻ĥ6K[ѩEw+5 )QLV5J{;-\NCbe?>~p+W6{/?8ժU U~nڵ+pM{!ieb)w~>imy.C=ы`0 KˮcZ^卣g{`uvyy___H)G=}`h4L|u֢lԖf^H%.<~BJd$HP0H"jF 38z3{<'5 @osf+xrjϨ@!XZԒҍ0(zvLnٿ!گQ)1Νu׹ZҺ0v_YñtٲgOa¬J:Zѵ=JΟ?}ש[g^{-I>W>][}suk/ZCѵcF'N(ONN4xHz)`˞z֩۩s˗c>ZѵOAF7;fP>{>tu\_ow}l;OsE (*Ν=];F+M6FDD@(NsO5j=2aܷz>dG0MѲ:u:sGQT /88#ϛ/O?)J<3l#m[7<;^¬W^ +ڣkǘL&M̙=cOw/Z(881cw);v5v\ފJ*]D+_%BYI=K@10荳z$g֢e2u`zs @ZbHO+HJ]ZDDT pR*W_}iӦM6}WQAv3f̞=drҪWq経PJkhBٳȲ,ey9*b Z [WB"WB7M<ɻ<|7|cB&M~I3^gZ%&&nݺ%yQӧOׯ__ѠAӧO={(p>#8u:گڷY3ЄSN={={δS]pE>Bg'sݢpH-5~bY!F AF\jC^n]]7o޽[kswgB—/_"l~Wk|VP4k|&tkP '"2*333999!!a0ׁ܎WaL&ϟTÇ?wمQTu9y"99N:Ũjժծ] $IB^ӧ/..͙3x?4ՔW|ͷSg}iSNLNr<|iy?'OO ~  a~!u=^7xBWfG=XDdcGo߾ܹs#M7iX}om@VQcEv ,^JniMJMM}1E]bȐ!sεZoQAq=7i=N3)9gQccc׮]gZyi6Fztꔩ6^:SFCSl3-Zp/t8aaacoh;hW^yo-/MW%'"MQJQa>5IJO0|l}q |.u8wZxr!H++Z3Y3ܼz3g?ޫwo9s.^تu; 0p/:uYz?sڴij`\۰aw<'L[S-mڴɓ'jl{~ǰ:~z[o})>mڶ w{]N}$nOG^Fd+xP0QUL IDATXV@ >g wzX ?[j'"2A&O3ɿ?| xv,"!5 e u`?:Z8YQB(<5YH @o@oЛ{A0=T_%](`PRKһ4d`*-H]Dqvl`0L>zѫW6mZ5j֬f¹s ܬY;w^{-<S>`NNμyFUNĉKA|[v/{OD8 =5 !!!$fqPP,Bʕ+iB빐}㙙 ӧOp] BL&Z^(Jjj(5a*ܰʥn ׿2rrp-Z۷ooݻM{o܍@EqiAuǑW.d7c`z#F720g`ܑ@~`)"@94~wa{$@)n(-"qv%<>8q,k= kbd`20qO|q @od`zHʕ+7onXڷoyfpժUZ ޽իW7nl6ŭ^Z-߿HHH333\|.j7ZڶmkK9ߜ}~*gϞ=z9R+B}YQ3338+FX20f۶mQ3''RJmۮ_n]}jaÆ ;(ѣG5jJ1ܹ_Qh6Ekܸq+e9..n޽ (jUQ30۩*r9={lǎO:5x`|_C8tGiӦ=V^]QzufPPPpX,>W$ZjXXСC읁y 0aBhhhfvڥjw{Ht˖-rb)-fgW6I}tkEyޜ?am67?K>YS_>3tBQg}r/_ro>7nN:vRYZrĉ-[>Eǣ$I5:رcG.YwމLLL^+5jT1eE O>S T ys3f?=z!RRR>YS*10U~~~ppt޼y͛7?sVbXV(yyyZ͢^j X9IުQFڡK﮾.]^ifffxxxzz0 La]y}۩*9kOX|z ;?… ٳfj׮Z/\p˖-kj-]4???S 2w\[o <8@f̘Ѿ}"EjСV5>>UVZ5EQ~I&=3W|֨QHaJn6CϮTxo>G&PYx}ºP}лO1rE>r뭷=zT-Knn(;vhٲdjٲvJ8--[nX,r+.B~4hbiݺ?4 u}=h(J6nX ݢmZx>_۩ T,|s T{3<>Ad?7ZG {IDQB(e/333O.*Yd`zfJF720U;\8720 @od`z#F720 @od`z#F720\`9川{pStGEz?>]~4!C_쐹w,qbCZB川S+!:R{3[ԟ|DdxzȤͺu ͅϯtzoľ5NF!8˽mo=ns{ə-.|ѽə->81ĺ/7 hUν!}c~ 㽩TzW{<ٿ?B\='ڴwm]K;|`|IurfRϕ^/P;׳%p-ꃒ#|R~:BD|R.utm!DZ=F=5N޻7~寲G./'ϫT[NSR!tޱϻǹMuIZjǠ~0jρ'0MQ~@ ^10 @od`z#[2O>u7B`0fsv~'нS!DFFFCBBkץ"d`W~饗,XaÆm۶iE5jԨQ ۫ڴiӌME"+B6s tM%..>p_:MӧOׯ__ѠAӧO> to0L&S-,YR*\NJp m۶?E\CS-Vڅ ϟhRN'O !ԩS*"d`&M3fΝ;Ngbb⣏>N3uA<iȐ!sεZo;@T {:uѣ Էo}ԘgΟ?ߣԩSիӦM+NSѮ9rȑ#= EYf̈́]EDD|wEZ;{ @od`z#@YX-eǿbib_L>yLmQB%=n"ʛU >; 歚B= !fSGx!vD+I_^ӔƱg;ݯsWƋ_Oo{Ks/ASC7띻J?l28tuwzGb?T [_ީqW;8Kl#Z}nĉӔB68hڏ권U_Y QexU|x*Y}6|jʚI__<"Xh!DȁM"՞)֯26T3Ÿ~{MYeH}|iԹԧjW Q.^V_G!qZ !8ku)B_4oSG6w~ꄸEK.c3:b% V~ϲCI{[Ԛ!6.}/AS1|PշX C}z"+5^;mcIiG_) 4:BlRSqN{ݖsB%N Bc !k=PG/u~3=-RDY-^_F)cn{sCqS #WPFYV}WSEj[qycwڢ՟>ZaŴ7QZ%/[Fro?&!ey̧yL6.ސ>'F_}Mgwm!ϭϩsC[3HȢ՟^CֽFҫx+/w߇F720-OZnܸq?P-4 l6k~ z>iӦto0L&S-,YRpm[2m ^}EWj.\%8|xߦ,IƌsNә裏<`SL:uРA%:Q {:uѣ Էo}ԘgΟ?ߣv+ In4Elȑ#G(yS5k&$$[QF720|[ԏ9=!Jy4~e=CŤf2xWFh,_%n]*eFB @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720 @od`z#F720]%X9 <3KLnzܣ۶3b)x e럚Xy30}t:0ִ<q@(''9 p@(''9 @D%%IBQ:r`0&ӕm(~8r@yFkt>CeK9b4UּEz  &xO,Oze`p8N": 1(((88ɒt߯_ L&SYGT^8ΓI۷m3MB0Qcb&S9$u㺡Sf?x|=%I>+]"TFV-[7mL/_+.I:/߰a\YR5ndڵcGZL &TxLlа21L5kڵ{vu#s:o6`:A !>NzwHNgR҉l]+p GQ223kPqSut:]&*EĚhkDS'n."*^zQ鑁w2tXNtX;EQ_5LƍwpٌYM&Wằk=o*Ƨl$YQdqcLTQ|bpknzk՗c~]7*4=20Eϝ;[fMc?bIOO3)p}*۝;{Fu;voc0*W&N\:0I"VktҭRPl2q] !D5:M&v>vLWǡC{4xHNv.rߍBB5cbӡsg- +""mrQ>;|cFF6l(dpLTQ|bq}S姟~ i^Ct&uuB‹/… hE8UVt}w-{ٵܝEVY߻t(I=|Ȉ`WϞ+V|9Z`a"j !RRR ZV6Bٳ4l׬yuiyggѽbiؠIɷ ];fA'Ohޕ8"2jΜ7orڵf͎kܤY|VQXl67kl׮]fy޼y?fR1yb1b…&d2-\pĈ%@[ܧO`mr{QR={&%%fy111 jl6k 믵EYbE-BBBK487W^9997nȸ[ƍt:os=>U:Nݾlٲ &xȣ⩧ZhQNNΖ-[~b_Jqb2qY{'?6x_rI}փ{fj;vڰU6Za!3{WRJLL?O>Kέ[R{/rN yŝw}0g?u84 У(>]j1pxl%_Edd!C Ck^|N96r'gU0-vZڵkW~`$d2l6+@!B8N[+WQh7jZbŇ~p=)rn4V5>>~رY{QFx{ azV%}åNJhڃѣG/X[9ϘzRR7|3eʔ>5+ 🀢(bܹӇKJ3ϬXrڵo.z}69s槟Xh5%W؛:017®].Zp,\H+[6=+++55'ǔrE%?_7v{vv+$Cz{Ż=ܤݻ8Τ䧟yF-ѣE,>Mv:;F'77'>>'ٳ}{sssҽH7+W;vzpq+W޽{<={xl6;ի։{JZHaav}ڴi?3Fiѯ>o)҄қX/d]kE>xZN?eڵMO4jԣSL-#f`SCi.ZV7f$|e\\㬬,D9s.^تu; 0p޴[Q\{R("˲,G~nɲl7<7fѰa6mmڴY;Ns|ΡY3gر#NW ?׼v/٥KWkv4kּZpQ Cz1/μXiM,2v=.ujWܹh{hly~ǰ:~ZA|[cw|+gބNSo<*p:Z=z8N0tQ5?['\?v/{ޔw1׽033K˄իkw VR0QEU5Me܄g3kYx˯Ⳳf>W`W_7n"LNNNHH>}(=#" CF҈)*dnܤY{Y% B!$ ={O[ܙ={ rAUI3rf2[nkZv#ʊ8+=p'Jcbq=\ڠA !E`.Y9CϒL_tU r嗹O(~הv$V1 UT%Q}"]8@1鑁کO׭WO]E rVffDD${kƸ2xcLTQdNrk&ĉ1Q鑁Y,=oiÆM6󰓬(>REQ232}v]xn@F<ߺ5o^$kÍ0QEU-ÝI?{g5Z>&% PdJRT UDł *Pe" (jAŭUyEyj)ĥ,IXy!la~?|̜s=393INdt#;ڨn:A0bzߨ{2Ҡ 6ttr222nin֎?"::cƌy_rWڷz[GGw!_D7q>ҍh.R0t$7RP(9ܧM4B6kn?JA_־}Gx P zt# Zz0\_5tt{a1iI3b;뿎8 TX D1^x6b8c28 CWGw؈᯿@*"ĀE{~@ bS_;/{r%á)h@ Ϟ^+ÕJq ookqǕMRϯ25OB*0D@ ?cxSӎvRT*q\)رc\OTus`C zR_28oQo [6vmںUesC!8 j(Dok VVhAOo7[z3sc9Aq9a/^ 8D$2컵!_QS6˗㳜zT@~}7ZP_gjf06KBs͑&Mr7G~o%-*N8okk;|p?DKG+**Bro]5U$Q{e&1ӦtX$Zehh8b{{S99O 9l͑& 5Q۶o;6D4aTWNΩӦyms&k~$&)1!--Mk.g#G}ӈ55r=7GzڴL&3zz#c˗/˗/Yz G&4q Fom$tֺĀZN/+]f}d2~ GUݯ<==$%K8~l6["nq8QRRbgkm\.411! Ut}?)?DذMAu/^h7QTݴi^6otJč/766LK۰1LCՏΙ~{ Hך}IIz?ܺ}=~`-7-䓾v^^ԥ~ag(ammm 6mk׆iSNYLÝ4q4hW^V6zFS/# lHۼCTJ%cG|ٳv|'l`怍SN,\)_zU(t6{YBEeԨQϞ=3440˗G&w9uBbWWW8QqƩyѮ\]g-]ti;wPi:h_R|P(8\vAu#k׿wݻ#=r 4[~=m465 rЍ7|~͂bC]’J( ì744x &fs&P(pd4h8~B־x~ 9yʕ/K ,z?QqՁ7lyBxWkTu''{%D"ٻ7YUs1؝&趋sDĮǏ P{BLx|iRA/\9og8粒] d2 l6PFzL%}2e>~_'.60Cg;RPJ%.S+8YHwR Z67Ta.iΠv~@ _@ 1A@ QNf?@ >~4WQ/]+"@ =^J?G @)C 6C 6B"|I{@ A]GwrbqO3sl6{֬Y׮]¼177󫩩鹟 bƍ@ OXd ={6,, {TRth`0]y+8Mwܙ:uϡjjj"NPZZ KJJ{'U^ւOw#Wizc7hߞ9eN?>a ۷ogO>5220ؘUdrܰoV~p+bUuǏO;/@/&&&B>ĉ֓'O-**2002e`/3*++''ODov@yfkkJBe˖-;w$PU0 S(wݽ{իo߾cp믿3g>lX$4]ʍW3gܻwvرӧmll8J3uQ&""ĉ׮]+**ڵkјC+WEEEC:]T=vXh={C1)8YSScbba… 'L+w6ޢw!1v b8f Z~*H.\Hfee —/_]Ç{xx!447o:PbX@xΝ3gx;vpN/q(4DƐ'<HT"erWAAA!l6[PmBA,@R/Tp*=]c0-H$b0=#G (..&$

h1%Kx{{WTTk*++J$˗/{zzv (o^YY ^_PGGGE{U nAU0^0LOOϡR=hC;v8pafkkkhhx…>?ah3(ЄmllknnΠ,|>Äf9rDs>L`Xs6lعsF"T,CD 39ϝ>/)l;SRRZ[[SSSBa/99 J@tќ:<ɑd'Op8@*T5H$III]\E%;;5;;ԔhxYYYDD_|Ao̙3NNNcƌ}}}u7r\.&O? HX)3H IDATӯUS EJJZ4PGU/l}!]>m۶]|CCCiQ&LKKի#""r9=MU߯">|xxxxjjH$j=!}ԩSk׮Wg ͛U___WW5~'G I/rfff6l%'{{/-*͚5kʔ)EEEY{쩨6mȘ0aÇcccAKhTB!ԩS޽KGaÆ?fff}q<&ill駟[Pu òF___ggg|}_|񅕕ѣ adddaa!#T<}tOO?USvڕonn޽C@ݔQyn"L<Ãㅄ席1c rڌ 5a6o޼|r''?x…[l!Iѡ ϩrm6ͻ ~Uŭ2vvv  ,`Q_]݋b/hoo Ʒ!D!~;$Ǽ$-mJ\Tv(q)ڕJ,y{k7n\?e gϞAJ@ L&ۣ @ jЯ! Au|, @ AP@ 8.$@ A@ A@ A@ A@ AD &#_ցRفc}.{T! &2@ 3n'w!5@cc}m.77ёfϚ5ڵk@' 9{II ٱX,;;|oB!& {У222yII Y|T*QA Mrȿڿh &DHgnnrǏwT*==={+h(vy&<7-D†(j'XT?@x</&&deeY& ϟ?wwUVuvUQQJ-T5=/F!֩S2339"yHHȲeJKKB!sPu^qi34 i8dx5}СOA]v/AP[G yȓ~~~}m.11HWWWWWwܸq@"}||={FFF`fllL-T5ԋdddl۶M /^Q|l6{ƌYYYg M(8k9>*QTTT*QBaeeU]]a xۛ888ܾ}ð͛79s=wܦM0 JL_s zpݺuĚ.wfff=C{)o&r¾[Ctss~mPPꫯk[uCǏO;/@/&&&B>ĉ֓'O߃RաaaaѕgΜ!EggG%7lmmǎ >}ƆhB[l(//߹s'X8qĵk׊vE4Ĭ\?(**H$xJҠcҘ`RAϓ'O㏕+Wnٲ,%Jnݺ5,,b[nC@RTT4a„.f߿oذ-,,uuui{>:%Kvܹ}J-D*i4T*qOKK[f +W]._ ðaÆ߿믿޺u {u?4lذr8 Siiit7mқxB׼G !C`0҂H$b01:rȀbBr?~H޽奯M5zK3! y]YEd.YBKKk׮UVVۛѨCG vq~4D~ 57lmm /\PPP'Lm{~b1+?|0!.)))++cGQxܹÇZ~H!ؐSRRZ[[SSSBa_ J@tќ:<ɑd'Op84BPCDdaa%U\\\[[[MMMf8EDD|4u0==?|۶m/_v?hSFse2ծ/ 47BCCiQ bӧ_}a---WNLL555}6HxxUÇOMMDP-e- 50ɒʧ ~z333kkԾ6gff6k֬x}رcC7o^|aO>ݲe 9}}}[[[_3?|޽*rm6 Y_{BxuꠃawWD9w5[b@ Q_l}v)GӣW}g~ gϞ"w!+xB W?b0(3j6P @ AP@ 8.$@ A@ A@ A@ A@ A@ AEvrر &!F4TC7 ݽD;>uh\nn#͞5kֵk׀|;;;egg^^^\.K,$AȰ622"%%%...gRe3$mh@湁@ F ƍ@  kkko{Yow롰~PO>嚙d2u֯^d.+|||~.9_XX8w\.r݋:|(@rs8sss??LiBT*=== sPu@'瓓{P$۷}mmĉ{Wm?{( rtt7ƎKTRSSKKKܹguyR?????nDۡ"@߼ysҤI+VԌ&tIIIÇ'vPCﮈvg~ p5HR`hӜ\.sqqQcRPPP(._|ɒ%l6{̙nݢZ0@0eʔ .řM4 |ݻ{2@r@ƹ1t:ms|>ٹ,WIRӬ鐜`zΜ9,kܹ'Ra0QQQ<Օ b}W^ªYfx(jL틊h 988풒GGGoߎmW ~B B 2 dڵfffd?JsvvqD>22h Uwsskmmmmm˛3g頙wj<322'N߫3y2MMMCe8~ΝS>>t4TpsscXnnn޽׽nҒ544o߾]WWWWWW ,>>81,ZZĉ@]L$ 1 >ILp+1W+W0+WPz=!O0~PX^^pNVbƏRtttt6lXr%&Pu8D"cbbTfć:1fKyq5((Օ<CCe9uC*L^*c0R Bd2CCC\@v$33:88{  7o^TTT}}}]]]TT1 {޽{SSS_xwcnP?{($lݺ|uΝd|}}YYY9rСCDK:ݷoD"J41λb(Ѐ:u{ڵ4cN4U2}LBqСӧw) ݄.]^} ;;{ڴi\.w^u떓T}}ElEh4Bh@beloFd2േS]Kj n 5G SSXPMM!x666Ν]YYٓ'inʨq<7vKihē@M2'''>m6oYG!uѣGē@H۾`ڵ~DL O\.o122+VxNr|6lw|}}qUq mP?ÄC!q={vnn.0$H٥~aΜ9l6/3p\'/x<o>T7yNÓ \iɠ;4tPhԩWf*宮,յ\ݐ{.*Drlj dvS222rss/\w^/]RxbTTԕ+W+D_yG^N@ -L&SGG&%%/߾ T!! I~&V471 h*T!!v^`藹@ @ @ @ @ @  =D Jߟ*?v؄ TNOs ,77ёfϚ5ڵk}M{hXB!qww/))|;;;egg^^^\.K,:-zQuZFFFŅ,_\*Rҫ39CZ Mhq<7}ʐLNL&K577_rǏiԡ .̝;G/^8|cnnWSSC?p=TW.]<A-\.幸@{uaJ<l(˗/Ϛ5 mll=zxUU-jHa=ٳ/]"dX---8ّ)J[[{`755ҒdL>ŋ@"ϟollLX|%Kl̙3oݺEBUa0 `ʔ).\3334iҏ?TUU9;;ݻw$iƐܹs3gdX}:::Q\nii#fz& 755NINuZ*;a0.ϼdNȻsˣiD#P?bBev Y,Vkk+<8@^z%޾%ڵk}BIRJKKvIIcZ[[[[[̙OtsΝ;SN}9!{ޓ\{Y 2Sݾۯť%2S1 khh(..޾}ncĉ<6+0633#ZX6l6j`,_\$xKr|>ݻ@( :ra'8R+n`0^|Inqu{nBA4=y\./((ppp0{677q8{655V.]#ӓ"48w)7^LMM?~㸧ٳgѬիWCA& HϘ1vE hkk_S*0XhѢN;!rԩSrO>Bm֬YC!% Annnoiiű֭[SN+V8s\.^d IDATp!!!U1DRl6  Ee 5553f/555NyDbccޜ{^.iq\"/\&ڬp5a DB{xx>|%==* 5T[[1o/qJT3Twssۼy+WNNNJR(AAQ04 O:-rvx^FT`l6{ĉ\.>R9uBfcccR}܈CAAA\SGO*rOOO3uԔVPP\X|>u~Ǐ7C4-5(#pF'HbqLLҥK 󁁁[l`-6KZp͖-W`8%,iiies6 V/[ZZݸkk+R盺hlmmaԮ.]D,58.ACC:E#_ј3J )#݅R]].===sss/]rJfT MS!1/2--Me LP%%%i V.m\NdÇ׮] .>4COpM^5wͦLra0a(h4.KY&r>uAA!hKRoU`<^WW'J{ 4,IIIG;v,ђd'Or8 tvvNIIimmMMM 4BK$$ .y⒝ښmjjJ4q,""/ gΜqrr3fL,ҠPr@.GGGB[[d^BaddX,~W_}E퇪)iii "%%Es-hSFUT ao۶@LӌJ C@NN͵W><<<<55U$iٳ'HRSSͧN^vm||>}VVV>d[d2CCC\@qppO&]]]wY__ߩ ͋?~Թ\.T*ݿ?W't:?!v{UTrנYΞ6m]xqUUU7 LГ2@âCq-'''ccc'''AEEKJP!!>P-Xl26o-L GWWW[<= {dd$X)9=*LMMcccC555FFFח؜;wjXeeeOć)Yc.ǎ{gΜK=<<|||:m4ai@NN$$$@gԩ`SSS 666zyyq\///XL#b=N+##Ȉp8˗KRww;= i >4ΚվN*C2 ԍN P(6n(AXXX[[}سgϪ8maaaܹs\.uww/**R' 2Er%Kp\@SRF zO?F:}cǎajr[&\n\.ᄏ offyzd_ЧL ++l͚5Ϥ?>99Vuڵ""""44cbbx<^ii)NjB a_UԩSGD<$$dٲeB0..NE7,,lD"= 4 0Xq<7P:@?ؾ&11ٳg"h߾}kkk'N[BR??????uBP] Hjjj```ii;w<<<>3 'wۥՍhqҥݻwܹӷoV2..ƍ7nܐJ C'؋/6mt29HLL422m>n8 LII>>>Ϟ=B### ttt0 366‚}}}??!P/m6@<{{{x{{_xǏ{{{ݞxAEc2iiiVVV b!KKK.Ǐ,Xb)/^p8d2̦Nonn>y~ 4x葋IdddWI(8k `ʔ)?HD&5U u `СC'N%L&322r񮮮?㬲]w3gN``ݺu뀐̙3oܸΐ:Ο?eCCCCCí[~c}իW]qXzzz::::::zzz,K'TH[nX0TbnnN ݱc_6L6zhuUn@G9PTVVΞ=fϞ=SZWWl*m\]]|~TTIII|󍕕ù\nbb.\ظq[o[omذ!//cl܏aNB.{zz޹sqPx 6m1'>x@P|||`` ~033#ZX6l6j`,_\$xKr|>ݻ@( :ra'8힧CKrׯ؀]ݻw+ ɓ'ryAAU`ݻ9//ݻ)//tҘLgϞd9 ƹK*櫩3f lQI$"DC*;FuiP`0㛛sssݻwKҰ0P]'*࿙X̙3r7Dfy 8heeg+++XLR5553fdkk뚚uBP!W^4=yH,,,X,֭[ALԩ8~֭S SsSEry||Eh Ɠ'OBsJJJ}ҟ<O&u$N8P(lvZ𗙞z~-.-OaXCCCqq ]MMMiF2@UO"/\, Dxxx>|%==* 5T[[1o.XbC*0s-*0V<䞡;FuiP`0ȥR)NTv;::E :CT"qrsqM\'..N"☘KBT<Ŗr?^Շ񎎎?sÆ +W$ 7lN>PBG9PqXyUSEGG1:MtuuY,Ν;l6{Ν%uT *եg?siiio9cffv]MZ=zlWWW{DU`*L Rirr9&%%=z4''gرDK#N^=(((>>>((ƺՑ#G:DgPPЦMܹ^SSCkhh>{]0 " C T!#/YXы @ z @)藹@ @ @ @ @ @  =E݈Ã^cǎM0Aű *h-Jkb Lk'vZv277ёfϚ5ڵk@' 9{II ٱX,;;| lllr^^^bF5@Ȱ622"%%%...gRќTCyn Q헤7)7 @F>66ٳ*u[XXX8w\.r݋ ~Bbaaѥ\zuɒ%\.W >ԡ ]OSG=0[hÂ5k瓓'N-iiiii:!O ww.$55500Ν;}RFD4tM@w!CbbmqaJJ@ yaa1zm۶ !ŋ][4&fee ,--/]$?^`"ގxۛ888ܾ}jd2̦Nonn>y~ 4x葋IdddFh5 ͩruuQQQQ L? 9[DhhPPfϘ1#++ d2K]8qEnn.!d2ǏwuugG9s_{gŕ\38!cf m"4(QA1$1$EM :\P@/EE 8qABf=>j" n ]?M=E5T{=t.o+V"vW3dӧOY~ڵN0l111_>22Aihgggccccccgg̉H?"EQQϟaJZ| :2t{7 V}뭷̅ٝq9#$BGo@m=6%XYY٤I|I8 #9i|j;sكa{pH$NNNͫ"z68[C-**b4d0% cbbb[HQ&8p@V|9O:U  ܶoP(8\XYYA^YYiND)>ӽ{ۑ˗/g!GHIeذa<mڵdLu'#G&ugo# 9M$rfΐ\H֐io\w'._Fza ׯ'Rlڴiʔ)g{Vޯ)iӦŬ,LFThhjuzzzhh()]iEAFы߿9=== o޼#6l  :aPgΜuttqEH$jllDf Z 6],;,Bb #8wE&J%AJs\#Fs/䡏&&&fĉW><dr8Cq}H]Rh4/vbC1AЋ8N_W-Ν;wΝJRP$$$̞=ۜӜA=Zӑ(% #h4>zhժU9]XjUxxr|ܸqtEΈ +0v!МN߳D"CFkw[#Bx<_jW޽{?N)|>_V>$ˋC4g{QEKV:sɓ:API\]]Ϟ=U*X,fSbٳg&][`N ܎hco[nLKSQQw?022 Z : !Cp}oaa!]PPGngff8iB!U$iZRDiy fϞ}!!41: ZM"tc#1"//qCKK AܯV`BY1ڛ˥|gU`]T*W:JBəǎ8p R ;vL=zT b@@@jjFIKKd"I+000;;[dggK$A) IDAT6m:ׯ_ee%)N׿NO^^^)))o?d[*'O,]=KݻWקZދ$%cܰ77C 0B^Ap$&h;;O?tݺugϞe;LNqп%s7fASL 0qqqiii/^hc  wޭT*U*՞={b9'R0ƍᔡ7ӭ#Co0w)J$$$=\wVkoov=Mq!rb@R(sݣZ:88۷X֭[FffD"ٱcTeeeppA(.""B$yzz R4**JZb^78;;;;;GGG I3Bi5^u T`?CFFơCz&$$DBHryVVVqqe-ZDONII)** /IqŊ7o~M8EHCfH=\˗ϙ3P&ܹCl}_ai )2 ΀8p'O"{/^]%9rrr<<&IW9x=gi|j;sكa~&M:s {>$:Fu:]nnn`` CWT" zٳ&Lc C{`4CB/SAjoooMpxxI߷o= !B# Fn\|h4tѣG?|zY|IDDꚗG*Fе! 8,\zH>Z@ hu E6ϟ?gtd,9i4OD6xǏp,cǎr )*ʯZ*-,,$ (++0aH$ںuk;^]1#dwH9eFfϨHO}ڽ;w20 khh_~m+P(žnjd3 A >oJ :Tbbŋ"2R)魹) 8.H͛WUU!!r}e2فj)O"ҐP%ɕ+W Ǐ7ZO#CʑK ;7O~ ٬Hd3$Gtrחqkjj:}%+:: & 8n0ƍweU)]v:"P7ĉ٣V Ŏ;/ׯ9|8Ǐts΍;###/_XJ>O3uT^OV3gLNNtuʌݑ~"E_Y>#+U`<oRb R8m4%ɨ =xZNOO c HC5556m2e99\ߛ7oD 6BÐ%Pcbb&NzϷo# HG*0ܸ|h4L2 A:vV%.ګmpB@@`*0>?tPP766Z2%"T`%%%ӧO#GLMM%=q9::WM,r a*0HT* P*]ZY>#dwH9eFfϨ*Vspp5kVci h4:>TF&ɺ\LLL]]JJIIqqq!cǎ 8j);j=*8EHC$J299yذamQ\\iӦ>[>u֝={s24 a/;]zw}-QQQvJIIꫯ8d[nU(O,,燅544t{O(Θ1g(_O]h)rb{ O8z!חHmr.3fONNN[Q ܒCLƙ:bb.7BCCΝj3uuu"ɓx%qX>]v+ سg'|b}y+3'RI&Q"Jө555DtJW_}Lu:ʕ+%D"YjyF$00P(2Ŀ~zHHhFH?"r/ˌ,_NQg!+Uw9Il6xhiiZhy.x0L-JMMnGCXX˽ۑNfM}]-{prO"444TTT8qbo]BB_/iaرcǺۅNfded_j @*0^Aw6PX T`*0k 6z]˘$35`Fd2-\Xpa'$7Z7xGt>P( \ qD~ǎ'Ns=biiG}$ RiTTVmDt$ 'X[iSvN'!!A$D6'˳-[FðӧO嗤bŊ͛7?x`ӦMQQQ"E!.H=?ddd:tҗ/_>gΜBLsNð_5##cʔ)m 2 0T!EAg=$%%=}ҥK.]ڽ{ꫯ߿ڵÇϟ?ðŋ޼y344O>!{xx:uJsI,\p… ,X`ff/CW,YwݫW8v]X`Uߙ Ð 7L^^^>>$:Fu:]nnn`` CWT" zٳ&Lc C۶<ɤIΜ9yxÖsR^^>aHKIMdرW\Od...C=u}v<o.\I&-Zk G?-,,$ Xh Xnŋmjj^AL> T={6vVT*kTJ>rl9D6ƍxlCed0q\Fhrss'OlUdX>qhmVKTܺu+wpDAAA!ڲe=ͺ/#=kHO0!??U`.]b= g00 |> ɵ:thQQD_TTD2R)魹) 8.H͛WUU!!P(žn"ELvZ}AO~MYCP"\r`0<~8**C@i >2V`AAArR⚛sssA\\\SSSnn' ""ѣ:N.Z؋ܹsl٢h|2Ց݌ti0~ ߾}Vݶmۜ9sP钓)?~N;w@a'Y677主ӟjii~ȑ#Tmm瓞O:U  ܶoP(]nݲe˨IIQ FDFF._cl9D6ڵk ;8 eeeWVVydKLJ ֮]KR8q={jBرc9sfrrNKLLlKJJtIII!~T*Utt4G@Ӵjdɒ}q̹۱~FRLLL6m]ʒdԫ144j:===44[) lڴiʔ)C&ۿssszz:͛>>>"hÆ -._رc3Tx3qիW?@i@>2P1q|ر?V%@6prrvj/6"9sח3'2ȥvvv~uΞ=qz4 a/;]ȯuL0a͚5f"*2l֭ ɓ'K.m]//t:]u:]||55ӳMͭae߾}z>##}}}/..nՊVtvni)SƆ`󸸸/^-:11_~o0 ItѭZ ڸqc}}%|wo߾)իW@@@dddTTTf/;,H](޽[TT={bsV禦&!!Xjrڵkɯ:t`08pU!##c̘1hI~ H;sCaaa|>?,, 벳{=P8c Z?U>755quGGGB+H[CV^ bΜ9|>? ޽{TK777gHaz>|x̘1>>>999LsK)2 Ǭ#v.cǎyzzVVV"з"""D"ɓ'9ZS 4h&33Y"ر, {ÇԕA'mB.J|Pm۶AbMME"#mFӷ\\\Rizz:c^t+WJ$Dj*QDD9O?M>RW_9;;DN #G <1ȆϏ{9,gIIɌ3D"H$1cFii9CSwpp6lزejjjEP8i$$00P(Zr%͛B8viv10{PU_zqD`2-Zwcł ;K=+vO}]-{prO"444TTT8qbjG-rppx;vvxŁ mV-FJtH+x閽VX H8 `m6PX T`*0kmX箂K˘$35`Fd2-\t^ܰ .;8Fb//T`yyy<;//:>ƙ3g ™3g*6'LpER͕d 88Dzh ~04 yT*Hٹ|@2Ȑ"Ӡts ]_|󍳳sttt7V7LӧOu…Yf Bggs޿ԇ rܹr~jn}3$åK>P( \ζ lŊ7o~MS;HHHD"(!!򬬬e˖wn0)))EEE_~%)")" a[#3?qС*J_|9s e2Ν;9DjS9"4PGrrr<< IDAT<ۋ׎O^tҥKUUUwnܻwoaZZŋ o޼'ǩS… .\XXXXXX` `m m4ha6669::Zãv!.\د_ 64h _I155ٹ_~s}))")" u"_n3!IIIIxxxg #GۍSN|v@dH/ uuu|'0 sppHJJJ#GKLLtqqquuM6~Exx@ q)ڋǏNǣG>s G]v9;;1⧟~ G}|>?88˹y&JZ| @YY٤I|I('Oxboo+VPmbC-9P^^$ccc)j|||^J/0l-7ny䩠`ܸqݩ>}z͚5k׮%&Kݺu+###99R>o V[ooݺLs3bQ|># llllllx<^ǝGF|w~0IRǥRiQQSx `qLJKbbŋmdޚi޼yUUU2{ ˜X{-RdPrHJ0gΜLZ ]B2Ǐts΍;ܶoP(8Z"m۶$ m9S>O}ԩz^ sITzzz9s1դBaDΈ5p$ɵkȝ[#vvtEBvpp 899Q;eJlHHWΜ9Hr M+0HD1766bs v6x`B$)J JH$3TKI\>n8 ӛn KHb1rΟ?8}l ȝ;wBBBܹY1PATtik֬Ah4>zhժU95%rFmZ# .,,,000&&ɓj{bn̝;wΝJRP$$$̞=3Dv:k,[fΜٯ_۷owǏcVUUu曋-ϧ}}wG'drLmðwygʕwiό^f"O=xG}ŲcJR0Ryӧ 6lD"ю;,Yґz4 anNLLLBB£G(o߾ѷ߾E۷9`Zҋ 55ӳMͭmܸQ5*##C8p`ԨQcfdd 1cƐVtv߿qq%N:t`08p.'_xR߰aCPP!$SL 1R5 췯FDD ;wRJKKGݪṷѻ>+ 222**Յf';,H](޽[TT={V}ks9p~?uP>,,燅544t{O(Θ1G3Dz9 E=nd/B1g>p="R$"((ݧ)>>(ʯٙx%%%AAA</((?yfP8~2RrqqJt?m&Z$00P(nݺ%$555' Q#""ȉGDDpD W8~UnCHt:ʕ+%D"YjyFj̟~i|>_*~u`ԥ gdn92x`t"a"{NYRR2c H$f̘QZZjΐ";Ju҆T՗p\$y,LEvր+*ł V/4G}]-{p!/oƵD,hhh8q͛1 =[[N,%--]Z*0^wYM&NǶoP-oA. mNX T`*0k QY^~fDҾyY?jݽc``޼y*fѻ???>?a„/bnnL&񼽽Hq̙Bp̙ CDb Ys>h JG.:R0Zm 2 GzʾY? 7Aw=dҏ>H(JҨ(Va^oɻ#EL&D gΜBLs.ps 򬬬e˖-ZO>RTT_+6oM6EEEbBBH$*,,D "F-<=sO{t#EjS9"4PGz/W߿vϟO4Heɒ%~~~w޽zMJJzK.]TUU{ns"ɽ{Dd0|Tz9rh7Oc͘f[Ct/&h4[ZZZZ Aj5ZV|w~0myxu0!??$ܕX,?h=\Batt~xj߾}'N$%KK&'''O a'O8Ds?~rٽ/NNN~~~o&Esd•+W?b=~;f@PXVV򭺺o]~=^ݽf͚В7R鰻`oݺe˖Ȗ7nnذ|ɓ߿߫W >2m Yf͔)Sבu eeeǏR\֍7 2=,\_ݑ#G޿d2?~\?~A(RYm=۷ÇcƌT*1 sqqٿFQ*{!VYYaشiӆ BDHT*Ւ%K-" cccoܸa s"aGyɣ{}a-sQQQqqqdUWW޹TӧO{{{d[.[jп>`ڵ̘1d+Wd?jԨ6y:q ͛>>>"hÆ B[M˱{R8m4%A/^ 4nʔ)/\>n8ƞqj!9DX~U*U|~@;:Vߢ}{RQ9sf֜a;V6sJ{ZMlǙ&!Fñ_0 z////D{(3DB` )WW׳gzJE=%gbٳg&][`N Cg!_ {~ᇑb"eۍZ~?Aƍ[zuLL (}Lӯ'PZZ_OWQ`d,$9U멳R[[9k֬?0--c?c4)7Zc20 r<))i!!!AL&OOXǷme21bD^^cOK/R!C(ꁬȑ#+=R*K]3{vB5YqqM>.=SWWRRRR\\\H199933رcرcZѣRSS5MZZL&HJerra9W```vvFΖH$T3#Ǐ0MnuN׿NO^^^)))d[*'O,]=KݻWקZދ$%cܰ77C 0B~-bmgg駟[ٳh"E5jgϞa؜9s{WWWFGzV#~!<<믿NLLdIHH=z4hZ\vZBSL 1'02q?d.CDv㏯] ?o9V0'b6k֬m۶:u/xwͅi5٫WȨb lllSRRMfͲ0Lt]vG0 cƌ}}}⚛[uuouuuVYĉÆ z7c`E77f{=P8c tsjׯ_wttuٲ>,,燅QG"! d/B1g>p=%{eBPP4Mغu+y`}ftvvH$;v젞 4hEDDD"OOϓ'OM+++ȕ40dZSRR( W#'As9|1c}||rrrHeE담[TT$%hhh%%% <&raذa˖-!hVߧ(}crtt3''!CP\\\Rizz:=K7o, ǏO~ĜXSS3|HĘQܕ*r޼y͞=Vķ!+Uw9IDVTP_[jɍRW5&0L&SKKKvviS1 Dtp暿ohh8q͛1+@`kk;{v%xapW"^sg}u@bcchhl!/c5@ݫ׍kWnVx~oYH6 ?y2Y8f>W._=c4 ||gu?_ cmBX :GЗ^.8pxe~M޽Ʒ#f˗ѧO_cNy?# ϢWhѨ]|/A^Vׂޟ, @^O0*m^^|a[E^j0Lӧ̑ǿxi:"h4-A &$2F0ή HAjkk:"3z2ݿ7򖓝 @AO:oeֶӃQWqNJ1dݞH`p Ǐ ;9x ?>>>۶o_xV։*0ځ8 .pIDi R4x ?oVΩS2ow" UaAUU+I.F}Qg0jp%>` Lco@FoDx? ɓvXoٳû"k1t:r/ Տ㶶 Q V݅%_Ke^`ИٳMJ;VBb&1 #c|hBV4t)i:@/~fx{yH0tp }]H:!3] |^/2|X_ST66 /Wq]H^y໐:@5I'N8c}#Y_jkM ].$:]HxYy/eY{zx|vvst {M{G@3[ݻ[r\@@gYdXp>o]zܸv] xƦw^;+*,0Ӓ~@7!<ؒf&جmߚ*EF \oy8or"e^(ogI19D" IDAT7+]1echͮJG~lm0oǿ59=whv:I>BtQc9T`m ZN5}\y֠۶Fol{U!iB}N?Vc#0 3ĕ_o0E`fC#p ҿ|/Szbj1u-cOTAhZw/; 3 ]Ig0|һ-aMި3t-&A,$@Ǧfz);[N=bMC Y~llf1060T0@R޶6j]ZҬk!Ri ͺR|oezrڀTf?egk4-F`֤miҴu~}S߲G5E@8;l`j|_~}flm1 _`c_oYRo@[A:0CuOg^(t4z5A`{[}˟֫b?nn86zMY~ؙ_ mZt5䗋c)K=/H"c`m_^+?ʛ[Le;x[+B]s@;yɾb /d2a l7^=Tˏ:E*:::.^kƦ/;L_4ʺObiVbB]P*0h^W `mjksuujRooF^lm; n^/? 6IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-cpu-1.png000066400000000000000000000203011351617527000241000ustar00rootroot00000000000000PNG  IHDR?xsBITOtEXtSoftwaregnome-screenshot> IDATxy|ź^fd%a'$l*(*(EQQ@P69r9UAAY΁((&dL23UGCg$CPO>T?S5beYPjYpɢۥ&&3cDDDHH`;N{@C:aB!@nzsLH>}p8, &y4,X4aߐA93,0ئ`wʄ!` L*0 p,c6p>qAqJn7BeYd0cʁ16̈́B0G~((!n ) Ȓ")B0ð`YG'LY֫M<U jPrUyiZ,R$j Bh4 @eYB8ScyW]h!lA=@a ?V-ε NYPEإ]9VfnRw= L!a:f'MEQp!EVox屎j 燇N1& H"W^fãv6{a^&|){z& :X;.tܽ9''"**`08Kl>:&;eQYV=sr"bbbf3a˥ "#1Ȣ.)axh ).-)ʨ,஍Gxf:p &dޣ}36]buHJ.d`B<sK(})"\rbrs% .A%WG/bB`L.I 0۞+^Uֽgr)(%>{IIM<`1qC52SP0LB@Fز WZm\MHTՖ(TPsapFuڇB[ R$E1RDdQU"YLBwF&w?^5r񒂿;L(}=slp^(!.\-Y0R1bYf60B`j2ސr!b,0&j j{vH{/_Y@ !y纥>)% 7]bo6}.r%H/e,δl0upunYf|GE{ol2X=gaF?شK c"q8W<˴H~ IpUn)0R$Yȹ#3"rӉ)\K DpHSe 0Rm/w E(Y92nK3f8S3rNf;|4Ne`Yy>,,h4<)rf7"@ګ ;0k6WQ-Bc1%A를PeA)0LQdZm u ,! eF.HJV1W p# %ܲ풥sbZ.85K1F۴(B1Yo!|cF( _Ȋʵ,QdǩUj}%QD0,2,[V!e9E%p8BCCYeh4r !3eU[,˪<E, &J[_#Ȓa#"pC"Db$m‰eY{xΥNk״AƑ0 !Xgޚ[9N[0LcB, P>3I]'_nQiռIO(uX9q W3rIi>RJV"+e>o%fBupR%dqBh4ypIڗY>wIAǏ;rh ,6nn3ҾCӧN$P?6pPsGX(,FDD9ϼq@ʪ˾ٺӕ_Ը~H['N\x~W|7z\ov~W^Yg>(RwߙGۯU:JKkV}گ@3y pJͷg]ڱcG\\m}y {Ĩ ?KGwկ?HO4 ]y-,48q f8q#<+ BXcP7ѳ׎?B.]'#F>9b䓞T2::uR1ό2 -az'%/[5x$@ x9t0[:"e&CG[@Q+bG^,h>QL̂&HDڪwLױ,4k6Gvv۵bGV{>jT[ڢE/(zAE -^PmQjT[ڢE/(zAE -^PmQjT[ڢE/(zAE -^PmQjT[ڢE/(zѣj,hf8S'OFEEbMol;]v-ZO]HߺmkQւg'<ƴU%K۵m{rZm?Yu J[w*?_Om4[j=cMFN|rϛ_ yWSԼLj{\p!)Oe$[m۶UڵXhٲ* 2M[54l2^bE-yWlڂqpĉ =SDDDX հj~#|Ͳ ++yf5IlR7!ӧo6vq2uʔ3>u,SxG,]de J0dC_|v]CԼ>Li j㏏5{3Zŏt_KvJ)s|WٳIMKkӶ]jZڜٳjP xַ<wO1^F+ ~՗>ޱ۪:( *ml;NO[=VGhf{Q#)~E/(zAE -^PmQj\kB ?raxMk$IZjՖ_i߾ş|p8K^At'x9rH>},!3l`T[@6m>l1k ԷSj+Pcǎ0`d~qҋaa//!|nC̦g'< E8`0$&&޽{fy̘ѡ~-/!v] $''o۶2M#bEQxWC ٰaÄ LFaBBBS^T[BH9Sec|MfAf+0VJrq̘1W>}ѣGEQ{ N8euv5v_S6_/J=Vnnǟ^ziڵ^|1""ed6٘_P*DQzn9ry_d cyPnzC[@NNΨQ:ut=^h4N:u۶m@XXXDxxHHd;(;vlӧYpL>ŋΝ$)/VNzqR.]Kܹ5kûuvIAۻJ-eYy7Ƚ9c/p/6Ֆ_c4_{5L&:JL_{ݻbo+j˯8yԿDX,~xyAxad2BڢE/(zcFzzzll̷x%3|SBg8p˽hkjf Bmxwɋ?ѯ'-<ڢ'A(zAE -^PmQjT[ڢE/(zAE -^PmQjT[ڢEn{]A >*V{c?(Gԯ.b[UYj й7m[n„ 0a„k7xl6Ϙ1رc0o޼Ea !+W3gN+wNHHX,zf={vXXOoF-MJJʤIFZ׮{PDYE8p %%elFFzf@DQ$IQEQ+aÆ7o k׮MZlp ҹseKQQaSThԢ4tuA4Z2jOqǏ jͷt >X--ur]LBǃAnKմ0QQQVkAFjWRXXРA4T UJXK'<ثwFY ӧ{T>mP%m۬Y]Yݎ}q\dTT.ĘC U0ЦM-Z( ˰`6TK*T:  ⛻2RPmQjT[ڢE/ bE71(:1Om5yRפe:vزe|yUGEEL&[^J?!1QXX EHIDAT nҲh&eY_RY(CAN:srr r5%Tn6ygY:e=INrICqIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-cpu-2.png000066400000000000000000000204051351617527000241060ustar00rootroot00000000000000PNG  IHDR?xsBITOtEXtSoftwaregnome-screenshot> IDATxwxU)[$C  "}4DWDVTlg9瞳9< F1 ]TnUVICԄ)Ki$FDDLGr ";cR QϦ+KNfEbGUU@z}ePXS܄paaa[P)&TBhӿ֌#ʲn"_.|NU)J(>3J)x%a[f s܁vM ]#r~D)-P=X1ZfUzJP7A[w^RTL6j5 }(iw86H Eo>Far{1EnG;=o)`* cz'uTUFHE aI^bw jK(`!B " hݽ]k.tuQE5e|sRB) mHbpL迷i"y4KnrQD0Dh]Bd4KB QP1%Ƶ[3NKv. J)FԴ%˲쮬~-,+**NlQ1 cL(U0R*,*F$ Q?3f!NA}z-Zl=_hD[ @ v`/Ew&OhJo$@*`3RH[Fq+*Ւ=5F":mp& FjfnC?y~8זYaLfm60C{BX 4dɭHn1"D\H cԺ_}Sɂw`L(ղ]tLܰ^֧gcZjldHRBLYT\ZN5]P)(GڙOݳ̝_\g=#|A["(%PT PUcJ FMRJ;yvMjP-sL ,;:WZ`TSJHWqp^$:U.i`b.~#c]EwKKVx @OS~F|}B+J;f[QVTZ͉ 4!^"9`䝍`(;[?!k`H`}9/G0FZKeIHKMFEv[~#h*kJ(H)zPB%D+1za(,qH Js:R˟RB(8%l(ܫC #&wK2!XE8]OjSaڄ@0&#-EeHEg JSNx*2Rd1qn6e׎_ں6qlRNnm6ns<-hkc<Bynץר;;wZq9]>W.dױDQT:^y~z"##m66 }{]eH;7$K.*S8^Eg "yzw1<˲/x٦ڋG_e65'hϊ5mEDFo߾^qKt$ǥx\5y jl%!aj2V9Tt8ɤ"ZVMD[-/ l Zl UQn]|#C/YD \"j}U;&>7%xB,f[AQY#TI[TT\+7H|!)xB*qdzY.K~SBZ-%2"EzYq񡟾 ˏ6̙=_Ж% F12Dzt7Nv P&0DQ0m1iajz13sd4jظ0 o1K[lb)^Nas"(؜0 v0 xKD.F]byQVCeYc58 èsմDEQ~?vt5eap1cos:фQ\VS(ҫoX,dIznjuĶ9E?`Ԟ05 ,V+1-wQ˭Æm~W^cb[Ķlպ #F:tH7z9 V֮*11q:͛.\;ojW{ޫ'ܛzsl ƭ|?vz.}w l6tS-f:ulؐ4hp6m_{+l޲ePRR=yK?~xrREYbe|||JxŋټCl7sx]T4iҊ+'MtUĶkYsN}pmc,^|Wl}pܹ;vL!yM6k߾}#GT2""46&JJJ"##5믽mo-ZzO>n8`<WO|OiIsoj\dYO=UUΞ2yEoWTTTTT,ZnO<(ӧ?K/m[q-us=iʤu|Ä 㟝y:vKI4499@EoUPPгWz;oj7~Bf͞} < $YV$U߈U#}.!C4>]82)%7gll/noڴMR<8np^[uDQ9ed3L={݁B-gЁ ?QQQuҦBP[~?~?}|kQS2ǿ[WЖ U1eʔ_|19uNZڶ{4$$GIۖv h\.~gggϟ?G \N9{Cбcdz,d/_>jԨ\طoߪUnYf]M1-;t- //y-… kSJWoRiIqѹ=ӓ,ھbڱb:ڵvm^aK8iӦC 'OWu72dzlǣF4eM[P%/x̙,U'̮223"-oM7nԩSK]b:7|SK%A-=Mv뭷zalFhqm=Axh$0m1iaL[ `b(F0 -Q0m1iaL[ `b(F0 -Q0m1iaL[ `b(F0 -Q0m1iaL[ `b(FDߺ"\eǯ%xo6r@qWil4wmvUL C8v3qWillļk9 } *|B~8b5A-Ev85ex~L"8 `8CFHhRy_gl(YQ& Vwڶk7kwL[ ݯIxwnf<[s{"1!ՅElA)P o7^]bź~p*_g&MN}Q;{J)):ST=["U^9kpڢ?0s_4M*[\kAuhR~O/io4vN >t!%L? :|%TV:++K3|HoUYh !c“IEe[ƌ={_bj{@Jŗ+,6Aν>MAOls"X~c7 @M7,,8u6~?O^JbZCNh*..뮻ĉg̘+8|0gEwq4u;gϞtٳg_R  fIɓ'pϘ;wҥKNܹs8qQEQڻYhn} >wK. ׯ%Ijh xgnNļy?]N81v옆v0 rmGyTŢETII;wwCl6[CAПwаX,#FHJJn#ȵ>Y,P4E,hF0 -Q0m1nO?mw^~0<`4]vvڪ)7Q{X,0 -Q0m1iaa\^q BafUU5?/࿅y>2"GbbΝkzu%EE:rȸ. \MUUdJߵKNjeRRؑ#7ݥK940Lݮ!}{n:*%!)UX2{YNƿ&GvUE~XԂ[ADQĘ}3l aL[ JiDQDA4 LtWC[۷oE1--}hpV^ݷo_͖_4;uICjkŊzZbEаlذ_G}T^^u.s`;wcY$Iܹsfff:itK.:tdڵ'(b\\ܦMtkӮ] 6hnbY ƌcݫ7*]1J[uƍ{9t .cƌe˖tHEQڴi77Rpu^R֙pqw]We[u2eSJ5kNZQ~ᄉkiҥ$>LN~?[o-Zx3<]gUat̙3gΜSӏ}zWQڢĵf)S&3i͚՚رӦM-ZǯQ;:thTTThhh߾}=;dĉ?Yfn;6lfW'O#22*{-[Gh%KWTT^sM˜1couLWeirޑD ewڔ ٻgv҂/-XPuv@UN;~|v]vELJIU%,}Ƨ;t1mw-V,ҧB`Ibbb=Vͫ-+QqYvQr)++~ F=IPj:DwzW\g2Oy:$0o>n51Xy%IY\i㢢JJ[ly~5AJK[8aR j-IǶlA !藃_wάCjC |۶zsN{+BdTT>ccb!XԆZi8lڵkµsh6Y!~SYԆ˸ODMdH\uH`mab(F0 -QA6/1AomՅIDATu;l܆QdՇw虘رcǞ_<GEEY,A|v?}.VMb42񸜥q9 3váH[ZzkPu+???**8?KWBmVUEmy~aON2 NT %IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-cpu-result.png000066400000000000000000001636761351617527000253050ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx}SH! z *XWҼ*"MQ/* ^RA"^ "])"=BBڦmv?ewvi=mΙ쬊V (y!$9ÒYX;+wRJ5|||t:zWf]v-!$#9KB(F`?r8qDANf^^V5 Z)˲7( ;ygG\& 9eijjJ2ķZBHPJ)!@8A*PRh*NSKjɷZ<ϫjnAF#RRQ\q,˲, _SF+VJiZ.k !VxJ }*e8.RARB_F;hDZ))sQ8^VK3b~c̹Y:RŵM#b.qƲ9saY8A\OMT*Zj'N(Rʔ36MHK T)cT-^v2rc7,XAU Ԝgzb~Ղ >:?͍FV]6 ϱb(\\ <ټj*N>={[n(V*44ZjYLBr P°fELB8<p<3sNx4%!9n-dJJSC k桞MfRM/>>ᄽBPRvwzN\fEzs23T*h[ Rl'>.D*|/'TU6MW_X,ǕV&M\rEV,90^#VK)9V4 W]L@c<"wvםn!{r>O (<جbf햂'ͣsv%???//O׋kZ|0(6vB#0A/0ZJJwWܹs111ܹsbAX; !s xm:s,eQxۢjU{H;y9"c]'RL O=Q OgS";dVfY |N=koT&bf/PmkZ@i>6mT?wӴvj>ԓ%{&Nnۨ]j//s=emO~g8a۱dqC]ktkz:*8Iv-o%)3޾qHǦY]nBnv543np,ϱө$߀\sK~rLW7ԧ 1ܘ#~xJ10cRO*:}S)4 fal9A̞c),kw&W__EQU]kl9|ҭlBHF6ѪF:vM,r7+ۅdBJ^Ko^/n3R mf?QQ~^V!$#;G6+ϺUmPCځb^ywe5ӶA\ws]FBH&'hpӠJbMȶB 4Z(eO=/s'< /Zv7ə֬|j&?;33zb~ܘ90JK:~'=r(Ϝq²Ts,!DxZ`^<ϱb6u:4Wܿ:$ר׌ԩ[j³@)M*p$3"XvNsla@yf/&eT;9IinffZjf>l(jU֬7艆zxߨ?2BtU.=S9u]!jrcB1%N-p35'VPmBM!5uJ%`RsrCAVn9x/kggeW!5 nE@]tqpQy1S)T9F`J9Oȶ%i0s`jp|+r*2V*@9VD8<pUKiVV'N'u8_?sVss,Y/Cmj̵ap?h)Y;/Еwӳc>sf_jQ}k /^sn:",@ jwbyvrR.;b:<ψF=5_^g1ӲU7cnQ|]nB5sksJlTV UCѽ)|ѝkx;T7ofc7۹_;w,=_Z rg7Iݸ[zo``x ӧ;9{ʔ2_WBJif>s"kmfeīNT*F[tS;&qE|[I_L{)wk0D׫s7M]c^SSFJQAAAz~iHHH@@"0mݺjZ,qV5 kժbtRͿځfΜIρ[ V3c5G}t3WԫtVeרnغZWTjt:hOɲI pւ\NDz,c~j4Hx0`@^^^AAbaYV6*tC(ʔ G4ZF7)*rwVGs]8w9;eˮK")K(=U|-^jUz^Yx(1 _9ݧ6 8R>>>>>>5k&x=QRիKo),TPB1 cZ9cY'߽)ϱfY£՜'iNNqE`>G"/W?ק~ӓ)@0a J#0އQ0@PՎ(M?Pp @iV:W!k OV)&*TU^pFpbJƛ&JQkEѳӧ,RQPؽTǜRu 7M2 f 'r7͕bgR]PX^)%==/S^\.'N,IMok_6W3ʕڴi^+-Æݸq)=))iAu8(ۮt]n֯__eyҚfΜՠad\ S΁9Ciwywy?5G8i#CHK^+BJwREReׯh=w!q낅 y ;u)ovMv/X0lذ,;)lH .hgS((9L6<С]w0xSh›Mϯ׊h47?oG?rJ0f6m]2dڇmv /F.+tHG6`0>6hWPϞ Gˮ_3'%%۷yeŮʑC p"ٳٳrrrRRRƌ硶b7s-y^"OZn/_SNyk|oyHEՊ+x≢6 (<+s>-(0Ƕiߟ۷OV.zu={ՊLsO>}F>Ymޠaĉ͝[ZvYw߬VPT%g'uMS]"]SPEԮc2gΜID+_%r'SP( W!%ݼ^.x|8G`[,~<>p#0ReV=$Pqegew `HH YYN)V*ȴV-dZ !ٙ" 3[r|8UH,ċ.T:VaMF` Q3[NP畠tWM65kL߿_Lܼystt`ԩٳgĭ[6jHFEEmݺULӧhӧd(ېʁ.Y~jZs٘QѺuFc1ꔵt}VD;|F9QgPQ7"F mQHhćKLTh!*Bz1Vٜ^n2&dwÙӛ!aT<׊ֽ}d3DԮӭ{׬ܖc)/sp<򐡄8WHN=b&zVdΝ{͝:u!C͛7oڴd2 /+,Yl6/^xȑb~ؤI&M|e"d7|;wy^/5*;;{ӧO(!ɓQ;v̞={޼nn޲G2UgPQDjq֬YCܐy @a޼ p)|mpZKarVJ%$鄼]pf'Mmn޼wmv!+3#+3֭wyg1nޘWYsss&L/ǹX(y?^yfɷoiZS'۶mڵkwin@@@hh5_;EfYأG^kN̏?PT&fmٲ%&&>*//G|k.ݾcǎX1aÆW^^r%22ClCaվ}۷;%jZL)5͍5hjjj۶mo߾=p);bڵw鎃8}z(V[nE z^ 6x QQQ'OtאBbZZz8#111NsGU<mvlb&$ RMz冩:L HHFzӵ'3{'vPP#$߾a{n7kDEEy9㶖kwSzu y-{cQgi`b2Ξ=;m4ZV R|2{vӦMĞ={)w&Mػw/%֛o%}P^]׉_tݻ)6nlԨ ]i!!!!ϟw3f :T|}ܹ`BHppS1 C)eF{Hm亚I&6mرcbbttҥKfe<4jz믿EZ ;ɲkV?#F TtmH'8\r&niӦR6mCV4#ޏ?n_\B{LV6nr$u}nݺ-[L<ܭ[ ݻwoʔ);w]M,YRPPx-[k&M|||&Ol45/YF4 uʎXQ#0ljxnz+|en23hCOgyhFѻwgyOގaFi4[!$22rŊoҏMPB6q֒lO. ! uͬ삸Dvf{Y_^ݣGޔ;Ϙ%.1s7ŮdW!ϻy9Ε_#0* \;&\p W"ZAzhѢf͚ݽ{WJRj6E=Aڐbt:&22Ra:tp(qQ:eGm۶[6\TTҲ23R${+ԩgر ? Pfе"͋L, h23~9|Xh~Ի)nDBԯ)/ArҪ I6{$QB 1%{fVvA\{:\^ =7$߾e=dq6f IDATUHSXJv[8W!J5+(:wbJ;||y\\goܺu[|ݻwIIIyEm2qČܹsnZL5k֊+8PV-)gTTԚ5kV_%&4he…(ې(''gٱ݋OHHX, R:ŋSL?~F]Wʢqzk'XƃkޱcbYx'hF|eGbN* 6Zy !d^sEiw7o00?CO6 '_呄l;Bi-Ɇ)RtZX Hϙx gö'[ t1|w76oo@Sa?1ͷ^y^B0!P s`Jn= w{T%g(  @i)۴iSfz}ll͛7GGG N:={VLܺukFZmTT֭[>}>}L& vOSw,YR~}Z-={6&&`0y(Tw&r#٪ߎ7n̙fycǎ-Ĕ7nhbСEǩ󑑑cǎ5jzŋ^~]&$$9Pq-zYi<D*z=z/ܹsgBHrrS{G"DDV`0Ho-ZԬYwJ)zbPJf'9]Y,NE RҩKתoޡCύL,w^^E{ u?nK.'@eT17gt uO6ѵN}.XE"ĉ322rssΝۺuk1q֬Y+V8p@ZQQQk֬Z_uTT8hР X, 8ClCٳgN,KBBBttRz)S?sׯ߿Ջ |hwD*TvdsT.|s&.GX8tld]tgOTs` FwW\%??Rzȑ-Zh4-ZH333{{%)!Dd0`^oٲ3g*nݺ}SN)mڴٻwoi 0@+-f(uO*wmmK'rSlde3usL%F`ג?ͧc$+_&T.T1*8@dJJJJLL9s&\Od(a&(}84D`JCDV @i @i @i @i @i @i @i @i @i-xER?q}ߴ}IT)?~o ڻd6g1:B1H|-oiNY7M@󈟊SܚӼۗJv-kAS~9A ǜ !|-ɔun3gBBǜ}*hl׏5Fq"ޚ<Ƕ9oiխ#wk\z5M丌kz)}wuy]9-f*5AB;yon}%wk옜:$Ȱ^m>n댗dϝ/J"7SV?BB~xahs.tGFl9A !Y\QJB[*~i2;uiPݒy0 C#v杝]e*q7 X0ud{yrOV6Ft2F_Ŏus2+qʉ{*RJ;Gc?Ou>OSG|qӝTaϜ<ތz<ž9΁( ( (h؆ Zli05j_*JRi֭[oRcA>}>}L_3mݺ_|y~~={:$}D)5#G9r}M4nҤɇ~X^TfE̙|'|RGEE}W #G^rv9yd__I&ܹ]ݺus)0 M4;wԯ_ҠA;wx ʮtR4MW^]*TaE8/~D)8qqqbJ```FF:===((ȩH:unݺEIJJSN;Pi!2eѣ=ʲ_y7}ӧH Z`bYpwʩ /0}QF 0{2/]4%%%""""""55uɒ%NO~˗/Ϙ18}v؈#FH)uYFDUOEjj3 @i @iC1e5 hE&a՚U=BU̷Ԉd:{ijZ._%*kTP*TDM R7q@i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i @i DAw/AP|<Ͽ{E T&aofNN0"0۷O6-77AxI[>ܹsg__ߩS  "##:ԵkWɓ'TThJ͛8pG>>>cƌV0@I k%Qf̘j{%~Rt&T RRRnݺyK,Ǐ_~=Zj>>>Hp@I 2s&L:u%K&L￟>}aTDJDRwرmڴիWllҥKU{ZZ裏6lDy{Ç_x/@ zIZ`􊋋۰aþ}^|'|ϯ"0h4Z5 }􉋋#+ @8=^R P^Jw( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ҴIk\|?<]2֐Uk 61E9G`O=\SB@9A__@a*BCג;nج̌T}bc+ G+!a&))))11q̙D>0&=pAP^pA \r #=̙={0Jєw* eoݼ~!VS^}BT8 Z;( 캡P(X7_;yDz=i4V-wi4-Z6iڔ_.+ϟ^}4lU}Xt:]&4Zݱ#Gj֬0Pi`}|eeDԨYcN1[c 0))w>ԯ߀uT*B͛Sޕ޼yCBk3 L;fkPԭkbYNbJ4r@ WvP)#G?Ӷ];sD)r5k4FkԚ_2dpleGF#; UƱ;=YVyR<UT%r@[^ZaJM BZZj5hKuudeex(@T;(# ##RJnn?&;*? P\ Dx/To-S'uxWLj4/Nvv6!zݑH[^;(;Gw Ü?ѣx)뀁rs W((!쩓"j>yMRV񅄆cr˨ZYW1N˘ݿBȞ={*,Tm~?qI @gSޓݺݸqCu(委[gf;tO?CʪS^fV!mvzkbb': ۦ7߬+q6cfݺQQ~oSNSyO?kW3ޚOJ9{֬&]׽BtK_ ){6l|Otw:ׯ{u( /r욭m۶.^awٲyl6r"## !'h;ɷwl2J !aI&.S;mڇ7otbǿK0v̘ܹsqoB+HЭ7QǿưCO8qʕM;L BOO|}ѕ+VzV&Od0z*~䫯ǎKb`7vlV1^dg+#GVĎ-X~ԩSj:vZȑ#6l0'OnР{nذaܹ߾~/[oug]jU)/@!{ #_Ă-[I^3fVZDDĵk6|nvC[FG>|о]A~hQqG ?~’%{t0L =H)x|xxx8`xl֒7:hРA?[Ɗ Ci#'nbVB}ر={*yFcdu/{EaY9_M|!=iƍ_ /PW(vX, cǎlٲ#GzuT*إEb UJҋQF-_W ÄrZ|Y7oܽ{G}W_}eBF|`qeXl۷z&qMkZh̙79~qe(9oF00?DJرʕ bJ)w3>KED Z-|:t_7mB.AC']-Yxz~cܸޚr eo&%1~޹s+W]/S֮]{ǎe%h;%$$N:ϗ]DqZHݾiӦcljǎi&ڵ…u^tf[xI.qR%/|}}.3f^x(jͨQf͚{" Hi)pK@[ڵv\̞o>!!/f֭G`'hF|eGG}Kl:(0HJ'!*QNNtd-[=3MzEY?R* hAX̗R+/2 #[Ŵ޷ot@vd9s9RN]w7?I֫?x?=W?3gSgx[* RȂݻw0d2eff6iҤxn۶ܹs11[9wܶm=u7P ,8|pXXjĔݻ7<ցϟ/UX_СX?2dhӦwX6m룗,YRq)XddN6{TT;wx3{־};Z Fns:flا~&vqo!GP玬3)af_ö}{f[xѤ7߮]NI[|F[,B2W/ձsgaìfoAeۿ۴GWo;NqO M&ӳ{`we2g}J>j\v>Pޚ4s狻իQ6f9cڇ3fyz sBLę3g??$4LREFF.|YiQJ_%j7~J.]m+W'!Uj%cpNi/\d UJuiqj;u%ז ~ X :4Zm1lB$?PEU U@ !Jv'w ?<.#mD711a׸=0:x.0@D7Q\+Q̢ "J0bMD^_5jlda^9)S?TᏞ9꩞g{O,M-]?DBDڴ$PΘty_k`TTga%]Q{Р?=~ȁu}Pnjl:w:Ǔ(KiOL4* GnNWT`}W0d+@W)ƍ~:y ݻwKK oݾHeN Vtlk3u?xAtmmmn`tFtE֧oPY>}@MzF?@f:{v>֮uWl\㵥wի7 TB.>Nf?%ҍx钻zơCÆF^pbf?]?ݻ`47o\w=oߡ@ű=K7╡0LӡM׍6'|?JlCmw 6|޴}!K@@C덾}b1{ TX޽zѷϛob(P 5cW`[@ 4בǿVV޾}lll޳3z@(dh@ 'O\P,F#`cKssMM+?'G!D@ PP1D*mkkk{h4F:8իE"P@ ŋ1nF}wo^ކU^U^0@zIyAS@@w3mlހVV/_FF1tT-ukK ~z`n@Rj}jrs[=g"]hə1c:ƐQ=3v1P#1 xaÆgg"~V߿w=_A i/ͪ#Ƥq#eee3 =  0rlf*q=?{ߟe&qT<S7_F,ͫ/7n9v!Kl,^zZ.[j~b^>i$] ՌkZZǏw숛7o.qY23,^̗̾ZU[&-ͪLȜйsC͛wO4=abnf {ʵ4z۷,].X&qU )3Nupϫ'O} 5h4.]ի+V,?s&ބm,7[>bވEvWT ؕ;qwg>}BaJȖ}x Wmm퇾dMt=刃>}xxx$'NOO2hLK0hw<{XG?K.=8j2Y`#^9  f#GXg@!?\QYY#Zl/rwkt_\\~}$kZT)̙ݑ#O?*eJԍnص 鲲LX} z}ٿKw_ŋҒڕ 5kfϞQK. acglBBR6aF Jذ?=}Hz1 {woVVVL2eؽp8׊+$'<{q&٫ѣg|ņY:xSz[2rƣG`-δ(aH _|IWZQJ4nxqԑ#yym~0`Gf8@p4a+q^zBQZG :h95q3g}] }w@nQB^0ۋ㡸fe?}{83c?p]/M}s~o lllҡIwBAڑ|;<{7}ώ<~ʦF  WD^zB*ׯ(WwG114uƯ6#;~}wJPS8w,\k_\zW׀r%H9{):hBg]`-M #AʚkvW^Yd5nqqpppݻ#F;vl3wͮw1 WV;455Ie]|A :@Kbc[[hF#Cjsccceeɓ'n݊zHBرci3flBaӥKr_f.oӧO0 {wCqQgJHݻ7@eРAF~w'O;w˨l\tٞ?P EbzLyٰ_sm&P_ƯF%~(4)=kfTfT’Q}`ggw~D,[ ҡC!&-1{2gʃ@t,z?k|駵k<<t/EL^tAtL'fWNLf =045 ޟ 1O߾666-o^)DXOT<}۽v:Tw @S@t,/_& ]U ctĝW45DgU`@ /;C jb@ Dg2=w~@ P_!@'>@ SA@ DW*0@ U`Ix<^w@ W3X~~\. ń- |WAAE"{˼<@0a„˗/[FSSSHHH$ Q*vXAZ B(@>effۓA-*d4ua|%%Cu:M~~~Bp,BDAN+KNN(駟>z;q٩SbsM& """o^_~L&dc"3l^ o(--U)))cǎqR*t GpΝ˗\bEuuFsvvq 6 6|WvXAZ B"τ\FR̤HĬ=ca4gddfddlٲEz~v#8<~| ( 0;e~~5 ) )**jiiQT ŅՅ&$$LC}]-񗕑NI/Yi566޹sgͽ ؋TLfkkS 0B 6۷eRR=aҠA,-"-ZdkkQTTdiwKZ YPua>6mdDcKG *P?1鮮1cGM>瓟?* o޼񒒒]\\ F? ***c_\2 ׯ_hhsX Ov횏h$zWWJZ3S1/8qqԩS6l(ɉQRdѢE_֬YCnso֭[|MRxB .lgΜ/6nx@XIbb%KgRGGGKKK:.((֭[\Ғqggg#|>`0 @ XKY\`*q\*9h Ç~xUS*w6;H$JKKsqqDfzMVXaj(,zqY>}^ v"T:jSva\N{w̙3ϴ\xzzj%9)KJL&kll zjĈϟ)DBK$gϞ`v1%~;e pʔ)\<} ʖߪ IDAT+,,OY1&LIIɈ#FA=@7|g=0^qOOONGt:rܿ_PZ^wII q\\\n'AKNN8p {KPj=* 1 {Lxi%aQQQ)))NNNHU(&--M.[RBcuj-TaÆ1>~~~999&''G*Xa؉'|}} о[[[S_ktt3%%%\T*aܸqyyy|̙aaaf`&̊+233)))k֬)A ݻ y޽̫Gkkʕ+RP( "cnnQXNKRTn:꣢Y:jQ=nˏD V^xvŋ@ O>bd}mllػܼ'Oܺu+'>xxbxرB /5ota0Q@ T!WTo! @ ՠ @ A@ DW*0@ U`@ ] @ T!&=D h\hQw[`cѢEwT`r\(B,((^^^SǏDVNSSSHHH$ Q*V`yyy>>>`„ /_&XAZ B(@ͳ(™nnnhPKJEu;d}þd.CO(Ο?ET^Xx<^rr2%FIu''O?ѣG_tiΜ9"H&_L@[… Ciɂ^_~L&d4Æ f.lv ÆzzaeRZ2vX)/JҢ"NwyLFܹ|r+Zrgƍ6lP6lꫯ"VXQ]]h򜝝 !4VPgBPEf'{sss}||JKKF#)dfRRRdd$?xlИ@ǁN322Z[[322l"Dt*=?9]\\?~L>o޼/Ǐ3:2ǖLVXqezBCC*""b޽]܅|+Qm#innT -~~~EEEz&L 555'N|qXX-QQQ(//b+t~~~495VPgֲ@Sd=“'O.,,ugh4zzz,.s@LKK3f ǣ6=zsIuui̙3G 7ƍL-.8޽[&9ٳvrtt>|O4(//W(d۶mvSc:M|>_Vj5ԩSƍnnn~իWڈ:n̘1fF& ㉉RtĈz2u`T`V+ 1 ~Ḇ%//\aۻ8...nb!wvZcrS;ٳgV|bQtudRGGGKKK:.((֭[\nEK|0 ,:;;;.PƊ)kMTT*>>}]TFP< ,mɝn X15e- |FVq'y{ʔ%JR&566RuJ.*0#F?^777$ .H={ƢŔRvV`,1N47H LESYYG]B1h@Ӏ*4g}N\RXbKkdر,ZcC͝;o+yӧ.d`INN8p {KPj=* 1 {LxiiK(TF&-n)QQQ)))NNN+ T*Urr25#痓hrrrR)%8qw{z@tt3%%}\T*ٴi ڵkwޝrJfL& 59z=ڻw/MاOjh;w qLSsoajkkw9fR%ccccccMfQw n޼Y^^JwvcrS;aߧmuܸqח|:y̽%wApp0m35jH$={vyy9! ,ZESH=Jr޼y@Pܽ{?eH$GglL&JSUUU><<\,{xx:uÇ$>˒MtpxԻPuc9t|0nܸsLwJL5MhPljv ]{㏡O2E 8::~g]7hKS$VаaVXQ[[Koܸ1m4nt?\*Ju֑w8v̚5ȑ#4ah:&wa;㋊vtX/{Pp-!͋/֮]xb_!Fq\6W^e{;i}דi#O>bd}mllػܼ'Oܺu+aotqx<;w wm{;ieU`esTx@ :T!WTo!K|#@ %U`@ ] @ T!@t5C jP@ ՠ 0I!r4h\hQ؉tC : *x.#=ǰEurP( (..&.((^^^SǏD-yp)$$D$(J+F<@0a„˗/BhB Ɗ\`fooO_Ps_¾d.CO(Ο?E@t;.\:t(MI{[BBɓ'i6@ǏY Z^_~L&dTKsM& """L0lС.\ /1S9s&%%4 / !N5knkג9+++00l 7v)KJJbΝ;">\VVbŊŋBhB 0s>ǎoѠ###On 4 0ؔAi+))vb"N^^{w[:;;s":O^rʕ+{1nr_ׯ_>| L0k?T`26,,ӧ,-1 իa6lطo_cc#{vRTTh"[[ۈ${{{ D: **2 ܴiL&#XB8rHhhh{# >Gx<^zz+aЖ<odĉ,ݡٔ񦆥yrbbccR_9s/۸q#Q!-Y$** ð͛7/]#G(_paee%K!Cܾ}k:--ۋ_g$&&.Y*qqGGRN u@.$FJ?~uu)3L `0K[8;;;.Pcuj)Ɗ\D(D}mvKW\PSȺ:jWzxx/¶mۦGtEEEL.8ŵ ¸|RܹswܩjwAKrtʠ!8VĪVP,Q*2l8r1bz}ss3yJ"%ɳgXT0RJ Ԓ2PXX8e.Wϝ;YYYIJgX1رc K9rdAAm,x<ދ/VT`C%>7 iӦ mI5^,SF@lx(LݡS2G%%%qqqqxvvY߿P(VZE+s->}jvtBf9܁ ZѣBlIR fmT S(&--M.[R뛛SRR[XAZ BĊ\h4Tb a'N0`{ckkK>CCOǓBOOϔ7bryLLR|˙0-==]קrE²dhSfj!ʢ.\.&!p&L_Ι3TЀ&wwwr=<< :z}_~eeefjZ;;;N{nR_744Xgz}VVYYY!++͍8^PPhovoooٖoBXtڵkce%i|@ fCx()vuuo Á mhh6ma555qqqiiiϟ?g~رUV%&&ڲ1c8òF8!AwSnܸK>Q-9-D"ٳ͖4App0tc5jZhB Y1yBTΛ7O (wXmBNLL w Je2T*MHH OUUUۓpXq) f;(>T(d۶mV쁱,ZL-J ZaX;REnnGUU&󠢢bʔ)|>ԩ3a,ӄ۷of>蘑AUT+Wd7;t'L onoob9[nDǏ!!wI눃'٧o_.7qxDccceeɓ'n݊a1@ -{iիOHHn[/ iiiݢU`xEm1U왻G5.,{A@ ^M^Mz@̍@ ՠ @ A@ DW*0@ U`@ ] @ T!&=D>R8tСCin:K1SrPP\\Lyڲˋ{yyPO?~\$mfooo555DRiiwK&L|2! ,ZGj`aZBU*+Pٗ e4 777^~zL&"##nzBBɓ'iΪBlʕ+SND"( ڵki?wܴiӄBSDDo[d(VfDk׮j&Ơ ;Sx٩Sb-Æ ,Rj:%%eر,)Jt:e2)sN``[Fdƍ6lP6lꫯ"VXQ]]h򜝝 !4VPgBPEf6\@̤H1 i:MǏφC IDAThmmزe 2ai;qqqaaauuuuuu y<^ BggӧOjZ}aÆY=$$ER%''+ [աOJJRTMMM۷o߶mI!!!ED绸?^@FDDݻX%[㗹T`$b*9WTTϟ??aBXSS3qǏY'iZ*gʤ[nPWS8eʔ|SKJJ\\\*4JPONݬ;yOs>;rYсU?T2o,']JKKutt,--t[nr9KKH$H$޷o6e)|` @`iw+ ٙt+PkMTT*eܹ;wj;vh5 S \~Z}A2Pk2:&9sΥ\HH3fˀ ,44tǎZv#̼8钒BCC^xq ehhhbbNKNN&_`'t:݅ bR >}^ &WW;v*JaPk24&v ݉hUUUYʦMVXXtիi[ݩ158ښLOIݫVJeBBqR<<<;T(\\\D"ѬYIOOgzGF Jpp0;d=J+C3N??`{1e[w*Çrڄb=s̃ꌌ3gV^KJ,-|>?33eFW`J8c +SrL) d}.agdٲe'MZB,,,&]1XDaJm38j/^% qAW*VW`%C2hnܺukܸqbx˖-"E`v6IND"!RNR&4@dV`b04=ljT*򢢢?TU*D"F@y'P/8~EaPkB턺 °]vT*RsNwfٲeD 2VwgƇ98 ̭G{BKjj* 5iɒ%_~%MT(3i$Rݝ;wƌ6Т䮫}||jjjhrZVH|xzzY|)M-w!ZXA.$SFƊ\@nR)1bz}ss3M{[H$Ϟ=cQbJ)UK;+0SKvʠARXXHq0+|1;_f@dV`ДV`fe@3o{ wbȑ# LZ :(B/)Fegg84 juB쌻",Z-v;V"A+0B2:zAl*$CsoJQ":3]܁ ZѣBð03[b痓hrrrR%_$0 S(&--M.[R뛛SRR!4VPgBPE3W9hp hMĉ ^;uIgJJ 1r<&&FT>ydq]斞SSS"aY2)a  -SNH)~^N fˀLIc<<<y-dee >hZ;;;N{n;Ƭ,RM>&%؆iӦaVSSi)7vS(٣R+H,~رUV%&&-놆G`;GLM1բ7.^ؔIpDZZZҜ -7o,// %BeYYYz"F+Dƍ.R7o@ P(we)B444 `r5jH$={GXAZ (b I4즂gɝb9BvvL&J 䩪{{{RR__.=<sT*J׭[Gܙ LJ Zkj'M޻wobX,Ϟ=ign[rL&z:q\r73pJmh3fF#_*y~7XYYyɭ[bFz@ =׫W/Hݶa m S@xea>|.ZxI0"^ Tv C ,]nޓ @ =B})R U`@ gfړ?0@ D@ @ ՠ@ =T!&yy>}ťSS'C :v \@;{?sE?i ,??_. €bBԖ^^^|>˫z"%L&*HT*-n)yyy>>>`„ /_&XAZ BQc5\`fooO:Af-a\K2tB󛛛YNtt_s)ׯ_/d2Ydd,OHH8y$-Zmg;W\:uH$D׮]HsM& """,n)VfDk׮j&Ơ Z2gϞ:uX,&2l0̒(efA!}|>_Zʙ2֭[...555Ԍe LoJcII q]F ʃ&Mj?oͺo.>=pIFѣ}?T>Sv[ϛ0Ν;7oݻw޽V`dЖkii)1Nt-\.giIrfA>o0A X1yLs6VPg֚rqT:xWWW3m>"(**J"x{{߾} CM!### ڮ^A ۶m^'=zTy{{3U08ښ/ ZZZI-sݹsVݱcITi 47rjC flϝ;zr!!!d0u15#p”:pOJJtIIIS/^qㆋ 244411Q%'',Xp Nw…cƻ8R N>] B@SS;\]]J%0 vrjg;UUUD4ܪ,NeӦM+V,]tմwZpOLLlmmsssxҤI{UJ2!!AMRT*Diii..."h֬Y<'3#%88K+Çrڄb=s̃ꌌ3gV^KJ,-I-[~Sγ@R%&&Θ1* E)EсLølmmP(f-N%ޢZZZȗtrWL,755QGCSaTT.D_@y'P/8~EaPkB턺 °]vT*RsNwfٲeD 2VwgƇ98 ̭G{BKjj* 5iɒ%_~%MT(3i$Rݝ;wƌ6Т䮫}||jjjhrZVHv- JL&kll4< rj.0cuһLE3>-r(SL)KءEQO1zzɦsH$gϞ`v1%~(S `Vjcv6ɬ)fYc#plEQW`䍛#G #:t(Q.S^Lݍq<;;iZL;Mw!E"Y(hZw?wWZEqV`݅d;4t%`j7[&AG ===t:;ܹsRfD<;p@B077W=zT(b} <+lIp __XBHMMh4iiir܊,"**9%%ɉBcuj-TUDRr>~~~999&''G*X Kر5Nׯ_?NO ===SRR߈111Jɓ'˗/gb77t^ʽ ˒BEGG/\]hu𥀜bwwwBM9WWtRh6'tL`Ydee >hZ;;;N{n;Ƭ,RM>&%؆iӦaVSSs+NSnBlP$ٳGR577ݻW"XرcVJLL%Z TMwb2EEE7n$v5 ^N<]KKKZZ7ozQ2z謬,^of`Օ6qㆯ/D6ߟ̑`@l.E5J$͞=ԣDB Y1yBTΛ7O (wX`;111?`|xΖdR4!!lϓ,K(487~Luc1;_f@$!!\YYD"ٴiyGl֙5:a 1֭[E"0>|1##x_$m߾ޞ.X@,SGt?\*Juw4àZ' ujg;ݛ={X,ųgϾE[C8ҢRV\)ɨAC;4t#\!'O$kmm]rT* AAAs`f:rS;޽{$~/{Pp-tdq@ = k8á&9@#A@@APQ$5c7q *h~x uըQ1/D9tpyU^z[,v fy˖-<8uj=tPbbѣG;*qtJjkE)Mר3S XDǺɯ>t޽K.%ཐgɒ%7ogzs*3d2}jZV3)eڵ{ey6N'^|E\.'Nx):th2{ܹ<_wX캫WKrk$Ŋ[/B?2d/u ##%##/9b9:AL\jUlllMMMMMMLLڵkˋv }||߯t: 3fSoNIIpucǎMNNnlllhhXbŲeMJJJ1c+nYœ??Çzҹsnܸ9%ܙi?=}Eef7sh S|ddd~~d:|1c(aUUոq***bccKb qB$((mh4DFFL_a ,Ev^cUj]pA%<0_DO}LKK1b=]>C(Iyyɓ===iUUU3gΔJ!!!gΜjV!Irjzذa\( IDATnСCQJJJ"""JeAf`D"t!N#|D;^v `\lR())aY!wUn0؍:d/-iوO]EI2U8$I&&&*cnڑH$?chnjP(n5jԩSxQۅaaa>lLSM?>''G &8Iss3uDjll_4<-[:P*]iÆ Ǐ՝?~ĈFiKXH ,===,,%gZ*R:l[ҖP8*$B`00 /GBrQzDµJXїlYjju}}=W-Ka|}}>l2l֙CT޹sG-L-lMGG|^^ux-maCٍ:`[ج L 7Æ ͵Ff6x`*]f6=Lٺ}v$o5 kmۄ\;mu3Br:Q0 0WWܰCW!uw b&VI  uFy5k_T\ޱBl߾=++_~%e2YVV`ؽ{L&#VI[BDDDlڴIקi4G;JBBBmmmSSSjj7% /X!Vu S"22r׮]z~׮]* ޷o_:KqssسgOѸzjZEFY|V}om[.&iӦMkLl|\.>}[)_a+Q  V;{lTqEKBcǎe-c-˩~}vZR֮]K*++8q-S(V~-cm_~=wLDX,N665Žx-mr/_W*}}En5ۂf69CҥKr?OqΝ^^^tԝr|ŊݻB`G_RT>2zjիXamk'X;)r B>}իW^"IZf[ vܱ8 /dgg 4ZZZ}]J%ɢ^zo{rʕcv"*V^wwZ>C,;"ټe˖vUj:t(11ѣg8:%u5FC}v_^[___ZZwޥK`JXkB"-[|gj*c Bo. E,SӻFGInݺ5eDBשdgϞjV^^^~~~III޾?Uƍ\|"=e!_ܻwX,Fkƃ LAAs 9Y bx ;v,0kiѢE+bx֭>>>C Φ7n;vR\bݥDR9f̘MfL!I,!i!!!?O<.Jy.e+VP*ƍqOuNefY=VqII 5Ff8'T` > ,D"D"&A :w tom/n$ ))iL Ȉ^^^$Izyy#FcTTTAABH lݺUm۶M*2l6#fs$I@Xkmu$IJ5p9s攗sm>rAAAԬttS,jj` x{aDR*tJT޹sG-L-lM!kDhx!IСCAAAYvh3FVH6lXnn.k;qۙVPKKK2S!fs~~~rrrTTɓyĖdP(V]Ne<nձC3FEEEvaaaXX}v$oo\.7Զ`,[mVVV]Cٹ,%%eYYY/)ɲ ݻe2EQ[!t%Kk^Hشi^OKKh4VwڦT0Wb (SRR 5OXDFFڵKڵKRXBĞ={KKՅVVVe4{i4W^M RSSh4˗/jo~p% ==d2mڴIx-)2&B{fvv8cƌOfΜi+Zh`7H6UgϞ/_[`0±cok=#Gܼyd̴[833l6gffP$sssz͛۠=44nI .^WJ?s?]X' 7 :vkټuV6'MXWWW[[8yd VZzjr|Æ MMM7nT*$իB~óZ8)gΜ  LN%UJE.EGGKh"M'k׮ÇӧPBZ( i󄏅V={T*x"%cDz~Յ|rjql߾]VTkһ&NAKjkk E``}*U쮁]~=""BT.[ k`Q!z길8n^re BPL>իڤZ4v]G Zy_ܱj!%8V۶ZyXC%&&=z^)bxO2wܯUAj#gޡ\usvvrO/ͭ/--ݻwҥK pi#~bH$H֮]{m^Vs!; h N>>4 @@@@@@6 v2F#&NXXXH ſ,,Hsss{\N,,,dsijj 3f̐3fjv%;;;,,L*3+[bXEbXA[l [ZTVy o a'|,̯n k 1>>~ʔ)<qa@ ;Lف@nzmǏ+JQEZZڂ M/6N.ؒ$99ĉ'N(//߰aCGmڴIVVVV B$I ?裯.yʕ={:tϛ7mܹv%99Ãz(!WX` "|,lgj0%o6&&@ߘtxz׈#(ɭ[L"H1112,44ٳ\-*b899/777))~ ܸq#22r˗/wSD vv` J#GܹsS¬V+d2j0={裏Fp̛7;v{G/sٱcGXXX=f`ӧOK |򉻻~ mᛣK @6HJJZ`SB$cqqI^^^!UPPh4tIFuVNm6TJ %lFfZ(vVo$I$Cu W\زVHTT3gNyy96c!Jehhs-A8q"22p!Dd|||MM b9yd`` 166vٲe&.{nјUBUZZZrrrd2٪Usrrh-fZf`Xr%+!vv`*ԩSf֭[/FEEEݻڻs΅ bal>}cN<ME>= $fѣO<~1V~h(+**r%ehxTKR0e$w~.9_T+_fFAϟϩAΝ;5 ȊiӦm۶MeddL6 !hѢ,jJK(/B.S.C566&%%M:)d[tݻK,4i0c!HlҒaגzkՅyyy$IbJP(- 8>Ř_n$ BMWW*Z!j|N֐ac;;GG$'O X,VU\|[ 6`޽h) d`BsرLg`RG.75X,7o~:e!% ! 2*5IR^Gt:*~#˛0ax PIIIPPhg6c0S+K IDAT[lY" ^/HV  NGeVU[]8 &ޥT*YtRy*235e 4/|)--MOOӟD-w!'cn1.؀)**A& 9oFzz:k d2͝;7%%EHS:NPp[[e5Т"j0,,L@QRRRoߞկ_?2,++`0޽[&[;+W"._dɒ^{DDDlڴIקi4G/$$$655z{{SBne-VUDؘ2dy"22r׮]z~׮]* ޷o_Z.772.سgOѸzjZՒFY|V}om[.&iӦMkL֐ac[BŽ%>>W^>L /^~w}nt &''/Yd޼yFY'D7 iiiZqqqϟ7uuu֭վxl4iRbbb]]]mmmbbɓ:50W :{ƙ3g===ùb?zV]]]ttT*fe׮]ÇӧO/))X_a݂+Q  V;{lTqEKBcǎe]V˕J%3jJZv-lĉ6..NP۷[#""Jeڰ3eXC& `X;;vt]C]m Paݺ9;;W9{חݻwҥ< XP(F4.T=jU+ !-s*$@W@W@W@W@W@Wؤ {@bZ͛w7o}Nhd2ĉ )w%sss%Ipppnn.s/鏅2lΜ9MMM>YbÔpׯOMM}wyq04^$!!aƍ,kBBBZZZyy9V\\fs]]u;$իL /aÆ洴4z4iRbbb]]]mmmbbɓs.]zuĈm3!g S،̙3ឞEvbߟy=BuuuR4::m1k׮ÇӧPBnZ( i󄏅V={T*x"%cr/`Y|Rd}uؾ}ZVTk׮wM8Ã)}qUp]~z{2!v,;vx<==CBBiiӘ)U6`a NMX~=v"oܸ_V?9aToP7 aOؒ_z5ST*LEv̙ɓ'h|U*Jr3a@;֟6 k5pWϷ[~DTv?Xsimm]x:jΟ?_vttEۦ聮Nک}ܹ_}Kp3u5FC}v_^[___ZZwޥKtby͚5~\?EtvNiiieɃd`<Á ȷy/<@հB~p8;OH;Qt6 @']'fxل>6_VT'lNW;V4FZ!DVs+w9pڹl7Go53GK BĊW1 G{:z<6#Lr{<3Pp(3Wj z2bXgoR$ajoOF>ݹcXF^I%T:-XkB"!xVD}z=XKh~}{uZH8}Zh+Cvv`@?D! i[GWzp yzfbx?}bufuYbV-j jZ_F[؎#OՌ>ńjE@&jcnSO[EeZ zcNu5[m= ɟ'{w8-sbʝ.΢7뚇{Of3WjqfjGiuA)ԭQ1I0BpER7ymP[3(u DY7Y%%;lp,K7<خܜ?a[wgђ{!,-wԷW7]g֎DXmb٥O/&#BاkLU 'Bgl**One.`-vHtr/6c1gz5ͲKP^ ͘[ ''Ѳo Unjbղ+T t Ɂ⋥ G~֟ha!z;;ʹQJnV%3թAgԖVѧ+]8DzaS5!m)BՋ僌=\C<=5ȓ-džy30&Cyˍ:3Oai^ȹbnT=1@l2[`Tq Kw]$GG֯"4zpӣEg`2[UF[lY" ̖ӷV6Ycl:qj=qzH>pxֱ;G}Ho71=:D?7~+otqvZ>"DϷB߆BAu[Eg0sEj1-Nl b+YCvvؚ2/{>Ҿ xV%kt=Z-wԊ,nn]7 lta}`ww_v g'џ#W7rN_cI~ӟ%a Ŷ?X2{}7DGO:t(~/$#c}fm1v h ]E*FBԳA爛_]Y\m1{'SǺQ۬prqLg'фHz׵M8WL;1@͹o/9r n"vIepaHf -qg-C$5ŽeFb+wGڇ枭3Ppnؑ_7_tq!F00C`A{iω W!Bޓeaw`8_O'ѳܮs[[ (G_v# 6/}}}5?dC Yj%f>b G}|?lٰ 29  vFLPIuHK6,@lʿPy5Mgr̭ \ʦ@*ju>={T6glnZbQ7[%Bj G"̭W Y`uKzzrfkvsתg DYάd"/w]wձuEFݷ13\uoq Va)ngA~^"83.C:y"@ły{PH VQuRL*.f T j07,-w~jo&e'aDJaءH|9rag`+ogA`gU_9Ԩ6j͂XɞoEn@{ 0+W_`#' @pc!!t ucYZٗ&Y! E h30>}d,X :) e`BFT*X, F`;BGwLWx!Ba&?Ag„rYb:C#&k5Fj%2yŭgHɉ}C+Btt[L~o98k֬S0AJ .|n_Y[Hm0AAk׬&~tؿp7oLV-$~4&SQ@8:tU+NKn$R[?.o!oB (^? 9P>b=ӵ55ܢm.-$Dڧz43GoT2t3%DO>H{_Y20_n(| ×.%O?(;:&W+\]\4?Q&XqQ!AAB*uAB[Ckn6-g^=\Hd f-uwD!*OB$o>p2={Qϩi[tjmE^nVN"|]tS>%o[q;׏T\jfSn+jh1]ƅx_p;Z ZyKzck7ݜ_!åG7gZl &xŌm20=].uItNI7Y'=t9;7L o6<ѧOC@DVNV󸛫ՊӼm1=ѧWMn7>ѧgbEe3C%:d[knn7a  &J-j59]]h6Ljl`` ߻K$,^z$ V*DEgmGWۤI3ꌭ-VjWbltml 6pc{8;tFK A͆f}hqo m20Py үZ\ݺ98;9pȉxu3]) O\Tk2LB|g>.ܩkJP[-c8cը6TWWٯ՘ؽ'v I,0n]ik{O\ijiGy_2062|d ;s鎰`Z f67䛣G6LK.۷oݝE"ѽ %@Wsƅ -}[q> ~S^& IDATxwxU?3Pj I0.E5DY\d-_DEE@V).E!HH%>3ǁa- $~=yx9mΙܹ!DRP !$\Pf/3;l RѺ' j\L={B42RBB)%Л/hԜСC%eeeFk4<NqxwvgcHQBkN~zjEJq^j!&RJ E‹ R!GTQ8V][ b.X, T*^j(RJ)8l_=Ap8CQtqORjVX)VhlVKB,v(%T馵xDR*r"_P*fӧZh: TZTXXj):m,L/8cUp8͛g(8NRi4ӧXJR~~~jUB/RA"ڭekVv J_-W Q2ݧ(k K1 f֋fByF/ 2ɴl2َ=:wܫW(V*$$Yffk!HyQ);͚]lrٻ2ciQ^VR⃽ZWX_NzGmѽ%d2T߼ԣתn2]RԱroC)-wG ^fYvoYjMƷ^x,h-19l1u͚53 j5.g@ (++X, /6mTRR׹sgJk>&k!@Eja+,(!L6KEyICMDDZY57/ )o\}Wk;/n*LG?3K%ef{+fiorg0l"F҅Hj/~U/\ZZ|M:S  RAHV3;*<ϋv׎E6B};r*N;ޚ˾TVwr$OZE)xjn6P[nesvI#ҭc_NVrQ`.3hZJ;ڀ___v1ThA‚$/vje;u|X(%+6[,yQvU!ǰzS񼃷EJ6A70RvGd`΁A~ZM8ql"RBf`Ř.zyp:vZե<#ץV<#Tel7kHhN9Vַ^ iUs+j ץEqKlNdyƈT*BgLVB7#ۗ}9Z !#urڵCGșr]7SnM#|P\JITVKl6\Qayf5t:v۾Fan4EQctON8OnN8 RmwB6yMx^KAEw٨(Y#4{]+" PvҢm(ˊĵoXÙ(tp#zͳ#j2{Gͪתү́.ic6+'WGȀRD;whjT=:x|@0YyOsms6Wuv^@||j􀸖n;s)BQE5cCe^)*ݘ/6!j٪<NߙxjHo@FxG~u,b_Jח=H 7a_'U{% RJѣG٣VG:b6(lEAEf5ydiU r=m$Tz w[NQpym^vT4 AaWBVTfT|]MBL /DQvFtK h4$H7 7~&}pp8XTTDv©JK=,/Ba/ ^#0ɭ%&(R*r>) cR4[n?Ԩ o.Z\g(E*ksRAvKȾ:WXj1dT:S"ec/d^.~<`1aO_Cao zmps_aINh,|c/ȶ[Xvodk)"Av1p7N[ouBӪ [s 6;W,.i.gHVq'R/ +(a9Q*:֠@χsi,E'd]7z:{+Kqm&{40R[xvQRRn5T*áj5+R~=R_V#9}CrΝ;2y^Ν;w) J9) i8*HExKAnynͦTPX\_=1hz.nwV'gs׾uPx?9q+r{%7NNvÙ d3 ZaCסMPXh_OEw ?u1׶ey?Uz!T%ֹuV^V~lhZű0r3}XTR>ӟq7:dG|FnOXtRQKJ+,Vǽ7}tHZ dDRکM`v~iDJ)ٕ[(gN(=d,KezW?2#da;(r*F}d@G JD` lNfTN/Ah4zdW!yјmobNEQa+,}rnjM~%lK;U^aڧ+bNA􋬠TD^ ­LV,0 ϲzXY*ToX-Z<]ٖG; vi ;wd``Nv4o/p] $eee6lX,ffHF#F{j0p@3fv3gc?b*YLv}?jVnҎj2,[ ]vlox{>\u"/Fju[vUڗDTjZaݮ5 ,CAFUVVVQQa6ۨHղPʔ l!{mwjN|X͊<߅5U-su;gV]*F*F~>}x)]evQT:Nѕ R7j"9JӪUg)բEPyy2/^*(vb=2g j*eGGwjLP9G`M']-r q}xUmE_PSu! YSy3zto28JǵjګG5;T?hUȬ^}VLPPPu{@}V3W![j=&DH*oCVΩv9Bu^jO5΁lZ2z^UiFu{k0Bhr4%T]59mb,Fzs^˕-Uiے "q )?gU\p1^/VlP՝Z;?[!??=>m 8tT U6lmsZeݻh&S" _j1x6"W2H zN^~HipBHa"Z<֯ġh+5!>Z3罎"2wP?(zP^nq9/x/|j}Jھ&rsZ =5wҥ;{ v~f)/󳯋HERqmJM)[%0㡂:Vl{s#.۞Ų ܖ^tW 8irߴv 7T^ >y}O@QBNj_O/ONSwDU{RP:%"!џP_8Ne496JyB@ߋ9%m/y~JSU |ۥx&<((n] 漇_/۷}e N?|+3"GT5B~9YW۩Ofٖ^pWvC pzVJ JJJ_ WziVP٭]ҽY{ݓ)s l!ڭSǑYE G͖,W~=SܩއT>,˖-lK.۷ :Qyv> SBkr &,n?|LL̮]uVs(Ri-[uc1ۇ:Q/>@\/RRBw^wmYm]bbyNq{2/fgq9rKn :-۬X,zC K>tK}V6/vyI5XaXxۦ0e ZXx[јq9s搦DDDJ !<ϫjVۧOn3&Igƃ0UGo[AFq7lG}j6l"SИ4s`90FÂ0BHbbڵߎ{IF'Q3LԠ&9=Kv!RV`+))˔/&uZJ_#DB~؍ҵHjرŗ=<ߑ]i šXƕKu%=30lJJ~cjw񫯿zdj.g`ﲒj kdsPXLl:釒rv;-L~MKK{g&O矿͛7WTzSNzn{^]MaB&z }?ϓ'O={F:t/qZVu7?ޟw-&&f…:_^z5Yf>>>N? p9!Qq ϛg}vڴiӦM?ѣGn0jѦfq(.*.BHpp%9<Ջz2-B*%3[ df^=F3ήp^p7|Sv}С -j֬YBB¾},k֬ڵ^ܹ+ 9h4ݺu;xhl m  ڭ! !!-[4x#G}']dݺu]tt ;wdqqqzKOOg6lܹFܰaK,..צRfΜ9mڴnݺ[}ȑ#O>'6lx뭷,YR^^m۶={H(&)99999|&,W~S3h0FD q2nQoq:!p:T*6=7XiCr!6nսzUmx^TVVidZ:Bj̺ҞH~ݻw]>XCB[ 4x;f o;h+WzoK^9{pndzTr 3ڄ^6{7[lٶm[iio9n8n:c=,qҤI .4L~ ;t:+RT*UٙĈ~~~z2o޼%K:.22򫯾ϟ9k׮j;v/;ڼyܹs?WRKM۟VMT`TTTqqqTT4n6D7F !ϤoB>l˖- H{OxoՇ~$"!D?!_ҕ Nۜ'NS‚‚W3S6Cƕ3oɒ+W\p;fDOYqn$VɜgBnOzyFaGѣVٳ籣GrBZ)2+*,8{ b4~mvƧ,7'sY!C9Y2}ذNSKyտ_eJJ\'>))i99ܹ֮aϞ;G !$88ɓNS5{'x-8q"(($jNBju׮]Uӧ^ؾ}{Jnz>;+4͚5۴Ǽܜ㿧O0 vJnt^6D KJJp4FV<~Ji\\`0+J]` y:h7n>::z^ntt7np:N :w;ukV$oyJH!n~>J"6ns-Zd2-^'t>4lkOҥKFQTX0b%-v;__ߟn朝Ok)e/o\՝j8uXqn#/\#0IH{`|bQa|||\w쬢‚yjN{3\Q &t|⋸|rРA/fg $5g۶m!Ӯ]Vړ'^t 68L>_k֬|<MKT$#Uڴ͹(3f 3s]fB>"dp#6n7ĵ'ngvȐ!)_|QTX%:J}R2]t^2ާX^.m\87?Oׯ?-Yҿz[6lw甔dgg?N>}zAAAii|Э[7{-]t׮]mڴrFFF\bX"22%3u5kɓF5x`/-ZpBޫWA '._&y_}~;p8dd,=<<|-fOz*;c5jurY3gm~Kqw8?lG%mCLIIܹs\ڨ˙b6SRRz#Ghy#O~~~v}{);jȷ~/ ; +?Wc95{GHV9YUO~^yyXnj3BHwߍ;aZ֯Cǎn~;4NJROs7.2 ܨTؑÇzhz)x=6}4sIIIC.T׆ܶ+)))aaaaذaϟ3rJ}bccjulltտ΁ׯ/*,X䳨(V۩Sŋ/CnmVVGFFСCu:СCSJn4( F%FqԨQ:k׮ǎJiݝnsm<̟QxuO(\ڧ;V1H E!$;ഺI8]vz2ҋU ά:]{:\qq?rӦb]?ôF{!D~чR)Vere cs#.8B=3=⽿zFݛ*r#:l萺m×V=y3_ɴ53Z?[,ד#ƌ7Ι3< % ~@i 엹& 3X^dsXR&\Wp @iv*$%u4 .()L(@>'MVr9.`m\M8Q|ְ` B( X8u=pB+Y:`8^P- Y88m۶6mb)k׮j;v/ fG`8S( ^zڴi,ofzA)gffȑ#gΜOzI7o~W/^(tؤIf̘ѷo߇~X~rkذaѣg'¬[\Pp&%''GfLz׭[5sK?裷~޳zrXK5@&l6R7F3u>`R߉W\ H:7qܜ9sZnaÆٳghѢe˖?ː7b^V}VZmܸj jּy~vIz4h`8q=wرf6lXFFlP]v=++kѢEO>$׮]^y^?~|YYق U`5Ϙ1k&#ޓy1VʔewljWƎ[[MgKn4_%r=Kzqb1 h4iYՊH7LR)ZO5W6U_.&9јq9sF`0Bj|Y(L5[4Y @0jԨF133s„ 6h"p ?<<<|>kNkp@Ct0!Pڭ s`Jknڃw=aFИ P4P"05lݺu]tt ;wdqqqzKOOg6lܹFܰaK,..NLL4 FKۆ8sS vARIz>))Kqyb`` %@=PG4qwʹ!)/8uu8燳OXo1 KsN}fY%)h}2eԥz*##l6[.88%;ɓ&>{XbͷnjZ7o_|)SL)SK^6DJ-jժ.]ήSL8qb~!RNsS90bz t钓#t:L)5LRtm1ZSRҩKת6mԻwo"O I~VZ8u:O9y8KOXMtK+=Vߟ:}􂂂>[n,[t]ڴi#匌\rbYbEdd$K3ffqө8{۷os`u{D{: OnmNn?akesN}΁q,Y|dyaeʲ'Ur9!^u G)pk՗˟I~h4fddlܸqΜ9a=q@[o@90!P"05'484D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4M]wJ8+ywYiՑk?~/پpݍsӃn<2p|}BF4-Ky1XBֲeyb:~Uuv/y! -5!ZƖ^gw?B:`Bs0sm}ŘEŞFLR׵WC꼘\ |AbV{Ql٩wip\iv ]{溽63ܝߔ IvuxAnꕸx܎ɑF[>._2A:SEnq7S^v?/BV/t#l9D &}!^n2nH~[=ev;w?;怌["jUibIL-r+ﻐ?X祃pL]Yrfocs7;˱w*RCo7 y? -[~UȒZ,i 1Y^ϦЇ}EF90!P"0!P"0U/[fM׮]z}ΝWX98NtRKBHqqqbb`HLL4QFaÆzkɒ%۶m۳gRj2X;U\\;T Y5"y-Yt_}|^ONN>|k۲eˌ3|}}_y-[T \5"Ǐ4ZݞUڲ:t@رcVVVջ39S111˗/ j߻wURO<ٿҼyTm۶W^%dddm۶hkSLIKKs8.]4i#F5kVYYYYY٬YFrE1c̟?l6ǣG4LՈ{YfMYleʲ!FͷưHh4*JR̯6P#8=t󛍸 4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`JC4D`Ju$-B_^?g8Y7}lS#f9_}trBM?ͤ n+ RБw{GudB!7bf)+g-yo3$zSSȉMs[W$8f)Lھe`y*!䟏Hp̜猧Hھeˈ$y?B޺%/Xf"ΐUإkKԀ.]85j̳R~V.6Ҍgf-6?!^zt=.6:7!d3fņgVM͑W+.=!{aB'IC>k6vˈ$i o!m;L6.%tmfvl[%ʵ-pX>b՘gwfXur2GBjj8E!So< !kgv̬^"R_PG6 tVISVENޞrl|cjn[ur2O3݇ӟڒe{|ڙ6=_vE̺v]YPGiqxN!1W gi@Vΐˆ]lxltBo4#ܓ !&ؿ*LYVM͑V#y'_riFpg~OR9{.%/*$7%{Vk,U&˵8be̓]i[o|l˻!.KHXfWR)yAvd99 ]Rz%"uR^M+2~X8LlC?uy)iv!)^V'pE9v4RicvݐfSA҆|~8IʋHvg8҆৉84D`JC4D`JCE`k֬ڵ^ܹ+X"qi4nݺ|MFvAyZkSRRjWY<8Z|s4mՈ;bUwt5o޼ 44)j\|צLp8.]4i$/G1k֬Yf5ꮻ HT#{f͚5yd??QF 41SȺKj;GuQkkժZ@7%je}߭޺S"sYgĈQam9SQBȷkkp%ʕ˿ܺUkݮLJw~Sm۵/)5:Z@ i`5uDkW3v984}xMf߷oLC=zT99J)uw_ZJqOa\{DQTng8}ᅢOwF#""iU]w?u]Z2޺AS"۲eKrӺ珴m U*< q7%""rsnfMXO8hN(.\B=}` *fؑC={e)@)..&hѢ;Tk`jn~񴴽^cJK @4QPBE*BfSm>ԽW/)CBk1T[72NX\\ٹKL͛?DGw駟 UCovZF}F`'j409mN{'.]VjҥK|IN祬{\UQSˤ(Yث ( x "o)"*) "__"R䫥57 ^dyw9;{᪞g9s̙gxxxKsݻڿ77 BػwM6YZZ8GH0 ݻ7YxN"%>lccӯ_?Hn'B:DF1_XYs=<s#Yƛ -33 عs0ʚ[[WaGG|}ɊFR۷/""Vð%Kddd~2@@OdgfLRݽ3y䨵{$sn$[ bcƌ)wV{$WV=( 1 [22%%AS~F7477>|xJr2!hÆ芊 ._gwK/tɒmٳO^ ðn ܯu1kv;_lnPЕ+Wn߾~Æ>)&1 ⋍/22v۷/Uʈ^zwjG`KweKaKio`-BԩSTj50$" ۶mO?bZ =Ν۷o5~ǃoʕ|>]_~3f̘;w.ӧOY޽{~ȬZyڴi1=auޟ,ZDT187833 8w2a+vӦʢ$$:t8#cg'ـh?>TVj:.>:;;gdd˘M>l *t {EJ%}LNƸqnô=/ [jW4MEepB3#CV+=yV(iiMPj{tٟ|Ivզ&Ee:RKÈK}*.G?.99ZܡT*ҶF6Bm0ee%i/PT_~%FyKe{2cc7}7F98":h蚽ɩ|Svv?0ݷoԨf`+WF67 Y/v 6n?#GY N666J%%577ۍ>}gRZV-Jj EKy;_pA|\@ i? Nuԇ4G5]혎{/jmԊk./ `87rL %$$j|5kFl t #o4iRCCCmm;52hР'O޸q~ȑ7n8yAXrԶmΟ?曃|$%% D6BmOyǙ3gNX,43,=zĉxO>Y}vSn:ʱ  ]J>]Yj]Ŝ{ a؜9zuL~e=3 þ:Wu΍$,59%j:k׮'N}-$oOK\ñjư5!>tbBaFs svuը5a߻b*NBRp'׷vW7&a:~UJ/7D2=1AMEo644TVVmڴ k3-xL(kGGX6=t.+mmؓiӾk>;NlQ~"o\orGm<;Zz Sy6# E۳ gP9kϿ#xyA&M!T* ̦zג+o0/!:r%}|Ae,IwM>΃>\.>s ̬W!C~5``i;(ciOsAtCԍx on㋿5a]z\R̞^z57gĶ΄mmZpxe,wl7N>Π[Gs9+M݈g#.ۧ7vWgu;ꇼ,.sss-=<=--]cϋEmm͛o=\ㅥ^73ձIykBM݈g. ifjۍIӭjjÝy/#xyoիRsjAm͓W.;ӷKQͱ=sA4u#N| T*Z4~e#GzLֆ ssA#FxҲ_~XB& bffֻoPp>m%Y}CPE  ={n@ S_WKnQYjj377q#gh@ ?y>|hMM/%+G!z"@ ;2L,nkkk{TjZ*633hܼ74z20.6Iϧ8W-T遱M2gÇ&75kUGI3uL!o㯽n9aOa71Ѝ 6 3Cc]v2 L?M]n>W[wN Xүe>Y8be!-տVQQaaa~ͤIXtw%T3 ''nْ0o\bWs\R^dɵWu57ɉWJn9AsLO8pիWSR&Mh=n4nf {57j?dif-\ L2W=ĥL!# ?yBj]x1<|٩S_o¶{uW]蕁vEm))ɮ}"K̿{.UHmXuЊ[^- +khɓ񛭬|(ߟ\t샪ف}7n\M ^*U+_.߰aƺ2 w#_GFxu[[={PkԼ)ȑN]'NѧOԔ.3ϴ+_ݼy αEߛ3Wz8JX"X뭬Zk.@@XgWzJ_{SUYY#4 qݷߖYŚf`39s|}x-mIQk?% D\9{l_4ڵkƍ O \ֈarYccCk ԋaXUVVL2%rj^w8ٯ˷U?~\zU3>tmYm.tt1wt|l`F3-!aF!l+"\]\?Ǐ[;F:|k7q7~ૃ? ٽ؛~<Wz"bUᄏ"be@t8tjĉNkEn//){faWV߹い&Za_~9]u7s͝zෆQYw:0sssxxjJرc !1Ae QH~|uœkkk{uKru7d  yW(D-333BH+n#^qq4u~ח-  4H& K?bsbZ/^zwT*%kDOF͞=+{A)vUT?h+~bTo r‚(VFFt颙={$I[ii@@@oF=zt=PXF2[ jllKlxA :FCldLt][[VZ-y`}2//oӦMg⫉7Llpy|bѣY3f40[[Kb5OhU^}GYZZbV[[;h jS%$?Ջ 7J1d]-4gτs:8bFZ%Köj/h@W:v7B_TVV~WToD/ٺG~(N1COD"Q'$?䆮Q9;ciiyرD"PhBٍ6?|0Zmzg<X25OwG5O]\\.c͓ӧwzߡ [d՗/_V—%===r\.OHH8O/54wޅ JeccC Bjz**.>pPTJOwqqaucZ|7F|||6|L-]S}{R}U.X-&AL 0hBܡR]kVJJڶ;VFDt? ٹ3###3㏌5M ن3k@t:\Go! lhh6lh*n'TK|}}JeDʊJ+++bg#jyzznKjHehBR֭+#W5xu>5o/3f]YD?hGkk6- 5\/aJr<owNv %5j~* IDAT?zoKJvOTΘO[Pᛃ۷СC- ??F웃"u5t(3l]IZja11´ޱaؤI֯@lSL4fM!3swH@ u@ Ne`@ ] @ !:p8m@ Od`RB_PKq\77Bcǎ <o„ ?hll 2+hgBPE P  rrr֠PJ$47> !C2tJKK|󛚚X΃2D; FQB>zKuqS BroQQѴi|mhhoZ^fD"H$QQQm"չ/'x -++S(ƍqQ,T3go#~߲eKWUU#Ynڵk ڵk?sc ZZ B<(++jݙQQQT^{tar. am:L^vvvKKKvvƍY.h7㸃Çɏڸ8___P?8889s4774//\[[[[[6/u5_^?o`ۛaXCC7lЋ=9ȐH$~KI+++ 0 &OdtBLldee999Ɠ ɃOrO< W^ejaVp8666ɶ}Q{Сqqq&۷ TTT"|A8y򤻻;;v/]jjɩVLWk̀O?N=qڵk1JpbE0Yhї_~ eiirJr@mڴۻ_~^^^/.\XYY N:gYZZZZZ[ɓo050%KP%.eee6668ؔT*kR$ vvvTp\Fh4<&b-WB"bСCϟ_UUŴ"fpooo+ jEǣjkk.^B| Ud#GTwww fZZZ |~BBBsssAAeܹ[nU*[li5 C T{nBgP X,tFyAdd$?//{KBAaF={/^@D>9h 5ͻ{EXd)))z~`֬Ywޥ_~}xxhxĴ0}tZD tAR)H3gٳGPdggϜ9K,%Iryrr3uGg`@4_A;kTKQMMMLL0ǂȵ}KvESQXss3qCBacc#C-Y:28T*O>nE"L&d2=dhCk׮?^(nܸQ _'M駟;w\xӳMJ[nAALMMT*} !A!9F!30ggg'&NH;t5B񒒒o>i$rWkkkXXqx2 a,]eeeyxxTWW#rP(hYR4`pqq[dh2},Z.@rV> (I6L&Hh:*?sZnjj"wD"ڄ.?~̢YERvf`!c?Lb𹧲2++'}K1&Ѐy󦽽=e`A:?ʢд4!puuUTĶJ";wxyyXt5e͛7Rv;*]tXZZsss^*#G| R >*aXttt]]]SSSzzR222Z[[333RՍj-WB YBܔ:byCZ[[:$Y,0㞞s߿5TTDRꚞNՒT*de˘09++KVgdd^吡 460 ܺu+&&f…BŽ%**?\~3gadddJJJzzY1aBXtijjjLL̢ET*}xbteo.87~T}٫\! yyy6m3ij ƍCx C¼G5 @<| @pUH@ e`@ ] @ !@t5(C jP@ ՠ I@t ZvѢEmE R)---%,,,tssrnnn]ǎ&4@@(Lh(=<|8Ns& jz͚5D"DEEi4Ӵ1[mpa;c6|g ˛CCC Ezzq㨻ho`%%%*̙3_~oٲe&4u֭]VP]?7jmmϷ#P_A; **`=QVVjI!3SSSXl8=&X 0{---7nd":>$?jkk|}}uU7o޹s ESSӱc-XIxx?@Ȟڠ$ ݱcGkyڦvj&4s7sHB!UBswIIZ>s̄ auuĉ>|lBIqqqЂ Tooo+hgֲtHb'O\\\LbVuuu_Yl 8cff#á3fLQQ!6m59sx_rYD2jԨӧOo۶fȑ~-QK$ƶgt\B( 777 ‰'Ər꫋/{U*cEE֠xrrX,/,,U/8W#(J>ވVꫯ-X[uu5N}]a͛viiiܼy8X;6 S;&|cQtudֲ2mll*ڵkTjBIrhx< - 1;]B"bСCϟ_UUŴp hH?[p30j8U[[K-vEcpppllZ& 9rDR3U08RPP H-sݺuRܲeK{nOC T{nBgP X,tFyAdd$?//{KBA ͧN;v,)[/8k4w}ŋdȵ4;g*D Pw#\Ӻud21<hpjZoN?0++ZOh ۩V M4;xT*54Μ9sϞ= ";;{̙\bX,6t}3f̠ ibvV,]*cf\nNNqyfbIXXخ]ha X\\N]k BСCɩxF4άR<}MVD-L> 46]6~xPqFr _'M駟;w\xӳMJ[nAA00ZԈ}!\ty///FCf`<N L8v1ikk?V^GZ6$fFV\zuPP!#!YU*;D ڦvj(3*ؠ 1N8cKN\eve-{\.ivuu%/oMD&I$.]a ̙3j%hH$z1 f]JZڙ:d )..2e!ʬXSObL PoqVVm LVҔB[ZI&ƍc;r%nܹ3syѣG*d`IKKۿnnK\Ry>aC GcKWFFFkkkffT*5DGG555Be-TUD Ҡ1އjmm=tX,fðǏ{zzZXXJ߿ݥR RIkzz:8R4..N&˖-cìgg第,Zax-І nݺpBvQ@Ž%**?\~3gadddJJJzzY10$4֋Itt;h>}DGGgffVUUAk\~]cj999ݹsf֭Đ󋏏6mQ1 zjyyyPPM5 lpa;cv(谬NUwjʕ+W<=====;ۆ4@[ :4zh@0{rBPkBEL!idx^^^%%h '..N$Ѿ:߿_"⤤$r}}}HI]]]HHP(tqq9qS5{N|C6daq899QB&5Ž󎵵I̙3)U4`a@ N^LHII;vտ)Sx<? zrEIIIbb"N1bDxxxMM !rʴivSRZJ,իWW 5kÇi®'MÅHLL,))['Qk`fDv΍$,9igŋC Zŋ Ypss3M3]S{hhΝ;^'Fڏӷ9{ $2//oӦMtKp8[nEI^  77=333;ʒg!g eާb y @<| @p @ ((C jP@ ՠ @ A@ DW20@ e`Nཇv:jvИv ZvѢE]c80|pZ-[]l-Z-/ғHR>[ZZJ]XXr ;&Kr(Ѝ@@(Lh(=<jïa?d]Sii7ϟ?~SSvΞ=;|p}[RRR^^[x!=BPk֬H$$**J* =!M6چ޿_WI Æ~;z2SN~':+Wܴiݻwcbb"##Iw^???% غuP(yP(ܺu -EIIoݺxbBPkB"L Gݻw߾}UUUdkPKHQQQӧOg7ElȠ1o޼7oJm۶n'??رmEWPSScgggQ>z… .\ھ}гaNN˗/9rJb6vؓ'Ov'ȐH$~KI+++ 0 &O|8((=Bȯ#'++ɉ0X\\LH}ӓ-p6o,&NXQQR:uYZաC\^^NQ||ƪj@HHȑ#GT*UII;S ---|>?!!2wܭ[*-[Ђ(a2hlHݻw+={· _p𡏏OII )aAZCInmٲETn޼y޼y0WУئ(ӧW\qpp >.]4""6@q<99%??ٙ%''T4TJ:g :&-X*ٳƍcX,|21Ͳ-I5>((Hd]j#! LKKSTNxDtM>]V|@ccӖ-[d2Ku*ׯg)V{uu5_^?o`ۛaXCC7l@\}J&D"d&g`C6dظvBƍ*|Aq|ܸqǎJG< L((H!-`,555Q%%%%.m F;j r f`dIrۙD"t`}>qB`Zj.#ruAZաC #B2ܹs8;waaa>>>. ,++ã<P(O tu  @yyMFTRPkٻ@SDW+qT+ )]d2DР2۟9sFV755D"(?~̢YERvf`!c?Lb"WWJRB܆3`,7yFUXXH&20v;30S@KK É$55ڴi,vBKR TEZVP'2 G!f#Gww7oۥq߿^ܹb Z,#+20=߿?77w%|~nnRއjmm=tX,fðǏ{zzZXXpOCCR 0@R%&&BWWt/bRi\\L&?-[lYE/YYYj:##Z$, mt[n,\]hM:CL0>3gh &cǎ%Ņ0/2NNNwܡ  p-uJJJII!>>>_|E}}i;v׮]jz޽z ݻWݻٙ8^XXںk.ݻҥK###}$i;C z_~Ev 7 :tۧhvަ_|||}}}]]]||i0 NHHLLL| KGX"99ܹ訧O8,k`:t5ʕ+֞EДZhJ ̞=\o*J> @i:C=f-WB Y1yL&7oX,~N\\v _J/HbqRRVVV.$$D(8qYE ʽ{D"Qll k`, QD `X;REnn 4xSLrSN%ؘ`$11&ܼyP(ԻtA[[[lr\._|D"\gЗO0A l޼ʊr7m${{ǏٳGLaMM͂ Rj=<< d8|ah:Y񺬢IաC|mooo@ǮHRZJ,իWW$CBBKLL a&ΠZhӔufFd`w@R%C 3V]xiŝV-**?w\w)p8g_=Н;w*HR_WKli?O߾U^郐ІʼM6aֻ=F#ⅥW^=-p8fff\.7))mA<3dffv^!98ļUgegtYq3 |$ =hz37@ DW20@ e`@ ] @ !@t5(C jP@k{xq # VqÇhlltrrڲeL&4!N]݄NǎKxFi~pҥK#""hTwpONNniiwvvxҤI;vP(2,))&rbာ L@0k֬wRm#% ;-wf<,SNK̻[ve`Jz̙{Q(3gDDD{b1KI!H30\.ONN1cUHRTSS4𱀺Œ]vۺ4ZL~,..vww'U1PHmaСL[<#gVq\T>}J&D"bBd&g`C6dظvBƍAx-mbHDu\.h16B!aLcc#i 3n{ 8Α%%%.7r"7ʙ@MΝ;s瘆A5MMo۶M.d[_ NCDڰah9+{uu---@}?wpp V&-Y> y<^RRRcccSS'MD~J@2.𨮮ix*B jpW'*j**11S#Jd2ٟl2f;*zqvvR"a9dhC n݊Yp!(< 39cǎ%$АsrrڵkZ"zA&cǎ%qqq!&mw^Fwwy(JKKKJB;v,ƽ{Bwww1)yȀO6 ðꄄ'Ot^v&v rySSӎ;D"Qڏ=bŊ%}||zjuw5ĤWKJJ֭[xb]&A;Pɓ 577gffZ^Z^^Dz ʘ1cݫVXhy%q^rӓy#5Յdx^^^K > 4ǡCF-fϞVDWB Y1y\gi8qqq ?K$XD)  ...'N``Va~-m߻w=w2!8Sd M0k[&y'{!4nݺ)֯_O^u, 27***;+++ !3n{ 8oڴI {Yf>|9o3}N|̈ nU?HX @ zǨ%[FγX7jڢsuUq.%6"ҊfM+4mZj۴@JSP%|駟.r^CCCeee^^ަM0 ݭ@  133iiim3 F.ݶ =!j̻O10T*Q:܍g4sKל-9 @@ ԏ҉l?)P@ ~f̡݉^^@ ZC h/*$@ DWB"@tP@}!9y򤃃CO(>|89" xǾԱK[^OVPP J|oii)!daauss+,,:v@ `/ɡ H *@@(Ɍn,<o„ ?! YP!TWP  rrr֠@H%<*!C2tJKK|󛚚XNLL̮]Qa=uTPHν3b/ef}SSS=zt… .TUUm߾|MM] ME-Z͛7o 5J{NN˗/9rFU7]qƍ~i,bRZZZ>}hЄOW\yMr}Hwɒ%1^:WŁفaW755 Bג{{{3gL0VWWO8Ç% Z믿 ]TTT]]]n*ۛ& Y,])bɼ.бo0A `K!ё6𠯠Zkj LE8K1c,]iXDHD"ϟ?n `Ȭ%PS񈈈&j3gθ/¶nݪt%%%rBPQXXHjYxq||Vݾ}ITiu47 ޽{j}HCO fetL2ċ/… `6H-cjzCG8,4?fA)u,8钒BCC˝:.99ԵlٲCt'ON:0ɖvܹz^(\\\oT*iA홐iiB쥰͍k]]Eکlڴi͚5իW[v5۝Sq΂WWW`:3gܽ{ZV* UjJrww'6PH$JKKsvvDϿvնz+==9;KPBBBhP(h:((h߾}j:###((nݺ?333##׬%X-|Y\\,ˉ䮘X,nkk0fr%6ψ]pj.1]"tR dhFEEX,/D")l6IX"BS)R:15#p”:p'RH7L6͔U*K"p/Aii)㥥LàL:MرCR)xw'2gر;?̸C]xgg'0W\  Ω.ԤUVmܸ* mmmv9s&… 'OtTh^BfU` 49ETՄLU̖JR&< .$@Rtޅd*"h4|>iXxxxȡLÊq_ IDATY SC{rrr:q^ooo^i޽{,*]L)jefjɀej!w ll r l֙5:aV`\yF޸8qbQQ)7R+q2u@eݨldzA홐ii]HHD Z];N~HqV`݅d:0b*VIBu:xS2E$mfI켼QF yyyZB{7(%C|||FaCl&--MP`lnnnooOIIqpp P_A' **"PTğr ??\F+JY,LvjNt:ݰat:]\\)HIIEBV*w05==]קrE²dh2/_zم{>!vss#$Дsqqٳg^OOO'f,8kQ~Bd. NVV`ʚ2e !j#Gt;w$n"r}LJn3 䁁111---111s0!666---..siiBP$ڵKR޽["X J/[ZZ3v731bҫ%%%9ggPePYutt"pܹPR/KP^{,^oѬF+mh [^^cggC>QMg's%$$D l"rss'M$,X`QDWB Y1ycT*,Y"|}}/]b j w~𔝝-ɤRiBBy. ֖477bww#G0U00?ю_ޛ'Y Q087~La ab6^f@$Ŀ~:!˗}||$ɦM;fά18 O7L\ԕǷl"^uҍ999ē"h۶m˖-(:?JRtÆ ĝ8@\\\xx80=BNjg/W\Y`X, ,zE[C8ѡR֮]+ɨ3v;u#\!g͚UPP$uvv]V* `9L6+WVDv?] @ 1xfsi ]W(Ln 0`t'1uoU`e`-I zC }}Q@ a1~ Nbcc?g|@ A@ @*0@ U`@  @ T!&y|wvvB ƍ9B,LVXXP(Ba@@@ee%!eQQ',**E"{K\hkk[pH$ZpRx{{ ӧ>}B}ZGjX`jkkKT&q'r/ꔡa K.moog"q30˥D^'d2LAMq( yKaYYo!D"Q@@ٳg-~9sB˗Nc]zu"~Z$L[رcoX,&?~`0 @ { ёi-WyA55"ǥR1c.]Z__ϴ{,D"QddD"ϟgPVVF8Z5phjj6>s挻;2,,l֭zl~ANWRR"˙*]p,,, ŋk۷[DPGAsCPݻWV۷t>T`6^f@$Cxb)Z-\ fB=7tBz`VtQRIII:.)))44zٙx钓I]˖-;tN;y$[3 Z ڹszPhkksqqپ}RgB º:777uuuiiӦ5kV^n:lwLŝ:;;; \]]Ϝ9sjZT&$$W9I*ݝ8 C"(--Y$͟?ڵkTztH/A !>XNNB렠}ꌌ uR)!wTyQ4_AeQfJq$no[,{졩c[TGGX.]1XFa̘1Jm38j]]]cD"!.Jt͍ ///X_D"Sxm D"!RNRuP-cjzCG8,4?fA)u,8N8GRn,))6m)7T*RD"2_@y'PRKKKAj'tP;{) ۱cJR* 4Odرci5+{wqN`n=\, SSS](IVڸq#MT(w5sLR݅ &O6м䁫ӽhrV Ov-M PBZ-Z.$SFLÃEeR)Z[[MY-zĉzzɦsH${`v1%t{w ll r l֙5:aV`\yF޸8qbQQ)7R+q2u@eݨldzA홐ii]HHD Z];N~HqV`݅d:0b*VIBu:xS2E$mfo!FR(iڃ B nS ^jiJ__TFP(,n))))+輠BPE3ğr ??\F+JY,0СC>>>#FcCN7l0NG =<8.fquuMOO{,L<QQQo6"2nnnr...{l֙:a-OE2 YYYSL!$Zvȑ:nΝd3777YYYP.Im<000&&9&&fΜ9544Ʀ566x045MD]vTݻwK$w~abbСC_~eKK uF`Ɲ#BLz?_r)ePYutt9::ZΝ;WSSJz k-Xhu% a}|||||'0%{w&Mߒ;i$H`S" j-TȢ)$ Rd@{%K %܉&vSvvL&J 䩺[[[R.ݏ9TXF;~zogY24GAÄ8#c2=0l`}xm|r/_H$6m"ț:@G8,4?05&sQPW"wp߲eH$zI7888gddODm۶/[L,SGt?X*J 6wqqq4àL:M^rebX,/Xիio 8k׮dAC:Pf*((h{:;;׮]+JBapp0[vuɕ+WI|+VrĮeq@ <Ϣ,2 ǎ?;,uh<~xLLLiiiYo,IKsqPxCذw9Oo/omm=|-[0 "c dzDۖ|>?!!aۂ@<* @<0Tc>}P-U }EXBه @< ̻E?iAouē en@ U`@  @ T!@ 4C hP@ 1Р 0O ;;;< +V 8p`ܸq^PC;=yt [bCA 3XaaB 좢"OOO>YTTD=ߋD"~~~BpҥNm…"h…JRPP-O~iBtPkB"ycaXff-9RLҝ;Cؗ uB/WFTTԞ={we= \x<^zz a+-m`6I=F]δ =vXjc{{u#@w8x<޽{Ǐ_PP@oܸ/Hmfv+.&&F"L>:>U;iMHfO?2cǎ[,-y<޶m$Ɍ3nܸ:MojXڌݡ!!bѣ7n9rȑ#?sBZjUdd$a7o^z5vvv666VVVVVV666|>Ԙ;(3)/a0 11qժUT ԗ8WWWt:]pppEE@P$ݻWV۷O 2|>`0 Cqwtt$@BtPkMMqT:f̘K3m H)Hr-w5phjj6>s挻;2,,l֭zl~ANWRR"˙*]p,,, ŋk۷Ӓ"i X۷KJJH ,5m`6I=F$Qt8uٙxzuѮaqOLL,((puu%:.99}8钒-ZTTg֤e˖:tHӝk@q]s}_VFڿ| UH0… 7o&6V8U`999 0lۧV322֭#JR ///X_pq**11q޼yT!WZTRt@ac333;;;322|}}Zٳ;wh׻eqq\.q mmmƌٌ@8j]]]cD"!Rq]2q_\58O:￧JԴT,۩iӦرcioq* VZ&2eRdڝ;,;'''NvD"S޽{,*]L)jefjɀCփгX=<(lI|rTTomT__TFP(,n))))-aZ BT3 ??\F+JY,0СC>>>#F %:th]]N6lN#)))ԿQ(JΝ;|sfTHX -d-ެ&!sOqEh>MDqqqz*M8lذ˗/jGvI ˖g^eqVV`ruu%$8i4={@\.'p0rРAW^~=L$2e )|Wu2w@CC~w^1cbbZZZcbb̙aXCCClllZZZ\\\cc#KwHk.J޾{nDbjL î^{zpXh'Ć?t7LJH8$v)]\\7"BBB4DnnID"т jjj!WB Y1ycT*,Y"|}U IDAT}/]b ߟ Νhb0>dggd2T@ %%bȑ#L.f_+Hnڃ=0%C l RE^^{]]4& ǔф۶mfrrr322ȳ*jڵ2}ǁAMME"ѶmlmmY,q|˖-"_~:!e h@G90Sx\\X*0@<<.E#h#hғen@ U`@  @ T!@ 4C hP@ 1Р 0!@ BT` B(TVVBP[yzz|OOϢ"^$/+++BҥK a[[… E"… JewK)((ӧO?}4! P!T<0,33֖ j UL&cΝ'a_2)C]P! Dh_񒓓/1J;88;n2ԩS-D2,,,~L@[^'d2La0z?&a)f*GTWW{j裏lrڵד/feeu-YJPرNj⪪*Xo4zRJJJrrr._f͚+WBnZ Ba7qw}zr4%ƈstʁ 4L"ٻw;whB"O>-HW-iiiV z71o.Ж$))ݻeeeeeevRSSe2СC޽0+++ acc㧟~_-\:lذǏ’+V :t%%%N-%))֖mѣGBnZ B{,2337m$Ɉ,P~b&]\\S'O...&$nݚ;w.'?"766 B\~9fdoo\TT? nܸ7f̘hK'z@WT/[NNٳgF#qV׻Қ0}١CGO1JrbE0Yb_W詑#G~G60x{{?A>-sэ79rȑy~~~D z7_%$&&Z*qq{{jN ( BػwZ޷o@ |>`0 )N/qwtt$&K+[LYkj LE8K1c,]iXDHD"ϟ?n Ϗ;w)xDDDSSAww3g݉aaa[ndt\TxlllgggaaP((,,$,^8>>^n߾AMhȠ]PST*={`u&^ 3a  N:u̙"ɸ&xp7 ӦM;s WǔJΝ;CBBG }6Uα% j@@\ΝB #ts?_BU20 kmmp͛NXNNB;;((h߾}j:###((nݺ>>FQP\| 4a߿P( @8ԩSH$1cm1y y\Z a)W{{{744E"4 @VlIqqٳcww7njjj<<<,uG/VK]L_1bZ)h4>ϴ{,<<¾%""歷ڴiӉ'wܙvZfL VW^b NGmϒݻwӄLKK p`hiiٱc9̀昘9s昛%?>R3}|||||6x< ~VKKKHH@  p-%77wҤI"h555+[B,ByQSAꖝ;wBݻ|Mhg.tڲt?X*J 6PE W{`VDv?] x @յ~+WR@ &0 ar _Z[[{-[`;SNEx @  ug"G T!'To!Gt@ bA@ @*0@ U`@  @ T!@ 4C L2{@ ѸbŊm+V_!0S* PPYYIyCmYTT==={HD K.moog;mmm .D .T*=;`O&P_A**Q=effڒA-*%*Y߰/ꔡaѯ<ٷxdKέ[Lu?uԢED"L& 0oЖ8yqhcǎb2zO>d2,""`0Xԝh4 zO܅1qƝo4IIIw-+++++ߵkE ܃/tLB [~~EC=|SZ_>ٍa_oooT ~~~%%%zĉӧO' 3f̸}vXXْjZd;7nxxxlKt~~~49WP@eMY/qŬYiY,1/^4+4 Nx6ydGmk?~ϙ3Ύ@CCâEWyy9S ;wd'N?333##b￿g: |Y\\,ˉ+rA,QG3f ig.8kZ@WW.HJ%@T8!K2hn@W˒y≌9sg}VZZJHΜ9m4 ˗͠JfPCo5cB:u`0@ pttD3f̠>&7oܰa;CKK& L $$$ڵk̙(UUȪU6nHv^܅ ,ӽiTFPDpgϞ2&w]HfgSֲOh|>* ooCDTdV.# zĉz<%HhtDr=.R3d! su>֦O"7cf3&ЄUh w}7==/_e(ZM}ʅŷLNJ N#u:B/ę}W f쿻ݽ{݅_ f켼QF yyyZB`bWWt^ʽ ˒ %;-666o֦MN8Aׯ_sΔkײ4cb6aHɽIddݻiGFFC{_p`0c{K \\\^J̚5k׮]*#--ё<%cbbZZZcbb̙cQw}LBMի'OO ,{`uV>>>vvv>>>Eq<Ņz7---!!! $$g1;i$H`B-PkBEL!iX(%K__K.Xg 9-Hh e2T*MHH OڒpX~ f{`ׯ_͓,K2hL o9p)S켼 HyPPoJL5MhPlv ]Ȼw~7Ϟ=[ ۿCB7hKSQ%k׮JB088||Μ9f>cT*J7l@ޕ؝ *=ڡ]]ŕ(W{`VDv?] !Gׯ\Bx1+W䲁n:OOϞ)z^j_|_mܣLKsqPxCذw9Oo/omm=|-[0 "DlllQ`bmm(z^jOKK+KwP@ '8~&9|3@9C O B 8X@ T!@ 4_T=;@ }0 W`(@ zfD@ D?{(f @De֢4/ _{ArǗyv*˥;0UA\-c:PP!Ah4F`/u:MhfZ4M4vS0hza&ӎlʈg˯4W͑6,27t1_-[a +#J:[BPEs=řV2dG/s0l[1 {q2rQ/<7ӽJza/>?d4aVV{/Z~Cwe2+?>[?š^/໲:%a|ᙥRVdLLe2s*|J(z/=?D6&..p5A6V;afW1fmmc9_᳷/s&rTe.7%^9Lߡ1_ VPYag 軩--D90/drusCSXN9[ڡ}eK6OƄ*2- $ ԺLS֚Tїa K{,F: wf]c'%_}^o04?ɼO2/?tjVVUua6ö^Gjnzc^|/~O.Y[[mRjB4.5lJ)@Wo70 λTv`22}<MW\s¯^=VVV*\2nC,`6e˯4Su:j]y#_bme~o&yvu8 #'cv0bCWV9׊<E<.cF?=%Z@Ctu,'sW\yuտl;]aMXӊL8*74LnͿjW dPW%][sW1o9 CY7iS~Ł2.XYa/>?w@7)Z/6OPNAmF`'??k˶q̙,] Lu|*`s+b29eEDIk0mՖK9 G=G¢,m瞵iR^l[9@o.>oh)0bZ)0ab.'1'=m]Ʋ0l36/䝺IJԇq;%!^Ojulf0tӴl| Zk`[n 0}WmcM#22Ԓyt_xQ0uѯI^_Rzezp}W6 #lfj@f< 0f ݿlst7W0~bױ#/e^7r]8$Izυ&^ 2?m#,0a{F]H _{u˻N7)FrlRikRΑ0 [r}sm+6֘+aL̔Ô"sa߲[m4l8rQ0ګ~GPqf dg?񁒚ld$5E4H/*{}hrm<|N1;0|?j6_Ӭal%Uy"ZW_}P|q㌙3Ǐh3X+ð-o:/?N?3tZJL^9ðVaXοnmoyi")ٵ`X[׀*b7ʱ[o=w3Jݬq젚Y}p:[BPE ƚ(B"%(,1<۴O\g0SG3_>[8~ krj3Ya'Ge\u"Qԟ'Ԇ?bBS<;}cG^j+,,K2*йرv7)uVu]:C7H"f, ~ ð 9y-E4/NU6s`ąE'%0 ?M3t_*mӁj &ֻjջwm qI`a7[իiE acmW\rfö@|x˖-N|x}w#vGbmmxq(}`f @O6 @{  z92W`w9s)nCVXO?Ϙ-H#hך*C 0l/KD  ,_d?d3r_pFztήRtЍ@ ?GXzWvS0]_ޡyQgѓBf1Fw@ B-~0 1bٳjTOŋCC#2tt:x/۷_qkk=0D[jaXkk[aŋϛ7*C܅0L$`v'LDn#[HD@f1(wO[[[HO8 /B5zgVޣ1"B"Pq„ ۱~+0ffccnmeE !<p#-$xdA @ C6Y3gΚ9c|FQqemw])UB-T$JDO !OeTj; IDATxw|egB A $H zD8S" ArtPJE@ n֙y~ Öɦ0)~ޯtg;3efv!T*8! JfuH 0Y|mN>>>Zi==zB4RBB)%;OhԌqBߗj4^h8: a&zcHQBN8="NJbTlj!%J yZaNAѬyulV3:T^NI$ҊիWW;uԂ ^RM6 0WTBSyJa-?\.C v\j*)o٢X8gr+B8a6ᖧ L;zOl?q^^^#n>WunÿWãYL je?oW8l>вgZ^^`hhh@@`Pq:jAiib!dggT۾}{qqq~~~TTbK |*f\Ca9(S;˱[a=F d`Yˊ j<%<%ʺdtha}rwtϖv9)TG %kNt/yfN]t\QEC-3NmPvag^1MlCn&\'~=Z41M;[ߜ ,h븘c7N^, |:s |}}|KĻP 7KI=-u)VSN9-%,hIF!Yv8m cR&DZ|/]J!Dbk_ !Faرy좰C?^6r59d]`v8f4jOn,*'fJ-_ < oк|{(&{D3 %s]GBH?uq`9wwé#YL*):q܆纎rUas#mMkMEqجN ?RbZ6;|6!WG)-*d<:_ |ݵXGyd2 ?(I/*{紐J' YnwS:UR*߿"<ׯTf`Ub<N&PgI{p!߰sJ a zmh_aŤቃN)o*_zaovl)=Dqn=a;u} N\(4BtZuh_cvF7JK˹ /1 #[E%k_P,lG)[} |X0^2qR!C7?!$10ḗJ%86n8N50RYXᤤJfYj.--STCөj|J¬ -WH@J!s\j޽NԳ,+R5\j޽ {[>rjSrfq-Y˱O-sX9Juܽ4R\xq)=;fy <2u&0Ƚ>v{= V!;3´m,pBb$d-C7JʭM›r9a ɏnAX{q k:F/jO V1BDݻ/*.[ۗ4`q/SSvEe[lvls>>7(f9OqfIwzqF#9 :g#T)<:lFۇznȔ}Vl>e:ؙ%. Hˮ#?W qI;²)O9nZڅ GWneRzoK)a6AV}yz>ڽ}vyv %E}Wl>p[ƲnZ<}bo]X;/}\yejz?ܳgo {RQ\wUPyVC~nvēC&nタ$&4UXke_ Z05n]DD/7z^n]U:SPυ7L#U"ίA]4884d`JYH9g`W.IX:ׁ(M 0%/so!M:XTX!T3(`hh3 EEN%VdZCV2c!f \=F3ήp1 sL+%4hw30P6mڴY-pIV7D:vuw^0===..N'++K(ܺukTTFܺuPh4 CBBd)t#6< 6nh0%˖-k۶J[ҥ^5jTiiڹ ϭ-_~G[ U@AH; #ЦͶfd /#Coam=n UL+sOGHWB2[̬m$nnzVm[u2JH!I<"ffn#qvnw+mszN6oNT_{mڴn+d_?Xb d-nϣ\gHIķRȝ-C\h'O޽VѣSjBxMnX)3+*,t;33ť^XX0\ĹZ[lҥ WiiK.;wl;v֭Pخ] .PJϟ?߾}{BNoܸѽ{k׮1Bٳg۷;-h***(QQQ2kѣGͮANt"֬Yhڴix4###O8##Fҹ5ҥBZjQf.Ν{Vf$ B &&v-'*@b%$hm3+p e"rܜkAAAG ̹vU7ys޶8ᱧ)v ɼ-ˏCm̌To!d2eee;*JRUݸ bbbAΜ!+v5{mJqQF-xyi_|%V4qsUVhhO?4s}ggΜ &5ZnvN'S#BH``ߨQ]$NV=J)k 3g;vL(+**/_.u\s;bz>/7i"oߖ?dM0^~ GB)ݷo̵#O;m/cbbn߈oVm۾jsA9]:wN /AB HhB(!pV5i~] ɴ:A#682;,uyuZ̮\aÊ n^Ν2oy9z^~]r ɿ-ˏCѵP~F7 O>5#N:[`|bQa|||\7yEn Vs2|}}ōOZloV@s>9Onݺ%<߿˅f$͛7gϞݷo_-ɵI&_^x500PA/[|ҥBѣG}||f͚e0d5<#V L:Ԯ]&Nt4/YD#O;mQa!?$$ צMQQQjZfo"o~24B(!_2 qOs}Ju ^g`cfNL$nrA|iQak 8P7/7罹s=̛44ŮK݄ߖk&?#W ̵J30ť>>>gIftCY,^/>MNNرNg6)bͪwH`6Zf)q!l߾xѵ۷?C2k޽t7g!u3 /7G_Q'OX0~^=lAOL">>W^2d{󊋋iU/|]v ϟr}jJ~zŲnݺHpȑ-2͋/1bLێ ,֭kxJ7,"QF攔8JٳggϞK/ɬLxR^yew}p8dgN[ޱ#l6/]ӲfCV'&NNz7i_qϠH!=XHHy)N:I vn_,;y'xB +S c9MاJXh43Kn#qvnwe3ό?q'/=UZmDWvw}?LN,OsMHyt-4#FJN{G?1g?wnԨQmP];rۯ !C?^T2JÇcccjullxFaau:mLGSOLO>-V`M6n5w6ڑ6y*Y&M꫞&Q|m>ut B~Iv._߸v-'* != 9ݕffm$nuqኋݶ+b]?oςFӭ[oq!z...S,freO ?Pjs#.BHꁜ9+X7/[ AdqOUOįulhfr=كj.i3ɔ1o_PDJk`hFna?ד´gZ 3;Y+84d`J{JUFP~|jvS' ܟW!=p{77ׁ=?yJ- uc5 Ӓ/o-0.0]N7=zxȐa۫jRJ(`)ee7obb8Xy)m6a͛knΝ:0*sl^ywſTl\e]ˑTTWZMָMpO|Z@)ɑ?$&10t3?f]x$L @QJe2t5^|$ qG(+-zL֩Ғ!CݳWҒW./QTO[a⏚SVa]{XyJݹv][-O@T1 3o޼-[nݺuܹ!!!͛7ꫯ Æ 111GR^PPP-222Ժxm)j^l2 }v=77?ӟIJk>䓙3g N>}񥥥ɉUjp-Ϛ5@]7@my7Ǝk|Mcfgg ޽{ĉaȑϟjO<իWk}V3Ҽ<*tWZi J|\ ÇoƍOkk~w!jժ^xu-[\`k{oe\ikp?f|QrR(<98J)-?~uqd`tiAuM6ݺuW2L-˷Ux<))I&4SO7vVnnw[X(<*3g}VZԦۚ?jb0YfΜ9333S(t-ܰaC۶mU*Znݺmۖ"8""B(_VTT$tٳgm&͛}}}N[_nn7];-_,tT)Vtڵ\ @I5[k1M-DDDSnnk!߿_,lҤ q\|ƍ `2ܦMk׮I+gM4iݺubeO njSD[X(<VBzYZ֔z{k-Fc`@(%%%3f̘>}˱)oͫHJJz !ӦM4iR߾} !999YfҥM6tJUFdɒ)}⺑ 6]*05=@9I|))4pSVOY}J۷o/kεPdXzSlj]t:L)rWz!𒓓;vxuG-Yu:on9m䮛C=lb!%Dɽd29jSVϿ)ޓr׵}uÌs~dI%LwyG%'h"ټx#F%#Gt-| JJJ>î]J[(..^`An\\~bYn]dd$ݙH~SҳgΞ=^ o+Wܷo_Vjqpjۍ[XHQlo%>c!!!kMꉚﭵНc`kc`tZXXZf0 ryP0\ .>Vccc;iӦ͒%KĚnsVjk~jdv"Mv )!|pNdj ^Z!cӑ=}}'OYFul5#4DKaƓn-00 p PކkF?>Vx!֯{ӑS;j0O&2/F%}\Qe]~%Wz݄]+vyqϭt*1;-{"}wG&nӑӆ\֯ɮ^j2ۏR;!$$-qw F6%}*{92o_B;N{+!JJޖ8[BlJ( Ґ( Ґ(jئMz}TTԺuBah4]v=~X(])!h4&$$ T*d`[n}WXQVVk׮/QJ+**lmΜ9F1::zΜ9U!BXGt}U^xy/[̜5k̙33332@W ߿Wv{JJJttmۖ} ]aZݩS5kJPsM+A'&.~o*-UC7w[EyY߉tB5T#1H"iT'B&fx^NeM)Z5r[W)$/\L$mJP[S ~gVg}汽 1.]:tKRe駇~X(iҤIAA֭[AAAN_z^*d`gϞ6mڑ#GǥK&O,SyذaIIIIIIÇw0rEŋ1:4LUzꩤ)S >|2?㼼7n,[̩BRRҹs炃ϝ;7w0U: &L0RZy2M~7Uq= @i @iV;ćzJ^՛ڕZM5l \׹@ Om{Ø}dee X3++K.z~ԨQBhLHH0 &i0Wf2zqƉdҤISL1ÇOJJ ̙m4̙#]~D d`_|ELLVO]+l޼9>>wԩ2M={611/11q۶mBaffY|}}gΜYd`SN]fl>xej&''3F|ڤIѣG_zU(fu]&mۖ!4D.ɓg͚ջw1cƈ+s)))iii~֭[ .|g=JYfĉgϞ=m4 PCwxsں)+]YY'-r=!D(r} B6r{*׹v'&$G:VU'YxqZZZtt;~x%K,\pϞ=͚5.ۼy󤤤'O O{90JvߣʨBy(]Bʪn̕.:)ҭRz}\rwB !۷?>wԩBa۶m񉈈 333g͚;sL)8{lbb_bbmdj@}V lǏ/--MNNB CQQ?u):܏?x֬Yb!3f9p-[ /mܸQQQxӧO Ӈ VZZ:dȐӧBz?}O?0LΝ7o+rz걬_{ .ԉOOO7n7mڴzW_}3g:twcƌ:thRRR/^6k֌2`qM81%%e˖-B:B:&[.\xGB;6qĜiӦ}'LmXi:0+u ">Tw߃ p54mG)H"s]GѺ4)6:݇=;~kv??'wUk Ӌח!Lݾϸq<mXfc3~OyxjgyJKBǏ0wdȘ7o10Ji^^pex*z !DVJbbG}d6KKKW>90`@ͷlْK]/JL<ӧϪU8{dž z!xⴴw}wBpdɒ% .ܳg~B?>i$__N( reggKj޼yRRɓ'}=wl4hPΝר^hH=S~r\ p_G$ - x ɔ3a<99f-ZGu]JUfwߝ7oއ~oPi>XBa>}/_>eBȔ)SV^-f`SNh4/~c͟?ʕkժX~zŲnݺHpȑ-2͋/1b tM,={v/@@U lժUeee[0`ѣrtRץ_}cƌiӦMpp| V n IDAT˗{Y|PاOZB?ݻ /ٳaҤIk֬ oo 0(//'Yfٲe˗/OIIj&%%;w.88ܹss ZjuرTPV1bڴin>h4Wmڴx +ߗHOʿ*>~^xxG5GDD+6HL( (>>j CBBDŽ)SƤ= I@ 8Ntu׮]C5 Hd` ˲jZ+33sa„ ~~~H d` qa0ay-[<駟E h`Xh4BFIHHشiz?$ wXeYVyQFYfvZ"NB8 Fy駭V_ 6 2DH@} T]^|Yxb<۷oի@=~ݸqȑ#򗿬Zo[&MT*^z'@òlAAO>YRRO2eܹfw!ZV0@h`ƌӵkN:-[LӽKҀ@X$@=]yg;cƌ7|sٲe3fN:e:: 20lWᯮid9efС/>8xnݺ}ݺu;|b!ɗ8Y?{RUTڎ|#2QU J5]vAAA~{ر<{O>Y4:n̙F Wz=Û6mڳgϸq}Q?? P[~燯@4;3 z}]SWp@w_20A8^ߧO,p֭QQQ&22r֭BhLHH0 &I(d$m= }(ن%===--d2=Sƍ 'OlٲK&&& s̉6s ge X~~az}LL̑#GBa^u??.]\pA,y{h"##C,o߾],ܿǎy^lmڴxSnYxq6m]tw:O>W\!ݻw??BHϟ?O/bbbZmDDħ~*3R7o:ujnnPضm[q|"""YfΜ9333StڵbMhpM>}񥥥bN@1 EEE?wץܾp8rss?YfRRSSg̘[l&qGEEyO>-doӧO6lXii!CONի׏?ƍm6[jjjyyhС!dԩk֬1<~hJNN3fzs= BfW߿ϯgϞ2m@] 0h|4>@_m_m._ ׅC<~K!ȹcp^\SV 0&)((uBa^X,8V+cǎW^yŋIwyӧO tyͱ!7Jjׯ_ 2LBqƍ?~ӦMW~W8@9sCz=f̘Cʏ %%e~mf! 7nĉSRRl"***ZlO0W^/t"taByƁ! !J焐s#KaƓħjP+2 u5Fw\\,+ Ƽ"sb츻:UvKh1˄8D^f/ذv g.]7L#U4///,,Je0!jZT׮];tP7oe__ߘXegbI>}VZqk~ 6Cxiŗ.]ڱcǻᄏnݺTa4"Ydʕ+'JǏ㓘8{l0<<իQQQřK.o~(dc]GqQRו֟Dڪ=5ķ C{ا i[ѹKWNVk*>|oa2rrr&L '''lE=y\(V$DMXEEd@kuǵت "XZEi ڟRy (R@cyd2Ya;>3swܙxy)K -[лu۷m޼RAE///B߿…-\055-ZdddC ;vҐ!C!=|pSSӡC\..8q={BaBBO(7NXJJJCCq ysssVkK;uTKK~ѻ1nܸ~#GTPiqo~\`0a0QFỜ{54hl޼/a؈aXZZھ}t\?Q~=z_5aFY' B0x;wܹ,!_Kl'2(jJ$6fJ'q5 :֭[r훛-\_#,ʌxxxorB33|E_~E:t3[@ pppho__ ,dk`] d`@aVЮ{OEiyv0c5a$9ðyjl `e%cfɵo'n2G*,Bӿvkz8/ykOԴ>סNnevۻ?W! d`20} @o!4/.6Dn؄d2P8oŲhL&dHD" o 7gkkksss@*.\'a55~2}Zŗ_~#lظ1`{{]E hc64Tb(ZyO60e^Ιr/a``ԤRM*m100PC1=ܺRo0 20 8 R!]3И~uƝBXX;P!aa{,s.[***0 3337OO^&S a,6[p@@o*diiyb ḋ փ?>ta_W0000a k? qqsqklܓFsWzs 0O[ߝG./#7lQhj*(((ޣ1mZXO&Zs!hal˃dYs+#t B=B=|Vk׮NߺA5J! C.@w@Tk \)¢BWNT*522IH&#--?LVYޞ@sBęLVO>OH&c>uY---,믞dy{ ] )¤2/6kU6vO~u%---rth2. J_K""nܸ}l߾}!63SalC?:A>i7ׯ/Kth !X9~#G-[jjڗsDiX; 5 _@ǐO8/>gNh[X "iDiS^s~ecЅ$QGPSPNJ@aXMMmN9sL6,!EB^P0,~w̙3*$jEh*n_:|Ҭ9s[8 >0@3HcApuP󷐂)6~ :|Ҽ~C\\,K'M 5a0 d0 oh#B  O).B:|d`]VUV#н@2uk6 uZUU5`@]Y k:ʏ?Z e{[.J[t =W!h:G @{W!h:G ~@; J[R %,aH~Z[ 7~C, W!@- Դ/CCþX=z P:B1}e`DqHh`hأG=TZ/,X_ّH$OK\raİba r5d0:.20aSS?ܾ+T Q`~8'¼VH8n{ұѣ|Qׯ ( Xo cл\\\G#ϯ^?ina``?vd.I$%%R?3qہPuMXXZH$- J5jh#d2ٳ/ heiS7ЩG& _8iѣ` B ca CFk̅ød C);57o\uundd$a#PVVr.LIJ0u}d`H&{ŠA`ºSEUU!< &;m?[,%2 ~/%Ё}`R]LF~+cƚ%F ߿jh|ϩ[,?7n\wu:8jşF0$C2 Ä74D10-FRZpC]pA\JSZXk駂b@Rܼ-ޟgﲲ6nTQQL()h/(. h6`w{wݶj7mQ90$CHWwk0Qa[YǏCCpLy``Ѕu֜:}ky'uppp|ٳ]g 12RJB#HRSի\ÐZ8Է@UUV= ma-jϳl=d2i^F8D[Y}0v˝;KJK6lp0N֎ݻ㞖dʼQpQ'O0&MjowtFMMM@@SQQQQQS@@@|jKKK{%/^^746 B;8p.y_l30~z!LsnnSU"CLd2$ʤRv+ypp3'NʂC880paeee'M`"ϱ6ϟ'J):q\]9l0&<}L*bl=ܡ|ۜܡx<O(''N022x7o422ڻw󍌌PDs9p`03g1MYe*--ٳ''OB##۷1̬,\aQfXΝ#$ʊ#N8accӫW/. i ʠ<̘/YXZyy{*΍>BhA@Qʕ+AA1 0l,4rԑ#Gu'@(bѣp{>dUeEW))6n܈K"o |cf3JKKwXmZ D,wQu֎̙3w-?reAP!aO<|ʊYugejI]6oBԣLaa|uoo֪4D _"mmڼYS( H$aaaYYYl6["H$5wpzb Z+V[[.]Q__OgҥǏ]t))HJKK׬Y%H$W\hH8aK,III'-9: ,PȰ̘ٻgOxG߳'F?̬6jԨ~d'3bSo޼0lժ{&|oB.pgEXt֭C3zdI|\xG,]aX ao6sV+_lٜ۷o[>.o߶ Sb[z-|)ZgϞ|N޼y3={.wpTntҤ"h]y;gϞ~Uy<G l֨y8´)!u ;P'>!hdڈGQ3)B2L&=M$jdBᛢV?11㈼K:e(c(G\l-,ijǔ>yrP+WZZgy]KXϕXR۾bݺuv62R,'LPSSSYY9|6:뇿}={G888޿ٳ}),P{zy_xq!?c||3/m۶ٳCy<~߾zȑ# 'ۧM\Z 4PP6{E\sw*^ ^y40$dag0 G8uiS`deˈa\)Gc#xNX,N3g]@P_/i-,Z&쎍 _X(b&?o߾UUU.nn<#WZJn#"oOk_]ݽ46555SN0왬~)+{(#=hd!P2eښ5+ǧ-QzE,jngQ_젙Ua6``MMMiiiVV۱?l@kkׅ:!Ȉa4tzx6 N$N an&!ƾYYj6JSZXkc``ϔ=S>Z: 誴602w O*FSSSJ9m]?P];a*^:HtYY?=`+-@o0埗xBo6Xڴ$PʘϷ}d`)J\[Scf6f6{6v@iJkbҞf„#3awi?ay\v!Dy"jo߾/O7а n;#>7oOwԴb@kt[*o``-D`k1A0u\40dÇw;2L,1ܦ0 ?p:` Cv~KUA&Cܾ4jqO lǜ|S7eӝD"X4n2 a;8z:)ܷ7 3^&B( X\e`@7ȸyK!H,q^L0N!"7.~t|*?|p QYl^50ş d!I B!YcCëW n\ѓ╣< cǺRE&d2d"6004hùX>0hv+藮l?x(|k`?߽%ץR=t0vFu@ԧS;ߵJB={TAÈ2|1*z[Q'U86:Kөڨ5֮[s~dǏ6m:(D 5?OX<Žzov~9 {vN_n&rUS5y3ao6~njQ3>jPrf\ƺ~U\R`G`QFGhfgDqqӦǏilhc}I.[+WV罥JJJLMM&LOndgg)vΝ;P/22.^ۊe?қkl7jlIdBfΙz 9O>wIػw„ U;rƆzX?/X`͟ԌU b#T= {&уf''D_\( ay;vŚ[XZs=Ҋͱ^|9yfFy\Bf{xx0><~nnvΖ>4ݱnD4akWf)L;tTEE8'OJQl7zꚔ7--Mo.!gF9DΝknvv6.|{{;W^9jT*w577|:_]]maiUYYbUWWq MP!睾Fqq8Af>R2Zt/m.Caau m6cٳg{ğY,V}]m}]m;6Qn!HtCr:0XEw}`AKKK쉏\WXjլYʟ$b\ׯw~Æ<\X_Wa9u5_]] zUZZ2ikҷZFv-}II^o߹C*{|i79WӒ!GV5jǺσ,7((a`7lą+Vtsu}E}]ǻ:}4ĉo=Z|w'eA:꘣,H||W/{%J7# 0lP(KB2n~o=-g~شyKqqT*%jPieϞ۷n8 8uHٮ68I \fg+Pl#7f~R.ɍpePF w~ׯXzO7 \:uBNSa;@ MרQ߾}kkky|=<H$|#@rMRT&!Ly`#7VSSSZZ}vL3ƀ +8uTZjʴiںy*!0իZek"΋/0 ۷/*u) BFVVs߾}3gϷn\t_Rqb1c]e-jkHٮ/>ŋ;vD}hʁd\Ϸnܴyȑ,KoS"(!GO7\nlʆ(ՠ2 Fa..cN>ĄZ[[k@zyÇXl|b6L,kp2.u YՁ?+WWׯW~5uT}9'BK#Y֭[bdO M>>>qqMQVQ]iݶm{MM,^,Wm蜹7nhnn=qqӧOmXܻwoH] D''ҷZFvZڐh[.^Hliga?o#5J[VYerR*ww//]֮߳Vl B ?^p@JJJj5uL@}hFlC\]ͭ6G럨-ŋg6l '+W*)-~E51ѫ׬wp0gOnuE#'4{wܪkG 4hӦr|1;w˽=z9T\a=^zg[EշaXssaY,vJ0RkĈ&xf'76((yOeÀR8pлC](|޼6jzV6~LUTv/$i>L_Quss35[kM0a˖ cJar3𭛣ьGlx񒄄);I:{UURk;<2a$.I$UW/YFӲ<  cdqc:uXg';ELThwڂبri&kC']r81׼s'E!d`\[voE;tFK/,o0t ΗYs:uUd;`:Kө@@[20d`F>;yHy/}#͚3O't=*uR3620} bnn.]XNN@ `فoȚVVV]Op8,k׮]Ӵ!!!'$$NB-e(K-Ґ9 J 222\\\,,,(=!rB%Cdn*,,ckhhmM]r ss$G4mmm?ϟ=Ο??i$kkkbŋLfmmmß={zbu|>ϏH$+eu1 C$<666,,2444>>u݇ pz/wn={ NO0޽{}!}rMLL^xAiaaaaׯ?p@MM fbb5M"-ZdbbiqM2VR]X-[|\Љ'BCCRߘ񍴴4gggybѣpϧNjeeE|o~uhh(s玢"扉666NNN .\JJJEZy{ x"pY///5vcǎݼy[&{bsiĭ ;|3g֯_'F"-/(wZXV۷oի!5>;wnƍffffff6m:{lmY#HHHXd Y"ݥƆd!D"ь3޽4DL&Ύ,T+++DH$,KZ@-9VVY 1L7ty敕)~_p8(.?{q㆟b(ԉ"L&322 J \]]aaa;vń‚ N<)󽼼M(a2o޼aٱ9999s޽y׮]Y!.BСCD)wMDիW#f̘=vXDD%F"̚5ի'h`2dܸqHa n޽*+!p8NNNg̙-[/_*9hX,|Z:uX,f~];vL ȝ:$ ӧOZreff&h'$$L6Mp?CJec5ضm[PPcUFF~yXۧdҥ_}9]e`ǼeXچ}M0ԴtRl%D e`iiiޯ^+9{PfeH5477kq0 4-5r*Ɗ72,MMMVVV^nnnB$RI]]ϯ3 ҥKbr&t.KE%[ieA2!//oĉttttttć= L=,\0--Mn L,'%%Y H$·E"q<~wŊ|*>|oz{{ dtXRRґ#G233 Df333O/'ѣGTTTjjjYYUollLMM0ԩS+VHHH011QV\CAAA111UUU111SLQI%4k`r_)D3n߾ciiCQDئ<~ȑ#9άY5MHY,Vpp"Ρ2VRHcHQH~_͝;b>x%qRr:9rxxb׳g-,,IUUՂ ]]]Ϝ9hB5'Or;vh(CF(d;;;R pL?Br1cXZZzzzfggӧTCq,_<##ߛj*Djyròw^y|⊳Ǜ7o>Sfg̘EcA;D"њ5kx<[v-VQP 쏲@s>@KKKի/^L<@oo981/rO,)--ھ};sannnmmv@!PGM͂20 o20} @@o 7RCtL&[hQ{{бh"x ,''G B\h7d\www+++ww\ӧOs8-4է6$$ᄄiQFdgg{{{X_v RƊR ) tOgddXXXQzB6J<%Cdn*,,ckhhmJ<))# u[[ۏ?ʊ_zu燅o 2._<|p9?UΙ4u|>)H4*.g޾]S}a+c6|˗/kTUWEEEB099ÃK </??_$]t{-[L MٴiB7oެE |򲲲l;;;\H+RzK)4=G833ۻH&B`&&&FFF4Y}: 2&Pv{w͛[6vd:99)nсʊϝ;ʕ+BӣG&׬"˗/v횜ƆUVVVVVk]Uxxo].u/luеkז/_EU][EF`mmMȅ/??_,_tiիWaaaZhkI IDATI BM@$ɱl,4M3rBQ?~^^\qOd2ۯJd5a?=ܜ0jԨ/⒲)SXZZxٳY,۷(a2{#F8={lll~\חر5s79&PvP(D Bwwwa7̙3VVV...|MAAT*DѣGȩ)r0̄蘛Rr#d2+JH!C577lJd2Yuu7|KYMME_z%秪C|[z>|z.::B˗Z|cP;KHHXd Y"֢"&iccSTTD3f̸{.BH h>VVV!$HX,5h >;M Cbcy L&:ty}S?'**zyy nܸq"!d2###+++ RcXL(,Xɓ"(??Kфb&͛6ؘCX3gݻw՚.BСCD)wMDիW#f̘=vXDD%Ν;v,!TYJqv+LD"7n\AARL_;4p8/koooeeiӦ:MZX,^ԩSbʜR~aZZtb].u/luX,p8ZTqkv1@ 7ȅu ӧOG\233Tg`iӦrRl2oi@ib۶mAAAa++ ͒ƓK~Wr(:e`Ǽ>ٳ@ BRUU`kkkWW3g(P,r ɓ'搑2nb2䫽B-ӏP=zt̘1ل|ߔ(SrPTY{B޽{)ſ'X, wwQ\(5''~/_߾}{ʔ)*)֬Yx<ڵkrjG͜9ĉrBX'e [i!xGFWk`xGY׏F xNիW/^x4dŋrJwww u⭤8\G981/rO,)--ھ};aFm)077g0w PP233[cSo%+O:;ЙP>QT .[tp:XN d`20} @@o ὇=S"-Z[8Mk;zJQ QWIqlѢEB XNN@ `فsssݭsssɻN>p5IhьڐRWWE bƏ5\H+RzK)42Va...Dm"tO}:Cr)Ϗfϛ7F˗.'l}%](//q#xbxݺu|>GFFJ$e'ċ/N2fچ?{L&aÇ|r[عs璓?\H٫Vھ}m۶իW_AAA*5Ff޽Çֻw֢?vأG/_xb\H+RzK)4}qԩÇeeeDm###NJ^wr`T]FM+W;wÇ={hdgg;vvv]ŋ7nܸql߾}4)φ~ov-+0lرgϞխ"KII&&&aaa/^Ѵ00KKK\8p^/Z$<<L BĉqB|177OKKsvvƝ'v=://<|ԩVVV7ׯ_l//;w(ZQ,bnnhcc䔛`kkx\oСڻwЋ/UUU&MAj(I&X@1F5`޽K^#r4776l?YYnaʕ(-쳈'O8[o磏>*--Q6l?Lj:͕Ϗ_غ!%$$$,Y,a2EEE666L&Ʀ!$f̘q]@ kxC7o^YY27aee%HBbiZ\ L&ɴ#@@ec)UEC*c~_p8(.?{q㆟qP y1JT*-((puu?رC, ,8yH$R4Xdƾy&''f666V̙{]v r .BСCD)&Y^^OH{Gn]IlڵyΝsŅ(Ɗa2;vhhh$[ZZn߾Xr<@Y-LHHxMvv . MHHDIII/LLLDgV4hNKD"˗=<3|B رc@0}C ӧ#V\x4۶m R9!MFŊe` JxMzzJO.]W_uyyy^^^L&%0tPjlnnF\.?(.w޺u+1#P L&d }R Z.K @FXCCY?n8n6liUYL&͛7]omm]__WD2rj{pp_TTԙ3gB!+sRQNde+PE)BBW\a2W\o&NSSҥKM:#B ,--իWeXMMM!PG2MxYYY %%%b777MkMss3 l,M3,V~T B*eqPҥKbrΎ\.˗4&(3J L!eB^^ĉvmLŋJKK y6Q3S0 : BhĈrݤQ ܼ!-2ÇB$???11qƌSLR켵5ِL&Se9Ob(Sv1M}z!]XXo9rd9rDW?]b\ + 20%%%9r$33sРAl6;33ɓl6#zFIP__dooeT MIIijjJMMהd0e(K-M ??Ǐ755?~xaw}cjjJ111!C$[$B77d/bAttt]]ݟl2z%--M,_搑2eBѣG۶m裏AyI[u7n8{leEرcvuuŅZ Ǐ儽{~ʲfff"h޽?Οc~Wb*>,H>KLfnnnSSW_}u///JM###__߈իWJ&1c/Nd8e;;;uTWWWUUL2ðW^ƦŽ~SBCCWX`bbBoǣGVVOjhND)o񱴴!(L9)58άYUrTWWX`Er#GyK+RzK)1($S/Νb|}}{LeRō'ZYYM4X`S0 qqqr;wZ[[\C:v옭Mzz:O? 닋ǏpviaaA9ܾ};yYYYrjPk?[)qƤI8 yF/^8e6mkkNn{ML֡{̙39ի]d3f̐;( ϟ??i$kkkb5'&Qjh^TT$ =<!w^PPвe5333d2ʷc*iӦ o޼Y4beeeMMMvvv2VRHiHkS ʰ311122N/?Ca@Ye7{o޼IOOߺu+PX'666,,2444>>^\BlٳBP(={^#!!!IIICX___[[s;vл"w srr.]Di4<<|H(ogߨ)[EF`mmM5/??_,_tiիWaaa~^^f+յ!T\\u=!rrrXQ6[&R9waD&4u Lfjjѣc5jԨ/⒲)SXZZxٳY,۷(a2{#F8={lll~\חر5I0B!BH( *Ke;vrdܐ{`P9h*TJKK'Nhee5iҤOBqa2111޻5u N{F;q݁$;&UZ)7 %F "xZKRcGi"E,H#bm :P/\@(uݽm}?<{FA> ,77r # (*::ZHLIIQTZ3Lp8C.}<@rHkUDZ4iҲe۹ά A1L! ݻlg÷iiivnp! Vv!"33_+̾>^OkYdIvvf۱cP4@ :n߾}e§r9&%K0R.66 NN`@ncͶ}KB!7oAˣ(*///..yϟorss)ϧ'|ÇSui[Lp-r9tł vBرh4 CZ;8!Ni"z׷%LlٲvZիX'[ݙ;uAWWWٳbƜxCd2)$Iۛ$Ʌ ^ziۛoY\\̝%$111p @}o~,mmc2h ëFDO\Rq#`\⠮ :aHk'Dډ&! wi2Fcvv6 ^;jY&,, f /YsÍ;uAgphB]XXw&Zj,uH\.5ͻw]xqE1m`y ɣWutt,_rhX>аZ,2o|<Tu.$Wj"}%>HYb45MOO|7xXԩSO:ef3ͺ,T;wvS2 oɀ 4Sᓍx9m[!SY9:EV`ܼ}a^7ӦMs#g>|888矇oϸquBQ)ʢottx֭{;S|}}v{aa^4K2dnH$@KKKjj[o%,t 7OGBX@J)w^^\\L f\hcpyP(++s8eee l &Pk.tcYY-j1-###322222ϟ/H:::233޽;rs7MC${nd6٣R\_ŭ_>77wܸqeXX'|͜;ܸ/ĴW G}rJ>pΜ9u}}}EEE^^^P˅ Z[[hH/!yvKv5烃݃r1K]dKѸtR\r%"IwwwLL\.7iFʗ^z$E=},ZP@WH'>H93,,S}"H-U< 180\-5 \a0  U3^JW` `0C'*f1I|N`0 f{` `0C߅`0 m]H `0G\a0<{y;v{DS'NxF2Lׯ`NiwsYZZu12j6 Ljhh`f,W8w\^ϧW^BrpLuώ^wӷJ ?ն][wu//WlϚ3Dsŭ[>3< ,77rk677Èzxx477(nhht:4܂F 2p\.ˋ5s_!'o \EAI&-[kX$RZw}'l ȩ%0S {1LQӧg̘8Wq\..Xn+ @ooώ;|||F#0r&! 7|}}\d˖-k׮^:))uuڝ\GDnnnuu/qxx={,hɁg9I&nݑB$I\pիW^B>XEENc먨[,(@RRRUUuc2h7 W_}ύ&֥R0fqPWWGD]]0"DNi;w4LF1;;~`bZ׬Y3^`լݹ: |rttB.,,PHVZyf:P.ݻw.^8}t6ѫ:::Xrr9EX,S|.%h4j4 PBl6s \_!']H"jd\" Eg@,ub:uSvlfYWJu.|JZX-sG-Hmm-Td4^N V`ȔCV`NΩ1Dh+0ʹijjjȬ&O eݨr ˹!k'4G.$It`0Owׯ_OqCV`.݅: 1f+$Ha@@:W%K8p%$$IoI򪪪' T(UUU6СC BG$gE-!~]z RXXhZt: Fp.\PP H_!')D*L|UcZYYiZ+++j% X"̸quBQ)ʢottx֭{;S|}}v{aa^4K(hiiIMM}뭷.H A 2|||kۋiӬs rú1B`% SVVp8^y(l&L(j׮]t3???Ʋ2ZjcZrGFFfddtwwwuuedd̟?_"tttdffeeeݽ{wƋk'4vQHݻM&l޳gJrI_|~qaaa|Iww7sF|p.^5 }$ )3gt]___QQ .z^B/vf=0V] 5烃݃'0_˰0~HcbbryLLv\^"IrѢE|"B_!')P扏h\t\. t钀% %IOO;᩼\ѨꜜP[[[DD-JHHP*Gv~,cvPX2,G!DT*~\ 4^N ǤĿv"S%88XRmٲ#4ADzoL`Dm6$g͚Eã>OܠsJyABQ￯VjM,@VVVBB0"DNi/_^hRT*-rK[Cg2֭[h3BvG;u"#`!̙S]]-$[NV+h9 ~\6˗/ c`v90qYN H- Cp]jVWW{yyA!Wy!E XTUU577 Bag%''DQ]!2 iYf|B#T*e.`V#333ݻw޽Rt؅sB//cǎY,rر)S=66`0_ q uaaayyy&wiii&Ʋ%ާNZH{xe^.65 WVR$2 l6+J5P`O:5{l(xn޼/)vEP+伐 LR|,̙S[[.`@@@/?DM>^/|I(ioo?;cr|̙ϟjv!b׮]fڴi'NعsNj/W_!!!**--m(drdd2bX,@Sx9m3--MR2嬔r 7fp x©"֢`* 222JeXXF8L&5k֙3ghgϞT*322n1cƹsiڦ&1((rʭ[lٲj*vt(Dډ&! zjZz~ܹ.igH&ii:N#w?KKK===a7 @&ufg9>;::Ν;WiljjU\z5<<\&vKg9\aD=<<EEGG744t:@K>xd28\jAˋk-Wy!WAjzҤI˖-koo&>$IT*Vw [gVN-BA$''߻wgϞov; !!СCE VUBDfff^W(}}}zֲdɒlͶcWILit27t:ݾ},i#O NrL:K,ab\ll,7fDO OAQ w \..Xn+ @ooώ;|||F#0r&! 7|}}\d˖-k׮^:))uuڝ\GDnnnuu/qxx={,hɁg9I&nݑB$I\pիW^B>XEENc먨[,(@RRRUU2ޢ^,Yf޽{Y,ujZ1S*&MDdۅ >5]Rht\2,G!sa̙J?&IR@4^N ǤCR/L&y2 DO OA@L&ڍW_sduT*1+1YQWW5 iH;D9Da||Ν;M&hΆkX5kքyX5pwqG ~l=._P( .ҤUVm޼)999fyᴺ/N>( ,/!3y*⠠k\w- _m'C lLkBջ\E*ɸVE@@}{Ѩhzzz,ub:uSvlfYWJu.|JZX-sG |mm-Td4^N V`ȔCV`NΩ1Dh+0ʹijjjȬ&O eݨr ˹!k'4G.$It`0Owׯ_OqCV`.݅: 1f+$Ha@@:W%K8p%$$Io仐UUU'NnP(l6ۡC D"k'OHHHaaj/ IDAT-**tvw.\PP H_!煴)D*gW9XEhhheejTH$Ç?,u3n8oP5~xha@@@AA1:.==h4޺u__b^XX(a9/---o%w cb???(A޽{v{qq1-tuNA rX9&wQDaGYY+%6m„ EڵnXVVF Z-}LKnrȌK$̢wxp&! Iܽ{d2{QT.i/֯_;n82,,O>fٝnEbګᣏ>Zr%IkR8g躾"///… qqq^eeevݥY;A`UWmd {`ww`"d-l))111r<&&I3TVVK$I.ZQD+伐"B<0K.!!!.]GZ"t85ZɡEDDђRQ n2k׮ I|%r2LAHRxg°/NrLIk׮A!2ZZZU*Ֆ-[;NΩ1DO7. JA۶m#Ir֬Y+**<===<{쯿 BNi-RT$e4O|,$Iii=RF#]<F sWFjj޽{z8}ɓYZ7999Gaـ>` |Fh4ߒg ϛ7$I$#""Ν;'0ɓO>=A;~xAAAsssDDĻ `oذa۶mW^MMMݸq#-"##[&%%-]Iܹidgg+ʦ&RjwW1 ---k׮.rHkB" 扏_|QVVvvz4% tA2 a Ν;ӧOV`$a[1tvvzyyb۷o׷޽%jbŊ+V455555%&&&&& wر N*BF3nܸ۷o tssH$cƌH$Px?O?ny常ŝS;kKHg̙38q"88X. h)Jo߮R^{ׯ tGd>eV ̷Ǐ߼y &LG iժU)))d֭W0}رcƌ3fرce2ߘ^g-aiK`D"^OkYdIvvf۱c+]Ȑ_C\5Aܼy3,,`0Qf i` IA fsrr2S~{{{÷WNJJb_]] qqqE /^ ([x1WWS/_~aN>=c ofAd'l$Wt2|f$Н|rss,av-Xn+ @ooώ;|||F@6???xmkk`I5`^'++)o/bSe%Eŋ[n+* N kQQQX,%%%QQQ*xTV 444̜9ST~bb+0d}יBuSٙ5L|,d2YiiiIIIHHSK֬Yw^a践Zݝ YT*{{{#L4 -Av!f߿|MwWTpaAW`% 0XCČ3K6|@ 1ÂKlfJ ëZ/7,AWTL&drZ-U*W *0J4cbbBCCSRR=jXDDg$Wt2|f 12FρBZ0: ꄧsNd4,Y"0 vU`AAA³V`X|-Ν;ש#X_~jAc٘S 9YS`)XVLƵJ|,શX,P|FFCK#p:uSvlT*V9Rܹ#ۅO)S+0%~A, N<p Z"G?(m [H ),,ZEEE:e6 [H_!')D*L)SpJZYYV,H$~i xƍֆ>>W\a Ǐ⴯f0aEQvaaa|Iwwۻwn/++sڸpB A555Vu޽ЮjN[ Yzƍ}ג6W^o׿:Վtx #Csño>1###322222ϟ/H:::233޽+Н$ݻwL&ټgJ7D"r劘 1?쁱6N?r7G)%ܥaOwwwLL\.anҌ/I-jmmBEZ ( ih4.]T.\tI@XXk]k4ZCjkkpss%]]] Jѣ\.N]R&dX!:QBokkC$ n߾]T:C())Lui4yAAkkٳIܾ}Al۶$Yf]v >Auvv._] Km4矿 ,\!gG.9:Sx\\X+0 <<.E#h#hғen `0W` `0 0 `F\a0 6`0 mp`0 3 et~`0O!N*0^ EDDDcc#Jٲ&00P&0}$IoCCC Ųef3ƒ$k4]JuuuPP\.={_ H_!݂)D*2@'>͍ i SF.'^2)#Ä\H!fDp!Jo%T|GgΜYx1I&>>l9Dv|h4Mrr8?^PPB͛7mÆ ۶mzjjjƍi_VVIK.]Ԥv JITfgg:!vwPQQҲvڕ+WB!WH E $??扏_|QVVvvz4% tA2d)Č8Eo߾[n0ϿkJ[lh4%']$OEqq4>4}Z(,X wލS(Z… \-.R4//ۻ&77sԩaׯN4)==IId)|J8vV~~~Ν Gvύ7XF&ÇãG%0ReŊ&Laz~_:_z-s͛7O0a„ }ѱcdž>&3>k?#HVbJ`mnn ã@QTtttCC@-u:ݾ},r9d2p8P  N W\Y7" jI-[εM|,HLIIQTZP__*]<""99޽{<8{?|f (`0hZ n 233zB%Kdggl;v%i 27)|JPΝs8?ƍGG+**V^l0cѢEgΜ9{lLL |:CI' ǫzYh]v y&S.% j\O , 1 JzK_YID"x֭[Ɗ Bӱڿb)))$%%UUUjn0sLR$ ]0L:S-| L355522kXdYf޽ê践Zݝ T J9¤I-݌yv!f߿|MwWTF`4}F.VȐ\HSBJJJxx:(9{lppt:]KK dtvvtwB s̙AW`rˋ$^{_ hiiIMM}뭷$$$jt:Wotuu悂OOO(D>kB"dϟ2e < ZjZDr gܸqmmmCE?,ZPPP֒NKOO7nzp8׷nE#dX!C:.|i$Krrرc|-[:u 7nܸk׮u 4MŲz꼼+VPl/={>lJJJQQQ{{;WBBŋGwwΝ;=<<&###322222ϟl H!?g?>88=88HkA*0guww.HU*++_z%$-Z H_!݂)P扏h\t\. t钀%0-dw񤧧T*GgHyyFQ999777ZՕT*=Utڵk!!!**--m[ K2d)a#^<+Ϝ9GEE;mƂ0k׮---G 6lb(-vB.={ӟΝ+=<}z,'͛T*q|Fh4åA$#SP<| @ 2LՁb0}ݺu%y_T*YEQQѪUx υْj???ĉ6l4 M^^۷ۑV IDATwRwH~~>;CԎ/rLBرc. $Y]'DKfYT2%CBCC n?uٳ^yf||s̩eud`` w*t -**>}T*e6xO< %www=ѱxb\>sspk.F3mڴ'Nܹ_ꫯ`֐J6?O80!Wy9z̙3e2g}vYVxӧ_~Ռo4dVNZSS8׋OA;J$gfS(ƒ twwg!!!̑E̝;W; /Z nll ruooAOpSUsΌ31ԣC׽NWVR ^ŦJ$IOOŋn3<3b+UV1%677{xx (*:::n߾}er\`Ld2p81Gxve-|M Z=iҤe˖sm $SRRT*V-s+NP p|=f={OKKtCQe0Z-W Az^PdffzZ˒%Km6ێ;R12dn WyQΝs8?ƍGG+**V^l00%?G fSAW_={,T`Fq׮]111#@o޼ɔlɅ$Iz"ooo$.\xUWf r9pviMd28 Q;9x$Ib"ZUTTt:ֹԨ[,(@RRRUU5]RFhtB" +d@%ēJΞ=N҂l/ai =_/f>@9s&$$p\."I^c>.RX'tJu.|JZX-!s:O<7n(..~յ̋1d Ӏ)t;S\\퉉bX,̧\|jeƌ`P_SVRV X"H>3u!"7n\[[EQǏ(*++0ӍF[{=8.N-..텅{,VȐ!YC_2apxINN;voe˖SNAƍwUPPn:f\& 39z:IIIٳgKϦ#{%$$\xptww1R"\r)3gݻM&S___QQ}HLGFFfddtwwwuuedd̟?ߥQ.~LBwʕӧ'=0Vwݑ烃݃'0_KR歫|Jtww򘘘ǸDeeK/DEZ[[+["B<0K.!!!.]ƽŃ$==]R>:C5ZɡEDDђRQ n{`׮]ʓK2dVW8FG8?PUU5Eu!2DEEEe0}Ne(`\a0'\oa0GF,`0c 0 `F]ȏ+ `0>k_sRE®`0 i0 `FC1`0 )߅`xI?:pa[`0Gܼ}gݝT`/*~:7?;wVU"1\K9>ǟC tz:nL?]6Ox%w+?.uwgy_~^O LH!RC@#>$ߴ^~vO!-a*ŠĐT~&^2FȐC3rЙX".vst[Zpώ>_AS&*&m?n+l9B3jG 1??90pqI>?}<6Emh&,~U}%ɇ&H98:ЭM? ] n6_nҍgp 6ą[|:7Fj<"7ߎ?c>}U,GS@i-RT$)|˪WOwn%Zn98"<j!_&sCru G?E"kmwW{_h}q?O\AufȨjB^\-Gb:HH!d`kã(=ff/ ل "D!Hz*^hϩxlO}zlscO [X}P1T H Mvd/3cq]f&MH@yxxfy;ÏٙYI$&]Ƙrdq 쵏| Gvߞ([fD^۷j|vEA{V3'DU3{R9mȊtSjt#ȅam>\o~LD:<֪vAuXT[Z ua&}Q8-cmW2`rD'Ox}sޅ\"z"zj+WN3X항gZ(&RSz8~儯Sߚ>11v.EI飹xf([c‹fI{Pkmb-4:=P+ 6娎ѡ s͉"uVgg}i/l?N7}p>8.0[9ji ^]KDc2"PhoNRv}q||[o}a6gOuǯ=D5ssqP+Wl mz7:܃ Ln#t̗ٓ&32)&:R'+S!".XOʺ򾼫.H2XkjEn6q6H JNIy`sK=HKhbR#^VQ?W{,gF_,?k3pe&DWlm!MMDrE R>yeO?M߼qzjjԃ 2͛DݝRl!{楜j|tsqN]iW6LuTe?N{[nϻY'z(o℆[u0EM4)?raY؜)6) 25]?^~Ĉv]qM] wr#"skϩ7} ̟bɌٸN6_zB?bttsuV( -2 a ZDeE?\MD K2v-}q[N͙bw~ "3%ٟ2XBXΪ6i醻SQ:ٲ"_fJJLS&"g{~M蔛^Q:d,]<3!}MtTđ%4Pezt9d3ߕrQlvvo_9?DC/1*}dS0^mɌѦJQ /귏lbsr{C5]w <" R}OQpпWoxO#\#cZ~棺c\h #r{EGxy C4L2S=:;d :a-7 m8jw$NO3b"6X  kPr{ĚfܩƹSX=?ljH็O̟lS[\I/Ĝ~lYJ@)Z1 ݻw?WZ^yytײj}#0B+~sqZn*Yn@Fc?x~ؗ2vOAځSCVfY Z+_bA ò0-"Lr_j2XkUU+ %u_8X틚fܛb7؎g0n4L> ޻й҄:߲l:in- Xyoqze`˾Fe)3"ͽD{kVޯؒIkno]xzՌqav|jTE#J $v g_쬺VuY^>HK`Z\҆(';NZyyeN&>ÿMޑwyֽ|8qMd)lɋs$1 lѡ8}QeimյuQks.%Q5rWlmQtnUqMQROӝw姲|V[iN ":z"Sj8GvE6" qS'O߾DʿdU"1"Y`c0+//YpO_?FAZFˍhkmoG\/ tL ԩ-- -1VrRr}{?I#G*Gc#׻ px嗂8N,YnRF`gjOѼyl|h:ɚ<9lNMh={LBDM soDyšj x߿]`4DΜFkqO9Sc%dEҋkי oJ#$cFZ\2y¿,>9YGu}y7y9% ,v7'hM?Qo9Y`% ݳaǙ]}.˫a:ř,ʈӄG29v= ]G[S^Np < < =Ύ sNH&=Ÿhj!CܨN7[b M#;|;*\+IL͋1DnVž8WW<^hZ%;\#yW.oTd D#Vmp.J.~-o!!#!fsx8Q#/8.SBj!0 32C9py.oUcwy}ilhB CIv⹦pshsz6uBBRɬDB 4CmY õ:t po͎fP- L:MԬ.O߫jc.9f\AyCGwOo\优pSQy]óMѳLYxɵqm|rIU0lzOY}޼SnDD: ?9y7G0ByC[16n4*$Il-<~ηݶ~ӦMKKK[==::Z q k`[{W;;Yv"r|kOVU< `4qu[ڥ'mmmїhx IDATt^&|?=@0IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-task-measure.png000066400000000000000000002002111351617527000255540ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxy|E?srp (+ܻ @~wUA`]A@A@d!rrDPT@$9wwhh9:$~/^3UOWu#jv;qΧQ(*W_s)ytf_"9rRٸzɿne6uyS)3{98Z]a~->jn:NR~nXFnSyJ=mtw~_䓰h***W^-]ɓ ,zj@@lkjժEj\E8j8Y̅zӏg%(ȁ5VKڵm-ۮlstðPJvx]MZAQRag/CAm|7*Cz_Jz1# o JMdJû3e6/1ݞYXҢE NTr64 D˓uֲHT|bgGvn[ܽ(! ,ʲ{G GD6*6vE^כon_ӷtʌG9#KeFWnק-/-*)i0N K/ "e+ƟrPkEEE?^)~˯90,KYZFVek׎rnXLUɚkT0,W8Iu]ȝܵV2=;(QJ)%z [)*[~{ x'XXa(//8j6VZf)/ ncצ˲,B$Ij5k8T_Wk:6L|S9nZ̬Ɵ#DYolfZsKzG S-왫ΖrBhav{X ˷_~f`jlb:Ъ)|v~qZh8JUoO޼77"HV\Q%45nm4 ,8nkP0WӶk?!$̃[P}#6;w҃@`l)!Jw].&VP9(6dgn ĉRڭfSY,lcBT*|03v300ָPn3gٓܛ9s___; !6[6 ,YAv8*F9c69}usRBȣ1Em` n?rw7Ubhԍ*fc)u ~p|_jZ%8f1kʧiג̋z>ff1SaK-|T> 4KXnjwdmlwm 7Z̷o_(;Y7GКDc kK74*EDh ~%>17+)/Ui|e؄EįOۖ:ZRnqK8f۬fw'|Uj-k]z;q=}/Vhtjpc> {C(ȓ:y$Uɓ'W4[DOYFȱ,jͳ(6E\XDŽ>p+ClM/zUO)c.ۏ\W^Z}Uq#W*O1H[khe9J-j?NTt*BHLh ۰VڮZ瞿 7Kz֜P.ԍ(B.ֱcDŽʥkgwҲjX;Qn#pP(l5Rʱv_Mh=cmlB{C|u处h9JiQiC#%FhJn(kmgJ_{,+%&%w;2۶~w~]N/}"9o#!Ddʭ{jr":$WV^/Wq0%Z˅z+7ʺy&9;!D.q'rPxaptVP(~8WwK/9TUZڊ T*qc> \.r'8pCC/(,"NI8sC_wٗp n>RHBr]\clߨֿ^qb*!_vܹe6C#nvj5kjqcNm[v@~_8s f 3edJmQ?2Ee|!~r69('_ h5*&,K(3TaxCB nܝ{; TbZX (&;QRP6XQQP(l6FQ*]R>Ʒ?R_T" 1yݻw;TSoۅ\k޽;!!aB ZmBZL6rkg6Ox,sZ5JvlJ9ʱ%<7h{m?xO=3 y]P6-a~{}rv7Y6BOqUզ3tj:`|bgB7ʫ̭rf5ɏjAeֲrl{0]AQZx/ Bȝm\iY ߿)Y;|~%5oG^ZVӥ]bV+|˄j2ztiDR>xO7G~=h5Vh6׃eτ~<;"e^RGϻQi޺V9ʫLf[>=RS[vrs[奆2aO*&%w\N)/eدE&Jͱ/gNvorLϥ Fh /Yn>\`Y6n(*zp@`X'J`Yb4Zī;eYVR]?s%T)qkYJvz~hҸ+6q*܃s/+ qˎC?UVU?ޯۓvu)QvlRm8uC]v6xox|I6#+6;-}e}w~ދUJ/?{C ݱBRdN܈>&\^_r6[k#Z6igy 3׊N\n~m~&vNjvwP0G ǔ(sqsgYm} *k"2K~̱gܺRLUsߙ),ǣ>zo˖-}}}e*~WOaț_[5߆ .h ř+9 WmvP\pD``Fqpg9/ A͛7L&hXvיJjÆ ѣlk -3o<">_[HSuTm5UGRk|Zu:@V-nj";ް$|8;kT*???ZY K¾qvSUyyyZlVRN'p@#***Ffsj!Dy =;JF|; ;ðv.Yw}4- x- ᥔs.ʿ/5 K90 4J0Z⁣jZovf) mzR(ZA{n ˿'M٭Vd6v_򋰔۪<+\.-++2 dzԅw_.Kb%4 L!VZnP(M~90![\lӶݭE OcvUJΨC8֦m;?aԡyɓҀߧ͢7n~O?Ytj[Wgؿϓ0ddVڶkPr֭?Ox6Ӹ.m۵_mV~ڇ֡GF3/|Cy^^ވ:>0rk׮9с;uጌ:tm~޼᝻ sjwLݸNKƛO?5<̙m@]ݸޮ}PK.ڵ3$$'(6-#.]8ofo^o~eC0~s֭/_Rm'5 V_RS;ϗ瘁I {/*Oeס O{աvCJe׮]?^acV^jgL>yw#GXu] 4$V:t ]V#J鏹yG_wuKH-?s&ڵ{Nj27f~|yyH@Q %‹!C_0pKh]\zU;魨U^bXr6uR9y_ڵKxXV\ٿv 29C=O$4l8xC;ӝ_ZgϞȮׂzqOw3-\Pָ[oy4Tp_1}y|Bh0L f0򲳳͛GW0KaA> 90!B1˻rQp5J @n wȡ2ҪC^ҒfA ^2!!+;`Z%xɴaZ !l-x֟rgg Мx Mxc(BZiպMvO8!ъNаHfffn4M^vfeej7oTT7o z}BBNKHH0 .;bD\Sކ t:dٲe:uR(֜ =٥M_=зW5Ѽge$NkH,UwΖ3;BBvzZyO]ntFV? !Z{f]#w!Z._!Zk:(>~].+vv:K5nc"єÌ`ٳV=ztEEE}6p8?ڤ^~Uj ;v>,_i0Ə?apԩ˖-^tibb"_8gΜ(^5gBB.s)!~3<#|~iNN˲Bk. =ٶm,Xh«yW6e:|ȓA Dq=tG-8Bq}\7/# O7iuٲ˭AL+%RIH2!{J%dn޽ƍY*_oL>e+?G+V\~D_ ƭ|L5<#.?LG|6{dOpz;Ξ_R'NݻZӧϩ'jBz̀VZ_!3+-)h4sr}Ahh(0֜ f67mԳgOz uϞ=onXm֫W/s.\?K..;"%޸qw׮]9rPo߾[nuXe1>}dm\.d'"-muDDJرcjJ1"""?##F٣GZj~Fz0wgdls~ݻU+=9 % B~&%!UwiEu5dZEH̄d8Ǚm܂kȿvU/֯p޲囸X)vu I|,KHme9|0TjJiuuuddqn!!77S( -X2o‚3_:q%]&=::Oѣ̯*L<ܹj$Os9w{}̙ BHPPPSV[VJjh4.;"=://y7ul6<ÔXN9sott#G$ =i‚|khѢ֭[n\?cIw̠t$={;rwfo:::fэfov8NoܲNp93٣GW_yx JBB M;ϴ OЋs[\K !'8 rÆ{.w9__oop.,jS켖]HcY⃫\nFx|Iuu%ƹ)>s!J{^._XZR|zaAiIE9'pj' LRyyܹs{1D~gn4hAv7͛g~GwSNLn:~i@@x^lYUUҥK eM{NzSPn ĉ,Y"ёx?&$ ח_fDFF*JҥK+Wx~ J0B2 |Ef`xZy.30s%*>e$cB=6G!C?K|0wGpg`y-}QQQ>>>ftƹ)uDB?$ոL&VަvBF1jfmO;w3jy7u8kvE8*4岰>{)UHan,*-).,ȗ'/?_?';f]$!00T#wmiIjţݪU ]q7 :'\z=tй++++,,ߦ׶wBȫZ\\\^^p|/?ʕ+Ӿ}{fDDĺuL&ڵk#""QF-^h49ReG 93k9/#OK.VdV!ų 6Gn֦M>;AƝ4q⦯v7f/6t wj;ì{mjTDz߳Bb5nc\nFFٳggϞ/VNw*O>~s`򊏏sGt:=߅ܑ~T====44T :¤UVVRJ<T*cbb %%%?FyDžeDG΅SOGhN:%QamŊOjuΝ/|͚V!!+-)NIYT*#""5APJz衝;wr6+Y-[&Qx:={T I^Q5߉߼aC$ιL82!} 9ٝ{f#766f˖o[nqf= *U^۽yO h5ؘ?^$KO{֓]a]nxtP87;o@heY8(, a6mtyΫ>0Z}8Evrd`F)j5VS*U.S4<PJ O p (?K/x305Jӹ}it}iɕ˗jLp vWj*grOVV}bxdרW]rYz<veFQ;m`8NPx!ժj&Up ag7o^PPPv6o 00m۶uk \{JRZ-U4:eSN]lYuuҥKǏeʔk ݅KMM;vl}qyĻCuB׷oڹ8xz  ΁+3f\|9&&F }޼y/!dSLyG!-]UV.]R(j 333,YR-Owrî|Կ=Z=9沦qؗo K_M[z] .PJϟ?ߥKs΅ɤj FZvBFJiuu5˽nKڭ[ׯ{>tn^5%yu>p=VXZZ4#஦JuKK/%ޕrǵԭsia}~Aֹy} eB Cnn{ァAXxhLII9r$_2j(W_}|…> ˹񈈈u֙LkFDD{?ӹkRzٳg͟?ʕ{i߾}@rufo1|ڹDx^Gktا?hq?RKJJ CCCu:СCϟ?;scbbJeLL̑#Gt>0;v\dPexjeeeCxxL ru]8.F9Z)=Ν;*ItWڀ>ӡ/=c]&/F~M!Mђ|'rP/970ͳwe1O_\?0Xrrj8*. n=z C^^^vvy20p@8 7d`rC 7<@n8 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7UcLkv|I\-tis.c!Ug]Z\{͗\!8Š5O0AzBm/p7=!a'FnĨ ѯ*w_|/;X糓cBȅw΃ 5Nemwfu~ΓWCÄp7ʞ`G jR7!$8awk)|lJ!Wp%>vVBH>,qz[|lNJd΁ ܐ ܐȭvؗ_~j###׮]2 0Jz=*WtxK :.!!`01|&͛}+VTVVرc߾}"Jiuuubbbbb͙3'**JGEE͙3vQ4e>+V=h wKVkzzzTTtԉ^PPyM]<aRٽ{iH}>.?$\I.+ԸC5;cZv$)[r;lk/ <]^\ ŸnSYatok͹ &0!gNX,3!No  9={8p"Jn駟}Qe˖[n:vUBH^^^XXXhj͞={l.]:uDaÆ%''WTTTTT$''1¡¨Q/^l4SRRFYZd`ǏONNNJJ1bD||DO>044444ƍ˖-s|ܹs͝;.4MlҤI&Mr(:lӦMvvDS!!!}]zhL|!20!2053 ])գzS]T4͍:w=yo^8F_7"JȈU(!BH87wCb,3+ы'(O8}x'o r+Cl|J[®[vck"F~ME\20!20!20!20!20!20!20!20!20!20!20!20!20;&osWC?)]@}=̳GV;QIG{)J%*=~Q Ұ5_/䗊q p20UH*$~ 7xaJq?2# j 2"Bܞ={jѣGWTTz>!!A%$$ Gr_Xff`?~ rzP2eʔ$^?bĈdpΜ9QQQz>**jΜ9uG2KW_EGG>̹ƍ|}}MVPP ٳgl̚5w̙999u%6mZZZhܿѣG%j;Vx۲eK1c\z/ύFڵk]tԉ.4Eu-ԩSg͚տc ;k陙֭[-z>LIKK#Ν3C ѣ_Ɵ$ ۆ'(.զ z^țc߱:^TT/… m\a+WܳgO-XW^B ٳg~QF-^h49x\۷A #""Ҝ+Pmee?R 80##_?`",,^4i_?9((CVVPS$%%%%%yRn!!!}'5" 7d`rC 7d`rC <()J!c /nQ/=ZI>q$zՖ5grn,/*q@ɺ 2fwp:=Ûͱ`? .Coy 5@ZcPW! @n @n @n @n @n @n @n @n @n @n @n @n @n @n.?NZ?nDw*g#,__X+:a %l':y_#dhqTn*QҘ FFb*k3׸FwkoK$B+%Ec_(_;ls;kF΁ ܐ !c6 K4j n޼922RREDDl޼/ :.!!`0fnnnϞ=Zѣ+**d"h(%ޏfL0~ &SN]lYuuҥK9sDEE9s-;)S$%%#F$''˹9Ѐj 6LFGG:t/d7ٳ B> 00m۶B!',,l֭B޽{uqbtŋq'%%cǎ rҥk4\r{xBHqq_qq1!K.ϟ'|Wj:<<> 7N6/ԩ0>|aNNάY|}}gΜ#1gϞMLLKLLܲeDMff̘1qĊT !Dӕ>?rf|'f ),fdd+.k<]v 6 4(22ғϟ?)>{1cưa***:c BH~N>q܆ ,KFFFUU^ڵ+!dڴiiiiFqG gcǎb8qV} Å\ҲeK1c\z/ύFڵk]&)x3/b™>zi]&_]]}{5a C```YYY -5L:eYZͷm۶^{ŋ,ˊwo߾3fO cƍ111)7J+++jׯ_ 4 aaa| &L8/\z믿o>B̙38пc>ң!D¿HOOOII[nM0aɓ7mķhjfX,˜ߺukѢE<|0!ȑ#'Oϟ>}~j2\5iw1!\q~yd@׫!z>Aam}䜟ם~K&yrrdʀWÞdIg1s^ awsyx&~[&m̓3|+6DZ8j9K[h=~D=o}RRR233'.G!%K,Zh׮]|E9z)S|}};gQi&99ĉGyܹsFqȐ!=zޢfoѐ{y';9L< pCܾwj)PWF[o I& 婩eŏ=ZK?S:޼y .|w]V{L3h p˗/OJJ"$%%^ZMR^z饅 n۶Mz4_r={ڷo/FDD[d2]6""/5jŋFcJJȑ#ō-XW^B ٳg~嗥 V-2UVUVVv!>>~̘1Bl^tZK}رc;v$F|||PPC=B=._oߵk(^x׿UTݛ_ԷoAt)SIw* IDATBCC_TUUBҖ-[֢E˗5ϝ;tܹsZ۷?rHFFPT*G9}ti4 *ϫjJx؇~K _|_|-&WM]-΁$}V$4M@3 @nVk on(aC~ $\BH矟' ýnI* ?UF}yrjʳ0>|Lo߮pgٙ @a{ɼ8u0al֥n5[W Ma䋱=y(.Gae-14=[u&|{xދ뚴 ~ p@n @n @n @n @n @n @n @n @n @n @n @n @n @n ѯidSH\&yT~R֤؎t#8oQm @n @n0Mqq_20AXV;`\p͑**""b|^OHHt /dD%&d`lӒi0Ə?apԩ˖-^tibb"_8gΜ(^5g1{NMhrj 6LFGG:t/d7ٳ B> 00m۶B!',,l֭B޽{uqbtŋq'%%cǎ rҥk4\r{xBHqq_qq1!K.ϟ'|Wj:<<> 7N6/ԩ0>|aNNάY|}}gΜ#nRf!Ws&49f̘1qĊT!' t'|Z.lO>d֬YB!eٌW^yek.33_aÆAEFFzO:go3f6lXEEСCg̘Aׯӧ9۰abȨ]v%L6---h4߿ѣң,55uرB'Nj?_|YxxC^w^??}J NG UUi5A`Mp mH BHƾ gnu_䲪uuf1 eee:t+0 _td2t:ej5ζm^{/,+nܡ}f̘O?1 ӣG7(z^~=00`0uⲲ&L0q/rկ}!3g>~„ 'ONOOߴi߾FV6bc>iҤ~ph5iwCBȕ/!|#jK{m!y_ci}FĜccs% |Gg*]P%:,;W7 c1O_\+Νh0򲳳͛GQy2044V!JRT׬YOX,w5۴ii&__蘘mgB΀VZŲonݺӇ_rҥm۶k׮ nF!%K\rϞ=Bt>>>g ®^&^VVtR6wG64v%p_uVa>&|$51 eh=Tk4JRcG;*#z-?i$<55b,^s^KzbJϛ7o… 5c u 0`IIIիW شiT*K/pm۶I`|Ծ}{0""bݺu&iڵ|Q/^l4SRRF)T>|xppt"[jUeeenj#l5,|GcǎرcPPtAAA= 5|o׮]˗/  T*_xB_WRٻwo~Q߾} L&=w}~ aPUUEIKK[lY-/_LNN>w\PPйsΝ+p6ɩUVZ ?CqbR//Odpi_{5A$z$DFF;vaŸ8̿^BscbF]={֡0$$s|)O&U"a||| ңGƎjq%2 ~'hbd`rC ^Kx@?4O0!?yVhsMww]Jw<cv|&gνs93 O+UH] @@k 5[H)} @52LOO/! !0,i?-G}XB@a֭ 2}c!ɤRY"H⦦FP(644Q @b ۺukDDDuu5$ahT\ϟ߰aCMM $aƍ'O$ 2Ν">VWW @-_PWW^ŋxVUUl5GE ꯼[]]ݕ-fp:s :#vr?d##;wٳgݺu#G:TU2o߾15`n\:s"-J_bBcn^^^-Fշ&~7n5CUOnT&[3//O s>}`>ފUׯ;?77WW##8B2Tפzṕi Z!D {#K+͛7&Oo 뚔YXZ/V ?zz'3 ###N/GQ[cq;#GR6$! \*¢L[!/Nq2lɒ%ƍ l/3`0hddaq/cH&S}p n #ܹs/^ܳgok*/+l˂ .Z_=<?1%BH kү_r33vH}`&Y+**-uM]\zՊe5|= :a{}Ī-+h$Ǐ>>ڲHqn>ځ9q5o=QwҍWOy:3G.h}٫gh` CmSIaٟΝ^@7¿yZ^;0%T>{th @x#6t GnYUUUř7oN|" :BtpBtpW!v@&6KKX==Cn(oXBБ h[$I˗?jo_:&}M66Vl!*CH,%FB=}}CC=TZ/,X_ّH$=q:Àae0 `th 0u]e`ܿw7RT7Flxwl Rۊfѯ|!mo_:wnz( X^e1.#F tvtIҗ7n\4ӁQ Î;z-KIQQ`^ylbn\v TYUieno?:(V5UI3a"D` iLkr"ynM8ȋAQ` <ȻusYp2`|7e.n-8H2dX>8m:x2N.20$~j:Ea])¢\_]|6cm_-DVzzz5\::L*?Ɉo†_s=ظ.1`00koG&`sŏ4.n?P[S@-(dHaPALD$a~1mT-%ʮw1XYY9n8 .^fM),еASM1~ )jjMЁ3S/\WVVL()h/(.o֯z}ªߴEd !'OFYs8r|ZP-oeϞ= ɱ2/]Ҋ-NN?ѾΨ˯]6cƌEYBuuuuuq5c`JHG,J4ϧѳgCZ8Է@Uma-jϳl(GCLڳg#VV?_|/@x2o(T:<Դ_~.\8qb{5ƍPPPPPP0nܸvA3:K`@^=ک: JoBad6_6Njqɳg<<,,fY۷Aӂ1͝Ǹܽ{MW a2ɐT*Jeۭ˗z8q+ b OJ7~D0 +))0q0lx5ǷpQJљ{mwuq122{vEc 7?_ȕ"WElÚ;oc;x5Aĉ<Ν;;wO PDFFF3gܷo`09sMYe*..ѣϟB͛71L\aQfXϟ'$ʊ#N8accӳgO. -Ay"1%7l*,,$*T }Pp+33&aV^QaaiU^Qa(GG|}&3y9uQ >J30X|w\YQ^VMr/D"  w9c5ӂlYmZ D,wQ5kV-9sW^%ʂC Þ?~Ze˒ejI$m۾ZGљ|77WEk?|xŊchѢګWO-p^h1@@y"93cJf={}0?c}yѣ͚5;9:NѮ;[@Pg`˖}{wbCS#BԚY<5X-MM7ѯ_ &L0app30 (!qcJrVS봳s@ ===T`0(*s@M0 H$Xz>'NG݇޽{B윜"%'B__o6m.--쳅ZWCZxμ'jjjN|H(@C9v۲=/]dժwޓH$EK-宮EbII)ͳ/-0զ}قZ77i."!HH/^x^$єU(nn{mjjJLFTB˸pw^"oH)t銨T IDAT2}}l?hmR(Odr(U|^)--_gcٳ>@ƍQgkSSB5&:Y.wpuu5qdG||}}ݰ&󣬍xE[?!$d2YD&L&6#Y ??|Сmt 7󿘘;wѣG#ѹsLLLh* Ԏ;nܸѿ>}K. 8իD% >lhgMf&&zȑ#Ǎׯ_>[{ĥh+@LeIp!gʠ՜!!1 1#?1F:Lb[,Ya?J*=u4vq S"S?{K)jkw'\¢jؘ B!a311pvu%a9|*xPr!N~"]mP_GVUUNaع&M26)>]&Ybָx|ꮫMMIZGϞʢlf TD<4~3337oތfY[['}WjV"!$lJ;ٓ:52ɺ\MLM33j6JSZXk qg``)-DtUZuotJc  O*FSSSJ9m]?P]w!$)]`nى^K`0ZU @o0xBo6Xڴ$PʘϷjkkh!T򥥕DE̬mN3Z mҔĤ='(*,8 S7EfdhqEwM rIQwﮯH$ҙ?}}C_vFx|۪ʊJe)l;N>@[ JTS tvt ~>MQ?pР驔 {f^†355-//ߟݖw}OOO] c&Y[sa:;: 'Ɇ Nӭݑdb6>c\o8d{wF22r혓` :0H }AaÇښ)ܷ7 tazDA\`q!L>t@t===#Ckkkssf)BX}=}#Þ=1V!"g֯]TV21G9lŽk`b1,׫n BC͒fB7oݾe؃╣<99cƸ RY&d2d"0`ùX>0h47;:dKWvv?Vw5sqRuUd;`:Kө@%=z| a@Yu GNvt|^6kFtI}:]k޾}qS 2SLVGHVܫc(FްaÏ9 {~Ww5qr=BH.(Q9N?uQON 8{fc5è~mm(E!}Ϭt:;SetfvF4W_[w6 kҥ-\be:-UVTTdjj{ilYYY_MJJSiimfwՊD -zpbYŏjw%BuZLȌЙ3C%% ;w?N}?;8*؂9O\}]X,-^as>T.Rx*ۥQ\_ΣGCnL&[dI^^޲eKϟώXKz5S;~ݻwwϞ ӧ.2L9ڑ3ag!NJD_\( ayOmb-,ϝˊjaiXggg _4)=~#GݹsGڗ%%!ČOedubNd+;Q;}߰aǸQ]F2}=[[K}-6])rK⮝:sI8 0!N[nc[pYYY/>gb;}޸qs(GTjjjuҪX^^niŪTaP3BO_|oy |u;htjɒ%=vM.c~~5 m4zƌߟ8WKU[S][SݎFTsA~~&BH$:thdF0L,?̝\;#~+Xr/KmYx\k׮usw/._ kk1 #rj5j0gE'LXZG0*kNL|Տ?p}T˗'OӦPvZR6ʣF9>{:q{77;ߧ gjj:iR@޴ILMMtX8Ԅf\+}}|J_x+u<htjܸqϿiFI}awK(|Qv_~9ݱGyIs,.6p}ML1 c0<];w3C9ʂǾ&o߼ٳT*}=3b=~5U  x)===\H{O'}}}bZ_XX(JTkbZO<ׯ] #e~E^ޝNNQ~6rlr666o^%ljC=z˯6TUUgffn޼SL|1ѯ|Ê7N:lvp#e⋗.m}}I7_orW#GdX:F)9ʍ~rۨLeP6D10 svmffv^\.ں w7t(WX,!Cb&(P<]ReoKJ7o^7...o*{fɺ^'rN",^jw⢢>'4{{{45GY9FurgӦUUU/_~p\3gݾ}'-{%cbԷaؾdHmR }:alʈ}cΧnu%߆_MDE(6Q޽jO?m(L`CrPٳW$֬^c+Wh Bݷ/999%|؀)S ʆ`Aam r>l!8bm _Y2 jjjһwaÆuc6~7ǔfnF3uk…v'K8'hw(sӳͳ=\AxF #بvwI"Xpey@5@࿫Wč1}`;1QIh :`˥ cGv>b:`<|#  ?) !ڂW{+݁6\zWipk@t ̚;DIul%]RN|ڂΗtv 5cGχ>J=R/@ף\+P<hS 5@)5Qegg 6 fee斓CuiCbƎ{MMQ]]pBBBjjj4-)RƊR ) tO0,==‚Q>/'lYd~CȐLMl6{uuu4B KQmannHy^|ISrpĉ.] dٶ/^hbx͚5|>GFFJ$ -%_Oؚ^PP  ɻL&#DW\ ÀKk.[1++iȺu֮]+ ׮]W_iZ\S(ec)R"EFFWAAL&#ܵkWdd$YehÀn6nH#t@tpp(--%>"RˣUVBpʕFB[___[[zcccCCC[_'}(/r<|PZ aUUU>ܰa>}r󍍍^zEiaaaa۷k׮ݷo_UU],,,pi`cc\Mk l,–EHOO_~=Ǖi}̙S\\qΟ?_[ܹsZABB¢E.666L&Ʀ!$FɔH$|A^^RXٹsgppJ9NJJÙ6m!~e˖)J, &O,l6@F[k`je`ǎrgG=uԃ ´S"VXx4 SL4obEX20e6m}aee_"i^|ҒdBkkjr &f[B<*a2MMMf6Q jjjZ4CF(ƃ<<<7nph݄ׯ_%yyyRT& ORQB9`ʼ۷oq d`ƍ>>>X,7nѧ_]]]WW{,YB ,55͛7rr#r/ Ѭ& ...EEEBWWWM9ocEXJo@F+++E WWWPkU)󤦦WUUəVFeooX\WWGrr:}5 "ʌ2SvȠvp &#N4 _Beee]|y޼y4jP'OADpϟ*&լU$"8X={|r|RVk`O<󽼼2:,11ȑ# dMMM'Od?*0,**.))V >>>ɍ)))@B-e(K-&,MÆ StO=~xccy<'9sԴu~$z%kRRWK ::毿Ztb=ET윚*/E@su0 !M6͙3^73PDFF2O>dW\;wLJJiQ0BpŻvڴiӂ D"YfQQQ{FEE_G}{;;; N:|ccceeK1SYYYQQMhR׉(gܻwۛ\My#Gr8ӧjVVVX`EC-e(K-ƐpO5ky1'!???K<$::}u9rxĮ/^[XXsZ[[={Vфbk`ϟ?r[liAh.&&innDK)lcjG=#++O:5,,Lfٲeޤ+W"RC;wR{㏕W=>sfw, Hj*V^MUT50=<٣[Gc#99""b…ģtfsG1 "𪪪͛7cL|:~Щ1ho4{,   k 5t d`20](E=@d  ,DžCqssrss!:}4iTWWp8ԠYYY^^^,kر7oąl,BJC$((Dm"lt!7}}}lٳh@%v177OLL$HCv޼y/_TVƍ3fp8|>?,,~Ô\(5qڵCrΤA,YHDQq9Æ k*T_J :ڵkUՕy3wxxxAAP(LJJrww'{+DW\ÇK.m[nڵBpڵ_}U jЈe˖466feeBXQ6[J!! V?^^^2s׮]4MVxHz(?LKKkhhHKK۸q#hS:lwLCii)V^^ﯬY_. N>HYMME-[vM9?5l? +///// oqU{սuP֩B7o\lY 8h*20:kkkD.bʕ+cǎŅo޼7n\iiiXXX 4ť!TXXڂZH$cEXJoi gH儢~?˗/D&4MV& cJJ'Q IDAT9YaԨQ.]%%%%D޼y3c q=E+ELΝ;|#.\cÇ㏸BaaݲeKknrL(&+++P nnn4nٳg=<<yjY=O$ϟ??fBV0LD! fΝ5ูkiꜭLSC>Æ rrrZn]MM&D, pbXeNO>$55URZ.O::BH,s8Tqiv1@ 7ȅuԩ iiiSNEX"##Z>B SL! bXe4PYY٦MS?VVV-%'K,ٿ9rX}}=˞`mm]]]Ma,xWXd655!Dq.555-(c"ec7n$&)Ja7!**j_|qI^^T*dӧj(0ÀJGl7э7|||$ X,;;;3n8OT^z޼yr5bF/..^zuhh:# P:::D"M jź \VZGizyyɭ"NmllD B h>r),+Jʼoz~ccWGՕET̓>_UUElx\FeooX\WWGrr:}5 "ʌ2 (˗'LS\\:i$| !ueXQMC9 B'Dϟ*&թJ(oc"4sFV͜9-(%Bz B/SNbb#G222 @f322N^xoaaAH***ΝkmmrYEET=5w2r]FML&ɉ|Rt#.G=zGVV!:u*7%0À<8UꞐ#sNy޽1e~a„ ,f]'JMeAÆ [lYYY.w^``fDUVx<zj⪜BӦM;qℜP7)IYVZG,ޑQRѭ<' MsssDD… GL&[p: +VpsskN]zxx}T^TV2ۏ042b0EUUUř7o0̠= c~y@FFFk u⭤SRRIg20:j'xJ n0h;  N|! @@k 5@):x!@Td Ɩv;Nڎ=:tPRvoձ?jq[`AHE-l~~>.윜777+++77ӧOs8zMs-hFuuuHH iA bƎ{M\H+RzK)42VaDm"tO}:Er))??חfϞ=Fεk׆*'l})](--q#xb5k|>ϏH$4)O.] dٶ/^PaСC]GEv> Rvʕ+7olڴ)""믇 PY7-hۭ+x:qDhhhk_GSSSp]/_%/_'`VUU 6+je`ǎr&׆S!CȝVUd2?ں!T[[2#4oer#D`__ߨg B?)5)W椢2ʜWV\(Sv1e1 .$20!_d2_NL%K 2T//7oзb566"B!)4eee2r! ]]]5-bMPec)o!eR/\]]Z(U)󤦦WUUwW\uuu..+wvr_1XDQVf`.?._|;?? >rcllL#H$իH$#III_蚚kҥ(QsjjX,NNNV!#eӧO7m4gzFPnB3v/rƌFG1c [0`3NNNϞ=ӧ*655D;wB??e3fbСC*:$H:KLfNNNcc[`>RgRI8?zhB/*SY}),NNNNH$3 &&"&&&00ð7oƦŽ}SBCC/_`llLoٳg*I  ~r5޽{ޖE)'&^Ù>}zaaTT`LH]?>rH9o)cEXJo)4{EMMͬYX,Ǐi5RRr>]/^ $sεvqq9{ "*WP?rl҂50CF.Psss'''^Ja ӍP&222\\\^x)FQQф &NH,)ܸ89֭[U!;v&--[[[|ɅCcǎp8[ndn޼|ϟ?DžgΜ=xJeee~)ve"KKK//z?5)G8qbȐ!rCGde+JNBYw___MoH$ZxիWW$Νs4@}}=&^q) D贵g`T>{thb%:2l…-[-n;d2٥Kbbb_޾ _}*HPYQodn?Ȉ`WUUgffn޼08 t[;Zenngee޾v E[RUcinYBש t]zt d`20] @@k 潇@Q0ڎG:T.­Xvv@ `oȚ999nnnVVVnnn999]Op8*5ӝ-,,Z!!!'$$F⚒bƎ{M\H+RzK)4dN= ;'d|>N5*!Cn2e7ٳg ׬Y|~ddgff=ΪJ۷'Np8;w4~ҥ@6mkkNn{MLICٳiӦq8&zd2YPPDQx…'Z[[s?6lI7s¤$wwwwsx\Ht>O>|tRz͌ //L혊[nڵBpڵ_}U jЈe˖466feeBXQ6[J!!MU/(N]vEFFԩ>-rPCM~aZZZCCCZZƍi@g=aaa塡ZV Ν;' Bs DkJCk׮[nٲޥHNpʕFJ{EDx36|fnA]]5Y"x__\X|ʕc7oތ74,,^>|f+ť!TXXz4B$eggɱl,4M3rV/(NL&suu_iT3̔GGGw5ҥK$00Ғ7of̘b<<<ݻhEܹs'1bą vacc3|W(,,r[lii< (&+++P nnn4nR@Y-[\Oaa!Y.7={8T: 3 &XYY?{ԕ.:=s<ǙwĚVi4RT-i--KU~<#̌;htT^B rIIv}v^{gGyO! Addd(ʰ0ڍp2l֬YgΜٳg+ʌ g̘q9EZ nll \r֭-[Zybi'rH;( Vjs纤I__<#L{xx0;@ܹ,--u$߀LFI6 Ljhh`pܹzOcSS7<"ի2Yp7_|}T~u{q}om[_n|5*\:!SCss3Gss3膆Nh I2%%ERio >d28\jAˋ W"WAjzҤI˖-koo&>H 8.3HNNwΞ=ǧvABB¡C(2 Zۅ ~^P(233z=eɒ%6mǎC12dnt}Y,G it,YLQӧn1 , c2 i\;s¶6??? __߶63ٲeڵkWNJJblvg/,_]] Cgb4srrYidtw$ɢ"ooo$.\xUmofqq1wv*uUbӿ:޺ص#Ww|y9e* N QQQX,%%%QQQ*xTV dRxE ѯ&)77_g Y⛬ OQgggjjjdd$0@]5kݻS< ~[[[j+T*{{{#L4^ɬ388 A6 p}kJ't8 dX!CFCC̙3JL)i6@IXRA!_ʙL&d@f LTBcz{{icyBtd-7 ks IDAT*M&=qJ%ƍq%0: 긆!i'rH;(߹sd2xպf͚0i/f7HG˗ waa!܅Bjժ͛7!r<''l6޽;<,u-FҥKryHHȥKP$111r<&&ޤ9*++_z%$-Z("rHkBE\!mX ̰0?wL񤧧~FVЇ"""hIWWWBBR?z(W ck׆$a & R)3 ar9&$kנr---*j˖-yY'0 'oܸܼ}!b۶m$IΚ5vcEEGII m<|$۷Aagg˕J%:_VM6;S YYYaHk'Dډ&! /_h"RT*-Zts77֭[h3BvG;u"#`!̙S]]-$[NV+h9 ~\6˗/qX]m_f gq`0Rԥ-[QZZZ]]}ĉU7 }z`^^۷w-ܾkxs+VX)11111%֭?7߼˗/w uk֬ '³I>,kĉ6lD{o2Xj/յrH$|?]n6Jϒ ~ԩٳgCaGGkvxᖐ >_hmm p(JׇL_!'V` ,E?2ϧ9sֲ X SK`=|[TT4}tTl/;::Ν;WiljjU\z5<<\&uÏEm֝ǮeU-lL"\xq֭<3<#ͥZ0:N%M}}=L&s8!1$IT*V [8rj0""99޽{<8{?|f (`0hZ n 233zB%Kdggl;vzMbLgXO;)|Jp/ c!^d +bcc4h-F rXd~2_8U\| "//8?ͥ(*??ֵ|ÇSu3fUn)ˡ , c2 i\;s¶6??? __߶63ٲeڵkWNJJblvg/,_]] Cgb4srrYidtw$ɢ"ooo$.\xUmofqq1wvjVQQXc:**j$** TUUj4k֬ٻw/ `2rss_u+d]uvvFFFr  LVZZ_RRd,ujZ1S*&MDdۅ >5]Rht\2,G!sa̙J?&IR@4^N ǤCR/L&y2 DO OA@L&ڍW_sduT*1+1YQWW5 iH;D9Da||Ν;M&hΆkX5kքyX5pwqG ~l=._P( .ҤUVm޼)999fyᴺ/N>( ,/!3y*⠠k\w- _m 1o<ƜWɺzbZe2*ooCY" >Ka]Nz)n6l֕CRݹsG@ R!V`|KQ| R[[ 88>812Ӭsj r:( q3mڴ>72+ɓ'r9 4w7 raHk'7͑ I$](l6LvӝwܐKw!qGB̄ي4 9R@(jɒ%`*z I[9y???jĉ- EUUf;tB7 YQnKÇybZ֢"N7\"%%l6xzzB!W"E &)??~XVVVZJZ-`?@3n8oP5~xha@@@AA1:.==h4޺u__b^XX(a9o!ZZZRSSz-aK 6:~~~PL9{bZ4뜂A.'rLX”9W^yJl6ۄ (ڵkjZx呑]]]ϗH$EEEYYYw"7MC${nd6٣R\_ŭ_>77wܸqeXX'|͜;ܸ/ĴW G}v4 y-C ̙]WTTDE… qqq^eeevݥY;A`UWmd {`ww`"d-2,,,\3]4|饗H\hߣW"E qyca4.]*CBB.]$` @ix?|x*//h4j:''>FKJѣG*]X]6' Q0!Jᅟ9&W8FG881']Ȕkii VT[l;: G9,2?(+Q<Al۶$Yfn())O$}v777(\|Rd^GPjZi&xg*!!!++ 2 iH;D9D˗-ZT*JE\vALui4挐ݑ tg9sTWW4I8|֭S "::~l…9W;aaa$X]m_f gq`0Rԥp8JKKO81rV=vƁ'OfddՍUո?tw݃/OFEf8o=_%?}\sƍ#Gl۶M`Jcƌ!I2??a(rrr-̣0 I5WH-UŰXCW >x0?{`0O,s!-R<>A`0˄_:;vp |}#t(0 `00 `F\a0 6`0 mp`0 3 CCرc#B8?{쯾 B i-RT$e4O|,$Iii=RF#aȅ*dSF144TP,[l6 18{5ĥDh4FMq$999Gay~A9Da}}yH$I2""ܹs.i?y gbbﯻd$vݕ+W.\Hƍm6IѬ O81o?b|\*k׮mooZ^^^Pr^HkB"?+>UUUAAAPؙyyy"-f~y Bd Ai֬Y%%%%%%#Jj${ݻw/...''GT*vرcb;vlʔ).i5 ׸CBB\*v]XXX^^dݾ}{ZZIe zSV+Ribb={+^oM I81J)aM>44`0SN͞= ;::^{7o D u@kkk@@EQz>44%g 9/S`)rz9sֲ X200w߉D÷EEEӧO0}`K\]`nW(^;vFaHk'7MCAoɖ-[֮] XzuRRd;?|qg j___={X,јrHL&?8#$Iy{{$p«W2m{7$&&}T+ N QQQX,%%%QQQ*xTV g+0d}יBrSٙ5L|,d2Yii)Eo Xf͚{ X" Gjc:+T*{{{#L4^ɬ388 A6 p}kJ't8 dXBFCC̙3JL)i6@IXRA!_ʙL&d@fR 53#N!:1L ëFDRTb Wb8#k v"s;wL&ј ?0Zk֬ /jV\ptAz\|9::ZPx{{](IVڼy3KR(srrz{{fݻiu/^>}:EQLX^BBgU`AAA,9r"Z,)jےO(r`٘r}w! VU&q=%FQp5Y" <4uSNv77wܸqeXX'|͜;ܸ/ĴW G}rJ>2pΜ9u}}}EEE^^^P˅ Z[[hH/!yvKvEZRScbbryLL rfx|饗H\hߣWy!E qyca4.]*CBB.]$` ,,ᏴDqYNرc\7{zznܸqȑm۶I$_ "JnjCd~~ö1Q&1pCp1oooG+V፸}zDIv񂂂戈w} ްaömۮ^qFZwߕEFF LJJZtiSSN۹sV*MMMJ2;;b0***ZZZ֮] ]+d"HEO#/hHKh ,L *dWƝ;wOH$Iuuöb4# yyyo߮ooo߽{7_KpŊ+VhjjjjjJLLLLLرc#7>T`fܸqoh&Hƌ#Hݡݻ~᧟~#qqqǏ;y0 +V7n\bb`p幹6o H_!')D*rXnٲE@y\\@w+TZ\\M>J~ d2ݻw VpW T*:u_롡&MJOOwuR!/Bx7oޑ#GR)se 2& f4440˙!!J_xᅰ0fclNJJdJ2eJuu5^~=,,LRm߾V\FFJ={vkk+=&s|v$δ3g~s?qDpp\.!R*n߾]Rkׯ_t| ˚;2ĭ0Fao?y &L0ᣏ>ҪURRR$֭[W^-`رcnj3f̘cd21% / ZpӖ.Y]'Dx]jSBmssA:N%lo>Ų~\g2p  @rHkUDZ4iҲe۹I)))*J~–CCCBA$''߻wgϞov; !!СCE VUBDfff^W(}}}zֲdɒlͶcV3 !:kyfXX`%̴̣A$ $ \5AdϟoW^: %"77 rss)^AQxb :&-_E>}zƌsW| < l4>..NHd>dH;+ıE ;Y.Z`nW(^;vFmmm~~~7&n$)dbdt`VKuuuA O3>>~Ν&h4fgg/YD`Lѫ:::g+˭V+bYZ2;wSG~:5 f1rHkRZ2kXUmX,1 G|7uSNvLRTrJRݹsG@ R!V`|Kpg @XoZmSS|_Q^^.lIE6 X|c޾} s`UUU'NnP(l6ۡC 1|D~PhiiIMM}뭷\RXXhZt:]%%%l6зBNi-RT1LSL'>VRV X"H>dw7 ySE  ߈tF֭[wnBh +d.-q;g޼yŋN3h|||\?i_6avE >w^^VVqYY(++ jjjV޽{]'/~z7 %m+B osd#AvGc߾}cFFFfddtwwwuuedd̟?_"tttdffeeeݽ{W;Iw6LfyϞ=*oLDr1b~cm n烃݃1SdKKü%\ܤ!*++_z%$-Z H_!')P扏h\t\. t钀%0/x5||)//h4j:''>FKJѣG*]]v-$$DR bL`ɰB&~! qup';VQUU&I4!++%ܾ}RtTQQQRRB5L֭h4̋ Lٳg$}v777 ضmIfͺv>|s\|9@3rww i<?XC:x>Xdwd/_Jdzz"}ZV7m&$$JHH˗-ZT*JE\7&~Tuõ6V`Wۻ|׃d yXrvG'Ofdd=l[FT𒘘駟:+Hu;i>رc\7{zznܸqȑm۶I$_ h <<3Z%Jnj#rrr-džW` yB`mqU|4w\stW` y2y\GGФ' `0 0 `F\a0 6`0 mp`0 3 `0 f!`BT`z^)F(eMMM``L& a/I666*e˖f(퍍%I266h4:!vwꠠ \>{쯾 Bi-RT$e4O|,$Iii=RF#]@h4d111WqR?~9""w߅›7orWۆ mvԍ7,22$%%-]Iܹ JeSSRvuC*eڵ+WBnAZ"I~ /hHKh ,L *d0!WR9p2d߾}nb aW*ybQTTjժ7xCsqA"yyyo߮ooo߽{`\IVXXhƍmnnnd̘1 ޽~===t˗/ō?>..ɓPh0VX1nܸD4UoBnAZ"9E|,JKKl٢h`cK ?<..NHbh<}h鵵P?.X@&޽P(Z Z]Ri^^wMMMnnԩSׯ_ 4iRzz y WRp1V+***Ν;400v7nMG=J)a(ˊ+?"M0aÆ 60t86׿[7o0a„ >cǎ }L f0|VG"rssWZŔ|AGss3膆N[t}Y,r(dp8hx}A,n᳖o \EAI&-[kX$RZ~+l >44Tx)DDrr{ >>--n :DQ`j\.AdffzBקi-K,ζl;v`%K02dn WRVϝ;p8~Ǎ79VTT^ 7aǢEΜ9s٘t܇O<A8W_}ٳ'_3v튉xM\dKdr,X` c0?u7~{ῲ"Dsŭ[QXEENc]Y~RRRHJJGj5ݲa̙J?&I  `2rss_u+[RQgggjjjdd$0d%%%!!!N-Yf޽{UokkkZ;AR Jeoo/sI&g[BfܿRF#h4\2!s:§uuuPr td3$Ȅ twޅ\:̙3!!!rIkqy?iӦ~5øU`AAA,9kr X,0~";w.|u@kkk@@bAc٘K+[Kjd2Uc`X`Kz(>KFF%aU`CSN=un7!J:T;wvS2 oɀ 4!,|qFqq~;e^͸ iԩSNip&9Nqq1kn'&&b(JN[jƠ 1vb0<_^^^UU5qD f:tHPH'+WZZZRSSz-( ),,ZEEE:C*)))]]]f ⺅Zb2L5O|,BCC+++VkeeZD">|888矧%7n\[[EQǏ(*++0ӍF[{=8.N-..텅{,VȐ!A>K f 𒜜RTTގ앐pEݽsNEAȌ;%32쁱>RDϟvww>lJ>>>Y111r<&&"!vwʗ^z$EB!WH E qyca4.]*CBB.]$` ,,u]<*R^^hjuNN}-""͍tuu%$$(JGrUp8vZHHJJKKa &@ a+3gάQQQN&ڵkKKKт 6Ƽ)atˮ] yϞ=osrwy>݅ [}ZV7m|Tqp큍+`jC1 FoܸqʕM`0Cw=BȋS=scǎrᛯHHqƑ#Gm&<^HRR9c \~a0ǚ_͍ i S)ȑBٳZ(EQӧO~:hȄ!"77WVO:p+c6(((wOILuͦP(BBB#l%00);w^w6^ZmSS| 8 ];9xpwΝ3f bG{𯬤oM I$/nݺgygV`VbJXAmnn ã@QTtttCC@-u:ݾ},rdp8Xc'˄+[WAjzҤI˖-koo&>$IT*V [VH&"99޽{<8{?|f (`0hZ n 233zB%Kdggl;v c2d@%ģVϝ;p8~Ǎ79VTT^ 2a`J?~Ϗ:ا ǫzYF]bbbG޼y)ْ I%EEE$I.\իr8 vӚdtq>vK/rLBWUsgIDFts +QQQX,%%%QQQ*xTV-fΜT*?cV `2rss_u+[RQgggjjjdd$0d%%%K֬Yw^:P ~[[[jǠT*{{{#L4^388 A6 p}kJ28D.VȐ\K'%%%<<}]]={688: _pӀz^̌} .:sLHH+0\EkZ}\kϷZ2kXз,1 ) c:uSvlT* ]RݹsG@ R!V`|Kύ7.@tttuuumm-bm04` z:=;v{bbb~~, )߲Zr1c+XEE!ϙ Njmp:݅0wm|)///8qpKBQUUe:P($Gᓀ---oϭ! ),,ZEEE:nX %%l6xzzB!W\Y"AL&S~~)S扏EhhheejTH$Ç?L]HHƍֆ)))EEE^ /^t8qLŷH$>>>W\aJ̙{nWTTE]]]wupSPv]re3 D`U]wdE`ww`"8RLJyj(111r<&&fp1.QYYK/$hѢV(D H"6O|,FҥKryHHȥK,qo "IOOWTΐrFVsrrCmmmnnn+!!AT=zصk׆$a &°/|WgΜY]]Mˣ)k04`&^O'Lڵ o O;w\.xwCB %!++)_nZV(s`ϟ?iRjZi&LXgQЎ'rLBe0v=1}\ I`06߸qʕ+_0O6+W88Eu!2D퉉~[p2] #/Nsύ;V˅o~#!ƍGٶmD"ň[`0ÇT*;vlvv6.0\yTUU Ec} Q{QQpY+0 8!?jrS|`0aW` [ 1`0 +0 `0ц}㊦b`0̣לT+0(0 `Fv6+0 `0P`0 yw!1^ŏ\zV`0 Q;7otw'؋_N̈́OݼmH$oLG7}Ozzoo#}[/Qn[W2_->?F9)ϸU|?p`?pLUDo8W%}{2X t ZHSU⯞TK%Fԉsp>Ey;tBL =@ 1#3X~>D5poz ;.-޽Eq{^@mADDQ"jMN\lIbƜy=O-= I+xzlzX6hlhDT,\ .{yk6̰ hLgwۼ(;Ͱvq ;T֮O$Itc%n؛4{;?Qeň$1b}.憎ڕoo\U˜V}푪/黫ۂVmQ[<\۵cFH4'1fU갨V5Q" vb&}Q4#mC`099S'9@BTxX.=vW̌|vΪsD{d)=nqϾ~۩L7q&EIydV(G[c‹gIӑֺ+F鿵88Qw\wСVn&+Q=գC5+"73'EvO:^y;׳18?[Ҕfeajs&d&D@2\kjE+k["1&,+).[H[jbǿD[KҿRq6e8L|TRKD+ S;(L} S&>Xҟ+7!"~dzS]qL:;=%Zr% toezt>dV-HݾFsm]y?pggDj#ɓ;Cil)'LűK%Nuo/PNQ&'F4ҵXN(ʎk 0]onDE`s*z!#{! s3c6練0OL3(L^´z;mDd2lyN fY=\E`p[?Zgŭ,L?.rXV5QYяQdfaNšҌ]dm }_pmgN3> h ͝[}Ǘ2\B_6 RQ:gٲ"p#~ʹYKLDҺ|)7!:=t٪Y8ӻyfBЙ%yKiTGOp|靸s_AJ{ٲp =پjAک>bT23SɦJ9a<"vS-M;G/=0?9O 9ݢNwZbH祼ɷJUyFx7 *6c&@`bTVV޻lYMKqœ3 nt뾂[;8w&oghɐ+$@EAYl03Y]^[w}Inb.(e育""u_*{"_eu呎v?/P(\/Hٗڻoyo.N~|kF7Ge.CvKu[}F.(h,ezt w|pKl~)۳&|ufpy{FVm&,J} rf˼yӌX=?'H๵K&L5i\HGOXM/Ĝ>Y[Rc&@`{}݊hC_errrG`d׺36V\U֡iFL7v1ƾ2@aڡ3A+ EٺhvVJg׵XGQˆ,J´܄7Q ayۺIE2Xͻ-%;L:j .t.4iλ,Nr-[uD<}):!:B%*W>0'Atv*LFOyt 8"8$J҇g;9:d&9vIKr~t9@6%jK&ϕ Sd|SHƘGvV,MTuK'M KHP4?w9u+h"|eSg.]蹟defڰ,p: RoN:ޗ~r.=j5`ӣwYhR6їq}# jr Պ7{՗X:l [WJ` ؟]=sB}]pD âZDՊ%zwd_쮾SVuY[CQጸ(C;{xN{[yEUwM%>÷C͏ߓ9wu`#6uMd)w6{%yt*!#ezt&j_#q b!Bmun?xuN-.ITͦ0*kZ,D[]&_dSd;l}:0oj,N߲G~?`)5v-^Կuo=cLjjJSQ}x<۷o_r?7Y___ssΝ;y3ֲKm;-5z43"0x4l>]8 Bqt8ec2廿FKќ8~tAjkyE80 K/?DSF.ADήΦ˗dkXrRrQQѪ#w+*ph4''%+#5}\ b0edn[f0h*盛r{n|ޕ5M3KLxl8qfF<~rnAay:>e/|KǪc]{sfV][{dz4@~Rש^V? {.SPSe70 i=,cdKJpʕcR<&b(WB#"AnvC8JNM2V5#0Q5ctɄkϼyrr0ޝ d1ߛFEE޽{k*|ЏCr%,bfߚ{IMds`pUQ]"ޜ dOqqeeO_I!DcS}=K^*zM2Ifr`f?1 VT.Z7n L-#F^j1O-G[rJ|^Y\0-w~(:ڰ|YxԨy]6ťgd]jMJNyo?Dgٽ5k֬\?} 2'gѩO*% c7 Y~,hn:Tw822M.Kߢxgrz^HSYYaC[ELd馌L^D/, Ž˖ݻl)c#Xll\OwhTfr8ŽE{! t<'66njG`:p ݔ3[T>Lj&M ݑ7 /E`9stecUApM-ljՏipx/4%Ds`#03#:`7Dhy;=vxW 86Ǜ8q>Z"0(I?Qx"FDCAuBB|tvj!F`zdV|ŶȉppID4)*\;%i;s⧧T u`#ӾJN1=+|w#K`};ڜJ90jOϏ }u99><=?'{oTs`#Y5{vV|tHDSS ϬΟb 9"0Qʟb lG`ظh$#RHڻ g̘3zztt^㸛[5.ެv Vaioo'".nZz=FACW7%p]ik:u} 0Fn f7è[#QIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-task-result.png000066400000000000000000002001071351617527000254350ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATx{\Tu?܇+b R-Ynږki[jxɼyIHRojV eWPqx9sA<w3sfޝs (J`FA),5l&jgK,]5ZvmJBFUMMM߾} !jBHv:B)!PJ !['jB~BO?UWL&___ZjS(}(wV1y$ehZFT* c{&T)%RJ2,exFQ(RATJ^j=fRZkeFTt:FCYRJB#q p0 cv;0,+~qOTT*F/[)FV+&C) Ka9lVqPJ)e %!DBT)UѓfJwߞћW]mWP3[MeyYYFjZnqyE`µankn_hfeYSS BT3gr0 0.H`, wS=z8e_KqENoRZ,۬aN ߬7evawج6ǭYJp)yéK>"4f+sL9K)!䩁V``Xjŧv8,vG:BIVòoHV,%j7']1X/"HQZW5nm ZuϥEe7Ƶ{KJܕ?Oy!$@Œ$&<ԥeȠ ^.ApT*Ƕ)@AȯUCovEMjpLȮ:q,\fZjGqX-檪*ɤjj5w> A;|˲,ŧ+uXr{vi W+x۝8leڭN3;eY&FYev%} :=Ur,hvT\b n; wW"1}cjng(Xㅾ:?DE2Kò KjiTi>nPqӞ -|>WK݁K-Od;{{r`ZnO_*Ъ:ׇ TDc W.\֪-'#u~Y_אѭʫ7M&m[iYݒp,,T5:a?_hw{O }5Zs]}}}|GijPr'J8q{*ĉNilYqR#{;n ,0az/_R9!DT<f!V8=e\+5O+)(1b%w ja)ZjzJkʫ ! rSqoZvhu>](V'JwsGBHK?UEU`i|js״Jx,rZ` Uʾ]w5B7G+* vٹۄRŒBKϸc̿=__β 8*3>-,-Zs?,(88ˏ~[nJOy;U學* 7@p"7:e8x R0 ͩq RNm 0@-N[fq6]d00WJMLֿ]²) !>?|Zk=,8vJǭjv!f1Ueo9cmٵcH!#yWU*SF?{|+-,x1~D纏jEVܖe\GXDvbZfAz6%!bkYmuK !ZBT#*$,jVkKqثMAIBc>Ɵɺym\v.*z"l6RNTe!ہroV%Ʋ RŸr}ISJYqmIXe|שb7l?̕6ѭSŢܗ*JY5M{RezG,tbstT\Fn|%SL {{Rqٮ#dؾw=v2ߐ0n ͻ^n"DiBZr;&?蔲FS+<#w2Ӄ턧!۬7Ǡqf@cʌUFҗavjjsDt,,1E p pxWL@ .g?JY*BӪ0 P iO)ftw;-\ !va2 =XTV:J,fT*vVUT]R>p=R_R!!y={8eSp8XKٳ'>>~Ϟ=| ·\):W!v[-eYKY8X rk֭ٔe ?:q?IwEA;F\.$n~9*/MK6 miPatZ"kԪ V)\Dݾ-Z2}>>`_VE͖=/ѥ]js-6e+\̈́Vk?إM2Z*yA=4ꘈvtam?B>`)ȁ]LxUfkl+YQQY]k?q?U) sc7ni6Gΰ,s ~O*'!7\R*J|kUj 0> "xE>L:WՍ]:Tva,P*j͸>>>/(j:]O0jmR',aZ-Gpah_~:stt7C[,˲n-3Xo=IV6?޿ۓCdYJi\AJ[|{w{BPW,e)Zj;G[m^垓A2Jnu/?3ߠ>]jjO:eo =.xc_/~<{uCUqwG#*Ͼ?sSsӡ6g)s%~@ĄV{^~ .:\-ua]ݺS#FZlK ??ѱnu!vҷ{ε+QRTtB; ȿ;}`Fa;T(oɺXbVa:N=jr۳_\۲eK___lZweĉ{6([ lb޽)))DxࣣjkL[m~TjOa?}:xlmB[tyzCRχ:]Vi4Z\n:j++++5nY̢[z=!0 &l6vѨHp'S*(0XwZZ RnVq8wH52{9;wѴ-߽ooʽwǥR5:+?eB@ժZv,e6vc};Z,D` T*}|||||ڶmI~z<VxTpXajkkn=W.oe5&ѭ[1;-jko"\-{>uԭǿ"؞/$4 ҭL!+ZQ* Wpvz?pȭaBiOc6Ѥڕ4{X'M4xN0t'NH~jƻs`J s^+o[=i\ZUͶqӦWgڿGOk8U%[ ;mN)ׯ_τu|с؝T.m ڶkvӮ}h=Zj׋/:'Q\|ٵ3_}գvطhZm>%eAx.C=gh;&J^!.x?馍9kt%Wkʧk… w Dngd|֥K]:dWYKSSx>ps馍}~K}'u )=No.[|ܷϼmI ?oyՙij?]&}JHXh] :4$NֻCh]V'Jrs۵菄߽~/?''޽gK2hժzW%$ nSÆ [pAEEEqq/KV&||| $ Z&0`ݺuVu5qqqy}?T*/ܽ{.=azf͚x(Ƞ-^7C|VC=><{o߾-\ڙ޽b7>s3g-Yڭsk=P] dXxА)S5v 0eM\h0јBW0KqQ! 90!B9˿tQpp(>0 #0 w[pHBe(/k. 3(`HHk N)V MdZ[1Cf Mdf\{f]*$,ă]hv0JhnE`i:U6m 2H/]"ݺuj{޳ge˖N%feeEFFꈈ,.`0x((!dʕ:uR* µ{Uq6oެy9%r/>6}h~?)ƝAm4Dl1Dh``` j}{2}~G]fU4gnnnllN3fdr7\2"$dzR̊YgOx i}jݮ}!Cv-!C!C6l(ݖ9E;M*Bȱc>裧zO@o!';vXp% /mٺࡃrʦqghDlEhO,X0qD\-Y˲e,n ғO=~Nh)S$%% dkiPBI&9ϊ܇3N2ҽ{/.CyYiyYiAAoKE3_?V^q&D_(ϣ牞 2~,L|jo /jh{ |3 UV6npJСCZmDDΜT* amX,[n3L>>>؝;wZ;vݛKܹs(gϞҥDhC۾};uOWˣFrO&׆DGo߾[5]8NDZںZݱce ̠K4*:A',FDD;v]C#F}u܌:!}%"/>}]ƓcPBj "BZR}{xiȩVkjj(555MgZH,d<.uDtf /\yJqGFFL{;!$$$vO8{}頠 BHPPSl6Jfj YfFGG>|uVeXz!JiLLS~7f̘| N+.*] &E۷o+zr}vh.3(1u7]O({~62::Z诲׶mSDrb|0=< %d3!c !dgZGvMhΘ?fժUt>JkODgv͚#G(/+?h|}}ٹSùPIOk)%$,a?I4hp>㘘ׯsoj*L8h4+W^bEϞ==YSLٸq#5 )kO)vڜ9syw)N6W^S~u n4awA ѓI&-_\!wmyYO.>ˈTTG!K.k֬$dB ?xo*СCQQQ>>>gMgZ̄|@#mfEwĵ'3;lذ?./+8-mСEϟ?G%>)v-%?N87^G`U&gWBt:e˺uv>E՚fJiMM N u҅Ġh\׍kUERj6wU>}l"rRP\VR^VZ\T(_QǏ[`AO=YM%F]fPt1h ,//hA[^VG IDAT>Fs`>hjK&9S'{x +{$쾚VO۷?MgZ%7}5;/\sQ*'rNȰS,,%?UHOƹs~#'E||| .s>%o['̜9rŽz,Xf͚{oߞq 6DDDpG^tlNMM5jDhCcƌIOO7111suչ .ݻD>/G+\zv1+VtWv*UbuGqXNw9#ڨ] %##cȑ{NK^޽0flDل#}sa`!/ LwܗӀRz̙9srpsGi%cBHg>YcSy֭<4I[Z,O?)<\tf;zٯ>5q*"=YP_(ϣ<GxuV<6W}||gƌ;uz յ!vP====44T>ٳa&TUUE)=p@=T*U=eee?V} ƄV۳gϓ'OUN}&J EEEi4Ν;p%OZ=w. STy:@ӟAw#ϋh3(-zh׮]ª\rMR.e˖o&U:={d %A#!u߉NOBѱcGsVd G1ĿgVNמWLLm۾n߾-&7YP{ݞݮ+jcbz|>]z߳,!YRW&~y2q9 dXxpc?h{Wxp0fPBA<a5i33ҪuSnmD;ҪhNII!=c~A#_%[3fsX'LkzѣRfUHJ(!dٍ&m'N/-ʯRCB+7@Ʊ4y{((~ \zСÇERQRBvRڵk贮U5;ow0 kt40,˲V{P(ڴiӹsowt-fw8#ztD`^t:Tj hBSb<SҟO y #0\<{Cy٥ p ; 1\zC_g 4)sOT*addר~*+ .].QxO[B!\oij>W~aYVT=!ͦ,@T)veee͟??88M6_5dĈ:.::^m6;;~@k>en=BRt:*Q@ólEEE~_Wq>h֬YӧO4d2-[,11ѫv;Wٳ6h,"0h(s7nj;wq󹭻v7,D`orܹ< e˖L8a„~K:uʕ+kjjVX%Λ7/**`0DEE͛7O"Q!BI{6O?補a)S$%% d|ʂ &N½q%-$Z[ޢ}=:Ds;xď wǎ裏z)>E{u;XƏɛO=jBublݺ566)d^ܹjرw\bΝϝ;G)={l.]$E"o NDKoNjRZSSYg,FDD;v̓^ASCG[ut.i!Ѫ)aEќ&{\.WˣFsvO4QNw}ޘ_+_Ih5^^ү/,>/.ܒG;/=n!h4JRT"9E`\?;?ĉקO "95fl6V+(!$ o̘1:f>|0ԬZD{Lu7"F<:DhUǔϢGhNqPS>.\b:tC|N&,܀B(Ӆ陙˗/޾)))5553fh/kҥg̘%XUV.\DigҨGw[gѣC4h~֔ ׅK/M2G!9E'(qu8vq I)te˖uʕ+|V5͔Қ>]fFͅa.]g]ھ}?,ݨh ,//w hUHHG*g!SV<.xvj$'ZD90<4sŋՋK\`5k۾}{>gDDƍkkk7l%=zҥf955uԨQ q***.\ػwozTj̘1f9===&&F)=s̜9s^yF322FU7ָG%㴼ELĎќ/܇qA\0"?(=D:<9{Xzzzhh^>|ٳgDPUUE)=p@=T*U=eee?V}G &]uD)јj{yI>Bر˝t*N)}衇v ρ5nI1g)hď х-ʿhh} zLE` >oL_TOHi'i#cDAqզO>1{=kŮi2,˰e`Y*L n;rЦFc~~~vvvJJ  @n"07-s`rC 7D`rYs`rC 7D`rC 7D`rC 7D`rC 7D`rC 7D`rC 7D`rC 7D`rC 7D`rC 7D`rS7vnQ(%LR rN=c6f}HYD]07hG;T&BKyvY{U>w9iwm}<|m y:SO'Ll9!7Vv`nC-X}}A{-*߁ }t9gan׮E\SN=/9ōDYwVw.t*+JXknǯף9v?6NB̓JH6Һ`QwOKs,{2+Щ&w ~r#t:u@خӦrS_e1HbNnDojaO$siCn~I%qH+!gkgjnB]!op;.쉰]+'?U/z5p%Z֩o@9 }8vOz,$90!"0!"0y}g={t6l BPսz:r(,b0z}||+{XVV;Szꪪo7QJkjj=m޼yQQQ!**j޼y9"[hի j#""֯_/ܪϞ=am999g5kVNN]h漈N:5x`w[m6[zzzTTuԉ^TTy7y&BPTݻwOKKk a^ݿ&J}Q.e˖ׯ: +(( 燅؜9s^z饃 .L:U"#M&dJNNNHp;JG^tlNMM5jT}h?c _|EϞ=}}}MVTT$Qՙ3gm%̞=w֬Y999!@SVlڴiiiify߾}Gȹlٲ'|۲eKcp)ׯ76l|2XTTԩS'BHxxtSN={|I0===33_~}ɒ%'N%&''/ zǶl""マ$Or/1$$$'@Ssa#_s` 4gXZl KYΏ B W!:!$sl^4ccJMBO|;5%TA.oGT,0 ]e%w]; <]m]PB1a%։SxwTy6Lt]h4׹u݈}pݺ)}'qog5#Mf;wkK|a)#Gm?h4ggg\5wnsJp\Q JgwUϴ\P>n7U ؙFSo}7$>D1\h              =Kq køND3&YEϋ֣E$:&l֓JZ=n9ךµ]JD+zapK˓J$via{Ub/\ݩGu.$OH ?}| |g]_r>K[6Jdp;YH  ȭ"0BL+hw%CDٲeKLLN"##juDDDVVh0z}||h|:n̘1&I=rW"0JݨyٲeKffh0aO?%N:uʕ555+VHLL͛e0͛@oSLd0h@^D`%%%#Ft śo{9>Qb+! l۶mvv6ȝ ۾};"?t֍eYnjر)ڟԎ;*JBȅ  j.]Dٳgϐ!C!~~~.]={GGGk4?Xb4⋞={N6Kԩ?>\bNNٳ}}}g͚#1gΜILLKLLܶmDNhʼO>i$ɴl2 !Dח?]KnEEE~ٳDJ)0hΡCk.33۴yGFFzٳgO| !?u˲7oZk׮iӦ}9rDz4\-['BH@@@XXX^^,X~3gՋsԩgϞ9sf^^Lt4\JOO\|9_NIIINN1chSJ+++/^أG'riii+VhժՅ yR[] ӹN_sl:I4Ry;q /˧/[j.]tРAVj?)ݍ{/%%e#A<VZDIJJZnM6MVϘ1c;v ނ ֬Yw7nݰaCDD8zKQF +Xpa޽J3g̙+H4Y^D`k׮СÐ!CƎ˧[,+V[hѓO>ٱcǠ n 2$((PgVow޽j*.1..NR=^xARӇԯ_)SIwCCCՄ+WhbժU\似s\>|8##OTTFz饗D>hԞgmժ+) [//Ƚ n뢝IHziXkBHRRwnLhk3p3g8%|wc> ) ͚DIis68 fv  @n dXxp͋flL_TA4B /RrYs[/nQxA[[BSD\[ZPW+ò\qbPB AAf|eO-+WUqiE8\%DQwss些C?/mqBSp5UzN]V9[%;swk$GtI>Gu)uu{~5/<=vVE غ;e`uEW ]O6~n7ZqlgXʲ,Re0%ȑCO2)))ć&-MNWTmq<"*zW/Y|XmRR|ҕ|+=fĹXs kW%Zq-H=jΣۛPŭH~Ws]]eg-[vMzn~|DCҩ].nkMb1xE)=u,Z#Nz-Q~vӃEH#VE4&FB              ԍ[ gm·#o҈wգ oړ^en|oh(یTed-5F*.ssQb+! l۶mvv6ȝ ۾};"?t֍eYnjر)ڟԎ;*JBȅ  j.]Dٳgϐ!C!~~~.]={GGGk4?Xb4⋞={N6Kԩ?>\bNNٳ}}}g͚#R'hxM>}ҤI&iٲe|L@OR0LFFƫ*sСڵ6m޼yxٓ'OrGa2>}tBHO:Ų͛VkFFFuu`ڵ+!dڴiiiify߾}G W˖-{'>O4I=s~)XTTEfNq?ׯ_?:"۵kɓzѣC3g^{mDΝ;\ʎ;uj.grrrJJ ˲Է~nhqŭ[^߿^}!aaayyyׯ_`Ϝ9ӫW/ΩSΞ={̙yyy90p !$===33sۗ_~9%%&99yƌuyzzkxTͫօկyZ\Opfeenջw2wv2q_`i9"s7'GBT*Xbb|`6M&62C iӦ֭[srr{ALI/ݥk2 oM6ۗ۔{M4K ,_|ɒ%w惿#GL2711ѣ\bXX_燅]ygt/"zh4 Âe˖Y֥K4ȵVjZ?{磻,^wPg dǎ1dȐ zH4C-Zo߽{Uĸ8JB^xJէOnS~L&=wy KVWWBV\٢EUVs9򂂂ϟz RNhvԞgmwqM\DѪX JEDuUlUPQjE"*R֥nRYHٷ@c^d ~𙜹sϽsdf +Em۶m6EAvxxxxx8DՒRfZIE Æ B\$ppphnnƷ|Mbðkc:82//O/Mt::[h4P(>|}H^i5X  Mk:,ghdLCa4400'ka6_u]10ȧ. pACFuoE ]o8:\^a="lZ' TkַQ/ua=nSΜN4BpZ>Ȁ B!T:EBIK]= zk~щt;FhIJ r+JF1t[Xv?= KB d`20C @`h 04 d`20C @`r9B^`4 042l"H.d`F"l۶-220@`h7n$ !vr577޽{[_~&&.{UOOOss+W׏F) d`ơ177_hQ߾}! E^2\.C^=LB2$~ qd#Fxqz5w7x0,|w޺:ޣG .+,,O>@Ǐ8qD33:N]ퟏ!02 _hO իWppp޽! NW/_qhii155œ0 Ǝ{ٳgsʔ)搄@'@.G! H[RՅ 8d2B$NǓI&k„ z$ :8r\.#AH.2a;r䈟_Ϟ=! . d`ƁX&&&#F(,,w555}ϟwqq!3p`|˗7n裏͛dɒ\qy ˹s<==~&PK0CKKˋ/W\YSS32SSS__߉';V n/VWWcV]]<P@`JKKN:b[[۽{EDD|7}^ G(|ݺ&888mVc8̜9mٲe֭ۻwo]]ݲe~K$c{k}S'F7nŋG_MMMv011zII Y< ɦM&Mp,,,0 /Mvڵ=((&&&fX20Ccffb SSӞ={ߕݏ?էOch4ZSSu1~:o(N={;v;a}@@ǧ۷<ޣ.{=S0J߉i4ZϞ= 0`333x>t|OOxBZ!Thĉ{I#m#` .a,筛7gd2t:ӓCur0eZ е`,6[?] ޽t C0웫%OK<~,nqeE]="Qc0|VX|833s˖-@$=dUH@W!0p#A.>UH \\&k0HFM*P6$@;!˟Y|J[o1m0uCd`b'M5z)Bd5!CM74sl8\NI{p5Ww/=2!9=+'a2F tj !ի 2-OiV֕&&P4w _-V4~1.dFYGr9qH˽;cnp_~TUUaoۑN5N䷇q'ޟXW[k@+(HaHwVL$aˁocr۩ZR] b(j]xQ\JWZXk盛6Ǫy&?POef[\]BݐS4`,H.Y|r[{kV+=z;7iVɓY. xR7Ϝza'6 >ʕ+3g4/m %Ԍ?_10_i,: 2={ ;79|R+ʊa"-oQ2x#Gaj*z5i$[|ј1.w)*.|s?2NgܸyÈhóg,,,x…I&۝6zNNNNNNǏYmii1 Y x: &ffaa={FH1?\KWRضV|..o.;;{ǎ!C|O+ONuNhff6k֬t:YfQ TqqwϞ=<{ݽnٲҒ`dff LMM E|b?SG7733SrŊ={L;oN޺u+@jϞ=,^0QK-I ܹmخ]~gWWWj\zȑ#Ǐիʕ+l6!_vm^M6k,z.\f͚gϞaj*)Sq@z"93?[WwNUۗ`<}ׯ;~:;͞=/68=q{v7*! T3""ݛ{PԈ=E2=X-M71p'N8qΜG89iŐ8159+~uߺuK(?Fdt:Ԩ: 0T7oj:yC?P H$JKK[x1Z#7w\5hH$bE4Bؘ?~RRP" jUEEEMt@ fjփZYYLLzeAtX37-OUegqGߴ=j4VK&r=~~l.N#!0"mA!$o/++yz$%D\3u(/d--r\.#$6F4hùNvd`H[ZFKWvMU߻%5ԥRt0vFu@S;ߵRB={A:ݔ4|1U{yC&U86:Kөh5WYsa'N6m6IQT9jtEEſﰰ9{ ~hw-0cw7zyo.c\o߾C>j4שM=ZAAa xr8e|աH=oZtz@\F# 0uc#43Z~/0גK?[pHm[y 7 &P6$ndee}W))Υ۷͞= P_'.\;5oGl#$2!3Cg ݟԷo߻w&=axh U%O\C}D"K0'%-cABOuڤ~@{W˻Bn\._d͛7#"?jǎ8כPed~o#n߾F~_/|73D%'Bv'NpssѣNJQ!dffs>}(TMWsDXo8+k.oȹsY۬ml7;;/իɓi9i[n)U$dF N O} rrrF~6n41nD4 aTjWFi,igg!E|ߞ:ξEQCѣkݩsI 8+ 0!F~۶mgXYs,\cF~׮4rhLjnn m|UU XQQadUUUiqh Eא!Է_|=7-}tjɒ%>[6okDQ&`XPښ3g~{_/X,V]mM]m|w7wҒ!X|!C( d $+عsǧs綴<}׮V\9cƌ%۷.ZDڵk׺{xY繹0TWHM]&JK٫hĉVS2Xlobb٫?w /N|ΫCiIݐS,2S#IR||.\|k٫u5o=̙3'uwh}',,,&OL?xߛ>yrGP+q^bo[b64?~| 0lSB a7WKT=yxXbP}]b }_~7PWg火 gq#0:۳{1cp!>Ae?jcTدWzd޴$ߴX[Saw"עhP۷<ߞ󍍉Q2?bCaaL&#jh_߿v_P%umHڮ~w& BB}6jƌlrmjjfٯ^%nH#ۃ? _~cedϷnh4W7Ç u~C wt<}k<R(Fc<Ǐ_SSx@*>_/r9UF;ř[l<_o |kdӧO$O6I ]&(ү_/_ZZZbVQQѿŪ1NH|411QF=s9rz4ya,_icÆH$8l58!<жhwi Ue͛W˦Nj$BhEV}D")**Z(..nPWscdWH|}7oR]]Eg *U:k7kjjN89x`\nnnwb8&6V{ONJJruunuMI۵22҄NO6mtqӦa1~ޯ\=G͟7wtuXPtن3[; 9?[K.͜R]]SYY1t64J= hnn^beQq5>jpG |||vԦQeFUwܱ2rp Z~R%QmGF<?wSSW7nB koð!CXiFJ0kĈ'|fWll```ssAOa@*|kfffC Y |Μvj6n(0uCTuPmĽ{r9~E¢>kM0aÆ1b3𭛣ӌGllpᢄݎ:I}`|nz{UVk?2aT6.Iҕ.UꀮELMzE Z)NFw=耍2Ka];>tx85;;afFөK޽2h=xW[atڃ(c; @@20.o!\WK6= t /@`h3SǏI=،Ysڤ^GUeECL|] @`h XYYk! l6; ??Zbɜwww&̙3dVV77nO?k3jjjBBB8NHHHmmB-iHK-ԐiX[[zhT ( co&vS~~͞3gN}}=h?nCJLLT) u;;yxBtp¤I\.ҥKSLavvvϟ?od͚5@ DEEI Ւ/'l> ? /((DIII G>'/_K..QRRԔeootdk׮Dk׮/tURoIcEXRoI"}]PP !u0(:V4zxH: H!cǦ566mڴB?Dprr*--%>"UTThDcvv˗aHHH^^^CCC]]]bbo띏  o}@;ۿR0 ~ƍMpd@`nnKh4 lllpׯ׮]j{챶]}T~Æ R.§SJ$6M'(Vk`Ze`Ǐ JgG=}CDӧ#VX% Mk8 !5*Ŋ:e` o޼900P1d㗷} O,Yr%sm544ssslll .r5555 2mb hnnF(n<!T[[wH.#r6mp8nBtt >W⒛7od2\. ?~LZS^^- _~K <8׮]JDb9㕎>uX{N0Դdl%D e`eeeJr#r/ѬjIE8\]]Bnnn덒"m,M KSSTJpssDxI*u j%sm)rtt|D"'vx< z„:VZ;d?@͝8q6.Oqqqjjɓ.PPPPVVVnny(B:`=z{v?~jjD" OLLԲ777Xob`y䉯J]Uگyyy=z1}`G4huI6|)6aXGu%1 OJJBorrrSSSJJP(U]WH%icI%R)A=ĉMMM'N`wXXX.x\Ϛbq޽b;[RR⯖BaLLLmm_tRzTU4⒚*H"8dtl`z͛?SjN¶%**N6l|2.ܽ{wRRҲe(:`D"ŋٳy bC ޷oG)))%%%Wݻ!%%ðӧO._zj[E@#mF3%UO^?!d|ȅ 0ěew̌NSܽGsë333lق3\XYYq\HԘ@5- d`tA W! d`20C @`h ཇ!r|b XvvP(doK主3Lww]gΜp8zԞR[[G :bƍO?BX6[R!!+H)...Dm(%ENd !dncs̩JmUbbGLa͛7ŋԯ]6sL#~wLɅ:\2l0%?5ΙH$5k@%JuRW`^J 6ʕ+:UՕx3wxxxAAH$JJJPܥV>'/_,.]GIY~ڵkE"ڵk =jЉ,{{{\H+ƒzK*$5=G8##ۻ@.B`ٳ'**_x1!Ǝؘi& !Юtit Sii))ĭ"&&& @ٳ^*Ϝ93zhŚ,JDDO?䧎-qqqaaazWo>[Wi [i!O?EDDQUǡ̭!#rI$˗/7?4,,LZTT*,,tssӣ=~~~JrX6[&(8h?77WI\믿R4YK SRRFmeeX`ԨQ.]%%%%SL!"PVV6sLyU+* c`Ĉ.\صk@aa/P IDATۺukknŘCML&S$!D";pYOOO&7ܼyK&{bѣt0>蘓T:-`0III%!l6u%roUY˒))Txyy=z֯G999+EN텭z;-Z(Q kAA--((@⠠{!B%dJRT*eXzԠ+loo7AX6Vꚠj`!C̙3D7#py<=AݸqC5DdCP, n޼ ۺuD"! ̝;ԩSb8//KՄ kllfqqq لYfܹycBecC(oݺ%J_x ?~|ŤH!0hhh81cF-ۭ`0R޻y&RjkkwL];Jpw\d:;;_VV"W8uTD1TVXW'i [i!$H8Uu ?~\(*5Ja>}CD"QZZB+V|=Jj30P]]]BB´iJRm:o)@j|́ia&_"YRxdɒ(#ڠ544sss+b(0dbjVZ<+WUa0mBstmmiLtlܻwӓnڴH݄ &|W^%7odr\(>~)0PzuZ#ڵkRX,=?~ѧL&?W^=o<)jF^:44TYBH,Gź*uub [iAS: !$~bkI1UḪ+ƪ MMML&S+#F\"RImm@ VnxhR&t+ **ZieF.#'NF)..NMM#R~ĉ,v]'Ғ۱cQ^^ܹ3eūV|>U9-B|ɓ'NO::Bhǎyyy;2mF3%UO^?sشDFF.\x4 jb www ujVJx #SUYod8NV{WWWgffnٲ0v;w PD222ZcSVZOIIi+O:;Й>QT .[tp:XN d`20C @`h ཇ5mD=Чm\._`almZ۱cdž E*4 Vnђ؂ B XvvP(dsrrݙL{NN3gp8V ьR[[G :bƍO?BX6[R!!Ҿ/0 KOOwqq&j#D(,rR~m!dncs̩ѹrʰaÔt\o񙙙J> Bii)C=H$k֬ **J*+IzBtҔ)Sl]xxՕ0lذaW\iVP!;|RRRAAA@@g} I;{ʕ[ly͛### z@%KFfܹ>zܹSt"//?Xp!.$icI%4J8}Ç9RRRBF a1**jԩugHFeݴbŊٳg?zH(ڵBF'++k̘1k#ڊ={|ƍ7n())ٻwge˖o>|'|$acƌ9w\ 2d@`nnKh4 lllpׯ׮]j꒭$//oyyymR'{챶m>x`\H+ƒzK*$5"==}Æ /L BɓqB|JMMuvvƝ'v=:77xbԩL&P6u]U+*VVV{챵urrIHHsttx"^oȐ!111:ڻwK.Q&MĴAlN4bߏ1Pp=50Y)VVV111CW,^__b ;;;H-%mƏ_TTDN: s^]J-"U'BbccUUxuYZZZZZ_ϐ-Zaƍ/^Lɓ'}}}ӷoO?СC߿OjL!tK7WK<50 CjHHHXh`(~,((e0!Xt=P(( 2gΜundJRT*eX`0DcEXRo5AՐXi'::yyyݿЍ7FP, n޼ ۺuD"! ̝;ԩSb8//KՄ kllfqqq لYfܹyJ\'i PxAHt!"n(--#$=4x$6BCCoܼm۶ٳgBPpTcEz0[GEE)[ZZܹ\x+j FBBBcccVV . MHHʼn/سgX,޳g̙3UM'|w߉+WxxxP߾}f!-|hh(r s^]J-PW␐DX@dSJ$6qvv޾}smm-"6l()H8uB*+i)ܽ<Hw8-ðlܸ_*;~P(T06L>СC"(--m+Vddd{|>EI͛7j Buuu ӦMS*Ŋ:e` QJ`2鍍iii=YdɁ6(w \///Dqܚ !!Ua0mBvmm!ec޽{\.wӦMČ@*&0 3g(J{tf`\.蚚G @NX}}$//SꦡC*VU`0?!4f`DI[x<=88/::ٳ"OҒΫsRUNduΫPE]LG !Dd`W2WR7iɒ%M;#Bg ,55ۻ, !$H1TPWR)^L&Sc puu-**B骮7͊MPicIn!uҾ/Z$U󤶶V TWWkPw/_H$.tvx^0ΨVf`.>rss'Nk`0.]V\\LHn-30Pzj FM:e`ۭZZZBzd`Æ ÿPCIҼ={M2OҒs\ECr\AV<)-"U'b>R#|;??>z(8zF'}w>>>qsss%bq޽b;[RR/bBaLLLmm_tRzTU4⒚*H"8dL݁z͛?SjNn6gܸq֭9sѢQe̘1DGB=Lwɓ'J޽{?~XnssX,޽{7!/g̘1H$XR...`4558p@^^^q%MMM}}}/^I+u% }]Bo/Y{I!U'bgg#GH҃RUUYY;e RRRvk ӧO._9qD33:Nr ...ܲe aq趘tʊF1xctRRRb208zb\=O4S5,IC7s d`20C @j1{1`hǎ6lR[)B+2lPfq(qwwg2999Μ9p4LOOwqq֣jjjBBB8NHHHmm꺒bƍO?BX6[R!!+HӾ/0zhT Pש%]@>dLM~~~l6{Μ9B&H$5k@񙙙JjPo?[)qƤI8 uN/]4e6mggNn{]Lޡ{|plmm###]AAAJgU &Mr888`D]x3wxxxAAH$JJJx7'ŗ/_K..]PP 5Sׯ]V$]/У())ijjʲDž"m,BRC_}_:{쉊S{:ˏtCMcǎMKKkllLKK۴i茐X&...,,"444>>U B{{sΉD"Ht9544%&&꤮+zϞ=uuu555۶mۺu+K !!!J0;;MMMFۇtf7~fn A}}=U(5///O"\|yܸql񥥥aaa%\VkQQBMztB,ggg)cEXRo)dHܭ}_\믿Rԩ%J/g0)))G&a|רQ.]KJJJLbccCDl̙,Ν;VTU ݻ#.\k.[[ÇxB___u֜&i=d2E"BH$S Kc:n|}} JCɓ'A㨣P5+..8q"ɜ4iҟ Um`r\"xLرc] ǍrcccUq- C^^^=·󽽽B .ܸq#BhÆ -R8^H'i3Ilp„ MMMMMM'Nɺ" TWW|r[[[fjT'wxy BL&ȓYNKsrr*++S<:T'NVgѣGNNNJ%R>}:a&[yϯ3n:yՉ^S/8)_9EQ45=jkk[PPAAAC B!񼼼߿ u0LwqM\i붻]u7$ `VMC TV "*ZK׮.E? (b^]1-խ^zAL"9?y3g&n39'g&nvLj@噗=Z7"U*Մ .]εM|,npf]]])f 8t}fgSoRSSm6 >>$IzFUxFFF___UU\.,^8++jn߾}0?f@C Vw^ټo>PSx9mŋX)MY'0 ݾ}jݶmے%K(!7oAp%I277766y؜$/[СC$I:u-9P-e2l6\پ}``v`B|ӄ9Ha[[ __߶63ټy5kVJLLdlvg/,8WYY Cc O NtL:Jh?Mhqȭ eLOOm 7oApc4iz+FHO\Tq#`\⠶ZaPk& &A va4 CVVAv Ųz갰05 IDAT*&NȪYsÍ;u8gp娨(\]PP@BAMZrMXBLc2v5k,Z݅ MF$ЙT*?.$j23+d] UDaX ,1 jz7xXД)SNpٔz{{u:󭭭^K%%%6ͥY;A`UWR5l}}}pp{pp0A.f mi0,Y"BBB.]$PBꊎd&Q^^/pBG)_A' *P扏| nsOZZTVTlP[[[xx-쌏W(Gv~,cv`X2AÄD".162¡i6I?5JM`RyfӬ7nܠܼ}q|֭Aꫴ<===<"1DdkFG8j{p䷿W_%7n8|֭[1 C@ sºsADxxs\~ĉyrOOτ6ʀ]wʕ a*lሊb-Wxs* +)'O\S"͏672tN;;RB?^~֭[^aZ~Œ'%%͟?_Tdee)&BjwWeee---k֬~tPkB".yc_߿ j _-y s4LK,ijjj;v"qܹ3mڴd/۷oյڵK{V8`;)\|˛\^\\v믿~-[RwWV^~WǏr&=쳬AXǏ^~}SS} <<|ʕ+^>m>؍aOL&B!䡡zf3~:5 $ɪP+d LHGBc1{욚VwKXrjtiӦI$f^zĉ}޼yގEd3fsp8sNZ=uǏر^F5hmm Q* 3 @$Jf3l6 i蘩J2$$)gܕ+W4뀳 fXNT8P(h7RHW_}Ӵ3gT(\7N>ܹs4MSS1((bŊ-[6o޼rJvt) &A g͚UUUeX,KUU՜9s\Τ:#uyxx0;@ܹ,..:z(߀TjXV:XpΜ9UUU|*KP^:k,Ty[>;r{O*im;8z-_^Nh_sguww_pa˖-<3<#ɡ\zxx477HjhhhZ4uuu܂F RnvL&B噗k ৾Nj-pWT&LXti{{;6 "99YTj4oF SKaIIIg6?{?6...55f %81/^yr111dpukfzCG9,4?/*. >u8Kdnnnll,Ѓ뽽999$IѺ-[v!$O:5}t-e2em6\.l߾`0 Z;0!NiBϏ򆯯o[[Kڙl޼y͚5UV%&&NN3wp뫬!5kݻf`ΦrPF?q@w tA,XիL|Bh/A>XYYVe}梢H@bbbEEuTR Yz={&/W`ј3,_A'RƧ޽{)))\B*8 Ka]z{{555N)&LW2kq x5]TR't0 dXFCCÌ3 L)i6IXTRB3:kfzCG9,4?/*. >u8N9h4n F#KT/`\⠶ZaPk& &A va4 CVVAv Ųz갰0*s&NȪYsÍ;u8gp娨(\]PP@BAMZrMXBLc2v5k,Z݅ MF$Йaɒ%2,$$ҥKX'--OjZReggӇhIggg||B?rW ck$a9 &% ug-NtLIk׮QBhʵ+͛7wfSc#Po]̕(ǷnJīJãz m۶Q{-[LP0#PH|U*Jڸq#ug*>>>332 jP;ӄ9H˗.\P( … \vƵkתj挠ݡ@ugٳgWVV4I8|}}}k׮UTr<**~l9W;aaaC$(uX@ 1$KnWVV?~|zpՍĉ鵵gbq5O$]y'"#^3o;W(o:/Koܸq[bw!BD2j( -1RivvöxT@xb>?Evr7" P(?8g;WwoU`edä]GC>ྨC pPY S>sGnysN@ bA@ H*0@ U`@ # @ FT!<{x=z{XS'L4A b@s'XUUVፍP#̖ՁR400y/$B@w1c0\*AAA2l̙gΜP_A**0'>ѣA-a*U"-\K9ehCCCrҥKM&񈓒gϞ^\JJl|ZVIII}a)͝;  ϝ;'N̛7O.{zz&$$N2`]reAxxxlذj p8Xy?~|ܹ >J~ɓ1W4,2wBBBsslϟ>}bT*^Oɓ'j5-pBDD{'ܒH>?l6]*k֬iooX,^^^+輠BPEN@W|,***-vfnnnRRHKu$qazW>c!G"0p0T+_qqq߿~lllvvp{D29HѣGfl>zɓ]_ q uaaaFg۶m&İ%>yb*MHHؽ{7pK4/syK_I2 d2) 5P^oN<9sLJkݼy3..N%T(ׯZ[[\>0H eə jX.b5558/DSou:ݴi5Lz饗N8AI͛NXhL&1cF}}=W ;wTSN=~;<<<^xᅿoT֐R: 2:4LRl6fs``)i6T*CBBZ[[rV]r N8Kob<|TkQL ",,v#5T*}WO>M;::Ι3OcSS7u^z իWg͚%JGɡ\zxx477HjhhhZ|BHR23///L_Ao \E8T &,]kXT*57|#l Yq:Df 8t}fgSoRSSm6 >>$IzFUxFFF___UU\.,^8++jn߾kf@ Vw^ټo>PSx9mŋX)MYGkaf7tB©"S'㹹$I2=x𠾾ۛzCd^^kٲe"IԩSn1΁j)(kϟor9g>>>eځ vMj mmm~~~7|}}\dk֬Z*11uuڝ\xNNN___ee/Yvm6 Cvv6ud4;THN&bW^eorgG{ Jtt45ʴZ-+`,_GFF۷l6EFF+***J%P<#_FcNN믿|K{DDDp  TZ\\Lޢ^,Yz={X,uh4c:+P(zzz#L0^ɬ3jpnǭV+twRI À+0a9  3fP(1A§r:&bRI Rh4&4Z Adp( 8HQ׿+|n4.R)fq%0ZkkkAj'tP;)۱ch4 YYY),ի¨̙8q"f7P8G˗rwAA 5iʕ6mb e2YvvvOOdڵk׬Yhu.\6mILX^BgU`AAA,92E4͔]nK>x]HjeZt^ޅ*X,RkXз,1 jaX()S%79P򈈈yaё233޽;EµoP;)$b׮]Fd2޽[T/]n]NNΘ1caaa|IWWsF|p.^}ъ+L^ˠٳgSt^^^Ϸz^K/l6f=0V]IoCk`ww`"h- m))]]]2,::I3A,\QDWyA qyca0,Y"BBB.]$` ,,DFK:;; #G*]X]6' Q08.H ?sLpphq/ cO_vBS%88XTn޼#4AzoL`D8uV ^}UڍeeeEEEԓAl۶͍޻woٲe yB￯RT*ƍ;S񙙙xaPk& &A /_pBBP(.\xpڵkj5sFPqNz3ٳ+++E$kתT*\E?`?{r尰!|U]m?d eq@ ĥYdۋ+++?>|V=vFqĉ 1ܸ'ԋ_2=zp_F|Bww7>uV ~6"cD"5jAyyyۖʍR4;;aۂ@<* @}"P-UCŐXB9 @`l|ZVIIIߒg ͝;  ϝ;'0IN:5Ӂ;vX~~~sssxxK ^~֭[^aZ~ŒᖉK,ijjj;vpuYYY IPdeeUz}YYYKK˚5k_qx|,Zc<X">\$Iaae<}hڴi555~?T*?ܽ{766V.k4spH$\ooꜜOO)S\~=44t„ iiiN .A'ιs>|X"=WV@i LCCsY$--mĉaaaƬ ~ɔ!H5yJJx0Rm6[qJr̙\XB:f̘W_ 2LV@KDm6Rk]~];uP'7,kFR1JOO|{رM67nܸq}U!\299ð-[ZJHwwѣG5jԨQGJ|cb6qoFxuN[gW1 <\)qqfIQQQ V+Вjw^ټo>LgRnvq@tPkUJ0aҥK۹AJR|7–BCCB8'%%ݿ٠ٳ۸TF7?x Iz^pUp8WUU%322z{{h-/ʲZ۷og%K02 a7ozZ8MĀ!(TɔĔ?x𠾾ۛzjժDy:,999}}}0666''$ɼ<ŋxnn.I-sjҲe:DSO.0wJ_SYa- ɕCg<߀ tg8&&&//$ɜa'd2*ϟor9g>>>A{[[umkk`5`=ꯤH__ׅoJtuww_pa˖-Ɗ Lղƚmdd}fsQQQdd$ 11:RZf̘P(>c1`1h4L!WɺT)w^JJJDD0J}}}EEE!!!N-Yz={uh4wwwǩϚBa„ h VkR`p]2_\58O>/dJi1$TL&D׿+5>qDeoXOB0[*JT`JiܹGGG&''9rl6 m 5Hd>dbhB j jkkqf\\܎;F`Zx"+0u#WuttV&Y,lfskɤfΜ9NրWʜWBKbJ\" ZfVi xwSL9yf3L!R*J;wTp)ejdƷdOC60X'nܸAKG4If\ߊ)VSNfq*0AV`T`&M> 7v^5o<;-+ ";41bh4MMMƠ uii)㥥VA$jRߘovsX^^^iiiEE[ zA\1=㶤[H )((X,:NպU;;;M&S~~>}K+dBPEF1//o\"44bT*K0 ;tPpp?OK3f̘6!$ǎKdff&- g~#Fզ [n{q][XXh X2:( pș9sM-Zė-4|p$spر---NZqƑ$sNZ'tuu ??={lKJJv{II/%qbٳg5 Ӗ?BBBVZaa_񵤍iNC,x(߿nݻWẍyaё233޽+Н ]vFɴ{nR7&aW\A'=0 Mvww>,N-]JM.tuuEGGdh&0Q^^/pVJtPkBE\!mX %KdK. X cmB'--ڻ/jZReggӇhIggg||B?rW=k׮( ,V/AdU8****4IFgff۶mS(N<===<bIݡ!|rhh(AiiiŠH|U*Jڸq#u0>>Jxi^|y… BP,\ʕ+|cRO @nFQ+@*0)@ ;NJ+[<|8'N>l[DВ駟:+HyzQu S>sGrވOqÇn݊ac4@ ZyGH$FJcN{(zQ@ 'QGs%;Gf kP@ 'ǥhx|Mz@̍@ 1Ҡ @ bA@ H*0@ U`@ # @ FT!")IVUUjryxxxcc#%euuu``T* f/ 666KL&JCDLL`pu*AAA2l̙gΜP_A**0'>ѣA-a*UD7K9eh*D t*$I^^-HuOOϷ~~~EV;m9Hl6|VjuRRn8;R›7orWnzՔ 6/DDDВ%K455i;vP¬,BԤP(\ ^/++kiiYf͊+(!WP@ C/())ٿ{{;=ZcRR?@ & Jfݻ֭[,!gΜQ*̳ Nrʦ7x㹸@[۷oյڵkc"+((Pcƌ}@K777 Fa;%{~駟vww-/_;v'NPB^|1c$$$zW1FJ-PkB"Eqq͛j5X +]$OԋBxдijjj(?0|TJD{nll\.h4ϟjvH$999SLN5~zhh \6%]PSѣG5L&+++;w\PP᠎l67nM?O9r?ɉ1Re_ƍ~zzˁ~_v_z-sرM67nܸq}ѣG?&1>m>؍a!''gʕL ̷8{xx477HjhhhZVݻwl޷oL&Rnv;- q&˄+[W*j„ K.moo&>A$''+JF7[ .f 8t}fgSoRSSm6 >>$IzFUxFFF___UU\.,^8++jn߾.LhȠ]PSJ:wn6l:|0ulժUfP c.\xgFGGSO}0 ănWΞ= ~1sh(o޼ɔl mC#Z&Ql6\|ďt޿GyK_Ið .lٲXUiZՑߑ3EEEĊ J[644̘1CP|APǮƜ_)d>kUt޽ac!JBBBZz={wh4www% %T(===&L@mf/ VkR4 0 0tɰB g͚?={688phږh3(Єw^PPV{.%A(>}:$$nL&"^c>.ƍ~mbZ *#WuttE", l6SIMM͜9sׯ_Av0Vt⺅Z)QX,TʵJ|,f3ՒVi xXД)SN_ hiiIIIy뭷(IHHHAAbtZ*ɝ&)??ӓB}u P!Th˛88=88HkA"0guuuEGGdh.HU_|E .\J j-T(+ dLr%Kaaa[cƌJZ n¬Y:~z3E,nٹs't!޽7ހv_:gL;POwA/.Ж$_RT72E 2T{` j{וoq c!׃!ȃ6lذb 濦C >L}F-_}ƍÇoݺC@<^H$B1}tT~!ǚ=lDT3j"G T!'To!Gt@ bA@ H*0@ U`@ # @ FT!@4C x=D CX|ö!ѯP8Z\.olla JC_~%AP\tR$0xzzzbbb1 DѣA-a*9T('^2)C]P!bXy"shH$yyy̷#===~~ӧ-ZDZ0 %N4iKxs*\el6|VjuRRnw;ሊ@R R;ԟ1 I&:uʥTT`ǎonnw)!gׯ_u֫Wlذ_x$""$&&.YIرC`Ldee)&B5qDZZZ֬Yb J-PkB"GwyL|,⋒ӣA-5&%%͟?_@M 2h*D :{uKH3gJ%Bӭ\!227x.Ж|TVV1%Ǐ~\hrsso߾]WWWWW޾k.S=8HB/_=PO2նr_q0M&B`J 6ɓ3gΤ͛7Rl6fs``~:5 `` IUUUcg!IDAT,9WP@KS/ٳkjjX,q8/^t*t ΀zM&H ^z'NPy湻XhL&1cF}}=W ;wTSN=~;<<<^xᅿoT֐R:?M80AWy9rȌ3Rg}vYFO%Irڴiׯ_g5 08T)STWWNrbe%c3SjrAGWWg}YdK.Lɜ9s͆FDnll ruooOp SUs0ԣC{_I翾 M 0 p–-[ygyFlrJf=<<$IFEE544Z-Rݻl6۷O& )Tjv50A2a>kUJ0aҥK۹AJR|7–'T(fqOJJ>Aٳgqqq6nA$zFvq<##J.gddVUUZ/^eZo> АAs:J:wn6l:|0ulժUfP CDooch^܌}qn+gϞ `0ܹ3::Zx7o2"[r!)N&bW^ue@&QΟ?f9)YF'N SUl6 0GʴZ-\ jdd}fsQQQdd$ 11:R 3fP(1+ Q0999:S-| L޽{)))\B*P,Yz={XB1*^mMMF=Ba„ gmQs8nZ<`+J0 NHa !dxg͚?={688phږh3(| Mfz13)rӧCBBv;]d2/// ^{5oܸf,%n&ɲ{zzL&Ӯ]f͚%~QP\rӦMT` SUC XaaaPPk_jXf΁5BMM͜9sC 8\_qgE*r=%AVwww3uA"aU`CSL9yf3L!R:+;wvS2 oɀ 4'|qFaaGj UYYYSSüsq& 4 BNh;=0͖'f(|E\O V@@Ik$Ly'upX68sBR0wmt-+--?~pK\^QQaZ<(1ƣԓ---)))oϭA )((X,:Nɘ$''wvvL|OOOJ-|BPEF1//o\"44bT*K0 ;tPpp?d̘1mmmC$I;$LZ>VMKK3 nzp8׷fE#dX!Ƴ:dВ4z7|s'O6lعsg~~ڵkqq04$99y,>ۡ/\`ۻ10+W0%g޵kht^^^!1 ٙ>o< \G@1 k ܕ+WM60@X;냃݃'0_K$歫|JtuuEGGdǸDyy/H… [[[)!WP@ qyca0,Y"BBB.]$` ,,{*IZZRd}t(--U**;;>FK:;; #G*]]vm0O ,VȠa[\ r_~}ƌ<22f, Mfr:tΝ; yo_9sd2wyzHz!Cgff2%}}}k׮UTr<**~~޼yNI￯RT*ƍr"3aG@;ԟ1 k \ff^wQfFQ+@*Oxy VX1Bxq8+V80EuA2H ~[p2]UG0e=7zh.7n>|x֭l-E C"=:++ _.<***>H] %;C "&9grP@ @P@ q@ )C iw!?.kz(v @m]rf"4I0 / ö98*Эkwz'_I/n1_K7zQ/w6\ې㏟ŧH;:{N7vٖgƼk85O6^m6ҔfYFtMfL  qҺf$yLћ[WZ~2+rGvcu+iϒ;_%qDzFv3#A|ܢBKk;D`>d^ڤM{.24|'G;zA:w+YƟ(o9)8(P'+S6X{ ӈ&K_kjEy2q=ᕵ}8ںĈ@Y_E.Yx.qӿYx'3pŇ>m*+%5M=KΫ-͈UN"+3VnBD<Ͻ)wl+5Z3=07l3͹at]LV2S=:2?nEe_UmeǾk:RNUuQUM=}O],ji Sx{{Nf'߲F((;&T6NL i2]]'7"jm{Ne!;D`ps,dz15>xӞrZϏK3??yDy{zqiF̦O˗eĜVl!"C֝y{QCw~y|+c*cW׋ܖ?5=tYFevY⥵ʊRBDa9q.8o[N4罴f'Nw,|کӣt<DZk"zF&1jtSC&"rg{}u{蔛S͢rFd%Cxy }L2S=:;dn;\2N?hF _u7<)`&uEVo_!"~w+@Jks$X='HU'4z/õ Iǜn|b,Tej ޽{_[ngaaPür#0B}gļi /tZṶ}FO1v1ƞttm { #+EYm炔0L(B ,3&O Sz]]PKUMTOߝz]b/Jks ܜ;Bڭ^Z9iNK{|g\˲>$ܒّU9uޗȍWVij"aO9)a#b@^fl7zʣAnNz.t~u#LW ^))'Víʝj;Asc2xNQq_-J 'o!cQ\ 5,8P}W%Ns4F/͌fU<`NOg9ן׀pR RRRr,x/>}mv_z3\ICڰ&'gSo'~$'~Evloje)֤3"s}nP{=}zy֭{w VN㧩j}WoQ갨V5Q";$gUc_<];/vݓeƴKK(cjhzȻCg_|hNûZyaqO&<^ᡚIC,:>8XpNJu:vI&r6CFTGOPMվD0mҩmkmTkm}Kj6%y0TTRVg&ح_V% nWKfҿ(i\yN ":vuE):d!]Եeo9cL՜#UxXPfڍ)Ho/?Sk ޤ s '/i;mο?ZΦ^$IOm۶e˖(555;vx [dl끪VGF6`fD`p+Y/~*ch8ժg$8e2[`di5ǎ˞=[YiV# oɒC_nx%1\]]dɳ/([ `"#"33{NIRy F1#++2"RV)#LS\).nh۪\4 fS9}v8 #$akk` M3pjr.wJrstz/[{ W;ǻ&v+%{HשV?c-Ss/|c4DeD"{N &tWK-XkI|y-ĘW 0 [%2:p($Ih4xݕ-ؙa_ysp5`1g1Ip޽Yx~}r',b9]ՙ:sS&150Ao`ʇUn3zmݺ'׬٨†Zq!1v̩ܻ$Q$s2'"0aY\2Y> ~ œ ֬][aaj1Ʈ6R]픩I$)p9>;ćE!}+ K[Pq^ff~##QJ&:DZ67\y  \_y?Da޽e˖z$ Dtۓɩi$'aE!,y?fy332D4BF`'MqUv]xn܂\Hp#,$3)**JNN^~XELnd馸x^oB,< |Ž߻x1)#W #жVѨgʛg! B|y#k[[[HHp(9t@)699ETĈƏۑ9E`$={n…U<Ɗ̜u`e>Ithp`$8vtwyZ͂EgI/;GDs3] Q0y/٬eO%8g׉?)Ix)JɊ7w,o˜910!:x"#q1[;/̜<&9~`pn*?ӴuStdf4yYI*͇N7W"0A0[weD ?0.Olg[==5nfCD`X}lQ>9ѧZxjFW-dӹZs`؉1bDNƎ\hu:6/F{!\0 c,}dtάrΎIv!:Ei+.w `V#HNrH&:mo.k5<vpDxK[HA t:8I#~99Yzm@Bp `uv<^hUV&ǎvN7 `r/ִL*$6N_Y{%'%\D`0%Ɛ=}BYECBXXNx(;Vy"Z4_;)bŪƻ'L1yzXiiƝ# f}WJ50jMO }Eeh8?b J0w7MOu= _yvuߜN"iцg'M2zM2lG`Ɛhp:MNzH#ǧ} K'M:5&&fog ^/q[u*Gp)Zj"v;;;z=@C׎n lWO444]ivsKFy IDATxy\T?ϰdň d.k"z%{ +\n`^\k^)w]4%LMq !M`3q93 3q=f>99u$+++`y !JUUV٧=%\qk!h.555 BJTnWB8B8#p갱<… *Jlllmllt270DohՍR#A7h(CMH$ !Fq8% jX+!D"!Vbm%q6}f8j4-:gY8$ Fm=ſ"3T*mll %}kCSi Nr_Q0q+XB_XYY[Yی__v)4l*`;hUMeYiii۶mmmm9uVjFښ~jccCJ$Zֆh8cYNhRŰ>ѪhF{T]Q<z^Q[[۶m?iXN4,R2at?'jFT=h|g)-RZK^`gpXn8.βϾ.=8w*ְ ]#=ayhj+++XU)j˪Ut|4~ڮ];'''t䱦L@Lں:h*0jRat;oRV2Q)UXKܻ8;UN{~]\:Uj.ݩ:s8Br,F߫CjqږR7gBonޫ^ݲ?ц{FЖmzwN"ܯP w͈ޝkgr#Ѽ,G4,|*V. v68 R=MT*k=R0jΎ^Fk(j|FZ }ܯ bGGBV*8VjRE7~{Pg9\50j6N*ml5?h?uЩcvvNvFprr7BŲFQ(ޠZhU*j ,NhV+eFH@%oѵw+!VVW_7yH)yHl$zf+  qJEnVW_zdo.Nu~a4zfKQ!*ER-??Y^]d]Qe!%+H0]:3˷!:³&oo.:VèYVj+k?+,+V)57nrhii;P@,KaߝU Vpa9 &bZ5c5v~]R.];;{7KdکѽtaE4j8e9 6ͥ2QљԳ0|;7أ l[[k .yrlքnܭ}Ҳ:Bq\z~G/7qPz&qO|9(tQ+boooeeuz__F?-]eeQ5.P S)j&<>d5~knܾ5!C{RJAp߹Wj^PY֎BJBRԩJBDV~/,ǽ7U~r=߾[:$s뿳MqIc[z}|-XbY VشmΡ^0GUB !ZxO*j4 jXSZD8c5JAHth8CB5w~B:**% **wOq*KRx*8e4 GYD"hFM_uqvpis!!ΦS{'0j]]ͻ%'̘0`k8-{WIꢆt᭢қwKeׁF_)'!DR2*FH$VVV+:ZYY3ZjӦZW 8cʊE )ZG:cYX a<)x|LFDMuo*|@f/?t\kweB#˩U 5![uM/Zݾ抆Q?wm+5u<|!B>!K_hhT1uJ5c1ЌaԄFbJBHeN~{Kmm'h4 zS~\/^H٥M.޺Ϩr[VQq >bƄi_{s^++):8ui=Zw{t2Ge% qԶƖh^ʻ}-h1s]Gyc[;'[;7^C̳w1cYRT41N]?i|GB !%䪼xKn񒝭ͣZes]]-!wՎ/,[\RFEXc9V7#\[=gX:{ԅ_=cfX[%Xptahl;x&tp5u3 ϵcYQ+kj=\;Qk/2jcY0jeY2)iAB?k^ڝ5~2ϲnqx)荷/HQ3վ̛jW(kK .{m۶666u*_ןyڷoD\ڗ%ݧ ޾R\jBa*~LD"4ZڽwQ݂K=:YYY]U'OKFbggwU(h n;v~?T+S,RQW/dߺa555f]M ЫOc ZCv>0O^aF>w}WIEs$ڣ*Rc̝C7"WJR͝A$d??hֹA[,΋8{Ȩg[1niڍݼys; um:#unֹg5ֻRvȐ!ɛ?LMM5qEO@gݨhKFtΝ;*))12pM ֟K.}}Ԛǎ]|M)V4RqEF*[8rĈsWUYAh&Yvg?7n4kg+8aZji˂wwr{;vBn.TݻeKz& @رc:B:tp80jT)Zڵwի翓7|tqppX`~/oSF3>ǎ[r͛7hiS-_>„[oĉ]4z̘1Oiouqq7n s0~;}qD jsݻwoꝀNyN#.\8߯0_BFȤ/26{xWEsݴ>}lKJy矯 {pSiPE{vvv^^^gϚ6mj+TիWM38={޷o߿Nd6h܉'Ң_'gLֱS&mȰ'Oǟ19N+//}_|Ͳ FsL /kkso#T434YT4=Z ',*@@Eh|g豯5hN}Z=yXN a f2+䔦3w0fl-48 <IEÕtG@E@E{N?zQ#GЌZ'>H7APĬ\ͨ:5rFqpp0dcڍ/_ dӦM_}U``L&0a˗icVVְaúu6lذ,1utڛc5|ϣ4.Zhʔ)W^ ܸq#m/n믿N0!<<6.^x͚5񑑑:,Jf̷  srr ;qmܶmԩSwصkW8ͭ*(( JLLwvR~ݷoߣG8QxΝǻO0~{n޼ǧ_~YYY6mӧid\ :t( }{1 #@X f wv}uuuIKK ܖ-["""d2󓓓>=z>}ݻwkʕƍr L꼼8ڲlٲ#F,_I0o]!e%e%MhU?ꫯzzzzxxhgϞR6$$dΝ۷o YRR?qD8B8aT8{=r}o[8 ^lӧYZJw(RY4/ZsJKҷr1/?x%K ]R^^?^ʪu9s}“g,}g:u-yyywvpp sQQQzjkkk=8ŋ?"Vw9y 8eCK5Wq`),<!ׯϜ9$''r֥KL&P(d#TUU%''T*UjjNMMUT۶m2dXYYY\\<|3fXjUvvwگ+[snnnSN>}Ihu O{Bvѳgϝ;wnذA{~ѽ#>[YYikkkgbbSz7$$$֭4iѣ;'$$sѢEIIIFzGE%׍@X'yBz;:8nԢ7%7SVwq#G<o 9>C_t {4yMdmmP( uP(:͐T*H$WKDϚ)nj]TZ{sqo faQ;ۻg4Z Ҵg!T={\~}s1"FGG뭰0`VX#® ro޼k׮4^0Hqo߾02OMw pIBș3gh HYYWYY!dС7oޤي]v64~p64涗&P*T*l$;wɩo߾e(p˖-W&ʕ+,fffT̚rCنF}۷VVO&_TTtڵ1{ֻF [z\._~=m>|L&{7 !ήr|߾}饗C[L`6mڼk>YXErW^yΜ9?ƪl>-҈ؔOO 6DDDȘ\~u>o׮],'ddd 4$YXE#B111eee[n5H9rK+̧V*NNNJR;oΝ !SLٽ{w>}:24f.j'q iB/g3b)3FB޽{WXah>{rʙ3gr7k,[oEy뭷 M$[~O?TnhM͛RG4HH˂Tnrx  0   ɵ)!R!قo7ѦrBv1h >pRQQ1ydɓ'WVV6r\y&r~iaa!?===^L`M;vXjvbNNٳg͚c|]AAA^^^A w?~ ~7͛}||뗕i&__>}?~vxAXXL& ՟T*MLL uvv,Y;>XoLOvڷ~5|p>k׮77bZrq\ze˖1"??? @;/WRў4 277wڵ˗EFF ʕ+cǎ]r%ߨ nhV 2fz*FqwwtҌ3,Y҈uΚ5><<\]?;)88XRѥi7! .6m͛7iwy{yڢ nhVU4SRihh(=>-((յqs0=;)q/^ 2?Иএ nVшɩAAA{ۻwo^hQmVWW'_ZZJJMM5s*j۶mC 1qC+++ϟ/؇vE0V4K(L*M:u:}=wmɒ%V^> IDATNٽ{7U4**J.zE>[YYikkkgbbSz7$$$֭4iѣ(J__cǎ ߾} 2OR ~9ٺ9H֡Vwq#Gq7,< 7~3@S uTnds@mE@C!0|u4***R@<ݪ3lr B:k=ƃzZZaT&B?[_:t( }{iܴe滏& uM bsk.atttRR\.leVZ3E] nqqqWxT@#XXEkb*wCG] bK$zCP8")))6lhܜeVHRN0ѣ1?N"3fXjUvvvsaڷ\gh/]QQ1p@Tsݻw߼y1bs7k,1&&fٲe*jܹ|o=jԨ:.ҧO{$ƥowkR55o*wKuTn#,", R@< ay: AE@E@E@EhkHևTn01`YTnCgh D{%R&ó|+&r7( ((+11Q;C?Ν;Ǐwww0a $J7oӯ_M6Ãd2Y@@@nnur;KT]F[A,U&r7({ʕƍr/-[6bĈ>DRhy1 vXCBB"##u֮m\.?~8k|Ʒ8`,51AQݹfͲn׏>|xx8ktrr VTt`> .6m͛7i v&ZSN&Vh8@배TꮷR]xe1;Bڴico`8eFݠn4JjSSSU*նmۆ b&&&VVVϟ?_vwN0poE~إ)m77SN>]'o߾ΝN>uꔏݻ>|׷HSNoIHH0q׭[WSS7iҤѣGwtt4p߿}-1>@+@*w=j;>|ȑ=jTMvpp0"RZ<<<ͽA*q% | 4****R@<'СC111%%%f2VlrKx{{7rۚr_ǧrx={L֏\\?Sb .C' lo~ eʭRΝꪽOe˖L6dC$333\oCقgjSڡ؂ 5CY*~*ҥKCCCC+V.((Zbō [z\._~=m4j-LmPl*֏ 7h|[,U4T?\˛4V}Bi07_Iquu{L&+,,tuuwu|6ݖH$$EFFnܸqJW^Fzjq4oOS{BFm۶O>$003quu=zhmmxkxnA=jѐnbMvBԩSOnf̘3gFm|ϣ}[n_'LNׯ_yUOOƜ={\~}s̡ۂ=@4̷m۶qԩ;mə={Yrrrh͛vjeeeee /˖-̤.]r_[n;\tp'O$zIB,iii|tOqq1}[qq1߇V:|G3g||)S{9={ٳlٲAedd=ij_kƢ"eϟ?x1c@+0wÇgddVTTT]]}˗VMiccGM4z4&ٳ[l.g ۿkqvvmF0hi{IIKKKIIٷo_Nh+ .,,tuu,##K.|QmVWW'Ƙ[ۼys||ٳJ%!dС[nUgT*JeRR( |+ںu OO(\#>3o߾Ν㳶C{ ZlٴiF׿7ޠKLL<==_r̙3۷o7 &q@B*7qD[ n[Sŋ׻|򐐐H]R%$$̛7aܵk lٲzjBr9?褤$\YLD~!ʭݸtB<@kGiiiJJܹs>LM9}ѣG.\H^KcU*zSHn8и&2늖FclTn߿wߥeYZsr=wwwV0cYVr,)T r-fM \h*س]vv2q;vLKK3qS\//ܾKqL2eg&B*7R@< aw     HTÇ>}̘1gΜ1h("fsrrs ̙3HH7hܛ7oڵ /`QpqrV4N*T*NIl_fEEE۷o_|yffFő nfo>TnB9s~GTn''zi*7}>bB*wLL\.~:(ʝaÆS0|+Zrr2}v*wLLLYYYuu֭[}}}4 .NSLO宬,..?6ʭVhkk3fZ*;;8h֭+((߿?=^SSC3fL߾}ϝ;ǟ2l\|ɒ%Nٽ{ƍ@MMߤIFmi*ӓ2@S===-ZTnb 5 rx  {eP@\&|ٳg͛ʽzj\TnbM'V{.= p\+FWWW]XXڈ#oEӏ.BqgΜI[Fm۶O>$00@*71ߊ&-H*M:u&MQQQrG./YIL|Sdfa(סC4hLH8H@*7R@<@Cxxxx -B*"ZYX*T 3++kذaݺu6lXVV~Oooo#=[ЌwH4====</_}3gDDD7N[rr2&®@32+܂hV- sss}SN*wEEÇ>|饗***ܹ3~xww &o:ǑJ~w߾}5v^9W4My{{c| -["""d2󓓓=wS߾}O'eܸqvڵkܸq...˖-1bD~~~@@>Mg8j;83+v*7!ڵkΝٳܹsi+ VX8f>T3g3 0Lzz:M<|xx}xx-54 ~״st@YW4Tn^Ν~g6//oɎaaaO!3 +Ɏ9W_d2>DB/V2B? 1ߊͫJNNݻ7}+222 Łd2-33sȑ...|SgΜW6 IDAT<855URm۶mȐ!:=z?ϸLaM0ׯ_^^ޖ-[hϤ;vsΝ6lG94Sǎ{O:գGo&!!Ag˗i[ۣFҹo8` {CƁ~O6S`* 4bǏO_/XXlٲetz>  H0+ ⁊⁊⁊Tnr[0dm*˗d2ٴiӪ4y|ϣT_u„ |{c|ŋ׬YSPPI-Z4eʔWnܸHO hML srr ;qℑSSSG 8y$m{믿ޭ[7~N'[-H͛7/++kӦM}u֭ //D_ 3|+qݻw߿]]݁:Srnٲeմeٲe ":k_Rh y]vmll,tʕƍr ~+ZSo޻w۷o[YYiUVQÇdofaa!m<{ylmmEs֩@E! ,prr VT4;88_yݠ< ̺5%{gΜ导 ݠTnmڴ_GFFׯa`V~7y@EV 7A$7@oEkz*7qׯ_-M)J'''R7:t֭jo֦H ćw3|+ZSRԩSO? nJE'B&UkXeY ˱,P1,ittҩso/֬YC bTnrxBE@E@E@E@*7R-Rt<[܂ yHl0ߊ܂ `̷1[QR,WMl4R̟Yw>-- rՋ۽{CXXX||cfRH6OQ[:0eN宨xwrSI_ĤUϣYYY_|E["[gA0[{p/8sA^EcY7.]UoHnCLIJJZpa||[";444..x@0k>(44t޼y2_#4{*w޹sɓ6l8p4 -ǼuRsrrqㆍ͸qL_%R'::zƌVnPТZ} wܴFofffv`1C0g2eʔ)SL_h4TnhvH@*7҄@zOOOڧ?jԨ-[]x>cbb-[vi@њSVZ2}~z|]='' S77'}Q`~b5}uktBHyy?zj+ %Mn߾yzҥ MNIIٷoʝ7ydGGǰ0~pBi~?Ln\+_;q/^gơI9-111r)C=کS#Gt޽gϞD6m߮]X7o0/2(666%%sÆ  ca2,##CP8p?2mJ~!Gʩ*j۶m_>>r|ɒ%.կ_62??I&=oW*ǎ_+WΜ9߿}Ƨ1tP'''ܠ5&&&:uG|MBBm󳲲z뭷!oFS===-Z\CYBv.,(|xҷ5vԹF-Z~S2իڵ hm]3gw `OL[UO~ҰγhKGg?i_!:?W^^~/b͚5h*7W!RW^A90R.X]A*7҄@!HF*7XVM妻_~իqMٻw/MȘF&TnreiT:m۶`С3f̨Lhk6;r4Mn TnחB<==O>ꫯY]]ݮ];z7JׯG90ErjB0>>>N1b֭[Νl)E USkjj?Jh4666}=qĘ1c~mZJQ0A92aS4(koaQ ewDA*b`TRFSj-M]P7 Ze0FH‹ ^FT -zDEUa~?t;iaffΜ}KE߿ŊBP*ZYYiʏ?(***zSX nN&(ʧO~GVCb BCC$ k;wBBB͛WWWG72ۚkafޒt)w[s`GA7.Lpel~~~vv>|0EQݻwѣP(dMЎ;w{Ə}vf5uxK^x1rHfÁ{m0AE =ztLL]vܺuƍMMML7FDDX[[GDD/ka}8j8`zH3w[3Z;@D섄~^ɓ'͚ M L81**jĈ.ҺwР=D3A3''ѣܻ-ݺ?e Ϊn6P;;999''˅YYYt`@3߬@\фBW_}goo/HI&ݽ{TQvvS6nHMII}}}KJJc׮]㸸Ķ~z&ŋVrww7osVӵkז-[&J-[vU.7)))11qf/^Lv2mllL,Yfz="66V,KRP(ƌ7{fYt,SAFyeBȹs_α7n>|2""B}8IY}i*** !z"m|Ʀ_~˖-cy֜IIIYfMbb':\[n]fM@@@޽[:saÆ޽0o޼?ٳu1cAۮ]g=ӽqԨQ.=uz왞1̙32YN/#pOt4cN:Cccfƨb8t$&rTgz7T40h`>P|0j*&H:q@2j*FYR+R:r,--U*G=S{rTnNLn)V'TnNMTn{B*7@gߊh_*wK= 4|XrtF|hNfYVVvGFFoJH-^1..nȐ!RxhzrsݹsD*7wMTnRtԨQiii2Ir+ܺ72rt HTn0Hkm *T40HT!Z܄2 惿:  *T40h`> :AR6@TSN7uĉti!vqwww-kٳgϞ= Lj n=vDGG/_nd`$ܺu+##cԩL km۶yxxܻwc۶m<+ZjjBP(۷/Gϗ/_۷{ϟ_tՒ%KΟ?ݹ8$$d[lalWWWTVVҟVVV:Z;ӧ0{aڙL8ځҿwwkצԧ)22rNNNz|gggϞqwNJJ {/ꛥlم1yaaavvvi"fڙi*^W/ZYpK6v,e{JRTfdd钬iW\H$K}Bw|999111aZ]]_ZZ!3CN>MޥuӻZZ}艿M&~n޽>>>-u+@Z A0asO 00IfS ! ,8p@zz:͚=z{* YYY[Ѽ'NWXXȜGcNzg\\\QQwQQQςڴiٳg}||4kЖ-[.\0hРK.m޼n4iRyyyyy9[̘1#44[PṖui .0a SdÛ5rXmooQ'\eNilBb1sAƚ윛º' `> 惿W  *T40h`> R&ݽ{-QUxRXXK ,~Ncƌ-Rp:kH$rpp=vS?^ .QН o}/ocTRWz @E G^STJtd7@$D\ Pр Eӧw_>ABm[/oo77͠cM BERT/_޼yc"32,Bŋ"U}AtQ,'$$ѣG~GR-nnn?IzI$t#X,]]]sssM69888998qn,))7nT*t'Olmm]PP|=,GwhЅ>>AAA'N`---?䓘>o߾Gfee{zv[^o߾RP(rCbcc5KJJRSS+V* eʕ۷o_xT4B233ΝK;wÇhKK|FBL&۲eZ(jǎ FA9sfС!!!O-k4ʚ 8؞8lc xMMog]EK뵷)vL6ܴf^UUe *\;ڪ*Xtb-k0^h+hkEȎǰAܰᄊ% ߼yJlV_ vvv^US1ׯ+{ l5 DhS"hذW7^BgdXj5忮_jw*P$wذ[߬QpHdkg;R6.PрBѿR3:/@(XZZ~ T45Xb-y]6.t*T40h`>P|h~c ZM4+ڠc_,5|=.<]?vvvLܱdIRH$LleFJe=P_ϟBk !K+ulJBÌ+(+uή?[ZZb+Xd1痾aIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-plot-task-zoom-1.png000066400000000000000000002016431351617527000252470ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxw|egR!@ RD)❨x;NSO z EbHW^y23;B4 UpBr RK(]v~e.>>>za=^zBtRljB B S:-b?q_ʋ KKK}}}u:ht.yNg a&fcH BkN~zjEF0B!%NA V+MBXNpr czM Dq^Y^V\nf Do l]\?HV9(T'T=>x!펞+.e/\(90&^YWd;|x"Rk9Nej-))aYJEEEeee9Oi@[qq e6eyzijG<DzN|ӋF9Z 5xk7r{k#K bm6f^?7uܙSX=鞓UA.㬥___6kQV+:b Tqq!I fϻqo)EhNZYI~98I"~ ?eN;:Y޼:PBO 10Oos'.xBȓX휃廄5xaOhSacmN>m^s.bۑb+y-ߠ͚xzl})wdA9{\S- \->Y[x}hܻi;j0g.WղS?!$]BH)4MCNTPʃBa5[N 7S&[606dGfIu*ifr+#ϱv`0u:= jLy U=:qD\\5z;q-HKт\Oqm`vcYNpm X!&ϱN-u1}<"B}1Dm%/RH0ϼBĶkؕ%stZ <5<`Gr|EjJ3+/fkgͼ NM.a'.X:G؀vb3?b4=;fٖ?*lncXPn?}E6̑Km Mm1kG/Rni"CСVzƴcvaw>͊C&\'~Z65 %vOK;tج ( =&404SG[RGu)E 쒻QPvaVyS.DZ1aW?B!Dami/ !fcYZrQŏ/J+_zBd֥]VQE= Vg0鴚^-*'jJ+> kfԦ)|fMȽZ3ؙOsFBHS?mqY季r2>.yjZam襃^_r0tZMNe~*!仟#Z2 &vR`BB⪐еÏ==u's]˦چD yOO;}ť!Z L'R 'Km :~qd羍rͨcns#Mi]Cqm.<RNbU58AdnB} U9t`̷:u- AIBxa>pgn>.wNFEUOQ-NUYj4Þ={!Si&ѫxy鰉G\iAxc$o)}?qkx5v>{~hJxzN~ aK_/,9.! dA@0VTd>4g?A[PO_ e  :&sGF'^x:F{CJKq:q50єUFeYf---h4N`0hZzU g-Z-07$7Kڵ%zeXvk.SZʥNYH{Q)<  ZjⲘ vk98gḯQisv"J^}8ϱj=Ql/gX l+!OlzF.ް;ee_):FG4ϻVslqIy-zO jہ~z;9}k%Ebqw*'#7\A_zf}+:=q8Q"ɏ'r}Ε} &_؀z2\`vu >>@d]'*8nUwyqN#9 :ƜS?TnF(v<:fu;g>3tcVnܻet= Jg"J'+QI=/pV>4xpN>r=󏜯vXVncN;$|S~gJζis^oM)\rެ ztjo?NY]vǺ6<@ j3VFޫ~zt]v-B5uxض?UV*xXN=&%g+KJJJztجL&!qÇ/---//ZNS6*&|:%J|V3huoMQ: Dzҫp_w5'Ѥn_7IGWxD1=(W Pju/pp\_߬ӫ6 Hi4-[zOTTppPYYrϛ*jYQYYɲi]/&ʱΊ ʊRWYWTOӀʛ??xKW+Uܥi ޵QUFᥗ^P >‚*~YF t.| ,d]ͯg ]e WjkѲmoiuiuhCK]>b($6HvOQjÆ &IlٲpFRKNe7Y֦pC}BNu3(iU6$Ҭy7yJw̌3F*--4\2̍?! ]8uVѾ}B5d_!͚j:pР;wfm6pР)%-eNYVrZ&ʎ|XQaANΥ 'WJ222o^RRoXrպ땛PeZ&zL/7'qo&+Os.]t#{{uQz@@@f֯[۩ mb06?qbl6ۦM[qqq۶m~m=hbϜ9#BVVVe"!K+WҥK#Fs{֭.es*7-n+mFt KO:II###u:]۶m,^0UÇ{jHv!qݺ%%32w֭[lPӧٳ'f$!+!M )M?*t TTTtرLd̈́DN,fB=ܜKs{y_e7]cccOS9i )-+Hu籺32^sc#X,̷~[h4#+ޛ??::&2y9/wƌ}ej !K?KNn׮ď5j._Kˎ;&MxUn8C'OL՜9sƍG8q"(($Ca0e"5*;;}7jO? +4L3f>xBNOeeGh4..ѤI[_|&W^A{n{Cv_EGG_Ϳt-[ o';׭[_z },|zbXo^>h7k弋`0tرc 9]&{+WDEE/xM\gBB| +*,XxQXXVի\Aٞp=رCZ{C)eѢ6mګzDe;V>H#Ww$0 Ӷm[sVdMaĿ gVNWll̖-߈Onx}t=z|kJ  ؘ?\(+Os֛%2˲ =DU87?v !9c&] Puoj7 *h 3{׭2'~~fC5oȟ |T{!͚[,͛7ϝ;r?0S^P 9z_%P[#e;h] 3ds?U)]\_p @mv,@Bȷl4hfE۷߾m{q\PmNէ/u{:"0jh4VMɆh'>@!/'GjK'wx湳g ¤T 'gZE]bEΟ210ahPNd-+-;EӷEp7 i0^^Qaj:{|%K_ڰaCé ո, O>kӦMVϟAz*Yemp;|O @^;9A8f@ȡC?GPC.lqfӽʚk۪r< >d2[,b6SSS;wl0zk.HVޱcGN.}iÆ &I|*[fff\\h5jTiiBbccFc~233fh"];ʯUZU``KlN4([3XQQQf9**j4e۷o/))y7z)pl'O^lYEEҥKÇX'Sd4iҔ)SfjXƌ3vغ:%e#eo7o޸q㤉.u={kvk̙3fȠ)ׯh4ZM6^VN,?|jjjQQSSN%$$%$$lٲE{7nڵԩSsssk1"];ʯ>jYT;úA'""B\O ٳgشiS|rZJjÆ  X,AAA#q}m۶t4{k֬4iڵk̞G-YdѵGv'K}!V~}={ݻʭvϩ0 D-: 䥗^>}4111)sέHLL|!ӦM4iR !999'%%-]YfΝh^ɩ}Q-6q_uqjPiVIpVB%[z]3gաC޾}{DQeehtIZz޽ `ZAdWyoݺ{Uޒ%K:w|eG/"m4.}8__{b ,**r}SWпgNpWp9;sVmdEߜ[ʚvzn+]xBb|5t=ԏEYŋ19=_.((())Y`Aݥ5ϟ?GGFF[rڵwg"}:O pԩYf ݛ7oުUvݺu::$eCtzxyx^9X)M~xŒys\>D`j?:p @mԆ @m#+p @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @m3'ܤhJ/:49_[1KX1?Rg7͏S!xʕb*K 61XFI/k.yBm7 tv̏#]S꽇tܶ/e}M l_rM {nSYRp.>)4ӑӆ۶rǽm^K3{߮} ֏R'!$xt hۈlU7ˡVx쭄j3-~6NҾB_!=P"0!P"0!P["/k׮Fcǎk׮ 0 w~!1QZ)!l6ǛLxR4BՈz뭕+Wm߾}޽K TTT$$$$$$xYٳfsTTٳkƬ|rx`0DFFYFhLHH򲶌3gΘ1###]h?~|^u8QQQ^֖N]aVۥK:jޤjtJom[3WT-)_6CE\y߅tkVC T!Zj8=o®~+2r*k3.]jsOw|+dS8]VXH3~źPwflȝ}ό: 1Q%AX=y}GS6mZPP@_v-00ХHXXŋ !aaa8@Ul֬YӦM;pt9sԤS0a„ \Ap٢E͛7+TWu;6D`jC6D`jC'>4@ *dl/um!$;k:y-osɆ8BBz5qmw!BH8֛K].*SЊ7N'=q{Ki7>Jb''Ndݱd1!XP~}g!Ԇ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @mԆ @m@鐇UΏ )lxWΏn;YPƘuGN̝,}Hi)H_㒹!W˾)@u!-P[uB⻐j10ںg!\ ALKK5̤3333..h45&xoX\ߎvE`e̘1cǎӅĔI&M2l6><11&Ξ=;**l6GEE͞=[Zv@e5h^駟gظqc׮]}}}NPթSlB322fΜ;cƌ!I6uԤ$պo߾C)\dѣŧM6G.^HS׬YcZ׮]{%NP|r3g۷ѣc`Z~A~ڵ 7~"$%%M8q֬YӦMhpk 50isでZpΝ;7o.-ۢE#GЧ?}j2dHnhbXX=HV oDNr PCjxRӽ ,o3̛7oժUwnݺKѣ"©Sf͚/Ҕ#G.Zj.^xĈ5!@CV{8p`^^^dddRR{zJӔ&i)))%zW^ye„ 4111/KPPߟ&>"F&=SL2e79k{1jC6D`jC6D`j(QJ y 0D|[_Kk"DJ{ GڽZ0aԖBV/@ZBvǪUܣFwhȷ i}W> dnZ38 6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jC6D`jwjB nW) Ko*WK^н^P]zUNրydkpTw1Թ)y%ed6->)>Mm4EK4s\a$te{3t-!Pp @mԆ @mV0r:q["0AXد_LޱcGNNfs||dX,4̌3F*--UyܖLQm㒖jXƌ3vX8ye˖UTT,]4!!&Ξ=;**l6GEE͞=[AAL4iҔ)SfCՈ f48@yΜ9#&*Jyw[ly͛=aaa[nS9ӹsgvm۶gϞul/^ܶm[FC9w\߾} C~.\@ٵkנA!~~~:deeBh^駟*ƍv;u\.ODDMȘ9s3222ԩS ~~~ [lQ Y5"ӧ?tɒ%Bd***zG}t~3gA8.%%套^9xVZҗ6l0p;zbVVֱch6}aÆ:t鄐>}?~ 6rܩS'BԩSV}:<,Y2zhǏ7<Nsssid!jM?#/^)k֬Zk׮tBА19gsgN W?;ylVF3 X,6m sg^4LzZϷ~+={8i.9;}'O2 ӭ[7ĈAbYY?{@Ft5--mرǏ/V^ꫯݻ2cƌo߾G~衇GC }x~y愐A;vĉɛ6m ^t:v8׮][p'B'N3mڴ+VTVVϒ!汱cCSϸdNt-Gå*4OdGC t>]7gk3u;Dzɘ+ieS3#`=įw?KzKw^{t_W^ŋFEE;Ǐғ>h…;w!СC&MMHHibXX=ʕ&U-9BVuȐ!ݺuSޢ;@ꅆp_{0LtpνQ@MU#>|/Œ3a1}ɒ%v}ѢE<{)WEv܍wygܹ ,x뭷d3T< 8p Mׯ˧LB2eիlԩ:^X`~<yZjݭ[###׭[WYYvH8rEYŋ1BZIqq{!pԩYf V5"O>M6 zGtyKR~U=m۶AAA4hPPP=#/_wܹs4_~Zg%j={/{&iҤIIIIʣ!z뭷P rBHRRҲe˚4i|d311AAAO3gMZn}1QՎ1bڴia4 :6kLKS'_?Gn} IDATs=},ޑA6錈xWd;I)HرxPԵkWF !SLƤִ6W[M C1NOo[)TA] w9ug!Ԇ @mԆ;z۹u D`8Z]h|C!8 >D`jC6D`jC6D`wԴ>nۣ]LӾI~5ib׻;v~TsodӿMLMf*$6zݓm6SҔe˿ 1v%l Cdlۿ}}o> iv~ڞ1Ǐ}u8yJ%VXՄsVEujgCTv|(!DpݓmrȩҞ|"^L<%cŪ-K9aim9edb9Zk4Viڂ&nFNy"7*M oךܸkDx(Mض|}M3^GDن$h~~/O"ss_.x宱һ% pBBٕҒ_xúNXJ&]ARDxhn5DO 5m6z-_:-%ި̿>赶a-/]sz=ǎmX˵ɉvSts%^4=yשi;>Z }37qzEķuM9zKL&^Ľio_ܿ_!$'f\q|mza_%| }͛єC??&_ibX/]!d_֦B!-Z'w̽5ulz)}iixA}۰'>0i鬋 9=qd.,(/p}N4q{ɫVoڽ֭9#;|ke}h-`2bDنGh7*^O55_[5_D*% "0;D\N=zk?ݰn.M|k?Ng?L [ !Ilyjܘm3Og] j1t9)$6DonC'RL'ۨ9Z ^igIrޓBv\p<uI=\4$4~*ooH(ۨl&m]M*hp @mԆ @mԆ .< P߽"0Vp7 ƪ[Ãb̟??00PB~ hh a۶u[ntnӦM@@w}7@@#OWid:twŋys@QA49ulߤ}v_5O(\r*QDU*e8wet{L>I& w. :tݻo߾2L @5òV}xGL&ӄ :]8zaӦMO n]HyBbccFc~233ibzzzǎu:]dddzz:M4&)>>bDF"00PN.XeYVۨQO}v͆v["0yBRSS-˘1cƎK'OlٲK&$$ٳgGEE樨ٳgDy7NN.NBL|'.]mvcp[U#6lh>pMd_󋋋;s挘*!w lٲ͛Dz',,l֭b4={:w,no۶ٳg])۟ŋmۖwܹs}5 p!d׮] "B:t萕E/z}DDħ~0R7nڵԩSsssibxx8>41##c̙3fȐV"g}&ju@ucǤ@F6}Ǘ.YD !&G?^JUәϜ9SL㸔^zI6[jJ_ڰa;vMYYYǎӇ VZZ:tӧBsq7l`SRRfsN!SNMJJZ;th[dѣ>kMy?&,""%ڳg_޽G\ ^:%%%))xIG 7 A`CpcH !$eo7|ºN"߿z1 0%00M64x9W+++M&qz߾+gϞ8NZKν{N>ɓ tmƍ111!7AmJc!] G A+L ^C}qUD%KvsMpjߤ) vVEEEaaaFF͛1~ *..fXJIΝ1 c2D:zhHԽ{weF۷ɓ'|mgD WWDTqСÇ6 󬬬7>NÂ0Tmi/i~Z+ֵx̙CKJJٳaÆ44nflntpbJoC;%ZL&^έ\7nܚ5k*** ]D"Ν;===K%Dφ ظq͛cbb֯_OҢ' ]]];w\ ΝDd` ʢ-[T_~:t<?~ΝB0..O>!cՋN4eƌnnn/^v={/^oݻwO,wt4kjjGG{ꕑ!Z^ɓ'[ZZٓލQFٳk׮C TPiq޽?S׮]srr݋ ]]]L/ >3&9tP|WΝ̙L eX/ >|aXrr={uwޔ\oyiϞ=>}5aFY'`0F6d''[nNNN׮]kihpw#l۶m۶md b^b;44444&?AQS"p8+VP:X0֭[r^z͝;_#,ʌxxxcwww útb``rЁhck`MDi SN:)}f@s,o%h @Uvܥ0nayfx,~ B3اM #e|C76[0 S`(+qfk'j\{y($mUX~Qh_[mN5>סN-pev;H p@@k 5t @c߼-~!h k! !0 i?-G[,d`D&d2$ d"HNHY5ę3g***E hϟǓʀrd`Mstt֬];v8{{E hzzz{cSb(w{xO20eNh͜r/`0TI CQ߹uCG )n @cƎQߟ644P* ޽WPP0vxŽp'~pjrKsK:a0zhGȻu6S***0LSSS#YX{!kM.@;9ƕ20]W!-/ Ko$cL@'e]ec߼W`0>7"O|g^޵.n.# ;hnߺqvL?ՍϞ#Ek( o]3dHy[}# }r1c[x!h104ԍWA2TSzṉt묏 Z!D!CNK+իW&Oo kXXZJRJe&# EH*S}5 G.h eaQZZ-p'~D*ˤR$҆_}u:#,liO]d\:!x:#cW_IȧK$1T^VR-rg #G-&+ʐʿҔml 4Gy/X G!k D ,g20h&L;3a6C:f=2Ϝvڂ0$L>v B#G,ZĤ!@$zZwj*l_@ːO?>sfPsX iD)\unE={cЎ$QGPSPJ#|Iðʬs3gΜ2e2YETaX7WO?B@3\5E'O?-vG apנvppvd`L[HI6~ Z|Ҽv횃CLL4K'M >a2555z Y [HX@O?7q0%BH }һwRSSӖv:H}`M&Yz-O\\G\+# :aXntobՖ e`RiÇ^|5G[)͇SU;R=F{ѣ*6uMx.7oE7^a/cmS&i}@p|^@¯ zqֳc}菢g+K=Bam+}PSQQQXXyf E Vc*$-@+W!h)*$-@+W! phdRiT`0L=:TVb1X @k20yH$ů_?XQQҾ"zta;pc``Ab`Z'%bg``ЩS?##JK޿{iuZmD˗L++6a( X=&N>@Sh7(սxǝ۷߿'JucԬ_?eLf^=Ro|1ki_Z6l7]裏( X#.-2Q_Wa:$bqq+W.3en`0t`ðcG\MDRP?\ijjX,֍K+ʭ8-G+ҪB"i`2!PZ~ M>@3!^,t)״O+K+6.20Xw sdBoEd266z̼kWg̜q!ɘLwSin\وT*CHu@iJۂLXZY}dlE0um]d`H&{M߾}u`ºSEYY< '-;?[,%2 F=0.VJ-d2[׻ addKL/h>1 իWK;(-5SX,~_J>r'㫫t E; 0  k߽ӏe~!D1ݧ9LRZp;Ccǎ0j $P͛y7mUIQ}o6dZXv]II2.hgڻi>}?25ݻaor2aH ּNC=}[={4kc_StR+ҒW/iQg]ЌXPƛ7owni/ւқ;z9!><{lշn2~ڧX.#\oݺݬޫD0 dH*I2&뀀ަ =ݏOʂC}98ݧ/aEEE'LO vZϝ;GRt&~w;bvr9nj53v˗J"M{iͳhgӟϷ駟5A|>ƍv3g>jȵp̙c2L&s߾}3g444),P^^^:u"s777###ww\yfSSSK0 ' l6ٳDYqعsg 5-Ay"1%nzQH#z[ ._?ðҲ2 KҲ2 ÆpY?!C98Gi&!JKJ޿KLܷvZ\z?rƊ>VXXeKZ D,wQ5UV̜9k reAP!aϟ?tҒXuWejI$o@ԣL~~|U//ϕV4D mxĬ[^S( H$G"H$5r,YBPKOOoɒ%ƍp…Dp.\8zr p+V?D$>|xҥc`_~͛p^h1@@y"93cJfN;='|ϞT3СC=d'b-[|׮Ef޽,ڂF}Lv.|udAAEn:x SNa ą.]:w\EO~LA)Nob }q) IDAT/M0%A!a|K.k344$[YtiN&|e#qFN:- wpTnt„""$D[^ܹ͛sÇõk׎;ֹse˖q8BzΝ;O2e̙;wnժUϟ?W@+\\\&M@DsfV6{5o^p!TsnTsfVT֭?[ncÇ;;6k786~'wjlT]@).f/޳'{(XpijD=hff5@aj[nwǏ?~g`APC ǔ*9hР7ng0RdSU怚^a&Hյk7|xP<駟j|B$+B0%%%,, _d{ QZ(!qpX,"e0FBbcܹ >>XlfOQAxTԖ~iƍHMMiTT@ <34󰃇IǏ'; wmټ ۣҎ'&k&Hbq.]bq֭%11I$KL$n\YYY\\W쯆 5 y=/O$UUU8rQ2rxxnoh+V޺u[". nnnEb/Sge {aT=ѩNIIy55wީ6mv"rD?CXB"`X~A$єU#=v˺}=D% ccB¥.]E"ѷ~{AS|Ѣp==ܹslO?imR(Odr(M|^ysJJf0_g:|x2e˖3!!_|y>@ƍgqIBmkT⪐bMLL(u@i6 gv1n'*^:Htq'V~Z?:z- lMWð^Ab%}6v@iJcR|?g$n9w"c陙+~J\YQajffc6v@iJSbҒlxa֏S7Efh`92y&u$C(EU߾}wH3: zzz>2-·(/kDATF!P4ROO0.20C[Cßxwԛw 5r4emaa)Ս?ĤOktXJz`A ؖ|fd}d?ښS7UHC& d@ӭődbftppwek>}Z-U sС PB.sL@AGwc&`Ѹ׏e_p0&&MM;uFA\`q!L>Btoh`mmmnn E74@ir`Z-X/-@맼~YXxݒ1^Æcs8{)bX7]ñf2H&C! 5HB>Լ{.5ND1µ?/J 2L&!$D6۷/=X>0h44 s!藮 z𡢼ݻ{SK:6^ialj.Ov}] :uAL>ef20;&bVt~JUMVi7wMzɕVd2YZZڔ)QxD!R`ag+1NÜeopQ\hܥqݺpzmBH.(Q9N?uQON6ͬg/ѣdg5è~m(E!ee):sIa)g#F2ZC3"֥P~@@~-\.[{KU+ƍGc[oo5k&CMH$JM=0wn+UHoCM5ѵ[wb!Z+ 4sfоnݺݹs'n׮qƪg+GeQU# PS-ݻ l!3G.VRx*ۥQ\.ÇCL& [0///<|ٳY˗ Xzɮݺ;8uVnݿnoy]uE9'B;vsss300p8 &4B?,$צ9ʂDXv}{5̙̭[YXZqYYY۷o'N˴Ӑ7nܐuQQ@SSSCCCxu֝lkjڭnD4takWzI!CMztArQނNhfߕ(!oޕ3䐋]u߶m;܂ką/_x&=v~!CIR|W}}=VΗ[XZKKK-j:xjB5qFux(oA^Bڇ2Zp.Ms~~U m6m3fpůlvuUeuUe 6Qn!Ht!PNG#s`&Ɏ1_444um[-vWY *1 #|rUj0s΅Ə_b%}aTlW{߽}?ݾs(US֮Y2 9СÞ=~f&gF9D1 H$[f-.\d뻷o*G裾Nw?Gӝֽgbb2qޔԉMLLtX8Ԅf\|}|_yd2;Z2;vlٳ={0gO>> a(*0PMuYi\_=Ԕwݓ|gߏ^8=L0 c2|ݻv1C9ʂ=L޿{۹sgTڳ)˴wUeafXB/`0p![n??%g^~Ë/R)QJ{z٣G|woYFv^&/pgg? (֬>}.[!buul:pIn+?%0bP}df511]l7 ۡmmmB'O4H}U%4]2i=*++u<@s H} kIRLd2£G]uEEEaaaFF͛1%7|ɓISLi橄p0;;+Wފj%t͛7у\: zzz!b#333e5c̘cƌ9sӐo6n\pOQFbZMx 8ZЇ]~Maa l:|h\o6]~Ȑ!l6[gS"(!Gq Lld(?Ć!J5Ĉ0eSxP9Qy@0w UȒ%$|%߹^vɓ'z9Z`ʕnŽcc||Di把ׯ?_ڠ_^___YYy<.722z%-E"Q֭0l_bH$wB+}:alײ/ٸ1… D1c|aӈFѷ֭[WbΜq2F9D)wĮU+WeK6_ۗ:KM&7PQ6B7WWbqssk~gi|OT… 3fVTToEM쉟_}}ҥ -,,_Q g֨+V;8b3v:ԑRUޱ#fnZJ>쳭۶}CONrW]]wozÿ>!4o^v1 ?fHMi#e<⛷o9XrcLFEP6 (}~dhhhcc3o^ rl!8o U=2 fbңKc{{Ƶy ƍa10𭃣ьGll۶uqq%8uu,+~y[7||d֯h7a$ .I$e˗/?mqe@}VcAw;&boE;FKfXL&sg[[F @2Lz;n* !k暃7x+Z܁6\zgqhpk@ ̚_Iul.  ]R6|u 5؉cGZc?J=R/@T+P<hV 5@)->Qeee 痟 neeMu).KfG}UMQYYr4-)RƊR ) tO0,55‚Q[[[9a.Cdnp8gϮMrj ssxG4ׯ_=Ν;7akkkb &MpBCC_ztbUlmmmmm#""$Io_PAb|~nnH$x"V8/Z^3<<.33sРAHC֭[zjPzkZ\S(ec)R"Ezzד'Od2!ݻ#""(uF#<PvR[[qF!ZmAX,Sqq1"VZZ秲Ǭ,''/>TWW4Ҡئ tJKC)I޹?@w(% ðz8YbbQpp7oh4-,,0 c0YZZ߯^z߾}wﶰ]2334͝7oQhhhnn5[XQ6[Ja¢~_nذW!t C7f|#99w5lذ\ɓ'[YYߛ߿p<==ܹhEݻ䔝gggxy\&**j!]pFA8s挧'1bıcnܸ%bٹPNMYmf͚5?#ӫWH# Q@y}LMM-[F,ɿ͛7vܙ?~ǧK.ݺu/ 3ٳg׮]kjjjjjnݺ3g4Nh 4k`qqq , K侻&Auuu\\ܔ)S4 30[XQ6V LM6+:~_XYY◷}O.\~9s>|@|dXں\ 1jWŪG544< !TUUQ2r]F96޽;rHkk7r\a!22rܸqk֬|2.J2L <}RSRR%޿K S<8W\H$Df rǎ+w)fVVVٳgܸqĮ 3?<! 4EwXrr׻wG~^(Y7EM28\]] B/^pssӴxV1V aRJpss &Q2Olmm+**i+#rttxX,!vx< }ƄbeFV);d?@?~:vOaaarrĉ.дi233srrK5E(Ǐa S"8sMNN[šjV&mHD,Ϟ=Yd >_)J50OOǏ^^^~ȑ}kr8'Np8 ÊIibYVVVSS`ggTĺ$@iqM2VRHT{{{E __ߴ4>O a?Iӂ?I$DB斐@Ւ@ ?-ZXb$''DK2r]F960 C=}tӦM_|P#(ofjv`2 .^ /_k׮ŋӨ)8`BaXXݻ7m4o>>>!4fK<$**}u9r䈭-Ϗ%vz‚X[[>}Zфbk`ϟ?x[liDh.&ennLK)lcjG>rȑ|ԩ*P0ᩩބe˖!RC]vQ{ϔW=jkk/^9δiw,4Hb >W\IUT50QTahihhX|G Yg22L"wn,$0# IDAT#c<_i[ @55 h@@+Bt d`20] @@J{"2l޼y-@Ǽy;8*2,@pqߐ5ݭݳɻN:r>\.7005hDff=zիWq!e(K-Ґ9 J4pjjQ'dij)>2?dM|___3{욚!Ь]1P/_~+Wf̘rmmm=z)9Pj*ҥK SIX,^jmDDD"Ѩu{{NMa؀.]QU7s>yD(&$$xxxwɽD//ZnݺիW իW_5hDxxxQQQ]]]ffAp!e(K-Ґt//'Od2BHݻwGDD4Y}Z !eL(즏?8%%6%%eƍ4BYiaXNNNGD[iiiTTfͺ|P(9u԰a5HxxիW԰e :::884(((66UݻW"@Y&ZG]z5<<UfnAMM5Y"V__\X|ѣGwލ;888ZPPz⅛[#jh"(++WNNec)i!Orrrx"~7& 1))iذadC^pM4Ғwf̘fGymE+EX,֮]lmm|ܹ;w8^xlҔz(J("B;p#GZYY|yyyR+ VPP 6b|cvv6RJɍ؎JHH(! !T__p+df55qwwBǏ|//c'''@ֱuP֩Bo߾m7V3 %ra}Y,BqqqSL! bXe4PIIɦMS?VVV-%' .ܿ9r؇999`mm]YYIƆXzPCCy(9eL二rlܽ{wȑ7n$&)Ja!22rܸqk֬|2.J2L <}ReFq^Y)!r劏D"!206=h .;vXOT˕+W~r5bF/,,\rePP:# P6L$iZP+֕e`:6: L ,99Kn]Quj]]BH(R@SMiHa]1VU-}(ꬬR?nnn-*eTUUVTTm) w9::^xQ,x<܄޾}KcB2d+MhbB2ʱA3~xu']iӦeffOƊjPa@,1!"0wd50XNUB|Ml4(5zr%n̙nDvy͛7pR)r9޷o_zM^__ aX1 㓘XWW$4-)eee555 vvv2VU-Nuuu||}iӦ/^kv`2 .^ /_k׮ŋӨ)r:&{DFF&%%Q D")//oTV9;;?{rWIIɎ; FH[n-///++ۺuI4*a؝;w^x$'׍u5T_D={ 50|՝r5ޖEzm5է< f-4iiiC rӧO.ec)R᪪Yflx3f%&OTTǓs[[[>Kzꕟ!)++ vuu=} "*?ޔ;i.&ennLK)lcjG>rȑ|ԩߔ(SrPTYcB]({~gdzΝ;Bܘ9?#><<߾}{ҤI*)VX|ʕ+rjG}Ǐ:e<)T_D\[3ZcE\;9in/_>|t#ϟv@@ҥKgMo"Mo>Z3eF֙ L&ȝ[7? (,,ؼy3a)077g2;v PDOOOӛbMo"M-O:ЖP>QTq)@ȷh mt d`20] @@J{zD=ЧZD&͛7O7qvȕlQؼyZ@*2,@pq!egggg[YYgggw:ukhD3*++\n```UUU#jЈL///6=zWBXQ6[J!!R/0 KMMuqq jlx9{z?-BȐLMg5554Bhq.]4`9a3-، 9(-Bqq1F WZekkkkk!HiR/\0i$cgg+e 0ҥK zTd`gϞMHHx䉟W_} );{ٲe7o?6mڴ|rBo:t_f4;v찶~;QF;vӧDžl,BJCX'O>>]t֭_|QXXHܿ{ѷ#N|蛣j/aH qqq , KX,'ObH$6mݻwBF̞=HʰH$!Df5-X,4hr(K魲&(R+FFFxbnjKH{Gn]Ilm߾~۶mf…*ƊaX[l n߾ [t<@Y-ŊtqqAAAqqq"(>>eXwDw1c Es*]3gΏ?(.]Av>-|R|PPr +s^Yr-).Ł"(..>l6]'O!TYY}vgg窪*d6lN)\.})te%ߡ_z?;aXEEk|B رc@0uԃ ”S".]4%%%6mW9t!LBŊe` JJMMMIIQ… ߯NAn1''Ғb5B֕lllXC",!@&x- iԤt^r +s^Yr-,NŔ}'BD|2ź|2}3q.\8fr];ֲ캺:P(#R@\TBWWׂЋ/4-hMPec)o!eR/Z(U)󤪪ֶBeԄfsttxX,!vx<#{- "ʌ41SvȠv3~xuu77BBBޫM9x @Nzzz>~·9b9ҫgϞ,YD.U|ʕ? 9޷o_zM^__ n=o|#nS$2*$&&%%% MkJdddYYYMMMBBqI2VRHi&VoZZZ]]]ZZϧðĄ>cddD#H$266D111-!!@UUU.ZH"*qqqINNʼn"9dLفzM zFPnB3zkΘ1Chr(2b]]]qa#LGٳgrBccO,[__ojj*vEnj7ߔ7Ο#F߿_,:tHC$ɡC\\\p ήۿ#{zzUj-_>V4 N?_Uu <%)uoݺl֭&M0ݻwIII111߿)~ɠ%KzٰaT450:j۷---;(SNJM*.;}/^LE(//`4%--mȐ!rRƊR i ) Yflx3f/{]#/%GĮW^YXXkkkWWӧO+P,r>>><o˖-X9d@077wvv&_61 a"==իW*{r(n?j„ h͍n۶ZұcB쭮^x-Byơ/^=n۶‚syf.?vvvvvvT鿢dΜ9xەRr,--TԤt!t˙V e9+9 eq.}}}\nTT!Hb >W\_ \LLLHHM3Y |RӔ"tZcE\;Zd@C&͟?q͇L&p֭[/_Ҿ4 m_}*HP^Vodppl`hd2ܹuЊŒ͛7c^kK UlllKZ.d`@;AnqHVֹziB[ ]20}VVg+to5t d`20] bDj>=:`7QNSTd`YYYB!kfgg[YYgggw:uLMMuqqhDUVVr*MkJffQQ\']7Z %&4h@DobH0Ec$"r%-BMNQt >Pyt7twu+YoTu5 gGsg}vWUSr<,,ܹsPrHkB")yc!Ai SFS$B^2)#P(/_n6 nO4FIIIqcǎös$Iddd \~ ( OOϤ$wۻd$v]{{EHؼyf6ihh(&&u O:5|RI{eԩWXx3wRRRkkb),,9s9j`(^˗/GEE[NeuuuPPPkkАӷcr?S駟.~.ZSSH_!')D*rT@]ؙ)))c0,DrdfϞ]ZZ:88XZZ}v!Ybi0YYY q=ص]^ͨn?wDXeeNcZ,h@rrruu5ܫVZd2YYY" &)//wa Y⛬ OҢ]vpoL*c]]VbJ9ɓ̺xv!f>|ܦT*x@7îK2dn455͚5KTn߾$I x9mJB3Lɀ:.Rp) D[n0~m>7L&z*JL ` A444p CZ;HYb45M__7xXs״ivlfY%Ju].|JZFX-ې!sqp*|q/ cr+0d!+0Y'0 O!31}f鵵|ndV`SL2s@!nTEEA\ÐOȵo.$It`0w7nHqCV`.݅:zs֠o"MBЮ(y\tCJ^BBV`N~ YPPPQQQ]]ꫯ T(6ȑ# B"b ahR"VUUY֪*Z $$$$jt:WJjjjOOl.,,BEZ"A3,v>g=z488W^τ ~uBQĉ)Φ_t xuqvqoIIn/**ߋF`ɰB DhkkKKK{.>!d߿nBY'0 ???(F>Gyy[o%6mҤIEٳnX^^N Z-}LKnq򨨨ޞ H${=9r& Iܻwd2ST.io7nܘ7a2<gs/sOFFBѨ\zWgggdd-ILLT*Ǐv~-cm_~}$O ,VȐa"B*?sLpptq/ cO_~ )RnJߑwur7nܸA?M(SA;v Irٴ+++====<Dh4/,=v# ϟO$I.\pIӧ,XP(<==:;;]*v]{{EHؼyf6ihh(&&\SϟT*cL:U⊗daL\DTLй8m;ydaaakkkddG}*iӦ;v\v---mʹʕ+QQQ-,\PT(ʖRjwW1 mmmׯoq 9YH!R.yc7ߔ:t i _-yA(d-[Ңv- <ܽ{wƌc^willlllڻwp{yypvPrʕ+W$%%%%%lÆ ?_}Ŋ.uwanڵAAA?ꫯ£I/2kԩS7mB{o1\z/=j?>'H^]n6JkCCC n 9sܺu+!!A%dhh( ʕ+|f @GGG@@݇EQz>44%g 9YS`)~k֬/rpgF3}SN޽_`J> 292L2b,K``i6@RBBB:::rVʵsigA̰'?NTT*i7qd2ٳϞ=KT*333n9s iږXjնm[n]z5S;k:#"DNizjZV^?o<3G$ɴqF4v;ןeee^^^'N ɬV+ܶl(gRSSwww73cyz>---k׮EDDdm8sKq]חscǮjFuێmJ"]|y۶m/K/$ˣZ0bbb:N%Mcc#L&s8!ˇ1$IT*V? [8rj0""%%=:?nG(`0hZ n zBKl]vzNbL8`X>Ç/^(*((uXѣE9sf̙s[rh… vBڵh4 CZ;812Ӭsj r:( q3}Z>72+)Sr9 0w7 aHk'7q$IPl x;7n!+0Brtw }ir0 vEQ3ҥK:T$Gs$~AAAEEEuu*RPTWWl#G( oy0嶄=z488W^q!6HHHHQQj-..t%RSS{zzfsaa'"},Zb2 O9XEhhhUUjRH$`0&L EQ'N(*;;2222F۷׭[)%%%vH|/%rBB@q A 2|||oKJJhӬs rú1B`% S^^p8z-(l&M(jϞ=t3???rZjcZrGEEeffdff.X@"twwgeeggg߻woƋk'4vPH޽{M&l޷oJrI7|qƼ &_|Eoo/sF|p.^5 9$ );w.t@qq1K.uttz^Bovfk`^Fְ/^ vww(B|-ùדE+cccw%x $/^("rHkBE\!mXe˖򐐐Y ddd+婢BѨ\zWgggdd-ILLT*Ǐv~-cm_~}$O ,a"B*?sLpptq/ cO_~ )RnJߑwuNA rXd~"7&wQ0WxرcIgϦXYYQZZJ'IrΝnnnPx+V(Jy EQZV[lwى,ÐO9M#^zuJRT.^%KC100`26lؠh3BvG;u"#`!Ν[SS#$ nذAV+9E}\6W`vZ`2RKpԜ:uYN>O?b|]*ׯZ555^^^Pr^HkB"/ꠠ֡!Z(3u!ðqa={vii`iiT\VZAOJJHHxsssKQ N8aX,ˉ'N8]PPRwWM&SΝ;ӅMˋc-K뽽V+RiRRҾ}+^]^ZK?-? K8ovRјfRɔ&j0v{}}}XXvwwϙ3֭[ -B߸qjAQ^ eəB iX.bܹuuu \rE%0_D?Ϙ1^pכoyi(Z`;{ɒ%r|֬Y/^jv!bϞ=fNڽ{믿w!!!**==}$drdd2bX,@ x9m3==]Rttt0嬔koo&Ӭ9aO~27*b- !"33STndٳg={9,,LTfffr8s .(j---p9((jժm۶nݺzjvtF(Dډ& #""zjZz~޼y.ig200H&iƍi:N#w?<==a7 @&ufG9>Λ7OiliiU\v-""B&uGg9\[[[aD=<xd28\jaˋk-Wy!WAjz˗/&>$IT*V? [hlldVN-BA>>Fe vMi~~~.igu֬Y::_Y#"//opp8""b߾}h4£$/ݑB$I\hѵkט{%%%^B >XeeNcZ,%M IDATh@rrruu5ܫVZ 3d2;L!WyT)~ZZZTT0deee}Uk߿NaXc]]V_bJ9ɓ̺xv!f>|ܦT*x@7îK(dn455͚5KTn߾$I x9mJB3Lɀ:63# OA@L&ڍsduT*131Y4445 iH;D9BaBBݻM&hɁ_kXֵk׆yX5pwqG Al=^P(U(IWXB\o6A|3(b:Ǯ+)) fY*bv-y"w!6i-WyzbZe2*ooCYb45M__W#%°NQ]ӦMfyf9T*ݻwTp)ejaƷdo%uuuSx9m[!SY9: G9 LQ7ӧOs#2e ,"S|w***vxB||w!I ~qFs.$7Hq_7[&!G@ hQ!d߿nBY"u)?crJp[Pb&MDQԞ={f~~~ЍPǴ(\`D"*..ξw0 Ni"$ɽ{L&ټo>Jo&>>~ƍyyy&L-ÿ^挐]$|!j0>UV ޴iӎ;]yfZ~ʕ(˖-kiitwvu999JETU Ceee[[[%}i +WN0!))`0U?@!W"E ">eee[nh4%_/]$ E*@]3f̨_~e…2s޽xBj/]"J=<d28atAEO+dM Z=y˗wuuqm $SSSU*V-466 w3HIIyGΟ??&$$vAbb#G(2 Zۅ A^P(z=eҥ9996m׮]$w f C&~!pu<q֭p@K{iI6$I2jHOO7)))LÇ/^ ?Y&99u@KD^^`MM/QUPP x ϧ(*??ɒ%\\uNMZbѣG):s̙3Vxْi|||\9| Ț@wV (v\.ٵpBݮP(>>>v1;;; ׷oLn'IRxրẞ_yi/?.4$W^Z,H._m6xaETVYYXc6::4::\]] jYf)۷q&)//wa YBN֥ OҢL&+++,-- qjڵk/]<NպkJe?sɓ'B7%.Al6ÇtwJhv\2_#\4A̜9oeJ̴AR\f3Sb0~mXUa ]Jd2L& nR*QT*dqzq"`'%x>#r5#dwd1"~ Y-544 <̈́ݻwL&јtR1EV`Ʈ+)) \.ZgEpkɤn޼yNƍWƜW"Kjd2UcWbh4j4>Z.ݴivlwT*V9R޽+ۅO)S+0%~a,᭎g N>pMZlI63gVL>}zmm-kTqGXI҇QM2~np8!???&&fv"[2W*LECCCN @:x$!#C,#GVvEEAV$IQܶlΝ;NP9yW_nP(m6ۑ#G 1|D~PhkkKKK{]RTTdZu:]%55l6ҷBNi-RT1LSN'>UUUVJV X"H=+dwL0'RM  tF֭__ߒ^TT$ḁ0A>n8NXXg}dlA$ vpĉmmmNlI&QgZ_??rGyy/Q[[kZ? Z>~5kl޼YW|-iz-ZߝjG:YHݑ!9t8pQQQ=== ,H$YYYN޽{M&l޷oJS"}50օxy5ŋG)%Jü%X\˼H󘨪z7H\xqGG"},ZP@WH'>Fqٲer<$$κ. xpTTTh4ZK쌌tss%===J\.N]~=$$DRaLBN GZEuugg50d>8M̰1 ,Ν;JkH^ɴaF< 84 #IrΝnnnc$gϞ}u(W\slW` y>yVΧФfn `0W` `0c 0 `\a0 5`0 kp`0 3 el{`0'^t "22 lY[[(kkk[$͡ bf H3Nc]&((H.;w Bi-RT$e4O|,$IYY=RF#]_={vɒ%$Ij4 |Fh4)))ccb0;ydaaakkkddG}nM6رڵkiii7oW\)//%˖-kiitwœRҢT*srr\`lkk[~U+["HE_Co)//?tPWW=ZcJJ…  @ 2LՁb8Er۷o0ϝ;RG-ūWniiijj~w%<'dΝƮ{|L UT`EEEf„ wh&Hƍ'Hݡ޽{~_~Gzj||ĉO> aʕ&LHJJ2 Nc]%?? "}t Z)cQVVuVF X|E 7JJJ|||3fA/pBLFEw^||Bj.]jvJ޵yyyӦM_ ܸq#44tNJ2%\H ‰'Z\.󫬬pBPPk}||n޼j77a?=z=~~*a$.+WoI&mڴ AAAop8l6xN&3j?>'HyyyWfJ`~lmm ã@QTLLLSS@-u:݁,r9d2p8P<#>   N W\Y7" jɓ/_ŵM|,HLMMUTZ*]<""%%=:?nG(`0hZ n zBKl]v%i 27)|AP.\p8͛111ǎ{+++׬Yl0cgϞ=|ll,|:#I o}yh={ uS.% j\ . 1_yp?rKO۶mVDU`:uvdwtt-Kiiitt4 99Ut˦Yf)۷$ \0LyyyS-| LiiiQQQ\B& 8dڵ.V600@jAHR(T*&OLmf/ A6 C6]RFh dX!Cru /ӟ=tmmmfH s Nw=(sΞ=p8 L.{yy$9gѣ_[lX#i9l +0]VRR͒\.Z7ym7n:::\u\\_qgX VU&q[CYb45M__-Avc6mZ}}n7.J:Tw vS2 oɀ߆ 4!,|yfIIGx SSSSWW<sqA&LKK˴iӦM Ls>Ò50ݞTPP f(ŢT*#;mɲG50V؉@FsXAAAEEEuu*RPTWWl#G( cd%J@[[[ZZ%!!!EEEVXӹz#u]%55l6zzzB!W\Y"AL&SAAԩS扏EhhhUUjRH$G+]<&LD(jĉEegg€B毖t:]FFh}up8׷nE#dX!C:.|qGǿ{[n͛7ٳpÆ ͸pbY&???--mʕE1 (&55u߾},/Z\\Յ았xeۻ{nEA樨ޞ 8%xJ!g\x188=88HbT*a퍍山̫,"awWz7H\xqGG"}t ZP@WH'>Fqٲer<$$κŃ. J h4j:77FKzzzJǹ*]^~zHHJJOO%% 2LՁð.~뭷g͚USSCˣ6cMח6my1SÂ={ }}]d/͓~!| yrA!E}jZVoٲ(Ѻ6V`׺z5'\b0 0 IDAT>ܼyU`ƆޞpCӦߏ?^˥71͛ǎ۱cO| l!JJ̙3q`i~ `0RϨ`*p`Cp`r]H `0W` `0c 0 `\a0 5`0 kp`0 3 e {`F+W>i+0B\}:NPDFF677CW-kkke2Y```mm-s׷~K$944TP,_l6 )8$FOMMMPP\. ;w"}t ZHiXH$2___777z4%L"G \7K9ed)jjjSN޴iSKK0N4willlllڻwK!/0:P;r1 8??'N4W ]_G'$ ߛfRdJCBCC n 9sܺu+!!n), b )7n:::7PCCCYrnAZ+0"^sձ X244pB 3fHRf7|Pյ`wwwK,fͺx"W A{h4ӧO?uݻ=<<^6 QTNH4@ ru,ǏϚ5K&~Wϟj={)1cƍ7XFC& Ayyyjzڴi|XADaa!~Mrfl6B!2lpp"`ڵkRU Z-vE_cP*&OLp388 A6 C6]RFhvB" +d@%ܓ?JΟ?ѣ!Nֆl/ai =_/fƾ@={6$$p\."IrΜ9ѣG׿lFӒ 幹fy޽U`Uի?3= lxڑzc 7ps+)) b]ZVbq"8F7oy"w!2;W\Y+<|*ɸVE@@}{ѨhB*0iӦvLRTJ{ n>L-# mȐA]|瞛7o111555uuu̓1d Ӏ)tń~XRRºfۓ eXOՒ̙3Y ( nS_NMM-..BJLL|텏c/D"ioogJΝw^400P\\EQQQ=== ,pyp SPvϘ1cx?\c;xbpp{pp0DsT*a޺ɷ@oooll\.z7H\xqGG"}t ZP@WH'>Fqٲer<$$νŃ$##CR:C***4ZͥwuvvFFFђDRq n_>' +d0ppt9|[o>k֬ZM _3ȄA39z1az`Ϟ=ȅo߾w}/˼yrLJ~B-0 L jBx N?XV-[wDvg:Jv?clh]+k]?pOyy͛WZ5ByZj ر{wUywΙ%!$LmYP$$lQAW@P-.ފW{֪m{q*K]*D"" HB$d!d$3r{9g&9=r3l_Eu+t?6lܵŵhkFfZÇ]`{{{MM֭[WZEDAo)ILL^@r<x-[_j\O|>+y3CCw_N!ڼB݊!iѹ!5@F0ؼ#0  f*$OO/xn\],#UW6Y7Y{3]k|;#B&㓣~şd Ã'GU 9D_.X+,i0Ą-5ZbUfjMٱaf퓯 âZDŊO压* QsJ{6(ܛkJ_'Ȑ.S=ţC1{f^StqT&%&]ȄcRcBT{{MmV.9G`$11|)ml/M463usLySD7!|g^}3MawH)1ғM;|#[lBbb튉$I$I1&l6Dł{¡S-7OZbgDvݵ*,X07Ջݙc#nu8#[uǘG{Wc_gqy[ҋSz #NbjbEQo'fy>.oJ?M5ݽF? 7?{ 1!߽q̝۱@ɺN" ,iHsosu)#B-kk-^ȋ?__7LE`]\dphIbµ?|yס5Q=ţC1"+-ↇj:{9i[֓T oڧUIaَnUܚ|,Mii'i![[}kNz}~|qG6q}tafcWi3DkU*EϜsp?>'ۼQZj8S =3 k7#B4:lG/lqddDkJn1ںlU8T<0m ;*tA>,Z '<ǝi4׫mȠJ5Y76[FćRt~RwJTS;QZl]r"ZTd~mG(]hAngufd|<{'$33uo?1&1rXVc¬X],ext?dMOzsW6L}Svo[ԈcD4i&sKGϣ6+йwu;$Ʀqw!ॸW Ǝ 9h#`7>g܈'U"09yг9YiwTz 7ӣ %m9 r\07tky B[kaQN-]6}U.ՋAj!bd^]*&+R" V̈]6+uG8MeSFOOy~ZBDSFG9Nջyn|zq)sإlqߕ>qwٞ_1=t"Dy(P?*ΞSW\׿5 w)~rӭ$^.7%GMO>VN &}Y)O6yh(O*S_";%X={_!jTySkTl,8 Ɋ}jۻϺWy3OEJ͘ _QQQ~~Ns}4LŷΛ秈wf;3#v۾vl yuiSDC2Z?e~L++&rWDTY߹jsM0RcBSQL{Havy/!:X aZ?]WDDmqs$ldYlwJ_7>_(X+K ѝػ{c/Oټ쩺NYq!z 5"=2w1Q[ _3k)!ڨpCMq7{w:Sl2Y]gd|mP>a5Zqw[rvXn4M:ZO׏s G*m6?/K/ndϯ9?kܹիEDMKܕ?r7?}B+E:,vU^[:zrFU3F{t}=仲Eݑ|1;3V-p2c*j;>Y$9YݮD.ȇWk+r 7Oo,WE4ulZ঎jji cƑOuXz{8Sq-{M'EOIpj蚓s8`ljwE8s8Cj{\cϟ={v~ ,!>!///M^ ^OOAi&!5Րڿ '`P!n7ٽpZ=}0` xmTjU᜛}Q;khl8pxJNnδO>G;32polzdZ8p@ZQ##2wtƘĺzz?UpH1E`_,Xp@jZZ.$%'k)DDI"cD$P7#0 Uw&Jb/=w7ן o'cQTTsεkgU^/NN|1vҮΎ'Keh"#+-Yp%D|9@!ozݟW$ѡCn\6lxZ*n?Md ";-3C$}Ԩ@Y-fyyԅuF7DKM˅D k-Kcɷ-gE>T:qL2q\[ƿ|SDٔwi{ikּG;Ĉ(!^eԍv;oJϴϜ6ɩZlQҪ JdsjncgLiD`}`8}t2{|JsjsjT|Fx09!A `sHf{NV꺏N,ō RNNyxN-ڬM='BIDATZ5xvxUJ~:ōr>x:a4Et&#F$1vTKxZz=Rr¨|=΁AUCgn)J)J?.zԥ)6N)J3<4Kq#.U(Cd6dsHۿjPx"28!ٜxﱋUH>H 5[z‚U8N/^z$st_1K ]ݺ5s6gi9]J 4wO4D(n@ʈ;] V IDATxw|E~)RBT} 4i (*4QP/DA@(EBHH/ʶyx񺛛f{{ BR$  cF>od-JJ11Q-|||<<<4 z{:t !9C!RBB6*qBp黹jZөj:,˺%6(;y[G-ou(TY/[T* ENnTM+d)%RJ Nx qFQ(RATJ^*5*Rj2L&JNh4 zRJ)U((n@y8yeYy^S**Jx{{˶FQE,WBL  tpE9J)B$X?P*UJzij٧DKZioSh(ZV:*f`ۢ9mX]`0,r')BT*ji\Jzl_RB8(eyʘ?$5ǜ[Țe1k孯]求G .֨U_B׫?]Xl9VLUj[ۢ{ Q_XXzjΜ937nȶT```5b"Y9%<>t9yYiA_/d"}?ӝU(\(G|TNMXJI&Ŀ\Wx련Li4‚s Y +$g~i[JO@@@5zJPM&!`0k׮ܴƍ˶xΧhՄ,O2ϱ<>F xfj%%%Eu]^hSSԜ{'wu f8aPf<%=ajzgre5/+-33HPznJ96J'"e *,^U_F1//ZiӦ/_jZ?~/P@ػfqj8kG XtD'YrTJ/BW]16r?ܯcc?/Ÿb@)!dh0g8E@naf -iIi?~GY;@Jj^=[hJ2{ܹmcįcVzWjIwd| N[>!yXsOYEw=gI^x>NYVo\na~9 :H6ZohUNy"z|lZsuVz{vR]ZZCj`oЖu;j̳tlЩY`V_aBn}43lfXl<'O{59X?Gջ^>ZFݧx#1L/'Ż g)rgK9sFժ̙36K -VwYFlxq,e-yegh҅YRѽU\F6!$#~:qU\vVѿnX7r#-dBD^MoQb.,( zJy>Ν\٬|+:&abg/?X[wi HǕYt֨z9 ~ !wfZPM\tZby\5QBfca(^e !ޞZBH~~A9QZV7Q̊qM:URO>X_%Ou&>$20q @Y,!~HSJXcR)0oכ/Ejn^o3fk^[⇦uS s, ,cwѥ ΥeE)G2HħS2d;I3-BxSȺ>2>z?oSҳ Qu_OqŬÓR!'L]\Gq|jf~`CD<*u {-JfN!DQz<2 pa~i% µ[ /) Mc9GZFX3$Re>~/gd( Vm<J Uހބ[wr{xBX8xk`RY`ēJXlR*,jZJ%ʧ? kb -U*0&^oHڿM9ITڿ>}/`T\ʦM˘ pxXO-X9JcLͦT_1q?dċlsh>w9EP  B\F\3Sl}tmBCC|v(&FbX WSZ˘e4p :_ tjUmUeHע"\6-m6k'\n]m~{U)<o5Ű43YX^( CRrrqS~(g6OmR5}3&GHyxknp)oq,ñc (JZ3[ Jd` X,6]ybR?6OyWXXGc^8}/=;A9֒mY7ˊo=vறw?Fyg JX7B) x-ݩsVRA9d65 d&ڻFq>,QJ8±dGDF&?½Kr/Dӳ "diZU/t&:4GJje4tn:<^2Al[^'w~ڷ5iه^7c-oV|_ !Z9UڞةRTڦD [=58Zzzܩc y*ʭw\K3ɵZKN]=w"=[V}?vdzx Շxf̙3'Np]S^7Zj}ҫ9)}j{Ϥ1y>>>@cd4- 9djN۷o6md[[n5kbQg= e{ׯrIVvB;]P5~~4^;zx) ST RNjjmypŃ@yaͱ^5R]vqYfMC’Y&H8aL&q,k^9PT\X1CءWTSWz]o] P%L:҃ L dfP"AJu38rS{wll3gmܰC鋗,1bDY֝5Rx:^{oSm暘m?3e4R.E?*E!*I&-ySVt'LxÇ 9۷dRn"tt: #"+E e`Eylpp~$+]EJ٘;7`0EmvW_~Yꦬ< y`9WA^͟2qd 7nZwΝ;^bXK.=sJ^~e{w="FrΝ;SAX-\ݻ‹O?ݧ|d!at' oȑ#̟gLD~AFӦM_pay[:Z̖-Z؏˫SP BW Y;v-w%Vo@%V'''`0*W]Rn%-rp @n䆳r ד@ah-qT 7 3Pο]j;+;+!t`%Y 2lJźPI֚Eyg+*]5_}rPMsET20PժܣgE+~۶m͛7j oުU+NץKgϊ;vhܸZ߱cXݧO^ߧO;RXqNE6m%˖-_RZsÚgϞm۶N8p`~~u"$4cǿfpů  /=/GB+nulbI1X#$xKזL9rH@`СC*CB{_V ӣgu/q"eef$'5rԔ\W+ר\IHHػwo^^{7l0p۶m2d/,;vٲe_|EttXG5m4;;iӦ}BB}s)!~ꫯ*lܸ񫯾JHHy^ja$k3fܸqQQQ111Άk_h m?vXqB9\U,ZXB -ml$5![[XX-mPB !dt)-y۷[h۝U̸q;L0ak >]b6o ʤWz*,N5jdRҽ]7/8$o7nwE7lެy%+!$==!CCB:urii+Vi.0)ن **,,L,o[noܺuK,_>!D ޠA0!!aƌӧO#tV谣"94--m۶mʒj~ .lݺ 8yh//Y<_,[h.]Zm /[&[gF숏ԹKoܸIl(;pKTAjعΝ:%qkݦ{: Rt~}=)IHVBvRxG+*K>L|]%L֬y^OB pXjtdKF3'c"rCٴisxxtYz=ypG'fΚi_ -|@Bޞ9k ͛2uTRRҡC33I1) ^ol޼楥K>M69RӍ=z{,nݺ%~4h@sX#___ooAݸq>6N-×/_^nݛ7oJ5/\pA//͛sɓccc cbb&MT]w$\ve˖Ç_ľ &3&22,yQF&M7n0\f_|Toȑ#fg_zKBg@ңG:uƽzݺux |}D>s7o>s IyJ|ZssTKp"[,)놷YH#!s yb۶o_z%BK/ɉH1UJwߍ-fݞS<;iGtx1[T*N(/&ҷOBWc&3g>hѢ_~%(ޑ'O33::ԩSba:u#N:. uDUVLLë;]v*Nϗԭ[wرIK.aK.^zi@j Byb6˭߈yqqo1Y bYڜgL2Jypz; *zL%!/ d!]̕[-V$jŃ79:eKsΑ#G_{- 0q9G^3gn% IDAT4iX|l9[*ȕVY+yzC0SO86777%%e %2mڴ kN,7oʕ+<" _~dZntuK,1}Y]:H;;Vo80..h4ŵjE$kM]xq̙SLq6\oN3O:Ͳu5vN0_|ƌoU1sbw_)6ױDl"-v- bf'X6_?^Ϣ7̈́zh %$-E.ؒm"YV+&BF+W.[6e1I5r8}!ټq`ԩ3z{lX[9%Z!TC%.t ,("ozIK/{nmyپ:zg۶m#"">iӽgCCC/w%Yfٲe5j/Ě111.]tܹs]:Hl?$$ĉT;uhܹ?իk8P(T*U'L0j(g<3δ֫?tKݺv?;w֬YZQޕ7ⱈA<VgoJ~:P"s|4z+mڶs]}w07΍bt.'lc:-BEbx(!d! ](-o.% }i~Ky`4v_}iN@`P gΚ9aW_^w=ñu2MA|8jr~[759+Q ø U~UVڸ'~$ok'~@`PeW?d 1 lNc`YHʡ2_P.Drb̍U]~O2Jo5aXKUw>@10!ۃPB Zp0앱~oDf^C*YHU((.xp1rC 7d`r=c`rp @UW }!TPPZ$)$!Ĩ$_P0 ұ1Bcǎs֬YVZ;w+W5kرc%j?]v|||Z 0̭[/_>bn޼W_M>]|uĉ#G_titttZfYVlyƌeo -!dIOmq+OW#D6g! d2zjX@.,, 5b5Fc0ccc #IBT*c)PJ)%Mʷ5}?p(**oNNNrrQ*UkPMT;V*(( ٳA+(5{TU6O :#= ܱcG1-e׭8c`rC 7d`rC 7d`rC 7 wdCոXqnPU#A"@bcr@Ta _/<0!ۃ /c`rC L=LU @n{ @nQm۶͛kڈ۷ooժNҥٳg;v4nXVرC,ӧ^ӧONNB)8 aM6ze˖կ__TJ9aM~|={#'lk;+!Vd}T!G6 Fq۶mbСCϟ?_XXp&M?l޽{X8y &N07tQ#BvJ)=}tddQ 64o H#qXR*,@Q=^FRx3srrΞ=*JR }fk۶My~~m۶?bٽ{wDDXذa+WPJ/_ܨQ#;*r߼yR͎;ڵfq8ITGu= :?楹s>\||9BTS0 C)eFպ(t!k>6NfO>yqJiVz~鞞͚5;qℋH֤*#z)*c G)˛:uĉm矋O'OX3iҤrH,vZ˖-n}&L3fLdd$!$99Y|F'M4n8a&]uøXe( GdtӥK6oRV5BfIw$2>*N<Ś5*,,kJM9aMc`È7#ƅ"Ꭼ6mZFFF^^… ۵k'Λ7oʕ j_d2[.<<\,0`%KFg}ֿ;Ο??"">=!!!Tq%=z8{aM[nկ_ҠA[n? NA|8jrsLZVmB T-ZZk׮b!ԺSBV-,,h4,z{{[,nz !dЮү"?;kplг;Cjj3^~=''`0ƒk۶ѣGD)8bE,RN7nB C:uJ2UW 23gN0رc,&%%;E}DEET0`%KFg}ֿP5 2dHLL̸q㼼zSRRBCCCCCSSS-[fS!&&ҥK.];wnib%=jԨQFLժU+>>ES(Q@n @n @n%]~fϬ/^@)/<ğXrC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rC 7d`rCP]@)uw@20'hݡ20j!z\$an z!d׮]~a^^0R;ϑ#G"##==={=@5ҨQÇwcƌ>>> ATGZhq'|cĉ5j@ ?d`j]v{}z}tt70!F8STSN  FB 'd`a0Bѽ{_CD d`qjZL!}ٲeȑ#u:s=$ @y8㤟'8p5k&Nw^ٌ-T#0!dСf^۰aSO=p$ !Fę"Rٮ]k׮/LW^ye׮]:u3 T#ѯcǎꫯڪU&OT*u:1 T#eddyyy=ܸqΝV{-^ h4xa&>@5Ϸk׮E˖-jSLټy3F>>>H`W P 6,22rԩ޲eN9sawGP . O?=iҤ{w˗/QFDDDbbdrwH2-[nZ5nxݺubBP(͝S(*Ek֬)a%ڶm{Ѿ}ڿd?7###((W(%%?s &;ve٤cǺܷoߘ2 (A6dȐqyyyEEEE˗.[̦t+ ܓ5jԨQl TVZ.­(= @n @nV>įh]@YQU#í+HA;p _;F20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!ˆ׸;m=Yؠ;2~ݦg! @nU,$!p/ Y3,$XxrwEx~C 溂BPkOSAW@v;㉏wԥVsQOiN-ܸ3S;vŎ:HIM{ﻨ`2O?~xF d`Oo@pJ)+kV1T˯^M#6( 8{U=d=z*effԩ[yJiU* !{B; (N *0yZ-<qƏ!>޺ݑLPIaGPPJ$ gPAP)U m90q?޽u-˿۾qL*9(I焉9np*qI;Ν!*|,?BeG+Q ;w&&&N4& slFRzo曍7d??_ ˛ėZPjzMZ :?!/u:bpV c9"><#Gޝ78H-?(\0 {wˆ#~޺ܡu(!d񢅑] 2$;;g!OؒPO 52r񢅄Gf팶 i&11@5Tk!{{كՊu~ئM /STJºPABk!ZF!>?*e&RX``PVff@@ʂ 0"ZHj8wd /X.}BCB_=7UyLA|8jrsLrK{J@j% 0(''`0ƒ*1=RSI g! *GwYHpJrg!@8 n <XlCPַV0,$Tfb,r srrK%T*}}|hQAFC0P~`*'20JeXBh4:]e#C?8ӫW 0VIJIGVU'`ʅ*UJ2e`FZNNO<8{IRoղufx?ݧOÆrh46W5'k׮Rk0Pf`=<ܲ RjծթSa U0))9ܷoThXB!Col|k滎Cbׯzp@@p`a ڡ4;'^S^n^r*UUiD7  WvPɑYxb ϶AQJJjܸJ:xt؈38 T"гJ;qHO]V@@@T֍; uի&9n ֪UK#-NL7Ex4waFi]7cX_ B%'.in9OA~k4wK*@&ԬY݁7[Lqi&33СCÆ sw,Irsssss'Z]"+TBa`y)tz}%9Gs6_ D@eef$'5rԔ*הhٖgQ4j z]c~yʫ;w ~\.ݻ~ IDATwϟ?Ѣ7 ׷cn 8pMNNN~"""'""_~>ǹ;* LjjwtwPY8jG$Zi޽bɕ+W?ѣNzs~$8dhHhX]N:]ID Px^\oݺ5tVۭk׭[˝ XMvA!U;XOBȢE6 oּEBB}0_,[h.]Zm /[&_7/8$o7n,eݔ8 0h%7or . oܤY{*3*}֭[jufN8V.]:fZM&uj#FXrJRT+W1bVu2 =zt$%%EFFzxxtbZ  K!jZZP|\~]vI%nݺEz><<<..̃T88 3'{s?S=z\vMj~(է{qgW;|3tHs…]#gΜիד7 {xb欙\5eN_ … ߟ=Q9/,;jԨ ,˲l1<==M&]T*MY'O֭[~~48'O~駳{=yd&˲?ŧ҂,Z, 6L>ݾ# !'N\jU~~O`g.' )ɾ:vnzNum۾mN|i^lua}.X7[WZYA1@f xyy1 3ON:Z0+W z꩹榤?-!JdtÇ8vbۺmr9ch[˖Y=9yol]F._leݺunP|a0<-ܹsm/\,SJոq͋ݳgO8̆k,|{f:v+GB^=z fxKREGf`s|4z+mڶ ~o+(Yxݻw[n̳akҭ(*RAAY/* ;4lبMv_:Np &&&թbV;q2gywM^^C_ֵ6?ݿ@z lxYfjݦ&p3v{쉊lڴi}jssεmۮMΝ۹se Ԓ%K9TF d 9xŋF?; ~Æ o֬)^c/00h ˖-+͸?{g5D JK&%YBqiXDQqAd XDڪ|*Ei-łT% K c~w-,3s{=sfrrg2cz+2)krr>߫A}fnfa1CG[s1 KxhkOcN6ug4Qu}D * rn~Jo2ygh9*^:R } ?GC%MWEto } >9ߐ@uƤN>rH@ Dر>{ԘЀR}Qnjl44|7^ғg<! !MT`uͺOoi'9REܹ,J5vss껡)7!P]Ǔ/|\Q~. hk:7\wyL;~ᱴ& [[Z4ϛu5X}=--ml| }ƺS7dZZr|x 'twrL&p3i[7,gZ1\{uӧ>P<@zd 5l.á( C[K{pݷC`zb`!dXg~@ 2i/USs:ttt341}"ódp@ /߼3ArEQh@QEb˗/o;Q@H@ BO3gZ35rER[3f )? V`@ wt̰)˩'Lxd].jPeHqj>A3[(:b_a'EL>Fimiz̍ j9;-[^z5p:d3PgkQ6}ׯw3gfX|d{'N:q$&9jL۷BªJsX &nc??G0[eȩHr ߷iuh\RGq $xv#C1t5Iؑ,c AE_VRU9'O{?NFnn=&''YXX<{l˖-65ER4-혟wȺUjsbQ2bQDsҥ^GGyظ8Os2Q:"Lv}ժUpIX lSƢ~<` jP}[IcCA,6 XvwF4y۷93rT(8ظظXkkk]]]?xKE+*++Bbo]5TQs&Șbsg-xӦ+tT \ihh8|p[[sX3r;#G7iv5f3hh è0sSM7x=s DgΞcߕBua VVV q)))sI8+$0E9j߾4ń~2yuF3:;;MĔ]_V_~m`64408z330h(5[؁{|ǚ Òbi&[X zR M$lZdəӧ?{d2E&N66(JҌ PMQ L&+QttW8x0&d6Aƍ/~T_nnjcksmW^ń"a MM GSR6o Q. oxɼs7mufZx(!/~;wZO.\}V?k畡LYO>ޯլ⬐`EK۶c ^B$l5Ɯ?t۞;?rh;mMMKj:L>L(De6mtrt|T6jyd ,>fwrmmIS}H7>bE ȩJAㇿ<EQHHl1u<8y*3-(E_o%V6v{B|03j :::fΜ s ކ9":j[g/gAZ[[1----LH;gΜurrP0?fguuugg'ރJFj=zP(o{ 8.8۶ݼY<>kqm[C/^Q(@u&aX^2\Ỳ)JL0<>;? ƿR18VM---+kt׻^ZQ(N<ܹs'LPaEU탱c+Gd3i c IGC\N~. 2CY RTZxF!bi\.7?da3mL&kq2+O@TAC?@ VCu#W{ʪ_^\p(ߴy۷e2ٓ'O֭[D"(GG~W;G@WNaaፍO>[ЭeEEEtcbr== [dR4"2R}IJJheeE=^G0ǵ18X[GUvr k7hLjk۷ooժU0(0lllT*7mټ)&o Qz#III>_aPY٩Z[Ykkwt7*~ yʕ%K<_gҋ{WWWDIMv'7iIqpp8Πm@訍&M2f̘;+t/#{DWWwiGS_z}[[۫ڵ/W(룾]A$ɸL&XZjJ05eʔY,`ϟ/H?9P1 >gŊ}ϘeeCL0e)JNI8tH.cWT Fĉ7:jqqqٹs@}L& %t󏍍o2a$2.o 333n; a~c:uؠZa'ELTw8ri41<> xI`!6STv(*w*G(vO"ȭ[7ϟ(݁`\zY8C X)GryQC.GFI1*0w&jPeHqj>A32@|@ `V`@ F;y_@ |^G[tE @ 2h+@ S`@ i`@ i@ t:ٳg@OW.nbӓf{zz žzgA3rssLٳ 1a^^gX0??Ɔ`cB`TB!:{ʴ,--pyiiZbX,P' y<^WèIi0wӐLQ t:=!!|O>D.Arʂ X,Ommmd[lx</$$}B ǒ.ݝ9Y ,::ᔕq8^ry&* 233ׯ_ /]#WW5k`7WVVcB`TB!*Mdsedd?~\ 򠠠e˖R㒐 v!C` ~n@7sرcϟ?WbI[XXr jZZZ``qI&ZNǿx񢨨H :t}B ɩ='[0 迱z FiҜT*srrRb-;99dk׮͞=JjCjSPkΜ9W^U2VE[[[mllTֿ:^Vg̘AӉ O~L",X`ll;%K0YfݹslBx<ޔ)S._|A33I&XjGGG.wޞd1 \xq֬Y ԩS7o޴ĶJ3f+p= gXd&HP .\.W((* Q i=`CP IDATm۶]~ܼyS.r`3 ^$Ӿ790&9a6駟*JDoN - ;zɔdزL&@es`WG~T}ʞ΁h4+@ h=#G+))%G=rӧ 1===RlAӦM[|VТK,򪬬Ą3f(,,5336*]%7rppphhhUU TP}6KA<-*1A2{C%Bqjn yv} ˗ \̀E3qt\RSSSZZZ^^d2?Lŋ >UYY9o޼aÆ]xqԨQ@.́1 LUQQ`08D}3?^:rTWQ9::&%%%''^rrZ__/1aBB‰',+;;["={baB`TB! H0q.y䔕֖ejj7CQ<,,쫯6z."T*חJQQQ:11'>! ?n:r?dXZZd$piA9FW~w YrΝ;] )!'Lkkڵk|}}R)=EUBCC> MNN@g.;wkÆ zzz,>#A######,PkDo!7mTUUeffVUUy^r!~ٳLR\\ߞiӦa>`ߧƏF(:uBh6oԩSt˿ |r HVVW_|W_}eaa1zh\QTTppL>O>!CVQIxxx~~y~Legs annn'((/ǭ r، 9am۶b >lѢE۷o'Hѡ N_r%YNvܩ~Wϟ?_f >Y>֖FwA -AN]T ?䁵x-@`??n<@zf+n?wQ\)GrT"Q佑#nݺye555999؏~[ `Ns8[[[X~A A |+Ll7d+02@ V`@ V`@ V`@ V`@ @ |kX'ry'r-]mE;'Q{XA :@ ˅K?u[WUHu^V?hjjdٞB3ٳgb¼<>b\]]KKK1a~~ ϧ(ܪL+--:99X+Vb upi04,ᐁ6Dc*BHn:pto84@@02>vXr\)HBBno2"wx###mmmmmcb¤$|/^`B###A166(Ev01***\B]7L̙3333@@qp&|r^.c[e2EMM)+///eggw]Amvlŋn݊ffbz__opƍ.gdd' 5C{9V t:f|w =-w,kݺu 譲! xq}* !k矿򋉉0!=sL[[ٳgAu .n vi3ycƌŶ?ʊbB۷owssسg6kv̙p1SޜիWM"ȑ#}2]X5f t72h"yٟz۷YYYW;vTVVnڴiǎb?߿K(huvv2̇\r͘0***55uĉ555=ƒ%Kٳk׮* \D i0ȹ/QMIIY~=ׯoذfXAaÆ>|oٱcÇ {s4lذoX'N:~HIIQuVGGO>3=2"}M-#G+))%G=rӧ 1===@o)@6 ￿iӦ~K>+h%K`*++13 lmm(ԁw>rÉ v?r`(D~ 97 /_\PPrǏlE`dd]]]SSS}}}K V7 ;;;.KjjjJKK˙LǕ)^x1==R#!8 CsttLJJjkkKNN}m.44^,'&&c„'NdggY,VvvD"9{,Ţ(D 'N ZNNNYYYmmmYYYx3E¾+ uAHHʕ+wyڵn3yw aุ̍@fd|~DDP(|uimm]vm|||XXT*%ӫ DBCC> MNN@-i- 90CŔ¯i&___333KK6gff6{즦>=kL6 [xlٲc.C-P4#F(k@͛7\.7>>׬YNa8n0yd77dFEEu0 2ri0Xþx9f ߿˖-ӧO3f vmVX ϷoNdoooGGǶ7't:}ʕPh4??;wg› oZ.4<~kQn@ AGGGppo_ GCkl!ICr/}kjjrrr  Ns8[[[X~A A͛2Qj5x  (`@ ނ@ x@ D @ D @ D @ D @ D @ D Q |s0wQxD%'O?~Bz(h`DC5t X^^gXA3rssLٳ 1!0V666 &??655yzzlOOOPH!ܪL+--:99X+Vb*=fH4 gsd-[x<N>&&&''Gn=v ?~石l33`D7,Yfy<.9_TT4o<6f]]]:|~(rʕ X,sssZD)B'qs@u ҥK=ruu]ff|Ϟ= 233ׯ_ ڸqcxxxeeeXXXpp0&p8eee'::B43^eCVuܹǏ \lٲ2>A*=yQvȐ F_xQTTTTT$:Dݾn„ +춟=?|֭[cƌz2eeesss/伯oYYYYYO7"?IKK ?n߾=iҤUV!JN]]]|44P8u]o˷FUX,p8ʶh4&IҼ<'''91VNNN2ڵkgƄVVVOʕ+D ,X%K0YfݹslBx<ޔ)S._|A33I&XjGGG.wޞ ~qjn yT/ }ݻ:::VWW )cr2:y-̝;`̛7ﯿ„kh4Zdd$qvvc`0>7n`ٳgs8HrLlmm) ٕa˥(ڵ Eѝ;w=g  J$D6l033#!N:舢hUU%P%//-//oܹv~'3--|„ =W!L1 ݛ:u˗/CGaHJRYYI޽{MLLu_Fj򃻷!_Fj2 %%%vVV?9:a„G)l"ѣGرaffd0(3L !F355511Yb@ PafCCC\1!?vXkkkzz: !F y5Agg͛7˗ݻW& Ϟ=+J &*4---yyy,yyyKFGGK$$i 0]ʍ7Ke`.^t)qByzzAeQtH/H۷l2LH۾FKx///⦎;wL:w866V*&$$XjՅ R?lkk>L&… e2BQbBP1T6L=>|U(`ڵkN}<{ EQOOτT ^[[;sLLҲ܊Nmii͵Db:0 Dvܹ~zecN$YYYaS & As޽b8$$p)2dH$]hQ+77T777LOY+s -ҒWwެY8ݻl6EK5=QC܌^zg8NSS`S<:'h4DhGGqWrgP(vF@)3_*w1ńRN$N`֑vH8LSS 9oT$n|b"Qp\ub>Q%~_~F]~ ~ Š www5uԤ$l&fܸq E'Ngg_yիWJ"T_|E"P(V~F.e;mmmā(:۷)SRdTFjoU` y#G>}а.JFWRRKȱ׃0!F{) FQBߴio%t%K*++13 lmm(Z2o6ZUUU^^(&RPAQmXqEVAdĈq+=G!(`o*WP`f]O:$[{qƐuS]]M ֒`' -c7G-d?%CqΝ;ɓ:f0{ۥa]vȑF PN|Y.e^YY9o޼aÆ]xqԨQx{Cwt4rR?u| ,!!ĉcƌuۃzXhnn bXٳ, :::&%%%''| !H$JHH8qb#L"H Ri\\a,&KϏlhh\` /_K&W! ((FYYYӦMcً/Vv";w888;88O544xxx0LF !!:@-Pl2&-t:ك%O. 9'Nx###LXWWj* ,J7mdjjjjjyfʔwTTQQQ 'p@?{(s D@Gt~;w.433믱***ldŋs8xǏ+ J:RēF8v`Qd*** sEE!UH-4<~k޻@ :ޥ秴^|_\~ʕׯWP[>g)ÇcwAPp/}kjjrrrB ȠNkii섄eń`/ V`dBSV1Њ&ȠV`dȢ1@|37@ @ @ @ @ @ Cd }}}'O?^ !j7TT`L&sم}MQ0,yyy|>bb|acc <==lP( |=p24KKK###\^ZZbVX!{R!y08>eH&'t:lxҚ^ӧS˗͛pW\Y`277񩭭?~u֘1cC'/>>ŋEEEEEEСC]R-+++++Q'pwPKmR}z8cW. m.J򜜜(^>~a{,,b`NNN2ڵkgƄVVVOCݛ:u˗/q!CR:/#5ۿVRV/#5Aƒ]vikkkkk0?&Lѣ. OhKll?#, Ev&I!h&&&+V*lvhh(˵>&ǎkmmMOO'zhQQv؉4-$$7o޴V/_w^L7>{T*-((# hX477V.]-Hߓ"08w)7LMM۟>}{NN533sڵf@ DssKfΜ Uj3vB?7o L(yxx세fNf?J;w\~=El6x*3>|U(;wL:[]jՅ R?lkk ׮]ܿ (ZƸpBLUv̙DŽemm>H$¾{ѽ\\H$]hQ7I4YJ’vssKOOommMMMussÄ] ?2Pb0iii---޽{fp8wfDGcH*f|իvvv>p=_V&ϰ*4M"(A\չ\P(DQT(v{h i|`a@hh˶mۮ_In޼)|~yy9e CNWE67n8::ɜ0aO%e0̘&X|!|S[[[@@3v)'XEE;Ś:ujRR6VPP+X\.y7No (Z^F(ڍ2bDB0::zҥ۷oǖ [jl=T^.iC՛w4\͙L&6{ڊƕ&` 2ߔEemm_#wuU|EQP4 i׮]dbr>\Ma(J+0rw1*$> BQ=77իWhF04 Uj |))) s`2'!!AN0R),J \OM"U)S+XƏD4VRƃ,Hr><2C{1KU?44^,'&&g`XN8=f%ΖH$gϞeX1))-99S)/&N%ڲLMMf(}W… ]H_BIR}}}T ?xB֭#CVQeJJL&KJJR_ ]FUT arʝ;w^v %&&R4#2apɩ 44 B]]d@~Ws9tH$jnnNNNnm>w׆ bcc{xqW@D"رD"144Jqqqx;;;={Th2Ȇ tIfcAŇr` [jl=T* kṔeeeM6f/^fI aQBqcccF&O)BhBe˘L .0?Jq gjjouuu522%ŋdds`UUU=v9F7ӌw9yG}dll>ŋEEEEEEСC&L[B_____߲2eB@3] HrrYYٽ{ܾKLNK*ի{ݳgOEE޽oݺu-X[>^zu#G466!@||ncb¤$|/^`B###A166Ƅzzz>>>B^$--mΝ<K__ʕ+(>} [Iz`_5ht:=%%iƌW^$O>]p!zˋb N733:uj~~~llɓ''OLLL""":(DIYxsxSL0 1[ȉ!!,;vl„ 'NŅt:=""bܸqȿ0w;wƍ1!YfݺuK!e\ti톆;v Ed׮]k׮%(ѱ uttttt 2!OѣGwYj b8((ܜh'Oۿ[dBTWRUU5g&9gΜ*uuuYYYDTWW;;;sHj?gaaf㕵|-[}w}wyyy=wS{N|aAPR޽{(|tC4iFM0ѣG bccGh4333%hooGQdRh4Ɋ+ d-6r߿ |cZ[[Ӊ(ZTT-<=hׯ_tvv޼y []|޽{e2ٳRΎlB8Ғb8ܜ[YtittD"ٿO2ܥxWssKfΜ ٢HDE L`*C٥AFmii͵$+CBBBe(`0U.\ Jg[[[jCdL&v8/\P&X,E,,,oaa! )\ꡰv̙9ҲV'PrʔlyڵAAA yH: gϞa'2 ;v`1QhGGǝ;wNJ1L:O&R466ÃBF={_x[OOτOÐH$N302dֿ2Rܽo%e2Rill,))ٵk6BUeggc˦# h'bcc-ZDfff|<{[[[SSS0aW?eϟ=`4 FZZZKKKjj#&wެY8ݻl6Q= ѣrCჳ_z1Fþ(p=g.h4 .\.;a ngB`(gX)G ٢ʒ3j. h4ZKK `rXLnN?"]!2x(~:F~:v[|D"PtReBʜGQdƌR[7nB)*͛W^Ml޼KzAApH}Σs8HH$p8X-cU3gb|bxpͨk#GG9rӆzzz^^^<4ӧF!TfAӦM[|VТK,.UVVb3fVUUښ"ڵkjo/VUU( J1P}v=w\U1b :::eJ!Bqjn 1[T*;&=S86xG@yw'S]]M ~/k׎9rԨQd!Oe#8|p f0{K2={`.7npWPH.8+Vz33ɓ'rMM[Up+'44^,'&&c„'Ndgg3ob%ٳgY,&tttLJJjkkKNNB! H(h999eeeeeeP-// ꫯp  ՞3T*חJQQQ:11'>! ?n:r?dXZZd$piAe8]X"PP$*G. ˻kgg]######,X ˗/8իl6СC"H,>|* {nuu5~- {ihh ZۻEGGϘ1CD"144JĻˁ#P: |OC`L>=##C&;vl-,,?~1j?nz޽ZVE2s̉jjjjjjruuk( rjn@7}m.++kڴil6{@kwqpp066vppojhh`2B Cd!u4ZBpٲeL&[t #3yl]}"""qr'Nx111ZWWW###\R__pUWUUU\.w޽0 2rύn8aG vrGG刨. TȾ}8:(խZ SWiT*ݴi\͛7cPV6 +**/^p8ŋ?~L(DQ??}4ѐH$ x(C?̝;d}}`eĉׯSinnnff #BQΝ; ,Σ(zq% ٹB_8tq!9***lvDDʤsqqaXӦM;qO׏7nܸqmmmuR * rZtHLC E<<&ljjdٞBB4r*JKK422奥NNN,kŊbQT~n@pQ헤d2ٖ-[x< ioonXEEEc_{oŕ$2f^FE& J`T *.I F0&:DP@ 2Q{|DE@AX^M=E5s=]}K \bJDȈ#:/Ν;W H$ ܿ9f45# `lj~'wP.gee,_|ҥx锔W\͛7GDDbBBP(,** ,"ޢuرVWW+ϟ_TT$vщ Cgs@ǏT@IJJzi~~~~~~uu]>%K,Yhŋ/6%"D5:7o}駄NC W!;CRR=l*H `O=aVVV988\._dɠA/^,YDnd߾}ׯH$!!! 9sLG'b&G #ݞ={\\\C䮱cǞ={P?~>>o^h4o!. gT f1١+//2e Ǜ2eJyy9!St%绍/To:tgw1 Ŏ .6ǒ8ËiˆbTpR)ْzz=c\Z4ATTH$u!dT?zp"## ˗݉?,XeNG6 =rVL.'..577ŵV͛hm֕28w(7dΜ9Z611ђnXӧO"5[hDȲA!X|^㈈eqp[[[srr\]]-[###AhKRB\hǵZǏoь/3ft:>xSS˶m\\\ K]\]]LH?"g}gb;<<|Ŋ|xEEhJ"cu٩Svt"|8;;;wN577D"DϞ=c1b(J+0f NFs#>{d={|G^eqnL5fщ?8>l0n/ B.**" }||LiY$i4RDiy͛wAOdib*t$*,D"qFv5jT^^-]3b%;;6[[[_]7K]̺LT&''w;0QQQ)))NNNy!C-|ѣG5͑#G|>!괴4L"" tnQhjuvvX,&8^RRy?1AUUU!wikxRHII~I&('O|q]uϞ=:.55^$4 `.3ן;w2uKJJ:ۛ:ެO6-66>66v=<...--->>ŋs]JessݻE")'R0ƍ!!!M6544P#CZPPv!J{kkkƎkFj;wdp8_Kh񂃃YDCL=^ b<޽{dK;;;{c?󉉉!NOD,o߾UUUhooO*BɓL.~)//D[lD.!Ԓ1l~nOii@ 04 {A.2]hd;C7+''2uY#t|֭[B98.Zޮ!&ZvժUbX,^x|||hhyu],--={P( g.++3%"D8LJR*|D"F yԩ<O*~}`eĈ˗/5=++I*"rF,Тtq'''''NҀ.PZZb];ůT7P\8=R4Nt.7]4tqcccee'15 ӯ0r{ [(p@`i4PX, T`*0#O1K,a6lX-LČX[}aF݋Ų l߾}}iΜ9`Μ9 xI&]tssse2 ,,,$ļc@ J#""49t}D"H$zcČcZsFB;رc222|xEEej\, m\Ν;7i$oY@3wZ3d/r\JJd;'Or?cFkP"dHYҠq~˗ WՎ;ÇfΝ񼼼_N'11Q,;;; Kcl,IIIYd 'ǔh4|>Axzz>֑D.VI?WрN8ŋ^r)ǏBT*~T*]TTDl8^^^>i$PӉcO'RDe3%eݾqޝ»20 kll,((ذau;@ D޷nd,8zgJ >+111,,...&RA*-ޚŎ .f'r}e2U*ՁHO"Ґb+WzǏ#"":7Z_#CʒɝK 37f͚u boVVVxx8#GhZ\M'..nnnnȺt/pz ._3*0Bsv1Sd"t:]Ɯhjj2gsD&]JKKg͚GJ{&LpppH$b.СC Y8NV`BPT8T*{3FH?"r/ˌ_nQwU`fgg7w|XPP`gg'H0 [redddyy9Q-mll***KɄ咇R.9:3zY}v*0lZ--S'KMMUiii2EEE777899brrrffѣG BGh4G,"E!R}:{hFH?"r/ˌ_nQw]"* ew=l6xhkkXt)q-x0K.MMMmG:Cpp+<=={ۑnfNC}{HQ arOC766VVV8q"::ð~ eIHHU%-0 ;zho,T`L#{>WmS@+[q*0K @`i4PF}JЗx}&h%K/K,ƇNbΡC vv*0; }̙#̙P(z\NNǛ4iҥK177W&BBryyy,"E! c}ۓzaa?_pass3HK t8(4@RdtaX8a:ݽS}wD"DFFzS-Ϝ93}t>xb/^;w@ H$ ,>xذaϟ.'kjjQ;78Mr@ ^r3qbO8A:fGXIJ?X H҈FPJ@ONV;3f.NBBP(,** =mN.gee,_x;aONII).. ꫯqʕ<ؼysDDHi =S;qjR_b󋊊d2َ;XD ~iӦu,.4PGurrrzۋΐ]vjo߾o׮]9rE0 KKK +**yfPPЧ~J4vss;uꔅЗXdɒ%K/^xbQ%򵵵Ç6".[ݻW^2d0?:Ï7!n4=<<~73+ny>|xEEej\, m\Ν;7i$oY@3|l;GL2ٳ4T*U*'>fdÓ'Ozyyq\WWCAi >2,i`溿b:u eΝdԨQ?ӎ;Rȑ#]?>w\uur^LO@ԩSq܉'^xLtR+V2DwQQ]XXҥK7l؀˜mii!_8ƆϏP={6~vT*[TJ>rLYD&&LrLCe,1y\ZVsssNjUd?q.VmFCˉTaÉ ,!vmٲё|wFœf}]-/#=keaXcccAA ͪuFzz=dzEb-^\\LەHWT*%["55!#.\X]]!Of/@%oݺE2l*'LQΚ5͛^CP,_rE?~8""CAi >2,V`555rTZ[[sss|~\\\KKKnn; 44ȑ#ZV.{{{ًɂ l٢h`|2ٌّri777LA3| ٶmFٺu6@@jIӋ-:~V=<@a'Q为Rw]~}d˂TniϘ1C|Ǜ\\\mP(]~ <==kjjQ †X6M,"X|5LOeUUVUUydK3b.n:bEɓwޭR ۉ9sZmbbbXRRVMJJ a {4-Z-[l޽,su,_8T*gΜId2j :pJJOO be HC7o6m) GrښN~y󦗗P(ܸq#yb+V8z(W,&Of͚ .tb>- hG*0g׿FqmggG4ptt$n/&B={ۛ8Dvd63+&H L(oWMMM"Ȕ'Y BRR$MD"DE@kk+8 &0'h0Ii qjp6sLŒc@ jĶFfF ݐ]$v^e65 Q?~[Ϻ??TZ&z\TTT}}}sssJJ!&''gff=ztȐ!dK>QFs>-RD"P*#F^j:;;[,p/))ټynJAٕ[###mll>ן;wi@\2f{._ ۙ4iڵkΝK|s]d2YLLBx_i)))=ikxf] i۞={t:]jj{<1KYY޽{u:]FF)z{{si\RRҮFckkjwҔ!&ӦMmhh>}:aϟ?KKKرc!!!~mbbA144@7447zYYرc۵iӦs{ݾ}6Rdү_???vC\fXXXH"EHCvZh}\]]Iߟ/\E)H:W d )2 H,?~ذaeMoNwI$DՍFYp]xqܹ@",X>lذwTt%?C@ \u lʕ<ؼysDDe| BH(&$$9\URR|r>}:%%800𫯾"Dd"E!w{v1{;v,##դbŊd;v䀑3fp_Ei )2 H,丹IIIO>ϯ޵k{ݼy3((O?%t77SN}%K,Yhŋ/:RAZ`NfooaaS%K 4hr%%%6NdРA ,x)!")" u#[~D"!<$(-- i7N:ܲ:=N_H!p&,'N0.))I*=:///11_~!x"$${{{߸q3frcǞ={Ν;%ɨQ~g0M0yч~X<17o΁577XɉzV|ʔ)<oʔ)SN \r%)nݺU$}>Ύz^ܜs"(66.!!Wիa؆ YZ"2aSaa Xykڮ[(.av֭dR9t萏o5[oECu{0M͈F]租~xLCHxpppr]wn ՛Yn ?R\\,J9T*-..*\.W8y<,r83|pfXˆmdޚŎ .f'@ D޷n"DL~JuH~L{(\?~щ 4@RL-ké ښZZZrss݉Gjr^L,XeNG ˗ɎfӧOxe rɍm۶i4[Ο?a3+VIIIZ611188t_*"2)))$$:H[[GM*f9s dKH-ZtqV{bSS˶m\\\ KKd@nݚxRR֭[Y#gϘ1C|S.)JwwggMM !t:@n<3"0s\8X|5LOeUUVUUuȌR}]-/#=keaXcccAA +T`AAAPTAAALGpW*3gΤYYY2\*d;T2T[[yiӦrX^\.w߾}~~~xM///PqF5 -[w^Sn@z5y5k\pAhi@@ >2Pp8Ǐ׿EU4 mmmm;;;#yP&v{1 MMMTٳԑ  sSSH$2I`V`C%IBRq\T B1@OulI:)'L@ nvzSSt H$B… 8l ȝ;wOG}tȊp'+0Kaaak׮E`0ן;w+C:4 `.3=zBv&MvڹsRߍXd111 ɓ'_uzxxPjjYu+ &nnn{t...eeeT% `ӦMl̘1:ncƌa3##Cgdd7P4Vݹs'KJJqz~pۛM? Ȁƍ !6mZlllCCC}}}llMTC+zaǎgcǶk9#Sk{1O E& hwg BvR*ͻwD.ֽ{` yu______>>HCCCpp0 nllis@ ={vEE!0&)b{s^ b<޽{dK;;;Ex@@@/:thܸq^^^999LSK )2 ,#v/GWUUR]__* O<gBUUU``=&33S"۷"^1A`n<|pԩ\.?4ITET~7x\.7 b#::Z L8Rizz:ϭ[ v=/--111.!]haJL<>>>44%2 8_z\zVj*X,W^M\DD:u*ǓJ_~%yX||>><oҤI.]"\L 1//ӓzzzbSSӜ9s9s 4|< ^SjooOEG1%IGh$4@RsS+f3gO󝜜/^L<->c@ J#""4 a:モH$$22x:8Rdh4Κ5@Hɓ'K.>|8ϟ>}ٳgb4Y۴iv4t@/fwvuUOĪVF^.e_/[WaȾT`+V?~QQL&۱cG8=PnryVVVII˗.]JONII).. ꫯqʕ<ؼysDD!&$$¢"P"" a#3رc&u#Er3ft8"4PG/}}7߿vȑ#-ZD5eٲe>>>w޽z!CMJJzi~~~~~~uu]L,$''ƢER JKK7lpNDӘ?M6m лF`hkkkkzNQ*Jjmmmiii1} ՛Yn hr* qJ^dzw/Z677ߟ777 Bb_.tsM4>|xEE4d4i͐LrY\tL09^Q>ᤥ;Ύ`̘1gΜ!ӧ;88syܹsy<יV]8Ν;%ɨQ~;vHґ#G&TTTD-[t%iҎ9O4I(X5uN}RqGUc?  6l`M^ىDSGy?u/vvv 22￧ڻwɓ~ٲe|>믿NNN&'Ocє!D2tO>_utt}6!"%\2xQF 9_~߰aYյvڠM60`nݺe˖𶶶7nnܸsԩׯKwIRC写]vڴiŃ ʌzǏ)\V^O,Mf/ѣ߿o4??(^6Ͼƶm#GxqT*999۷OV+ݻwSrtt0l̙Æ #.b"E$˖-2d$""" O>qㆩfv L>|w!ٳ禆5ϽcFDDWSS;xzz N>; ***bbb/_N6wNknn&wD"H$z fSFVX1ӀdԐUȗqrϞ=}Qxx80}v#""6l ֬YE3J }U$#Inn. I. X̜;wG}r1z1iz\4k֬ӧ8n4ccc9֭[=<=ztգGJESiIȑUH&+))ټyޣ^"뛛SRR199933ѣԓ|>ѣȑ#|>RSSjuZZL&cJerr#:9vvZd3#Ǐr`РAήkkxRHII~-H&('O|q]uϞ=:.55^$4 `.3ݔH@ԋ4O>vVU(9D ΝаuSN}Й?v"1QRRaUHHHJJ̙3wܹVVVi4[[[VsNz>##cܸq,w\\\kkk.|ξ2ٚk?44T(y'>r]C7+''\VdzH|>XF>_ZZJ3Jjӯaiii!6F|ZSL!#jWZ%bի+hH4I"߁<oذas%Y)v.t4C>>>>={X[[;l0^VVT*MOOfitt@ 8q"ESbmmEB!mF}*{Sw7+ .0l޼yOԴ{'Q=n(롸p{Jb`P_Glf`0h4ڲgΜa)C2sCcccee'1x*@`mm=o^9xaT"^s7g}u@beeV `h#n5@߯ߍkW{O`oVt~UHԩ!8~0u*s/0p<=\=c0 llllmm=80+0>; \4PX}`*+#?ӣo+_\s+R{++~M5G& '/:m,=n Awg9~Σz z"׬ciV]|@V >*pGO7͚OF? 8n4M0`́˻t⌙3"wp0 < Ҁ#;iuw4+ƺ 8p ΁As HH+oIDATkmKv3nUQN8~o >0/MF`Lcn@Cky뭷rNÿߵ0T8~\H,u2 ZA;K@fsz7&0ߚ:u9o^HOXDT`m6VKlo\q?8kk8 œ%C-~0 klli޼y3gΠH;W!1 EvQӾ d0w! Afsz4x?MMM==<'cC_:DV`o8G+t:V.$`dL'2= |^ϯ:rȸmX_inn<`?B߅QL]ԪG@ȻV͵mQi V6Vm75jocmcm0~\;ۼkAڹQRoԪp 1Ì8~uhčįyᘢU׷T}=(VM6ozsUZNoԷ Vm_ðMJg6V[Xkt psKA*.=b-6}Q~Vޟ900Y֟[[[m*m[լַj-@|GŐ7yP?k+Тn0ðM[M5 `_?u;r:-CԈ`3Ǜkcma'|+k/;Ҋ>#Bt/>=B9kpϊ5Y]Cs'#M:F~)_uv"6pȠyo 6j嗋mc)__D:̠V}}aUa؃3uB1p@kkk~]'yo555ovmC7ׯ5|Z.^LyOMIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-select-b-1.png000066400000000000000000001640101351617527000240410ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx3SHI $Ԅ^# "bA@xJQ"']A)(]%"D)7,@He10,gyxxvϞ93w2JE@ 0Lsf>ol퐠JeO/5 jHoߞ!dgyJ !RJ:Шㄪرcwr5^h4u8JlP" ҍ# G]& ;et׋iT*0\TC+(%RJ(^uFabZ_(sbARzVKNph0(RaQq q'(>RjV Tl)VhlVT!XX(%T鶍+yY(RJEQBTjZ3p$JeV%_ 7^^H- jRN'mRy20}QܶPl͝;eYxEyjaJh&M$R,`0V‹TH)'Pj^'LB .W/3f{@C2< D曶=_Ff@z@ڠA,2lVsR*h_\$k•+Wzv9s\v-((HªUf.,5Q(٬i9?JPȁ?My"Xey"{&?2U(<תF|T7.ػu8'e{hqOod[&mJK jㆥS,)--(44ZjAl(b4=T۶m[nnnzzzF[J:Sh6KB/PNHY^9[VˋNQB20l;YpHH}bhQ]Wl$f"X^>)T@ %Es؇gz'Srͼef=/;=++a`I˶lrxU>ZlyjҤŋOBBBD*T)k5q[];HEJj1ZDJr}3*֡qH:;r#4Q3H0\۞7sx1:J\Y(3}ƑB6%]I/tnCׄG;4^=PLoz*xVBJ-7Du6!7 z y<9q p%>!Y9(@9CQQ9־Q95va=~lBHVXZR~p:Tv%-Ҳ^NZ|جSJxsd^ߡGBQAxllu|.˾w͌Oݽ #k|jZSn: ]GIm?kNMFĵظRA  V:7_,Vzzξb'w.n6!}:lIT(R1%l!ӿ=Z9CQmܼJv}3325N&˯^ȕsG222*ɓ'>칦{,e0BKcIZxDfY y6:0Jhu-:iK5Q-?Z!IZcllїS?o\>ӪaNSTg>b/;WMi} t;N6a󫡡AAA~~~@eb6m6ϻd4^ׯ_֭[[nٳg֬Y~L:غKaRZ ]hu~Z(nt&jF;\U5n!JJy4M@@VliVy_Er'///OrZ._5 R d ߹sl6s2+jM(,U=Wktj$kݬ#U8Z*cew*ӯ6ﭸ/KPRѹP;X:(OE0*3Nhtf7D*,˲wosjET*_ZS_:R5jT/((>pQX?@"ϲbylu_J_x|577yj}o@Y,~ l|ԥ??>{WëUĉO(:?BHVf? %^SRg`84s$@EҐ(|B֬q;~&qzw>;dɓ&O>|y"͈ڑrRRR~mghhhI'(ǭ_!::y- -zw7lX[S^fu322,\8rȲ;)jHz.xgӦJ)(1L6<}߾͉H~*]>x? GԎTՍ7^0KG]r^7vl#휗 |Qv-\ۯ}jGF)ӝOٳ'Q2gs0m<3 'M<޼uYm͚_g: BKcofr3톷CMPEF1LF111q֬YJU"dU @i8 4 x5+q yV^w:0g` @ FhCrBJ+ɪ{P=փJYkY !9Yز%PIlUЌYHQx߅*$򻟁9߆"4,<4,fz8qC+KشiSft:]ll]͛7lRwR-[5jhbbbl"`0d2y(tcex.;[`0ؗ,Y~*Jne$.,2?Э{ڑQ:<=WVu,QIhXDD aY>Ǻy6mA.lV?!m I*R>ex,ڿhX޽{]*ㄆGԎ٫~sY!2N^V^/YӥXn<7TiÉege^=j'yVQycǎiӦ R͛7md2 Kʒ%K ?x?lҤINNN&M>C.;"{\SBǿÇ%k׮/A[s6=G"پ}9sϟwxu:yxrKTA6Q !(Ο"vV-nuc]|nj7c #J 2nٲ!ܼyBvVfvVkwygq.+^e˖^swtǫ2L BuѣGܽOU~#jGwuP>1M5OJJ_>B222^;2cGZly6mk>YFVUVڍ7B 4 L?yd)Bw.;*N_M6egg5?yjӌH\YL/Y:.;t ,]D*dg!$&vԹVDGڭ]Nn(t% t?nJqV^o˵j;\)~_o$fB~"d#!)׊Ǐ>Ǻy~CH^جw_ >er,X,Zf.u:]Ν.\uRM|߀]C;^% 30e׭[#=:޽fڳgoOuܹg~'ǤO6=|Pٳߜ8Q^*%%e=Y3j  ^{m/-^g[nԨQz_\ō7H Bw<ڵkα9wjFtҺu^~]yܹ={4kȑ#"q"EF"ҵk~CLŋH}O'N= n(t7ů 1);+33˧M2HBHZZayaC$n$;!򉝢>fӦMټj*fya; i d4!ܫS77l6רQCsF]t)..NsuY#b fY!BNgS111gϞ7!!!RS.#q"ΑfOϻ{v( y;ZZQu:o)Uq玝?n^z+V,'EEy]U: }|͛7hڬ%?֫˲4tvaׯ{oadP{ IDAT{?lA;g(N"{MEH*!+{.kʶo>scǎUZ#,ZHH(!]Qw-[c۷[i+wxT*3z(fQ7opi۷wR&_.AWe/0 cf(аpd4%E.]Xac/-+-???|dzrss^}lI{'L4)333//o޼ym۶ gϞp#Ĭ^bZJ>:p E 0Cˎ$s̉uϹSjAfsBBB˖-=DrEDƄ o5c]5xM<**j$nLFeG3vݿR\c*kѢy͛7/dY6 e9|aٸ?g}V?9{%xB6Ǻ)5uSyo?tGjB \ܲ>m^x^=jtKVuu\gƲѣǦW_J@RT*Og!+J~b3Ҹ'>*f=]l/`J+_dz(2_ J*ܘtt>X*> c–P04d`JJپ5{ׁJskT"0ǿ0NK 8 4d`JC4@iP}I0%T_%`ƹR0 -M8S^-Ȑ@#'CQ%IH:0xHsc ̚5z[l9sf5![n*O7m%j? UVbbbZ IJ7.]:rH_N}Z*ܲeKF4MLL̖-[œ} }L&.;b eu %Kԯ__RɭeM~|=G{#X,#NXI+ ++[HPFl6oڴ)44T*>|ٳg ͛׸qc088Z۷o 'L0v±cǾ ]vDN)ǎҥѣ5k4kٳ(ʅ.#qYR*]Y{Cx0.{qI1e/sLӧO*JR!j۴iP'=nӦ?lٶo+6lҥKҋ/FGG{(tQ#޺u]vׯ_0`\C۶msXe$.kRd`TG/&zCCCϞ=̙3G!=>sL !իWkjZe),t:.;" 4h4:ܩj}:D)mٲ\`0L<߿iӦ˚E!>Ƚs} ׁCR7qqٗ'$$lڴ>N0a֬Y3f?~|t$\rE#Fp^ĹӱcǾ]t!5A?~1c|\΁1Rv95كk?:a?^D׎+BH *bC}|a0m_3Ud4g͚EpGV!P Ґ( pGVa @i @i @i @i @i @i @i @i @i @i,glO+A7C]q.\T.C*@W?U%cҐ( Ґ( JmذUVzQFV aFѴmȑ#rO !999}5 }5L  *Ae˖ӧ/[`ǎ_?I&999M4K5@UV lܹ˖-ѣNo_/^,fkIIISL!A7n(~UCY/rjΥ3_qY/''ϱ !͛Xk׮R!ԾSBN+,,jl6+Bm_E $4qzYOv ^#%Yގ|'k3Ud4g͚EJ4֦M|Rٳg)= qXN:׮]#:uted`SN;v9KIIyW$??#J06l0:f̘˗/ׯ_*/]^$oʕPHpy,( ::z߾}ݻw2eJPP^QF=zhBS͚5=4 |c7ZjH@ys%>@ t;v<!>>>00I( jVرcRR ѣAc{СC by^HI!o߾6l5j^2p;! }H߅jZaWIKK{gڶmۼy%Kt7|sjժ@8yt2qiӦ-Y$??ĉɓ'YvtC`'?~#<ҧOإKVV-66699bx;@! |>hРA 6 !L>}Ȑ!?СC d`tLQe|Aڭw C%k:W@v~FPJA\Ys"-=)j gPJEQtNX8*eee֩[WJij!w b | LYNVVsy_F# Ec_DBoG b%OIȏc_]ȳZry d`hqNG˽&(DDiYmeW''ݻw?cǍy᳙eHF#Ə?xO[#ATtYwVsPAnMNN7~Cr*p!Jo׮]BBB)6Ne_nAUc- ?!L۷'9rȐg]g! ҹarrrp-eo|}~3lذ],?\xEۼys[n ?AY໐>>?INNnݺysɃXuS*/d7SBw>H.p߅)(̄RU  v PYP.$O)Y˫@ֱs}{ԫ_u6ju*%F5O-ȿ| =e`={q~EB32ٵ[=w?W3:~2Q9r1xXK2 hzY;U lC3rRA(Rʮj|2<BKsL]|7@F]^}B\8 J( ᡡPfX\>~XFmA4vmw/5lѪIӦrE۷aoRhFM5ɵjRkr0~^9@Q5kرsmq膪N cٴ/.20 tJvO}uHwQ+eYeB9rk8*:u8W5r$K (^fܻwwhxxp*M Ʋz] tgR$juF*/?#Z.=wڣFDJEUReX/|ԭWVDm?87q*M bz5k*ЗݿEթRh'|4NuS>t67aa&0(PJDA?E[ԉc/hj\'''RF o)zG࡛eٳg5W#K2SS5͉*״hّgQv( m#^yԩǮgrto>gΜ]3^惇z1HII KJJ4h)7&.\pBlll+}Vyv%SU[n=hoGۋ:ѣRRRinU6!\t{u֛9аp0##c#:v| H"%HE (2ƍÇun]nTn04,|ٲ۴ IIMM8hPZxB_0:iIIIR|dyt6htD#vRMɏC,XӨY۷o7o~LM6_ ŌJBqFFӴiÇk4ŋVMF|rZV/_>rHNaYwe4{IIIҥ_׮]^*j4YfFFF&&&J%F#/(=_mwSJ7nؼysPA*rXw\9̙ש[GϞW\t>6)a5^<eOBԭMi׾~%1cε"j>nu7NPf`,ˮ[>&!233nX|ڴiR;'\?Էj4^3{'V\B<_=r￿{ 8!!$%%e=YNܩϝ;{fN}`Ν;׵kSkƫ={b˱R/gc޼yM^Ҩ\z9=ztbbbNNN 88Wr&MR&M_K'LЭ[|yp&LOg„ rMFg}[oIO9lk֬|8_ ǷjݦKI4ƲW+*) 9rsΥkᡷo߾5k6l0 SLiР\ !C9ҹ~;%%饗z뭎;>+W,4\ޯ`VbydvVZ˗/ov{MGЪeK)QFN~ťP y27'.Ycz,[AC-RJϟ;Qn1w{j){aaa8p/mlwc(79tilٲÇ{]AV[V QB8I5mܸqժo V+愄K5(~E80,k[H%,ka>ʅ1c,[O,FDv9^/|G|_'jTA$%96ZW^-7ܸqӦMߵm+4{֬F;?;o7Xb+HY `Yv'ȅ;v\beX!>3?c9Z,fw1bȃl6)Ap9ud&ԣGqwh|7.],_Ʋ_.[|2**j$KJi8^SPwĉC-߫㰒6mӦMON?aӦM6ò{n-_k.K~aZ?|In]FF96sLbw ƛ*z̘1g_J4 -odCk.%|d\C _ !xkִm{72:>f`}/u!!r}2'cb'J.XpΝZ?Ssٚ|+'TEQ/ufh6^<0Ry?IÆѭ۴]n]k]v׫W͝;ag|[j]{R̂_~%..d2eee5iҤ•qo5 ޺u3gڴiۺu3glݺ588òj…YZT`;wF?{g5Z&!YBqiXDQqAd XDڪRĝo-łT% K c~w-,3s{=sfrrg23ß966!z>/63eWM:O?=?W3S7Aŋt'O1L63(pZ>"D AS=M7S"RxW~6DC6ӍzoWڊ H{qF[شڱÜ<~F>B&=sw~}kmk,& /X s{=e}jkl ^7:;uEԔao6T"vgط{(N*?B466DDD =&hiiq892(bo%3dYUeLIk[g {ҠF"7`NBa rr.` j TWy`!C---kۙTaÆDg UzZ o(#Mg0 Z[[;ۃ(?oU! r>~Jo2yw(W*^:R } ?GC%MWEto } >9ߐ@uƤN>rH@ Dر>{ԘЀR}Qnjl44 }Λqgt/TWIL3TWOݐ&*mg^' ƙirIEƆ;wn;HRܢn#f<Ɔn(vvMTWy` :;;W>'Aq>iW^ (L 0TڀG$N]l9y:y̙ O8 yWwGe5;֧Ÿ:Iyyio8kK~j9Q3>vr*R}V3IӘK(Snd20&ɶ۳2[}2h(~߇TU>hzɓ'~?ө {))ɖϞ=۷oҥKMbT*MO?p.y\X-;b$hX+Zdᤤ#Fܽ{7.>S c7w.>FWWb%%EÇJ[W;b}FFE52f؜<g7=ebn ^ nggw.;AwG$ZywHS3^~~v1rΝc|0P? >7e4޷1V]]ǎ3gJ!:]]]kkĄTB w>݈b¿jk?2wGqp靝&Db Fƌׯ_c_6f0t|P4EG-TWW}CGSMM}@ra}H5vmۿ:u*Ԙ@EN9­[\.P?%QXY}dhhxy}==.p1@jLML6dLǍd]q2 iwzu^NJZjUݫEZ 4=oDQ4pMMn߾-ɞ1(9:A\]Q10;&L訝Guǎ1Ev8n6 _ػ7? ..~ҤIɇ'26M+ ~`7w|0~icȄT޾!$$ܼ3C W1}`k1QI ˥tttxn09$q@zΟ2OQ9ډ\މ"w()? n$ l ^<w8rg=w`sH bIGrT.G%%J~&8qNasC!8 j0aU`@ XA h l~@ dɧ{ lђ/@ Уux&>@ O@ @ `gϞ\>M\:9&///6% {]A͘upp`2f*,,Ąyyy|>bb|[[[akk Q *JOO222奥,kbB(x] &0z8MC2ED*B[ UV=}\ʕ+gX=S&m޼x xxp<{**SVVpbbbzٳg(((,//_n?&tRRRңGV^ 7lQYY Q ƫl6Ν;qq@˃.]ZVV8@KBCC,XЅ]i;̉ϣG>\A%maa!%$&&AAA۷'Nrʞ;ŋ"@p ' vmCɓ'(VWWؠIsR4//YA.9\PP ɮ]6k,L*E 9Lf@ٳg_zUA`0Z[[QmmmUiT.U+?8jJJt:iӮ\I󍍍qg^|xb&9s;wUh4Z||<ǛqkP]]rӓ"`C/Μ9`XYY:u͛VT:}'O(4S9alrylkNNNhh(RHѡ FKJJWD"aX=ܻwoʔ)/_?z}YY\ZZ>!Rb穋w_/ F.8?b)AKJJvܩA]=Acccף7:fCCC;MGquuŖ@vZ|Uexƍ/T^ B?W`B.{̙g'-..ן|>R+njlo>777&: 'kkkMLLYpi\ل@z^D90ގh{{;R+hH$[p!Q_~;v5--Jjnƌ IDATy)s -Ғ ݻ7sLk.6h``#GAC܌^zg8NSS|Ba 뜬B$ e\ BEBa3J! 0sWscuׯc7o:::vvvr>_^^l0uuu|>իW8cW``#q㆓S{{;>d2Ǐf?SCH@@mۈ]ᴵ`Ua22 [drHl,O|_S90}e4bĈ%%%ȑ#>}!&7UP !2z7o]YAN/^X__ۻN>ܜڨH$v횇G;!!!aaaUUU+&RPAQ,q [o!C\F{u4P0]͍!Ϯ]>pbcc144|rAA5556L jboor;KjjjJKK˙LǕ)^xرc >UYY9waÆ]xqȑ@.́1 LUQQ`08D}3?^:rTWQ999%''^rrV__/,,,0abb',+;;["={baB`TB! H8a„.y윕֖eff7CQ<<<6zGGG."T*חJѸ&))'>) ?vZr?dXYYddpiA9FW~w YbŎ;] CBB㓒(!'Lkk5kR)=EU: KII@g.;w{qqqzzz,>#A޼y󢢢룢WkDo!7nXUUenn^UUiӦ^r!~Y&O\\\ߞo߾ʩSb>`ߧMMM;F(2eBh6m?:u 9N766^l_|On^}||oꫯ,--G ###8o߾iӦyxx|'~**Ϸ݁il~n!L4ݝ帍>^!'֭[/_g-\p۶m):T+V i4;*((W'z>h4.d ȩJAㇿܿ@ H_ߍA Hx}ާN9*;\Jdr9J?[nvicccMMMNNV"2_dPJ@j>| y,  A`@8*$@ @ @ @ @ @ &#։\މ"}[WENI=V`~NG@ drOUqRlP(ksL&s֬Y0//X,77RLokk`0lmm) *JOO222奥,kbB87`30 a8d M=t:=11bժUO>D.{xxV`C>̖L6yXlY4ϙ x) L111ucK.%%%=zm՘pÆ !!!@C?EٮQuܹǏ \tҲ2>0 Pq`9 of=s!{\.x [ yȝ~~~zzz}m.!!H[[[[[{ر099-[ŋA---A) z;vx ĉ4B?Uq}Ng١}¦#Gb}]`` Zv-~nzl@C<oܸq_|DZ篿jbbLrϜ9vYwP:pt 444::… b L(䆍͘1crssϟfXjжm+**vލچ9s8333""o7g&fժUgqq1yH$:|G}LW,FGG3D/ ZHy={Zj۶mDyVVVnn.Ur7nܾ}{$Ŧ]GAd>|pŊ6m„iii&L֦P0/^{;wVUUi"PH`q(@΍\hjjù\~}l6{ 6СC|:4l؛aÆ7bM0aʔ)?Cjj2-[899}'&5!A9h4ljA h41bII .9rÇO>mhhI߿奧5zK1!AFq~K>+hŋc*++1 )ԁv:|0É v?r`(D~ 97lll /_\PPrMMM̀(kjjoi*Faoor;KjjjJKK˙LǕ)^xرc 902Srrr[[[JJ ksaaab8))&&&8q";;8b%ٳgY,@!H$JLL0aBիW{xxPL4f2g Leg` ð/憍>^ΪBۼyiƌtu˗;::"m۶3ɩ͉9N_b4ǎț * zrT[!d@P[>gɺÇPܽ}KƚG?C*$yC_dP FԼEG ^Ƃ@  XA ! ^@ 4@ 4@ 4@ 4@ 4@ 4@ ]޴Qɓ'MMMC@#P|>rss+--ՌO݀1d2g͚UXX Ϸe0ˋf{yy B !=VeZVVVFFFٙb-_\,ST 5C0P8`D&m޼xv999 Ow롰~P?fH$eoܸxb6-[tsl6V\\#=C+W̟?bYXX"JN:\ၛ TT`.]JJJz葛ի5S7x&XPPY^^n:LՆ """*++CBB0aLL )++p8111B!*Mdsedd?~\ KPTC(` d0ŋ"@pAuuuǏ]a000Çn3f ~ԓIII (++w_~%|}}}}}n HzzzPPq'\Q2v%&&@u©'[ b1Qu@4iN*9;;+ȉrvv.((d׮]5k&~ 666B!5 5{W* Fkk+-{pl5%%et:`ڴiW\$`Ƹݗ/_.^dΜ9Ν;d+d&O|O8TWW;99q={d4sWscȣrlsϞ=\.ɩ(WHǏAeQtHClfΜ9 cܹ&$m_CѢ8 'ܸqVWWϚ5DEEcbggW\\La޾ [.--upp@QΝ(ر# hx'p@?{(d0mmmP"`GH$Z~9O \p)'''EGFF-ꮮyyymmmmmmyyysQ;(;9ǏuH외 fb0v`Pwޔ)S^|I: C TVV2 WWJpcbb}׿2Rܽo%e2Rill,))ٹsX\\~` X4vB?zHa1V=Ž sss%hooGQdRh4 d-6r߿ |ѣG[[[;{l'xHBCC_~MlyMkkkluٲe{dxgJ҂{{{ FۿKKK^^ڿsss^^neɒ%111d߾}=(bsrM@R',YBܤr^^^dPu+0oo}I${.]󶯡h R4!!ۛΝ;SLJ(V\yT/١Oq%Ƶ`LbPmjjܷoP(Tp md? gjmm رf͚`S1Ϟ=CQ+11Q*Θ1S%Gl?AZZZrssP ;v[N؁DԀ§2Cd<==yzzܳgX, E5\eff|r 44Y(*.\H*رciii@CuuuS0 Z #==%-- p޽3gr8]vljz4_z=6ϊq8&b&&&0yuNVhEю2rP2 RgXQT6b. H$B #\q8̙&r54 H$(((㏕D$r%|(J$~:F~:1g,kʔ)L̸qNοkӦMVBDlٲD"P5C!> FkiiAU\ԕwE]\\Q;0t۶mSP01ĩ2&ުTȑ#>}a]1߿cu}///===oo`BSAF*3 ѣ7noug-:xb}}}ooJL8}ª*;;;sss=;#/UUU( TP}v=w\Uy뭷!.VzCF!P8w57T^١̺tHvÆ AGG: %O  [n[~"J qƝ;wɓ'%%%t:Q{`0v?wiEEEk֬1bȑ#ռ_e#K.lWVVΝ;wذa/^9r$^a]xرci)3~}]tXbb'nj/aaab8))cb%ٳgY,&trrJNNnkkKIIB! H8a„.y윕֖eff7CQ<<>>%%% حXZZ?~ѣxK:>xH$Ňr1!w5Qr9ooQC!t @u! ӦMȐdG6mZM(B*\%^'PP\ Nf-ZE,Vwqtt466vttojhhd2B Cd!u4ZBpҥL&[tSYKr]@Is'NxfffZ777###\R__p/^H6AVQy'w@.#Yc.*>;񫪪0!0\;*C“'O;kjj0!9of?<&iii'؝l6{޽FFFnʕGX*nܸlӦMؕ)hE}||z=!O0~PfϞDAAA<?0g&inn_c73UTT8;;l;-p8gѢE?V;:5.t'Dss3pݣP'TTT0  eCZXV)hxדנw-@ t:KOiooOOOͽ|ryտt5&rʕ+QQQׯ_; }MW;5}Iu[_666DDD 2=@ A Nbى@ o_ A Ȑ|ZW NcM z=&Aܕ ȐE30c tfn@ M+0@ M+0@ M+0@ M+0@ M+0@ M+0D)@.'O455U80OIN'>>Vz):Tpsr8|+Wϟb,,,|}}kkk)_LMM؜ǰ)?~石lssD"j42l<ㅆvuEEEsel6ͭO@.{xx̀-էc:F 33|ݺu&]}uҥKIII=rss[z5&ܰaCDDDeeexxxHH&p8eee'&&B,WuܹǏ \tҲ2> Pz >0һ=z B,i \.GTpofÆ eeex?'N\r%K3f ƏO- tppx[ƌb=/x /^(*****쒺_YYYYY>D]]]jCñPr8u]o6BpTL: {ea[vvv.((d׮]5k&~ 666B!5If{ٳg_zUA`& IDAT0Z[[Qmmm%n666U{8jJJt:iӮ\I󍍍q/_\x1ɜ9s;wV*4->>M<0778q?5vrrr{IF0]͍!ŋgΜ`0N:uM{{NlT*>}'O) 04-..lҤI()9i)d젆F%%%V"X,W̙GѾ?Fھ|RQ&d0mmm!5`0|ɍ7+..Ƅ"h>äheeeriiC]]]̙OP{LK\4lP'THKypᅰ>HKAdΝڪ+0̏?zK<(-?zKnsss%hooGQdRh4 d-6r߿ |ѣG[[[;FEѢ"ri4Zhhׯ :;;o޼imm.[lϞ=2 osYTZPP`ooO6AVhoiicXonní,Y$&&F"۷'EL`qRn ?}4$$EQlkff5k̀KK.͘1"gF7oDIP(T qfL2f畕 wرn:l6Uf(tuu=tPkkP(%8wܙ2e r .H_~Y&88 QT&q2Le@mm3 ++Ze}wyHdmm}9 4lP{ EQHpn OhC%33 ~رִ4wwwLOYy)s -Ғ ݻ7sLk.6MT 0 2rύn8arɏ>x̙}ٲe*)L`S8xQyС/R:Tdffb<<<h$(.DGGS{LtgS"33<-- o_WWrJCt^$xgɺÇPܽ}KƚAtX~ yi Hvvv?/;A:XA $ @@ CXoA |37@ @ @ @ @ @  B W<wɓ'MMMUГzwA l6] =]"77dΚ5|VZZ mmm m~~>&ljjb^^^BB4D'tUVzz./--uvvfX˗/dugw;= i >0վN*C2qN`d7ox</44}lllNNc͝;fl77beB@!L0Kqŋl6[lAK*QO_~ iN<(mDrÆ RߌyɾOw0uItRRRңGV^ 7lQYY cbb8NYYÉ !7SuܹǏ \tҲ2> = $4@@Y܀u1}MBB‹/Ս?~~~~~~eeeeeeʄ@?g{嗘m:T6իWٳ{߽{WY477ߺu֭[b8>>|PQzj˖-nll-C###cǎń<OOOoٲe/^FFFhii!bll |}} (@CHzz;x<!FEE+WQ=}7ړj_tzjj%1|ӯ^I>}`Myꕷ7Ų?d:`nn>eʔ8 I&OX'O8;;DFFvuP4&O?`b oCCCCH=X8z'L tzddq\\\Ya8>gΜ[[ 6`B<3gμu2CʸtҶm o# Ν;׬YCt EQc=hiiiii0 eB@!ΣGܹrJAbqpp:0t'Otppx%ɨQp8":jL&sUUU*\\\+vqqrQQQ~&&&ݻRWWf'$$(ky͛7{ަM{ vmCRǽ{P耇FiF?~G6`ˏ=277hxKގh{{;ɤ h4333˗  VZl6;,,߿G;v EEErӃ!F }5Agg͛7e˖ٳG& |||Ξ=+J &*4m---yyy,kyyy%KH$}$i >0]ʍ7l577_tiƌ- DT桂?Ԏ]h\+++|Ϟ=b844(T։jII \… R_~6Ddb d2BQr߾}B¥ kkkg̘jkk ~8+VHMMŖ׬Yð}&0aܾ};e(vttܹsgʔ)T`(限 J<==)i4ڳg|ŋJLL> 9D"Q$J8(*ɘLf:/#-ۿVRV/#-Aƒ;wb+TXpppvv6lffF1z"(..n…Daff&dzرciii@CuuuS0 Z #==%-- ޻wo̙g׮]l6xl܌^zؘFaDQp8MMMLLL Uh4vuttqu.Ba3J! 03#lQVQheCcd4p0X,&7V։jgg'x|îAQX\~F]~]W-\lفD"P(Yd2!OeΣ(ZRR2}tT7NDEοkӦMV" kjj6mL?V8G>@QP9H$BQT$q8 uܾ}n} *3gY>X|iCCCLr}///===oo`BSAF*3 ѣ7noug-:xbBXee%&>}zaaaUU9+]ၭzz;rHHHXXXUUUyy9xE`:`C}YAz A2nC!8w57-*G͞)#ѩ&]MC ?SUTTjkk}GQQњ5kF1r@N|2 >|8uikk3ݻwD!ܽ{7P}Æ gZzҁ|_~177:-O!D]ጂsVVV[[[VV E ^p[yz);cJR}}}T mll~Bk׮%CVQUjjL&KNNV_ ]FU%r_5E"Q2y4`A__{ϛ7/**>**j|r)))ѯ^^f<(bC\2!OAwVWW"{u`|||JJJ8@Ό H]]]LLӕK$CCCTJ8" uy|4ƴi222d2ѣGMFJaG300p˖-設oU$3{覦h777ꞻ*沲Nf-ZT]] qGGGcccGGGFOOO&H!0DRG% .]d2~w%N/8b''22'p gffouss322%>>>ښ|PxŊ\UUrӍq@.#Yc.}`H*w40yT|Ҡ@eݻsQh]]ʕ+1uHҍ7bشivQ'::Ehe깰bѢE,Zʄ@?BE?ӧO D G0t?Ü9sL_^&Lnݺ:eꙙiii8"Eܹ3|~Q@ D @ D @ D @ D @ D @ D Qya/2F4J:y򤩩c=BXz@ح**0:8=4dnnɜ5kVaa!&,ͭ2 [[|LfB!hHNnUneeeddKKKY,bqW><_{oŕ$2f^FE& J\0*$D#ᣎ?P㇀"やL5+QD7dz~7Tn֠S{=tꮎLdq6?7jT{$kU|S[t:w}'H$Idd^go}'N반?e@ \bJDȰa/Ι3G H$߿9f45# =`lj~;wP.gee,_|ɒ%x锔W\͛7GDDbBBP(,** ,"֢uرVWW+͛WTT$vމ Cgs@ˏT@'IJJzi~~~~~~uu]:ŋ/^hѢE-2%"D57o}gN] W!;BRR=l*H 0O=aVVV988\._x-Z$YD.d߾}ׯH$!!! 9sL{'b&G #ݞ={\\\CѣG={P?~<}t.K~yEHHq ]RRT*9rd^^^bb/B4x𡿿cLLLfDL$8SQQ bcc;0gΝdĈ?3PHdF&3i:uۛ㹹eeeaLPpaC6lXNN) <8 ci'OrJB$uUSLqkڮ[ԩSEEEaaÆpK83â 5%"D$ׯ__p!aMMM+VprrZGСC>>>o^h4!. gT f1ٮ+//4iǛ4iRyy9!St&绌/To:tgw1 Ŏ ,{7ǒ8СCiˆbTpR)ْzz=c\Z4ATTH$u!dT?zp"##_|Im`0._N9-[t:Ahh#GZ\.f`vp8qqq---|>?..9772w܄Fm۶d5 Gƹ]a&gNNNj|t z577>}͍BK$jGF&2+WǏGDD0 .ýҒJշlIM B_*… ?jϟ??vXS8 f<x9O>]|]\\mP(X\XUUFZUUeJDI>={+V!K<==kjjeذa\.eݺuDLLuq#Gd& ugn# 9M怦त$lʐ\L׽%eݾqޝ»20 khh(((ذaqb v IDATSNE=XT*g̘Ad2RAAAPTAAAwY6 Z/.o߾t???By󦗗P(ܸq@ hḊBmnn&<{!>c8. #8::/*ep4 㭭mH$"X EKZq L(*JǕJe/|gFl1ug_hSCITTĉ׬YseqpZZZ?ЛM B`0S__̰2Ą,bp1.\p8EaqwءT* EBBܹsMH?M9xAAѣZ-i$n:t8 G^/W 1].7`fL04ҩG6PbhyV`8j.2ހk*ڳgIjU*Ç8WTTxxx /hP{yxxT*=PgϞ*st:]SSK$)H3.Rtc rɨq@nɫ^1<7@&9񩬬ܳgL]{ mTc?C !6 too"bLJp8aQ EF!>8!Eq|ܹ$D&BGRB*Gld#F9#hQkmmq L  +0Z{Sԙ ̬Jerrr{UWWԔDəG4hْ=zT9r_jjZNKKd,"A?;;[Vgggb%%%7o/;0`@UUrV8pV'E귟d2YLLBxɲe˘0={t:]jjHi@\2f qqq9x^߿?mN%Xh3yDFF|ׯ?wdvýt7quY B:ujlll}}}]]]lli0 {y\\\ZZZ||/:9]v)ʦݻwD"S"Oa؍7***BBBHC6mZG.44@رC*R&$$=TwFckkjw>Mp8yyyjz޽-NQFeddt5T3S.:m~fͪ@d}dggᇴ| .:\~חѪ>88744,"{4 żyx<߽{Ȗvvv...{%4"C0233%X,޾};*00ޞTBCCBɓ'&]S^^'lҁ\B%c0R@ai@N3bׂ\#]d"isF3fWNNd8Kh[ Bs9q]p!ѽMCLZUbX,^z5qQ/444>>PSXZZ:k,P( g͚UVVfJDqO>L5T*DB2t?ɓy|֖Pnݺ5{ܾ}[)0 aZΝ; GtRyyرcR)Ulܸ~ ۷o_tinyi@@ >2=ɽfnxxxOr\$oB8߾}?z_y1 q^ӟpoooHtR,,,,))x>c}|رI$ V\Y^^NDK R2r!v̷a^cFf ?;;[VgggbRSSjuZZL&nsQQQuuuMMM)))NNNyA-|ѣG5͑#G|>Hi@T&''6]zZ_KJJ6o_]Bdd~su%c2T﩯#s#""bΝ)))| K3&2,&&FPR\\_ʕ+hv) ͡7xŋ-ZhѢEKkkkJm\CE\tݻw^:h MOa~u/To:tgw1 C>'h4zxxof>W򸻻?| j4I(r\ӝ;wn„ ,޲Lfv&쏚'4iٳgi"UT8T*OOO}QMMqɔ'Or?cP|dHYu fɓ'?~Pv)HFO?رC*>7sx^^^ׯ_'aŤzڴis83jԨ3gΘjpbs^^WL3Aب}tNqolltqqٶmB`w˗/'$!DN' bŊ/_2wѦEd"]F0a@rss#WWת*S"#lp r]\\֭[GR8qݻU*Bؾ};q={vrrVMLLlKJJjIII!!!,"##YœE+Kݻe=+0ǕJebb3bVVL&#_AAAPTAAA,޲LivSN52h\}ZZZO~7o 7n$@LqŊG%bPaTTĉ׬Ys… -8CF;_Uh48RD(666Rgz{{'Ȏf{iY BQ$]0+0PT*qW*iHhii\>n8 ?)" A8N.\pGZtҀjef0=zz/ʜ l 9MdLLNgZ8&߱cRT( s55ț`\z563g#GLMM%S"S ![/ Sp8&LMrBH$ mzكUH5 5C=*lޫLCjTR{yxx˜C={z&`>.{;No$%Bp8gΜ𨬬$^H$zs6iD"ZA|9ND5Ǭ ҄ӓv1vXȷ!C% "1bD^^rY 1m!8p233[ZCRKO>5*$CNh42z\4siӦF`IS4u Zh42# 5ЎU`ԫԾ}W!5Ǐ]yYW痚Vd2YwkjjJIIqrr"̣G4l=h9YEHCJ299yذa?;;[Vgggb%%%7o/L;(;sxdd~sux2 Kƌse2@!v9&LXv9soL&Q(O>ŋDcǎ| Ђ^___O HeeeGnz@@M͙|pmHI>}#"" 5s9a@`׮]Ji"Ȕd٩MHH)j4\n:+h...oDFF^3f )ҁis`P}}}pp0 ?P ̚5g8_~ח -Rd1IUMMM,"uLDh^ |dHiUG:`C )olztN"H$6n4gΜIŋsd'!C?>]43ɐ?e@ \ lʕ<ؼysDDe| BH(&$$t9\URR|r>}:%%800믿&Dd"E!w{v1{;v,##դbŊyd;v䀑ӧOow_Gi )2 H,丹oIIIO>ϯ޵k{}ݼy3((>#t77SNu߽ŋ/^hѢE-SAZ`FfooaaCŋ0`ѢEr%%%6Nd)!")" u![~D"!<$(-- 8p`HHș3gXD>a7N:ܲ:O<9,,sʕuVHG=|:;y}rps=TTTDXRD\//WbXXXTTa6lgi ȸq3OƍcNFӧ׮]kkkkkkn:lBaح[222IСC>>>o^h4<֭[45#u~ח1 1__#XIƆvyd/To:tgw1 HqqT*p8Rp\^^xp8СCaILL #Dzkj LCG,;::.XCڟ^ **J$y{{ߺue2U*ՁHO"A~~!3X,r^qDDDP|dHY2o& @.J\\\KKKnn.Ϗknnuww'9rDrooo3{1?-[t:M7 /_&;2O>)h&ȥ'7BBBmۦhn:oo%R~Y !DN'ڌ'rFf ']F0a@rss#WWתOQ{YKHO}ڽ;EweaPPPaJXPPЁT*UzzzPP^+0ǕJebb3bVVL&# vU` n޼yԩC&ݷo_KKKzz!޼yK(nܸ| #EKݻה,FEEM8q͚5.\8ZЂ )T`48رcQFxkk+uΎhHI^LBacc#U9{udf3b4B!(Ly.ɷPT*qW*Bt`0S&[Nq&Th0]B"pC= ["rΝiӦ8߹s;b +0 RXXڵk2 =Zz_~IeNQ̅ :yJbZ8&߱cRT( s4ڸ y֭ٳg0 $$]|p8?0XodɒRٻw?pa[[[BA-0 {WZuΝvLegg7gB؃qї.]*//;vT*e1 S*Ν9sfܠq~A(n߾}ҥ7LZ!LDEE%$$##c̘1hlmmZΝ;-XRRbKh󸸸/^D7nnԩScccbccMfʥ _! -((;v ?˕=MZ>y")2ӧ_xxxDDD Lf>>44%2 8_z\zVj*X,W^M\DD`nhOGnrrr|||x<ބ .]D2b^^'#ٳg ٳg+ i4xȽz۷ޞԑcJ$beIi >2Ы W3gL6;99-Zx!Z}'@*FDDh4 t:w}'H$Idd$tpȂh9s&-HƓ'O,Y2tP>?mڴgv&iilo$rͺg5U߭X"bղ.]MeaKuo؊+͛WTT$v w G[\URR|%KӧSRRkB\rett6oA BH(&$$HCHLb:vXFFIH022r2 0T!5?7^ū~fk߾}|]6| z BYtݻw^:h "u>}_]]k.S" }5GpBTz… 6>|0OMf{C,F`0Z[[[[z^hjJRZZZMBCw0cƹ\Jq\Ryzz?i]VMMMB:ܹs&L Dww8^QQ"" 9MZ3dI&={&"%FofWTϟp8iiiG65jԙ3gzڴi\?>guufsND2bĈ~iǎRt~~~"h˖-(j Aa&L 111~t'Ozyyq\WW˚(f/ŋpH$z%KLj3m477k*oj5h.**"BS"s7o9]H]$UWWwd?~|RRReee-5MȜf{Ǥ;n8.Kb]]É Jrvv#p_x4+ ***XD%6v+WPܲe#Q Nh*UKssRT4־xӧOj<~\aLaXCCCAA  +;Hjȑ#N DFF~]{8q"/]/[,99ZT*wMLð3f 2455-]4>>~РA"O?466ƍ10%bv{8gϞϟi?NG$^MM{陛K68}{G4Y|9`SLYn??f*--^j͍QF7s`7o 7n=Rl?+3fPŬ,LF|>q<((**==88z"ݼyԩSMݷo_KKKzz!"%.]w^Sn@477={ۛ8y@ Sg.G8J&D"BBpFҀ|dHۛ  J%J;5q5k\pٗ\JdzH2}6jZtҀ"Dr!!!8Μ992558[t:^|S"kd)^|(ɢ?uM:j`4 hsANc:y$qŋ/NJƍi9yD,H$bĈMMMypvjXa?FFrɓ'[r.سgIgU*ٲWhj5qT4<<< a̡N D"ihh0 ܹs:%hg"ٳg,&]LZdLQCW!_ ={|b"eڍذa/ƍ[fMTT(uLӯ7PVVR/WP`$D*$ SbדW;c˗sX3OtM;& ^/˓fΜ9m4ǍF{ll,ٺuhq|Ĉyyy#-آJ B|`[#GzHh,-)q>|ܹ Id%%%7o/T+DTTT]]]SSSJJ!&''gff=zz2=zT9r_jjZNKKd,"RLNN6lX2 L-3  vRZ:4f//B4sYa"|b |~ii)(5Oy&ذ6lkkkqJ2O4V]jX,ūW&!E'm?x!C̙3<{d̟fSر 988_x왣cmm!CSzYYYNNNR4==`ML . L݉ߤT.Xðs>}RVDq*ފwK}Kb#5 hčFښ=ct Hh4:*++O8SkksC̹ ü>o2+++Zݯ_?fC+q x:F>}n\2{{~B&O/?|ɓ{@ur~l`ccckk^w,|?^*$ {TYY}[D@ß^זXYYo|92W`O>|1ng-vKԏ^ӿ w^]<}fKZu˗ۿ@_dH8~*oҴ} `ַ]qhjo~}e\ޥKϘ%˗Dp`H:cdGIDATq lllz coemWYW`O>?w n}xxdW (o8/SN/= 6m^4ĝ w ]? 8 p9 n p7ju۶%aaEX('pw: Z&q#n d17[! Z;9NdaMEpNTW?z$: Gޠ% 3[}wZ`L|x\6/Y455Y[Yt_ЯOw!xB(&M8iD .i@z{֖TV݅B߅7s~UۅlcFIǰf{v$_&*0޽UuEzs/j@W^*?z37]iF~YZYYG6~|W=cV`E{٧!DL[4[[\(zԈa];n6.$w{z-獒;Umabʪ{Vt#~e(zF`Dѭ:?ih1k+[ߚZ9=o;Oh<+(o.iSפZ`V6}>rgF 4w.}ԪX ~?~6ZY:tqc9T` Z5ζF1X[[X[~\~֠׺ZgplU#iF}JƿNc1 p 3ˀF#n$~ -G;@;xDwhZ F`Էԗ?Ǜtzj0h[4! ht}l FLoFޘ{I>5kZU:VoԶzZ3 [4oTm wkִ[he΁wMwJmUi[[Į&EJolV 㽋*0v;凌/X[fu+5kZխ*a@?ҿQߑ@xTFw@?\kk >[YcoV<>Hr }|ѐv^X o?͹^X>V~%^7~:p }cYj {W4hQ+\l;KYN|A h7Yɰu'.WݼHj0_ rі;T`CG6-$ u?шQ+^?^@?<xxP]/?k8'TnQnmllzz*s`{”Uݢ{)a%a EuӞoԼ5ɷszO>z^ juOf"n=r%IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-unsync-events.png000066400000000000000000001770241351617527000250370ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx3+"%PBB$H ,WWQ(\E(HG@&RHdCMmI lHS383;B h !$X`j:$r?0,ZFZ`SN!`=YJEBE"|hCd'BRZJòlU0#odP(JZv[+ZR,f -WBLVE nv Z9E9QEQ`!DP(JUT OO[*W~U?~ sҢBoooZ-F6ZGz LZy*eYfYVe9~iaBR&L@s)tfRB8AQEfOf%;^A苼u XA] bAa>^>ZoV7r@+NR>zc-X+UZ"MjzP %Kvر>˾nkc,/*B "9亙G8E=YE?Zᛎw8ХE@kfWm뱼pLjxBo"-پjL?yyzsI9kaBӞپAAA>>>:Nld2B233]T۴iSIII^^^֊)7i"ˋ Z9c-VNuM$Dk1]/+)d "D"/q+6t)6o}s;O ݌H*>a:[ʣXb0rfGcFYL׳G/mJ'"|-'Uf~¯R㪫Udds礚򇴕-k` l88^Cڑ> iskOtz5YRmǁ06r3 s>ӿSބ6Bd_+Є:7VpP`C`zԋnr&lśٸ&}G'*Xϓ'Qyj6]b_7s(l,++3 ^Rz0mf` H_`TթSbbbȭ۩ShCڊ6J|Bk1c-qZ.9Vʫ/k1G4y]'-"tk]KT_@]\ڤ>;?R*X>֣Ԫx(ҏVvя HXYV&>_Qla6[YYVOeWzR8wMoV>=l9g9cb|#ec+'|(K~|lzs1t6{Dv,buJC`#Shpu3 K-[u .*x0lB<թN,,8{I|gXo* YY{xjtj_}Zxzz|=Kr7QeZ˝:v*u1V"e1f X宦DZ(E^MxZ7|_#P0mwńbbcm=t6)*򲼓ydB&rB~, yA-r/NTxZuBHAqma`m0?sL굢F?_,<9B%e[JۦގcW+`ʩN-+!#%Mx2 S)ۆM)(B|bW\=|f3x^9Rͪ MEeV?}Џ>.S50Q\ʾՑ#G]v]Vt医Vs(s,!DxBysJmEXZM=*_l=}kQ6a)r(yEm:),5J%FEV[Q? <fx%ydh9 dnҩ^6} WQS_:%0<9BJRS!AZ|VrS^V*.eK=d4k_&T6 ;9wQ*x<U_*_ew7{`zRT|=\fԽ{w (6"DD<j=Ʋ:޺]G!2ϓ9j-Z\DV^pI ] o~Q) = Jh͆A^(Vs_1 ըSy"Pr]/V*?!Z8yzk`F(3qB8j6 ///BFQ*|Q>g@oT*!.sN k9r;ls>}ܹSASvbb&Q8AxX'ݖ\y.EAyǰ':.0kɸ׍Sy $ {`6k7I/8YB8d\rө90ݴOѬ1$ȷC˰?.^7{5{Rk5䇵i"򜵤3vCUg{W*Bn&`QJ~{KqÞt!8oh4߈uoV^`3!Xo5ٶgUAb Nmнh+Vj9Aa.mxcļZɁ/%<\fn"4IRJJlۈ>hRiҰ+g شv~iD:;HOOn& '摿]#_Ldg3hÃ@F4 y+=7no;jlW8‘!q&tjϵ=:BPئDM~:Izxԑ[VK;dŷk7\3^+[\8u(?[RDi_|O???OOOZ zfرcr]~=t: !dެ YřˏZs9ZF^Q(U*u>aѮtk߼_6}_."l`=Zeu i4Bq*C?u?n <hAAA@b06l`2Fb8ǙJjm۶u[}}v5}t"_v{Tnʭrg(UjZ!>t\U5_L&W*AxUT^^^jZdג p륥jeYpN 20<`0_n4YujzJz>=eJ|N(U/-ّU8U8.Z*Wmegikw߽o֯ΫPs}xEQ/JF 2@ѨT7Vc5@NPxxxxxx4hР2D (++rQX+jL&Xrם9g<ǖ/< pG֯Tޒ^}yGwqV?~?ZA),JW(307;q.w޳YyVBZ 3"3{~/ؾ݂ou7_P֓c4ny3;vhP0H0 ܱg*nW;w ~m^syOg,d!*_Dc }4tv ^4nUfTPIUX D0Zޝ׿ ?bW^_ʑ7T/:AR5ٝ:uLʄlBPUm Z^浼\~UϜ_s=V%&\˵Z+V?a}{+3\5~w+ nvGW7C~~sϿ(nݻQmQϾf#G/YjGվCG/”:vXB9snct ix㒸 idVڨڷpU(DQĉ$r,J7!v^)76Ml%Qafffر9IOo+R-l+H>}XRR=z̿]tUiEo%7vkV1^:e|z:vuɒ%e?Ct7T*}۱c#Y]RQ}]vg`wh^^޾Cǧ۷\o_.H'.3|DAu wCY7BVqǾ><'FMG{v@v952=OܶаFz>33sƍӧO'5DX*m܍ R}5.M>50w 2j,d &`X~eef\tèr kˡ524BCtP f`ZG;Wrv4 ڵkڮ g`: j컐0u| =\5mŅj}>}}afϞ={֭k$?\+6>-D:u!w'_V \~qq^\v5x1cdgg?xժU5mڒ']mcuf}t}Q aFᆍbn!![*תLٷo_Ppݻw;KF i#.n+5|ŊʅqPUVTXue!Op]ZS | 4xCoRM4Yt={Zl)u$~m/_R]NhѢD___N׭[ 6Ha̛7/<<\PH% 4jhӦMRosy[l&hߏ}xyyەaj4C x}223 i/+P'cPp?ӴYDTV[lvBy`Yի .LII%#GLKK3;|pϞ=CBBޯG͛7wvԨQF~%رc}qƹs?.y(0L۶m׬YӺukm===c,,,$u)2oooZy7x… <3 C{fF;_&^^^ٴvX9+9ESN7Ӊw#ٴ 'ϴNH[rx%~uMyu7l\zՋ/{9c"m{zz_Km*Hl67h}ܥc66օj>%Az}ffƍON*EPpFђC=Z{̘_~9d$eD//u~ݻ{}+(2??_ڥ!JR7o^zzzddiG~\\\׭[e˖֭[;k[T었BHRRҜ9sF`p%> q'ib IDATѤU9 Ys̙2eʥK0`Q''GѣQ#=#Rۻ>pH'|ʕ,<5uZlYhӉnjf {EnV, y.N΁54zҝg!fMzmG2ݞc p*,Zށle=9,E?NW5JgSSS `HMMMHHpQyȑ*j̘1g޼y3-6mgϞ=ydmfϞmZw{ڦM_qܙ3gubXw{=y-ܹs埩.8^llyez8m{ tM?mjZr4HHmcK4Ӧd 2QUت*3T>k`Ї}hh؛ȁ&BHDg_Ÿd(5r X{(vT0w|!mu^mi*c3\Wb+侍+iNI[%mD)_)0Ӓem½ &nɢR#Gvhc~Ti3cnSܷX]NG|O!G@މVx{ȚyfV Ԋ纇S2L耆23k%V1WrytMCOp~][:0y? 0 !a{sƒ=tHmӉk%VOSߙ׍Vi} yFBHdgRR:i7[ ؆*29j|,9G ,#u"@i+#d*BQI>;Nњmhw*yGADoZpZ%Vy<b["?YɌ c8Aڝ;6?J쏕R/X !LF[" 5htM?YQ7 ':}_Y6DQdZ$5Y\5t "\8lG;űrV߾0+)gà aJٜGu+fަC](8Nt8#d&1{I/v5( L]#UJFd:EB.y:Xbx^aߙ  ;DDFi~TJK2ZhTNٯC=1WrV89{#0ӧO ٰaX~VˋjQQQR| 6nx{~%S}B_w}3BڽV(Xyzs:UI9ZUh2<ˉJc;O MLڦrq_*}p9\)kD9{%7.p(.F`߰29#Vի?K/ʕ+?ĉz)BѣiӦ]v%%%ߕeYsJJO?}}!ڛu!GVb7|wi5}1F[~:OoH _zGϗO%10.V\j=Ɉ>bsRz0dt<ϫT*i[V @.//Z u`+/^I3337n8}tR;8UV{vS<0X4 {H~:_Pj`/NBJ|[z'=WKopߒߑVލ¦COvؾwJjt^n8_1܆{!MBB¤IϟI&}Nop߅VtqRT]tqP4yRt}:/1bDXXXHHG}d_AU {Wf!ta؍|:Qֶ}(A@P8I ,vpp *m= ~9z;X !8JE0BH>}ҿ_tBH IԸ^ 4hPY^^hDRրň#鐄]vښj[Kx F4hl~WW\ٻwoV$ jP]0RP(ڵkw%d2˛6mҥ_]KDWnn_yW_}u V/_]KD8+((xgJKKz_RӓaZpTA_}I\l׮]V,Xhƍj*Q}|||}}=<\S3D9 }:55Ua EnwСmۆ;R޽{O:ҵkׁb jG1w\d0誘VMOO.]:tP9sL"*l޼y鑑ӦM}#GTTcƌ={͛? lܸqJYf 80<<< @йs=ztaÆ4(55wޙ.@RY;/Lf̘Q^^N6b^_RR2uTiɗ/_8ٳ=-_tR&MVKjwP{FXp7d` jFzzz˖-5MwI ׮]ۦMVC8qnذy**""bÆ O>:O>zEÁ! ,hܸBpkbd%65]aM;8q"&&F0`0w}ߌvB}:d`P3lٲm۶wy^k׮MOO>?O `IIIpԩőSNuQp>۲e ++"IR?_t1;5j{żbB^?qĔ)S B@Pm®*ӧOz_|E})z倀ZZ(ZVF@:nĉQQQaW___//dffž]vuM^~;if…~)l6_~{sXhߧf`8 5FǏ=Z^lٲO>>=}1cT@<7k,??̘1UҥK[e˖I,ٹ Po紴/^ϨQ BsXhk`ۮyWÀ k`d2iZ[l#h4(byyT-lj֬tAVG+h^/**YM|;K6msB>]\X#A-TɄ  JKKgϞݮ];Z~vjذT3""bŊ&is5{\:h˖-3˖-kӦ}xJJJ>ۻU(8Ú=v&ƍ#{~ax t+7>#X;VP` J-[z}9ZhשLnZTnZȣW^W^BF}qit)<]Ѩ\,СC۷˧pvk.ۙ0Lxx'|b:ab%įW~ 6Htܹsm۶|뭷ܾ}j4f7noTT7NKKߥKUV9u20W7gYի .LII5vĢ"Z-=za6׽M*y~ժUǏ%#GLKK3;|6++_~ӦM{饗\T u󎷷wbb˗mv߿„ :_ o>tPN׿sιyƻ={̤ÇOII0aٳg.]*Uݻ믿.uV 30G  (JrE1;;.t fRRҜ9sF`:7o^zzzddi,U3gΔ)S.]k`.Ο?b̝;W*LHH4i^2dMOOό mX-{'U9rJ3fٳ7o,?3gӧm؂ ֯_hZŋ=6MƍMI1k֬;R;wѣN6lXZZARSS{CY7BVS5zŲ%/oڵk? P7 V~J^qӧ[j`M]> poBn !p7d` ݐ20wCv w20wX^^^||V:x`qqq```aa![XXT\\lSe>oРƍ0aƷ~V4it"A;=z 󓒒,YB.^811100ЦԜe٫W.\0%%""?9rdZZhܷoÇkb~P7 lCt?w!dر}q,.ZhرQ󎷷wbb˗;>|xJJʄ Ξ=50F;E1;;Z @i׮]DDĺu֭[!J.z٘7o^zzzddi9@W3I&!C±cΟ?O>7njyzzfdd#GT1c̞={wu"p_ŋqZ8pиq㢣;wѣN6lXZZݞ ?T5 ްaMJrJg3f̘1n''''''Ws} 6k`fZ8=~*Z[3GeW˳ZQ!DzhSL~[eae$~oM0wqL9;;!}7ip+W<ʏöEWBHR`?G䅸tଲM6޾g< /ĽEyk˓o6:q,TƢY3r뗐<*hқhy+[EE_*RM:XB[8~*irI;ޓ]m6$!p7d` ݐ[2իWGGGk͛/_2 0Jj׮ *d`6lݭ[7ZWPP@0Pg5ji&Zr//~R\\XXXH;l0|ߠA7˗/ƪT*i)///>>^FEE׆MgQ9>*(tcȑiiiFq߾}QU gMMMMNNJHHsQy…١ ,Pؼys˖-5MϞ=333i'L^uZ2v>8e-Z4vXgm !󎷷wbb˗iɞ={z-F#پ}СCu:]/؏+gaT{vSRR&LpYvؐ!C#|Y IDAT bS( կ__:w$%%}}X,Ϊk.""bݺu(FDDĸh!JR5(7q7oŋ7o*Ƞ#GTTcƌ={͛]Oj\f͚5ppׯ L8p}gw/^\VV㏻WⰉ}T94nܸhRΝ{ n5eĈ#FSNO3gΤ*ʕ+㓶4ir!y 6p\MrXaT3f̘1cNNNNNN&PK50@MX7+C)!dŲ%ݷf7fk֬w3sV,[B'N2eBP(:0{v!p?[/,$!p7d` ݐ[3W30TիWGGGk͛/_\0Jj׮ÇDn&L6lk6/\иqɓ'v…#G %/Yp Lʨdj3f̘1c}!C 2Yr߸q}\kg!} !pڵ554@ͪ lРA4B}C;pݐ20wCn !p#k}7q}2mOKk#Rl?%*{֭m !_B|#Upء뱞vM!Oyx;$|d'eyj_Y#/h&|M1&տ~>[6l9iz_޺=;w4[bB&==;-̗.}˖i뇆-#/o]N c/.m;v2 --GwBC !$uWRӾFyc/]Ii?W oFfɶ.m1dK)>b~_>C&ݦ _ V,Wdll;5aČC !+&Bt'v;՝铣?Hï}òd+Ȼ> WBG !ɿʷZg?z,wgЖS0`B{dy DϠoYiW qNzfCwfu_y:>ŘP6>`3ٴzZ胯ɣM?لy9ݦH^o=8.ѣ}y m\]y:NڳҚ;OCOM!%K_]]IsWtrR4 ҵyFҟl"=wqx12 ?S6䛬nNSrK{xGcNBȀ[.dB? !֥m FzI"Tꟊ19.N}}D-yOy.̙ߓDiDŘҐ7O-GNw9$˧,uK,T%7->0AJ> mUlMKy F%7םA,isl4/ET`0RSS8T: س>啐… CCCCCCsss,X`SAI]6dȐ!C:D7n+܊['>!p7d` ݐԄU{R>9?BV,["KV,[bْ>Bf2zĉSLQ( z~nq V~7qݐ20wCn !p7d` ݐ20wCn !p7d` yX֥Ţ UpCUVE\zk+*VQ;J{]wo-EQ*.U@1sd6l3937',lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`lC6T`l3l:8tk!Tz5zLdMրP]ĤMY`Ad<}xy4}TSZ,hVL;~\XXB0 EOY`B@=h q B@=h q؁P EBAd}p8*Q2d2* *0[r^XXX߱4 ۵ -Z ZX ׮}FP3"Asqa-ZjZ<͐R}{nnn|wD \.3++#'j5puu4hK7KFب|K[0"HAmwq8?mK++BZHMB?q3P\߱4,-Zji1jrzN8F-ZطyyrT*KKߟ8~e|>6*,--|KryVֳrCSΝ:d26jFHw E^Qq\^!QNl=|Nt}3>6*^233# I$9N׮8ׯ0XQ*Qh2R:8Tcz)JT#QUUP$Fب A}u[XK=@p8dROjc``A@? I*O7` buA|GHSĂ*Lߒi[88 |tkA*I%Aܽ ޝ}ϴu5uYFڮ71jXPP0l0 ~g=kfybi#Ik7jl55p;-_7noD]=6l 3;71}dffvСcǎΝ1bD}Sk fggѣG 6~jEEE}P5%ڼ~z4Z߄ުeI&fff-Թ={TӧOZt[lc۷_kn?֭u}$TJ%P( r ŋqu45mղѣGvmI̡iǏvջfA 1¬Sg:A Eb+ksѽ4ܾ#,4e˖B`T{Vv::?Rl-"qWk0qnVV.\W3* OG522JII122ںu)SIm[l9a„]vq8k׮ &lRG_mvuumժLGGGccc'',($$Ԕ&$$P-Ae>YE[w$=jccӺukX[$UG-&@2#3n]Eg jI5#3Ms+W9 |.| G^JHЩ'v}Dǎ>|ɓmgGU`IЖCINhYwVuݻwOIIqssR ᔗ3-="B.WT{ѣ/|"7gϦ(ʝ;wN:ҳ5200ɤ)TL&U$Iҍw}&:wDs6ʺpš5k[Ԃ'TU96V[߯P(͝{cǎMݵ>$$+;7m|]uԜ_F*ڴi#6lH7GGHe]tǺ/_Θ1" JwBR豣tŘqvvھcgEoݺ-˳̝K;::dy<1\iiidj 蔔Hbccg+)899ݽ{D]"bNJcǎ͞O]=رcRTG_m49"bj_'';vGFnwvv7Aj8;;MTn:Bk/tt3g![>… UJHmhBFћ>::VD#A|=yzopa3uk׬nV`kMu^;@7~qCbqע"BɖwusQC eUu$I*JR(IGiP5olܰQ(ҥy$0PMׯ[tvB{G4p]xqrvr{._N͛7 ^nɒ%{wh$5* pႧgaaa^^e>իwϞ>|?o^G_mڲeիW?جmvTKxxŋ;w_~ 7AjaƌSǏn߾_֧OaÆu3o^Xm%@25ڎrXܹkZGfݫߏchAp {uL>v^#Gq-̡G Ad<}xL{Ϟӧtg6[¢VQf!˞?n߾C~~\&.%Tz3f6_\޿+Qm,,, ~<j/.vQ&!QUUܱ^>E ._ dwLԪV[3)-/Y:hzJ_4KSJL;~\XXB;M;~l`` ogmDXH~ȈcԵUfAe E#&FMIfpNBmw萐pZsI扅iPȺھi}42hjZ5H4M^E_*:t`l׹BSAWC$)j=\ _Y*!27AC$Y?SȦLUU TTJ$ب8N^M? MM;[lGxgt-cHTU$'yD}N}GP#lT`?JRҤ$I/Io:bk& ߽{ڶ  %*UU5Ol=|Ne={n}GP#lT`ݏ98nV,ղ7n7c;1u` .߳Rb`СC܏?V~~~d``D.:g IDATx:P(>I?y؈;aC =x͛w@p*JLNq 9={y#~CǏ W}{-}[lDԩ<~}P#,U7K+vƢ[!O=zσbޑ7ӾC={t45mmܚ$I$V%@g!~l!+*$ Zhژ@jjb, #ӎ@WG/}\/p8ۏ/hpL&GtyՍ=< D"C*$IY! I$JJ޼ys#zV ?9PtYY) EERT*$JԲP(yLC@Rԕm]wK,T546j!Q߈ $VZUM7ce*0 A64|F6W+87`Hk7"ĉ,ymy4X$I1ﻵ~Чoߓ&MּeA;v_>GM|`惶tu6ݨQfQTv>ۻ'?22d􌳁3ob2ͩ{Q~d~Þ=Odž03DdGĿWRq4:ʯo23XV9:|G/\g,P ̙3k׮߿˗/7mhnӦ]D?xf-BQbb"_5jL;雒99>_l6 m;Q>hzAvV634kO~8qҮO~dk}߾}rss?v:]+͝=娅-Z888Dnۺ{nBRg 8E?h7q-"3g??l>mWOPPw[Y[n]xyyyͼ.<~AAk>'yfLIm۵={`?2枝z c9Iǐa]Y().3Y+0oﱒ;z|>_R\$).&NNk$)JߵkWFu4!? vԊGilt‚ ꫗/r6}ql.]yW>K-t()."B5⢢‚=w/^An:;;kZg5ky##.ܾs_Yl/JmʩܷoOkֳS˳cArj-]j?Û^K r~:zs̓oۮ=CFۻ76.nԨ:t3`QcT z|3O/X~ c9BT*|bnѲU+Ad<}x$IHsv}_߿p|\b88'ԬS?[ٮ} 8嶭[H5Rz՛ ؑٮ}ojݺB#Sz#ӎEAt[ZZJ200U޺u;Ic=<7nؠ6O?bgϞ) z ۮ}o߾_uvFZlٍ)F˖~Wl͝=Tw/} !pm^qN0zRu9:P+_0 f GY[[n𿧦7.I٫ɓ'{t>ߛy4i:w~޾}"+k# \.wE.[P(J%Tj~x+Wfgg'$$Z_F-t،Zɓ'wD3wRtA^zmjyoպkצAo^uS ihhL$Ν;kr2t &Yfښfo駟d2P(N#~[6;;ׯp]Q֮Ybe>}|>k3R))ǸpfX,GA̤h?)4 ESSӓ?`bl,E"Q5vPwV|: ,udU ~f~T1i$Yzg``P+u2]QQӦM}!ܷo Z$IϞh[nd9skjvF)uVNR/!1Yu1NQIcNTFߵd-w\`A]OwӦ3iU Xu7-yvÈc UJ> y} uZאj$^^^ ,ζQ߸!`={1x-a:jj.. XԳWo33+mk߿E}ٳ;Z rq-++{իW}oHAwfC;ջwQ#G/@%<\ugGY^^>f7zm06~l֩e˖]v>y:z6Y'zYQ`ڦڼ~3"oW*UGGڷicңG \j59nV#J/z7Μ9+"bk^wE5Wƈ}` c'$W(RT(I,U(jGmU`"q76<\S$wF|#<4hPM;^h>ylGjZ.@SW+aN|S؆ m ˗/; hN8zmhWrG(4:c7v;s挫+2dȵkרD777@啚J5?ɉ999?jd*c#@*cW[8{{{ =55C L}'{}szM?x`Ն٫W,,,ҥG͙P( ܱc]{w/̙Cg̪T3dmmݭ[4{=~]vuuu>(;VVVvqՓMII111ݻfP=-?eNNΎ;V^M,[>33|c>ydڵ)FU]d[~ٳ+**ܹq5kP\rQ%: 1Js9 Kvڸf.[^^NdEE2],$Y\\\6 (jcsUFlٲ+WP-7n-PG[>cfhBFMρq\O.[ii۶̙3:>}z߾},ݮ];ƾU:#Փ'Ox<^w:q~:s.$T~%{tttYYYLL[-痔DEER?pjo!`LnFJJRds }W ܹs e˖:uK.%Tl֭[x… sݻ~i[^zEU]t3. ;vݵgOOOjyǎ`Μ91Zm8unݾJ ׯwTX,>vXYYUθwy):NuLc33SfffgΜShcǎ}ڵkǎvZJJJ|||HH2431ӦM{qJJ]d׮] ַ?44Lj!nFT`{ǏM|r#G9sSrŊ-ZbE DXZZΚ5JQP(|ÇŋS=z644ѽ{G?~ڵkW^EdM1h΍m۶)J$w=w\]reBpϞ=#hΝ+W\bΝ;W\YQQA7 =z]pa.]>=K =@#D\.:rm۶3g|ݲgϞ]v=zԔjw/9c:vAs :vhѢ~J1277?~a-ZgcccooS 2h۳6mDD>}O?44iL3όӠ< pttС ]96mڴdɒ}Qo_lɓLīW/_:N^VV|rnnn7l޼YΜ9sժUg:Յ@a@+99O^?y6[3gZ[[w,Q ?&ջE˖Gw;nk섄C?M*$4"4j?y5J=j *0hPo@lC6T`lC6T`lC6T`ZᗃvTСCjac55(T`nnn+55o wWWW>?dȐk׮Q:srr:}@Synٳ)S-#u-Zo2vg8|%g&-IEIIH$vorN*&&&zxx#))I&]|yȐ!TCVVIϞ=stt8c?ҥKj<$R'''k$\͘~зoߋ/R-999Gҥ =7oƏ tmQ4pܭ[Z[[ܹs[lٳO?D3wwwX~4`c:7JJW`bgTզӧO5'CNU!=|p7bĈ?jԜunܸQ$ : ?Ϯ^J5>{lȐ!"hƍ9qvvNII1KZZJ̙3W^MUf͚:Q捌q2&c5lxeeeTcyy9H$ϷQT* >Ndff& 6k2vLLL,+++++KLL>|C㮙ϸ8[[ݻ6e՛ Q;CmSGݻw޼y;u:R<==322Twg]v^rcc߹oR?6 ^P ,""~b4Xȿh=zvj=zD=7lll5y<\.'IR.|q\++]N<9''GGj75{   Xr=m޽#a\SH婮P(nܸ@ݜ4ie2ԩS?.J\\\4r7oD@yw%&&ңL0!,,|ӦM5QӀ1Qyh*}*]qCy[ EGGSgbuVt şxiӦZ2}ҤI[lH$aaajfGqgL}=YٓK[wm;IeeeCU}gLݬY/_8ZwQM2.[RRB߬ Bٳg׮]G555 R۶mgΜE3W166>r_xADNNѨm :vhѢ~J1277?~oFFد_k׮eff:;;X&hjӦ N$h``օ$So;GW]hժAGu=eŘ΍ǫVP)Ljqd-kh.\.p8gϞ\&ǣ O}z߾}ЪhN@'>Нj9fDi.gff@CgF5cvUxU6wgffRS.==}bxժUJg j.deeΦ5m]r!!!B>sokkkccKGB_(~Tcnn)SD"QG`Th"+++++ŋSWNJdhhԩSczq2&c5l3g$ɼy󬭭UCp|w}Gɓ'BPݟ+3 A< 4Ye  6T`lC6T`lC6T`lCUC&@Ti:tR[ΥJ*3gθ!C\v6}~1%11M xyyRϟwrrxNNNϟ|||BOqqFƁ*-z{SSS=<<ɓKJJ4SKm 5c%4`L>cP\Kݧ'i^x;ܹs#FD/^=z@ >}uD믿ZZZj'$$} ZӧOKPhccP^^^I P/42lɒ%ցU9#FBP啒cJۛ^q 5W}awJ}%J%XRRR|||zzܹs_#o=5ĘgFEE=zkƌT… CBB222ư0H&t4j˿ߢvf'O߿999t &Ne_|3i@0%1 ڵwW^5RڵkbXKܹs+W\paZZB\\ܼy[nsʔ):B:s5sssw﮻Ç7o433b5QGm۶~:999999''gU痖6}ӧk&áCddd-tĸj5N?=ۤ%; `pT?^봥D$QIII2C H|왣FƁMlO/]JKKI,--urrRKT:::?tGn\u}^xj=zt.]߼y3~x>?hР۷okمnݺںwΝ۲eMϞ='jgϞdFN0湪s;}Ax<ÇoܸP({Ri~VӶ5 r#""zuyRcrj6c5.G$9oAQ9|D{~0rrrzjFWVVFO=Ũxol Auww?>uٙ ϫ*Y޽ϟ?v`JpUU`5& $S1n` A@k5N\ w痔DEE3ƴDFFZuAJoԫDDD$,Yyvtd~5]w&!Ғ HD؉'vsJ///PH40`9!?cᝲȀRT**22"Hv%^zW_ǡw#Gx{@>e<^J=!p>l˖-׮]Űŭ[ \z8tE455HHH(//}syhf))x]v1NDBp߾}[eee>>>R[[,.^H7A9܉-mSFϳ #ӧOϘ15--:lFZ02 g8pW:tO?֝~(hjjZnP(x-o>>p\\\222p! d2jM]m;`iiI蹹^^^<oٲe ޝ]].{th@ͳ gy|w Eg`S՛6mD"(<<\0߷o߅ (uY̜?>>>>w&B?3qN%͛K,"(((ӧ1;5;#xӧO?}4RJ244U*U3~ dOУLG*&'']xL˗}||K\\~;={}0\yy IDATy **A'C׹s璒N0,//?x s &K\rʕ+VXb m"OXAg՝JHBBի򲳳>S\'۩Eq՝;wn۶~oll{ݻw ŁC+7o8|p}}LXZZ6;v,.NjD"33J\pQ*\lŊRA#ǎ۲eH$=),, 477 r =agΜ _v1@ǏO0aĉiiifwecc gʩGyy^eHݻ i|󍅅͛/]XzuDD`֭!!!d0 #;MĄh~BE˗ Ehh-:4uOvww8pFQ*Æ Ӗ:hw||ehDuJqqܹs\ܹs;^]]흒BvҦz~ٳԔhk?nڴiĈ#Fظqczz:ȝN? tPT-0L"`}eH[,k„ W^ob%G`h\.5bBq-[V^^!%Ϗvss{.J$Ǐ7778q a^^^v˃! !7hkk}2((hΝjh|YJ%J&]X,޽{y<޽{ +|ITTRSQ2&N_|/_Hj#DCZVbΝ; Exx8Y6eNN[,ϟ?R_>sLfCt\.;/\PVx< ;{{{LR7Ų2GGGPVVM  >#G!!!:dHKEEL8o޼ωfeeM:!L݁:OO&a111**::ߟ;ŪH$/^d6 66y7JC'10ZrkkĄ=y')1P__uV ,44455  |ԓ}YLNNH$D8q911;) 5T]]} hs J/sرDOOO\vuu~-'w_fѣG僳xyU777+++%a@ hhh 0n8C4#hum5~d](JP3ZQZ'Z[GݥԄ9\W(lkk/zڵC,b0 #/bnܸbnܸ]/e2YTT'|My rrrT*ƆĴð/^lܸ/ 7n ]*Κ5rF;P1X\.0L. ZpKB_Zn~OP]g !CVZC(G=|3g,,,pfff>EK@yy9b9rÆ =ϔ^l6{ɒ%gϞᢝݭ[gΜ)rڵk-_< DlEDD`K8=|W.LLLۄC/JylmZ:Z<:ۋ93x.cӮ!C deee#333$$dȐ!C]f q'>]y@ddu`!ug۶m Yr۶m]v_~}xx8H H/PJS;βeˮ_/gbtiiiis|T- ]iFdJQ[[P(lmmq166ԩSG&ZxTRyY--- A‘山J///PH40`9!?c#J277WTG'Dk.Lի>K8889rDVދZ8)Y,%rOx ۂyAAAW}wﮫݽ{/j޽ o޼Ⱥ<( šCP?"%%%oom۶ՑCShOP]]eggRPTˡC38 3}$Z}ӧ3w_fMtt5k۟8+/tOg0{[۱j2fgC{h꺃 5M:>w;a>\x/}ݬn1;}_l6od͚)>KSFMy3lδQEM#5ms??D!ѤhrdЋjyfOBL13qO:Hz-K>4Ϻ׆"utqh[ǫfB\-g>x`җ9G[[~72ʠyRSR 1g7:2*+v*SLVWeld$cØWAvhD &)^0 بS([qwʝ#pQҊa{-ycSnmP,s"ԐaR{MZ"CLa_=Y>`Z7N boZ ѠQzqMfP~Fm@##4dіͭ?V[oo=>`aW~)~1t1vcF 6}XҕzvڱkkU6Ղ! -zRG ԯQv tuɼ!.Oj *qq54jNxh:9 T M#Q[5`@9F[Z ^8B1AUMVͼvcnϷݤjOndo.ѧˢYӎaԎav P?"ڨY )M; 05ha=v@Sc:4uV711Ju; %uxVxZ[Vݢ-LthK0'5٩1tѣ3oh3Լ^>5xFFF)}\-onգ`ZZmg3_QN }Vل\MZCȚ554k j3Pjc-~^ oLL>j~ DG]*wP2=ueh`dfs*lxMVy6qsrŅQfSGRPw_ǘϳ}! hs=lH&YU6e; Gs;2CܩI# *f NDhEѫߏj Dk+JR=e䅻_+0qx\*W2t# 05NB0_*O0 w?Sڣꥳ,r3 yceV`~BEbpшzMk;sȶv옴\RG䪡IC|L`jܤl{)3Nivǣ32{Rmaܞئ=NEM)*f <25ԐZ׼^*dbϷ|?(`.Q35yԙ[M$P!{__xMk^2$P#zE}tCO 9^44I!SGsNgMSrkz#G 252o3f?fWo͙><چD.mX3Ɛl 4m#VQ2&gЙ{/{چ=,-}9'6DC>2'/-*EM?TӍBK>BvL^Pc?݂zukߪɍO_7UM񷸣Ǎ4Ҟle0r٢nwVWJ5uvM|'e2SF] ~jcG =l@ZVk~;>.bjf8~dKY榎6}ǜ] .c>k{U[D PR : TgxG6G 1Ԓ^j;PW`*5䄊J7?ۧ0*55Iߴ8،>nߕ[۰Y-ց5 hieM -J/yK냢zM[{vQ=26t2; NϪ|^lblwo14m*NN^.Vγ6KS3ZݪҴaƠKE/b|rylmZӪҴFԻODC>2?x=b;ak9׀1pSTߋ1( m^, M4chh`zEf?~iuB*B<E8׼fUȞ0Tjqdq1q*u+FYe\AjKZӊaߟ8 F1.tg-^7wfOm߸?#'{UeO]/Shܡ&`2oh}jt?^52PC8N 4^E36Z-P#<~a]gM[;~m?c#̚8xMp[W6yL9xY?_ǿMл A˫[falƏBEA:e<^S^Ë ?"=ߣ^7(߬ 9-FpfϹukW>c7FJW[ {MYu j:)[ـϙѵ ݞ5Q J@t'~\1!ߪ]FFFw j~̮sacA߯?רl>zCwޅbӃ΢ A[)<^ӃE.l ##MQ@,j+$V<kOP3ӺFdp9]d31b>}ɿӳ_~M?#c.ti@ /]:l>Wu5z@ @ Z!@Cl6]@ }+WӧO?r{""]/D_ Vu;`iiٗ?/|~@@L&isiii\.wٷntD|||rs& ⒑-Tb}W[/zyyxe˖) ٍ&2&Rh<Zlvll,% _|K ]~}.\PQQ }|>_,)J],2V7m$D"QxxF8=tL׌H/t;w\RRɓ'{+%ATTTOJk׮]j.^|9...??/رٳg۷o c*B *Ꞷ轠tҼ1 +))qvv6QJE @ RZvٳebbt~9Nss3a... baŋh} r@SM)Ct9o5ڸ}[[[J{%TUUd.ꚕ,+::Z6H IDAT(N<9##Wz%$$ٱMA`Xqqq+W$^b.9R:DGKUU#e"ߛ߿y&.13gμs.Z,tss÷sss1 +..={@ صkWǞ'TDl=ښj')1{O>fIJLlݺظϏvss{Ag1G`h\,31a„|[ѫWƷRDKBbXBpܸq˖-+//g :'N ښ5uTϟ?R_>sL\ L%YpZW**::=D*Bc["}6V`رcMMM G1 0\GdDBL߉'ejz ,(󛝝*[> 2cDDĜ9s޸qkC5(eCI>4hF^ohkkkooH$fPƍG!KbXJðVaF}k޼yS+0.;a>4442." -ZN{JfͲ"Ϛ5}*mlld20X \a\.ACߖtMGX%K>{LW@-//gX1:dȐUVѣG>| \yA@@Y``Çen0r 6SzA֭[3g o>,۷fnxeCI>4R}zm8;;[XXR}[0ÇI 011`ekРAz/fmm} B)---((r'O9,k}mllΝ;6-ׯ/..xKTp8!tͷ{Do =g`啒Ғ" S􌏏oiiIHHH$=m.""VPbllSRSSGMxJٳ<[5#ccc'N))/a۷oϙEnbbgmٲڵk@>e\%w?{¡vu14#Hv%^zW_1533+++lwCQDSSӈkoo_TTԝJT*ݼy3JBR8phwަ&rGoom۶Q ,ؽ{w]]]mmݻ}}}q'O W!e2ҥK\'Oyf稫r=m.%%eڴi|>%%%HYBaeeAh*2{OzA?kooQPnHw.O1ckZZZgS@@۔S -n0~V~~~AAA6P[[,.^}صk~C^p8݉aءC>Smݵst[T6ؿΝK~rrX,NLL$WWW/_\ ]֭DdQRmذA( 7W$ |>N,___{("hwPu_=++z!vn@hmm [j~-޾jժv+#zE dK&Mbڿ s~… ;vӋ7alITTZ~K/@jjjogPDۄ_I}-SxA-AOF 04h@ ah @ Р@ A+0@ CV`@ F}J/6}&l\XrŮ ӧǏ`L+06 |>? @&4www.;{[nbzzD"x>>>p\\\222PCIǼScǎ988XZZznn[lB`q~>$@|hJe4ǏXjzӦM"H$k4m-\lmmWX?|͛K,"(((ӧx_~]_NVTTп*vmpҝ̜?>>>>wZB ߷o߅ A,**|X, S*]Lw{uV`pB}Y;QQQ //O DEE9T\PPvZ˗/|嗸~;v<{laaa BE!@{ޢ:w\RRɓ' =44tҥyyyd "~KJJZ`A2-K>42a` --ѱ 111رc֭{{&M|r@BBի򲳳>SK C_bʕ+W[bŊ+C櫫'L@n\AA\fǏ޽;zhMo{uoo;(h!9ο7 %m^xՕ888|]B.h)e(f޼yϟǕD)Sœ&MwZd uuu"aEʊybO~mX,VttP()̙3'==%==}޼y\fs8|[TbwŜ.{n@]RR;w7tBIJLxxޓG9y999[n566666iI_g)8F0Lp\Xr„ >??3b1ꭶX,P(7nܲe<nnn4 ]UTTx{{KRBٻwoSSSzz:ۻwoccczz 88ٳ*J*؋NPPΝ;j5Eokk}6ё xeGGGA1A J={.]찎|JhJK^|U* z}+Ѧ4[YYYSN%Zb1!-.{pBZ0 khhe2=-[]ťj5(mXHHHhhhMM -JtD:B޽{ =,x˲2GGG\qpp(++6,4Ж,kĉ~@9s:tY&۷? ƪTW`111**&&&00!!+0Bΐz]Yѣ 1:_a&ˣ?#,Hĉ͉~~~ 25T]]} hsZp^رcMMM_~ٮo%@t144455W(2#""̙ƍ]B)JyF+0 ,k̙?YQ*l6`ܸq;EG 444W'f{EjUCC6O:}&r9ar0mmm hjjT:k,zmmmoBb0qŢUZZZ֬YM^xbƍ_|1.+0 &4KD:^^^/^O3@WMrL'hV[VZZqFڈ".ZM:5>>?O -E(,hIaX a4man&D"Q}}}^"r0RI#Gs\jss3ѲW!pZZZ86ߴ)3q!>իW)?tܥȑ#~aHHHgkˀ:e<X,֕+WKKK .}q:ܦ`mmMYL<ڵkjZPW`:zEjBݓN1sLЏK ̋b{ʔ)ɿ mAav)u)r㢢"OOϯinn&.MVVVr{N.h4R4&&fѢEcRz>|J·J%yc? EhV`̺}%Bvmϟ>|>o=7--- EDD*8[[[\=uTjjѣ</55UT={1x pryllĉ;9WJJJKKKJJP($aVPP}?\H;|g[lvZ @>e<\%C'D3{ofɒ%uD"ٵkL&{W_}eqqq{RT**22YwxZ)Oaoo_TTDrQZDnnn\Jwss󂂂(J Ju Y`ݻjkkw ڻwoBBBdd7oΝ 믣p1888''G7zQQ]ֽmVWWKcƌy!eH_~!!!aaa>|Vu>A\P(:dmm 4TWWGEEQKJT*ݼy3/h'Oh4ǏDRRFIJJ1c!R҅0;90oomߟt]JJʴi|ŋ{0򹱱ð,+++F+P]$܃⡽d2ҥK\'Ol6ޞ|*B^O1ckZZZ;@@۔S -v1È0TVV=Bޮ NNN/^dh1PVVciiI9uH$ #Dz3ݽ5Ax9q'>qvIJdٳ/_.(ոg\NNNNʼnȿ QT6l Bpƍ`<`}駟͛rbw>HCruD"Oh9sƆToeeN}:tgaaŋ@ XxqQQ6Cѝg'N\vmuu5ΝK|zyy|]߱cBwR0u_=++z!@ o VW\ٽ{7zűbŊJf =u5F &O1Ą{w? ^Q___ZZz…;vӗ_l6Ȉ۷WEz;Z!: gt,}Хw0,oW @镣Q00:+@ @ Z!@C 04h@ ah @ Р0So5ocgbF{{ʕ+{n|6ֆXrpŮ ^:Xeddp8 |~@@L&isiii\.wٷntD|||rssq@PjMBя;`iiI蹹^^^<oٲe A$)w h@M) uC ~{_jMD"H$ -"u%K|HS\?~$1b()|>sЙ֯_cǎgϞm߾=,,0>u(@'zڜT*MNN.((Xv-v˗}||K\&-TGC׹s璒N>^$UVV"4Po"Ԑ9vؖ-[D"!Naaa``y``+WD Μ9e7.]rz*CD)SO ^Aŋs\2Ot';;rLPڒ ϝ;Ν;y޼yWvqqY~=!ٳ>x99:y}bp]=x{{[[[޽.A!fݻzꈈ֭[CBBZB2k,SnnY |7|caaaaayf|u XB9}5R6l?""(H?| qʴ{( ܹh^|օ 2&RJL_UQQ-J e޽MMM<o޽NNNxgϪT*Tc/:AAA;wTDGz3Vcc˗1F``w}T*ٳtRfuSԐEJO8ń $ښ5uTBѱbccqDKP/_~yJu3gbCCw}goo/ZBgϞ bbb)ӾpBZ$˝^J...V|~F8qB޽{ =,x˲2GGG\qpp(++ښj')1{O>fIJLlݺ? ĉ͉~~~tG_a&ˣ?#,H&S+0mo߾`mA ҋ;v)11]]]~KPg͚5Gs?yƍ.%Д5s~(J Z[[l6o0n8L#իWܬ#ӛ L  < O r9ar\ n- 'RY(b:7:9tX[[Cgƍ,|&ѣG}ᇏ=b]1+0 ÈԥիW7txbƍ_|1.+0hD#'+""ŋ#mG'((hr\&EEE}']"ҳXW!NDDDTTԋ/e6~aC~_%!!]@t Z!: pWw C uzC}21(]$F 04h@ ah @ Р@ A+0@ CV`@ }wK߽pkRQuwp,77ˋ-[LPtLOCy:zOrgϞ}-\LOOH$<'77322\\\8KFF.644L B џO^ǎspp$tCE"i4|h@MS3V+W|}}y<+ ޲?b8,,LTjMD"H$ ǟhoo_h%P«WVZ5a{&qfgSEy? aaZօjuȚUCr5o+ХKI$ht J ֮]j*\|r\\\~~ϗ_~ׯ߱cdzg϶oQQQ //O DEE1PCXx{ޢ:w\RRɓ' :P0<<|… ,Д^>~fرc֭{{&M|r\ +k֬qwwݻwGnLLLeeeffffffyy ƚ"RX|X,qFaa֭[Ϝ9Ӆ td8hSlK{{{[[[[kkkkFѨJeKKKsssssSSScccX(vQЦCc9Nss3a...?u:|<~QT^^^]P|K*k׮͞=?aXII35cf^sνz*EN:C%;;;oxEy`ggf O~\)//"bZd uuuʢ[waXDSL/'MoPRRimmsT @SЅٳg ]vx>/^tuup8=.B5::Z(N<9##޼yðkk뚚 Ü?R:hllKW[ݶJ/]7ޝEq sB4xđ9`3ܰD4"V(ljk-+hЈDH#h$jxV,%Us5+QAQ9ѿ_3 S~o=C*KAyɷn jmm5]4]*644P-۽{wmmm[uAôLHHDuE\@Pr9H$r ]EAAW||ӧOٳ_n-[x{{26/?oAzVVwuJSygyCuttܽ{wÆ \N*8qBќ_r%Kee|n:+/byyyWf4ݻӦMC1[nyyymܸsPM {Rsdǣ >|hH$2 c T$6oޜdygH$:tZ>x`LL dVzBff,5  ./^ Q1//N!U.@bF2T*U(cBg= [xyy)JR|rss/_L_VRv/k111wܡ_Ԧ\h233SD!v͚5K,WWW/ZH"۷-z^K$KAV˗/ɡ;w˳-zqRRH$Ia a[&CJJJlllnnӧ+W̟?ŋr<))*//d C*Z JRJo1JJ74c`U*(>CFxbbb@a/ _Vk(..hmm#bL"F`lBOzAB1iҤKͰ t^WT*T{&J[ZZTaJMky ̼X/)Y7:ϟ??##xYY׼ެ 6DEEa###sssi{{<~8&&fժUU4w0ZYYu흅dQ{{;=*=z455u>caU`0w޽hѢ `I .(([n !Ic>>' a>^,((N謮 XJTNVUUyO>_ r\RQ={=zԩSԩSZɓ ۷O߿?::J"Rܳg]-gRhJJJ|}}X/:k4**ͮfP-t:Nc:RTTdXPttt~~BhjjZby9h֬Yz~߾}EcKf~Jm1c?`0{*Dz^1ط֮]-[_^t xYYחiӦ>|8-- !t100iE,?%KVZsNjLW_M>huQO%%%򂂂 X R=qd.#Ⱥ{zz7t:B8qYь B(55}֭gΜƍg~=>W,ddddeeUUU!8Β%KTj:n׮]%9r`09rd̙V۷oW=6i((,, ə}w2F -Θ1wL^EIIԩS===/^lz)jN? QQQoߦlooOII)))kJEAgu/BtRXs}zKn'ǛN%??f=:i$__߯^U__?o<:"?S//ӧOWa1WXdaXOOO;Y}رc3g aaagϞ6^VaeR"PC"TWW3*5նwAg>E-L0ʕ2 tsgFӭ^w͚5 kJKz 2믿Λ7O,ңGݩcT$ #"">ZZZe2=wq??ɓ'Ė a]s#sj /Я043Q32 0 aшƆu0pi aLkb½CCҖG,`tԲ1"g˼c`$4AuttoZZZr>,$BK)SO$< W LΔš!!_!Of`'ϺgOzK B=go,$CisՀ۷~KyT*aOG''q,$B0~܄ Ƹ=FnVa6YH[U.=jdf`߿o"( c45oG/LZxȘc BgV~aOjn}G}ǁϏ뫟FX`[vV ĉlQwZGúNmIcD"suphmZYpp}i:wm(f(ۏӽl0$F\wǨԚ=gM8jՇ~v_ZtDO2rr@Na<ĝj=ǟgMZ,:Ն*D/:s},F00G@ʻHTX o4&9n-rx\]ҡur:<.G'/~Fk!=ܨL=ک#F!uv ILR慑B΃:;a O#]]Z#Ab#Ad1QpQH4Zg|w&k!A'u͑+BN?1F|`8WǪ-mK%}ܢ#ū9FX[M:օKʯU` nNk$BȑϝBw zlbf`E{ $i610? 󤡽7FM IDATxw|%iЖ2 -" ?d_(P,)C :(ۊ B[(3+GH.i-}=}H>wgPLF@ PMJ![cٲTHO2tG:OzKyyy[&(!)نqY#!ܳfrJ~Bpl'''BR YX&}B(|ț+>xOɁDל4֊phd2EQ[!$+q8%42,r!"rv8Nj d2J@Lnp,V98(tE|@Iiah4aY{2L.;88HVAPuz>]A81,[h6Y8XBL_dr\ ڷhV̅_RmW^R3!/+-558Rɟm|#0sì`7ѸtR`4ifYEQ2LP̘1)Vu:IAYa9 gi^$Cgd,3#E] ^,egDZ%SROx#XV+MF^ah#E>+ ;;;//o˖-.^Çnnnvuu Br4˲1^~U {ș?k3Ғ|7Rm4nYȑe(٨G ٵoWfJih[߳vU [GS#Þ'vtUmBDLdbC^NƼɃ2tyFO՞<Vrj !qqq6oIIIՓ'O Dzfh>5@/^F1ڨdX1M WP%7 x{s_JG ݾp+}t255kNf%PV4ݕf[NUϿ4MVVM%U*((BNӷ|)1,0r:Y~j?kGYz61F'Y3Er2a±.fȳYAN5G8#Z,`cmy6ngSk+' 0쌬,'''e :MZ86[ܜb3*Tb 0 C$ NϛjԨYv)HPP-_jh%1Ba5[hӴ6 i,G)~ꡝ_{߰kѦF\}fq!kf\[Nt6rK;Sk,r-׹[o7%ˑ\اu|ht}+T[ߥC*UݔE=;%!5Xvdԝy|V߅Ҹ+!$lJt`F&^mӤE?9:=E6!俄_-z8q6z^2^V*}B_CE&L/0cYVctk_ެY3bvu _/L|BQchmԳ M3Qi6r,K 8aڨR+!myrbRe]}i\ȟOV]WeF#ñLZn$#IxA|f,rĨש:]75n:q~Uu:QX&8\;%IO3t YzUut1IFˏ~9P)e_.ěl4ݩqծM|>$WN0ӻ.?KҷնwZ>xYiWۄIĹu5w5vJfqNnY CƧ1wT;9(?H hBF2MV*66*/66֬_P7yOtahl 6rg4M,0t7;̽!2չqOa:!$%((z`.i ZQy'Z,Hqwgh_4ðfǬ6kMeT>Gb]DZ4ZׯrbܿB^ʬ]͉(;lmBHJm6izwcv,ah#2,CwOզzN\zVRϏP4_8N,K]tIٮ];!v)S|),,{ :9eXed2!fcgjoPնNBk/O'R>WncYr*I)^ ghcA,11%x3.m|w2㒲nN}PvVP[svuj ֗Zo !r*@]dZ֬6kM{y:wL33MS]B d(x&X"qfX6 &ܾ'$޻~6_iPURd˷5'.o{\NzrZ7R.cb> \.,ԩYs/8cfVXURfmkN-a61A!w!Y62lK ?fYBdU?\x4˲ahѬ~^-m4tY6\Yͽ~ ^?o=1kHVFQc(mru,}<0.Yѩ 9Yrfi^F1ufGN0e4!:V7p[-MqqRBl6,C ET*ڤchcnvzFF$!Dg_&c>X2|TTD>*2 ©KuޝpIBSN[L<g,òѠxῤ9c6L&ٌM>;-anXdKӴ*cYi߳ǰ,c4tɲORt:׃T WJlS_$oJLFE}ҭlB(݉iqlF`xg~,'M}M/1 C8h?Y77G+wR3r!JFIAo{'.a'OHv(ݛejI)|N?/gc)9E !3 C8PUͅ450JMK1A0 h`J&MILNFN,ɌFR|e?R_.#S ɳRG5`:i!*fGѣB oRfu܅k-Dz4˱ FOx,yvY=J6>q,2+w>kE=pݚjto'RU׳zU_Oݠ(/b%O83茄8ϛݚiyLU7}Z6~\s@U' :>1t4ڐEku|=[eB`/c2sV:1cd1 4޸ZzJ9<-3Q]_O@kte;|̈́ VglZjT|HfrC{SCFoSqwhN BH5 \$qF9>ߎ{7ߠObrЙYZi`~oh&Q ?Lz8GbrVZFLT"8< p)8w6/䢕+Xzl&g'8V9(NJN[Qh6iȱ,%);vttA z~ *ᅴ7{0B0.$m{KGl7,{weYtGf긁7=u܍\M϶ toʲ㔬W"p<cCٶ[&e9X Zm8*s5tO9jmF3_oӥU<̟wl~h)ic?ƽn?xӢ?'o*_]!sf}`h}܄65V׫]Ј>z㭸$>]t'uCzZQR?z۵Uɐ۵6!upkK|2ŲLfi`h#?d?싺yӷ_{BrBf-l߿.޿q19ɉZf\ʃ3{9φZ6?E}*n|z]ޟ'z^G FZ( e6dѱE;6^lyPjdOzCZMx)JLvAe<,lHIJR<8a,˭^^^nnn@j5^iHFPT޽{7mTR:wruq=~xXX1[lڱ6/[3h+*G_:U&oUtjVkpŲ4C ROL ܬ,hiDj>Ca铝hFhT?(M)O*&a&>-W( ɍOyV:CӦpl} <5}'JgKBw/DZYFQ7RT(_<C CMmtL&stttttV=ҔR3''Gx¤;S@1,m0Z-MFG?GƼ<ڼlѭ4Mb>_~O%TRw=DZ}6^9V a7G`JGBHjJ 0@xTdHMaK6\"0]Ȫ|&DʲrծmW`B\ZO)D7jֺ=zY[/)5IOLs>Mz"/)4Qz?x`0숈>cӧi;cVdĊ5YJrrajةWShj~ m޼eV~EhQT3rPf~oڰS` bREhžsgg"/ׯf.^yfJ5yҤ-[Y1|jժo>+]W.7~J8ib-(HpX8U__ /~Bf. ;roGܰ~}2%. 㬳*/zhSڨM8::>|FsGQM۵ke^qӦ[v9rޥզMڵkWF@G`Ŵl׹y-Z8^U ޣui,Y>}zEiѼـjש;c˖TQ7 nt¾,FTQݧƧ=lĄ2rοz@FFF\\\tttXXD+U$&T8 5D`R]HG`qI?*,Q2@y`R30 @ %2wi]](R˺ +,'C!--,Bw%TC.J>|BB%eGx2l-"1^>^>Uv?QO\lذRlѢѣG}5nXRoի|bTTTz E```TTޫW/ZݫW  Q&D'(o׮]j4eڵjՒdBmNS.~۴yS{Px^>QۨhABrzԳ?%! TI ri/o'Nn?}|vv vݎuY%8|xp ,-5%>ѨwF}0}l%+[bbb:5wÇ틌3gҤɢ_ҍ7툈oRXw!UJQܻqq~Ͽ}>|' dY:u4 6%$''=x_tY(q㦦͚{T%4""#˫W'ݻI&NNN&LHHHkժEծ]O5k̙3ZKm@&%%M81222--Mȹf͚e˖5i,k/[ޡ}{RYvuk;WkѲΝeKjZvh46$///tRپ}+O{~%/~d+F7JK`0ڵ;00;{=ztk.?mHo}}_sTBG?2-`tԽ{N8L츢FQZ0aݻ6Zj]v;*wݹ3EBB$ծ][fD5ҿZͲQ^?bĈuըQѣGB7n?~ٹaÆ/^=dYƍ;vr遘5UV&?ۯ߆vm3BC,_ M˗0t馴Ԕ䧛7o;wh' !Cǃ~wrhB:Ҙ΄7mb]H5!n׈Klcbڵm۰a߿_4O}F&N!uVw_[;"bV3^b<'F͈;W\ɧ\pa*j)_,2kLgg߈d9qĔ>\F ]m߯8ʚ>}/t\z5vԩaaayyySLr )oԨш#,X6:iҤ1ctЁ/dnݺSL7n\1]a@y>yw]jh(,)]v >n5t":|]~o֣GY' 4y^^urF<n!nD2ﳻBXzˏ9ŋcƌqrr t_ h!BHժUCCCE`Gϯn֨Qcر!!!nݲ=^Yq.E1 CtbQXXx_/]:aD=vf0`_d> ={`zu/~䫸g2 >9raP '!_p$d!v]-!?}!qONJJ֢Z~Ѷ0[^=[om(#/B?C6o 6n0aW_]eXfffbbĉ :!dƌ)))YYY˖-k޼9hѢM6?~OcVw wQb Frʾ}Hmc IDATx-ZXvϲQ1Y7h471dY|ԩ}4ҥFA\ܴ>ӫWFYꍛY>T!!cC:ۿ"fDv2j(8`Dl0/Ybl_/ ğ GH!{ nE -!Rv&Gdϐ )-5e;Dt;wz6Ҍ`t?2O=vӏ,;mFW#/BG`˖~}豚5kP( 5mvηz9sQ|'NBgvԳ6W]%"č]D$P"C޻w_BȐCK,.~Xu۷qzz}j׮3ٓ&Mg{Ypx PݧƧ?wޗEgP!]g[^>cBq(=^>qqqUk`Xʽ~H̍k]>7 TkyѮRyWɇ50!!.T "7Ė)m[I9` BH H H4E0)TU%CQe"qEq\ в8@Y!$_P`Gµ1<==}}}.\XJU/|޽{T ;wP5ժU.ZmE ;nݺ#G4ѣ 6̜9:ywy';;{ժU!!!h45Ϛ5؏">7>KKRªD]H-EQZVV3 P(,B\\\R|jlgmEmqqqaaaSՄ\nZ8KLL8L%[LȊ٩PO?Ȉ5jT \m999իW֭[-3P&_@(\0ez[P4;**j vm% @j g ƒ "0([]x(e; 5D`R30M@ c@1lGĊgDB b`?䡢ض2/C .yBaPƃ֮m?<۰ ezF~wśeV[RRZ~U.4t5>Rrѷ>ݪ lͿVj5ڔ(\0yM4YO⥿wk0uמ]X,&O[Έ~iW}aP!H\zάVgO knyݬR;@qTP>}YWkd=9!$ }5lHoV޳D:|b/' O^?wNZ4簑_jW]\Jn4J޻?̂/7EݺZǨcNA&Zq1KW\&d/(pnE |ʤ˾>qlEļfO~.##;>l~XREצu=W~MumزE,mYNN^}.6`&-3/P~.$ݸB%tZzv6KQ%^/ $l!܅D)[;uѽM&ʺ/"0(I!^yg;4/?i`MdddÆ Je-='۷q*}W^իP(^z^zeddHm2!=FyvR[Ӫ<<}OLHHڵk h޽R,#FXn]5=zdɓm<_Z9%B50B8.++k'O6M\z5vԩaaayyySL)7jHޟe&M3fL!fM`٨,D(Y!dw>I;·o츲s۶BU*-0a (ek3St:Nǿ]zM?ǧTLGGGFSJ>gݺu>\^;w޽{5jWZN:uܻwJ*fyi8MĶCgddEGG<2cƌe˖5oޜO\hYE ܱcV~XBѬ\o߾6Eeff.^Eݳl3A^\MrݯedYhNkšd.$i֬Y-|}}O:k.>q޼y?t\B֭[׮]~p>ghh[<==oݺpB ]paB=F(ZhNkP0;vرf;utMD//cǎٓhCZ+ShNkPp @jLe_ ԁ]6H H Ode١#ؙY~{ȷk3؍:b50(:JM /(ek+M[=wY}?m|ʥ}ΣutӮ=ԮٽLRvgRFFY=?\D2{꨽mPFO gSQ?-+ ތ8:vVTp&Zqإ"k!(D\0 lAϥ7,9C&CߟlӾD*#ADH y2ᦠ5ӟ_sfHέJd;of#ϦƎ ]skV~2^ >ۓ9h1Cg>-lvHݱ];wJ˂/&̙FtR0$&ƽOLJJ8eq䞯ҳ1Κ1I=s;1ΘsµFuMVoxpGZ sgkM ~ZgNZ syjLՆ!ZJ'E賩Z5ȳUGjS2e#/UO&rYu|GO:wp|;Ai$ suxXF?--3YMqյVv՚=A #t95=z$dHH|ʇ)k'$&OH|οڵ#T.ݽxv?`GOl/ Flv0aIStRuջBHd#L&[6ykY5MU.7noh?G;zGIS&h^^|/dڵC7?}xFg2,$9{ղ*kS:9/\|bRO߿k#F-B!n>uiKhF2 ]<="vŘ&r'{yek_b瑃||< !w:z|~Rsvvp)z5:PիUR#7--q/?J|=)ZM]o:9C 0KWj/&O6wy|%am:(*5-Fo*=D`P^Mf^\25))鋖oÛap'O׷ӊU4ջdVO~.##;>ΝZZG7X˴إsK˱Il*ni"~ `)33gZ4o`xi~7Bf|<%%#++w7͛umlE`BHqC%}mZ7L`Ӿ2M7k_X6􋉷n? O2:^ɿ޼᳜t9]ĵ>)Xj= ڴNgRG'֬M,ݓ,:|׮tyƽ[rR~5{_x}wDsLP̜e5ߢ:kG7?:(VJ#QtǞ>U(BSw\?k[7v\Y깈m[ !KEQjime.R.ήxi,'*J%2 (ZV]2mqqqaaaE'LV!/brFrtPp@j b(Յ+"0(:5E]%E*pkFyfk9B֮V¢:ʺˢ9mb?_ri[dgI<"QDT]x[tj@xE-}m16u "&Ze\N۶2ټT!bb ƅ LzOa[5DT]x[tj@m@f˾Ѥq`bm]Sf+[k`[ !_.ç[ע9!${j-洐b؆M FuݽoO\F~2UBȽ :Oܮ} !G]8BHJJG'ѻu޼}!!䇽4Զvn(pϽ1C S6rHambeB-,ڐE-wѩaA/mMGg']͈n5Ƅ׭3B"getWvGM\4gn}}#-{tҲ^` {Z}џw+Mkμڳ-ݶ4v6˲ ?IȪ_&!dE[7d?}[aZ; joF.GXAD`P,k*L&VV.fC.bm٨[zr)e1히.wM ZZt9!..Nb1gr{{Z/&-gY㸕|i&ⲯkwBΜQj>'!9z[n뢅S?7 /V}YWp[a9sغiޚu?xw?A&+xI 2[SX,MB]%-"6lZ5}-.խmrt{R]JB\.2a7Kgig1}Yn][WcԱgj\7wvڷkyO ΙHGߺU0i#|Tւ/7Dѽa֓?˳kV|bvMo;.d !d\-F ؄ɋ IC}5}yZXpw{?YwT)Ih~E鉵#%;)Z xGٴk/̾vM!]SYtmU_YmT~γj-Fm:ߴ~ˢ@tkRֶ .>`5pҭkkOO7ǖ-DU{Z\擃s|kؾ]S\Q !?\ުeC~S֍x_~[7}n{oyX;+W6iQ#QPtc{k{o%Աk܏dO &6*ok [DGԾ_-բi걽Ux=AĿx9Fv-f+I> !k\:aVI@]u*nkBȸ1SFy?E8@e^~[4mR@}Uį"@yk`R+\gϞ&MTz}w|"EQE)͛_xQH4-h^z^zeddP!"ymܸ1''СCN=_܃㸼;k?~PPPzzzPP k X"[tƍtT*onURܾ}bbbf͚4s̘Bt FV(Lk׮uVdgm j"Ԯ];!!n@2}oӺ?\xL PB(J.oݺD*4#le|^{zR;tS. &ڪ;R|S4Gw%1}WRDV*&~Pfȣ 5/-gȵ _usPɨ '|s3 |Mf 5"kA `N[1}EiPSYfgΜqMwǎwwurrYB@Et_3ךּs:Ǩg9܎ ͞={ҤIΝ3;v̽{  ӧY~XBѬ\o߾E;@QnSgN{*BD` 7ns>}uf#u.* 3BiiBH۶mc~?1 PI!0 VΝ9zc&0Fa"0)4a^z9ߓ"|HaFl['ձ40Jd 1&!C铧n SW%S`b>@%T|k>^O+Ovo@q=zӧZaF( Kd222^_d2LV1q8V^^ ɓ'Ν=z7oؽ{7qnnn2>*  Çwas]vmvvXPֽ!(uEkSLiٲeϞ=[hn:WW-Z={VՖu@jPd2ق x:uxxxB͛7pӧO߼y.@@PJ̙3 JgzuqϞ=G6lX.]˺ 5D`K.>KRիcǎgggBPJQ,ֲ9"0!"0!"0!"0!"0!&L IDAT"0!"0!"0!"0!"0)ʺ/!κ!t{mh}Yf;xbK~zbONȶ|فyB)F{/zߓW )E{0#l̶鼀m-rF !܆[Ϟ3aD܆BJ|D!X"#uBH,bT(Qd>Hɝe<^%[H H  ٳI&*^z}HQEQ y/^M JY EEE͛7oƍ999:uꔰ㸼;k8| -]tƍ]tQ*۷o7ݪRBBBn߾]=x"vZ׮]m5 AAA%+Y<(\u`kk|r=2W&g{*g4|tvʹl8/`8\EQ!"f͚9swޖ,gtB=<H H J=s>ӷe%?gm*~"m- WЙ0Fb4#*~j6;KgNyVчS/2#|ebň "وRC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RC 5D`RSumkYwT[˺ 2,w! @j.$!a\cG F|%c˺ xV^YwJW$(R8(^٫Vږ[+@ϾҶCJ_/YԴYs:eûUQgK/IҜO?A\:* ]xr0JAˇc]ťV|TDZ,kmRҽGΜ9mD`)5j0 q\r]2p =>*? 552gFP ñ6M,t8g@q=iDM#e2 Z'M|:wl^0aX8"X sWk4MLlciz)Ν_TNkBB(W.@i0O嗳gN2,-[B8q\;wN6]HFodtj|Qdj/[(>ޯ_uwqV6z=pg3m4OO~E1+j|Qdf^ !$##s#G84]TU>B7˗uh~ P9–P i|>x:||!䥙f>mٲM6={,"0ʩ[^~ Py'gϞmڴeKɋ[qE&R,[5!v[ȲFۧO>}Edq+@퓖UrcK`Xio!*{Țc^"۝:~fM6+@@!U\m-[VTk+c￯?z[Ϟ[Em[Kw߱Sǎ޼O?2r42|-, 6m`c+EQ E^3k@@Ң fE(%PBqiZPqG ~ Xتql5Ue!y;o ,g={̝;y(Uv9k*]S4:}e<;P,$fܽ}а,с mn1͋{g|7dܤ% QT[[[^^u;@hhptZr\ :-BB:X*Cs2HүQZ/H`UH̀@Dw^Y[[پh`[ZRt}}}JE` ډF`0,"|QGWW__wom|vA"W~SCcp?3Dܹ}GRi@HZt݀nF`--%ŏ=X,֌!CD">V#-,b/V~l_ }}}3s6IO?޽ATL` vJP$dݨѣA hb& +*ݹs݃L1рQROo؄HTVVn~nl0X)D~ɣq&"= P555}Hw]>HB/ͽKPaiz8(D7 XAPss')O?3~<:~RkrT-.w3dXSS3e~W =$PE70 ?xpgX$|~خSiRFƖ-[+++A5E: pd2x_>yDV)ʥa~]P0΁n3v\W?r%捯|E&S<=gzJ0qP2e ii:Euּy:ULh=6~_Y@ цXL~}hНRR  TuU%vႅk֮SՊ u, 2΂%cƎӓH}m3GdnDǏm޲D x1WyY" 8իӧOlwTFmm)Slll^z+)Sh{V[[[;ۅU Ç&l/ڂܛ{p႒T̍˯"7oLrr6Fݽ;x D'o %Sl'=|H޷$X"bX,.+Yw̙3ظw^Υ!ryAx֣FdA˝6}!hePz+%]lwe>}X,VRRA* , L w3Furv.--E+hy2A(boߞ1c&AUզèU79__Ȱh7xP1cO>8TP(oݺuU!G`~^U5W}QQQ;nVxX$'1 h׮}EM8zW^V{9}ep2///޽{Z5zZӧ BtKU؁ut>ܻw/55O>AAAt:oڴO>gϞ?>WnܸDރXŋ_v֬Y*n5Ƞz~~∑֨PQ[_~d285-u-i&9b؂m>~ܸiӦhTXfCq._~) itA0 sȐ!*Q ZE^s(obӦM6mڢEߎAF`AC Y;L|]#8N$瀂^A$Z[5gEMΝ;y7|. lnnNJJZj2[#H9t6gk hD(H2 è]]]9BpȐX{]vmΝ'NHNNPTT:tϬjOaƏw2%E,]ܹ_m3ٴ'<ߟv.!ᨚ|(/# } {#"PmBB@(< vUTTXb --?<|}޿/ϝ?pC>ұ=X~ÇD"QYy5kфP˔ %+jsss\ܡU {txIII+V549::HW,.k(v I@}5~aϞk׮+ Bu{"A^v'7>))E vѢSF,((XDZ];wubhѷ֣F 0B?F͸|2C/nll1z̙SqkC_EwR0,H$ɫ_"477*xȕK"F0LQoܸ72Dwt]퐜{gΜ)d]`0*mΜԹ*޿V7n1Zw1# v횇GmmmUUNf/^5j/^ Еܹsg ~#ׯ2~F+YXbń bXXؼy F`cƌ2eV?tPG4 ,@Ly/`̮^QjyƏG Aмy>$]ѣ{f猙di}DDܚ7/ٿ) ǎ|- H15U?bss3A"MmEBz3S/JVBsWwϷw;iammgC?\3g@Kn12xcCz[X{AW xi*]T[[[^^)N|ぃtttLfQ*aDz$=%Ev-bq{RFC0'!FFC^,{c0)|yӳw-?+ʎ,uJ#Y,j/B6 j/,;YS r';v"@gIH$7]uߗ!j%gj(dL:yD#0]!CV{7JՀP56zg3VA{Fڋ216JK %~/I7~sv0Q pmMͣG666H>u`[Tw@Q,COT{Q>ԁX,~STx17s@ hbKœK׵+'>7 z"&d&1224=?BGGJ&H$ҐCAL4tICGZ[^ 4hȁ} 0 dA͍=^L&Ba ,^/>} ("XZdG`_?*tOTVD"}al_~?𘢸ZNa@U') O>]*ҁ_D.)+- )66I{ @b`~ښj Kw\ht Xz5Ç~~%!2N0| &6&6^__NZ^z}W IDATbitm5~O1dsfDďèt3++ )Ϝ9 6cTcllܫW/GGNjH}֯7`ggg+h~ab2x= xeڕ~fXϿqIi/]8/\Pr68}}};;ǎӘK2Y&pS~qb`233_o~5}1cljbdϷ`U|MM0jUUZUU5JQaq3!3n`PZZ /]8 pS/?mջMb6|D^}_ey{{͛wܹh4W_׉͆|{ܒ0 333iLv# PXUTTK*xy@tȦHsV1bj7m8gϦ͛oܸyuIW_WW[sر C AP}˦O~qUaĶkڵ>_=z{׳l4o<䥜tcǽyq#08$nB$۴y "\.xu&9lO?!Ν;਼@fΜ|5)9yFFF :=z&IfV| ;héC7X"HtuI֣ ݛ`Abn͛_p^͘۳gΜMMN:NPfxs;'{BMo`AD0?;qDD)hUE*}w>}b__ hPrss3|QaQ\#1/lݶT,5i#ۿ P__on8EˆۮM7߿7axObX7̝;W(┓nlK F\pyMQCj!_! ~w_GG>I6Zϟ3gN0l=jŋGm=8C0΂mnD"p4>dVX,H`>0Tx-v֖gddCrމ/D2A4:pc gVwu ++;wp8.bUdTccc  ]" U]]]2 B/ "Ovw<|1v)rU_~P(3 Ej@"uCFv޵{Hݻvnٺm̘14Mc"(l.\ڹ!C r Y `oDv!: />Ύ*G0+(8XD~ɒ;C_jؾm7̓mq^>|0x,p,`)T覍GG8tHP`:aeKMHHH\ò:, AuF,t|oxUWW nBJ"퉧' *+/755E~l1#0zA...in!QrTTdPzQMLLn"SヒgϞ3D?,;vl0 -W.A|>l9F;)8=z3?7N?-3f^#42ܫW/333?-T^v`tY^H'&}?.[wD"A pȑkyxxl߾Y >K7u'} ?2&&vԨQ GtEpMk;*wusc𐡝xF 4ZبNwI$ln;CqDO￳W1}`]g1iNoE;QҐdt :fw-gPB׌)0,aaVt@ Y.}x_Zn 3*b ,HX"V|]od uR t0ja%w!@-:`idG`ROwWSTR/@J%V @Ӏy P(]KW9SfocLJ`+ڠfeff:;;hɓ'߽{feeq8:陟TCvv6"č*E \pJNN555Ennnt:}ѢE B60jli4n @wSLQ\(J\\*$VVV˖-{q% g͚Eӭ/_[ 7ndl6;$$D$)_'*ct/,,**d0̨(9z***49WZXXfDxʕW^yzzX#Bܨ q AW^mj]x1%%ԩS\..X8p@|`7 Y9yĉ{.ŒpGW׮]ˇZ[[/YDy'<Ç\.{!:pcggWVVpii=ݐdMYYYnnn2&,›7oN<F 2lL1\S޸qCFHRannnvpphӨD"Jd511qܸq Eرc_H\Y :yhI&=zkB&ccclѣ^zKKKkk_~)PZZb٣LFIn=qnont{._,ÇtիI~ܨ 5!6=|o`^~}=333gggϞ!Bu .HO3ghh8zh=f*))_+**\Çw؁H6olkk[RRlٲ˫h׮]4XBӧOٳjժǏGDDܹٺm۶iӦ|ROO$‹nە=ȒK.={$33O?tbj&LXX޽Ʀhfٲe_nxG0a<݆HD@۷o ={JlW!!pJD"E"F넡f{OU3{liajj*éBVN<ܜqJj\Caaa3f̐n4dTjrrrSSSRR+"|ɤILΝ; FF?. =$ɍ7+tVdI`ffRL!cUd2χaUzUgX0 w8dA&zqnont{BCC=<<6o|mDr}X,H8Naa!n1\pٙ| HϘaZ;wD"tF1`L2EPfʕ[l % 9F BdY(OjBX _^)=sIdFs`d2rdE~?J?~s#O?qJj!~?]>hQ(y#qݽ{Ғ(ǻy󦷷wo߾rppphhhII r~E:::2*0 ?{ GWkÆ ٳg(ʰa.\ߢ[v,K.uuu_۶mǏ?`Tw\&۷iر_}Jggg[YYuAw4˰qV<7zFb2pÂM͛7/Ze̙g޲etyP(ŋcd2Wv߯XR~qڵvppptt$ɸAgos5o^; :hmm [@I/s]1,K`D" J$~<j U"ЕP(L& @|t%|7ay*膀rUHMF`4 h04YHүaD C ðX?7݃ӠP(\ku۸ 굁:S__ns4mwEYYYN{zz#l*M-E \pJNN555Ennnt:}ѢE 긭np㌛]06HMs= %..NzJ<++e˖{Dh?nu36x[mkMF(j#6T%DEE1̂&ns999k֬GW\ꕧ+aPPPxxxqqqXXXpp0B\C?Eޮ uŔSNq\T`~Wu=4g4*agFĉ߿"w]%*@=N???˗MMMuuuuuu l… ?|MMM!с hذa q ۷lC"___CCC__ׯ㶮NNN4mĉGM\pL~dr#//Y" [BLO>t''ǏCyK.![/_i&Hjg&Fvލ8((ӧ)))in2{=26lg:P( #$$2?,>|8 N^p\Cl6{~mj~޽{fffϞ=C,---.\ Um] ,))tFWG: Y䆽Iff&~ Ж-[vڅچ?ݻyyyhade˖~://=zt„ t"##MLLLP0oMF`˖-۲e<---33)u֐oݺU%I^^ʕ+UX,h/^Xx adddRRȑ#uuu ԕo¼yvڵcǎ \D2i=8dFpp% ǎ[f n1\n߾n:hѢR9m۶[9rDO>m۶VTHGiccsڵcǎݴiW_}Elʼ ݤ'%ɚ1گ_?ϟǏ=zܹsƈӧ>>>Os\o 5Aׯ_g- 2o>~ڵŰp8{׿~577Zaaa~~~@۷o8m$44ȑ#2B}}D.%3Eژry*[kYZZ&&&ۜɓ&Lޞo>ƌ722rƍ[neX q !w2Bt5\ 6\r,СCWXM`u`ԨQ^^^7Fp=ne8AW9 nnYSh߾}7n;v r͛-ZAl"K.uuumii91P(/޿L&o߾]zLݞH/]9@t6[Ž3Wr ZAkkkpp?l_@ Y9zh^H$[<##y觛\= d2 tiz?+(=j2* tCx h9*$#0MF`4 h04`|9Zdh3gΘ˄KIa7@-ꮡ*eeeq8:陟:4c.33ٙFM<ݻ7VT*!;;0 z!!nlkkkjjtEJ 1M@Y"Bpƍl6fD"2owSRa?yoa0|>_;w̛7` |]N>`0 OOϼ6R"t'܀\~}֬Yt:jo߾䴝 t5kHhcvʕW^yzzXB3>u M1'''55p͚57VAAAaaa0**d0̨(!!kMX/:u p8lӓC(8+@W>|r"._YY9b ;짒€gg/^|ǎbbbCkXo#Fx&Xz 96,--ђT*U$0,h4L033[h%Pf`0BCCY,ӧO!9qDssɓ'QOpK*"䐐*bvvv… # K.p@ qrrš755eee766feeVϟۧLFInpܮ ,['ϟ/I&|||fT۷ ,@ؼU7d2қZ[[=zdcc:#V,YҥK֭[0,hHkPHa?~}Ə___/bL\?zxx9r>::9WZ(Ia8@ W۷'NDmmm߾} jE~ǞAbbb233mmmaW7 l߾}͚5ڎ:gggL ȜMd2g$ 111s̑sϞ= !!!G`"چ&G`0 xٳgK ebu椤$///D޳,ʰ3fs72ZT*599))) ɓI&1̝;w2 zd2]q㆓2Ί1̺:ЃMf B&|> í˨:B(CF&P F`Ҵ,['Y,"r<w2f #0&8SWW:[uC&x<ԍ/R^Lx< HL`31 g۷o۷occB\?q맒¢"ooo:ncc >\fЉ" 6,[ y\ 8pGEE̐vㆎL&755m\w[ZZݥ"vЭ\r˖-d d22ЀjǏ?zs猍v!Kү_?ϟl>}c``3DH&߽{A%By 8pv,EP͛ghh[\\ǍwݒGGGKKKxo߾rppphhhIIIaa! ÈPGGGFgϞ! у}V޽{CD"Q+# nۛ=6Jv(nႛuT7o5mPPPHH"M H1`@JRT)**R2rso i&J ~ŲϟS(6M쭮.Jݵko֮fZ_~PN6:b\ӧ]|hy'ODMK; #OhcwtVWW744[YY!BXt>:]]]ZZZ9Nj9rd~O>X?5O%["WڵFڥK>\$ 6B4~SND'N%q ơCx<^CCÑ#GX,AL]Ab\prE__uA&t2긆p;vlJJP(f-,,Mo߾455E%K.e2vvv/_ƚ \RR̝i oaxnt1UKuw◔ Bܔ+,,tqqaX۷oGub;!6o Lg0_}T+++KKˤ$N|?"%K06{`@~z 6 W.] pddҥKecB\?q맒B:ujff&b]fKtڵiӦh4KK~͍`܉U/**;w.d2s}捼݁qCc@x14667tc{f,EEET*ݽH^UuRsk޼wf*Xub@ P(zH$JNN̼z\Dr۷o+i~ TW! Y?gX߫r>n򌌌p)tY(l_$&T*5::}0`-;aE ZQ܄6`Wv.`-94 h04`i @Ӏ C H3gΘBͥ6F`4mwU7Foܰdeeq8:陟TCvv6"a0>>>B\Cm~w>]|>NYiӲ766.?~1!w TjKK y'0 bG"L . ߿ogg.\pϞ=B-t .''' L޿SSSVVN߿cccVVjeQQQ|>߾}dt7ʍE^^H$z]pp0 UV7ahllrĉQaZ،dH_޿ƌccc̙f%ҫ #11Ɔ`|72o߾f  +ȑ#NkkGlll%K\tI ܺuZ*00w"(4 i_- ۉ'" [[۷oʫww:hcq XەTGX.in0Nj={vI49%55 u椤$///D޳,ʰ3fs72ZT*599)))>ydҤIL&sΝ CZ= RXcc#z '''d c`2uuu5?Vd&ϐʱ*d20*Xz;44@ >nLP͛7߾}ܿE,K$SXX[ y M]/OK:c:H{ܹ*F1b2e̡$]WWp!tSKKK@@;rCXQQ7NIHH@&rrrKba&c";0Xpx<^}}}TTՉ;p_r-[epKjJ#t,4:t4<1a9v옳9FCf/ђ҄**7yѐֲG/aq: p}}=ͮUPȌ75͛Bbd,&0UgTڊ#0l28Xˏ;6sLd aoo7n,[܄MiaZ9?ñcdB^  7o޸[|ptt=9kJ^5=ztvvLbnn LKi  2G~։;p'cgڮ:BrIU#6 nhhRg]ܰŝ>}:==-I| t:"tuuMHHhiiILLp8Bx#Gs-774  Åaaa=*t钋Q,``` I(@`hh("##Q}||/g޽߿_z5J;vL(&$$(]^pTRPHŋo߾͛0888666>>~ڵŰ0(ɩV7 44ȑ#2B}}D.xUSN=tkllLLLDnmx񢯯ubbb k?~7oi|~NN֭[|@ E899!w&H+ڵM%9@f̘QSSS]]1k֬v3 $ GaXWH3[RcmWR KKK3f ;wniiiAax G 6ѪfΜ94mΜ9VH \ h4WW?-IPƏ/s]a߻w/2c~s>}f[XXDGG޾}ijjJ.]d2._5UisD;q@.Yc3gL0aذa&MD^^^ .l ٦V7@9G#G;yخiڵt:񈌁bnNNNdd$A7uT4%RSS,--򕕕K,a2xkײll/*Lܒ0 ?zh֬Y"֯_oaaaaaaEEEse2L&sܹo޼W'<6pKjJcݖ'撪tX1͋{gaX;P5D߱NgΜ9޹tn5*d! Q{"H*>n򌌌p" B"ۣE_wzҹmB#0ЕPAۃVF`[@_4`i @Ӏ#0MF`h໇b. *3gΘ8^(L ݤ%(4駟 ]Qwptvvh'O{."p8t:3??fgg;88PTlDXWW`0||| (RງUVrr)*wss-jhh˼GMYHGUIE\?[6ilBpƍl6fD"2uX;}t`0<== qRȑ#;w̛7` |[g*"e@?ݭ_EZ:v3g@rr" wߴ=SRRf̘*@;KNNNjjjaa5kϤ_r%>>իW+V@AAAaaa0**d0̨(!!HۄպxbJJʩS\.* \`AAA9pnHH_%;(ldFN&2s 'ݨhyI# A1(N4$5ʨx1L:DcBQ<VtѼ4Xos>]U,8(}`&99ٳgMMMGao2mڴCBBBBB*+++++P?b3//A$===44ݻ1Щj@}3z())ٷoߞ={jjj;wkyС[nݺuKR>|x|{_cǎ׀d ӟ&'M3 ZXX`6a ,--(CBB&M,YD$++kΝbx 055 |2=AO/<##Dekk[RR'O,YS^xR)av177ONNH$f*..NJJ@GyxxXYYvR4bx̙~-PL$ BC4C=mڴӧyll)S<==_ƙvz}ѢEΛ6m"v[n3K.m߾lǎ/^0,444::ð]vQ]"0EKKK &L0Ą~BEkbR"""֡;uꔫo[NV ;v:#Á:n…\.w…uuuvoiiϧ:OkS__) LIIٿݛo󓓓on?Ö-[G_]ksA֣4Ͳe޽KL&#=8ӦMJJJ UUUqDBp8: NrYD!E"իXJE B{Y^^~K$ ]ܹsիse7Ąѻ IDATyɒ%Zaggw;;;B0FpNoll'B$k֬aaaCܼy3+ƍuuusΕH$d_Ryʕe˖#躺j HU Y=O\1`06ie0@ >4΃͍_5fˀ@Y{u155`! b&NHibbR__ 8y!o566GҰ~'"O}cqƷz:K&Np*r={7mE;Bg4"RkPի^ZYY9RH${ѣG`A# C=ʇ2%nmmUT@LII-((]C'T$⣏>:}4ՐRܸqX,F ovѢE\.W"|gs`2} 6랗gmm-H233I:# |||2A>}zʔ)Tӓxzz԰t'79aRdf]MMϏ0,X\` 6L2eʔ)7n@#^@ F??ggvdik} 6.a3ͷO;pΟ?w^ uO@ C G^гbA@@@U7s#@T!@T!@T!@T!@T!@T!z'{8r3z~E8:uԩSi SD0ZFxX*0s qhhBWWW.;7nH&xFX #=yyy6lXn/]ZUUqӦM{}aLLLdd$Aee@ HLLdE.fg4Ά8?S!IrrgJKKKKK9޾eڴi#%TVVVVV~Bf ^^^ Hzzzhhheeݻw}}}?cSԀf4tC@w!Brrn?O@LKKœ&M |-,,0 0aa@!!!&M ,"sNX <\|y1fnnagg lmmKJJɓ%Kp8ȋ/xcccL2δK- uvv޴iɰܺuK!}\tiffffff;vx"aڵ+,,Ḁhiiibb2a„ &p8}"OHRUUUVVvZ T*UDD5:4tNruuoj{}vn@gE_ f19[p!]pa]]!} 'G59 pQ È_H$ZzuSS1qܘp6mZUUmWRRRhh(خH$8K$%tAt:."B (h@{hP(JݻDL} 'b8Tqz%A__^۷OՒ Μ9hrT*e`vq<>>wvvVV\V8048*7 ?%%E$%%zuvv^tBK$jG&4D7ot'ODFF2RqIII]]]T}߾}*****H$@\vs4իWΝ4rp^dVxAttt8pNP4LcooبO I֬Y"""hy:sss3POpvb;Aeeefb& ugnC :Mt??dpfgH_. '[_wnt w. PZZZbbb{F>)ʤKRżc!:::#XYYٌz18V n݅B!8a)!g- C<@ P*A(qth٢Ͼ:2 l۶ڵkP8.!Jb67e__M=ag YA-b]8uQX\x!RP(W\Oy mmm5 x9eZ)IA}}}?޲e˧~Jl\.7o-]3b `i2ԧSlŐцFp8,VGQFFIMttt|ANNN,>CE j/''nsE v"=P666W\j*% iP(YL03J2 $%y1 z6<ħ!##? c.å1^R1uTP ؗХRiee%خpuu۹8E>OIj|pP?9Oʕ+O##Cզދ1lxnɓ'u:]vv6maƇ%X0yDEEYfΝW\a:Mqx`jjZ]]=Rfoqqqmmmqqq>>>=<>>>===!!ŋCs|#GJJ:zP('Bݹs> 4gϞ6uh肂u:][[ۡC$ uoKKKbbjL>|}xqqqOO[B`gjٳgL_. ?8_|y}}=,@+#??ߧPVVfiiF>hrYDCL=^ bժU\.$[?~Nll,0N0>abHtArWcc GG .0M0 ](۷oM}Kƌa8555|>?66g\iNa8]#]h" pFN3gKaa!h8#ӧoذE_ nooohh8޽{1(@ c@GO@ FT!To!Q݅D 06C 06C 06C 06C 06C 06C[ĘO:5uTڹ*![t77,F˺+,{{{ ~ h+,,tuurϿqd2򪨨bqq3qvv...f*B Sݫs}+**<<|-T(_uO.f/FDDZR&:tE̿@Rh8#@vvӧOi"Ƚ7nB)QLeڴibxx?p֭ɓ'GpHNN~YiiiiiiSSӑ#G?&`4fh:g4b|uiϩ[bF> |8ѣGA;99ǨF)***J m\j\2|oY@3}$f^p8Atww;;; .p8{{jh7i 4,i08rᅲJ}}}`F}葁)+Vp\2 8$lllB+==|tC`pOMM !_L9Z@ii Op8===h 8|pu\ʹs޼yJ/H$1Riee%خpuu%n 66vh'TU93j}~r2߹ʊr21 koo///ߵkĉ'N8@󣣣BT*w`'c48N#Bq\#X+1mڴ*ڮP]UURA"-"jMMM,^2{AW&eggwww8q*B Q$ݼyS=y$22rh7i >4,<80scٲeϟ{ ͠9sFrT D;;;/]@z(Lz q\͛7'B8|߀(2Zvhc.Xѣ l֬Yڵkϝ;h^:w\ EDDЖ,YjAhACй*32|5G|FF8NVVVWWWffp4#BT&%%-]*d2r}}}O8ݝ-ZZZbbb!Z/޽{E ޽糈, `m۶]vmhC7hiRT`/ɪ͠XYYdkW8j z{{‽:;;Gzcu:YqiӦ? bd8XMMͲex<ެYg\>o4Ђ dxNNNfff|\. mS 瓨ϕ70Ą {wRP(_|y}}=i;eeennnnnnVPo"!HGt}];;ǏSDŽLCCv9uԜ9s,--]\\ ;θRh 3 G}8`3AAA… @;`#660ku:Oqя?X_w}V.'$$;O$ gmm-H233---k׮TJƍb1Uh47oD"h˖-dMMϧ=_VV>Q;O2#Wsg4Rw!'!H IDAT aS[:Fj6įuց{׀u֥#C/""y1Ќik} 6.a3ͷ211arΟ?w^ o8$&&ubĉhaXAAX0 T!W '_mĸU`5[b@ ؠ @ ؠ @ ؠ @ ؠ @ ؠ @ ؠ 1foB'^$qG5!!!7+GHH~IPa9uԩSGñq8!3@fNa<ꎎ>P(F\aa+˝?7XTT$xA|M4АB`N W^:uꐻa~jڭ[bX|dz_~+V|XO?SNzH9uPqhӔ,Giiŋ|>y&,?t}ϗH$jz2`e ?DEE-Yd8R $&&9\W]]a]t)55?MØHo"FY{v1{={6''ɓMMMjժJLv!ð999ރ 4 0X!H ڋٳҦ#GkqƟ~3fXv-a顡wAc/i㉐`LY%---ӦM6n"nݚssss``!.… ...Pj@ )K`/_\hѹsraX뤸pu999EDD3DR*VVV튊 WWW ֭[k. vʜogg'yW_;wqS*_|D"N:M,"yq8!f2uxX`AQQQOOOOOOQQѢE <'pzzzZƺ:q<..N xzz׳ڷo 9֗-''3?>(''3ð]vM8qĉU`:c\pt:A:"XiӦUUUv%%%|UU82% ꭾ)0 8.V^!%Ϗ R޽{@d'N =afٲew{ CHtMNɓ! 548dknnUTT;;;A3gh4\.J $00p߾}Z}dGf3`ҥK,^f6pZUV;l |>7 ))IѤ׮]{9FsUjOPvuuSw͚5lY^^~K$vCL[\.xdV谳;pB`wΝ6l 8;;777QFEDD|6M,"Htmp~pŒ'llltpp}ccaq|nǎ`E =ݭP(<N)))&))i ,99Y$''ORXœQ+Ǐy1~FRLJJZt)U˓d{ĉL___oY5=hzq8Lݻw]\\ݻS((({E"k0::z۶mv@ >4ΨܹskV nVVV D tttPT .2 C(dP0+0@T* P*iP"~\>o<oRb$Z\v qY'<<ӓZ=~x˖-~)9!ؔ)St(yxxDGG_p\>g[8&R* "11qʕy}khhزe 6555˖-xfJKK)q%yhqJ2iS(b}@/ǐ1 IZ\FF+J>Tɖ 4p8|ӗ^NNN0P%%%4Qávihh;x$%݅˗H- g8n ʕ+ZVRQ1+0b5a`dP̝;!moԩ^3g,..N^A8Rֺ"Mޚ|!w!t~t"N'˓-[C9&MaS9k4Ve:@B}7d{Cw }dB(Ν;;g#{ZZZOOOzzL&msѭ**55)))'O&[xZ}-T(ʔӧsZ/|HD6#:&&O>'ҞΣQQQ&&&k֬ٹs+Wȑ#JRR=zT(<;---2EZ-wɓ:.;;{@999:.''gΜ9hH~ 9دyzz{Cmmm~~~\.}>|r(;;; (++ssstss#z Y 1E=hP(VZrG3TB^N3gKaa?@@ߒ1C M1# iѱze$/\Ғ`Bcc&77W,D"^1`nձ>k`@#F!UݺuiiiLqqq׮]Ɗcǎr1~0777ik} 6.a3ͷ211arΟ?w^ mG18q"2770a9xx5 T!_;ڌ1zLqk+W+VC vjU J_7@ A@ A@ A@ A@ A@ Ab0η "^i^$qG5!!!7* o1C|# P;;;s8ggb4:::|Bms\.w7nbQQL&x^^^@-T2uhYYY^QQV^RXDbx|4АB`t W^:uXYզVݺuX,QQQ~zep]~}Ŋ|>_,O@:uիWGITC 'JKK/^|͛7TF>Pmڴi޽>4OC 11Q TVV 6'򪫫7lðK.VUUyyy}@-T(_uO.fg443BaaX{#99ٳgMMMGaoo/CCC+++޽ŋx%$$$$$288888Lia&L0 \2iҤ`\>撓-,,wObZZX,4iR``gπ [54deeܹS,555/_fA>}: `n\xQ*r\!3hHɯ />9!===Ba\\)B]B˭[aخ]XZB2ooXXXDD˗/_rrFIJJ#+//% )Az{{f͚E*JJ NveڵΝh4W^;w.;::8`ggP(XZBd :S. ¾dVT*KJJhٹZO 'n߾ NnT@oll4֗-''3?>(''3ð]v +T`'Ne::N0~FRLJJZt)U˓dRA8 Looo}A֋deeuuueffݻ...`1 Ǐ ,ضm۵k׆08Z!E Ν_SZMDoo/u4"Oʤ8`/&HRKKKf{jY ! y2(ؔ)Sȷ@T* P*tM$ͣM08֩`E(BWڵk8S?ЖЀSѷpL:T* Ebbʕ+?MdFi*B޻wҤIo#O<0 q}׭[W^^N*Ǐ?vӧ̀ [)0 a?q<ϴ^+V7>|D[[7n͝;W")+W,[lPnؽ{cÇ3xZ!LDGG'&&>~T~`fbbB&_Aܿ<iH/&[躺j#٠2Ą@=1l+n2qDhRܴiSTT&8X3W%粴dbccŋǏذt'1>lpsoMM y׌҅ N8AOG8q"ٳg?O²FB^^}!<Œ9в4,,p$e 3gx<4zzze2hnmmUT@LII-(((i<<<{zzE"ٌ ꘘO>]yyy$33xxxXv]"'$$ [n8~-vCP4͛E"H$ڲe # u 9~h".+H>39I3ҷFO2e m:P0㖖Tyfc&񬩩Y|@ ˗/gp 9T`jש0b@ ^׭[6R2V;vlC%(x~7z <۷> nooohh8޽{1 {c=E ĸgĉ#X~!Ɩv10C v͘5&Ù0v܂"?(P@ ~ɛzgbaA+PI@ alP@ alP@ alP@ alP@ alP@ al@ SoFx]@:i~s󳟇}k`<o*jfFڷ6\.w7nbQQL&x^^^@,..vvvp8@ Eb~< ^}-,,HP:X,l^CCjxn ʾY}egmm }ϗH$j0VuVX,CE-[F TuM6 'iilcHԶ[}KԶ[y-o"|ưaB><w ,""bժU2СC@jt# 򪫫7l 2IDATn: ^t)55?MØH &&& J@"B aoٳgsrrN #F)***J m\j\2| :::>z z'''jiҚA{-\&B%8nkkknnNm0{˗/ҒWXr]\\ʘV]p?|X,9s7|s!D2cƌA}}P(ܷop2q!lnB]]AllscooW_III"Ʀ0p}v_K˗/B˗/ ptt#Ĵ6Fgg'H]}yj5H]TZYY Ċ WWW}"9ݻwg͚s.. 2U| 4"sFQQѼy8H3f Ʀ4p8|IqqqӳE$s޼y}w׌֗-'vjjST*-/?ٳO4=i|(v0|׮]앝P(s̙WCbnn󣢢KڮǏ/Xl<o)))@|aO>eSL_}f_VVV"tY2͛3g4?  ;ҷ榦/r׮]@ٶm}]]e555{!/1Rav0L޻wo߾}aaaw܉۽{7mѢE?o )Ij`CC:0۷{{{WUUM4i83Ν;G(tYu:X={/Y~sisuuu|- sq///=c .;gΜoJ0kk묬RyQ0F+++ Ö.]:uTp*BQT 'OfDFFzzz/s玾f >}wW322?oXçi܇9fddd||{Aykݻw]\\ݻ|cJ2))iҥT1//O&A8q;33|&t: t:eY x{{s Z/ՕD袳dBxxC;;;ɗ%%%R\< VVV%308 j ^6](*  Ő3Z!ln@ P*A(WXtt mv5Rd_Vr)aBdww/AM5X?===ឞ D-[QSSl27k֬4pjZ^x<}"۷m2PQ/_,..NJJd{R[[p50|\4<<JN >QZ]f֬Y+563fZrXw!AMV]]'ꥸWVJjmm Ĕ܂dWPPVϜ9ӓ.XD!RLII>}<Dd3Csι;r0i$}W5FIHH E''TdXB3av{{ Vfx/hK a8vvv'Otٴ]?8DEEYfΝW\ Mjw'NXr%aVꫯlllhY=+Ξ=_$%%1V$&&ڒZ;v!O2?f.Mv_rmFP(t Xm/^{=}3|zENscaaaM0! 55uҥbŊ &`V4Ç#t9s氈R4>>k@~ |666n2ٶ` ܍hgg7LFcC~~olN &1ql',U4BDLna4Uu4TQU] kG7j@a NVJ`5K B $4B8ǎ}={2wǦvyu~c9[l+ҧv̙ZSSsܹȑÍ61zBYOIijj?H|&u _rg)n@k`Č5lm^~E$Y,˲,;Sy&BH4Q淿z7n8tЎ;J,65=/@S}d M ^0I#@FO ϟmѕqNkBB$KW>EQJW~z}Cz/f)TW>n#͒$8l6p,^eqv7}& tsb] Hw:np0]{}j֖eW,zD79skx*]#+,R@ +q&Z[[[[t;Μn[WP\RB)t<8uS+7'ZtiRY'۫V֬>进6oNАj$)DJJ2# 3RBq3]HXphȝ+*#0Ixn_[Rz˃En9܅(24a?|٣#DzSv&Nc$"^҇'K}I {ƭ&K ?.;wj۶ ,"8Ji aO%Q5Ie*#ii i 61͇׬Ygޝ L+#ү eo\$u!ći aD_lC1 huuS鸢F&EA"漼"˾eQ)ELLIdZbB;r进6oBSBB% \b6$c.$LhMSoj@& ~FFFTX"iҡfh;z h\Hki t\HLTVVN#0YQ!s!྇ v5 oXQJS427/34d6Շeq1w Y̛+*#U:yVTY4(cQBȍwH{/L@x${{zW֦R`~u._rӭ*l.o0׬]ruqR*W3dѢD }DPl!LeV#fgMё]U2doó_ܜ 0̰w?ʗ9J)7u~8gvvXeJXX vk޸?+/zכ';wHΩ_Z,2g\KAf9I# FN}68Y0=[cB@<~Vi}v焰/=9kf -0 7XXFDZ_Aq,2DXh>L.5G!B "S9:Yrd5/JFٗzGأ 7g$SQâ*{ƾueф rXEI ✜㚙 HB0$xNIX( a'7u07s,0u#Ű d it|8[Dz̄ N_#a Fsg|6f&PS`׍[s 9<2 PB/( e9k=%jf Nqel]"e !O-,b`U|ը Ɓ$xU:K n >h>O)e|/_{r{jn6ٕ6d,VNvg}UE&HjB@Ҳ Wj'$ʄ=.*4XC OGpZh2ҤRQ 2-q#7|ݛVYm~xd2 1 3Os[XwLՋ- cGGG ˲O\tI`_7LџDҳxgY̩{B "d_IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-zoom-in-3.png000066400000000000000000001632141351617527000237420ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx}SHI znA@z%T)WЫ^iQP"BU,X!!-aٖM=s]g̜UBJ%Y< $g3|#ka }{eP+F^aaaN!jBHF>sGB( J!cP5;w^nf~~Ztjڦ˲B͉3Y!/u(TY/KT* ENnTM+d)%RJ Nx qFQ(RATJ^*@D)5 L&JRi4bu[^O)* (qq,˲, 8T*U*Fm/FV-fX&)%m9sRJBI~T*qJOO[*~q?˵xY#90/+33[PJZnHoP1~/yƲ%KaY8AOM) RVg͚%RfIM T)c6sn!kfxc^nnN^ A9;YNW:oNVz^V{c-X1W5m%$GA~~~aa]W;ŋo޼#^j2jB PNJg-lJP)~0e ug~:0౺;PVhGQ]ϩL){)]nTsɔط] exAOJOog&ĥi4‚SGs Y +$g~_V-^Rr6|D1 .޽;777--iӦ%^)4r5!)SA s%3GOQB20c-{,@@ulhQ]Ul.N95aN8<)O %Eπ}ا/gܷ|#zFgJ T(zbeZVBk92h8j޼+WOŽ9 )/Pl88A\#ڑ> B8b1>E|IR %Kyv4*E> I(g=f%CW>>r1*`< BJ.03<}0Eu6!yRD)9l'f`bwIs ??_Պjx> I L E%;… ۷'fo.\ww|d%>!yXsOYEw%#@Pj!)&FbXKRu=eb2fnPcr84vZF/BTi!A[bYct?¥TFȳr Z7 0 ^[&d0&3ۮILZ5_{R! h4mӨۄ>wcO]"tiӠWO6$P2$냺7bH-0Z7 i(0%=Oܼ{&3.~#W)<b~^ Ù,,/~)yY9fP'lHNئ.<,w~5ӻ0[i~^?c-oVC߯}؄NN~'vT**)QH6O^pdcu rێӌws-.NOVզ{y ߝ-m3/IOϖ~@!^<ӧ]w/Bz-ǒ%g9Ϙ )-Beī6( Jh[wu5GdDӁmU7oXoʋ}^)zqڅZRp#烥; mHxV>sxxx I~~Ν;M&hX,8Q:nڵm={uȑb}L.r0b*dLF!: ,ުܼp%ϱjKh,^t////OѰ,˘z1C =ȲìHш7'ʳWm:Vd%>RkUjG,yА: WḬkU.n}Δk_8Z~?_&ÿ^JBcR_*PB)Mjjmy07:j65RQ^=w٫NG%_ݝ&JI8aL&q,kܡ;7[XX(~Tpknn.qj@L~ kdj<|.|hU̙3/(=4ZBHfF? XWTZg` 7s$@y90![\[/(nZYS]T~]Qqn bIFNeީM!Owy7w)˩elwnZuͻiRZNui lظq's2 Ʀ)jX`^VAd\v!>,>p]u?XǠjǍ{ur0h z-`~=Bw-[JеC^PpL̢F<ѷ}P9u>z؝z뭷Gx"-NPpTDe-[6i~hOC|[ooݺ&7۴qCNӗX1vҌ5Rx:ZwyoSm暘mӱc{{c,Oe w{J{PpJj֬el69{vu:n)uwcǎ Z|y zt)$N7y䈈J)ȯxX([P[?^~ݕ,"l,\m0^z3>^OÄ(9n*=^ŃZB:^e5dB=px ݧρV ݧ76~iYe8|Ɗ8|ko̜ZFJBByѣG;v숋9r/(N8qժU}YTTX4o<;;y|BBs)!/F%lڴ/HHHy^ja$t={,^xٲ7 7v|䩓Lrr8bU,[\B ˖-/ml"-!ܭ^K Mv)!mѪUw8qޚ2e ח|d͚6ntoRj諐:vqIIv`xjݼyK,d\ly&-ZJHH6BGڥkgI{Y]uIQnܸQ !aV֭˿J,q8#$$ѣjN~G)˛9sԩ\tOŧӧO),,6mH,~z֭njc}SLyWwNINNj<ߤIiӦM4ElJ7nk׮}rX PW;,,tkկ֫W/|]u ygoz86A>7Ӧԭ7&BdY> i$d!!;Ym!/B br;?ofnR%8ޥn(^QTWJQJtQ"""((رc7_>'$$Dctڝ7WabY ^j׾>o5n%DKVO?r]z*! B|LFBd۷oL¨۷wawX6jxSL~[mPBK}Y(߅*q[*}pߞ̇*@1 Us`YH*s>*ܪ/s\}UW_AE㵿^UyȥWÇ90!ëPBȞ] @`K]B6~%d!OLfl*$ܐ pO|ܬ30CU"Ba_H)U(_Ud`PHɐMbT~I/(sXՄtnLPԮ];((hΝ .SNݺuw%VHKK0`NkѢɓ'~W^@@0۷W^=vXnݺ_̞=[:uq\2**X-,+})S L2c ;"asu}RƍyEAB8f<P,OM&2wNNo+JR ?wߵoަ6NfOd. v$]xqDD}xZK~mСF166M6."qXC>¸?d( 8KlllHH^'\"ڼ ([VT[AdffO/++E v*3h V۶m_E$k  a7r'qxL!f`ג]<b׿4՗J6~Q ;f`$e @n; @n @n @n @n @n @n @n @n @n e_}ug5z:KlOB\rɝ('7p @n @n @nnڶm[N״ioF,T( BVw̙3R6O !)aUP12;wΟ?͚5?v옴RZXXfk|Aͳ7o/jؒ%K֬YӫW/V_[otQQQW\q9sxzzΞ=;!!!Tq޽{;0Lllll 6$4jP)![%_Kξz!qib׿4qêM(JժUuEFFR:O !ZPѰ,mXv/ !v|@3Vt&+:<]rrr C|||LL )9۟8q&J)q/^/BoFF8==f7oB CXXXqPu#;w)SN<ɲlRRĉ]T0`@ttt~~~~~~ttAl* 2dŊFO>**ITB8NRi4.]$$$ 6L׏? IT6y^< P(zwߍ1B׏5IT*8NVI![n7nN{g=<<@ok@5>>H$p0jbݻw9s;Sjժ3gϟgx20 SO=5mڴ{_~WUVDDDbbdu`P)  :q~~~>#*:@[jgV:N\uփ⋽z vm *H-?(1w0Նu~Bݳ'aرÇ?g]Ps,!dݻu9rdvv6B@_%5b-@ adgg92{˖B:0mK.i׮]bb"20߅;YgJ߅Qv-]<~ UwJ*((P*օ B.@ $~p߅QĻQ4x bPJ# d`YTT(u`߅Qܹ#kVVV@@}ytzȑ k^@yԩWb+ / 20.^p>oul˲BC"{{Fx9scp"b/\lU(o!![m3a/HF^zJaXEPzU㻐P͜8vӫ<X7/ʖ35@`NN`!Ub%>T?RSI W!T W!T UHsP8tC!Sf4']ܹӱS'F)uS*if*Tc#J*='wUOwZU\ <@9ߠAaڌ7p*M BZZjݺueA?B²2J z؃Mޑ݌byMXg /p LxTFc᯿xWODRa]lBH:u*:X[Q(?G \ɓ'\s!yyj~7 J@BX/?c;KIXX-frv-cvv !w3W!U\X(gΜZGξ i1?~o!2к3>wpVG]C4T ݧ/R>P[PR_wl,cMW3qaÞ@]b}}CB;w~oUl0h׮]ffѣGG]ѱ%1K8pxFfju_Y  .O+>XqV r*+3#9qߘ9wp?믾.KfϞ=/^lMÍ8yd㎤$??CVt8e&''g1pJxU*:* Ljjw(p]Վ?.))I*ύmŒW;~ ? GڥkgϕkE(*۷o5*_ܶ[$ϡ@5k۵XnذݻgSJm֪U+^*2Xg~…oЫwׯK Ҁb/jǎ{g!YYadfeB:v$~^ҵ[:nڴ ʞ a͛??dVfFFu־;b[o駇%]";9$$%%=z$3#ܹݺv|45˱.^X{Oj>K.EFv/;w^߾O4ݻys2b:s}ҥΟ_ܨzY?~||||vvvFXeY+9OOYfI_R*f,ӧ#??_ӧ?S>}TeY駟SҎ,Z,7Ξ=۾# !SN]n]~~#GΜ9S\N,yLuz]ҵ~?6J-ydֱcK)Ž;nw99M4!̙+?|kr\AYq? PR5o|'%xnܸaŢg~W:nԩ˗ =0m BHsGGPߘ1c1Νr{1~HL9$DG/'&\vVe:nذQZ O>_tӦmͽNbaĨJ lŊgΜ֭[Z;qFCbb֭[z9s5j$z~cǎo'!!a޼yIIIξh7|K.>x`2{믋;iN*tپZZBBB]ۭo>xmڈ7v:w4dOW,鬀g`'3xcU=ѧ0 5v"K,RXQNxͦw0dȐ!CLR1s8 P8YEޮM6O۷oW(<ϫT*Sg!eY{w-tm۶}#G,VrFclliij5 |QQQE(B0eX0N)R`ҤIk֬ׯ/0AAw_xэ7۷`bK4+e PGf?Iq%ֹso6lyaolCDqӢÁ>^o[bsf xyy1 .][0k׭ |Ʌ榤L<-&Jdt{3S%//o[6)r8 FV^=Ϙ>7={eÌ7˻wv: |fe={Fg*A]SP;ysAA#vlliXM.^6mz\\bqٳ'72՟>[գGԈuOO iX.\Hn{b3P*U&MZ(f߾}ŚRv 2Ύҡ{bw}dv]sα_4a! 6lء lΜU +؂LR|~8ÛJJV,_~޽6m=3pؚt+'TAO,X_* 2Ըqv;[9BJoq,[kO1e^/++K,Êټiì9oF儱Xm{mmޙ5q}Bү dE ;E*EŪPD[-Tт AP ***߾JwZK %.UdI’_fn&a ̜sϜܙW+1QԴhAo+HO['zCK3 'H1xX-RSvG~ `cDoPU~Mt wT[[GKK$AQ{+M*lHڻؓ4rs c{[PsIK hiiỶfG܍1f<[d l@ i:#FOmkkjWc).}Xp歱\\xJJ'{A6?  DGG7]7s#K<(b+Lc|M]z:@E"MT`Z&#CJE]xvw^;`GKob2'Hf4x u4Q9nΕ~[$MN;Qx(x-'ggT1^C[ZZutPĄgŮ.9~8.| AWWףʊ9>nPGQ#M&FCc/R5 ,=IGG{CCV:UCpז~GKKo;P'H?3 ?p h*\.4dn\.dWtuLzFlw3^[{R5xW/ܾ5}QF@ 'HonȰACw#"J놶̧LH(m8}})0P}B`1!<@ A*0kֈQ#9NBQXXZ#F| ]O!@-;@ ƆW_ݭ?NGGY3g1Y,VL!@xٍ8998T.GQEΎNEQT"xF/#G^9  @`l&&]]]]r\.\*bZZZǏg?A @ Yr9ԕ~,zs`޶ԥR  0A BgH;?q@ EGA::#Ы04䐈৯¨[m-}U1]R!B8ݴy˗/A ZP}*lm# Q3fzz{$o0@'O>=17rIL[zo5f8VnƅDEU>GM***'O{s ~PAaT~ېS:尭k94`H0eA(c0 snl۾=;;E~A C>E3s`?w ~EN5эݻIIIxVXmjRiz1wnuɫZ"l1ceE[Ģ>2˽$%3Ν;|aAW')"d{ D/VրKjjOeٳg|PJLL͞> dUrrξ5f씩nݺ֘~{1cDIE9r$JJ%F쳕UUUD!*ayk 8\ϋgh`9XOc3KJJ}"x.4005jٜOA3h1c' Դ֘;v?Vx44aTWN3f䌌 V}}gJ!:尅#GZYY%&LMM՘K (Y!)֘ n^^&~k׋f̜ՅmH$&<^:hhx+#QM EACQtqBMM͇1p;jwdP,Ns?68rH㚞W`n"aeN:&)68lt(++DQT*fff meAd2Yşcb|XG\\lؖXK>}"ؿ/*`]B[lXe+W`BAbEƣ6iA7FQ[x7QQ3q :?;wp-'/ٶuǧPI,刍gΜQjV` qVH0`"с%ؖ0 VV/~.6ϙc{ϝ;m:u괍m3v' ӏöB}}}5&G'I2hC>8|; {N]]r\3eڌ醣FJ/'CQT,[L1O~v/R+;=ߛWm?;NAI Ξ=b qUs@E|u8/~7~_~]as ާaZZZZH}}[+*+N>wq;jjjT;N_P׸qB$ދSǹoQ'qmٺƍ,,\]|\ntRMBS8v zo oLQbIބ 5Л%7332x<^:{YG ̄(:eԳgϚO*8 {&TVT7لg2H$m%Q<0\xm_ljj͍D<_-Ξ=dɒJp'133~:@nرc?n`` ȫWƍGJsʄ6yEQ|&LPq㜝:;]|3vڥ2`]Ą?P&5b=`(QCF฾ٽ˗{#IXovڶ3f0LDaP.ܼYri/(L e/(KQPRi gϝrvd$&ʹ1I'd8<0?Qy E4 8ZZZ}U/$%Y ++_~bŚ7 N(.`Mnݒd?j}ޒp@H$DD;:k9\vrtljjzZn(..H$ͧOM0_زL&ӓJ{Է ȑdT?IIVVVԣs #p\!::_^kW˗q-gg;^掇EE=F_ݺu+t/Vq&&0EIeccRi󦍱q=Σ(#GS|\]|mS.O#,2yoT˖y655748Ѹ '$88qm!v'77t&)SqppQgP6 BQrLLtH)S?~m }/9r3` r;/޹sgBQW}H$㉓Lٕakڴi,XX`.\(H?9P5wJ~ g{!&%'Ձ|&:$˱+|SܼgvA;vb a0{p[/|a߾(SNM>4x> >A~nzT]+{ȇ M&DܥSS 0H/1^7ƨZ 5vrHD%>w?(&Oщx=asX ?e\r EQ EP9*BQKAn޼AV`4<tG1pPgO{ +Ē.9*˻\Jdr9J3LWq5yT+2pP%!`!AЫ @:@ 4ؙ@ 磏I?+_@ Gcë>L|@ H+0@ M+0@ M-$O>h/ 28sxu{s`͞l6S(ֻ~1good2Ν[TT |>ruu-++Ä666 Ʀ  nUniiihhʜX,ʕ+b1:QF/ML`qV?7 e鉉Ufffk֬y u'r˗-Zb|}}zL&ۼy3xaaa+<-vtYiUT`111}ӧO59WQXXUQQ~zLxŤ]DFFVUUEDDbB`TB!*Mdgfff?~\ +V8 u\xnDpL=` HzN|;vٳg B,i\.$11qȑjzzzPPq֭)S^N&$$<X :t}B wO0 迱z1555FiҜT*wrrRb-;99dWΝ;JjCjSPk޼yW\Q26ElllTֿ:^VSRRf͚EӉ fΜyeL"-Zddd;ŋe˖19sܾ}lB}/jii`ggW^^-O4>݅;_{~giO\|Uͩ<Lu"d?_~166>&rOnoo?s q2@ђ==iӦ 4qZ~;@ ow܉InjiiY]]l۶ͭr# 2޽{{Yn]gg;wvڅmϟѫ$isru ,,,::߷?~|^^ܹsVVV,K q颢H19E7k֬?KJJțD"ё#G>eb8000::zwuunpA%KL4OlB }EwB"@Qd@4UU$/Y(^V2220!0Xp2Pb0魭iiiݻsp8vb*=zT=h---+W찹+|V477{066g)&ϰ*4M"(I\չ\P(DQT(8C!z8w77=...[nv&qㆃCWW\.f@ S__ooo_|I35A6ׯ;::vtts`L&sl6?V8l۶N{{{``3vX&)ɰeLF=-RTwbMѹUhWv@@z[/ƌ_ZZK=zȑSN`{yzzzyy_́Q5 ƍnEӗ-ԅsx?lsttLNNnooOII}'<{쫯"CVQejjL&KNNV_ ~nt[0UVرի044IIIAAAȐmݺu ~~~R؞"E+V92<<<%%E @{Y// 6*{ .jlllhhZHů!MǿܸqcuuiuuMɾK sΝ6mZII ~{f̘}NOO4iRFFFll,Uh# ڴi_SS9N722-: ^^^= #p}_|񅅅Ÿqp޽{9֙3gG~**,((033݁il~n&L:͍帵^!'֭[W\',Yd۶m):\V"i4;*((ٳgk׮'z?{h4.d wUG~9y`]^@ 344߿O@ ^;8Em]rT.wQ:r(yg7ofESSSmmmnn.V"2_dHJ@j>|5y*  C`@9*$@ @ @ @ @ @ !cх\ޅ"}c6]({= N @ şz*:/ 477{zzlOOOPLܹs0a~~>gXeeeƆ`Px  nUniiihhʜX,ʕ+b1:pt94Fg` p@|z1tzbb"q!$ٚ5k$==}ǎ<KOOΎdΞ=;++ ig gG!7JJJr9U&YXX֪B/_bXvvvwAd֭ϟǶ^pa˖-ag&9o BBB9] $NA jI3022"t: o6=z[oY,W_}*Ǜ8qg}֟/߿rӧO9s] >9 amm=~6Dy AyFaS Fi1cKKKqѣG9r)Lr=OOO]]]///9[! !nܸ~ Zt:}ٲe؅*L8k֬j[[[SSS uz]9pbcc{  y@2y0C kkkK.rI&AQg?>Y__ UΎfddڲ &yqe.\TH΁A ȰSRR|~ ohhIIIfff011ĉ999yx#HΜ9b( 0D"Qbbyw^Pٳ Au%$$DDDIRb{]]ݺ~`$<< ‘#G´b]WكٸqeJJJ355;wnss|ߞ~Af̘VVVћ7o޾};ˍ(ѣ5֦M.kiiik׮uww0 ]:ud2{`v94*a kkk}}}===U3߿y3g?~֭+Wtpp@ٳg۶m#fc{{s:jժ(i4;3d q-A&4>zPn@ AAggghh?h_ kGc+l!)S5JGGZέ655b?&W!!kNp8@ C?d((=j2T  C`@9*$@ @ @ @ @ @ `HߢJN<9i$pR8 k**|>b\]]4Sf3̹saB` lll MAA&lnndٞBB4D'tUVzz!./++srrbX+W-UzBͰzO?e٦Dׯ/[fxTDÇco`0:::P`2B!fbbbllrJ@@*Yfs\;;{aB>ر `K5=QC+b7nXYYa{dx3gHB;;; F;p@kkk~~>:p@KKK~~>ne111d(bsru@R'/_NܤrdPu+0//K$}X󶿡h R4!!ˋӧOJ(V^}yT?ڢOq%ƵxbLbPmnnؿP(Tp mτd? g/...nkk رnݺ`S1666O>EQ311Q*͞=S#Gl?A[[[,--Q ;v_^؁DVVVԀ§2Cd<<<{xxܳgX, C5\eee|r 64Y(*,YB*-##--- vSh>""b…FCA`޽;gk.6MRMOFW\Y1L?&ϰ*4M"(I\չ\.  {QCF!P +0"*>]r1D迓ud+09܌;CFa#D~D"|\.WObE׮]h׮]#;gBa쥰ݝbM>=998qBщ_mڴi͚5սD"P(Q~F.e;큁ā(m۶)SRdTFj_U` y#G:u.Iƌ_ZZKȱw1!F{ FQBwݸqo-te0YmmmMMM)Z2o&^]]]QQ(&RPAQmXqEVAdq+G!(`*WP`f]o:$[{tttjjj1Z2 RYY`0eE'd@?{)8qٳg?~\ZZJy< c5u֍3fرjމ2KlQ߫,X0bĈ .;o0v`.\&:@2C᭷ޢOPQ%&&8q"''g}n{ 0!0V,+''G"9sbaBGG>O!D\A);;=;;ohEEEDD_|AݲǞ +JRitt4.NJJ"xݻW(>{쫯"CVQejjL&KNNV_ !gsϏ!x٘rGdPeQtHfٸ3VVVy{233;::233?L"H Rc1ąvvvص`\&_paTTTcccCCCTTԢEyŁRRR_|c$l@?{)n߾qݻ.vtt466b6"baaq񎎎cǎ-l6СC"H,>|RĄD. ٳg6lK1v )Ձ̜9333S&;vl̙ H pxhPAq5?Ξ1c^tXݾ}}Ãdzxx455Q) j +V0LGGoIӱOe-}޽{(iĉ<$66TWWjhhK|||8Յ &* "/WWWN|`(e8=pL3¾ER'~'~uu5&\EEݱc~WeQtH^x1~'~mm-&$mC"##lG}$++4-- f۷ׯ^<Kҍ7lڴ 2httc@o{& &^ y0C"(((]ϟd2MMMKfJ'''6p'>Yrҥ,]ѣGvpC΁hii:{33Jggg\YYl}uR r:@  Nw)yyy.]?D._|9**ڵk~`46ϝ2uQ (s>MMMwO!dBӵlvbb@2Xb`0bccd+02l!ߩhE}> dHw+02l @ =@ D @ D @ D @ D @ D @ D Q}!2 ~~~dɓ''Mpx5K**<{{{&9wܢǨn>|>b\]]0aAA )((͞l6S(RTUVzz!./++srrbX+Wdu ϰ?..NA7,,lԆ^gi-ǎ{왂Kڢ".K<\t믿 )//nݚ2eի)\˛={6Y^__?ydja``n޼9~x qKHHxyqqqqq@ 8tP}}}}}} $&&9rT\?&}7͇-\*;99Q}0@>GYXb1 e2իWΝ ?~hMM5hHaRg޼yW\Q26ElllrguVSRRf͚EӉ fΜyeL"-Zddd[|Ųe˘L9sn߾MBVhxӦMtR\\)S~GAMM#ݳgOo2~qnn {.\0gaiiwݸqή *Jg͚cfz& F711:ujAAJJNeZ ;hIII~~~*5H$,Ke'Soii 666/^P0w  F{{;4&+0`>Ēo`0:::P`2B!fbbbllrJ@@*Yfs\;;{aB>ر '(+H^zElu +++l{Ϟ=2 osTZXXhggG6AVhhmmgXhiiǭ,_<&&F"߿7EL`qVntttX!`K=%U`(D%K`xD$,YYY|>Oh774777LOY *s -ښ ޽;gk.6MT |@@m۰e|@C{/1zKP W`(),is&^-{p v7e jY[[]]rjFQT(x&5 D#n:uիWe2X,7q\.S (3J 8]F3 I655O>Puww˻rʚ5k(& 0 BZ|8_~ejjL&MLLT kkkT-KR<=zaC-WS~lڴi 'I&a%8U`f K$O:O#KuR_U`*ohhIIIfff}yP'N?obrrr$ə3gX,&tttLNNnooOIIBDDssny䔝ޞmbb7CQ"""/ϟwppE tuuJ*Ih\hmmD ߻wP(|W_}EKKTL0 0Ȼgs¾%,,LGGgժU;vz*& =x`RRRPPE32*k > 9rdxxxJJ@ Py:tH$`6={kÆ 񺺺=XXX|*;?%*3lR(RA1=c 6tҚ7exE ۷=<R"  +V`2;ޒN[XX(\ptvvV˰ݻGI9N8LLLbccMuuuLJXYY]plr7w@.#Yc-'O̙ܼU6S04 &Jaq8ϕOAAA&&&,Fbnaaatt4ʄA7o<<%LMMWp8DE"QPP#E l۷-Zlh8Rtƍ&&&&&&6m/^YYtRp.]#e}w2Q{Nv[K}5U`UG~9y`}YA H_?2,=p<<B+02PAӃ!ȠV`d-2ȁo@ 4@ 4@ 4@ 4@ 4@ 4@B@3I:yI]Лv A ܹsl6] "//ޞdΝ;|ZVV lll MAA&lnndٞBB4D'tUVzz!./++srrbX+Wdug8=i >0N*2qN`d7ox</,,}lllnnc=/XflWWגeB@!ߘw+ ׯ__lxnulDuwЛ'l3g?..NA7,,lxC"=4 0 PqV?7 @<@?| lb@p!'O+_yyyyy2!O)WW׀n$%%% ݻnnn9&'vۭhqʕ={޽ܹswQ2..͛7o+TT`/_ܲeˑ#G0 !!{ 0arr2~9&444DDKK A###LXXX秫[XXH!Cw01*++._LlSޤUFSSS-,,f͚uLɓŋ3 k˗/X,!SSӧǛM:~<~x޽$ ~n>|hjjJLMM Eю&I!h&&&+W*lvxx8˵w&ǎkkk zhqq zH^zElu +++l{Ϟ=2 osTZXXhggG6AVhhmmgXhiiǭ,_<&&F"߿7EL`qVn`ŋgƅlQH$"Ŏ&0vҐFǷYZZ{aaaDNVKKKpϟJ?-!2L&;/^,X,ﷰ .RXWW7{lliiYWWL(YjUjj*nݺ`<ӧO19ؾ};e(vvv޾}{T`(ᑐ J=<<(i4ӧO| LLL> 9D"Q$J8(*ɘLfëz/3-[VZ^/3-Aҝ;wb+TXpppNNlbbB1Az"(>>~ɒ%DaVVdz-##--- vSh>""b…FCA`9::b»wΙ3ڵf=->=>8[ZZ+Wh4(؃1~ FÎN2rP(qF)Bq}-*0,y=vҐF&:QX/rؕ"1(k׮hk׮jqqq"H(,_\2Q---5kT*V'NPJChWW_iӦ5k6mR^XX*i#R((G$(*8:V n߾]e>FuAAA,X,Z3`̘1ѣG9uꔁ&w1!F{ FQBwݸqo-te˰ aUUUp֬YEEEնH$z;oˡ(bBR E߿EWdAF q{i|`}DNɣf ]pz}hkk}Ԑî!*++ duuuxݺucƌ;vl`` ~'>YSDGG5:E贵 ݻL&sݘK@03-pD}BjuYr?\^^WΘ޻wO?Ɩkkkݢ*0[M8 b8)) &&&8q"''gxK#HΜ9b0crrr{{{JJ ϧ aDpFA);;=;;ohEEEDD_| ϟ?࠯>=tuu1RT*ƅIII_?{ gϞ}W~**LMMdkqV?7 DH$ T&]*UTTL:3 FEE566644DEE-ZA/^8p %%%::˗=YlCD"X,>|0U& "rΝZAwH OiiiGGGccc\\;3 111fR.H R)r09#{3g̔dǎ9s&z```|||```[[u Ǐwtt;v-[ܽ{U̼y󢣣]]]{ʮBzH'P觟~z)!HQ~?>455/Ӌ땩geeBP}EE(:ujĉDIee3pvvP:~z\HκJ'''6w^Ibf̘q rXmmmׯ8qĉ*VU =u谘N@ ҇xxx #Oc+l!)S5 ہ;n~T[[ Ȉ~@ '''g]n @oyB >V`[_o@ 4@ 4@ 4@ 4@ 4@ 4@2H{؇  9(}AN<9i$z)(([UT`tqghɼ<{{{&9wܢ"LY,kYY&,((a0666ӓf{zz B !АʝܪL+==999X+WD}yR `!Fu@Fz+_OPd7ox</,,}lllnnc=/XflWWגeB@!ߘw+ ׯ__lxnulDC0OǬ9 {XXXUQQ~zLxŤ]DFFVUUEDDb˜S^^pbbb(@ClYٳǏ<88xŊ|>?..yyXW pd 3@hwQd%j0*$D#UGџ (%G3.HDQaAqcz~;Tn֠S|{=t.d`#l~nH "O>ϯ޳g{Çwdɒ%K-^xŦDHB```XXXVTTt͠O?Щvh@S3b:*dgHLL'm׿SRR$ɠA,XSB0 0BK,4hŋr94ԍoذA" <8$$̙3)ȏhvvvsqq! Ǝ{YBy3\.qŋ!!!|>ƍL+.vvvRtyyy NNNο Ç2 l~nOEEE@@H$ČzݻwK$QFτBf"m<|Ĉ999hgg=tЀqzDw:ujXXU իWM2ӧ׭[gkkkkk~SNaaƍé.8\N666VVVVVV666\.ה)_~}ѢE577\ɉjÇz^Ѽ[BNÔS6 7 Ɯf|ʔ)<oʔ),Lѕ6~P0 =G,;::.\p8Kp8Ç/..JHH #R)ÑJdK.q<ED2sQh@QQQ"֭[(8RҒKZ7o^||FٱcGW2#ܡ09s$%%iڄKFbZZZN>Fl%#B#Tb+WzǏ#""R>IHHhmmquu۶mknnYPP@/J qѢEǏjϟ?~)CD3Gg̘|>MMM...;vpqqQ(,.uQrss#quu2%"D$}پ}+W%t5552b.~z"&8vѣGL3 &s@Szpppbb"qd 6eT.u%jio\w.T  6nHXi#ݲe˴iӐ{>)ʄ3gRŬ,LFTPPU*UZZZPP!v]iEAF֋妧͛7BMAG'b>jKK ٳg8 qPDё|Qͨ/3fhpokknE"qR((ZЂsOT`BPT8T*{?;5e#>BJL&Ov . @.}ڊ>Dlljڟbݔ!&d8pá. ,Xk.RP(͛gJDiy Ǝj?J+%qӡq`0qÃE4e}QLECR1:{ԩS;:AP ϝ;際]"VNDgϞ`v1ej3 HKFs'r]^ m2ỷOee}>pf/d8Gh7\ğq6lQ ۗ{{{ۅ>>>vff&4N,4 )"4<;t'41:JE"T8b#5*//@][[㭭.V`Yڛʥ|wU`fT*:Wd'''BLJJرcѣ|>RRRjujjL&c:(^j:;;[,p/))ٲeщϠAZZm\\)zxx$''S$ɢ œ'O/_٥]\]]ۧRRREL1l~nˡCzh5tQ<,BɃ$22>۰aùs S[; >~رk4[[[V{niSgo&fv3&##C8p`̘1ʥѕo8@ ={vEE, +ܞ#;;ߧq뾾V </88Ed1٣P(ϟݻGsqqٿ?91h8)HbΝ;]UUUR__* O<4r???Hm۶N2 L-3 hLr ]"݅F&O3:|qrrr S>Bl߾](s̡xmmEbjW^-b5kzqqq8ŅWٳg BP8{첲2S"O|!R_K$j矧NR_~IF^FbŊZSݳRiZZ)"g-JǏwrrrrr:x 9M+-- r,u9] AuC_dž8#UOC}{HQ arOC766VVV8qb֭EwϽЍ@-z `i4PX, T`*0Kk %K0Ç6v@{bF,-D׾0bY~jooߗ/̙#̙P(z\NNǛ4iҥK177W&BBryyy,"E!; H{Mboaa?_pass3 2 GgE.))'FI<''/,Y98paÆ1;w8ql4?R4""BјcNJ$D>&AO9x=g-S˿O/ Ba|||OYYY%%%+VXt)!>}:99800𫯾"UVmݺ[l`)" a#3 +W?~QQL&۵k̿ CRd@ p'OD".]$ d3sDS丹1Ç˖-{իW BBbbӧO󫫫1 zbF1-9n ՛Y^ h2eٳgY.Gpww!1jsssizssP($rN;wܤIXeIh͐rU**ӓE?,# U`\.7==5--ϯ+SQ,_8T*fΜId2AAATTiiiAAA,޲Liv˖-ӦM32h{M///Pi&@"2cTTɓ׮]{… נ-ȐBF/_5 FQ& !qtt$?Ȓ8Fq*۫kx񢟟^'+07|p@45539"T`fGNII!= &888PWM$1rС fp'+0PT*qW*=Z?#dwH9eFfϨ*v;wCBB}=4#dwH9eFfϨBZ؃ꆲ _snEDD,]ƥK#!88xʕH3b=ubذwqꧡ+++O8uV u7&>>ʯW kk뗴0رcB73 P2a|2_O Poǁ'sX, T`*0K @})]@_eLnGa4,Yst,Yc_;7xGv>xb/^;w@ H$ ,>xذaϟ.'kjjQ;78Mr@ ^r3qbΝ;O8A:f{XIJ?X H҈FPJ@ONV_"##g̘]VxPXTT$ {ڜ\.*))Ybw N>\\\W_Un-[DDDx0ʺ 38tPuu5\rEEE2l׮],"a[FFƴi:Wd`#CL:L 999nnnEgHLL|i~~~~~~uu={LLOO߿ڵ#G.Zð԰7o}Dc77SNYh}%K,Yhŋ/LX"_[[;|pj ,e|||޽{!CPnz 3pz; ݋a9F~3Ç8WTTxxxXƨVB:ܹs&Mbe 4CǶ3a<)SΞ=K\Jq\Ryzzϟ?jjj,X`K<TTTL4I(Đc?~+WHIIOOwrr>|Sr'Nx")N2eҥ+W4e:2Ooo"bKnܸ 61BBq7 ?<{lMT~7R:}4Lrss'Lr)YIcɹjZVN:Ք#0\.WV86.# ***XBڶm#9Z_FZ)(*/#-ðƂ7Z[[[[[U3>8zgZ>+!!|LTJDzkj LCG,;::.\Cڟ^ **J$y{{ߺue2فT*IOV5k͛7懎X,r^qDDD'P|dY2jjjr9ƶؖ\wwwAhhѣGZ\.6 l۶Mtpe#annn,^f6cF};l&7$$$!!A&%%-ZtqV{yjOmmmquujkk~ѣɖR#<1cN8755رEP0aÆ+VxzzN _re]]smLYD&bڵk 3D˪*777Bquu25,2ȖgĈ\.eĊ ɓ'ݻWR);w9s$%%iڄv+DVXsssdd$K@Ӵhlٲ̹ױ|RLHH9s&Uʒd1((**---(([) nٲeڴiC&MOOommMKK#?ݼyK(nڴ<1ŕ+W;v+YCՎQQQ'O^v :1N#  3~TE8Fݶ#8::PRlPDUΞ=Md";2 f& &HdʓBRq\TE"r""}r 4 48paUje`xњ5k r(s*C2+<4Q2%2 :y$qebj,X`׮]JRPϛ7԰"ojRr͚5ڈ&Κ5=:%%8OLE$lI4LpYF35MU` B"466e/+W!qh4Ԝ۷oL>#NT*eGB2 j.k7SiJA^cuYghPTVV۷> 8} f g I9sãT{"ٳgqݦ!hst:]ss33+&HfV`LO:i" Fԗ4؋r{ԨQyyy Rg1Ĵxff&̤6.++ohE J"/M>}Ԝ 9MsȜSdrybbYON9&Ma1\ hZb[PP3#tn.@;WQ2S H]l(?owgݍ_JJZNMMd=m.**999ɉ233;6d%?vF9z(g)" (ʤ#FtsZ/lZ-f8lٲ?7%ʭ⑑666}ن Ν;q4 `.3=ȯL4iݺus%.2,::ZP|HމOdz!ry\\U!۷o'E BZ6n߾sngee999IҴ4ڼbjW^-b5k FtBCCICW?8CJ_-H~"#GJRdBC~dztBP(Ξ=̔!dIǘٍ1bŊ@ 2e vPZZ/̹֭`ĉ,a:i 4Q=n(p~xY0K.MIIagΜpB8[,^7Jwo4Nt.7]4tqcccee'n݊aXvX[[3/;;;+++.s^IMMmWxafѽbA^y^購\ ם^9j\oŀx}+*0K @`i4Pe*ԼIՓ IDAT>h4F%Kznne dɒn|iW]奝 ,//ӓzzzYƧN4g@0gBrrr|||x<ޤI.]D2"2Ho"Ґ{jooOꅅ|>…,"uLDhZ |dHi]G:`Æ -mztoV"H$vn4g͚Eŋsd'aÆ?>]4+ɐ @x#T`Vںula:A||P(,** =mN.geeXxr;aONNN... ꫯ@Hi<鞩]^?cFFơCI}ʕ/**dvb###g̘8 CRdtבXqssm/^;>}_]]gIII'LMM +**yfPPЧ~JnnnNA*K,YdIQQQQQŋ/^u 0+++ ,QK,4hŋryOKLL'm׿SRR$ɠA,XSBD-RDF7l H JKKCBBr#GtڍSNy{{x<77N@dH-/ ~'0 KLLJGKHHprrrvv_/^ 7n v{1y3\.1xرgϞei{nD2jԨ+$LL=zx*O͛s`+Wtrr(//2e Ǜ2eJyy9ԩS<==WZE۷oD|Ç'7CEEE@@H$!EKHUz*!EEEaqpȀL08TXX8ad>}zݺuׯ'&Kݺu+###))T>ozF[oСCoݺLS3bQ駟|}}y<1%4llllll\nםGFBw|0KR#J 8zg >0b@4pb… Y<%D"[Q&8p@R5 G%i pjjjr9ƶؖ\wwwAhhѣGZ\.6 l۶Mtpe#annn,^f\zr#$$dǎfgwLoxxʕ+bpppbbVMHH&+(( JHLLL vѣG=gΜ$:E?~\՞?~cB۷'&&8}vșx<"3ft|>ߔKJٳA8ӳu:@ h7Ff.X,vqpkwŒ'Ѳ͍P\]]:?MdFzƵ{w HK0`ƍĉv*T }W`8+ʄ3gRŬ,LF.2Lݲe˴iLL,Z/.ښG7o 6m"_H`ٲe7 QQQ'O^v :1N#C  3~TE8Fݶ#8::eRlPDUΞ=@| M0+0PH1755D"Stf6tPD(*JǕJP($3 Tɖr|„ f7:5LD"*\pP?ȖȀܹsg8Gwa鎬 qCn:d ãG֬Y_CS!gԡ52sႃN8Ǐ1 p81o.]T9r֖PDz2! }իW߹sC>z͝;B;vKǏ/JYD ÔJsf͚!7hlڴ ;w\lYW 0Ӏ|dH{%8QQQ="bfccC/o߾MAӜ^L """KJJȎff|8NCCCLyEX[[#U"##;7Ʀ ĕ\z.KK&/^xQmm3Kw#F\.>>yyyTydc&,--={P( g.++3e| iET`z86p^FҥKSRRk@;;;b}7=ub7pOC766VVV8qb֭qO>u7_@.;,pWZLycgwx7 xgb, T`*0K @`i($ޏzz3"ܼ,W5zRYg9B>pNihOGirrr|||x<ޤI.]D2b^^'#Ħ9s9s( i4xȽzۓ:rё"uLD0Zl, 2 GʾY}̙ӧ|''ŋA=D˲?X H҈FaNo%D"$Y0f͢)xҥKO~ٮ?M6m ЋD][vUkVF^e˿_<,|Ұ Cm[r󋊊d2ٮ]zht ˳JJJVXtRB<}trrrqqq``W_}EVںulABH(dzHCHLb322:T]]MEG䀑3fp_Ei )^3[_]6rEz BYlݻw^:d"u>}_]]gS" III7Gh"Tz…ҍ79r4OMfGC.F`0z^hjJRZ[[[ZZZLBw|0cƹ\Jq\Ryzz?i݋VB:ܹs&M"Dww8^QQ"" 9MZ3d)S={&"%FofWTϟp8cǎ63f̙3gz\?>w\uuf{nD2jԨ~i׮]Rtȑ׿~~~"h۶m](j Aa&M ~t'Ozyyq\WW~˚ f/ŋpDuuu8]bZVg_H]SyVmFCwQQ! #߼ysϟ?B"3S5ĉ+++m١i"E4;:& &p\"GImT*s}}=рN8ŋPPQQ"/_rmwWZ_g0t:FQT--JRTgO>yqգʇ[a7n&`D"?Z}їC^bgg'"##;ڮO<-[/_DO<0ɓ',)CdС|Ǐ?ϯsmBD.:K&\reF2  ҷnƍvZWWr˺u낂JKK7oLbaNuֶmnܸi&bS޿_.%IM |dH;fnݺiӦ4+3"##ˏ?N(rYz=7o&dzLGh<~N;~xkkkcc@ {ѲL?;v $Gƍ]Tb䔞VJ޽{UUU9::b6saÆ1"e˖ 2]D'ܸqT3;D Î9;gsSÚ?M1#""bccīyw<==sssO|w+V  <?\~=??g.--ݺuիin3Cy󦗗P(ܴi@ b|,^T&$$̜9*feed21AAATTiiihǹ\^q\x<Pmm-[Mf=d4h\nzzzkkkZZ!"%-[~Sn@ᴴ={ۛ8y@ MMMSg.G8F&D"BBtFҀ|dH;  J%J;5yk^pٗ\JdzH2}6ꨟ}Zlٲ"Dr͚5!!!8Κ5=:%%8[t:^|S"ud)d2֭[;>uM6j`4 hsANc:y$qŋ}ы/MF&Li9yD,H$bĈyHvjXc?NFr٩SZr.طoIgU*ٲWhj5qT4<<< a̡N D"ill4 ܹs:%hg"ٳg,&]LZbLQCW!_ }}Gb"eڍظq/&LXvmTT(uLӯ/PVV7P/WP`$Dv*$ Sb444Wb.33sܹ}Qjj*qinUGǤrybbYOhtwwp8۷o085*//v[[)aÈOs|@V`>lU)M%%#GΟ7KW!d˖-y{onnNNNvrr"ĤcǎQO&cǎi4G|BKIIQթ2ED"P*III#F^j:;;[,͐?ow A:jjH#99 LP(Nb~/d0Rs|\\\:8@ۅZV%22>۰aùseE담;f̘Λ7ðδԬD?cHH7|Z][[?vXRh4r|7MP__3}tS"-shS/DdO>ڵkZVPc S"asmhhؾ}S{T̟f^ ())0*$$$99y̙;w\+++ 4Vݽ{7u ^1n8;66]^;ggg_luLO,Acc#/--f0Ĵvvv#FXbEmm-NI)SjWbx͚54Hc]@7lذsg,l};:! Ϟ=stt6lyJ/++I*Qt֭`ĉML-  L݉߬T.\ðy=}RVDcq*wkKC}}rh4FhlkkΞ9sah4\'Nغu+O% 1/ Jk ^Hj؛ mčw4Zqx ͊B a>:²Ғ6dǟxNes`7וs o^zFyώW!, T`qأʨG'` Lcn@FDx?[SN== Mߦjmۿ<cmM`![$^vaM?͛7oTi*$aIDATðn9}ڷ F.$`!Lcn@⧩)8x8 zK G_aN8w ߅,D17B_G cV`666VV4 8Pq]H^y໐:@5)'O<c|q^_lmmM5j5].$:]Hx0Y}/e^]<ȑ66kTp {MgG@3ݻ[j_wYXq<{[~vt/sveU~d'vc1fV\Ta9} ^B4t9ԭ-LUv?ՊGM9ޖ:&hB"}y^((Ϣlh?;`VV -o<ݗWj8,C+0'n{mF#Y[aܪNء|3Twx@ǸpYAywIUf`6#ԪoM<35T`Uth' Vm6 >~o -:QZAyתvmV6*ڪFmm:fݏkgw9H;7T 5Zua8cǯO5/S ﰇs`ܪi36Qf_\oJk6cتm=xҌ*011׶zc'Yc֢iS ZQfqkk΀Bt{liռ9R2#֢iӷ oUiy9o+UoJۦҶjۈ]j}_٢{9T`w_K+_ ysRi -6 1 kѴTZà6i`Z_" 1A?96s6}˷4,x3r- p@a_<3o/~sDZ8[J*54|2p cgσYiC{[4dQ+\l;K^N|A 0['.WݼHf0l@?o rzі;T`}Gv-$ ݿt?шQ+^?\@?<xxP ?g'T~UQomllz{*s`z”Uݪ)aV%a N몷=,Mm~kykkooF~;vb?8kIENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-zoom-in-select.png000066400000000000000000001654561351617527000250710ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxw|E~)$zh!;҃XY#*E#<"J{"E"tQH @Hk~ ,ɦyx񺛝7{{*BZ&T sF=C ɺ^8+,,l۶-!DKȳI%!@>uը*Ǐdy{{kZj0 S&@T;z9Qx&dם2qE|ijJ2S-!$"@9^ Rh*NS# H9bpV N#<FAAPTts"vPRX8aa8y{jZt:___tZfr-!bXNoVxAxJM>P5jvШZէlVŭ_R7^Rk/t z:J30H0xavaXyДJRZv4Rf-hZ"-!xA`8n5[&kN!csȵYSCT!Y926cYN`lV7As,#<إ9YkVy{ؾN\"tiܡIi&K*k <[t:UykdF0sݵ, Φ6YuZu՞ dX+ Vf?u<,t5zNLUתCЩv3  mVߓ2smvhozW6lB4ӶFUksI8|gc$o\̕ 'g/?[7Ч7.CtO< %MQgPuBz-=kE9Өr}5yNt]\umi.'gI+9Ғ*:jjµ[9 j=d !V%hԂC,s.%g`[& Z>~1+)ݼt3ӳYHF r P׮]*>|>8Za-TI30Wk9ձcGپ+me`v'حf= sq73j_zBkWBs\vxcơ, VsQ<'OԩQQuwyי?/rHlVR2åY 7-0}t7q\9R`jH[s5cSBUp? R]MZ-b v]6![O+tUصZ^-i{X /d2$ w~&}p0 ͊"bG,c3GhV'N <׿9qx7:Fz̼!CDg춻cP9lyq߉˙|B^ q,cʰlxrZtI!N]Rӛ˰\ Zf k|JekC8P KImru  =V*mq5JηZaYn5jazFW @jzK}F$ R{u eY1zȵۧO{-HҵڔYEyxX~ wK=Gɲ̽Kx.3;MM'p;ҳEzRJVOΩT*9vg3!dl^Ph9s&!5B+'G|6?r)};4kNY9mvls l6[I/HWwxqV#9 2Gc;~M]yXƖm[ntu\~wO=ւN_+H^sb//(ݡ铒v vR?cz ̶&Oef9VXuoӠb+a~<lhI_o\v3=;?Q?\ CzV^yě1pcס~bRpoڧcZlŤ4Z.; ͪZAgF}rX}j},ccɝ~{ NaBF:kvVx^PS?#>xuU*7[]M3α؋g]9s$=WZ rw_G;]jUoooz zfԩSGq_~BF*BȦW=e[5ZۏmQDzҫpֿ5*ceWJ[^wHŨ'O ΅YczQ_JM&Z>l^Xn}VHj/////5jxROTfjŧ]|/OxH>>{HPFӨQO.tXtرV qcFng}0M6 Xлt"tc Ûcߌj򊗁IN GKKH1?s&%%ٳw |/xতRPu`kKA^>QNNNjjܴ]xyy]~MwGQޥ;v\jf[bENy{tF/{}OtZhT+Vرcq;=?)((jgۧd^yWat'y o9V-zzLL(^|5BzyI7s~m˖-M6QQQ{[n4 :u:}4-ܶm[Æ Zmxxmhavvv>}Fc>}L&BَT>}e˖!::://O,_tiݺujl1qƍF}]u֮]}eXmm }JݞVU'! URC&*0(8fh=w#[!4V=׮[Y*@%V1̌G8ij%; v͝1ci֭[lb2}K.-,,\dILL -7nݸq>MlG.^y1cdgg0 66nذ/LHH8N\QS٘]uwĉ/rذankǎ[pk[zn*+CvŪ ZS !^|Bb yUJp[[]U̸~=w;vlkW2֯lXg! zѣ'ZRR~Cj?P10(xO7o4"!!A\D%}vXа;;v\\k-Z Nt`F ~mͽx㍔ZXn]B=tT^=Z0uToo)S]vTOpɒ% ,h޼8l1JKK{7lْڒ.XعS'^_^eKr$K_> ->CN5BjFnaFZ+(;bU~Ï? "5o]d26Nb&'B!d;!w[?Z){?uu;b_z/vf}BH``l^ԩE~-Fes&|+:7n OMޫcד}i9wO>1w*!iӇ=;4)ڼ>8iVbb23҉GT*h|76mhO=}qQF ^xaÆ;,RRRhS^=1W-tQժU}}}|uؚ4ifټv7$(Ν۷oOӦM=S٘l#F,[vb/Ν;ץKg7H_Sxg)7vl;]mf ~ʲ…Ή-]~{3f̐ :aLСijҕHBn _#{HhBx4򎄄:4mڴSǎ;v쐭s%G?n`0~q^e)>>>G''Y?~[a.W#r3Arss'M4n}'lӧ&L;wnaaall=l}Gի͚51b*W^dIPPPbbZ}gz9kРAzznjGc;v+ҹsgBHrr  1jș3g^rŋi X`IѣGZac^vZݻwwݱS琚Cӧm{'"KJYH3!s)b˖o{9Bs=ʼnH*$seWcwjnRׁN;{"=9hd/]t)M8fB6mN| .ܳgOpG}Wcbb;F kժE]%%%ժUM!իWvBΝ/^h6{բE ZXvW_}xNecvf͚^z^k#GdeJq!jJ˥/Gs/<x`k6f\h4_~%_:uT cWcwjn(W*bߍs+V_._-///|·ssrrJ[ IDATRSS|slq{'L<9###77wZ}ъ+WfMfxxu,ڵkų ZhlP#*''g޼yQQQ piӦM8DGGř游H7,ۅ n>ޚ0;vakIIo ),,lǎټd7SGļ;;v;{ UAY[7#""vnc7֬Y< >87݃B!1l.ryBGH<'CwÇ?ǑѣFdY 6uqn?vj̫إ,X++U3?ٻ:uہ{޵[7zO?-((l!C'[U-[ 9pƍw.9sJ BV^t*U|qqqfllŋ.^8g7k֬y0!(Jh8vѣG9sܹ3 `ժU_}NecoE. ;3yʔu\.]hsL>=y ]yZG ~bHhX o%dp \:. 2{/bC}睧yƍ7 (y:HI/ !~l$dЍ~+~2a~-qںu`uv_~N`PpzM6v/xs?v.Kvn| J@Et Jr '~x''3\wSl2Ec`YH(s9**/sX}EW_A瓌Eu>Zy>x @iB D 1x$\~6GaZMYHGRyxJ Mf,$Ґ( pO|Ҥ(*Jr.ncn%T0b2^ JJB<6RΝm۶9sTVz?#֯_?ФIÇ?߿FAnOIIYlȑ#YqƗ_~9etܸqF[xqLLLZft<B|}} ŵhd[5_~)uɔ?w\c`P)FBF>*ARSSA Sɶ,Y1`{d2%''=\Gʕ+z9x` *o FZӂuAAA۶m"[v,P20!P20!P20I30ܑ@ ~`rN0 P[\X120"wDMb0Pz\I @i20P(bd`HJ0Pz\a*FP P4P20U l˖-M6QQQ{[n4 :u:}4-ܶm[Æ Zmxxmhavvv>}Fc>}L&BَT.uҥK֭VӧOl`0DGGY]Z S PzGBxGlSߒeߧ+(hRYڸqhʆ'[(ۦll:YNYJ(OŭCzf-[iaΞ=[XX`FªUܹjرߟN0aرcǎ}뭷vD$`jk_iӦgϞy^,lժՒ%K ,Y2uTO:裏|MOͬrp&(}J6rH>_Xȷ' Ǐܹѣd e۔Y&ӳfRj"e`jZlP3//ˋ>nٲΝ;m6ێ;ha/_,¥K4hPڵk}vBV[XX(BaaaÆ 'NxTTF9$;Zm(%Tq30WS@ȷ'[ڴisƍ5eÓ-mU̕!Ϟ=PsΜ9#FϜ9@ kt:.nn e;"DGG'%% 9s^h4N2ۻI&G˖-+,,/Hto>*G-}GI(wPJ)7oIT::5*2. kZ{?.kʆ'[(*f "]F!77wҤIƍmٲ?O'L0wǗHGի͚51bDqX ?~̘1pK, JLL_7<ʏ2|G˾ =latnjT ;vW^ys΄dlxVQ`0O/^ܴiӛ7o%zl6 PXX(,AN(٬\L\ÐՠAХsS۷oo߾NM&VV0rTno4Bho"r8vSQ|@vd tsY'O`VZ>hŊYX3<<|ݺueڵpРA-2͟}vD̛7/**X;g6"##j ?~ڴi'NtM_ZbPzGBxGzd<8OeW5*.L>a)tlxmٝ$X\\\hhh|/]D ƒ/¡C5kh5k&ݻ^ݻl P_lWkL͛bMJUv?ܡMAhݺݻKb|!1}Gz6j?ާ P'tX6?o@uRXZ/Fv.mֲ[k.5W'0m.D{7xȟkB=UZwš ZjƤWq^z}~DOm5ϏXs=pJE֩oڌ&cv<{:wUy٧˼ I8ط}ٱZAp3rjKb \-F\,n-idw{l~n!0~P|3,n9N $<cK| uED䪲Cw!w'dxwh0$ !]eq*xbydF~S/Nߠc" r+MnN¤lT>vmi'ZEͥH7-Z0Fdz2q<5d"P20!P20!PZ2͛77o`04lpڵPRT*V۪UGBc4c2R*Fm۶3g._>MSkzpO|!P20!P20@rƿGO.nP[.nUYd:}YjZ._%*m /*Jd~g! @i @i @i @i @i @i @i @i @i @i @i @i @i @iڲ##!duaB"TXAA}=xBh7HYدۻH!aq'B[ e^KLi~¯wGl/\K7[;[VB{CF1>εѻEKk}aqŝkiaū4Cl0-\צ{+!dtBHfaS_]urBۻwc Oѿvv04T"$8TeX_'wQqDGVp,ꖮw?eBȺBX[5&'xh’IU)m"TZB^'!c8+ﴽYPa ŗ zw-a'JEx0B}|ܥv0>3)myExh ]~{?;EB=5zwB?nv_]-"/ ~ːztB!!Ec넵uuVoUӳώI3ں/i<"žK%]n8F*!V"}Là+eH=aD?E\˼ }&.tn)i,, : DNHOnɄk !E"yn#NW).DZĩKuPW 3l{,Ar壅",ͻ1F:dYb9OߧEt]r.o7IV XEhTЌ̨F%%]xz!aKX( !bgYe0qm*;}δZwCZ⠤N7!tQqOCi5v?!HVva]m!xg8'ZyHlVB|10!P20!P20/ۼys CÆ ׮]K U*JjZ:zX(]᩸s9@W l۶m3g\|y~~]8 .0&&&&&AxT ϟ|ݻ5kH K.tM12GƍDTY_Ri4իW]V e˖{~9_UjՌ`BHzzߦx,iƎ{aa_}U7;`(FƎ3g={tSyٲenZtCV'7o5j`x'@Ex@q˲,ˊ?Ozqڵjg10(w8 z2l0k_7 820(wZnժիW"/n߾CbrP`=u֭ÇKʕ+'LPjUZm0*aY6##cСO>1c̙j{M t:]Y p%>;O=TV""".]'Ni&AT 0(w޹sI&͘1cҥyyy&M믿N:e:: ʝ}?uֽ{ZlY*U:dX::'MT~}B̙3~?~Сe @@^'4k`0lK.7o޳g?߽{w rDBЧO.]B|||H@R `(@JP20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20!P20iZ8+!uqcQ ~2Ҋyݡg! ~`Oy0!dӁ+ٗ8|VfFY{r@GJ`PdJJJ;w. IO9y02PNN>,$2PNN>,$(g! rQ4Fs=HMf+v;X .aRSRΟ;k2:rDVW4"d q IDATNz:`JOJe``3v.B=EVt:PցTfPJxq=i_Ԯ]7ThJd`6CEiӶI ]׬h6lQk86.=|w7;u6`࠼\EH%@^ !fs'j:quvbVmrKYYW2c߿?!d׮] DO,Tn =?vIaR m3f7##Uqu%wPVdNWңgϓ'OFawFR@^xiߩKFQm^p-/_`uZ% TVfFrѣFO44zMMMMMM- ! ~ZAx Z,Ylႅ;u[t)-Է_~ZҦǁA~Axævر`4i?<7h&M9rD.^W^jB0F^?r+Vh4FbŊ#Gz7뺚=z qr;wեKk׮BV;wxZBjqݺuo.Z]o&""h4=$=XWd?{ΜkծӽGW : W38p'B2jծEiӶ-ߡc!5Zٰac<n߸qSxCfefd^rŌ3h;\x{aMJ6&NTz{D]ӧ|ȑf]MBߗNy{ڴN;$ߐfX3?d8sܹ.]:;O6WǮ']ѣ<N_ cׂ ;sfq3 3zz1 0sޓ'OV'O N0k׮yyyL0o߾ٽ{0aXa?ߦO lO2Ź# !ƍ[re^^޾}=XL\ FC|{t7-ڴisy~nB)ASxg)7vlRJ AqƋ?ڽ{֬]&bѱc֮`07O'$$!̿$a->sķ9b/]Y?ICBHllС+W^Nb0yȑ#q[m0&߼EKעFGWcbJ**آE=کSk;páC6ol4NZ^=w5O?ȑ#IHH>}zbb/:/:tx'WZU(7d?{fW{7xټX=s*U^reכܾ}摑4QFΜ~m :+ `ĉ.]XϞvnnZ!!!%C`a./Z-EPPРA  /$CMN\ "oyȑ^z+~JqF?{g5ZkmYQJE(JQ*T.Y\*ZP*;(*تZĝR,HKY$2vif=<<3gs$Qe mmzݡǏ/:|B*oSSS\bŊgkȁҒɤxD&jiwEQ\/|7|L6jGd={v?yҥK;wYYǓQe2wޑd{pERRT&;  mhhx٪U>o'AYٟₛ7RP(<~8>CllOڋWoذ;OVYɭ$%IeKL$_鹹uj(ޣ#RSSWEEnnn{PhT*=q℟?vĉRBWYO%Z[[ߟ HX+tpyGO*~wWovtR]t_`!Od (;zV+NOSO5OMMA/==38#X|]x>UG`;w{z~9~D=gϝr %QQcǍN9k_E@QT.G%H\---Lf˖/ fsOxU|` _Fw#Jy9A1 ,νW3ZxxVwmڴi꿛@(JYK_~=f̘:k_~N0q ><)t*::|0ra$**ˣF}O?EEE+VZbHHȢE&&F`'OF`*}u%.j]@(PvJ\ŋy+] #3u1s: Y]G[{^ǜ4y˜itCի }-A] ?h_S"Ru1vA]oB:Ń'hoo\Vz* l&cxC{}~GJKn1xШI3hi~]T/xcya2s ݐFBji1cLƎӭב2 =n,f9⃑ڽ\yuIS  Twؾy}@ Hߧ5WUjj/y`ꔩL0&z@ *x~/N,GGGQE EQ7/_Y @1/o4&&mmr\.\*bZZZ#Gd?A@ %mmS-r+c;Y߻cim۪K67Pa샍.Nv t([hPGgpdF`v_Ĥ]a{nJ}`nAT_;ߏP=٧6nիA }E [B)S_~gROV tĉS?q9r;6bwp!QQaCsX ԣ"nn>NyyTbU|4TN9l#,Y<1TQ ,]ef:M۶eee65{/@1+(LQ 9'Oy.9;;R kwIϞ=ۻ7|ɒئFH*wYJmQ,: _FQQ,ȸ/X@bСC޽~q: cu Eч՚={*@bSṔegzw&޾}ݡO~w^R$111VVVfXDQtȐ!_~Y^^Nk9"wۻ7nh?b?I*TT p_n``0dSX;tʻC1hݡö1r,hh èЮS&O}3DO'NaߕBnua MIIјK (Y!)aF7ds999Og8 ݡî_1yvlD"1:C#ׯׯ_1uuu*:LޏL( C *++?=ߡJC }pa}H83c- =U$lXhщǟ?{d2EV(JҌ cccPLQ׃ L&+Qdd+y V ` >{*}B7oY}-W\"a uSR6n R. ovUՓٳfnHnG0۵f}/~ܽk >7o-|EO; eg2e)G,pӧMǏQÆ>'!lkjZWa~fB$m]zGgO|.`杇@ԇr\3~Czb ȱkrAㇿ CQT,KLiӌŋO4yΝ %}}'2hCYz0ݮ]UUU?^{G&H kmN}GZK $ICCF人,R鞰0"r )I*_b%uUl@m˖|e\io绺a0Դ3&w 1FT_Ѱ"&uz>بriG4He`<$0) ((Qik;IPu&Ygފ^w'胍-^<}0gK\.or9*(QP;)67Pa샍.Nv  @ G`@ Fqv2H@ Rlbn@ dQW[@ ң@ #0@ Mx`gϞ>N\6|s` l6](]A͘ɱc23f̸q&,ťY[[3 kk뼼>lʕO>D.A|ܹsY,Wuu~dM6x<ڪ~Hw`<7w9nN]g|#HSRRp"##ٳg,--]f&pBbbG\\\VZ ׯ_Z^^ Q ګ6N:qa@,YRRR󣣣)qIPPмy: 0 PqV=7 =Ǜ9yСϟ+q%<_MKK[vq/[L}'^xQPPPPP ۧ~Hor`ߖnJ@7Om={lPPO)F%&&z{{V"X,ݻ7iҤ/_؈`kk[RR-٩_'Yj_`_8v^yjSge{KCOyOq A;vhcPΞ?nhh ћ NgAAAWtAgggly,k$?0B 4F_vx]Y?_}:ٹs'ШAC؈^rg8NCCccc|Ba B$ mmme\ BEBa3J! 0sgsce˖k׮a7o۷r>_ZZ ,0555vvv|>իW8cS c`-~Ckk+>d2ǎf?3]֭[Uᴴ;99a0LL-d2i9P6|Ep +"Ζ;FѰF;>X :ǧ+hEyxxc©S޸qԔڨH$z[;ˁjii)(l8y[Uy뭷!.9Tr`(DƀgΝp8QQQXYY\x1??3X 0ajjjjkk4Ө>-MOO%UUUťL&ϝ;`Xg4hйs Teee M@z3ϝ>Åӗ/ɿtZZZ|~79 ʼnfff0>>ȑ#yx-HN#CN-[xzzߺu+>>]x  eˮs>5Q\.GrT"kQoݺuwK뫪Ξ==J@t:A _:|C^*@ p@ p@8*$@ #0@ MG`@  @4 A h,$@ HvEvAۃQmOMp5t:|e@/g.eB@CC;vww =m.''ΎdΘ1ƍ077X,bLgmm`0( *JKK044Ŏ,S,S[L`qA  į'NWB♙\ӧԕr77 L{HǑٴnF7 S92E`DFF5k` .$&&>zeժUp!!!@C?km"k:u*##,Y[L=`i_vÛC=\Aލ7\.$>>~L{Ȁg܉啟 G x<ҥK_x BȈ[hIKK۾};<(++|2:u]󶶶L&sڴi]/L 8k8> QXXhgg'˱2ܼJzꕇŲ{. [l9s ܹs7oF3@?31=̾w}d``~z|N322i2{96022" t: ڿ¦:;;cgXWƏM@o5h=/֟/=xr'N8 aee5rȜlӧ---Y,)uVWWײ]va!!!'NqFaaafffhh(^7g&fʕgaa!yH$:pLW,GDD9D7 [L<{?\r֭[򬬬mۂ7lذm۶n1I nj۩zۙLÇ7bˆqUUUikkS:Eڵkǎ=`! s#000..N.(f`1 ׮][nDdРA ~m zsN4hPHHȷ~ֆ Y,ָq&Mtҥe7ovppOM<#mC rܧhԂ@ h1:tP"\r?~ܿ]WWi dC1bÆ [|VТ-.c©S޸qԔBغ.sp8(.G BqLːsŋ\.w̘1b@P}v'#kjjjkkʾQrt\RUUU\\\ZZd2>LܹsV 900#0d>悃kkkbqbb&?rHvv6qbeggK$'OX, oB! H?nܸNy蘕ҒebbCQ4$$du zW^r=}``dٮ' 07ccc׮]KQ ߳gP(|իinn󋋋 J򺺺=ָHpppBBp0E@0:rWx*06l655HNNis3fhhh۳݋ ɓղwy'""bӦM۶mrQQQ@CX_[ Bwڸqw}rp--->hժUnnnF&Lpuu￙LfDDD @Y LܰÇ*޽{7m4eʔ#Gb7oٲAϟoݺ+VppphiiysbNӿp9Fپ};q=!.9]ysB5A_J @ }@۾@ 7B'L@#*^"ߣ/cA >A oA > @ i @ D@ #0@ MG`@  @4 A J_t/ _ځtѣGnj.5 hO,77X,bs999vvvL&sƌ7nXY[[3 kk뼼**ٳ owSSe?>~/`٦Dׯ/ZfxKr`l6fv|ܹsY,Wuu5nnn9:P_؅ =zj*={0gffYc~򐐐@LpJJJ8Ndd$hʚlYԩS< `ɒ%%%%|>?::d(eȁY܀@#qqq/^((((((._SS3vvO5vvv>uȑ#LrroIIɽ{\]]N9]RRRRRՅt9t30 iiik׮?n߾=~e˖!JN@u±k]G[)AT b(Gh4'JsssX9::dWΘ1ZZZ>yEJ+++ !АT(Ԛ9s+W EfkkkF[MNN:u*N'2e˗1@ ;wn˗-b2ӧOs YFx'^x1::t?V޽[X0Ν͍Oa`wrDB=~ fE<f͚`0fϞ_aBr44-,,8991g0~1aee38NXX9&666lmmKJJb;;;E}||v؁}}}։G @?``BD"hݺuD?1ry]]ݱcP"g$P977%77w֬YʺLKK333;vUH FCС(z޽I&|:t(//wvvf0޽;׾2RܽoE%2R/**ڱc#|hhcǎ}&b=zxIڊhkk+ɤ h4cccOOO@@*Yfs\[[cB>Сt`I=QCkb7oZZZbK.ݽ{L& XɓR4??֖lBÛrssY,Vxxxcccnn.neőd޽d1 ƹS&auXX'ŋ/&nRH9wwwr2tuG`{H$%K0!9o{'J<<<ܹ3i$ᘘTbٲegΜJ? J:%L&֮yd2hCC޽{ͅBc@o&$@?:;;'$$477 ¨(l_ P8hmm3ERiLL .zuui0u jrdp+|ijjɱ@w1P"۷o_fC',--2Cd,X$&&f:w-P 233|>9E(*bbbϟO*5==955v, 4TSS2ge`0ҚRSS ݛ>}:ٹs'ͦ('xH+Wbsihh `lll gXd&HPmkk#.\.;.gpQ8:.rPYʉD":2 #09Ѐ;C۞Fa-D|򉲘D"\.W313kh4ڵkȎ'@?XI&%%%a31GVtⴷ_7n\r%$@KFGGD"P3C!>~F555\ԕ;---NNNĆ(:__߭[)SRdTFjw:~8~A] :ǧcu}www]]]`BSAF*3 Ȉ#6loug-:h"===rL8u7nTTTؘRT2\QQQZZ(&RPAQmXqEVA䭷BDGG[QeBqln t_ҡ:u*$[{~ USYYI V`2-c7G-d?%pѧNzIQQNxjkk3]vOjfAAСC fBعu1e^^^>{A;wnذaxyCwܹt4rR=.u|@##Gdgg9m_kkkbqbb&ƊbeggK$'OX,LҒ)@C"(>>~ܸq\A1++%++/hiiiHH˩KvAtuupRT*VVVć|={BW^M!)))2,))Iu-](rUύ<>_wi0 0<(RRRpaYGQ!iӦXZZb.dddfdd|ǘD"HXXL222p-v-<#̙VWWW[[6w\A^|իk#Oe7m6*'']v]ZbEQQQkkk]]vk# nmm=t^f'bqBB˥ UDYԩS֭եh;BLc@C@L!:4eʔNP\TKNԧdeeMUWW`&`z !!:@-Pd&%t:vTV\ٳ(搏9LLLM...vŊܹsdd@+**Թʺgs iFؽt_։߉_QQ )WZZjooroߎ_0(*$/Yl…,\ʺЩsc Erqq)..Ƅyyy ://644lwwwPH![ʴ,,, qyqq#du ΀&jnnNOO'zhAAyi4ZPPׯo޼iii.]t2 /bŊ'OJ|[[[ F ojjeXፍŋGFFJ${Q4NƛIaaakkӧOQuss;{,533X 0ahllp´ipaZhO>͛(i& ccc,Xa%U6ЮͩgY9s(s -Ԕ ݻ7}tsN6MT?x*TAa؈^r8NCCcccNJV9YFI$Eڈ˸: ( .7M! 03)e˖k׮a7o۷r>_ZZ ,DYӀʴ{uV|d2ǎf?3]IY%8L&3**A,۷kPgVVVb&MM' .KѣG+MEIU(vathH$ ###/^N`wݺu+7hXRcmWSktJ KJJqڜdbx.\iƿ`(MY4ZVVV0rUW\Q <^EC0#n0aիWe2X,7q\s)LU%ZQsFN` BJIIϱ.Errr\rJbd LCޜo&%%EaL&yyyǫX T*ŖR)?vppXnvr$_58qb^^e̘1F`j^Jcx% [X';H(3,ѵ\Xw֊D33? K||#GGdXɓ, :88$%%$''| !EE"Q||q:幂cVVVKKKVV ^ EҐ˗3gw"ʞJzzzR4""ZYY%&&x{ ϟ?_z5JXXXd$յpiA2rUύ|R'KPP_}WbĵkR#aSu@pppBBp@fΜo>Hؘ|)uR`nn6D"߶mD"100Jx[[[Ӯ]:4AQRdΜ9aaauuuaaas:Ƃ,\:tlU!`I]M΢KXVVɓl… +++0 û0, >`W(ܹcooodddoohUWW`&`|J (0DRG% ,Yd2~w$N777WpGͰٳGI99LLLM...vŊܹsdd***Թʺgs iFؽ=z?622>}zNN.wuu]ti& 0 ١؜X^W_)S' ֮]kbbbܰh$(.GDDP{Lt3gS"33455/_SSl2Ct^$]㑏 DQΝ;sU4Ta7TT/++[p!p8 .|:ݡyrT%5v5n+sX_(ڝ;n}||WA$rǧkѽ΂ {Ewݶk tugO8xjo}«ٳ 9!ۡ:::p5/A{QwݶOJL OG`d[ @ i @ D@ #0@ MG`@  @4 A Jw!. >YG3fcj !н*N>f{uP;ENNɜ1cƍ70ann.gX...Ř0//ښ`X[[a†www6. )@Ct@[iYXXbGGG) "rzݣ |`U 1=T[dn(k&6mx<^PPPkk+ug*8eaAAٳl6vqq),,T& ͸q:ׯ/ZfxK:;U@GSL9z($%I@@KҮo:s.=Av05kIpBbbG\\\VZ ׯ_Z^^ ###9NII É !/SuԩÇ \dɒ>4o<~(8 =}{EAAAAA@ طoucvۻK'Po[RRr=WWׯ TZ4rݻwUVVvw*+x֭[nثW6o|29344ľm>j(Ltuu.] Lhhh  FFF0??[WW+??B4ԍm߾abyxxyxx\|XEǏ{xx`SFSRRͱᛦNzLy1 gʫW<<yh4&mhc>zHaSLL/#SSSfjjd0(2L !F311166*lvpp0˵>&jnnNOO'zhAA#~z=hAAA_&hooy󦥥%tݻwd2+N<)Jmmm&*4-<<)77b766V/^)HݫNF|`;oX566^paڴi- DTh`Pj. h4ZLLLSSSNNQ{nXD*Daojj -[vT?P"d2y޼y2b(`nnw^sssPHᒚiӦad jeB@!_-(!E0={Iƍ`0ͷmۆD:mmmwܙ4iE3U<9(.X ..N*,XBF={;wZwwxNݐH$D G`Ee2ɬ}]e&?{ߊJRe&#R___TTclbj-PᣞH$?>QquuMOOonnNMMuuuń= Ԅ̙3G{h(h1TLx޽ӧs8;wleC؈^rȈFaDQp8 ^ Uh4׵qu.Ba3J! 03)#lQ6heCcd4 ws0X,&VVj{{;î|(qs5v5U\pҥ"H(FFF.^X2Q-**:uT*VG0DEkƍ+W$ 6nL=??O>Q8[:@QP9H$BQT$q8 ul,m۶%G`TW!O8vZlO,u 0tP"\r?~ܿ]WWF=}A@@( !2bĈ 6oYAN/ZV^^ Nzƍ SSS\W$]z [xwRE1!R)x)NpppmmX,NLL433ÄG9r$^beggK$'OX,LҒ)@C"(>>(h9::feedeeP--- Y|9.~z*;bJR===T O?={ϟ^\YC,,,RRRd2YRRZ84 w9ΪFg{By(G.JKKkkk]9sjkkΝ ˗/Ó#""^zյUf۷O$℄.L(Dݻ899ڵh+VEGG3#RSS9uTe@*.C]< ~!> 1eʔ LvС)SP777SX777?|pkkC߼y{ڪ[̜93""!""ŅΡ*dSs}ڿisYYY'Of7 P$fLݍFM@`T ".I F0EGE"(E Q =>jp"kwCSOvu5s=]}7o޼*ĵBF  2@d .p8'[9;; ''&&81N!++K$ ]vjjjI144绹@vA^nWVV ۷wa^40d8]p2bς\#D219# K&Eeǎ|>ߜc8/YnFvZw֭[G\ q<>>>44ؼ/ϛ7yUTT~"E?䓣GR UVD"j矧OpbW_}EF^Frzcݳbqzz:)"gxaaYbyǏ=:|pR^^f{]r%)ҳϏĘLSr?0++>&1R\rÇj*JSW! amSŽ_ątzիW{xx#}OScwh1 "v`럅.mnn>uTtt4azS^q?.n@~ @U*0K @`i4PQs{WnFï(=~XG1bc,5DfG2u ;pLrB˓H$\.7 =<!x`L " \^c222\\\IϏ.^XPtv"ϓk |d >I}3ߘ5Vw߉D"HڵԩSuY,((1cx׮]3&"DvgԨQ ˗ϟD"ѢEL0ck9{ {(JV\|rB<{lJJJiii@@_Mk֬~m"""1!!痔i3(v{8q"33õz H$ݻwwv"o84PG#M={VPPPPPP[[w^#G)qٲe˖-+)))))YtҥKH?bN$---,,֭[}SԀf2t9UȮdooORL"w?ɓٳgl˗/CBB\͛7V]쒒bرc~G ө 2TUU .̨ٳgH$3f?L(l'نyCCΜ9p\]]1Z&cep}PL IDATp#G5*77bbb>ݧOfB$yucq 6nܸ̙3EEEaypK8ӗˢ 6&"D$K,0LP^ёj#Gx{{:NVBNop9#$BGm@n>N%Xeei8δi*++ ;9cxv둻ۇa{X,P(6lŋkkk Ų-5rR]aaaviiX,fXblfu::0HCf.A4x<^TT@ }6!J$*C?zb"## :::^FhѢ۷kZAhhc4T*򢛠waXqqqmmmyyy\.7..5//`Zsd5 GƹSa&&11ђXֳgϺ"5[ ڑaɃC*Bڵk:ɓ't R~JLLlkkuqq۷oW(T bB\dɓ'5ŋ'Nhь/ٳgkZ.xKKΝ;e2Kkjj\]] \\\jjjH?"~b;<<|y::B5jvvv޸q#cqooo/,,;v,4;}`i4%%%G cRwrvwJ1LO0hĉA}}mfΜqM.'&&Ι3*fggK$2:T* HC& ^l6;##-==חoݺl:;1x?bp--- Ffԗ R8S8`d.gAި|\.q\.~fkd-Ǝ m,y#Ӊ:u/]t X,V[[B766= !ǩE̥KX,uQ\hѢݻwrL`c"Oc8^TT4~xFC9|pR7:;::?~nݺ/*VWW[.$$XwT:i$tEΈ3+0z!IИN=|CFr[q\Rl,\SW_xA*GR8T*ɖnnn=qݝA4fyQE]Tч:;;AP .\j K S &]ZfFOQ܅0ɫ^ѷ<37@&9񩮮޿NL] &e1NI#FհɾWII ]\\MlgeeX,s\"IV"Oc8`Ç"Kc#Q*d!B#61c 9# dggގx[[뱛CV`Rwr*0ɝkLTTTccBHIIqtt$䬬Ǐ:lr?V;r 755URI$ikb/''GRBeee۶m/:;O|bbb8FVVH$ v"wۓJccchh(wss;}4=`]%d[2z )//x111,ڙ#,52L$ L3:rȄ <==sss S~Bرs1%KMh֮]+ Bu눋z8LJWy|>?o޼ c"O'|rQ!\j*HD2t?9X,ꫯ˨QV\Y__o{vvX,NOO'E0ɓ':zDN7;lyy9!nw6{Q=m둸p{xijl 6Μ4fA666]n޸YSNEGGckg@_;? @U*$ @`i4PX, T`@O*@-[F׏92bcR0]3bn',:XFF}~lii x2z{{s8)S\r$ (..&|6ᑟ-RDtX/qŋ+ ٍ7d )2 ΖM]rr2Ox_~'O3sqŋ#Fv:uo| jhN$DHN1 zcF1-ƛ9DvĉÇ'LHH%%%|>?!!I첲+W._Ϟ=RZZ_5k>|m۶o"Fy;=c轐z H$ݻw3]~k 2 0T!E|Hޕ+Wlfh\WWW^__?rHfqŊݻ~СCWSwHJJzYAAAAAAmm޽{?&Ao9x3gcxv둻ۇaMvy=zxUUej4dGPx5Nɓߠ2Ȑ2drJCύs:u؛l$44رcF*zyy"Ŋkmm={+) X,N4iիWqZ&dr3E:<Ovm̩S۷OTd]vQ_8텅cǎ%\dɓ'5ŋ'NHW6XJC3{lVKT&11ǽ;O2#Wgd fgddvgJ+0rybb9sbvvD"!?00СCJ2===00[) o۶m̙CA/޺uӓoٲ1 ́5u_tkC7 Ҁ ȐBf=7^ѡ%IYYaÆdsW,KV8NMjmm|/_ 9}G--- bH;Xyyܹs\رcSSSsRtҤIU>|L&30AV`8ϗ8^̟;O2#WgSϟ?dȐbwֲX,}w/_^TTD*=jkkK(oo<^g 80***--:;;WTTtgZ-J7nH|c@Vj4={mڨnjP͜9366166v֬Yt4sf'RDΝ$_pf ).\p|}}߿3{GSSSPP jnnms999~!Ǜ7o^UU!ܬG\(,,qpp!oBz E=Oz!ח8|Hn|.G0agnnngW ؒCLnƙ2bb,7-Zd|>ӄh!6bbbkz NgϞ=,ڝ8۷>31̼J̎״ivttd%K|Kr|ժU"*j4k BPn:dyy3p֬Y!#Dȹ*325{|F=uҊ6UH\8M=""bĵ 5@/_<55 AAAWkGz 3M FޙS 4Ɔ? ]\]]}ԩh 7&!!ʯ kkW0}B3 P*a|__!Poρ'sX, T`*0K @)]@ULW-[{r,[c_L]#8G1bDo8'eLT`v/Px`Lrss9Δ)S\Byyyb~~g)" QK@pqq'b???.xbB xk 2 G=u.^8bĈ.wj}H$D:Xs͚5:::.]x˗ϟD"ѢERivvvYYʕ+gcvٔҀ׬Ym۶EDD0x0ʺ 3ĉ%իW/\D"޽A0~̜9sf"L |dHiSi` 77յ IIIϞ=+(((((ݻwVz7Fd JJJnݺg]]]Ϝ9c9'-[lٲK.]3rb|}}ȑ#(0+VwׇJ}+OڭG.nazww~[77G8^UUn&//@W(|>JZ… SLaa myiӦ?@dJqR x⣏>[h9.Ӟl6Pj )CĬO~IBٳgH$3fO?{nX}:͞1cǏ6/^P)SXrLr"'Nv0 u"#GƆz_fzڝ7-*)/3= ð梢͛7[[[[[[U~t8t:cZ9`Wbb"/--%^blX,P(6lŋkkk<4ދEEE //۷oD"9xR~8W(2ʄQQQSN]~K0N?  3T`X'*jv궝`ذaM[ZZ罼LdGz3󽢃4A|>v"y)ϗ8rҴ@ @NY῏T*4i}48tŢUT*Պ+YGGǏ׭[_CS >^!1NPP_TTӧ #c GgѢEw2,!!aƆ5yc訮^n62ΝrǎJD"E$_aX a4cӴ\&D"QssI/> Zg9qBUT-;{n@RlcKSj/wwwB}S?>.Kuu?Ύߠ rɨqXsιWWW u/}[ <>mAAtVP(^A0{)&Nh!moĈD}is@n3&??9A 趐pbXYYY~ACT*KϞ=3*$CN\ӧCt:T4wYf@@1i4b[VSP3#tn,@VQ2S H]4k'Oyzֳ755URI$6بP(RRR 1999++C%[rǏcǎq\o"\.ONN5jT<7痓RrrrB! :G IDAT񲲲m۶}D;(sxddiӦ .ty2 KFse2@!8SLٰaoD"dO~7]BޓF2dFgh&踺߿_զuc8;;WTTX9pV$E///zA!CVmmm5͞={[3Dg̙MMMf0ŋqqqiii/_$Z8q"$$oMLL@` 2'$$.jTqF+h·t4i"33SeffN0Oc4;90m)((Yt]NN·~͛GV }|||||"ECttY#{d r8__-윝"uoH:rȄ <==sss0TƖRd  YFYHǏwssAYn766|77ӧO3PSS`ooODBp׮]HofWt&="'=R4>>wx;vb}}%K|A6رsnggg;::tyQg0DGѬ]V( uBCCLJПyG,W}`݇ UVD"i8~ÇL)!wpp&a@_Y^^>o<>͛WQQa2ctFrzfM6|;(//x܉&O\YYƠO΁Yڦ{ {W^|T~ܹK.c}ҥKs;;; vOScwh1 arB677WWW:u*::ð(_vvvVVVl6{׮]}U.9PChg.oybo:}r~*W, T`*0K @`i 24#0p#F76Zw}'D"Qdds|y99yiiiaaa%%%n ̙3weٲe˖-+)))))YtҥKT>ؚ1 0u Tlٲ/]T*${{{155U$ 99All,)"]BB 1,,,** ð͛73DdҤIęI&1t'#@ٳ6lݸq#Q6!]0ۙɤroo~[өw}Ї~m`}O?p/ 4Ɔfwydz/n=rww0 OiiX,fXbﯰlNNp8bX,ȑ#aILL #Dzkl tC,K(6lŵ I񢢢۷ Q"sX'O`V[[b,cwY|yQQ8p~8z- a tC/Yvݻw;A/;;>|HǏrJeeĉb1a\.pܹs;[l|]VXѝ4 0>2}Ǐ[oa u7psq?Lszѡ^',++#;қu+sL aXMMM&x7F6 5kDFFvmuMUU}+\t^|z'''=Fԛl6y-//']:}C]l6{֭ 5b^\7lgeeuz>>^0 22?ߴiӅ 3TLC{}7gʔ)6l?>݈D#ɞ>}7tٮ{JJ ifȐ!&>>YwB~V)O\QQAUnjP7.33S|XHg& x tw^\P('Lfc30\,,,qpp!455q86xyUUUVH"ECttyK&-\޿liggLqCҷ#GL0377ki`l!ErAeĞ4qq77To766|77ӧO3PSS`ooODBp׮]HofWt&=>}:͞1cFuu1O:T**r|ժU"xl6߿b#::M<bqzz:;v|xf/Y0Dv %&ǯ_b_lFYvP( ֭#H"]B?O>⯾,>>u䌌ѣGn0tb988x{{SE dz|޼y|>ϛ7˜!Ezz*Q=m둸p~ xU˗/OMMWTK&o4FScwh1 "no`럅.mnn>uTtt4azScmm݃з iM~CNQKҝt38}l-No:}&L t38۾")"1 @`i4PX, QoF]L={M+..r/V(]6<p8SLr !I$.P\\Ll6#??[ZZy<^ppL&cO7X {{{RG.:R):F%A2Ȑ@\W4ϝ;7k,.tR!vhYQQ'xtP"u={VPPPPPP[[w^c"4G4`ɒ%bҥK7o>zh"e̟&A` зvNjjJT*ʶV'~TE01l6[T8T*=<<~|<{Ϣh tBm???Tj/\0eBtss{UUU "Ґ4h5mڴEg^oY,VZZ ƍw9B5k9/^̟?xzzҭлX={D1cOwţG׿E4۷oNFQ92 s2e Ϗk>}ӓf㏄\DP䔟O0s}v/_xCC@ hhhqͭd~7Z[[5*VDzyybqq1>[Ǝ .H.jkk2UL<9))dNM)ҧ1 ˛4i&Rqц@T:99566 lɓ/_LUUU "8qkרNn߾}ذakFcC=/&jjZT*rϟ={ZI퓚5Տb? Ú6olM\ STǎ{?,vvv</227uS+\7|LO>0ӧ 1C"h~'O?ϯ:l0oo;w"r2ڵkC 3f`=g@PXYY/~7o&׻TVV]6lX^^uV#z ôZ۷o~-[{L>t.Ij`#Cک0 6̜9tݙQ_YYyI2E˪eݺu+ ;vzɓ'Zɓ'ښy< s΀b{g„ ?r9a*J.۷ZMMͰa0 3gΈ#HBXbE||СCE$~ill͛75aL0ѣ>qv/^0643"""..H#//lpYhPUUrJ!CJKKg̘qF͛W^^vZ7ƍ)D΁ݺuӓoٲIi>|/̙C% C)t4l6[88`~۶m3g42lvFFF[[[zz/!"!VXqcn0@b?ˋ8y@--- Fr08yF Nbj5m@ d8d.gA9|\\.΁EEEM:u.]"Ee%f/ҮD")//ܹCg:gARXߟAGGGuuuBBBp///;w.;vljj*qhZVrD$aaa6l "Ec444'&&J$O(3gd?M 1  󋊊:}4q˗/gΜIJ&M28ҲX,!D@@ E|~@;󤶖<VρHΟ??}tKΰ XEVS_~p8⌽R$[vJ!Jp\0 j/wwwB}(2L$577s … ZVP@ x9 zcFVYӀdԐUWqzqxx80}v#""6oI֯_e`:oHG0<"u;{ cIȫݱА5?8--8c4I7Lz1 tR4))iܹfq\׻ƲX;vznjop[[)#Os|@V`K>|ر5e-eeNG/\[W!l۶m_|E{jllT()))uqd.{qZ}1.K**--M"0HCr<99yԨQܠ_NNJ d3#œ'O{r`ήk4!Ch4xRtwwOII~-H"dO~7q]L~Vj~/dЗRs|>*u?͛7z"Zaa۷MMMAAA'((<{ Eh {d r8__-n,i:ELL q}fDBp׮]䮚{{{Rill nnnOwa:VYYٝ;i`l!5?7̧iV mIDATGNbrȑ &888xzzˊL3iKr RsgbZ[[ ;;QF\$ih4k BPn: R$1xbR4_W@@1bɳGi|"Ů7ϟ6~Ĉ)lGGGXNh7yd&%K| ݉/^a؂ =3y'Q=m둸p{Jb`@l~cGG^z^ޞ3gl HQ9O:Szkk B̹ C>o2+++J5h fčw@ 5pƵ^{z\]\ z3?)?-'2iL_{@oQy T*卫W^u[w Ǒ7SBmT;o0l]q^olAoyH؜+W.Ϟ3G,V` < ;I^g:p0v^#`74E cm1﬿ɞrQp'_gΜ_{'; *-Rec;;~w$18XǏGn@o9x?;v\V r"~knjQGI\,i Up4x?n3>IXx{R0uOj Ŏ ]Z w4*oMja{;}7cްuض_\^ݓڿXֆ78, s0%/ ̙3 &Bb 0 c>46P X2Ы@iii +!ð^!3#{Jվ~ XGLo@o߅7j믿=:.n'kFlll '໐w!MkӦN6u*Fx4a֖TR ߅u o"kcc_מhX^N66kTp {gG@oh^I=eѰb1 W{z~_VVV LSXiI1annt)/4S6uԗJ`yOxąDn^y f!kiya?K>8JYY55l~Śaʰ3TF?a]1k+w[_mumtKwU6Or[ hThtZ=ȦCil|[e˥ύ@'hi;[:֑RQi ~{~}x{ۃl4:}ce wҖ6r:AZmRammecm5IymmTz6F-r7Tw[ڴa8cǯO5/iﰇs`SҦnz]SO}JMV׵;m?qç Ptc=5zNwەNiktZہBtlmS3x\Vu(VVxRA@'}OPVJMRަi'v)T6M;!3dU9r:迕W[\+kO;UϼG:_~4=W x.L;r p8!;yC"Fs`co<;kU?th{NCX5ҫCo܈/H"s`f[Xa ?e RD. "X|8$Xt4P4 %BAJpQQ $Tqp7e^Ȋ^~5lUʲ }vU=<Qvy>A~m۲,/o`aa׳bi$"2+"FYzZk X#NMѨ3A뺎(şzKm$ AW+IENDB`trace-cmd-2.8.3/Documentation/HTML/images/kshark-zoom-out-select.png000066400000000000000000001725451351617527000252670ustar00rootroot00000000000000PNG  IHDR+t+sBITOtEXtSoftwaregnome-screenshot> IDATxwx}SH!  Dz&%r^JQ@EQvtAJR"RBQQ HB"))n6!$ϓg)̞}UBJ%Yp drm& leߍM h4H:BԄb"BB)%{OU ㄪԩSw 󊋋jNSuJlP& vʷMdޒ^yd{QėFT* CAx&J y<! Q*JkTPQJͦqRt؝y^SJ) B\* ˲1 0 qʖsS؅w>>p%>!Z82VcY2V9<26jxeZV)<צqY{d/ ,Tvs<%բӨyR<,cPmxaZ~=Nz!/EVo^~º OreӷnhUm, Л{ӫMĞҲhZ¦v 0p"ka[X^.ƕ r#tyEVWS;,Ϥ5:ef\:}?g_ko/H @_'݄RHP&oЛBuՕ|ʥnLZZVf!S؄ BTJP.ibac,cԲNS*Omҫ珖BT*\#ܗKzPѣJ)qB5V)\rK.cWX10,&pXj<-jvtSBkSBo:~"~xcơ-6~Xs n0,QؠM69*;_p;ҷK2.g YQfIrHJmէO !=zO30 LJ9"r |HSJyc$o(l;r$P䟷 Qu`a7:?6Uym"8PY}Aq:AA~!J!Z*,؟XfεYmlt%nkF) {;a!~9BȰJyf xrnABiqTPV710ḗ0acBY9n P*K̬pRRT,cJ%0ZVR WSJ}^5* IoHڿCkYsl߿_ʡOV3c3Sgys,2x[ rĽs,ܿ4R\^A 5;ݩֻ,BѨ^h:MPM)\pf0No1_EqdhTDЎH=ҪoWXC Zccw r5[vekԪg% ! "K\G|{}SەkUdy~aI\zZ5Yl g !´mV'#;OVR*XC{2̗iԭx HoFܺѣ?SH/]bƷj$"vϱEwmqR*8j ah6le86 ɺ]o(J#\K)W8yRY%v79r>/Xk=,dXYɣp,#\wP(7m~-Whe<ojޝ&.ˉk}?88_8Z~@!<{w/BVz BȆCW3 8k>X-VelF^ RVkq׏n~Ț{BA[jvyLmV[{(WϷijJY V~ԐտStHHVs6ɯ;/ C۷fdZYV:Q:nm۶U=j:p9s10bbh3]]Rk~Itw t1D;\U5o~1ʊYeju@@FYq.QV!nwJUz ^G(8 m_rY;VvR]^ K)\(ޝUx,j7KOR*aʔ)'g`!$/`2 T*309~ܐȭbB֩[_97qͨCe8Vn=?bd aȨ};{M*pJ|Mَm_l~E]urm6ۚkLv!O`m\C]V^C۷ uԩӹӺ"խbGi_/2#jFvCyzzz! 6}8zw~jkRSSNv4HYr?DNzQbVERa6lجY3UӻRhoq`ޜֺk:tp?dM*şΘ_o, ? =!pC)Sg?]!<nczQ*E/\ɓ+Vt'LHxsQ#GΜ~'&~~xn/tLx%!r +[V*J/իяWpKH?|0;==}߾ ?^_|y'&8^F]s^*;w¬&CݸqpעLveʕVu]vuoT^x}rt~Q-_K.edPzϝ;ƄGWlU_D}4[>|ysIh4m6u괅 TԸbm?̝aE JAp(jFAg3VVC2/`0̙3WW1Jޒ 10!B1K8j ,6W9p30\ eV;,!2ύj=+̃OP!0a`T330PGGԩ[WgΜqӋiQfͭZj  lҺukN׵ks ۷oo޼Z޾}PXPPп^߿Pr Ν;Z\\*$$M̒KJ,im=z>UcN_ɓ\Jר2 GlOInOIi?#! BҥK7nT*i#9%7$/urs[~z^/>HRx. HdMg^ {ZB\`>JÇGOKѰzQz޻od zz79򰦤*J9qssc^2} ʝ]vٳhƌ#G lٲyf3}/1bX"9]&\;ɚ2(!Bf7| +c>lfKll7lqU!?/7?/ƍw~{„ү_˖-_v*JU W[e^ &{sEfܼVgΜn߾FСg:$w׮Y퐙^b>}Zmtt]Vs>0**JP',֭[郊wZ;wLHH 6mzJ˗5kPr bƖVF#h46o~ѧOvd1Kn:lrlٿVV 6\h +fk5Bɘ !mۮZR&[az7[?h߾=yJPB~#$;>n;رyR)pxn&n߾͛7eu~q"$w\G&N,80623nM=q=[Y֮v+o۴n-`ԨQχBBCCŚfQJm6VuS(9!$((( `Сu֟}h8p9etYB%N̐ Ώ^Zj]N_97fs'篠-5BWoZlWNv˖-ᝒ{qF[r_۶ݼiB YO0B(!C Y{ziӦln䷟O>ǎnfE<I$ιL3u^˗/4p`~^}{]qVfNs?1[INjX)w hаu/Z$?~|ĉ:nIǎwniÆ t^O?MjTTgGLKٳ s/RZTT4eʔ8yO?Tx:y9sٳgO4$\v-..hժUK, OKK{pd1K_xw}ի.^,ez^z5hPK/5lGۥkzQ7of[OM4DŽ ;OOD_q\fn߾=iҤNju\wɴhѢ)HPXX8o޼)/^>}k&nذaРAkK$cvӿCd^<7 z\mF/>oܸѹ.]@ 9h"$lW DVۦM͔vrs3%$wJ_[I2ɝ9`.1Zoŧ;v|׺uNNHHq>Ci[}BİTdUp#N 2f\tzgzW> qO|_#χ_$#a0SRR+BJ2 | ~@nU엹'O2 r>Q+pswܐYHJ(!d) F;z/W *BpEOɌ20! 7}`rbJP( )u~p p!Ĩ$_PpT1B1gΜzm߾>]v:u[BNNu:]˖-=Z !!!!uMII)_oTK6-33>=z4˲7o/M&,8qs=W\\x⤤v.WHoP3p3f >jΘ1#00pҽ{ z~Ȑ!/_.k灁O=ԍ7*7p !^OQT˹(YYYRJ)b{8 5Bbb/1f jgŊ%%%ݻaÜ+(<|op7 naZP8<<|vгy$20!20!20g`#+<)"T~I^\X520$T- |?B0-}/W9:0! /c`rC \$ {ܐ  ܐAuyVZiڄ [liݺNڵs۷7o\VGGGo߾](,((߿^߿`pS(9ŽdxΝtC-..vn&f%#\;xH5p#"F.m81{x{!dMJK$ ?n26o&1… Fq-Z wmXv"N>~VuΝ BaӦM\B)|rfJT^IVFJhl޼"磣O>&f7;D"vw!ȬFa$WoRIf`jyM8*vAE/~'ׯݻ` =|PٸqcBH&M233(88>}|B+{-[ꫯƎz7o/:x`@@@ǎH$^Møڙ$:0(EEESL8q}yrr͛?Sɓ̙c4gϞ=iҤ H(vZ\\ܨQZjɒ%iiiwHۅP!fW;GkjFrEJ\YHfN.^UVnKZdFfY8$0LMx;vԩ`0q}Ho">:#՜=IXg!qGVΦN[TT`v s]|"##Śk֬1ͫW 'LE Gyd޽%1K/&G|?BM HdM'y L!d`W3 ^8?$eN*/WB|*$< :}ߏ|/W=`HOOOII3gC  @n; @n @n @n @n @n @n @n @n @n YDc5ퟺoaov_U aHѧMTFM(tXT+L+G} P 20!20![27iF5o|BBP(j]v'N :<%_`03|* {e˖ٳСC"JhLJJJJJYf̚5lQTeeϟlٲG}TFGGWKu:]RR˗=m׮]ivU2d`k^\-l111ٸqcBH&M233=SB6ʸQp|$U P(!**66vŊݻw )uBZhh4 ZVA~ۋʿݘ/v~UB.y;i'd0SRR̙Ct ,>>ȑ#(,^pAH!۷o84iР7! 4(T]eO>a„G2 6n878{ٳg'&&:T2d'|b2-Z4xP5!{gfϞ=~޽{geeeEEEEEEegg/]ԡٳ?>(OULnj3fB uIIIqUXX؏?X@n @n @ne.\ݓr9jP3#Dxx͛7+|oo&..NѴkn߾}]v5o\7'' Klm۶hJլYm۶I ܦjd`P6lXyҖ-[6o\\\}/, yyy9G'j܍q &+b:e4999))W_ꫯ!ڬY qܫj*ׯn:cx饗! ԪU/[^%> 3225jrJq}+߁ *tEYE;wpd!C "JJT2bĈ#F;p@n @n *EEpܐ,gώP( رcqqqW)͛7?+87HyQx Y_ @2>Hݻjj۶mVZjʾss$#}W$?CYc!YVZ9sLB͟?nݺ7xùsν z~Ȑ!/_ 'N痘IѣGT[G߇ f=z<`BHLLLNlrվ}:Wp Ñ?hԷ @"\5`3f%IƎ몂7[.,,Յb_~9::ZXIg5]׮]t#GtUG:ҥK.]I @P(7mڤh$?ETQh*\ׁ ܐL]H20!20!2aSfr/rGU٫SP:C @n @nV lƍmڴt͛7_zPP( Zn׮݉'BοBTYeo{-[dϞ=QJFcRRRRRQJ)e Z(C6e˖=Z6::_钒._\T7e~^zZjْcbb*"*L]!( Jjժ ѠWFU-~EBBVߏ#e5sQ2#G 8y]۷o!Nj g!O>a„G2 6n878{ٳg'&&>tD2gyfǏHLLݻʟ}YVVVTTTTTTvvҥK*=))u`cƌ3fCM%ԩ+܊j,@n @n @nsOֳdC@.vI~jd`u@ P(|Yo6,$ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ ܐ JCtWpG1L<{;ʅ |f#HzCBȺv1s̢"$aP>ܭ[73f)"TC4kСC={{׃ `,{>}M8VZHA8Zݮ]={ 0@'%%" eU*Fܹ] nj$ d`[8)={nݺ駟#FG20-,˪j! #ƍ=N{'@5yo8eYeş':tU&Ngł-jpv`#FX,_|qڵ=NÑ0Ґo(v]vMXd6رsbrPaE8}_\bɓJN?<Paeܧ~'?~|V'|Rh4o Pp%>'|]vK.jچ (j ÏE@U#Gvmʔ)3fXtiqq)S~嗳gl6oGP1o0`Iy~%$$|gjJHHHMM5ގb:0¹)S4m4$${ >/^|駽 @ABBt:u}ƍ{g}рoP1PT???D߿{| d` NtJ|!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!20!xd헫aMJ~P%T lýCHzɡg! @nU,$!FzԚ#xя7B)klvP鲲sWP(j~7i⼴ d`o;کs]h:75ܶd Wz|pi=Y9o]7fcǎKK@vɁ4r羲F+PPXJ?(.Zz5ƠGGr߶jd`,ZV' > IDATO>rd]N~544ĻW^T?b#d>rg![pGN>,$g! xc9(*w5jCfm?ل ːAb&+3o c!J28(Ull&M4 7ɕQj16;?P*5N狷ծ6x˽}۷qhƪq=C*QƄl a*|a` ՆLlvSoq<֋tHRm&eK~w_ߴic-yL+Zs<5nݺ*B8lX?|T:ut5];캡#clÇ UBaPB׽1k Wf'CAFMjаQaaXJ U * 7oӏa6®4920v,5uпAQJ%JRR5oBTK=2rsxWU*U?Y?zkGV9'5cCoX/|4lԨnoҫa U:u0?7hThՓww>PI6k}Ku۬VoºP( ʼnK!2]qWS/5?9աS??DR<ڵk{;L[;<w6_=~=).*%@TQPByBL&3#=}ꑎ$GTmr+[IW3XPP0h BȞ={<jȆ*߰PQJO8bp!))3f몎\]4x :u̙31j{*s{S?~S͢[$K^CʕQF7mUذ[!amz|42~ǎzwD۶m~駑#Gz;$d  L.WVAaq:GNw̚5kZ@܌cڔ1jVVVVVVeHȳ(H9^.z!?ҥӧ_׌}Wuܹs޼y .~}-Gb0HKK ߵkСCN1  JHHtҥK YeY!MUٰdggwQpyN3湴4D86֦m{%W\h }aB۷~fDdT]4<0=ȹڡCo Bw |^oOIܥkz _n}m'x.30Ͷ~ͽ׊g̘!ο|X+joNk3O_7wkST^T<%ݿ}{?]mmHIKKy !oN޵Ǩ{؏s0[ݜ˧Oo>7ү{j6& K/{_,X{5*I>,0cƌIII)((hҤ 0 xO:UR:uy.=ܼy3## bX|Æ ܹs.\Hŋׯ/++S` Y}ɒ%k׮={vrrr'7 1Odȿ{fe LqPžQŞ\l44#3cM'[=slѢ}bҤ3u4*M'3VZgOçd2&MQ#>bĈNQ Z[)["iRİaf̘1c /ƛc#0`?%cV6_9z肂>.Z[[utt$ Ш2T A斖o`lQ}rG!7)))l\.߷o߲eڜ!JKKK&5c`LJ4eEq!&&&::e2و wٳK.m۶СC)J'~`!@U?";ŤVU?qM;ßUT\ݻ33'%" He2٠Ad2Έ\hii,'%B''^xbo'NPmjjT0-T* 8AP[[={uE+֮]wg+W2%&/S {:`T5E)))+V""wwνHc(4R*8q?p TJ,PSuml'H+t+**p ғJ=Bxʕu;/]ԮtXD)zoknD>uL]bEJJW^^|~Y۶wjTmq"v=w5/DG׏3v_8 EWEQ\.˟!JryccÓ?0/fi63v+W c@ț7o (jGB6 4b0͛okcO^c0ϵݻw+h}G ^tť۷FSfeСϟqǎ:BWYbbb_GI/_"HD Aco߁d;w埀"ўtCCumQ#}|in=-sPjKkfY3zG/J"dRǽWϷW+z1QX[[;{\AΟ;(MKMzBڋլ \Cd4uEɉ[BT" ~k;(6OddKS!`GgϞ G'8N ;Eg<Ҥv W#G;0'ơgϞTl;~`!}---+oU~YLEgK?A*z/pJt̸";qUF}}}e2ezBQTsNd=h$:::tP_FߗxPŞt)LdF6#j/jƤ:Hׁ=uT.hb=b'/?7b04`_T?jk ޹y76j/Ĥ;;H|'4uCz; ?nꕟ~@(MN;Qx(Z[Ss'ggT1AttT}7b3bk?ޅ@ۍ+hmm}ZR|*ۗ` hb@Q\ʹp}~'J=ܤc𡁽QcCfy꣏^R]]jii@u.x`t:::#>:  nHoG#W!rQ&njſrL.EG[gqo[NpmnJUA.GVwĉ ,3;:gНHR8i'1cH(lmP}1c 30xO=Ea:bB@ F`w--~s8:Ҋp`? { TB ,XG` ? @z>5o**<_U~9·&NbX䭀90 NC P[n:998T.GQEЖEQT^/~V @)7w&&--r\.\*bZZZÇg?A@ %--,r+ѣ;Yܿkec׬K6Wa쁍.Nv t(8 z CN|:+zolԧFtIuz㜕ujo޼9cAQ@`ڻ(DIL8۷/"o0@'O81b3ȑPozo%% N(kmmݿxIE W^D!*ay]v 8\#"~04b؜?4f>abAAB =Չ ?x(^nnvaNKK#jUUU}?qdrS[߿UB|܁4 qVH0`?x? nvv6&iC?xoL8$HLxuЈ[l۷F fMMw8!CO|hwиH8zX$tQSDºw|&-X/_a~ÇGeK3<푕Bo  ɨ)S`BOWU1TW {  j „Dܹ[\R|I''Lj;㏛R^^ڊЦ!C :T(2u;Ul׆o*la:Z\.w㆐k Er65I,֛k% W?x1` OG(O}}}͛A niiiYY[xuy;cǍ;uԘѣUw]"m#JZWWg3p7!7rT.' =zdw[kkk+**Ξ=(y' [plda N:4wܮn^N"bffvu>߁\m!CzAo:X* Ummm2BGC:;OsvpBs ۷mS('!O?d,6GEX ZPخo;v>r4i"V6mn„ L&ScQh9 op. 2CY RTZxF!bi9z\.tΛd6&5jHL'{ mv_hw#H)P]zzb˫޼ʿy9shz(8huܹ#ɞ={rj$YaHH$vttrt ^[[ z.\/Hg1b&믿eL'JwFDnAIIR[F`utYt۶˗/Z[|7!7Qܹv7KvK8 ٻwT*7_6::f}A]<._RRR0,qVuBk++zg!H\g!/_`Gmm]uۑ#;Ѱ=quuH$A** \ ㇈k׍;A(U, %/GEE;n7oޤPɒᇇ $cvT;7޺uW_/AQ[uH$㑣LԔnٕak_Xhbcg͚%H溹v (h 066ZE{Aڏ/+b)KQrRϊ={r9vEZ_AzcƌX먝Ge˖1Ev֗hW/C_llܸq'3%ZU:>I7޴gH6]jnn 555mn; I~oQ?k4dIt{+݁6\ MGG'&:u؇> 3>ʸn>Gh+ry+rT܊hAn߾EV#h+xE z`˥W/_@g,ĒV9*[\Jd-r9J|8N7pGj:Ud{`zKӫE8C =7@ @ Q8-~@ |>|zԣ8ЫS@ {Tzć@ t) IDATp@ i @ D@ t:ŋ@zgWϦ.X]] zeA3河Linܸ srr|>ruu-,,Ą666 &77  nUjiiihh X,X,P' y<^{èIi0ΪwSLQ t:=!!lϟ?D.A|ٳY,wee~dx<ܬ~Hg6owY3ݜm|#(STTp:ŋ(..^j/&pBbb'O\]]WX KKK‚1!0@!O{5Y&֩S>,py``E|~LL :. 3gN;"w0Ϊx7'>:K!7nr.HBBBիWw;vҥKw2>>իW`Ϟ= t'Ǯ m}ٳg([[[=IsR4''IA.9䔗'ɮ^:m4L*E Lb@ӧ_rEA`0QmlliӨ\._U>>*5H$,~zpP;;"l^:!R js//SxGv.ٙU~}c>zh֭ԃ/_"bddDy.ClvHH޽{6P x#G~Wm^ B?o޼illlooCLrO8tId2zzzǏWiPYYO?W @wޭ[b7ZZZ*6mrss+))پ};>H L&{;[ZZݻm6lw}7cƌ?_?$0ʍwȲ3g<|zӧO[YYX,bj#'LXX؉'nܸQPP&hfgAAyH$ڿɓ Ç ڵNJcccAΝ;jԨN t s` Ef& C៪"(66vܹDaFF-斖ؘ Q5PUUUXXجYHMMmhhHIIqttĄߟ:u*ٶmnh@@ACV__^rg8N]]ccc|Ba B$ ---e\ BEBa3J! 0s{sqk׮a[n988r>_\\ ,0UUU|>͛78cU a`-~css3>d2Gf?sCߦMU4558;;c0LL-d2i9P6|Eʸ 1"ϖ;FѰF;>[ <ѣG?~&y'UP !2lذkoYAN/X@OOӳN4ƍeeeFE"իWAeeejii)(Cl8y[U8p :::e>JN l۶m'::: Akkk/qQF&LUUU]]]uuuCCfcriiid>|Xs,|t̙;wܐ!Cc0xOURR`0D 9u?^8}♼Kg,1)))99wjXhff 9EgXYYYɓ,   aD1cƴs-''̦Laaa| 3g888 ]]]eRT*ą։ħ|Ν;B˗/W\I&dIIIk=rUύ#CVi\33=Legsaܸqnnn'00[[[Y`12ٸq_|1wM6Sh_N/Y,h[lQիW|rŊWmlllmmi4w[B5AGww ZZZ}}};:Q܁ f}.jlnrUD"Dɇ޾}jkk+**Ξ==J@zt:A ބ _:|G^*@z p@ p@z8*$@ #0@ MG`@  @4 A h,$@ H^VEVA{QmOMp6t:|e@z/g.a6B@]] ]m.;;ޞdN6ƍ0''X,WWBLkcc`0lllrss) *JMM444充NNN,K,S[L`qAo9 įv'N'$$WB♙-_ԕrww L{Hljٰf:w R91Emc{QQQDEEuUVb .$&&>yuŊ0(((<<4,,,88[hg(56N:~a@-ZTTTuՁ{04-a݌C^| rƍ\.IHH߿g#wz{{uxCCCmmmmm#F`¤$xW^aBCCCA122(DRSSl01JJJ<==<==/_Ll]8ɜ2eJFFF 0䞏BnrlL&P1޼ybݻ ƍϜ9m=w܆  LLW7'7s@| \'N|xvv6VVV,Kڴi[II۱Y۰'Nܸq ###<</wg&fgAAyH$ڿɓ ÇS!^Ki/^˗/ߴiQܼysHHHiiڵk7o)0IAAQUVkk+|%K֭[ ###SRRƌSQQM~0,X}[i"PH`q(@΍x\VZ,ڵkk֬a^^^o߾}}͛ׯ߻pR~¾\bƌcnn~ҥ(ݰag}FmN~FaS Fi}}}=zK<Ǐ`xxxzzz?́R4lAaÆ]~k Zt:}؅RL8iҤ7nښR[m۶~zz4P0ݒ=rnX[[\x1//5 X >ddUUU]]]uuuCVw ;;;.K*** LÇ);w.-- j!!8 }f蘔Ԕ\hhhuuX,NLL433Ä G"óX,DrIE-P4!ƌ.233233MLLb(}7u%Klٲի'L .#ǹ2x aุ̍իWS#w) _|rJA|||R)neee5'o>aCCCPKaZ!!8`1Tv=`֮]cjjjiiLMMMVWW7yd]v!2aldРAׯ߼y3ˍ(5ֺu{.kiii}'+Vpww0 l]7nd2###;\OOv94-aW kkk}}}===|8b ڵk'N>|8vƍyMl2GGǦw't:}ɒ%wVh4__-[gȺ» Z&(<}|nT[!GQS[9v ѡVw˼kkk+**Ξ==GBB w:plmm j HoDūQ{e,ң#0-ÁW!!@4 A h8@ 4p@ i @ D@ #0D)EK;69zQ¥(|ZXX:4c.;;ޞdN6ƍ\acc <<|X Emz! gsǿz*?????_ ٳ|UUѣ;Wa??~ÇǏz2~~~EEEwss式OQQQQQw"^?sرc.](i;EՁz Ǯ m}JGFӤ9T ')//O&]zuڴiٳg([[[STlB1ӯ\ d0(666PT'4jrrIt:ĉ/_Iٳp_^`ɜ:uݻwV*4-..?ŋ111cǎ厎\.wǎ4zqnony_mֹc.X^^N+ӧOfQTHClbƌ c̙&$mWC"""83g]~O6DDDcbkk[PP@aή[.,,GQw֭(nٲϏhK/ l&O5  J$Dk֬155%!kjj;舢hYY;w%...999MMMMMM9993fP;(;9fffG> 5W!R__Oc(:E߿onnkQRŅ`cccc<^ KOI~x=**|L HmmGnݪ,66?0z,rtOON&G`(Dعs rssKKKkllLIIqssÄ= UUU͚5K{h(h1ԆNpmf)J*hz|ʕ+vvv>+p5V9YFI$E[ZZ˸:B| i44B Æ [vo.t <==KKK1InܸQVVfkkkjjJQRO 4_ -+++..FQjii)(C,zȿ" 2p@Attt˸Qv( ƻ@Sv(0N VcrbL$ @JJJ շHD4觚‘#G:uٳg=<[mmm}]XBB‘#G{/b811 cb$ɓ'Y,&tttLJJjjjJNNB! H0f̘vy䔙ԔibbCQ8,,o.aOUJR===T O;w —/_\\YM,--8 ɒT¡8dȁ"Yh/+|O2S2ͬ̔)Spg0aV}ӛ'OI$T*#:$==aׂq |֬Y555gF׻wNNN|M׵짲fTSۼy3pkjj.-[ѣG555حXXX>|СCxI:޳gH$r1!wQr)OO5kRC!t @u! 'NLOOd8qbA(B*\%^'QP\ 233'LfϟE,Vwupp022rppo7oɜ7o^mm-YH P(\h.=t IDATtttt:;u*+ISuv܉]@IsGx&&&JWWWCCC\R]]l2ceeu9 Beee܉LegsiFع, SnٲfQTH^x~'~EE&$mWClg}$##4%%fjҥX*]dݺuؕe˖EFF(l2ǀvLLj ~`٘!HzjGtҥK3f`2~-v3SIIV^RR2|pϟSe߁SBI} EW|`U H':N|?|,py``E|~LLnHHȜ9s gs\:K!7nr`Wp.((/z?Ν;cǎ]t)KSL!˫FM- ۷b=^||W={ڥSTTTTTN !!Ԇ%UGͶ]?]l?~-\*899Q|'@NGYXb1d2իWM ={hyy5hHfR|gW\Q2FEmllrfͪ&''O4N L8˘D ̞= 0̩S޽{lBxϹsN`0,--;v-;;VlT*4iҳg) 04-66dܸq()9i)dlF%&&V"X6+!Θ1#''|}}=~y L`0pC +`0>ׯc[kkk[PP E"њ5kLMMuIQR슊B{{v455555̘1CYݡ~ KjjKoۣ)>zh֭m0?Fɓv5OhrFX???lɓ'Xr% Fss3L&B4DLLL d-6rp5?V&ϰ*4M"(B\չ\P(DQT(vi i|`LP7^v ܺuU.b`1 ]Lׯ_wttlnnG`L&sl6?W8Ud2b={\\\MMMMر uF`%%%,<)) lO:.K޿#GT曂!#P0b111"H(FEE-\PYtoӦM2X!`I]MuFG`jSݎG`(),'N3Ll/ف+M7551 e)QFʕ+T3B֪hMF`MƍzL&&.pr7 2D+jie8ëd***8_`](ggg_reDaZ}9~d2wBB`X[[KRlY*SGG5k`\&sss:QFaCp"y)fAH$o9`tFB!`I]MuRgڸ?44Z,'&&ug`X95|p$ʒH$'OdX1)))99S)/ƌ.233233MLLb(}7̙3H'RT*ą։^|Ν;B˗/W\I&dIIIkqV=7 Iv.!!!:::K,ٲeիW1appp\\\bbի)i3apɩV 44t߾} &'' ի>}={D"Q}}}rr2vkS<==׬YK]ӧO"$//oc@*e;۷oiEIUYfEDDTWWGDD̞=]l6 X,޷oUV'pwA[UfXRcmWS(RA19a6= 05: w:889887Z̛7dΛ7R ) j E1LGG/I-,,.8:;;+\Q3;wQo#Gx<h|See!.^lñ:wY92u]F4#\=:yd##Sfggr77ŋYL`Ӏmj͉믕իWX,www# yyy*033355MIIWUU-]DWx^T(޽{wʚ#J׮]kbbbbbn:%%%p8gO>UV'pw(sU!`I]Muʄ\90-lV*y(v@:`___t>\.lt3o޼@nQ^]M[l!ٱ0@GGZޝ_/󮭭8{lxx8 EttNTT~y{ AnT^(tp@z*|#|{0Q/sC h8@ 4p@ i @ D@ #0@ MG`R4C5phNsYG5jcj !йz*N>fuPl{{{&9mڴ7n`œ>b\]] 1ann ńuuul6C(R*JMM444充NNN,K,EG08bT:ǷQ: M&_xfgϞUp3gl6ZPPL(1cƴ+ ׯ__`x/?uBe-R]:5OXK'NxQDInK$@AAARG`kzzY:dWХ;L^^^FFFqqUϤ_p!11ɓ'+VAAAᥥaaa0**q8( !B"==eZNJOO?|@ 偁-***111 !!!skIiu5]M||W={WUU=>>>>>>EEEEEEʄ@?\]]d??ﻹ}טXm*T֢Ǖ+Wvر}ӧO߻wOYɘ۷o߾}[,um޼yaÆv>@||!m#F`¤$xW^aBCCCA122„yyy>>>yyyBN$55u˖-<SOO(?~[U'=O4:~ ,bI&]r<|Μ9 OOOegg<~*t:=>><77766lܸq?VٳgNNN;wlo%igs!..?ҥK-DPh`Pb@Rn:4z1cdggB:sΑ#G:;;##p}ƌ~~~666AAAԩSo߾̐2.\i&͛7?A??PAnOt EQcj tttttt 2!Oɓ'w]t) b800̌hGۿ{dСBTWlRVV6}t&9}6ի333+)//wvvr~&$$gJ^xq||nݺDZkG[~ (T~}E|>hEFM~~~'OLMMi4)^`477(d2)@C4K PxJb١\.:t1-- NNNزAF}X֭[VVVŋw!˖-;yT*˳# hݻw744XݻwV.\%HvڥNF|`ەp”)Sp!1[HC#+h ٖD;bqHHQGaMMM1ҥKϜ9#J[[[jCdL&v8ϙ3G&X,E,,,veaa! )\RSXYY9eOT& ,Yز``BRŋd̘1 bXL(r]sssfyr0Q7o^||T*7o:F{?wu Xԇ!ÑH$m:z`Ee2ɬ~[$?w>KOIFѣG[n&VF`YYYز EKzD"QllܹsŒ >g[ZZZcccJJ&lYh*,,l֬YFCA`6448::bO:l۶f<- 'z|ʕ+vvvFFF4 %(é#`ll>x1\ThQB\չ\.a gB`(g8SEzG+Kb@#+h 进b1JV[[[veEk׮hk׮j/DB0**j…ʄ@?9G&M$JՑ#G* %QCQZnˉŠuyzz*SOzZ`T< 0D(D"C7of:{*'V^5#\r?~]UݕT*B! 񪪪O8?#FuT#GTՉꚖFD"dO>]r%}z8998p@Ѥދlxnh<aH$&]z]9rdUUUzxx}>gΜx ^x˾l<o߾}r\P߿_ ~BE nܸQWWG_a[ZZ֡ -++j---{!f0)))iҤIT*KKKZM:#!/y}iĉsrr4MvvĉGDD$''GDDtvv2Xwtt<|Vf3""o͛7[̜9311-11Ϗyޡ*SsChs| ezyyxyy7Zr8V]dL&[hwё[78pb>BnnH$ w&v544Y[[Jssshh(wqqbEޮ;vüikq6<7iD]#]h"]hh٥"sN>o1?MMMK.V׭[~`^hhhbb"㉉:((]"O~Q!\zjHD4t?Y8X,oeرVjjj=//^,gff"tF8CE8=ztdחfVWW3t'6۷j*Bg]uuNjӛTEEE3fr|Inn.}L0BggUF=zիW+Jc]4؃ƖI߈@ H``5kۑ(:}rv`k_.kmm?ydll,aS@ 9›t/,0"C B  z27@ A@ A@ A@ A@ A@ A!C#s5(qu;;;cL% ˪"a,,,p8ӧO|2$ +//bqqvss+..b[[[pp0 d "ԐE+++ښ}||\%K Eo'b8C<4 @:(I[{h6l J) IDATD"QTTVen{'ORXRR2{l^K ~رc{K.-XDŋ߿>f5#`׬s`O@7&P*UUUZjŊ@x &&&22III|>'%%1PCEѵ999nll$5k,ZB"ٳyˁ >4Ά?R!IJJʳgJJJJJJܾi̘1/_|򊊊e˖-[L*>!֫dddUTTܼy3 /:y^ kF @C0t/X[[gbzzH$1bŋ={Dkkk 0 T*]|#-[&JD!#eH<TWW92$$ٳG4++8::CĮI&;w(?;w.&>|2$$zxxܸqn*%%E,O0899_~ >|ckk׫igsp|}}A|||f4ݻW$? 9[Da^hhӧ=<<8s^^Fd nj3vBB=z/8SN=BϻϚ5+,,mڵ@$~5]tq̙M6YZZZZZn޼EGGcupK8ӗϢ%BK.0LPYޞl#Gxzz{ZVR:hw0>2 tFPt܀ؠ'}Jڙ3gr83g2Erhxq;70 =,K(.YڰX,SbXcƌJNN ەbbb%j8kZ B (h@{xh@q- J$C>!{b^zEn}ŋwءhǎSRÃnޅb%$$tttq܄"… T*ծ]Q4^冁dSFX3g8;;"9[(DȰ!PxUVHJ28<-"_f.,KR8E& pd}(J(y *0>/qCQEyu%yd:3fظqŋ@S!"+ zc]P__2D(bp'1/^dXEapŋ#e2YRR… uP?u9xYY٤Ij5sєR:ǻ=z~,ׯ_>$$DWwT:uTJBgĀ=ӤK'|>!]9MZ8T*l6d?T*/^ T*q$Z<|:WWWQ!kgg'p>Թsf͚ۉ ppp8FQ(.@@)Lл2J %#ǹ׽oxgnMCS__>,<< KWcIq;;;P K`lX\C\"IRNP.q_pÇ?tvvYGlhS:#(Qq A+0J{]ԟ7Vf}`r<55xnnnV(iii@LMM-((5jђTcǎq\ z{{+ʌ D B (^>>>J2??_(pꫯz;1bDCCtZ9rZNLL$DWW״4$I\\L&{ʕ+л&==^4Зgsp>j)5S4= Āeaa_nٲtk#G[_pu@3gN|||KKKsss||?a/^HHHHLL|e3o>\P(/tP?"a7nܨ ! n߾lв2VҲgXLԔ4i$]U*Z޻w/4 b+ʃ2N38&Nh'N\y=0bx<^PPP]], @+܁#??O>PZZeccEhp[[[DCt9^2lѢE޽{DK+++GGǃc>É'q'\H$ wMjhh󳶶&P>r) z{jkk;K4еd8S]]X3 )G4.5؅&ޅ&9rd666tWv 9Pqiҥ^CtjuBP(\~=xbbbhhy_ |>?((F*8=zlH.^Z$ ?0px;vUtu˳ř(8qСC̯G4qחfVWW3vosX@Gq*@ (:}rcaak_.kmm?ydll,~@  lb(B +0"C B  @ ajP@ ajP@ ajP@ ajP@ ajP@ ajP4SĠӳ|r~;;;ʱ*>[1t7]ŒɲNdmm=/ڂy<^ppL&hsg/_bQQD"r~~~@,..vsscnnn BE!+P{uoyy]dB`x;4АBmѶJMM%믿~1Cwc!\pΎ޽ɓf5559ő*h4 6D"Hj?&` fd32 z*Ǐ>|q(>aRRϯIIImN*UUUZjŊ@x &&&22[5uO.z/YfѢEdϞ= b3M |hHi0O> ._,ȇ h3CD]:;;1c0w޽vڨQWSHIIyYIIIIIIcc}?&` fd32?^l~ 1 <|̙ΝcxÇ8չƨZ.** mThΟ??}to@1wJ3h/N;;;^Slӏ?طц4R4s_k(qnWVO4ÇЋ/,XpKKKbBCqq1PzeddL4j`C`X,VZZˉ?ߧJrz1P JGBlR$&ЀfO6ҥK`/SLz*rw}'czxxTTTrOOOkkkOzACй.32|5>WM_Nf)(/'3ðֲ[x<^tt@ uVo'c2lVq\r8X+1f̘Jʮ䰰0]YY RA,- K,illd't}%IvvvggCO"ԐBիWZǏ###6PCCʐ}k =7ϟI7///<< JhhcjT*"JHHhoo?s挳3!ի bZԩS\*0Lw@(xocΘ1c2lNWWWii K.=qZp”)SfRr8ܹs5 >SSSjurrr{F;O2#W32Qf:::2333r<99y޼yd1//O"pС̀o53g]As 7otww۶mx "!C :FGGϘ1cƍ/^PC J(U`q//DRUUm֖ KbX* .W{{;h^t[3f O N*s &sRtԩ666U=zL& *0lj rrV`'TuisՂ F_5, %bLc_bEYYuݹsW>SzAwҤI/_2eX,fŶm~>{nziA =7\]]---'T*o'+w1 q^8ӡ@ 8tחWUUq8ÇiҷG>~ÇʬD"ak׮5:hiaaQWWG_J:l68$VWW7 @|||Je~~P(4O};==]TfddH$6ܬP(큘[PP0j(%-((PTǎr BE!\.OMM;vl<ҵ8WUU|W̢Q/lrynbi/=`}-{MKK[z5C3:$..N&=}tʕ vG^o杻Çhll 555ARI͛7o T*KKKZw^GBBBGG)ߜ9s[ZZjZ0dFP~BECF&`~*L&[hw^? ---'00u'</((R//////F+P]$ܣx cBE!!9rd666gHM]KF)4 gbӈEWn,^Xo3 ͡|>ԩS@vF\\aH7tˢ݉ ]us [T.x}͜9X<{{{XIojjZt)'$W^-ȢZ^nP( ׯW$}||x<NR Ќݡ~BE_FB AcK_$۹Mb p-ӳbŊv/Ym1hF̴4EOs?w,,,ܸ~e'O0lQA `eeeaaʯ7 ss״0`]02hFU`u~@ )P@ @P@ 8@ U`@ U`@ U`@ U`@ U`@ U`Ac> 1x>h4zzz/_>pv,_܈/zž Α#G±!8>"1-88d6WXXpO~e I$.W^^b7776V\\-T"/ =:Errr&r.dB x^ CC Mc.\`ggfÆ "H$EEEiZ]-Ϟ=r-[~ҥ x}: S>3g}:Ϗ'$&2eʔW'eoo?f̘ӧOgfMv%B9s+\]]׬Yyd`+Vlݺ-[NBqiii)S荛\.b1yi=gM:f S3ēŒ3JR,**5k.W:`lR* kkkA*1b|__ߺ:];v찵%ޝЧ ̸};ewdf`ZVVuVssssss*z1`ZqVpL`1c*++)|ee%xeb%[]SbXBvɒ% Rx֭[@H$ٝ" 5!483d[[=yW*JBBBGGGQQMHHhoo/**rqq BCC;VR,^xǎwww_rHol9sٙ+]PLBBBvڥRvܹh"f Q$''TҥKO8V/\@.P *юB'''򮮮 &-bϝ;Wp\w(ݲe˪UnnnO<Fx†YիW]i=gׯ =,eCC3Pt <%;v,vttܼy3X8cƌwvvdݻw`pppjjZNNN[ꔔ ETTC@4iqA9:p#yyyx5:t3333 [)@ 555̙3G{ЄbYYY'7om۶ f͚W(2J3fظqŋ0JqF5eʔdER8E޶ lmm(!E緵syxxLDGz3ý5A|>xjkk< rr9aZ @'-:::A*N:>n?!B 1A8N.^bG 2 IDATRK̺=z~ʐ l :Mhtt}||O:N3@Wt-ŋٳG.d VWuM~ڈ"VWWϟ?N0!=="E$^aX a4]4]&D"Qkk^/A J"܁<==g98IUH!Rdٺ|ӕ^ą0PΝ#>.K}}>ގ3ԠtqFW!)XgϺ y/}[ <>m R888?^( 9zfWt& )SP<فs@l?:A 趠peX555}tvv&={fUHh&𼧧>HGJҔSFIQu xjlT*rj` Eh*0UfCj_]ĉ^^^|1o=36J2##C" fBfoo܂QF-\nAAJ:ve*B ryjjرc{9O~~R D3ǫbbb+]"*eaa_nٲ}g(M}qL~h D3}M6-X|sM/$..N&=}tʕ}ꚖFޓZ9rZNLLdh&8;;8p@Ѥ]8::PrȪ*VT*Z޻w/sK]̙3'>>9>>ð/^$$$ddd$&&|<~xHHw}to>\P(/tYF$J`JJ7o_Astt<|Vk"''GL< O]4{90__ߡm%00t]~~'|񂂂~8|nooqˋ -Td0D U$'=R411;y<Ν;ԴtR>OƝ;w>wv^^X,̤̋?!:jzݺuBP(_\0 ILL % _?Y8X,oWDd?qѣGL*xzzup/񬮮 |>?((F!h [YY;vժUMMM"Ǜ9s&vP]] ?66M6! t'uA΁ AcK_$:@.X"==={6>>ŋ`lٲ~+E ~(:}rcaak_.kmm?ydll,aQ@ }嗕޽{x5ddd 7T! m&=(F{tH!+T!A9j^o]F 05C 05C 05C 05C 05C 05C y uLL 8Y|ot^0˗/7n^os=Xqqvss+..6O}-88d6WXXpO~e I$.W^^DhBE!+P_-=++ښ}||\%K HS$1o4RhH@0p;;֦FٰaH$DQQQz3|"\.]Z`D/>.\`,'ɏf68EO2̞=x4Ri^^^UUժU1 ;sLZZZee߷~ DhBE!w{v{?~<''Íf͚EUTTH$={0ĀQQQsuDi )4 uC (,,tvvl/:RRR={VRRRRRظo>Ç' yf@@_|tggӧOC˗/_bٲe˖-zSA`0 0Sx'RGl2T:RRR?1==]$1bϞ="4Po"ԐڲeH$CBBFrYѣGCBBӧ=<<8s^^^ @ 4&ׅٳg$ώ|^ܐsuuu >>.A!Vڵk@ 0l֭ -:u*8T^^>uTD9sfӦM7oe% nݺJ(G|ZJѣGߺu9fD_O?yyyq8! %C<)XXXYXX;b#wo؏a{*++b1ŕPfkZǵZ-1EbƌCKrrrXX؆꭮) X,PhkkdɒF){xh@q- J$C@E@IIP(^zU>~822C Ai >4 LYз___TJ( EEE\.7!!4 =vZJx;vh4}#~gggtA1A,=k.JsE1;l <7<<|͚5^")))j:99900p / LII !UZZ:aB10SSSu %(K.=qZp”)Sk.GGGL;w8sNЙr8sj4.%\r9  j47 \#N(^~@Wd-)"=Jͯ̌7߻SVQ~_Nfaeee[n'VT` ;:D0}\.ONN7oY˓H$RAث L9srX^l6;++#337otww۶m#^PqA]n0@x=cƌ7^x A(iRTQ`XSL?IVT*]]]m+++֖8(^t|~[[Y9w ydz3ý5A|>xcnkk< l?ϗ8r>O8 'M$JSNL78ѠE @Wŋ,|Ν;8gwa qi&h=z~ʐ :^ }ԩN芮x={rLp>OQ2V*[Grm#|3,566X,}WXQVVF(=jii h2Lnð>hݺuwϔ^VVV ,{ 5|hH%8III="w}0 6o~8~mp?!/"##kkkfpX,VKKf&vڨM.ѱK\ehI˗=jjjrpp`NcDوfjݥSN:t|ennfo(}>02iӦ?077iE,)) ?N! Tcǎq\ӕJeFFD"hs "--F"ZB*B ryjjرc{9O~~R D3ǫbbb+fĉ^^^|Aܠeaa_nٲjЁd\&Co_L>}ӦM , 1 Hd2ӧOW\giii囹#GDf j4MzzK<хcMM Yݾ};,8qbNNFΞ8q"999Z6''g@QTjz޽#G2Çk>.F/^$$$ddd$&&|%4 @߶m/!(s̉oiiinnBZVVj[ZZC|4i^Z?~"*6lwxxxddޅ'=Oh۷O.+ E׺^^^666^^^}BC@:?TWWDv\GBECtpzK&-Zx{{߻whieeHqC·#GL<ݽo ik!tAiDB(((pqqihh:}944绸:u%8???kkkMnnH$ w&Dz3ý5AxYlٳuy+Ribb"YWDīeپdX7mڴZ ۋL;wz=xqqq.AijjZt)0Dt OLL eh ׮]cX׮]c6EV[N( ׃+Pcϳfp8bo!KLL$nd:#]ktѣGSX,OObH?ѓxVWW|>TSSː"=Jƺ i*-5w=7ӳbŊtc hee~EeXl?F#Dhi~6N0~;qZ[[O<aذ@ Csss#_%##c]@U`m~C N QSҟ38o|l,(U`mgP$; `"+Eb@ S*0@ S*0@ S*0@ S*0@ S*0@ S~ɛoތ}x;yg]9r.dBg3 Maa'Ù>}˗XTT$H\_yy9l[qq1ڂy<^ppL&cOݫWVV5C*D ˔@|hH ĐX4Ϟ=r-[n{@˚?HJaFٰaH$DQQQPS)<}tŊcƌrΝO(zd8nS!7m6-r݆D[껈#VG 0h_=ؚ5k-ZTQQ!H3MT*˫ZjՊ+x̙J??ok׮}ALLLdd$|~EEOJJb0cEu񜜜Ç766:tѡ"1`TTܹs{7h`CCjxn իW߿ƍ[t)ПJDDݻw]6j()))Ϟ=+)))))illܷo.Ç"RXtX,xbuu֭[=ڇçpЦLC .===]]]]]ZVѨTJ] /6n?rgQ3f;;;qtssc~EVPtBT*h4ϟ>}:]\\>|x]]+5d4)͠fΜy9]tLquu ,+##cҤIVVV'N<{,PmllxbݽnޅbݻW$?~ڳgX,7nܿ/Р[ ر?EN8А67 v|>?..įs)www6?B0p}v/]WWpwqqĔ6ϡF{{;H]]yT*JQQQrOOO]">͛7'L .H.2UL6-%%^o^M*ҧ1)M:fTlnn7n8:;;A6=mڴK.&|o]]HĦLrU;v찵%0_5q[ѨT*eggG{B.ښ^xٳO>n|a\0l֭N P*ǎ{?+++Sv++KT544b6o<;;;p*BQ(FbDFFqㆮfV4t=z?g8 ]>M1###@=y?tss+**"9s? VZE49rdeeٳ7o o ]nō's`7otww۶mxRl?yż8pСLq6jqjAd03g]AAf:::233]tL8x.7 {bۉ?ϝ;Ng|~[[y[[[ޅbT*ǻDw@ pd}(J(a|>_.8._s`3fظqŋ yY +H{۷o/jY9tP* Dׇ8^]]=|.;a„tph4^p\]"M6m"PQ^*..NNNH$Njjj̙fu50|\}||O:N}]t>{򥵵uss9s/T:uTʑbG @ K 4V(zh~FX*3`>#8wܬYL9>`wJE~m8pŋpNe4Q J%qAW4Ƚ\]] aNe2H$jmm FPL <.d+i@]2rH*k]8^__>,<<(.+4= __ȭ[zyy8>uԍ7FGGS4<555}rFPTTRW!)hB\W^.X>`8>M ^vL ZV*̟?xsNWWמǏ_\\L9Ғ-t|`[&LhxH.kr7n… jj@OŽFDGG777+4{{{ O&s܂Ju1. DootR!HD!\.OMM;vlWnFZyFIDAT9rZNLL$DWW״4ׂ$I\\L&{ʕ+л&==^4ЗRsp>j)_h\,,,-[?.+4= \_݉':th…-Ztppt$guo8~xHHw}LZԔ4i$BQTRtBs̉oiiinn%(Cߟ|1"B~Zd?@.ð ܹ|+tOSW49aü###0 333 IKK7o.X 0JeiiVK!''GL|T| "ߨH\S+--򲱱uhpGP]dL&[hwђ2ח|WŁ83SnnH$ w&v544Y[[Jssshh(wqq9u K9܉M]KFa8>>><~'>tˑ#G&Olcc^XXD/nee%Kr)FYmx :31`jرVjjjIE} Ő'ϟ655bqff&9Kcccy<޴iMtMMMK. t݉˗,Ya… =}N|3P=hl둄pJD 1Tn0T|kii~6Hڸϟ7o.asHommm?ydll,J@ a. 1/ =@oAo3G/ (fffJwann*0VZD>l؍WxLenVqp@ Y/6q>t,^t @ װrsszLaww7;/,^jF@ `rc]D 05C 05[zNVF?@ Cz sfffÆktgO\u;sW988(<{q3C+W:wjVz麟=BT@0 ÆEZxOO󮇗Ć͹|ybZzdmcm"q=G@t8a@+0ի&cYV`Æ o8~ag#' @ AptC p'^O_{-^4ȝ wrI=ݿ;u:0' .n M7:!o}]+ӊ0#H'pw֖fvwQ/M=x&41(r?i/$,<}U`O7<n݉0D7w7raq֬…!aRuij5ضӟ\ޓǍaͩ78:0b0kIu\`VT… ͛K֍ ~}kO(A߅D"Ā2 x ?mmmAn1 /B+079\ޣuw軐 d}hw!r믿7.!a6`F,,, Epy'w!.$6@53f̜1}q܈? H>Oͯ^YZZқJ軐M}x0Y?OƲH\/l{8 5J28}>ӳ#u_TuwwݻwQToocYXܦ[z v튱@ ̍12533>ld4c=W`X^ @XҡFYţ6 9m,sUPGtq0 @ 獪W{YX3Vh@q:x@ ƳdŭfImWOaXrmPgkoDCir@Uh(CBCН8yR$74ڐ &bDQvb @ ;j[̆5+wEw֡1om<7AC]yΎ+(`?K^4j5웍Kϡ%@ ooU;|RӍc[Y_o}.龟/,OgsPе9ZuqkÑGv Q^A@dFg8`Ýν;3D/wnni:{sVUUQяDS6Όgs~W7wC׮]={6i1kғ' fͺ~:Uj{ڨ䐧&J4MIO`.]vyKC9 )/_r~ɧ#=$┗ܻ!R577SmXE:\fKՖ%iiig{:{!3c_#6&*m:EgV+"+BZSMǭ8Ȋ TR6+Ww,;;WcZ119R4!aӧeee ˥R)Z[ܲonnr=%ؾcGBBBsSЁ!H[E}83Zm=63R!d° E2CKV6mVPU$;w|WvvR1?ZsrRV'BNϹ=A{dem=vhʊI.1C  ohqg1 ;w`) Ƅۋ %M575m߱^홶@$cMƱmLH:JΆa-.wmdF3nydQ)zfBMVEo}NCX,tAkVPՙ/'1txͽŅ(JX{7 ?GZ`vA02r0HmݲewNNN&lJV0+kP X}{U_HcHTa֥!֥>b;bI2b!DEܾ-1f3{_{^ 0 .|>h2MfL1Dgzc]Ȳ iwvҥ>~[STTDuuuuuu%GAIIK-0B`eWJ΂/\P*u:YFgQQ6{WHZW"un,ݻ'-m]PpDk|PmmKN0@WbEҎ_K~]3+5Z '$&͔yyG!sYkBkצ `7X<樹EMJU 40b猙d=M/MchPM!+BH?l&s;6x:*G^d0 ;q bS*~'mebR\Sa$/JP[?vry4t*=Eo,âl>_K6n<OPsq BgxذlV®L_jEBL?-]=}!t< YڿSTۗw?ۣL;R'M٤p H|tꏮΨxsyIJȲ 9m'p[JMWon8BQK00 |>` ~=5Z͛V+HjI& Bx!~) >~~IENDB`trace-cmd-2.8.3/Documentation/HTML/index.html000066400000000000000000000561061351617527000207540ustar00rootroot00000000000000 KernelShark

KernelShark


Table of Contents

Introduction

The Graph View

  • Zooming In
  • Zooming Out
  • Graph Markers
  • Graph Cursor
  • Graph Plots
  • Task Plots
  • CPU Plots
  • The List View

  • Selecting an event
  • Graph follows toggle
  • Filters

  • Task Filter
  • Event Filter
  • Advance Event Filter
  • Introduction

    KernelShark is a front end reader of trace-cmd(1) output. "trace-cmd record" and "trace-cmd extract" create a trace.dat (trace-cmd.dat(5)) file. kernelshark can read this file and produce a graph and list view of its data.

    The application has two main viewing areas split by a paned divider. The top half is a graphical display of the data and the bottom half is a list view of each event. Underneath the menu bar is the graph information area:

    Graph Info Area

    The graph information line displays the timestamp of various locations. The Pointer: shows the timestamp of where the mouse pointer is. The Cursor: is the timestamp of the cursor location. To select a cursor location, double click on the graph. Marker A is set with a left mouse click and Marker B is set with a with a left mouse click while holding down the shift key.

    The graph is broken into two parts, the plot title section:

    Plot Title

    and the plot area:

    The plot area contains the data of the given plot, where plots can be per CPU or per task. The top of the plot area shows a timeline. The numbers in the timeline are seconds. The time in the timeline is taken from the timestamps within the trace.dat file which are architecture dependent. The time usually is the timestamp from when the system started.

    Below the graph is the list area.

    List Area

    The list area contains the Page of the list. The list can hold a maximum of 1 million entries per page. If the trace data contains more than a million entries, then the list will have more than one page.

    The list area also contains a search field to search for elements in the list.

    The Graph View

    The graph view of kernelshark shows graphical plots of the data stored in the trace.dat file. The data plots are per CPU or per task. When there are too many events within the resolution of the graph, the plots will appear as a rainbow colored bar. To make more sense out of the graphs, you need to zoom into a given location to see the details of that time frame more clearly.

    Zooming In

    To zoom in, left mouse click and hold and then drag the mouse right, release to zoom. When you click the left mouse button a line will appear and stay at that location. When you move the mouse to the right, another line appears and will follow the mouse. When you release the mouse button, the area between the two lines become the new width of the screen. That is, the graph zooms in until the lines match the width of the screen. This allows you to zoom into a specific location.

    The area that you selected will now become the new width of the viewable area. The smaller the selection, the deeper the zoom. Note, that you must select 10 pixels to zoom in. Less than 10 pixels will cancel the zoom. You can continue zooming in until you get more details.

    To save on resources, when zooming in, the beginning and end of the full trace may not be reachable with the horizontal scroll bar. If a plot contains no events within the reachable area, then the line will be empty, as CPU 1 is in the above image.

    CPU 0 shows two tasks that were running. One task is given a pink/red color and the other a green color. The think colored horizontal bar represents a task other than idle was running. The small lines that jet out of the bar are where events occur.

    If you zoom in enough such that a single event has enough room between itself and other events, the type of event and the name and PID of the task that was running will appear over the event.

    Zooming Out

    To zoom back out, left mouse click and hold and then drag the mouse left. This time the width between the two lines will become a single pixel. The farther apart the lines are, the farther the zoom out will be. Zoom out will stop at the width of the full trace.

    When the mouse is over an event, a tool tip will appear showing the event name, the latency data, the event info, the timestamp and the task name and task process ID.

    Graph Markers

    There are two markers that can be placed on the graph as well as a cursor. Marker A is set by a left mouse click. When a marker is set, the graph info area will be updated. Marker A is represented by a green line:

    To set Marker B, press and hold the shift key and click the left mouse button. Marker B will show up in red.

    When both the A and B markers are set, the graph info area will show the timestamp of where the A and B markers are, as well as the difference between the two. All timestamps are in seconds, with a resolution of microseconds.

    Graph Cursor

    Double clicking on the graph will set the cursor. The cursor is a blue line, and when it is set, it will also select the event in the list view that is the closest event at the timeline of where the cursor was selected.

    The above shows that list item 217448 (sys_exit) was the closest event to where the cursor was selected.

    Note that setting the cursor with double click will also set Marker A.

    Graph Plots

    The graph data is represented by plots. The data on the plots is either CPU specific or task specific. If it is CPU specific, then the data is the timeline of events that happened on a given CPU (which CPU is shown in the plot title area). If the plot is task specific, then the timeline of events is for the given task regardless of what CPU it was on at the time. The task name is also shown in the plot title area.

    By default, all the CPUs within the loaded trace.dat file are plotted. There are two ways to plot a task. One way is to right mouse click over a displayed task in the graph and select the plot option. This will add the task plot directly underneath the CPU plot that the task was on where the right mouse click took place. The other way is to use the Plots menu.

    Task Plots

    Selecting the "Tasks" menu item will bring up a dialog with all the tasks that were found in the trace data.

    Selecting a task in this dialog will add the task plot to the bottom of the graph area. Unselecting a task in this dialog will remove the plot.

    The colors in the task plots are different depending on which CPU the task was on at the time. The CPU plots change colors as different tasks run on the CPU, and the task plots change color depending on what CPU the task is running on. This makes it easy to see how much a task bounces around the CPUs. Zooming in on a task plot also shows some more characteristics of the task.

    The hollow green bar that is shown in front of some events in the task plot represents when the task was woken up from a sleeping state to when it actually ran. The hollow red bar between some events shows that the task was preempted by another task even though that task was still runnable.

    Since the hollow green bar shows the wake up latency of the task, the A,B markers can be used to measure that time.

    The above shows that the epiphany-browser with PID 28072 had a 479 microsecond wake up latency. The same can be done with the preemption latency.

    CPU Plots

    Selecting the "CPUs" plot menu item pops up a dialog that shows the available CPUs that can be plotted.

    Removing a selected CPU and hitting "Apply" will remove that CPU plot.

    The List View

    The list view is in the bottom half paned window and can be expanded or shortened with the paned handle.

    The top of the list view contains the list area which has the list page, list search, and "graph follows" toggle button. If more than a million events are stored in the list, then each set of million will be on a different page.

    The columns of the list are:

    • # - the index into the list.
    • CPU - the CPU that the event occurred on.
    • Time Stamp - The timestamp of the event. This is in seconds with microsecond resolution.
    • PID - The process ID of the task that was running when the event occurred.
    • Latency - The latency is broken into 5 fields:
      1. Interrupts disabled - 'd' if interrupts are disabled, otherwise '.'
      2. Need reschedule - 'N' if the kernel was notified that a schedule is needed, otherwise '.'
      3. In IRQ - 'h' if in a hard IRQ (hardware triggered), 's' if in a soft IRQ (context where the kernel initiated a the IRQ handler) or if soft IRQs are disabled, 'H' if in a hard IRQ and soft IRQs are disabled or the hard IRQ triggered while processing a soft IRQ, otherwise '.'
      4. Preemption counter - The index of the preemption counter. If it is other than zero, then the kernel will not preempt the running tasks, even if a schedule has been requested by some event. If the counter is zero, then '.' is shown.
      5. Lock depth - The depth of the big kernel lock being held. The big kernel lock is recursive (same task may acquire it multiple times). On the first acquisition, the depth is zero. This field will be zero or greater, or '.' if the big kernel lock is not held. When the big kernel lock is finally removed from the kernel, this field will go away as well.
    • Event - The name of the event.
    • Info - The data output of a particular event.

    The list search can find an event based on the contents in a row. Select a column, a match criteria and the content to match to find the next row that matches the search. The match criterion is one of the following:

    • contains - the row cell of the selected column contains the match data. This works with numbers as well.
    • full match - the row cell of the selected column matches exactly the match data.
    • does not have - the row cell of the selected column does not contain the match data.

    The search will find the next event that has the matching data within the column.

    Selecting an event

    A single click on a row will select the row, but a double click on a row will select that row as well as set the graph cursor to the location of that event. If the plot that the event is on is not visible then the graph will adjust its vertical view area to show the plot with the selected event. This has no effect on graph markers.

    Graph follows toggle

    When the "graph follows" toggle is set, then even a single click on a row will move the graph cursor. With the mouse focus on the list, using the keyboard up and down arrow keys will move the selection of the list as well as the graph cursor.

    Filters

    The amount of data that can be stored in a trace.dat file can be enormous. To make any sense out of some traces, it may be required to only display various events. The same can be true about tasks. Kernelshark has filters for tasks as well as for events.

    On start up of KernelShark, the task and event filters for the list and graph are kept synchronized. That is, any modification to the list filters will also update the graph filters, and vice versa. By selecting Filter->Unsync Graph and List Task Filters or Filter->Unsync Graph and List Event Filters will make the graph and list have their own task or event filters respectively.

    Task Filter

    The task filter can be set by either a right mouse click over an event on either the graph or the list view, and by selecting the option to add or remove the task to/from the task filter. A set of tasks may be added to the filters via the Filter menu in the menu bar.

    When tasks are selected from the menu bar the filter will be enabled automatically. When they are selected via the pop up menu, the filter needs to be enabled by the Enable menu also in the pop up menu.

    There are two types of task filters:

    • Task Filter - only show tasks that are in this filter
    • Hide Tasks - do not display the tasks within this filter

    If there are any tasks within the Task Filter then only those tasks will be displayed when the filter is enabled. Any task within the Hide Tasks filter will not be displayed, even if that same task is in the Task Filter.

    When either filter contains a task, the filter can be enabled.

    When a task is not in the "Task Filter", the pop up will show the menu item "Add task". When a task is in the "Task Filter" the pop up will show "Remove task".

    When a task is not in the "Hide Tasks", the pop up will show the menu item "Hide task". When a task is in the "Hide Tasks", the pop up will show "Show task".

    The scheduling events

    The events "sched_switch", "sched_wakeup", and "sched_wakeup_new" are treated differently by the task filter. Because these events deal with two tasks (previous and next, waker and wakee), if either of the tasks should be visible then that event is visible regardless if the other task should be hidden. This may seem confusing when an event that is hidden shows up in the Task column.

    Event Filter

    Like the task filter, the graph and list event filters start off synchronized. To make the list and graph have their own filter, just select the Filter->Unsync Graph and List Event Filters. The same can be done for task filters.

    Now the graph and list view will each have their own event filter. The event filters are enabled through the Filter menu on the top menu-bar.

    Notice that the menu changed after selecting Unsync Graph and List Event Filters. The events option was replaced by graph events and list events.

    Selecting either the "list events" or "graph events" will bring up the event dialog with the events that are visible selected.

    Note: these do not mean that the events exist in the trace.dat file. They are selected if the events are not to be hidden. Events that do not exist in the trace will not be displayed regardless of whether or not they are filtered.

    Clicking on "All" or any of the systems will either deselect all events underneath or select all events underneath depending on the previous state of the box being selected. By deselecting all events, it makes it easier to enable just a few individual events.

    To sync back the graph and list events, just select the Sync Graph and List Event Filters.

    If the selected events for the graph and list event filters are the same, then the two will become in-sync again. If not, a dialog will pop up asking how you want to sync the filters.

    • Sync List Filter with Graph Filter - will set the list event filter to be the same as what is in the graph filter.
    • Sync Graph Filter with List Filter - will set the graph event filter to be the same as what is in the list filter.

    By default the Keep the filters in sync is selected. If this is selected when OK is clicked, the graph and list filters will stay synchronized when one is changed. By unselecting the Keep option, the filters will by synchronized by the selected method, but a change to one of the filters will not affect the other.

    Advanced Event Filter

    Filtering on events may not be enough. The ability to filter on the content of individual events may be needed. In order to accomplish this, the advanced event filtering is used. Selecting the "list advanced event" or "graph advanced event" from the Filter menu will pop up the advanced event filtering dialog. The graph and list view each have their own advanced event filter.

    The "Filter:" entry at the bottom of the dialog is where the advanced filter is written. Above that is helper buttons to pick events, operations and event fields. The syntax of the filter is:

       FILTER := EVENTS | EVENTS ':' EXPRESSION
       EVENTS := EVENTS ',' EVENTS | SYSTEM '/' EVENT | SYSTEM | EVENT
       SYSTEM := any system name
       EVENT  := any event name
       EXPRESSION := EXPRESSION BOOL EXPRESSION | '(' EXPRESSION ')' | OPERATION
       BOOL   := '&&' | '||'
       OPERATION := '!' EXPRESSION | LVALUE CMP RVALUE | LVALUE STRCMP STRVALUE
       CMP    := '>' | '<' | '==' | '>=' | '<=' | '!='
       STRCMP := '==' | '!=' | '=~' | '!~'
       RVALUE := integer | FIELD
       STRVALUE := string (double quoted value) | FIELD
       LVALUE := FIELD | EXPR
       EXPR   := FIELD OP RVALUE | '(' EXPR ')' | EXPR OP EXPR
       FIELD  := a field name of an event
       OP     := '+' | '-' | '*' | '/' | '<<' | '>>' | '&' | '!'
    

    Spaces are ignored. The example used in the dialog figure:

      sched/sched_switch : next_prio < 100 && (prev_prio > 100 && prev_pid != 0)
    

    The sched/ is not necessary because without it, the filter will process all events named sched_switch, and since there is only one event that has that name, including the sched/ is redundant.

    The next_prio, prev_prio and prev_pid are all event fields of the sched_swich event.

    If just sched was used and the /sched_switch was omitted, it would still be a valid filter, but it would behave differently. By just specifying a system, the filter will run on all events within that system. When a field is encountered that does not belong to an event, then that compare will be set to false.

       sched : prev_pid != 0
       sched : !(prev_pid == 0)
    

    The above two filters are not equivalent. They are for the sched_switch event, but not for the other events. The first filter will return false for all events that do not contain the prev_pid field, but the second filter would return true for all events that do not contain that field. Again, if the event does not contain a field, just that compare will be evaluated to false, not the entire expression. This means for events that do not have the prev_pid field, the above filters would be equivalent to:

       sched : FALSE
       sched : !(FALSE)
    

    Letting filters contain fields that do not belong to an event be valid allows for various tricks where two events can share the same filter.

       sched_switch, sched_wake.* : next_pid == 1 || pid == 1
    

    The schedule events that have next_pid and not pid as a field will just compare the first part of the || and those events with pid but without next_pid will be compared against the second part of the ||

    Notice that event names in the filter can be regular expressions.

    String fields can have regular expressions used in their comparing if =~ or !~ are used.

       sched_switch : next_comm =~ "^events/[23]$"
    

    The available regular expressions are described in regex(7).

    Note: When adding an advanced filter, all non-advanced filters (added by the event filter dialog box) will be removed, and only the advanced filters will stay. But non-advanced filters may be added after advanced filters have been. The events that have advanced filters will be shaded in the event filter dialog:

    Just do not click on the advanced filter box and hit "Apply" unless you want to remove the advanced filter. Non-advanced filters can now be selected without affecting the advanced filters.

    When advanced filters already exist when the advanced filter dialog box pops up, they will be listed in the area at the top of the dialog. Although one filter may have been written, the list will be per event. A check box is to the left of the filter. When checked, the filter will be deleted if the "Apply" button is selected.

    trace-cmd-2.8.3/Documentation/Makefile000066400000000000000000000057751351617527000176610ustar00rootroot00000000000000 obj := $(obj)/Documentation src := $(src)/Documentation ifeq ($(VERBOSE),1) Q = print_asciidoc = print_xsltproc = print_install = hide_xsltproc_output = else Q = @ print_asciidoc = echo ' ASCIIDOC '`basename $@`; print_xsltproc = echo ' XSLTPROC '`basename $@`; print_install = echo ' INSTALL '`basename $1`' to $(DESTDIR_SQ)'$2; hide_xsltproc_output = 2> /dev/null endif define manpage.xsl if [ -z ${MANPAGE_DOCBOOK_XSL} ]; then \ echo "*********************************"; \ echo "** No docbook.xsl is installed **"; \ echo "** Can't make man pages **"; \ echo "*********************************"; \ exit 1; \ fi endef do_asciidoc_build = \ ($(print_asciidoc) \ asciidoc -d manpage -b docbook -o $@ $<) do_xsltproc_build = \ ($(print_xsltproc) \ xsltproc --nonet -o $@ ${MANPAGE_DOCBOOK_XSL} $< $(hide_xsltproc_output)) # # asciidoc requires a synopsis, but file format man pages (5) do # not require them. This removes it from the file in the final step. define remove_synopsis (sed -e '/^\.SH "SYNOPSIS"/,/ignore/d' $1 > $1.tmp;\ mv $1.tmp $1) endef # # Most likely a docbook.xsl is already installed on the users system # instead of creating a new wheel, lets reuse what's already there. # FIND_MANPAGE_DOCBOOK_XSL := $(shell find /usr -name docbook.xsl 2>/dev/null | grep manpages | head -1) MANPAGE_DOCBOOK_XSL ?= ${FIND_MANPAGE_DOCBOOK_XSL} $(obj)/%.xsl: $(src)/%.txt $(Q)mkdir -p $(obj) $(Q)$(do_asciidoc_build) $(obj)/%.1: $(obj)/%.1.xsl @$(call manpage.xsl) $(Q)$(do_xsltproc_build) $(obj)/%.5: $(obj)/%.5.xsl @$(call manpage.xsl) $(Q)$(do_xsltproc_build) $(Q)$(call remove_synopsis, $@) TEXT1 = $(wildcard *.1.txt) MAN1 = $(patsubst %.1.txt,$(obj)/%.1, ${TEXT1}) TEXT5 = $(wildcard *.5.txt) MAN5 = $(patsubst %.5.txt,$(obj)/%.5, ${TEXT5}) all: $(MAN1) $(MAN5) # Need to find out how to export a macro instead of # copying this from the main Makefile. define do_install_data $(print_install) \ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ fi; \ $(INSTALL) -m 644 $1 '$(DESTDIR_SQ)$2' endef MAN1_INSTALL = $(MAN1:%.1=%.1.install) MAN5_INSTALL = $(MAN5:%.5=%.5.install) $(MAN1_INSTALL): %.1.install : %.1 force $(Q)$(call do_install_data,$<,$(man_dir_SQ)/man1) $(MAN5_INSTALL): %.5.install : %.5 force $(Q)$(call do_install_data,$<,$(man_dir_SQ)/man5) html_dir = $(src)/HTML image_dir = $(html_dir)/images HTML = $(wildcard $(html_dir)/*.html) IMGS = $(wildcard $(image_dir)/*.png) HTML_INSTALL = $(subst .html,.html.install,$(HTML)) IMGS_INSTALL = $(subst .png,.png.install,$(IMGS)) $(HTML_INSTALL): %.html.install : %.html force $(Q)$(call do_install_data,$<,'$(html_install_SQ)') $(IMGS_INSTALL): %.png.install : %.png force $(Q)$(call do_install_data,$<,'$(img_install_SQ)') GUI_INSTALL = $(HTML_INSTALL) $(IMGS_INSTALL) install: $(MAN1_INSTALL) $(MAN5_INSTALL) $(GUI_INSTALL) clean: (cd $(obj); \ $(RM) *.xml *.xsl *.1 *.5) .PHONE: force force: trace-cmd-2.8.3/Documentation/README.PythonPlugin000066400000000000000000000067431351617527000215340ustar00rootroot00000000000000 PYTHON PLUGIN DOCUMENTATION ============================= With the python plugin (make python-plugin) you can now write plugins in python. The API exported by the python plugin itself (written in C) allows you to access most information about a record from python. To write a python plugin, put a new .py file into a new ~/.trace-cmd/python/ directory. The most basic python plugin is this: --- %< --- def register(pevent): pass --- >% --- which obviously does nothing at all. To register a callback, use the pevent.register_event_handler function: --- %< --- import tracecmd def my_event_handler(trace_seq, event): pass def register(pevent): pevent.register_event_handler("subsys", "event_name", my_event_handler) --- >% --- There are four object types that you get, described below. tracecmd.PEvent ----------------- This is the class of the 'pevent' object above, you get one of those via your register callback. It has one method and one property: * register_event_handler() - example above, to register an event handler function * file_endian - either '<' or '>' indicating which endianness the file has, to be used with struct.unpack() tracecmd.TraceSeq ------------------- This is the class of the 'trace_seq' parameter to your callback function. It has only one method, puts(), to put data into the buffer. Formatting must be done in python. tracecmd.Event ---------------------- This is the class of the 'event' parameter to your callback function. Note that it doesn't just contain the format, but also the event data. As such, you can do much with this, and this is what you'll usually use. Each instance of this allows access to record items via the dict protocol, and you can get the items via its keys() methods. So for example, your callback could be --- %< --- def my_callback(trace_seq, event): for fieldname in event.keys(): field = event[fieldname] --- >% --- Each field returned from the dict protocol is an instance of the next (and last) class: tracecmd.Field ---------------------- This is an instance of a field, including its data. It affords numerous use cases and is what you'll be using most. * If this is an integer field, i.e. 1, 2, 4 or 8 bytes long, you can convert it to the number contained, according to the file's endianness, by simply casting it to a long: field = event['myint'] value = long(field) * You can access the field's data, as field.data, and if the data is really a "__data_loc" type that will be resolved automatically. (If you don't know what this means, don't worry about it and just use field.data) This is it. It's pretty simple. A fully-featured plugin could look like this: --- %< --- def my_event_handler(trace_seq, event): trace_seq.puts("myev: %u", long(event['myfield'])) def register(pevent): pevent.register_event_handler("subsys", "event_name", my_event_handler) --- >% --- Tips and tricks ----------------- Be familiar with the struct module and use it, always checking endianness and potentially using pevent.file_endian. If you need access to pevent in your callbacks, simply pass it in yourself: --- %< --- def my_event_handler(pevent, trace_seq, event): pass def register(pevent): pevent.register_event_handler("subsys", "event_name", lambda *args: my_event_handler(pevent, *args) ) --- >% --- trace-cmd-2.8.3/Documentation/kernelshark.1.txt000066400000000000000000000015341351617527000214170ustar00rootroot00000000000000KERNELSHARK(1) ============== NAME ---- kernelshark - graphical reader for trace-cmd(1) output SYNOPSIS -------- *kernelshark* ['OPTIONS'] DESCRIPTION ----------- KernelShark is a front end reader of trace-cmd(1) output. It reads a trace-cmd.dat(5) formatted file and produces a graph and list view of the data. OPTIONS ------- *-h*:: Display the help text. *-v*:: Display the kernelshark version and exit. *-i* 'input-file':: Read trace data from the file 'input-file'. By default input is read from 'trace.dat'. SEE ALSO -------- trace-cmd(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-check-events.1.txt000066400000000000000000000024011351617527000233140ustar00rootroot00000000000000TRACE-CMD-CHECK_EVENTS(1) ========================= NAME ---- trace-cmd-check-events - parse the event formats on local system SYNOPSIS -------- *trace-cmd check-events* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) check-events parses format strings for all the events on the local system. It returns whether all the format strings can be parsed correctly. It will load plugins unless specified otherwise. This is useful to check for any trace event format strings which may contain some internal kernel function references which cannot be decoded outside of the kernel. This may mean that either the unparsed format strings of the trace events need to be changed or that a plugin needs to be created to parse them. OPTIONS ------- *-N* - Don't load plugins SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1), trace-cmd-start(1) AUTHOR ------ Written by Vaibhav Nagarnaik, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2011 Google, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-extract.1.txt000066400000000000000000000062001351617527000224100ustar00rootroot00000000000000TRACE-CMD-EXTRACT(1) ==================== NAME ---- trace-cmd-extract - extract out the data from the Ftrace Linux tracer. SYNOPSIS -------- *trace-cmd extract ['OPTIONS']* DESCRIPTION ----------- The trace-cmd(1) extract is usually used after 'trace-cmd-start(1)' and 'trace-cmd-stop(1)'. It can be used after the Ftrace tracer has been started manually through the Ftrace pseudo file system. The extract command creates a trace.dat file that can be used by 'trace-cmd-report(1)' to read from. It reads the kernel internal ring buffer to produce the trace.dat file. OPTIONS ------- *-p* 'plugin':: Although *extract* does not start any traces, some of the plugins require just reading the output in ASCII format. These are the latency tracers, since the latency tracers have a separate internal buffer. The plugin option is therefore only necessary for the 'wakeup', 'wakeup-rt', 'irqsoff', 'preemptoff' and 'preemptirqsoff' plugins. With out this option, the extract command will extract from the internal Ftrace buffers. *-O* 'option':: If a latency tracer is being extracted, and the *-p* option is used, then there are some Ftrace options that can change the format. This will update those options before extracting. To see the list of options see 'trace-cmd-list'. To enable an option, write its name, to disable the option append the characters 'no' to it. For example: 'noprint-parent' will disable the 'print-parent' option that prints the parent function in printing a function event. *-o* 'outputfile':: By default, the extract command will create a 'trace.dat' file. This option will change where the file is written to. *-s*:: Extract from the snapshot buffer (if the kernel supports it). *--date*:: This is the same as the trace-cmd-record(1) --date option, but it does cause the extract routine to disable all tracing. That is, the end of the extract will perform something similar to trace-cmd-reset(1). *-B* 'buffer-name':: If the kernel supports multiple buffers, this will extract the trace for only the given buffer. It does not affect any other buffer. This may be used multiple times to specify different buffers. When this option is used, the top level instance will not be extracted unless *-t* is given. *-a*:: Extract all existing buffer instances. When this option is used, the top level instance will not be extracted unless *-t* is given. *-t*:: Extracts the top level instance buffer. Without the *-B* or *-a* option this is the same as the default. But if *-B* or *-a* is used, this is required if the top level instance buffer should also be extracted. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-hist.1.txt000066400000000000000000000027311351617527000217120ustar00rootroot00000000000000TRACE-CMD-HIST(1) ================= NAME ---- trace-cmd-hist - show histogram of events in trace.dat file SYNOPSIS -------- *trace-cmd hist* ['OPTIONS']['input-file'] DESCRIPTION ----------- The trace-cmd(1) hist displays a histogram form from the trace.dat file. Instead of showing the events as they were ordered, it creates a histogram that can be displayed per task or for all tasks where the most common events appear first. It uses the function tracer and call stacks that it finds to try to put together a call graph of the events. OPTIONS ------- *-i* 'input-file':: By default, trace-cmd hist will read the file 'trace.dat'. But the *-i* option open up the given 'input-file' instead. Note, the input file may also be specified as the last item on the command line. *-P*:: To compact all events and show the call graphs by ignoring tasks and different PIDs, add the *-P* to do so. Instead of showing the task name, it will group all chains together and show "". SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-list.1.txt000066400000000000000000000042261351617527000217170ustar00rootroot00000000000000TRACE-CMD-LIST(1) ================= NAME ---- trace-cmd-list - list available plugins, events or options for Ftrace. SYNOPSIS -------- *trace-cmd list* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) list displays the available plugins, events or Ftrace options that are configured on the current machine. If no option is given, then it lists all plugins, events and Ftrace options to standard output. OPTIONS ------- *-e* ['regex']:: This option will list the available events that are enabled on the local system. It takes an optional argument that uses 'regcomp(3)' expressions to seach. trace-cmd list -e '^sys.*' *-F*:: Used with *-e* 'regex' to show those events formats. *-l*:: Used with *-e* 'regex' to show those events filters. *-R*:: Used with *-e* 'regex' to show those events triggers. *-t*:: This option will list the available tracers that are enabled on the local system. *-p*:: Same as *-t* and only for legacy purposes. *-o*:: This option will list the available Ftrace options that are configured on the local system. *-f* ['regex']:: This option will list the available filter functions. These are the list of functions on the system that you can trace, or filter on. It takes an optional argument that uses 'regcomp(3)' expressions to seach. trace-cmd list -f '^sched.*' *-P*:: List the plugin files that get loaded on trace-cmd report. *-O*:: List plugin options that can be used by trace-cmd report *-O* option. *-B*:: List defined buffer instances (sub buffers). *-C*:: List defined clocks that can be used with trace-cmd record -C. The one in brackets ([]) is the active clock. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-listen.1.txt000066400000000000000000000030441351617527000222370ustar00rootroot00000000000000TRACE-CMD-LISTEN(1) =================== NAME ---- trace-cmd-listen - listen for incoming connection to record tracing. SYNOPSIS -------- *trace-cmd listen* -p 'port' ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) listen sets up a port to listen to waiting for connections from other hosts that run 'trace-cmd-record(1)' with the *-N* option. When a connection is made, and the remote host sends data, it will create a file called 'trace.HOST:PORT.dat'. Where HOST is the name of the remote host, and PORT is the port that the remote host used to connect with. OPTIONS ------- *-p* 'port':: This option will specify the port to listen to. *-D*:: This options causes trace-cmd listen to go into a daemon mode and run in the background. *-d* 'dir':: This option specifies a directory to write the data files into. *-o* 'filename':: This option overrides the default 'trace' in the 'trace.HOST:PORT.dat' that is created when a remote host connects. *-l* 'filename':: This option writes the output messages to a log file instead of standard output. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-mem.1.txt000066400000000000000000000071251351617527000215230ustar00rootroot00000000000000TRACE-CMD-MEM(1) ================ NAME ---- trace-cmd-mem - show memory usage of certain kmem events SYNOPSIS -------- *trace-cmd mem* ['OPTIONS']['input-file'] DESCRIPTION ----------- The trace-cmd(1) mem requires a trace-cmd record that enabled the following events: kmalloc kmalloc_node kfree kmem_cache_alloc kmem_cache_alloc_node kmem_cache_alloc_free It then reads the amount requested and the ammount freed as well as the functions that called the allocation. It then reports the final amount of bytes requested and allocated, along with the total amount allocated and requested, as well as the max allocation and requested during the run. It reports the amount of wasted bytes (allocated - requested) that was not freed, as well as the max wasted amount during the run. The list is sorted by descending order of wasted bytes after the run. Function Waste Alloc req TotAlloc TotReq MaxAlloc MaxReq MaxWaste -------- ----- ----- --- -------- ------ -------- ------ -------- rb_allocate_cpu_buffer 768 2304 1536 2304 1536 2304 1536 768 alloc_pipe_info 400 1152 752 1152 752 1152 752 400 instance_mkdir 252 544 292 544 292 544 292 252 __d_alloc 215 1086560 1086345 1087208 1086993 1086560 1086345 215 get_empty_filp 72 2304 2232 4864 4712 4864 4712 152 mm_alloc 40 960 920 960 920 960 920 40 prepare_creds 32 192 160 1728 1440 1728 1440 288 tracing_buffers_open 8 32 24 32 24 32 24 8 do_brk 0 0 0 368 368 368 368 0 journal_add_journal_head 0 6048 6048 6048 6048 6048 6048 0 journal_start 0 0 0 1224 1224 48 48 0 __rb_allocate_pages 0 3289856 3289856 3289856 3289856 3289856 3289856 0 anon_vma_alloc 0 0 0 936 936 864 864 0 [...] OPTIONS ------- *-i* 'input-file':: By default, trace-cmd hist will read the file 'trace.dat'. But the *-i* option open up the given 'input-file' instead. Note, the input file may also be specified as the last item on the command line. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-hist(1), trace-cmd-split(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2013 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-options.1.txt000066400000000000000000000014321351617527000224330ustar00rootroot00000000000000TRACE-CMD-OPTIONS(1) ==================== NAME ---- trace-cmd-options - list available options from trace-cmd plugins SYNOPSIS -------- *trace-cmd options* DESCRIPTION ----------- The trace-cmd(1) options command will examine all the trace-cmd plugins that are used by *trace-cmd report(1)* and list them. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2011 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-profile.1.txt000066400000000000000000000761621351617527000224140ustar00rootroot00000000000000TRACE-CMD-PROFILE(1) ==================== NAME ---- trace-cmd-profile - profile tasks running live SYNOPSIS -------- *trace-cmd profile ['OPTIONS']* ['command'] DESCRIPTION ----------- The trace-cmd(1) profile will start tracing just like trace-cmd-record(1), with the *--profile* option, except that it does not write to a file, but instead, it will read the events as they happen and will update the accounting of the events. When the trace is finished, it will report the results just like trace-cmd-report(1) would do with its *--profile* option. In other words, the profile command does the work of trace-cmd record --profile, and trace-cmd report --profile without having to record the data to disk, in between. The advantage of using the profile command is that the profiling can be done over a long period of time where recording all events would take up too much disk space. This will enable several events as well as the function graph tracer with a depth of one (if the kernel supports it). This is to show where tasks enter and exit the kernel and how long they were in the kernel. To disable calling function graph, use the *-p* option to enable another tracer. To not enable any tracer, use *-p nop*. All timings are currently in nanoseconds. OPTIONS ------- These are the same as trace-cmd-record(1) with the *--profile* option. *-p* 'tracer':: Set a tracer plugin to run instead of function graph tracing set to depth of 1. To not run any tracer, use *-p nop*. *-S*:: Only enable the tracer or events speficied on the command line. With this option, the function_graph tracer is not enabled, nor are any events (like sched_switch), unless they are specifically specified on the command line (i.e. -p function -e sched_switch -e sched_wakeup) *-G*:: Set interrupt (soft and hard) events as global (associated to CPU instead of tasks). *-o* 'file':: Write the output of the profile to 'file'. This supersedes *--stderr* *-H* 'event-hooks':: Add custom event matching to connect any two events together. Format is: [:],[,]/ [:],[,] The start_system:start_event (start_system is optional), is the event that starts the timing. start_match is the field in the start event that is to match with the end_match in the end event. start_pid is optional, as matches are attached to the tasks that run the events, if another field should be used to find that task, then it is specified with start_pid. end_system:end_event is the event that ends the timing (end_system is optional). end_match is the field in end_match that wil match the start event field start_match. flags are optional and can be the following (case insensitive): p : The two events are pinned to the same CPU (start and end happen on the same CPU always). s : The event should have a stack traced with it (enable stack tracing for the start event). g : The event is global (not associated to a task). start_pid is not applicable with this flag. *--stderr*:: Redirect the output to stderr. The output of the command being executed is not changed. This allows watching the command execute and saving the output of the profile to another file. EXAMPLES -------- --- # trace-cmd profile -F sleep 1 [..] task: sleep-1121 Event: sched_switch:R (2) Total: 234559 Avg: 117279 Max: 129886 Min:104673 | + ftrace_raw_event_sched_switch (0xffffffff8109f310) 100% (2) time:234559 max:129886 min:104673 avg:117279 __schedule (0xffffffff816c1e81) preempt_schedule (0xffffffff816c236e) ___preempt_schedule (0xffffffff81351a59) | + unmap_single_vma (0xffffffff81198c05) | 55% (1) time:129886 max:129886 min:0 avg:129886 | stop_one_cpu (0xffffffff8110909a) | sched_exec (0xffffffff810a119b) | do_execveat_common.isra.31 (0xffffffff811de528) | do_execve (0xffffffff811dea8c) | SyS_execve (0xffffffff811ded1e) | return_to_handler (0xffffffff816c8458) | stub_execve (0xffffffff816c6929) | + unmap_single_vma (0xffffffff81198c05) 45% (1) time:104673 max:104673 min:0 avg:104673 unmap_vmas (0xffffffff81199174) exit_mmap (0xffffffff811a1f5b) mmput (0xffffffff8107699a) flush_old_exec (0xffffffff811ddb75) load_elf_binary (0xffffffff812287df) search_binary_handler (0xffffffff811dd3e0) do_execveat_common.isra.31 (0xffffffff811de8bd) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) stub_execve (0xffffffff816c6929) Event: sched_switch:S (1) Total: 1000513242 Avg: 1000513242 Max: 1000513242 Min:1000513242 | + ftrace_raw_event_sched_switch (0xffffffff8109f310) 100% (1) time:1000513242 max:1000513242 min:0 avg:1000513242 __schedule (0xffffffff816c1e81) schedule (0xffffffff816c23b9) do_nanosleep (0xffffffff816c4f1c) hrtimer_nanosleep (0xffffffff810dcd86) SyS_nanosleep (0xffffffff810dcea6) return_to_handler (0xffffffff816c8458) tracesys_phase2 (0xffffffff816c65b0) Event: sched_wakeup:1121 (1) Total: 43405 Avg: 43405 Max: 43405 Min:43405 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:43405 max:43405 min:0 avg:43405 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) wake_up_process (0xffffffff810a4057) hrtimer_wakeup (0xffffffff810db772) __run_hrtimer (0xffffffff810dbd91) hrtimer_interrupt (0xffffffff810dc6b7) local_apic_timer_interrupt (0xffffffff810363e7) smp_trace_apic_timer_interrupt (0xffffffff816c8c6a) trace_apic_timer_interrupt (0xffffffff816c725a) finish_task_switch (0xffffffff8109c3a4) __schedule (0xffffffff816c1e01) schedule (0xffffffff816c23b9) ring_buffer_wait (0xffffffff811323a3) wait_on_pipe (0xffffffff81133d93) tracing_buffers_splice_read (0xffffffff811350b0) do_splice_to (0xffffffff8120476f) SyS_splice (0xffffffff81206c1f) tracesys_phase2 (0xffffffff816c65b0) Event: func: sys_nanosleep() (1) Total: 1000598016 Avg: 1000598016 Max: 1000598016 Min:1000598016 Event: func: sys_munmap() (1) Total: 14300 Avg: 14300 Max: 14300 Min:14300 Event: func: sys_arch_prctl() (1) Total: 571 Avg: 571 Max: 571 Min:571 Event: func: sys_mprotect() (4) Total: 14382 Avg: 3595 Max: 7196 Min:2190 Event: func: SyS_read() (1) Total: 2640 Avg: 2640 Max: 2640 Min:2640 Event: func: sys_close() (5) Total: 4001 Avg: 800 Max: 1252 Min:414 Event: func: sys_newfstat() (3) Total: 11684 Avg: 3894 Max: 10206 Min:636 Event: func: SyS_open() (3) Total: 23615 Avg: 7871 Max: 10535 Min:4743 Event: func: sys_access() (1) Total: 5924 Avg: 5924 Max: 5924 Min:5924 Event: func: SyS_mmap() (8) Total: 39153 Avg: 4894 Max: 12354 Min:1518 Event: func: smp_trace_apic_timer_interrupt() (1) Total: 10298 Avg: 10298 Max: 10298 Min:10298 Event: func: SyS_brk() (4) Total: 2407 Avg: 601 Max: 1564 Min:206 Event: func: do_notify_resume() (2) Total: 4095 Avg: 2047 Max: 2521 Min:1574 Event: func: sys_execve() (5) Total: 1625251 Avg: 325050 Max: 1605698 Min:3570 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:1605698 max:1605698 min:0 avg:1605698 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) wake_up_process (0xffffffff810a4057) cpu_stop_queue_work (0xffffffff81108df8) stop_one_cpu (0xffffffff8110909a) sched_exec (0xffffffff810a119b) do_execveat_common.isra.31 (0xffffffff811de528) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) stub_execve (0xffffffff816c6929) stub_execve (0xffffffff816c6929) Event: func: syscall_trace_enter_phase2() (38) Total: 21544 Avg: 566 Max: 1066 Min:329 Event: func: syscall_trace_enter_phase1() (38) Total: 9202 Avg: 242 Max: 376 Min:150 Event: func: __do_page_fault() (53) Total: 257672 Avg: 4861 Max: 27745 Min:458 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:27745 max:27745 min:0 avg:27745 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) default_wake_function (0xffffffff810a4002) autoremove_wake_function (0xffffffff810b50fd) __wake_up_common (0xffffffff810b4958) __wake_up (0xffffffff810b4cb8) rb_wake_up_waiters (0xffffffff8112f126) irq_work_run_list (0xffffffff81157d0f) irq_work_run (0xffffffff81157d5e) smp_trace_irq_work_interrupt (0xffffffff810082fc) trace_irq_work_interrupt (0xffffffff816c7aaa) return_to_handler (0xffffffff816c8458) trace_do_page_fault (0xffffffff810478b2) trace_page_fault (0xffffffff816c7dd2) Event: func: syscall_trace_leave() (38) Total: 26145 Avg: 688 Max: 1264 Min:381 Event: func: __sb_end_write() (1) Total: 373 Avg: 373 Max: 373 Min:373 Event: func: fsnotify() (1) Total: 598 Avg: 598 Max: 598 Min:598 Event: func: __fsnotify_parent() (1) Total: 286 Avg: 286 Max: 286 Min:286 Event: func: mutex_unlock() (2) Total: 39636 Avg: 19818 Max: 39413 Min:223 Event: func: smp_trace_irq_work_interrupt() (6) Total: 236459 Avg: 39409 Max: 100671 Min:634 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (4) time:234348 max:100671 min:38745 avg:58587 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) default_wake_function (0xffffffff810a4002) autoremove_wake_function (0xffffffff810b50fd) __wake_up_common (0xffffffff810b4958) __wake_up (0xffffffff810b4cb8) rb_wake_up_waiters (0xffffffff8112f126) irq_work_run_list (0xffffffff81157d0f) irq_work_run (0xffffffff81157d5e) smp_trace_irq_work_interrupt (0xffffffff810082fc) return_to_handler (0xffffffff816c8458) trace_irq_work_interrupt (0xffffffff816c7aaa) | + ftrace_return_to_handler (0xffffffff81140840) | 84% (3) time:197396 max:100671 min:38745 avg:65798 | return_to_handler (0xffffffff816c846d) | trace_page_fault (0xffffffff816c7dd2) | + ftrace_return_to_handler (0xffffffff81140840) 16% (1) time:36952 max:36952 min:0 avg:36952 ftrace_graph_caller (0xffffffff816c8428) mutex_unlock (0xffffffff816c3f75) rb_simple_write (0xffffffff81133142) vfs_write (0xffffffff811d7727) SyS_write (0xffffffff811d7acf) tracesys_phase2 (0xffffffff816c65b0) Event: sys_enter:35 (1) Total: 1000599765 Avg: 1000599765 Max: 1000599765 Min:1000599765 Event: sys_enter:11 (1) Total: 55025 Avg: 55025 Max: 55025 Min:55025 Event: sys_enter:158 (1) Total: 1584 Avg: 1584 Max: 1584 Min:1584 Event: sys_enter:10 (4) Total: 18359 Avg: 4589 Max: 8764 Min:2933 Event: sys_enter:0 (1) Total: 4223 Avg: 4223 Max: 4223 Min:4223 Event: sys_enter:3 (5) Total: 9948 Avg: 1989 Max: 2606 Min:1203 Event: sys_enter:5 (3) Total: 15530 Avg: 5176 Max: 11840 Min:1405 Event: sys_enter:2 (3) Total: 28002 Avg: 9334 Max: 12035 Min:5656 Event: sys_enter:21 (1) Total: 7814 Avg: 7814 Max: 7814 Min:7814 Event: sys_enter:9 (8) Total: 49583 Avg: 6197 Max: 14137 Min:2362 Event: sys_enter:12 (4) Total: 108493 Avg: 27123 Max: 104079 Min:922 Event: sys_enter:59 (5) Total: 1631608 Avg: 326321 Max: 1607529 Min:4563 Event: page_fault_user:0x398d86b630 (1) Event: page_fault_user:0x398d844de0 (1) Event: page_fault_user:0x398d8d9020 (1) Event: page_fault_user:0x1d37008 (1) Event: page_fault_user:0x7f0b89e91074 (1) Event: page_fault_user:0x7f0b89d98ed0 (1) Event: page_fault_user:0x7f0b89ec8950 (1) Event: page_fault_user:0x7f0b89d83644 (1) Event: page_fault_user:0x7f0b89d622a8 (1) Event: page_fault_user:0x7f0b89d5a560 (1) Event: page_fault_user:0x7f0b89d34010 (1) Event: page_fault_user:0x1d36008 (1) Event: page_fault_user:0x398d900510 (1) Event: page_fault_user:0x398dbb3ae8 (1) Event: page_fault_user:0x398d87f490 (1) Event: page_fault_user:0x398d8eb660 (1) Event: page_fault_user:0x398d8bd730 (1) Event: page_fault_user:0x398d9625d9 (1) Event: page_fault_user:0x398d931810 (1) Event: page_fault_user:0x398dbb7114 (1) Event: page_fault_user:0x398d837610 (1) Event: page_fault_user:0x398d89e860 (1) Event: page_fault_user:0x398d8f23b0 (1) Event: page_fault_user:0x398dbb4510 (1) Event: page_fault_user:0x398dbad6f0 (1) Event: page_fault_user:0x398dbb1018 (1) Event: page_fault_user:0x398d977b37 (1) Event: page_fault_user:0x398d92eb60 (1) Event: page_fault_user:0x398d8abff0 (1) Event: page_fault_user:0x398dbb0d30 (1) Event: page_fault_user:0x398dbb6c24 (1) Event: page_fault_user:0x398d821c50 (1) Event: page_fault_user:0x398dbb6c20 (1) Event: page_fault_user:0x398d886350 (1) Event: page_fault_user:0x7f0b90125000 (1) Event: page_fault_user:0x7f0b90124740 (1) Event: page_fault_user:0x7f0b90126000 (1) Event: page_fault_user:0x398d816230 (1) Event: page_fault_user:0x398d8002b8 (1) Event: page_fault_user:0x398dbb0b40 (1) Event: page_fault_user:0x398dbb2880 (1) Event: page_fault_user:0x7f0b90141cc6 (1) Event: page_fault_user:0x7f0b9013b85c (1) Event: page_fault_user:0x7f0b90127000 (1) Event: page_fault_user:0x606e70 (1) Event: page_fault_user:0x7f0b90144010 (1) Event: page_fault_user:0x7fffcb31b038 (1) Event: page_fault_user:0x606da8 (1) Event: page_fault_user:0x400040 (1) Event: page_fault_user:0x398d222218 (1) Event: page_fault_user:0x398d015120 (1) Event: page_fault_user:0x398d220ce8 (1) Event: page_fault_user:0x398d220b80 (1) Event: page_fault_user:0x7fffcb2fcff8 (1) Event: page_fault_user:0x398d001590 (1) Event: page_fault_user:0x398d838490 (1) Event: softirq_raise:RCU (3) Total: 252931 Avg: 84310 Max: 243288 Min:4639 Event: softirq_raise:SCHED (2) Total: 241249 Avg: 120624 Max: 239076 Min:2173 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:239076 max:239076 min:0 avg:239076 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) default_wake_function (0xffffffff810a4002) autoremove_wake_function (0xffffffff810b50fd) __wake_up_common (0xffffffff810b4958) __wake_up (0xffffffff810b4cb8) rb_wake_up_waiters (0xffffffff8112f126) irq_work_run_list (0xffffffff81157d0f) irq_work_run (0xffffffff81157d5e) smp_trace_irq_work_interrupt (0xffffffff810082fc) trace_irq_work_interrupt (0xffffffff816c7aaa) irq_exit (0xffffffff8107dd66) smp_trace_apic_timer_interrupt (0xffffffff816c8c7a) trace_apic_timer_interrupt (0xffffffff816c725a) prepare_ftrace_return (0xffffffff8103d4fd) ftrace_graph_caller (0xffffffff816c8428) mem_cgroup_begin_page_stat (0xffffffff811cfd25) page_remove_rmap (0xffffffff811a4fc5) stub_execve (0xffffffff816c6929) unmap_single_vma (0xffffffff81198b1c) unmap_vmas (0xffffffff81199174) exit_mmap (0xffffffff811a1f5b) mmput (0xffffffff8107699a) flush_old_exec (0xffffffff811ddb75) load_elf_binary (0xffffffff812287df) search_binary_handler (0xffffffff811dd3e0) do_execveat_common.isra.31 (0xffffffff811de8bd) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) Event: softirq_raise:HI (3) Total: 72472 Avg: 24157 Max: 64186 Min:3430 Event: softirq_entry:RCU (2) Total: 3191 Avg: 1595 Max: 1788 Min:1403 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:1788 max:1788 min:0 avg:1788 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) default_wake_function (0xffffffff810a4002) autoremove_wake_function (0xffffffff810b50fd) __wake_up_common (0xffffffff810b4958) __wake_up (0xffffffff810b4cb8) rb_wake_up_waiters (0xffffffff8112f126) irq_work_run_list (0xffffffff81157d0f) irq_work_run (0xffffffff81157d5e) smp_trace_irq_work_interrupt (0xffffffff810082fc) trace_irq_work_interrupt (0xffffffff816c7aaa) irq_work_queue (0xffffffff81157e95) ring_buffer_unlock_commit (0xffffffff8113039f) __buffer_unlock_commit (0xffffffff811367d5) trace_buffer_unlock_commit (0xffffffff811376a2) ftrace_event_buffer_commit (0xffffffff81146d5f) ftrace_raw_event_sched_process_exec (0xffffffff8109c511) do_execveat_common.isra.31 (0xffffffff811de9a3) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) stub_execve (0xffffffff816c6929) Event: softirq_entry:SCHED (2) Total: 2289 Avg: 1144 Max: 1350 Min:939 Event: softirq_entry:HI (3) Total: 180146 Avg: 60048 Max: 178969 Min:499 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:178969 max:178969 min:0 avg:178969 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) wake_up_process (0xffffffff810a4057) wake_up_worker (0xffffffff8108de74) insert_work (0xffffffff8108fca6) __queue_work (0xffffffff8108fe12) delayed_work_timer_fn (0xffffffff81090088) call_timer_fn (0xffffffff810d8f89) run_timer_softirq (0xffffffff810da8a1) __do_softirq (0xffffffff8107d8fa) irq_exit (0xffffffff8107dd66) smp_trace_apic_timer_interrupt (0xffffffff816c8c7a) trace_apic_timer_interrupt (0xffffffff816c725a) prepare_ftrace_return (0xffffffff8103d4fd) ftrace_graph_caller (0xffffffff816c8428) mem_cgroup_begin_page_stat (0xffffffff811cfd25) page_remove_rmap (0xffffffff811a4fc5) stub_execve (0xffffffff816c6929) unmap_single_vma (0xffffffff81198b1c) unmap_vmas (0xffffffff81199174) exit_mmap (0xffffffff811a1f5b) mmput (0xffffffff8107699a) flush_old_exec (0xffffffff811ddb75) load_elf_binary (0xffffffff812287df) search_binary_handler (0xffffffff811dd3e0) do_execveat_common.isra.31 (0xffffffff811de8bd) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) --- The above uses *-F* to follow the sleep task. It filters only on events that pertain to sleep. Note, in order to follow forks, you need to also include the *-c* flag. Other tasks will appear in the profile as well if events reference more than one task (like sched_switch and sched_wakeup do. The "prev_pid" and "next_pid" of sched_switch, and the "common_pid" and "pid" of sched_wakeup). Stack traces are attached to events that are related to them. Taking a look at the above output: Event: sched_switch:R (2) Total: 234559 Avg: 117279 Max: 129886 Min:104673 This shows that task was preempted (it's in the running 'R' state). It was preempted twice '(2)' for a total of 234,559 nanoseconds, with a average preempt time of 117,279 ns, and maximum of 128,886 ns and minimum of 104,673 ns. The tree shows where it was preempted: | + ftrace_raw_event_sched_switch (0xffffffff8109f310) 100% (2) time:234559 max:129886 min:104673 avg:117279 __schedule (0xffffffff816c1e81) preempt_schedule (0xffffffff816c236e) ___preempt_schedule (0xffffffff81351a59) | + unmap_single_vma (0xffffffff81198c05) | 55% (1) time:129886 max:129886 min:0 avg:129886 | stop_one_cpu (0xffffffff8110909a) | sched_exec (0xffffffff810a119b) | do_execveat_common.isra.31 (0xffffffff811de528) | do_execve (0xffffffff811dea8c) | SyS_execve (0xffffffff811ded1e) | return_to_handler (0xffffffff816c8458) | stub_execve (0xffffffff816c6929) | + unmap_single_vma (0xffffffff81198c05) 45% (1) time:104673 max:104673 min:0 avg:104673 unmap_vmas (0xffffffff81199174) exit_mmap (0xffffffff811a1f5b) mmput (0xffffffff8107699a) flush_old_exec (0xffffffff811ddb75) load_elf_binary (0xffffffff812287df) search_binary_handler (0xffffffff811dd3e0) do_execveat_common.isra.31 (0xffffffff811de8bd) do_execve (0xffffffff811dea8c) SyS_execve (0xffffffff811ded1e) return_to_handler (0xffffffff816c8458) stub_execve (0xffffffff816c6929) Event: sched_switch:S (1) Total: 1000513242 Avg: 1000513242 Max: 1000513242 Min:10005132 This shows that the task was scheduled out in the INTERRUPTIBLE state once for a total of 1,000,513,242 ns (~1s), which makes sense as the task was a "sleep 1". After the schedule events, the function events are shown. By default the profiler will use the function graph tracer if the depth setting is supported by the kernel. It will set the depth to one which will only trace the first function that enters the kernel. It will also record the amount of time it was in the kernel. Event: func: sys_nanosleep() (1) Total: 1000598016 Avg: 1000598016 Max: 1000598016 Min:1000598016 Event: func: sys_munmap() (1) Total: 14300 Avg: 14300 Max: 14300 Min:14300 Event: func: sys_arch_prctl() (1) Total: 571 Avg: 571 Max: 571 Min:571 Event: func: sys_mprotect() (4) Total: 14382 Avg: 3595 Max: 7196 Min:2190 Event: func: SyS_read() (1) Total: 2640 Avg: 2640 Max: 2640 Min:2640 Event: func: sys_close() (5) Total: 4001 Avg: 800 Max: 1252 Min:414 Event: func: sys_newfstat() (3) Total: 11684 Avg: 3894 Max: 10206 Min:636 Event: func: SyS_open() (3) Total: 23615 Avg: 7871 Max: 10535 Min:4743 Event: func: sys_access() (1) Total: 5924 Avg: 5924 Max: 5924 Min:5924 Event: func: SyS_mmap() (8) Total: 39153 Avg: 4894 Max: 12354 Min:1518 Event: func: smp_trace_apic_timer_interrupt() (1) Total: 10298 Avg: 10298 Max: 10298 Min:10298 Event: func: SyS_brk() (4) Total: 2407 Avg: 601 Max: 1564 Min:206 Event: func: do_notify_resume() (2) Total: 4095 Avg: 2047 Max: 2521 Min:1574 Event: func: sys_execve() (5) Total: 1625251 Avg: 325050 Max: 1605698 Min:3570 Count of times the event was hit is always in parenthesis '(5)'. The function graph trace may produce too much overhead as it is still triggering (just not tracing) on all functions. To limit functions just to system calls (not interrupts), add the following option: -l 'sys_*' -l 'SyS_*' To disable function graph tracing totally, use: -p nop To use function tracing instead (note, this will not record timings, but just the count of times a function is hit): -p function Following the functions are the events that are recorded. Event: sys_enter:35 (1) Total: 1000599765 Avg: 1000599765 Max: 1000599765 Min:1000599765 Event: sys_enter:11 (1) Total: 55025 Avg: 55025 Max: 55025 Min:55025 Event: sys_enter:158 (1) Total: 1584 Avg: 1584 Max: 1584 Min:1584 Event: sys_enter:10 (4) Total: 18359 Avg: 4589 Max: 8764 Min:2933 Event: sys_enter:0 (1) Total: 4223 Avg: 4223 Max: 4223 Min:4223 Event: sys_enter:3 (5) Total: 9948 Avg: 1989 Max: 2606 Min:1203 Event: sys_enter:5 (3) Total: 15530 Avg: 5176 Max: 11840 Min:1405 Event: sys_enter:2 (3) Total: 28002 Avg: 9334 Max: 12035 Min:5656 Event: sys_enter:21 (1) Total: 7814 Avg: 7814 Max: 7814 Min:7814 Event: sys_enter:9 (8) Total: 49583 Avg: 6197 Max: 14137 Min:2362 Event: sys_enter:12 (4) Total: 108493 Avg: 27123 Max: 104079 Min:922 Event: sys_enter:59 (5) Total: 1631608 Avg: 326321 Max: 1607529 Min:4563 These are the raw system call events, with the raw system call ID after the "sys_enter:" For example, "59" is execve(2). Why did it execute 5 times? Looking at a strace of this run, we can see: execve("/usr/lib64/ccache/sleep", ["sleep", "1"], [/* 27 vars */] <... execve resumed> ) = -1 ENOENT (No such file or directory) execve("/usr/local/sbin/sleep", ["sleep", "1"], [/* 27 vars */] <... execve resumed> ) = -1 ENOENT (No such file or directory) execve("/usr/local/bin/sleep", ["sleep", "1"], [/* 27 vars */] <... execve resumed> ) = -1 ENOENT (No such file or directory) execve("/usr/sbin/sleep", ["sleep", "1"], [/* 27 vars */] <... execve resumed> ) = -1 ENOENT (No such file or directory) execve("/usr/bin/sleep", ["sleep", "1"], [/* 27 vars */] <... execve resumed> ) = 0 It attempted to execve the "sleep" command for each path in $PATH until it found one. The page_fault_user events show what userspace address took a page fault. Event: softirq_raise:RCU (3) Total: 252931 Avg: 84310 Max: 243288 Min:4639 Event: softirq_raise:SCHED (2) Total: 241249 Avg: 120624 Max: 239076 Min:2173 | + ftrace_raw_event_sched_wakeup_template (0xffffffff8109d960) 100% (1) time:239076 max:239076 min:0 avg:239076 ttwu_do_wakeup (0xffffffff810a01a2) ttwu_do_activate.constprop.122 (0xffffffff810a0236) try_to_wake_up (0xffffffff810a3ec3) default_wake_function (0xffffffff810a4002) autoremove_wake_function (0xffffffff810b50fd) __wake_up_common (0xffffffff810b4958) __wake_up (0xffffffff810b4cb8) rb_wake_up_waiters (0xffffffff8112f126) irq_work_run_list (0xffffffff81157d0f) irq_work_run (0xffffffff81157d5e) smp_trace_irq_work_interrupt (0xffffffff810082fc) trace_irq_work_interrupt (0xffffffff816c7aaa) irq_exit (0xffffffff8107dd66) The timings for the softirq_raise events measure the time it took from the raised softirq to the time it executed. The timings for the softirq_entry events measure the time the softirq took to execute. The stack traces for the softirqs (and possibly other events) are used when an event has a stack attached to it. This can happen if the profile ran more stacks than just the sched events, or when events are dropped and stacks To have full control of what gets traced, use the *-S* option that will have trace-cmd not enable any events or the function_graph tracer. Only the events listed on the command line are shown. If only the time of kmalloc is needed to be seen, and where it was recorded, using the *-S* option and enabling function_graph and stack tracing for just the function needed will give the profile of only that function. --- # trace-cmd profile -S -p function_graph -l '*kmalloc*' -l '*kmalloc*:stacktrace' sleep 1 task: sshd-11786 Event: func: __kmalloc_reserve.isra.59() (2) Total: 149684 Avg: 74842 Max: 75598 Min:74086 | + __alloc_skb (0xffffffff815a8917) | 67% (2) time:149684 max:75598 min:74086 avg:74842 | __kmalloc_node_track_caller (0xffffffff811c6635) | __kmalloc_reserve.isra.59 (0xffffffff815a84ac) | return_to_handler (0xffffffff816c8458) | sk_stream_alloc_skb (0xffffffff81604ea1) | tcp_sendmsg (0xffffffff8160592c) | inet_sendmsg (0xffffffff8162fed1) | sock_aio_write (0xffffffff8159f9fc) | do_sync_write (0xffffffff811d694a) | vfs_write (0xffffffff811d7825) | SyS_write (0xffffffff811d7adf) | system_call_fastpath (0xffffffff816c63d2) | + __alloc_skb (0xffffffff815a8917) 33% (1) time:74086 max:74086 min:74086 avg:74086 __alloc_skb (0xffffffff815a8917) sk_stream_alloc_skb (0xffffffff81604ea1) tcp_sendmsg (0xffffffff8160592c) inet_sendmsg (0xffffffff8162fed1) sock_aio_write (0xffffffff8159f9fc) do_sync_write (0xffffffff811d694a) vfs_write (0xffffffff811d7825) SyS_write (0xffffffff811d7adf) system_call_fastpath (0xffffffff816c63d2) [..] --- To watch the command run but save the output of the profile to a file use --stderr, and redirect stderr to a file # trace-cmd profile --stderr cyclictest -p 80 -n -t1 2> profile.out Or simple use *-o* # trace-cmd profile -o profile.out cyclictest -p 80 -n -t1 SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2014 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-record.1.txt000066400000000000000000000500741351617527000222240ustar00rootroot00000000000000TRACE-CMD-RECORD(1) =================== NAME ---- trace-cmd-record - record a trace from the Ftrace Linux internal tracer SYNOPSIS -------- *trace-cmd record* ['OPTIONS'] ['command'] DESCRIPTION ----------- The trace-cmd(1) record command will set up the Ftrace Linux kernel tracer to record the specified plugins or events that happen while the 'command' executes. If no command is given, then it will record until the user hits Ctrl-C. The record command of trace-cmd will set up the Ftrace tracer to start tracing the various events or plugins that are given on the command line. It will then create a number of tracing processes (one per CPU) that will start recording from the kernel ring buffer straight into temporary files. When the command is complete (or Ctrl-C is hit) all the files will be combined into a trace.dat file that can later be read (see trace-cmd-report(1)). OPTIONS ------- *-p* 'tracer':: Specify a tracer. Tracers usually do more than just trace an event. Common tracers are: *function*, *function_graph*, *preemptirqsoff*, *irqsoff*, *preemptoff* and *wakeup*. A tracer must be supported by the running kernel. To see a list of available tracers, see trace-cmd-list(1). *-e* 'event':: Specify an event to trace. Various static trace points have been added to the Linux kernel. They are grouped by subsystem where you can enable all events of a given subsystem or specify specific events to be enabled. The 'event' is of the format "subsystem:event-name". You can also just specify the subsystem without the ':event-name' or the event-name without the "subsystem:". Using "-e sched_switch" will enable the "sched_switch" event where as, "-e sched" will enable all events under the "sched" subsystem. The 'event' can also contain glob expressions. That is, "*stat*" will select all events (or subsystems) that have the characters "stat" in their names. The keyword 'all' can be used to enable all events. *-a*:: Every event that is being recorded has its output format file saved in the output file to be able to display it later. But if other events are enabled in the trace without trace-cmd's knowledge, the formats of those events will not be recorded and trace-cmd report will not be able to display them. If this is the case, then specify the *-a* option and the format for all events in the system will be saved. *-T*:: Enable a stacktrace on each event. For example: -0 [003] 58549.289091: sched_switch: kworker/0:1:0 [120] R ==> trace-cmd:2603 [120] -0 [003] 58549.289092: kernel_stack: => schedule (ffffffff814b260e) => cpu_idle (ffffffff8100a38c) => start_secondary (ffffffff814ab828) *--func-stack*:: Enable a stack trace on all functions. Note this is only applicable for the "function" plugin tracer, and will only take effect if the -l option is used and succeeds in limiting functions. If the function tracer is not filtered, and the stack trace is enabled, you can live lock the machine. *-f* 'filter':: Specify a filter for the previous event. This must come after a *-e*. This will filter what events get recorded based on the content of the event. Filtering is passed to the kernel directly so what filtering is allowed may depend on what version of the kernel you have. Basically, it will let you use C notation to check if an event should be processed or not. ---------------------------------------- ==, >=, <=, >, <, &, |, && and || ---------------------------------------- The above are usually safe to use to compare fields. *--no-filter*:: Do not filter out the trace-cmd threads. By default, the threads are filtered out to not be traced by events. This option will have the trace-cmd threads also be traced. *-R* 'trigger':: Specify a trigger for the previous event. This must come after a *-e*. This will add a given trigger to the given event. To only enable the trigger and not the event itself, then place the event after the *-v* option. See Documentation/trace/events.txt in the Linux kernel source for more information on triggers. *-v*:: This will cause all events specified after it on the command line to not be traced. This is useful for selecting a subsystem to be traced but to leave out various events. For Example: "-e sched -v -e "\*stat\*"" will enable all events in the sched subsystem except those that have "stat" in their names. Note: the *-v* option was taken from the way grep(1) inverts the following matches. *-F*:: This will filter only the executable that is given on the command line. If no command is given, then it will filter itself (pretty pointless). Using *-F* will let you trace only events that are caused by the given command. *-P* 'pid':: Similar to *-F* but lets you specify a process ID to trace. *-c*:: Used with either *-F* (or *-P* if kernel supports it) to trace the process' children too. *-C* 'clock':: Set the trace clock to "clock". Use trace-cmd(1) list -C to see what clocks are available. *-o* 'output-file':: By default, trace-cmd report will create a 'trace.dat' file. You can specify a different file to write to with the *-o* option. *-l* 'function-name':: This will limit the 'function' and 'function_graph' tracers to only trace the given function name. More than one *-l* may be specified on the command line to trace more than one function. The limited use of glob expressions are also allowed. These are 'match\*' to only filter functions that start with 'match'. '\*match' to only filter functions that end with 'match'. '\*match\*' to only filter on functions that contain 'match'. *-g* 'function-name':: This option is for the function_graph plugin. It will graph the given function. That is, it will only trace the function and all functions that it calls. You can have more than one *-g* on the command line. *-n* 'function-name':: This has the opposite effect of *-l*. The function given with the *-n* option will not be traced. This takes precedence, that is, if you include the same function for both *-n* and *-l*, it will not be traced. *-d*:: Some tracer plugins enable the function tracer by default. Like the latency tracers. This option prevents the function tracer from being enabled at start up. *-D*:: The option *-d* will try to use the function-trace option to disable the function tracer (if available), otherwise it defaults to the proc file: /proc/sys/kernel/ftrace_enabled, but will not touch it if the function-trace option is available. The *-D* option will disable both the ftrace_enabled proc file as well as the function-trace option if it exists. Note, this disable function tracing for all users, which includes users outside of ftrace tracers (stack_tracer, perf, etc). *-O* 'option':: Ftrace has various options that can be enabled or disabled. This allows you to set them. Appending the text 'no' to an option disables it. For example: "-O nograph-time" will disable the "graph-time" Ftrace option. *-s* 'interval':: The processes that trace-cmd creates to record from the ring buffer need to wake up to do the recording. Setting the 'interval' to zero will cause the processes to wakeup every time new data is written into the buffer. But since Ftrace is recording kernel activity, the act of this processes going back to sleep may cause new events into the ring buffer which will wake the process back up. This will needlessly add extra data into the ring buffer. The 'interval' metric is microseconds. The default is set to 1000 (1 ms). This is the time each recording process will sleep before waking up to record any new data that was written to the ring buffer. *-r* 'priority':: The priority to run the capture threads at. In a busy system the trace capturing threads may be staved and events can be lost. This increases the priority of those threads to the real time (FIFO) priority. But use this option with care, it can also change the behaviour of the system being traced. *-b* 'size':: This sets the ring buffer size to 'size' kilobytes. Because the Ftrace ring buffer is per CPU, this size is the size of each per CPU ring buffer inside the kernel. Using "-b 10000" on a machine with 4 CPUs will make Ftrace have a total buffer size of 40 Megs. *-B* 'buffer-name':: If the kernel supports multiple buffers, this will add a buffer with the given name. If the buffer name already exists, that buffer is just reset and will not be deleted at the end of record execution. If the buffer is created, it will be removed at the end of execution (unless the *-k* is set, or 'start' command was used). After a buffer name is stated, all events added after that will be associated with that buffer. If no buffer is specified, or an event is specified before a buffer name, it will be associated with the main (toplevel) buffer. trace-cmd record -e sched -B block -e block -B time -e timer sleep 1 The above is will enable all sched events in the main buffer. It will then create a 'block' buffer instance and enable all block events within that buffer. A 'time' buffer instance is created and all timer events will be enabled for that event. *-m* 'size':: The max size in kilobytes that a per cpu buffer should be. Note, due to rounding to page size, the number may not be totally correct. Also, this is performed by switching between two buffers that are half the given size thus the output may not be of the given size even if much more was written. Use this to prevent running out of diskspace for long runs. *-M* 'cpumask':: Set the cpumask for to trace. It only affects the last buffer instance given. If supplied before any buffer instance, then it affects the main buffer. The value supplied must be a hex number. trace-cmd record -p function -M c -B events13 -e all -M 5 If the -M is left out, then the mask stays the same. To enable all CPUs, pass in a value of '-1'. *-k*:: By default, when trace-cmd is finished tracing, it will reset the buffers and disable all the tracing that it enabled. This option keeps trace-cmd from disabling the tracer and reseting the buffer. This option is useful for debugging trace-cmd. Note: usually trace-cmd will set the "tracing_on" file back to what it was before it was called. This option will leave that file set to zero. *-i*:: By default, if an event is listed that trace-cmd does not find, it will exit with an error. This option will just ignore events that are listed on the command line but are not found on the system. *-N* 'host:port':: If another machine is running "trace-cmd listen", this option is used to have the data sent to that machine with UDP packets. Instead of writing to an output file, the data is sent off to a remote box. This is ideal for embedded machines with little storage, or having a single machine that will keep all the data in a single repository. Note: This option is not supported with latency tracer plugins: wakeup, wakeup_rt, irqsoff, preemptoff and preemptirqsoff *-t*:: This option is used with *-N*, when there's a need to send the live data with TCP packets instead of UDP. Although TCP is not nearly as fast as sending the UDP packets, but it may be needed if the network is not that reliable, the amount of data is not that intensive, and a guarantee is needed that all traced information is transfered successfully. *-q* | *--quiet*:: For use with recording an application. Suppresses normal output (except for errors) to allow only the application's output to be displayed. *--date*:: With the *--date* option, "trace-cmd" will write timestamps into the trace buffer after it has finished recording. It will then map the timestamp to gettimeofday which will allow wall time output from the timestamps reading the created 'trace.dat' file. *--max-graph-depth* 'depth':: Set the maximum depth the function_graph tracer will trace into a function. A value of one will only show where userspace enters the kernel but not any functions called in the kernel. The default is zero, which means no limit. *--module* 'module':: Filter a module's name in function tracing. It is equivalent to adding ':mod:module' after all other functions being filtered. If no other function filter is listed, then all modules functions will be filtered in the filter. '--module snd' is equivalent to '-l :mod:snd' '--module snd -l "*jack*"' is equivalent to '-l "*jack*:mod:snd"' '--module snd -n "*"' is equivalent to '-n :mod:snd' *--profile*:: With the *--profile* option, "trace-cmd" will enable tracing that can be used with trace-cmd-report(1) --profile option. If a tracer *-p* is not set, and function graph depth is supported by the kernel, then the function_graph tracer will be enabled with a depth of one (only show where userspace enters into the kernel). It will also enable various tracepoints with stack tracing such that the report can show where tasks have been blocked for the longest time. See trace-cmd-profile(1) for more details and examples. *-H* 'event-hooks':: Add custom event matching to connect any two events together. When not used with *--profile*, it will save the parameter and this will be used by trace-cmd report --profile, too. That is: trace-cmd record -H hrtimer_expire_entry,hrtimer/hrtimer_expire_exit,hrtimer,sp trace-cmd report --profile Will profile hrtimer_expire_entry and hrtimer_expire_ext times. See trace-cmd-profile(1) for format. *-S*:: (for --profile only) Only enable the tracer or events speficied on the command line. With this option, the function_graph tracer is not enabled, nor are any events (like sched_switch), unless they are specifically specified on the command line (i.e. -p function -e sched_switch -e sched_wakeup) *--ts-offset offset*:: Add an offset for the timestamp in the trace.dat file. This will add a offset option into the trace.dat file such that a trace-cmd report will offset all the timestamps of the events by the given offset. The offset is in raw units. That is, if the event timestamps are in nanoseconds the offset will also be in nanoseconds even if the displayed units are in microseconds. *--stderr*:: Have output go to stderr instead of stdout, but the output of the command executed will not be changed. This is useful if you want to monitor the output of the command being executed, but not see the output from trace-cmd. EXAMPLES -------- The basic way to trace all events: ------------------------------ # trace-cmd record -e all ls > /dev/null # trace-cmd report trace-cmd-13541 [003] 106260.693809: filemap_fault: address=0x128122 offset=0xce trace-cmd-13543 [001] 106260.693809: kmalloc: call_site=81128dd4 ptr=0xffff88003dd83800 bytes_req=768 bytes_alloc=1024 gfp_flags=GFP_KERNEL|GFP_ZERO ls-13545 [002] 106260.693809: kfree: call_site=810a7abb ptr=0x0 ls-13545 [002] 106260.693818: sys_exit_write: 0x1 ------------------------------ To use the function tracer with sched switch tracing: ------------------------------ # trace-cmd record -p function -e sched_switch ls > /dev/null # trace-cmd report ls-13587 [002] 106467.860310: function: hrtick_start_fair <-- pick_next_task_fair ls-13587 [002] 106467.860313: sched_switch: prev_comm=trace-cmd prev_pid=13587 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=13583 next_prio=120 trace-cmd-13585 [001] 106467.860314: function: native_set_pte_at <-- __do_fault trace-cmd-13586 [003] 106467.860314: function: up_read <-- do_page_fault ls-13587 [002] 106467.860317: function: __phys_addr <-- schedule trace-cmd-13585 [001] 106467.860318: function: _raw_spin_unlock <-- __do_fault ls-13587 [002] 106467.860320: function: native_load_sp0 <-- __switch_to trace-cmd-13586 [003] 106467.860322: function: down_read_trylock <-- do_page_fault ------------------------------ Here is a nice way to find what interrupts have the highest latency: ------------------------------------------ # trace-cmd record -p function_graph -e irq_handler_entry -l do_IRQ sleep 10 # trace-cmd report -0 [000] 157412.933969: funcgraph_entry: | do_IRQ() { -0 [000] 157412.933974: irq_handler_entry: irq=48 name=eth0 -0 [000] 157412.934004: funcgraph_exit: + 36.358 us | } -0 [000] 157413.895004: funcgraph_entry: | do_IRQ() { -0 [000] 157413.895011: irq_handler_entry: irq=48 name=eth0 -0 [000] 157413.895026: funcgraph_exit: + 24.014 us | } -0 [000] 157415.891762: funcgraph_entry: | do_IRQ() { -0 [000] 157415.891769: irq_handler_entry: irq=48 name=eth0 -0 [000] 157415.891784: funcgraph_exit: + 22.928 us | } -0 [000] 157415.934869: funcgraph_entry: | do_IRQ() { -0 [000] 157415.934874: irq_handler_entry: irq=48 name=eth0 -0 [000] 157415.934906: funcgraph_exit: + 37.512 us | } -0 [000] 157417.888373: funcgraph_entry: | do_IRQ() { -0 [000] 157417.888381: irq_handler_entry: irq=48 name=eth0 -0 [000] 157417.888398: funcgraph_exit: + 25.943 us | } ------------------------------------------ An example of the profile: ------------------------------------------ # trace-cmd record --profile sleep 1 # trace-cmd report --profile --comm sleep task: sleep-21611 Event: sched_switch:R (1) Total: 99442 Avg: 99442 Max: 99442 Min:99442 1 total:99442 min:99442 max:99442 avg=99442 => ftrace_raw_event_sched_switch (0xffffffff8105f812) => __schedule (0xffffffff8150810a) => preempt_schedule (0xffffffff8150842e) => ___preempt_schedule (0xffffffff81273354) => cpu_stop_queue_work (0xffffffff810b03c5) => stop_one_cpu (0xffffffff810b063b) => sched_exec (0xffffffff8106136d) => do_execve_common.isra.27 (0xffffffff81148c89) => do_execve (0xffffffff811490b0) => SyS_execve (0xffffffff811492c4) => return_to_handler (0xffffffff8150e3c8) => stub_execve (0xffffffff8150c699) Event: sched_switch:S (1) Total: 1000506680 Avg: 1000506680 Max: 1000506680 Min:1000506680 1 total:1000506680 min:1000506680 max:1000506680 avg=1000506680 => ftrace_raw_event_sched_switch (0xffffffff8105f812) => __schedule (0xffffffff8150810a) => schedule (0xffffffff815084b8) => do_nanosleep (0xffffffff8150b22c) => hrtimer_nanosleep (0xffffffff8108d647) => SyS_nanosleep (0xffffffff8108d72c) => return_to_handler (0xffffffff8150e3c8) => tracesys_phase2 (0xffffffff8150c304) Event: sched_wakeup:21611 (1) Total: 30326 Avg: 30326 Max: 30326 Min:30326 1 total:30326 min:30326 max:30326 avg=30326 => ftrace_raw_event_sched_wakeup_template (0xffffffff8105f653) => ttwu_do_wakeup (0xffffffff810606eb) => ttwu_do_activate.constprop.124 (0xffffffff810607c8) => try_to_wake_up (0xffffffff8106340a) ------------------------------------------ SEE ALSO -------- trace-cmd(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-report.1.txt000066400000000000000000000542561351617527000222670ustar00rootroot00000000000000TRACE-CMD-REPORT(1) =================== NAME ---- trace-cmd-report - show in ASCII a trace created by trace-cmd record SYNOPSIS -------- *trace-cmd report* ['OPTIONS'] ['input-file'] DESCRIPTION ----------- The trace-cmd(1) report command will output a human readable report of a trace created by trace-cmd record. OPTIONS ------- *-i* 'input-file':: By default, trace-cmd report will read the file 'trace.dat'. But the *-i* option open up the given 'input-file' instead. Note, the input file may also be specified as the last item on the command line. *-e*:: This outputs the endianess of the file. trace-cmd report is smart enough to be able to read big endian files on little endian machines, and vise versa. *-f*:: This outputs the list of functions that have been recorded in the file. *-P*:: This outputs the list of "trace_printk()" data. The raw trace data points to static pointers in the kernel. This must be stored in the trace.dat file. *-E*:: This lists the possible events in the file (but this list is not necessarily the list of events in the file). *--events*:: This will list the event formats that are stored in the trace.dat file. *--event* regex:: This will print events that match the given regex. If a colon is specified, then the characters before the colon will be used to match the system and the characters after the colon will match the event. trace-cmd report --event sys:read The above will only match events where the system name contains "sys" and the event name contains "read". trace-cmd report --event read The above will match all events that contain "read" in its name. Also it may list all events of a system that contains "read" as well. *--check-events*:: This will parse the event format strings that are stored in the trace.dat file and return whether the formats can be parsed correctly. It will load plugins unless *-N* is specified. *-t*:: Print the full timestamp. The timestamps in the data file are usually recorded to the nanosecond. But the default display of the timestamp is only to the microsecond. To see the full timestamp, add the *-t* option. *-F* 'filter':: Add a filter to limit what events are displayed. The format of the filter is: ------------------------------------------ ':' = SYSTEM'/'EVENT | SYSTEM | EVENT | ',' = EVENT_FIELD | '&&' | '||' | '(' ')' | '!' = '==' | '!=' | '>=' | '<=' | '>' | '<' | '&' | '|' | '^' | '+' | '-' | '*' | '/' | '%' = NUM | STRING | EVENT_FIELD ------------------------------------------ SYSTEM is the name of the system to filter on. If the EVENT is left out, then it applies to all events under the SYSTEM. If only one string is used without the '/' to deliminate between SYSTEM and EVENT, then the filter will be applied to all systems and events that match the given string. Whitespace is ignored, such that "sched:next_pid==123" is equivalent to "sched : next_pid == 123". STRING is defined with single or double quotes (single quote must end with single quote, and double with double). Whitespace within quotes are not ignored. The representation of a SYSTEM or EVENT may also be a regular expression as defined by 'regcomp(3)'. The EVENT_FIELD is the name of the field of an event that is being filtered. If the event does not contain the EVENT_FIELD, that part of the equation will be considered false. ------------------------------------------ -F 'sched : bogus == 1 || common_pid == 2' ------------------------------------------ The "bogus == 1" will always evaluate to FALSE because no event has a field called "bogus", but the "common_pid == 2" will still be evaluated since all events have the field "common_pid". Any "sched" event that was traced by the process with the PID of 2 will be shown. Note, the EVENT_FIELD is the field name as shown by an events format (as displayed with *--events*), and not what is found in the output. If the output shows "ID:foo" but the field that "foo" belongs to was called "name" in the event format, then "name" must be used in the filter. The same is true about values. If the value that is displayed is converted by to a string symbol, the filter checks the original value and not the value displayed. For example, to filter on all tasks that were in the running state at a context switch: ------------------------------------------ -F 'sched/sched_switch : prev_state==0' ------------------------------------------ Although the output displays 'R', having 'prev_stat=="R"' will not work. Note: You can also specify 'COMM' as an EVENT_FIELD. This will use the task name (or comm) of the record to compare. For example, to filter out all of the "trace-cmd" tasks: ------------------------------------------ -F '.*:COMM != "trace-cmd"' ------------------------------------------ *-I*:: Do not print events where the HARDIRQ latency flag is set. This will filter out most events that are from interrupt context. Note, it may not filter out function traced functions that are in interrupt context but were called before the kernel "in interrupt" flag was set. *-S*:: Do not print events where the SOFTIRQ latency flag is set. This will filter out most events that are from soft interrupt context. *-v*:: This causes the following filters of *-F* to filter out the matching events. ------------------------------------------ -v -F 'sched/sched_switch : prev_state == 0' ------------------------------------------ Will not display any sched_switch events that have a prev_state of 0. Removing the *-v* will only print out those events. *-T*:: Test the filters of -F. After processing a filter string, the resulting filter will be displayed for each event. This is useful for using a filter for more than one event where a field may not exist in all events. Also it can be used to make sure there are no misspelled event field names, as they will simply be ignored. *-T* is ignored if *-F* is not specified. *-V*:: Show the plugins that are loaded. *-L*:: This will not load system wide plugins. It loads "local only". That is what it finds in the ~/.trace-cmd/plugins directory. *-N*:: This will not load any plugins. *-n* 'event-re':: This will cause all events that match the option to ignore any registered handler (by the plugins) to print the event. The normal event will be printed instead. The 'event-re' is a regular expression as defined by 'regcomp(3)'. *--profile*:: With the *--profile* option, "trace-cmd report" will process all the events first, and then output a format showing where tasks have spent their time in the kernel, as well as where they are blocked the most, and where wake up latencies are. See trace-cmd-profile(1) for more details and examples. *-G*:: Set interrupt (soft and hard) events as global (associated to CPU instead of tasks). Only works for --profile. *-H* 'event-hooks':: Add custom event matching to connect any two events together. See trace-cmd-profile(1) for format. *-R*:: This will show the events in "raw" format. That is, it will ignore the event's print formatting and just print the contents of each field. *-r* 'event-re':: This will cause all events that match the option to print its raw fields. The 'event-re' is a regular expression as defined by 'regcomp(3)'. *-l*:: This adds a "latency output" format. Information about interrupts being disabled, soft irq being disabled, the "need_resched" flag being set, preempt count, and big kernel lock are all being recorded with every event. But the default display does not show this information. This option will set display this information with 6 characters. When one of the fields is zero or N/A a \'.\' is shown. ------------------------------------------ -0 0d.h1. 106467.859747: function: ktime_get <-- tick_check_idle ------------------------------------------ The 0d.h1. denotes this information. The first character is never a '.' and represents what CPU the trace was recorded on (CPU 0). The 'd' denotes that interrupts were disabled. The 'h' means that this was called inside an interrupt handler. The '1' is the preemption disabled (preempt_count) was set to one. The two '.'s are "need_resched" flag and kernel lock counter. If the "need_resched" flag is set, then that character would be a 'N'. *-w*:: If both the 'sched_switch' and 'sched_wakeup' events are enabled, then this option will report the latency between the time the task was first woken, and the time it was scheduled in. *-q*:: Quiet non critical warnings. *-O*:: Pass options to the trace-cmd plugins that are loaded. -O plugin:var=value The 'plugin:' and '=value' are optional. Value may be left off for options that are boolean. If the 'plugin:' is left off, then any variable that matches in all plugins will be set. Example: -O fgraph:tailprint *--stat*:: If the trace.dat file recorded the final stats (outputed at the end of record) the *--stat* option can be used to retrieve them. *--uname*:: If the trace.dat file recorded uname during the run, this will retrieve that information. *--version*:: If the trace.dat file recorded the version of the executable used to create it, report that version. *--ts-offset* offset:: Add (or subtract if negative) an offset for all timestamps of the previous data file specified with *-i*. This is useful to merge sort multiple trace.dat files where the difference in the timestamp is known. For example if a trace is done on a virtual guest, and another trace is done on the host. If the host timestamp is 1000 units ahead of the guest, the following can be done: trace-cmd report -i host.dat --ts-offset -1000 -i guest.dat This will subtract 1000 timestamp units from all the host events as it merges with the guest.dat events. Note, the units is for the raw units recorded in the trace. If the units are nanoseconds, the addition (or subtraction) from the offset will be nanoseconds even if the displayed units are microseconds. *--ts2secs* HZ:: Convert the current clock source into a second (nanosecond resolution) output. When using clocks like x86-tsc, if the frequency is known, by passing in the clock frequency, this will convert the time to seconds. This option affects any trace.dat file given with *-i* proceeding it. If this option comes before any *-i* option, then that value becomes the default conversion for all other trace.dat files. If another --ts2secs option appears after a *-i* trace.dat file, than that option will override the default value. Example: On a 3.4 GHz machine trace-cmd record -p function -C x86-tsc trace-cmd report --ts2ns 3400000000 The report will convert the cycles timestamps into a readable second display. The default display resolution is microseconds, unless *-t* is used. The value of --ts-offset must still be in the raw timestamp units, even with this option. The offset will be converted as well. *--ts-diff*:: Show the time differences between events. The difference will appear in parenthesis just after the timestamp. EXAMPLES -------- Using a trace.dat file that was created with: ------------------------------------------ # trace-cmd record -p function -e all sleep 5 ------------------------------------------ The default report shows: ------------------------------------------ # trace-cmd report trace-cmd-16129 [002] 158126.498411: function: __mutex_unlock_slowpath <-- mutex_unlock trace-cmd-16131 [000] 158126.498411: kmem_cache_alloc: call_site=811223c5 ptr=0xffff88003ecf2b40 bytes_req=272 bytes_alloc=320 gfp_flags=GFP_KERNEL|GFP_ZERO trace-cmd-16130 [003] 158126.498411: function: do_splice_to <-- sys_splice sleep-16133 [001] 158126.498412: function: inotify_inode_queue_event <-- vfs_write trace-cmd-16129 [002] 158126.498420: lock_release: 0xffff88003f1fa4f8 &sb->s_type->i_mutex_key trace-cmd-16131 [000] 158126.498421: function: security_file_alloc <-- get_empty_filp sleep-16133 [001] 158126.498422: function: __fsnotify_parent <-- vfs_write trace-cmd-16130 [003] 158126.498422: function: rw_verify_area <-- do_splice_to trace-cmd-16131 [000] 158126.498424: function: cap_file_alloc_security <-- security_file_alloc trace-cmd-16129 [002] 158126.498425: function: syscall_trace_leave <-- int_check_syscall_exit_work sleep-16133 [001] 158126.498426: function: inotify_dentry_parent_queue_event <-- vfs_write trace-cmd-16130 [003] 158126.498426: function: security_file_permission <-- rw_verify_area trace-cmd-16129 [002] 158126.498428: function: audit_syscall_exit <-- syscall_trace_leave [...] ------------------------------------------ To see everything but the function traces: ------------------------------------------ # trace-cmd report -v -F 'function' trace-cmd-16131 [000] 158126.498411: kmem_cache_alloc: call_site=811223c5 ptr=0xffff88003ecf2b40 bytes_req=272 bytes_alloc=320 gfp_flags=GFP_KERNEL|GFP_ZERO trace-cmd-16129 [002] 158126.498420: lock_release: 0xffff88003f1fa4f8 &sb->s_type->i_mutex_key trace-cmd-16130 [003] 158126.498436: lock_acquire: 0xffffffff8166bf78 read all_cpu_access_lock trace-cmd-16131 [000] 158126.498438: lock_acquire: 0xffff88003df5b520 read &fs->lock trace-cmd-16129 [002] 158126.498446: kfree: call_site=810a7abb ptr=0x0 trace-cmd-16130 [003] 158126.498448: lock_acquire: 0xffff880002250a80 &per_cpu(cpu_access_lock, cpu) trace-cmd-16129 [002] 158126.498450: sys_exit_splice: 0xfffffff5 trace-cmd-16131 [000] 158126.498454: lock_release: 0xffff88003df5b520 &fs->lock sleep-16133 [001] 158126.498456: kfree: call_site=810a7abb ptr=0x0 sleep-16133 [001] 158126.498460: sys_exit_write: 0x1 trace-cmd-16130 [003] 158126.498462: kmalloc: call_site=810bf95b ptr=0xffff88003dedc040 bytes_req=24 bytes_alloc=32 gfp_flags=GFP_KERNEL|GFP_ZERO ------------------------------------------ To see only the kmalloc calls that were greater than 1000 bytes: ------------------------------------------ #trace-cmd report -F 'kmalloc: bytes_req > 1000' -0 [000] 158128.126641: kmalloc: call_site=81330635 ptr=0xffff88003c2fd000 bytes_req=2096 bytes_alloc=4096 gfp_flags=GFP_ATOMIC ------------------------------------------ To see wakeups and sched switches that left the previous task in the running state: ------------------------------------------ # trace-cmd report -F 'sched: prev_state == 0 || (success == 1)' trace-cmd-16132 [002] 158126.499951: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=002 trace-cmd-16132 [002] 158126.500401: sched_switch: prev_comm=trace-cmd prev_pid=16132 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16129 next_prio=120 -0 [003] 158126.500585: sched_wakeup: comm=trace-cmd pid=16130 prio=120 success=1 target_cpu=003 -0 [003] 158126.501241: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16130 next_prio=120 trace-cmd-16132 [000] 158126.502475: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=000 trace-cmd-16131 [002] 158126.506516: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=002 -0 [003] 158126.550110: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16130 next_prio=120 trace-cmd-16131 [003] 158126.570243: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=003 trace-cmd-16130 [002] 158126.618202: sched_switch: prev_comm=trace-cmd prev_pid=16130 prev_prio=120 prev_state=R ==> next_comm=yum-updatesd next_pid=3088 next_prio=1 20 trace-cmd-16129 [003] 158126.622379: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=003 trace-cmd-16129 [000] 158126.649287: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=000 ------------------------------------------ The above needs a little explanation. The filter specifies the "sched" subsystem, which includes both sched_switch and sched_wakeup events. Any event that does not have the format field "prev_state" or "success", will evaluate those expressions as FALSE, and will not produce a match. Using "||" will have the "prev_state" test happen for the "sched_switch" event and the "success" test happen for the "sched_wakeup" event. ------------------------------------------ # trace-cmd report -w -F 'sched_switch, sched_wakeup.*' [...] trace-cmd-16130 [003] 158131.580616: sched_wakeup: comm=trace-cmd pid=16131 prio=120 success=1 target_cpu=003 trace-cmd-16129 [000] 158131.581502: sched_switch: prev_comm=trace-cmd prev_pid=16129 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16131 next_prio=120 Latency: 885.901 usecs trace-cmd-16131 [000] 158131.582414: sched_wakeup: comm=trace-cmd pid=16129 prio=120 success=1 target_cpu=000 trace-cmd-16132 [001] 158131.583219: sched_switch: prev_comm=trace-cmd prev_pid=16132 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16129 next_prio=120 Latency: 804.809 usecs sleep-16133 [002] 158131.584121: sched_wakeup: comm=trace-cmd pid=16120 prio=120 success=1 target_cpu=002 trace-cmd-16129 [001] 158131.584128: sched_wakeup: comm=trace-cmd pid=16132 prio=120 success=1 target_cpu=001 sleep-16133 [002] 158131.584275: sched_switch: prev_comm=sleep prev_pid=16133 prev_prio=120 prev_state=R ==> next_comm=trace-cmd next_pid=16120 next_prio=120 Latency: 153.915 usecs trace-cmd-16130 [003] 158131.585284: sched_switch: prev_comm=trace-cmd prev_pid=16130 prev_prio=120 prev_state=S ==> next_comm=trace-cmd next_pid=16132 next_prio=120 Latency: 1155.677 usecs Average wakeup latency: 26626.656 usecs ------------------------------------------ The above trace produces the wakeup latencies of the tasks. The "sched_switch" event reports each individual latency after writing the event information. At the end of the report, the average wakeup latency is reported. ------------------------------------------ # trace-cmd report -w -F 'sched_switch, sched_wakeup.*: prio < 100 || next_prio < 100' -0 [003] 158131.516753: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003 -0 [003] 158131.516855: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 101.244 usecs -0 [003] 158131.533781: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003 -0 [003] 158131.533897: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 115.608 usecs -0 [003] 158131.569730: sched_wakeup: comm=ksoftirqd/3 pid=13 prio=49 success=1 target_cpu=003 -0 [003] 158131.569851: sched_switch: prev_comm=swapper prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ksoftirqd/3 next_pid=13 next_prio=49 Latency: 121.024 usecs Average wakeup latency: 110.021 usecs ------------------------------------------ The above version will only show the wakeups and context switches of Real Time tasks. The 'prio' used inside the kernel starts at 0 for highest priority. That is 'prio' 0 is equivalent to user space real time priority 99, and priority 98 is equivalent to user space real time priority 1. Prios less than 100 represent Real Time tasks. An example of the profile: ------------------------------------------ # trace-cmd record --profile sleep 1 # trace-cmd report --profile --comm sleep task: sleep-21611 Event: sched_switch:R (1) Total: 99442 Avg: 99442 Max: 99442 Min:99442 1 total:99442 min:99442 max:99442 avg=99442 => ftrace_raw_event_sched_switch (0xffffffff8105f812) => __schedule (0xffffffff8150810a) => preempt_schedule (0xffffffff8150842e) => ___preempt_schedule (0xffffffff81273354) => cpu_stop_queue_work (0xffffffff810b03c5) => stop_one_cpu (0xffffffff810b063b) => sched_exec (0xffffffff8106136d) => do_execve_common.isra.27 (0xffffffff81148c89) => do_execve (0xffffffff811490b0) => SyS_execve (0xffffffff811492c4) => return_to_handler (0xffffffff8150e3c8) => stub_execve (0xffffffff8150c699) Event: sched_switch:S (1) Total: 1000506680 Avg: 1000506680 Max: 1000506680 Min:1000506680 1 total:1000506680 min:1000506680 max:1000506680 avg=1000506680 => ftrace_raw_event_sched_switch (0xffffffff8105f812) => __schedule (0xffffffff8150810a) => schedule (0xffffffff815084b8) => do_nanosleep (0xffffffff8150b22c) => hrtimer_nanosleep (0xffffffff8108d647) => SyS_nanosleep (0xffffffff8108d72c) => return_to_handler (0xffffffff8150e3c8) => tracesys_phase2 (0xffffffff8150c304) Event: sched_wakeup:21611 (1) Total: 30326 Avg: 30326 Max: 30326 Min:30326 1 total:30326 min:30326 max:30326 avg=30326 => ftrace_raw_event_sched_wakeup_template (0xffffffff8105f653) => ttwu_do_wakeup (0xffffffff810606eb) => ttwu_do_activate.constprop.124 (0xffffffff810607c8) => try_to_wake_up (0xffffffff8106340a) ------------------------------------------ SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1), trace-cmd-profile(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-reset.1.txt000066400000000000000000000074461351617527000220750ustar00rootroot00000000000000TRACE-CMD-RESET(1) ================== NAME ---- trace-cmd-reset - turn off all Ftrace tracing to bring back full performance SYNOPSIS -------- *trace-cmd reset* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) reset command turns off all tracing of Ftrace. This will bring back the performance of the system before tracing was enabled. This is necessary since 'trace-cmd-record(1)', 'trace-cmd-stop(1)' and 'trace-cmd-extract(1)' do not disable the tracer, event after the data has been pulled from the buffers. The rational is that the user may want to manually enable the tracer with the Ftrace pseudo file system, or examine other parts of Ftrace to see what trace-cmd did. After the reset command happens, the data in the ring buffer, and the options that were used are all lost. OPTIONS ------- Please note that the order that options are specified on the command line is significant. See EXAMPLES. *-b* 'buffer_size':: When the kernel boots, the Ftrace ring buffer is of a minimal size (3 pages per CPU). The first time the tracer is used, the ring buffer size expands to what it was set for (default 1.4 Megs per CPU). If no more tracing is to be done, this option allows you to shrink the ring buffer down to free up available memory. trace-cmd reset -b 1 The buffer instance affected is the one (or ones) specified by the most recently preceding *-B*, *-t*, or *-a* option: When used after *-B*, resizes the buffer instance that precedes it on the command line. When used after *-a*, resizes all buffer instances except the top one. When used after *-t* or before any *-B* or *-a*, resizes the top instance. *-B* 'buffer-name':: If the kernel supports multiple buffers, this will reset the trace for only the given buffer. It does not affect any other buffer. This may be used multiple times to specify different buffers. The top level buffer will not be reset if this option is given (unless the *-t* option is also supplied). *-a*:: Reset the trace for all existing buffer instances. When this option is used, the top level instance will not be reset unless *-t* is given. *-d*:: This option deletes the instance buffer(s) specified by the most recently preceding *-B* or *-a* option. Because the top-level instance buffer cannot be deleted, it is invalid to use this immediatly following *-t* or prior to any *-B* or *-a* option on the command line. *-t*:: Resets the top level instance buffer. Without the *-B* or *-a* option this is the same as the default. But if *-B* or *-a* is used, this is required if the top level instance buffer should also be reset. EXAMPLES -------- Reset tracing for instance-one and set its per-cpu buffer size to 4096kb. Also deletes instance-two. The top level instance and any other instances remain unaffected: trace-cmd reset -B instance-one -b 4096 -B instance-two -d Delete all instance buffers. Top level instance remains unaffected: trace-cmd reset -a -d Delete all instance buffers and also reset the top instance: trace-cmd reset -t -a -d Invalid. This command implies an attempt to delete the top instance: trace-cmd reset -a -t -d Reset the top instance and set its per-cpu buffer size to 1024kb. If any instance buffers exist, they will be unaffected: trace-cmd reset -b 1024 SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-restore.1.txt000066400000000000000000000077471351617527000224420ustar00rootroot00000000000000TRACE-CMD-RESTORE(1) ==================== NAME ---- trace-cmd-restore - restore a failed trace record SYNOPSIS -------- *trace-cmd restore* ['OPTIONS'] ['command'] cpu-file [cpu-file ...] DESCRIPTION ----------- The trace-cmd(1) restore command will restore a crashed trace-cmd-record(1) file. If for some reason a trace-cmd record fails, it will leave a the per-cpu data files and not create the final trace.dat file. The trace-cmd restore will append the files to create a working trace.dat file that can be read with trace-cmd-report(1). When trace-cmd record runs, it spawns off a process per CPU and writes to a per cpu file usually called 'trace.dat.cpuX', where X represents the CPU number that it is tracing. If the -o option was used in the trace-cmd record, then the CPU data files will have that name instead of the 'trace.dat' name. If a unexpected crash occurs before the tracing is finished, then the per CPU files will still exist but there will not be any trace.dat file to read from. trace-cmd restore will allow you to create a trace.dat file with the existing data files. OPTIONS ------- *-c*:: Create a partial trace.dat file from the machine, to be used with a full trace-cmd restore at another time. This option is useful for embedded devices. If a server contains the cpu files of a crashed trace-cmd record (or trace-cmd listen), trace-cmd restore can be executed on the embedded device with the -c option to get all the stored information of that embedded device. Then the file created could be copied to the server to run the trace-cmd restore there with the cpu files. If *-o* is not specified, then the file created will be called 'trace-partial.dat'. This is because the file is not a full version of something that trace-cmd-report(1) could use. *-t* tracing_dir:: Used with *-c*, it overrides the location to read the events from. By default, tracing information is read from the debugfs/tracing directory. *-t* will use that location instead. This can be useful if the trace.dat file to create is from another machine. Just tar -cvf events.tar debugfs/tracing and copy and untar that file locally, and use that directory instead. *-k* kallsyms:: Used with *-c*, it overrides where to read the kallsyms file from. By default, /proc/kallsyms is used. *-k* will override the file to read the kallsyms from. This can be useful if the trace.dat file to create is from another machine. Just copy the /proc/kallsyms file locally, and use *-k* to point to that file. *-o* output':: By default, trace-cmd restore will create a 'trace.dat' file (or 'trace-partial.dat' if *-c* is specified). You can specify a different file to write to with the *-o* option. *-i* input:: By default, trace-cmd restore will read the information of the current system to create the initial data stored in the 'trace.dat' file. If the crash was on another machine, then that machine should have the trace-cmd restore run with the *-c* option to create the trace.dat partial file. Then that file can be copied to the current machine where trace-cmd restore will use *-i* to load that file instead of reading from the current system. EXAMPLES -------- If a crash happened on another box, you could run: $ trace-cmd restore -c -o box-partial.dat Then on the server that has the cpu files: $ trace-cmd restore -i box-partial.dat trace.dat.cpu0 trace.dat.cpu1 This would create a trace.dat file for the embedded box. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-show.1.txt000066400000000000000000000053221351617527000217220ustar00rootroot00000000000000TRACE-CMD-SHOW(1) ================= NAME ---- trace-cmd-show - show the contents of the Ftrace Linux kernel tracing buffer. SYNOPSIS -------- *trace-cmd show* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) show displays the contents of one of the Ftrace Linux kernel tracing files: trace, snapshot, or trace_pipe. It is basically the equivalent of doing: cat /sys/kernel/debug/tracing/trace OPTIONS ------- *-p*:: Instead of displaying the contents of the "trace" file, use the "trace_pipe" file. The difference between the two is that the "trace" file is static. That is, if tracing is stopped, the "trace" file will show the same contents each time. The "trace_pipe" file is a consuming read, where a read of the file will consume the output of what was read and it will not read the same thing a second time even if tracing is stopped. This file als will block. If no data is available, trace-cmd show will stop and wait for data to appear. *-s*:: Instead of reading the "trace" file, read the snapshot file. The snapshot is made by an application writing into it and the kernel will perform as swap between the currently active buffer and the current snapshot buffer. If no more swaps are made, the snapshot will remain static. This is not a consuming read. *-c* 'cpu':: Read only the trace file for a specified CPU. *-f*:: Display the full path name of the file that is being displayed. *-B* 'buf':: If a buffer instance was created, then the *-B* option will access the files associated with the given buffer. *--tracing_on*:: Show if tracing is on for the given instance. *--current_tracer*:: Show what the current tracer is. *--buffer_size*:: Show the current buffer size (per-cpu) *--buffer_total_size*:: Show the total size of all buffers. *--ftrace_filter*:: Show what function filters are set. *--ftrace_notrace*:: Show what function disabled filters are set. *--ftrace_pid*:: Show the PIDs the function tracer is limited to (if any). *--graph_function*:: Show the functions that will be graphed. *--graph_notrace*:: Show the functions that will not be graphed. *--cpumask*:: Show the mask of CPUs that tracing will trace. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-snapshot.1.txt000066400000000000000000000032171351617527000226020ustar00rootroot00000000000000TRACE-CMD-SNAPSHOT(1) ===================== NAME ---- trace-cmd-snapshot - take, reset, free, or show a Ftrace kernel snapshot SYNOPSIS -------- *trace-cmd snapshot* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) snapshot controls or displays the Ftrace Linux kernel snapshot feature (if the kernel supports it). This is useful to "freeze" an instance of a live trace but without stopping the trace. trace-cmd start -p function trace-cmd snapshot -s trace-cmd snapshot [ dumps the content of buffer at 'trace-cmd snapshot -s' ] trace-cmd snapshot -s trace-cmd snapshot [ dumps the new content of the buffer at the last -s operation ] OPTIONS ------- *-s*:: Take a snapshot of the currently running buffer. *-r*:: Clear out the buffer. *-f*:: Free the snapshot buffer. The buffer takes up memory inside the kernel. It is best to free it when not in use. The first -s operation will allocate it if it is not already allocated. *-c* 'cpu':: Operate on a per cpu snapshot (may not be fully supported by all kernels) *-B* 'buf':: If a buffer instance was created, then the *-B* option will operate on the snapshot within the buffer. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-split.1.txt000066400000000000000000000062561351617527000221040ustar00rootroot00000000000000TRACE-CMD-SPLIT(1) ================== NAME ---- trace-cmd-split - split a trace.dat file into smaller files SYNOPSIS -------- *trace-cmd split* ['OPTIONS'] ['start-time' ['end-time']] DESCRIPTION ----------- The trace-cmd(1) split is used to break up a trace.dat into small files. The 'start-time' specifies where the new file will start at. Using 'trace-cmd-report(1)' and copying the time stamp given at a particular event, can be used as input for either 'start-time' or 'end-time'. The split will stop creating files when it reaches an event after 'end-time'. If only the end-time is needed, use 0.0 as the start-time. If start-time is left out, then the split will start at the beginning of the file. If end-time is left out, then split will continue to the end unless it meets one of the requirements specified by the options. OPTIONS ------- *-i* 'file':: If this option is not specified, then the split command will look for the file named 'trace.dat'. This options will allow the reading of another file other than 'trace.dat'. *-o* 'file':: By default, the split command will use the input file name as a basis of where to write the split files. The output file will be the input file with an attached \'.#\' to the end: trace.dat.1, trace.dat.2, etc. This option will change the name of the base file used. -o file will create file.1, file.2, etc. *-s* 'seconds':: This specifies how many seconds should be recorded before the new file should stop. *-m* 'milliseconds':: This specifies how many milliseconds should be recorded before the new file should stop. *-u* 'microseconds':: This specifies how many microseconds should be recorded before the new file should stop. *-e* 'events':: This specifies how many events should be recorded before the new file should stop. *-p* 'pages':: This specifies the number of pages that should be recorded before the new file should stop. Note: only one of *-p*, *-e*, *-u*, *-m*, *-s* may be specified at a time. If *-p* is specified, then *-c* is automatically set. *-r*:: This option causes the break up to repeat until end-time is reached (or end of the input if end-time is not specified). trace-cmd split -r -e 10000 This will break up trace.dat into several smaller files, each with at most 10,000 events in it. *-c*:: This option causes the above break up to be per CPU. trace-cmd split -c -p 10 This will create a file that has 10 pages per each CPU from the input. *-C* 'cpu':: This option will split for a single CPU. Only the cpu named will be extracted from the file. trace-cmd split -C 1 This will split out all the events for cpu 1 in the file. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-stack.1.txt000066400000000000000000000023001351617527000220400ustar00rootroot00000000000000TRACE-CMD-STACK(1) ================== NAME ---- trace-cmd-stack - read, enable or disable Ftrace Linux kernel stack tracing. SYNOPSIS -------- *trace-cmd stack* DESCRIPTION ----------- The trace-cmd(1) stack enables the Ftrace stack tracer within the kernel. The stack tracer enables the function tracer and at each function call within the kernel, the stack is checked. When a new maximum usage stack is discovered, it is recorded. When no option is used, the current stack is displayed. To enable the stack tracer, use the option *--start*, and to disable the stack tracer, use the option *--stop*. The output will be the maximum stack found since the start was enabled. Use *--reset* to reset the stack counter to zero. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-start.1.txt000066400000000000000000000023611351617527000220770ustar00rootroot00000000000000TRACE-CMD-START(1) ================== NAME ---- trace-cmd-start - start the Ftrace Linux kernel tracer without recording SYNOPSIS -------- *trace-cmd start* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) start enables all the Ftrace tracing the same way trace-cmd-record(1) does. The difference is that it does not run threads to create a trace.dat file. This is useful just to enable Ftrace and you are only interested in the trace after some event has occurred and the trace is stopped. Then the trace can be read straight from the Ftrace pseudo file system or can be extracted with trace-cmd-extract(1). OPTIONS ------- The options are the same as 'trace-cmd-record(1)', except that it does not take options specific to recording (*-s*, *-o*, *-F*, *-N*, and *-t*). SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-stat.1.txt000066400000000000000000000033571351617527000217230ustar00rootroot00000000000000TRACE-CMD-STAT(1) ================= NAME ---- trace-cmd-stat - show the status of the tracing (ftrace) system SYNOPSIS -------- *trace-cmd stat* DESCRIPTION ----------- The trace-cmd(1) stat displays the various status of the tracing (ftrace) system. The status that it shows is: *Tracer:* if one of the tracers (like function_graph) is active. Otherwise nothing is displayed. *Events:* Lists the events that are enable. *Event filters:* Shows any filters that are set for any events *Function filters:* Shows any filters for the function tracers *Graph functions:* Shows any functions that the function graph tracer should graph *Buffers:* Shows the trace buffer size if they have been expanded. By default, tracing buffers are in a compressed format until they are used. If they are compressed, the buffer display will not be shown. *Trace clock:* If the tracing clock is anything other than the default "local" it will be displayed. *Trace CPU mask:* If not all available CPUs are in the tracing CPU mask, then the tracing CPU mask will be displayed. *Trace max latency:* Shows the value of the trace max latency if it is other than zero. *Kprobes:* Shows any kprobes that are defined for tracing. *Uprobes:* Shows any uprobes that are defined for tracing. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2014 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-stop.1.txt000066400000000000000000000037711351617527000217350ustar00rootroot00000000000000TRACE-CMD-STOP(1) ================= NAME ---- trace-cmd-stop - stop the Ftrace Linux kernel tracer from writing to the ring buffer. SYNOPSIS -------- *trace-cmd stop* ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) stop is a complement to 'trace-cmd-start(1)'. This will disable Ftrace from writing to the ring buffer. This does not stop the overhead that the tracing may incur. Only the updating of the ring buffer is disabled, the Ftrace tracing may still be inducing overhead. After stopping the trace, the 'trace-cmd-extract(1)' may strip out the data from the ring buffer and create a trace.dat file. The Ftrace pseudo file system may also be examined. To disable the tracing completely to remove the overhead it causes, use 'trace-cmd-reset(1)'. But after a reset is performed, the data that has been recorded is lost. OPTIONS ------- *-B* 'buffer-name':: If the kernel supports multiple buffers, this will stop the trace for only the given buffer. It does not affect any other buffer. This may be used multiple times to specify different buffers. When this option is used, the top level instance will not be stopped unless *-t* is given. *-a*:: Stop the trace for all existing buffer instances. When this option is used, the top level instance will not be stopped unless *-t* is given. *-t*:: Stops the top level instance buffer. Without the *-B* or *-a* option this is the same as the default. But if *-B* or *-a* is used, this is required if the top level instance buffer should also be stopped. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd-stream.1.txt000066400000000000000000000027031351617527000222350ustar00rootroot00000000000000TRACE-CMD-STREAM(1) =================== NAME ---- trace-cmd-stream - stream a trace to stdout as it is happening SYNOPSIS -------- *trace-cmd stream ['OPTIONS']* ['command'] DESCRIPTION ----------- The trace-cmd(1) stream will start tracing just like trace-cmd-record(1), except it will not record to a file and instead it will read the binary buffer as it is happening, convert it to a human readable format and write it to stdout. This is basically the same as trace-cmd-start(1) and then doing a trace-cmd-show(1) with the *-p* option. trace-cmd-stream is not as efficient as reading from the pipe file as most of the stream work is done in userspace. This is useful if it is needed to do the work mostly in userspace instead of the kernel, and stream also helps to debug trace-cmd-profile(1) which uses the stream code to perform the live data analysis for the profile. OPTIONS ------- These are the same as trace-cmd-record(1), except that it does not take the *-o* option. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2014 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd.1.txt000066400000000000000000000047121351617527000207460ustar00rootroot00000000000000TRACE-CMD(1) ============ NAME ---- trace-cmd - interacts with Ftrace Linux kernel internal tracer SYNOPSIS -------- *trace-cmd* 'COMMAND' ['OPTIONS'] DESCRIPTION ----------- The trace-cmd(1) command interacts with the Ftrace tracer that is built inside the Linux kernel. It interfaces with the Ftrace specific files found in the debugfs file system under the tracing directory. A 'COMMAND' must be specified to tell trace-cmd what to do. COMMANDS -------- record - record a live trace and write a trace.dat file to the local disk or to the network. report - reads a trace.dat file and converts the binary data to a ASCII text readable format. hist - show a histogram of the events. stat - show tracing (ftrace) status of the running system options - list the plugin options that are available to *report* start - start the tracing without recording to a trace.dat file. stop - stop tracing (only disables recording, overhead of tracer is still in effect) restart - restart tracing from a previous stop (only effects recording) extract - extract the data from the kernel buffer and create a trace.dat file. reset - disables all tracing and gives back the system performance. (clears all data from the kernel buffers) split - splits a trace.dat file into smaller files. list - list the available plugins or events that can be recorded. listen - open up a port to listen for remote tracing connections. restore - restore the data files of a crashed run of trace-cmd record stack - run and display the stack tracer check-events - parse format strings for all trace events and return whether all formats are parseable OPTIONS ------- *-h*, --help:: Display the help text. Other options see the man page for the corresponding command. SEE ALSO -------- trace-cmd-record(1), trace-cmd-report(1), trace-cmd-hist(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-restore(1), trace-cmd-stack(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1), trace-cmd.dat(5), trace-cmd-check-events(1) trace-cmd-stat(1) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/Documentation/trace-cmd.dat.5.txt000066400000000000000000000167771351617527000215370ustar00rootroot00000000000000TRACE-CMD.DAT(5) ================ NAME ---- trace-cmd.dat - trace-cmd file format SYNOPSIS -------- *trace-cmd.dat* ignore DESCRIPTION ----------- The trace-cmd(1) utility produces a "trace.dat" file. The file may also be named anything depending if the user specifies a different output name, but it must have a certain binary format. The file is used by trace-cmd to save kernel traces into it and be able to extract the trace from it at a later point (see *trace-cmd-report(1)*). INITIAL FORMAT -------------- The first three bytes contain the magic value: 0x17 0x08 0x44 The next 7 bytes contain the characters: "tracing" The next set of characters contain a null '\0' terminated string that contains the version of the file (for example): "6\0" The next 1 byte contains the flags for the file endianess: 0 = little endian 1 = big endian The next byte contains the number of bytes per "long" value: 4 - 32-bit long values 8 - 64-bit long values Note: This is the long size of the target's userspace. Not the kernel space size. [ Now all numbers are written in file defined endianess. ] The next 4 bytes are a 32-bit word that defines what the traced host machine page size was. HEADER INFO FORMAT ------------------ Directly after the initial format comes information about the trace headers recorded from the target box. The next 12 bytes contain the string: "header_page\0" The next 8 bytes are a 64-bit word containing the size of the page header information stored next. The next set of data is of the size read from the previous 8 bytes, and contains the data retrieved from debugfs/tracing/events/header_page. Note: The size of the second field \fBcommit\fR contains the target kernel long size. For example: field: local_t commit; offset:8; \fBsize:8;\fR signed:1; shows the kernel has a 64-bit long. The next 13 bytes contain the string: "header_event\0" The next 8 bytes are a 64-bit word containing the size of the event header information stored next. The next set of data is of the size read from the previous 8 bytes and contains the data retrieved from debugfs/tracing/events/header_event. This data allows the trace-cmd tool to know if the ring buffer format of the kernel made any changes. FTRACE EVENT FORMATS -------------------- Directly after the header information comes the information about the Ftrace specific events. These are the events used by the Ftrace plugins and are not enabled by the event tracing. The next 4 bytes contain a 32-bit word of the number of Ftrace event format files that are stored in the file. For the number of times defined by the previous 4 bytes is the following: 8 bytes for the size of the Ftrace event format file. The Ftrace event format file copied from the target machine: debugfs/tracing/events/ftrace//format EVENT FORMATS ------------- Directly after the Ftrace formats comes the information about the event layout. The next 4 bytes are a 32-bit word containing the number of event systems that are stored in the file. These are the directories in debugfs/tracing/events excluding the \fBftrace\fR directory. For the number of times defined by the previous 4 bytes is the following: A null-terminated string containing the system name. 4 bytes containing a 32-bit word containing the number of events within the system. For the number of times defined in the previous 4 bytes is the following: 8 bytes for the size of the event format file. The event format file copied from the target machine: debugfs/tracing/events///format KALLSYMS INFORMATION -------------------- Directly after the event formats comes the information of the mapping of function addresses to the function names. The next 4 bytes are a 32-bit word containing the size of the data holding the function mappings. The next set of data is of the size defined by the previous 4 bytes and contains the information from the target machine's file: /proc/kallsyms TRACE_PRINTK INFORMATION ------------------------ If a developer used trace_printk() within the kernel, it may store the format string outside the ring buffer. This information can be found in: debugfs/tracing/printk_formats The next 4 bytes are a 32-bit word containing the size of the data holding the printk formats. The next set of data is of the size defined by the previous 4 bytes and contains the information from debugfs/tracing/printk_formats. PROCESS INFORMATION ------------------- Directly after the trace_printk formats comes the information mapping a PID to a process name. The next 8 bytes contain a 64-bit word that holds the size of the data mapping the PID to a process name. The next set of data is of the size defined by the previous 8 bytes and contains the information from debugfs/tracing/saved_cmdlines. REST OF TRACE-CMD HEADER ------------------------ Directly after the process information comes the last bit of the trace.dat file header. The next 4 bytes are a 32-bit word defining the number of CPUs that were discovered on the target machine (and has matching trace data for it). The next 10 bytes are one of the following: "options \0" "latency \0" "flyrecord\0" If it is "options \0" then: The next 2 bytes are a 16-bit word defining the current option. If the the value is zero then there are no more options. Otherwise, the next 4 bytes contain a 32-bit word containing the option size. If the reader does not know how to handle the option it can simply skip it. Currently there are no options defined, but this is here to extend the data. The next option will be directly after the previous option, and the options ends with a zero in the option type field. The next 10 bytes after the options are one of the following: "latency \0" "flyrecord\0" which would follow the same as if options were not present. If the value is "latency \0", then the rest of the file is simply ASCII text that was taken from the target's: debugfs/tracing/trace If the value is "flyrecord\0", the following is present: For the number of CPUs that were read earlier, the following is present: 8 bytes that are a 64-bit word containing the offset into the file that holds the data for the CPU. 8 bytes that are a 64-bit word containing the size of the CPU data at that offset. CPU DATA -------- The CPU data is located in the part of the file that is specified in the end of the header. Padding is placed between the header and the CPU data, placing the CPU data at a page aligned (target page) position in the file. This data is copied directly from the Ftrace ring buffer and is of the same format as the ring buffer specified by the event header files loaded in the header format file. The trace-cmd tool will try to \fBmmap(2)\fR the data page by page with the target's page size if possible. If it fails to mmap, it will just read the data instead. SEE ALSO -------- trace-cmd(1), trace-cmd-record(1), trace-cmd-report(1), trace-cmd-start(1), trace-cmd-stop(1), trace-cmd-extract(1), trace-cmd-reset(1), trace-cmd-split(1), trace-cmd-list(1), trace-cmd-listen(1), trace-cmd.dat(5) AUTHOR ------ Written by Steven Rostedt, RESOURCES --------- git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git COPYING ------- Copyright \(C) 2010 Red Hat, Inc. Free use of this software is granted under the terms of the GNU Public License (GPL). trace-cmd-2.8.3/LICENSES/000077500000000000000000000000001351617527000145775ustar00rootroot00000000000000trace-cmd-2.8.3/LICENSES/GPL-2.0000066400000000000000000000444511351617527000154510ustar00rootroot00000000000000Valid-License-Identifier: GPL-2.0 Valid-License-Identifier: GPL-2.0-only Valid-License-Identifier: GPL-2.0+ Valid-License-Identifier: GPL-2.0-or-later SPDX-URL: https://spdx.org/licenses/GPL-2.0.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU General Public License (GPL) version 2 only' use: SPDX-License-Identifier: GPL-2.0 or SPDX-License-Identifier: GPL-2.0-only For 'GNU General Public License (GPL) version 2 or any later version' use: SPDX-License-Identifier: GPL-2.0+ or SPDX-License-Identifier: GPL-2.0-or-later License-Text: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. trace-cmd-2.8.3/LICENSES/LGPL-2.1000066400000000000000000000654251351617527000155720ustar00rootroot00000000000000Valid-License-Identifier: LGPL-2.1 Valid-License-Identifier: LGPL-2.1+ SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html Usage-Guide: To use this license in source code, put one of the following SPDX tag/value pairs into a comment according to the placement guidelines in the licensing rules documentation. For 'GNU Lesser General Public License (LGPL) version 2.1 only' use: SPDX-License-Identifier: LGPL-2.1 For 'GNU Lesser General Public License (LGPL) version 2.1 or any later version' use: SPDX-License-Identifier: LGPL-2.1+ License-Text: GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. one line to give the library's name and an idea of what it does. Copyright (C) year name of author This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. signature of Ty Coon, 1 April 1990 Ty Coon, President of Vice That's all there is to it! trace-cmd-2.8.3/Makefile000066400000000000000000000265231351617527000150420ustar00rootroot00000000000000# SPDX-License-Identifier: GPL-2.0 # trace-cmd version TC_VERSION = 2 TC_PATCHLEVEL = 8 TC_EXTRAVERSION = 3 TRACECMD_VERSION = $(TC_VERSION).$(TC_PATCHLEVEL).$(TC_EXTRAVERSION) export TC_VERSION export TC_PATCHLEVEL export TC_EXTRAVERSION export TRACECMD_VERSION # file format version FILE_VERSION = 6 MAKEFLAGS += --no-print-directory # Makefiles suck: This macro sets a default value of $(2) for the # variable named by $(1), unless the variable has been set by # environment or command line. This is necessary for CC and AR # because make sets default values, so the simpler ?= approach # won't work as expected. define allow-override $(if $(or $(findstring environment,$(origin $(1))),\ $(findstring command line,$(origin $(1)))),,\ $(eval $(1) = $(2))) endef # Allow setting CC and AR, or setting CROSS_COMPILE as a prefix. $(call allow-override,CC,$(CROSS_COMPILE)gcc) $(call allow-override,AR,$(CROSS_COMPILE)ar) EXT = -std=gnu99 INSTALL = install # Use DESTDIR for installing into a different root directory. # This is useful for building a package. The program will be # installed in this directory as if it was the root directory. # Then the build tool can move it later. DESTDIR ?= DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' prefix ?= /usr/local bindir_relative = bin bindir = $(prefix)/$(bindir_relative) man_dir = $(prefix)/share/man man_dir_SQ = '$(subst ','\'',$(man_dir))' html_install = $(prefix)/share/kernelshark/html html_install_SQ = '$(subst ','\'',$(html_install))' img_install = $(prefix)/share/kernelshark/html/images img_install_SQ = '$(subst ','\'',$(img_install))' libdir ?= $(prefix)/lib libdir_SQ = '$(subst ','\'',$(libdir))' includedir = $(prefix)/include/trace-cmd includedir_SQ = '$(subst ','\'',$(includedir))' export man_dir man_dir_SQ html_install html_install_SQ INSTALL export img_install img_install_SQ export DESTDIR DESTDIR_SQ ifeq ($(prefix),$(HOME)) plugin_dir = $(HOME)/.trace-cmd/plugins python_dir = $(HOME)/.trace-cmd/python var_dir = $(HOME)/.trace-cmd/ else plugin_dir = $(libdir)/trace-cmd/plugins python_dir ?= $(libdir)/trace-cmd/python PLUGIN_DIR = -DPLUGIN_DIR="$(plugin_dir)" PYTHON_DIR = -DPYTHON_DIR="$(python_dir)" PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' PYTHON_DIR_SQ = '$(subst ','\'',$(PYTHON_DIR))' var_dir = /var endif # Shell quotes bindir_SQ = $(subst ','\'',$(bindir)) bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) python_dir_SQ = $(subst ','\'',$(python_dir)) VAR_DIR = -DVAR_DIR="$(var_dir)" VAR_DIR_SQ = '$(subst ','\'',$(VAR_DIR))' var_dir_SQ = '$(subst ','\'',$(var_dir))' HELP_DIR = -DHELP_DIR=$(html_install) HELP_DIR_SQ = '$(subst ','\'',$(HELP_DIR))' #' emacs highlighting gets confused by the above escaped quote. BASH_COMPLETE_DIR ?= /etc/bash_completion.d export PLUGIN_DIR export PYTHON_DIR export PYTHON_DIR_SQ export plugin_dir_SQ export python_dir_SQ export var_dir # copy a bit from Linux kbuild ifeq ("$(origin V)", "command line") VERBOSE = $(V) endif ifndef VERBOSE VERBOSE = 0 endif SWIG_DEFINED := $(shell if command -v swig; then echo 1; else echo 0; fi) ifeq ($(SWIG_DEFINED), 0) BUILD_PYTHON := report_noswig NO_PYTHON = 1 endif ifndef NO_PYTHON PYTHON := ctracecmd.so PYTHON_GUI := ctracecmd.so ctracecmdgui.so PYTHON_VERS ?= python # Can build python? ifeq ($(shell sh -c "pkg-config --cflags $(PYTHON_VERS) > /dev/null 2>&1 && echo y"), y) PYTHON_PLUGINS := plugin_python.so BUILD_PYTHON := $(PYTHON) $(PYTHON_PLUGINS) BUILD_PYTHON_WORKS := 1 else BUILD_PYTHON := report_nopythondev NO_PYTHON = 1 endif endif # NO_PYTHON export PYTHON_PLUGINS export BUILD_PYTHON_WORKS export NO_PYTHON # $(call test-build, snippet, ret) -> ret if snippet compiles # -> empty otherwise test-build = $(if $(shell sh -c 'echo "$(1)" | \ $(CC) -o /dev/null -c -x c - > /dev/null 2>&1 && echo y'), $2) UDIS86_AVAILABLE := $(call test-build,\#include , y) ifneq ($(strip $(UDIS86_AVAILABLE)), y) NO_UDIS86 := 1 endif ifndef NO_UDIS86 # have udis86 disassembler library? udis86-flags := -DHAVE_UDIS86 -ludis86 udis86-ldflags := -ludis86 endif # NO_UDIS86 define BLK_TC_FLUSH_SOURCE #include int main(void) { return BLK_TC_FLUSH; } endef # have flush/fua block layer instead of barriers? blk-flags := $(call test-build,$(BLK_TC_FLUSH_SOURCE),-DHAVE_BLK_TC_FLUSH) ifeq ("$(origin O)", "command line") saved-output := $(O) BUILD_OUTPUT := $(shell cd $(O) && /bin/pwd) $(if $(BUILD_OUTPUT),, \ $(error output directory "$(saved-output)" does not exist)) else BUILD_OUTPUT = $(CURDIR) endif srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR)) objtree := $(BUILD_OUTPUT) src := $(srctree) obj := $(objtree) kshark-dir = $(src)/kernel-shark export prefix bindir src obj LIBS = -ldl LIBTRACEEVENT_DIR = $(obj)/lib/traceevent LIBTRACEEVENT_STATIC = $(LIBTRACEEVENT_DIR)/libtraceevent.a LIBTRACEEVENT_SHARED = $(LIBTRACEEVENT_DIR)/libtraceevent.so LIBTRACECMD_DIR = $(obj)/lib/trace-cmd LIBTRACECMD_STATIC = $(LIBTRACECMD_DIR)/libtracecmd.a LIBTRACECMD_SHARED = $(LIBTRACECMD_DIR)/libtracecmd.so TRACE_LIBS = -L$(LIBTRACECMD_DIR) -ltracecmd -L$(LIBTRACEEVENT_DIR) -ltraceevent export LIBS TRACE_LIBS export LIBTRACEEVENT_DIR LIBTRACECMD_DIR export LIBTRACECMD_STATIC LIBTRACECMD_SHARED export LIBTRACEEVENT_STATIC LIBTRACEEVENT_SHARED export Q VERBOSE EXT # Include the utils include scripts/utils.mk INCLUDES = -I$(src)/include -I$(src)/../../include INCLUDES += -I$(src)/include/traceevent INCLUDES += -I$(src)/include/trace-cmd INCLUDES += -I$(src)/lib/traceevent/include INCLUDES += -I$(src)/lib/trace-cmd/include INCLUDES += -I$(src)/tracecmd/include INCLUDES += -I$(obj)/tracecmd/include include $(src)/features.mk # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -Wall CPPFLAGS ?= LDFLAGS ?= export CFLAGS export INCLUDES # Required CFLAGS override CFLAGS += -D_GNU_SOURCE ifndef NO_PTRACE ifneq ($(call try-cc,$(SOURCE_PTRACE),),y) NO_PTRACE = 1 override CFLAGS += -DWARN_NO_PTRACE endif endif ifdef NO_PTRACE override CFLAGS += -DNO_PTRACE endif ifndef NO_AUDIT ifneq ($(call try-cc,$(SOURCE_AUDIT),-laudit),y) NO_AUDIT = 1 override CFLAGS += -DWARN_NO_AUDIT endif endif ifdef NO_AUDIT override CFLAGS += -DNO_AUDIT else LIBS += -laudit endif # Append required CFLAGS override CFLAGS += $(INCLUDES) $(PLUGIN_DIR_SQ) $(VAR_DIR) override CFLAGS += $(udis86-flags) $(blk-flags) override LDFLAGS += $(udis86-ldflags) CMD_TARGETS = trace-cmd $(BUILD_PYTHON) ### # Default we just build trace-cmd # # If you want kernelshark, then do: make gui ### all: all_cmd plugins show_gui_make all_cmd: $(CMD_TARGETS) CMAKE_COMMAND = /usr/bin/cmake $(kshark-dir)/build/Makefile: $(kshark-dir)/CMakeLists.txt $(Q) cd $(kshark-dir)/build && $(CMAKE_COMMAND) -D_INSTALL_PREFIX=$(prefix) .. gui: force $(CMD_TARGETS) $(kshark-dir)/build/Makefile $(Q)$(MAKE) $(S) -C $(kshark-dir)/build @echo "gui build complete" @echo " kernelshark located at $(kshark-dir)/bin" trace-cmd: force $(LIBTRACEEVENT_STATIC) $(LIBTRACECMD_STATIC) $(Q)$(MAKE) -C $(src)/tracecmd $(obj)/tracecmd/$@ $(LIBTRACEEVENT_SHARED): force $(Q)$(MAKE) -C $(src)/lib/traceevent $@ $(LIBTRACEEVENT_STATIC): force $(Q)$(MAKE) -C $(src)/lib/traceevent $@ $(LIBTRACECMD_STATIC): force $(obj)/plugins/trace_plugin_dir $(Q)$(MAKE) -C $(src)/lib/trace-cmd $@ $(LIBTRACECMD_SHARED): force $(obj)/plugins/trace_plugin_dir $(Q)$(MAKE) -C $(src)/lib/trace-cmd $@ libtraceevent.so: $(LIBTRACEEVENT_SHARED) libtraceevent.a: $(LIBTRACEEVENT_STATIC) libtracecmd.a: $(LIBTRACECMD_STATIC) libtracecmd.so: $(LIBTRACECMD_SHARED) libs: $(LIBTRACECMD_SHARED) $(LIBTRACEEVENT_SHARED) plugins: force $(obj)/plugins/trace_plugin_dir $(obj)/plugins/trace_python_dir $(Q)$(MAKE) -C $(src)/plugins $(obj)/plugins/trace_plugin_dir: force $(Q)$(MAKE) -C $(src)/plugins $@ $(obj)/plugins/trace_python_dir: force $(Q)$(MAKE) -C $(src)/plugins $@ show_gui_make: @echo "Note: to build the gui, type \"make gui\"" @echo " to build man pages, type \"make doc\"" PHONY += show_gui_make define find_tag_files find . -name '\.pc' -prune -o -name '*\.[ch]' -print -o -name '*\.[ch]pp' \ ! -name '\.#' -print endef tags: force $(RM) tags $(call find_tag_files) | xargs ctags --extra=+f --c-kinds=+px TAGS: force $(RM) TAGS $(call find_tag_files) | xargs etags cscope: force $(RM) cscope* $(call find_tag_files) | cscope -b -q install_plugins: force $(Q)$(MAKE) -C $(src)/plugins $@ install_python: force $(Q)$(MAKE) -C $(src)/python $@ install_bash_completion: force $(Q)$(call do_install_data,$(src)/tracecmd/trace-cmd.bash,$(BASH_COMPLETE_DIR)) install_cmd: all_cmd install_plugins install_python install_bash_completion $(Q)$(call do_install,$(obj)/tracecmd/trace-cmd,$(bindir_SQ)) install: install_cmd @echo "Note: to install the gui, type \"make install_gui\"" @echo " to install man pages, type \"make install_doc\"" install_gui: install_cmd gui $(Q)$(MAKE) $(S) -C $(kshark-dir)/build install install_libs: libs $(Q)$(call do_install,$(LIBTRACECMD_SHARED),$(libdir_SQ)) $(Q)$(call do_install,$(LIBTRACEEVENT_SHARED),$(libdir_SQ)) $(Q)$(call do_install,$(src)/include/traceevent/event-parse.h,$(includedir_SQ)/traceevent) $(Q)$(call do_install,$(src)/include/traceevent/trace-seq.h,$(includedir_SQ)/traceevent) $(Q)$(call do_install,$(src)/include/trace-cmd/trace-cmd.h,$(includedir_SQ)) $(Q)$(call do_install,$(src)/include/trace-cmd/trace-filter-hash.h,$(includedir_SQ)) doc: $(MAKE) -C $(src)/Documentation all doc_clean: $(MAKE) -C $(src)/Documentation clean install_doc: $(MAKE) -C $(src)/Documentation install clean: $(RM) *.o *~ *.a *.so .*.d $(RM) tags TAGS cscope* $(MAKE) -C $(src)/lib/traceevent clean $(MAKE) -C $(src)/lib/trace-cmd clean $(MAKE) -C $(src)/plugins clean $(MAKE) -C $(src)/python clean $(MAKE) -C $(src)/tracecmd clean if [ -f $(kshark-dir)/build/Makefile ]; then $(MAKE) -C $(kshark-dir)/build clean; fi ##### PYTHON STUFF ##### report_noswig: force $(Q)echo $(Q)echo " NO_PYTHON forced: swig not installed, not compiling python plugins" $(Q)echo report_nopythondev: force $(Q)echo $(Q)echo " python-dev is not installed, not compiling python plugins" $(Q)echo ifndef NO_PYTHON PYTHON_INCLUDES = `pkg-config --cflags $(PYTHON_VERS)` PYTHON_LDFLAGS = `pkg-config --libs $(PYTHON_VERS)` \ $(shell $(PYTHON_VERS)-config --ldflags) PYGTK_CFLAGS = `pkg-config --cflags pygtk-2.0` else PYTHON_INCLUDES = PYTHON_LDFLAGS = PYGTK_CFLAGS = endif export PYTHON_INCLUDES export PYTHON_LDFLAGS export PYGTK_CFLAGS ctracecmd.so: force $(LIBTRACECMD_STATIC) $(Q)$(MAKE) -C $(src)/python $@ ctracecmdgui.so: force $(LIBTRACECMD_STATIC) trace-view $(Q)$(MAKE) -C $(src)/python $@ PHONY += python python: $(PYTHON) PHONY += python-gui python-gui: $(PYTHON_GUI) PHONY += python-plugin python-plugin: $(PYTHON_PLUGINS) plugin_python.so: force $(obj)/plugins/trace_python_dir $(Q)$(MAKE) -C $(src)/plugins $(obj)/plugins/plugin_python.so dist: git archive --format=tar --prefix=trace-cmd-$(TRACECMD_VERSION)/ HEAD \ > ../trace-cmd-$(TRACECMD_VERSION).tar cat ../trace-cmd-$(TRACECMD_VERSION).tar | \ bzip2 -c9 > ../trace-cmd-$(TRACECMD_VERSION).tar.bz2 cat ../trace-cmd-$(TRACECMD_VERSION).tar | \ xz -e -c8 > ../trace-cmd-$(TRACECMD_VERSION).tar.xz PHONY += force force: # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) trace-cmd-2.8.3/README000066400000000000000000000022531351617527000142540ustar00rootroot00000000000000 Note: The official repositiory for trace-cmd and KernelShark is here: git://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git For bug reports and issues, please file it here: https://bugzilla.kernel.org/buglist.cgi?component=Trace-cmd%2FKernelshark&product=Tools&resolution=--- These files make up the code that create the trace-cmd programs. This includes the GUI interface application kernelshark as well as trace-graph and trace-view. These files also make up the code to create the libraries libtracecmd and libparsevent The applications are licensed under the GNU General Public License 2.0 (see COPYING) and the libraries are licensed under the GNU Lesser General Public License 2.1 (See COPYING.LIB). BUILDING: To make trace-cmd make To make the gui make gui INSTALL: To install trace-cmd make install To install the gui make install_gui Note: The default install is relative to /usr/local The default install directory is /usr/local/bin The default plugin directory is /usr/local/lib/trace-cmd/plugins To change the default, you can set 'prefix', eg mkdir $HOME/test-trace make prefix=$HOME/test-trace make prefix=$HOME/test-trace install trace-cmd-2.8.3/features.mk000066400000000000000000000023031351617527000155370ustar00rootroot00000000000000 # taken from perf which was based on Linux Kbuild # try-cc # Usage: option = $(call try-cc, source-to-build, cc-options) try-cc = $(shell sh -c \ 'TMP="$(BUILD_OUTPUT)$(TMPOUT).$$$$"; \ echo "$(1)" | \ $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ rm -f "$$TMP"') define SOURCE_PTRACE #include #include int main (void) { int ret; ret = ptrace(PTRACE_ATTACH, 0, NULL, 0); ptrace(PTRACE_TRACEME, 0, NULL, 0); ptrace(PTRACE_GETSIGINFO, 0, NULL, NULL); ptrace(PTRACE_GETEVENTMSG, 0, NULL, NULL); ptrace(PTRACE_SETOPTIONS, NULL, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT); ptrace(PTRACE_CONT, NULL, NULL, 0); ptrace(PTRACE_DETACH, 0, NULL, NULL); ptrace(PTRACE_SETOPTIONS, 0, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT); return ret; } endef define SOURCE_AUDIT #include #include int main (void) { char *name; int ret; ret = audit_detect_machine(); if (ret < 0) return ret; name = audit_syscall_to_name(1, ret); if (!name) return -1; return ret; } endef trace-cmd-2.8.3/include/000077500000000000000000000000001351617527000150155ustar00rootroot00000000000000trace-cmd-2.8.3/include/linux/000077500000000000000000000000001351617527000161545ustar00rootroot00000000000000trace-cmd-2.8.3/include/linux/time64.h000066400000000000000000000004641351617527000174410ustar00rootroot00000000000000#ifndef _TOOLS_LINUX_TIME64_H #define _TOOLS_LINUX_TIME64_H #define MSEC_PER_SEC 1000L #define USEC_PER_MSEC 1000L #define NSEC_PER_USEC 1000L #define NSEC_PER_MSEC 1000000L #define USEC_PER_SEC 1000000L #define NSEC_PER_SEC 1000000000L #define FSEC_PER_SEC 1000000000000000LL #endif /* _LINUX_TIME64_H */ trace-cmd-2.8.3/include/trace-cmd/000077500000000000000000000000001351617527000166545ustar00rootroot00000000000000trace-cmd-2.8.3/include/trace-cmd/trace-cmd.h000066400000000000000000000354711351617527000206760ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt * */ #ifndef _TRACE_CMD_H #define _TRACE_CMD_H #include "traceevent/event-parse.h" #define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0])) #define __weak __attribute__((weak)) #define __noreturn __attribute__((noreturn)) #define TRACECMD_ERR_MSK ((unsigned long)(-1) & ~((1UL << 14) - 1)) #define TRACECMD_ISERR(ptr) ((unsigned long)(ptr) > TRACECMD_ERR_MSK) #define TRACECMD_ERROR(ret) ((void *)((unsigned long)(ret) | TRACECMD_ERR_MSK)) #define TRACECMD_PTR2ERR(ptr) ((unisgned long)(ptr) & ~TRACECMD_ERR_MSK) void tracecmd_parse_cmdlines(struct tep_handle *pevent, char *file, int size); void tracecmd_parse_trace_clock(struct tep_handle *pevent, char *file, int size); void tracecmd_parse_proc_kallsyms(struct tep_handle *pevent, char *file, unsigned int size); void tracecmd_parse_ftrace_printk(struct tep_handle *pevent, char *file, unsigned int size); extern int tracecmd_disable_sys_plugins; extern int tracecmd_disable_plugins; struct tep_plugin_list; struct tep_plugin_list *tracecmd_load_plugins(struct tep_handle *pevent); void tracecmd_unload_plugins(struct tep_plugin_list *list, struct tep_handle *pevent); char **tracecmd_event_systems(const char *tracing_dir); char **tracecmd_system_events(const char *tracing_dir, const char *system); struct tep_handle *tracecmd_local_events(const char *tracing_dir); int tracecmd_fill_local_events(const char *tracing_dir, struct tep_handle *pevent, int *parsing_failures); char **tracecmd_local_plugins(const char *tracing_dir); char **tracecmd_add_list(char **list, const char *name, int len); void tracecmd_free_list(char **list); int *tracecmd_add_id(int *list, int id, int len); enum { RINGBUF_TYPE_PADDING = 29, RINGBUF_TYPE_TIME_EXTEND = 30, RINGBUF_TYPE_TIME_STAMP = 31, }; void tracecmd_record_ref(struct tep_record *record); void free_record(struct tep_record *record); struct tracecmd_input; struct tracecmd_output; struct tracecmd_recorder; struct hook_list; static inline int tracecmd_host_bigendian(void) { unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; unsigned int *ptr; ptr = (unsigned int *)str; return *ptr == 0x01020304; } /* tracecmd_get_tracing_dir must *not* be freed */ const char *tracecmd_get_tracing_dir(void); /* tracecmd_find_tracing_dir must be freed */ char *tracecmd_find_tracing_dir(void); /* --- Opening and Reading the trace.dat file --- */ enum { TRACECMD_OPTION_DONE, TRACECMD_OPTION_DATE, TRACECMD_OPTION_CPUSTAT, TRACECMD_OPTION_BUFFER, TRACECMD_OPTION_TRACECLOCK, TRACECMD_OPTION_UNAME, TRACECMD_OPTION_HOOK, TRACECMD_OPTION_OFFSET, TRACECMD_OPTION_CPUCOUNT, TRACECMD_OPTION_VERSION, }; enum { TRACECMD_FL_IGNORE_DATE = (1 << 0), TRACECMD_FL_BUFFER_INSTANCE = (1 << 1), TRACECMD_FL_LATENCY = (1 << 2), }; struct tracecmd_ftrace { struct tracecmd_input *handle; struct tep_event *fgraph_ret_event; int fgraph_ret_id; int long_size; }; typedef void (*tracecmd_show_data_func)(struct tracecmd_input *handle, struct tep_record *record); typedef void (*tracecmd_handle_init_func)(struct tracecmd_input *handle, struct hook_list *hook, int global); struct tracecmd_input *tracecmd_alloc(const char *file); struct tracecmd_input *tracecmd_alloc_fd(int fd); struct tracecmd_input *tracecmd_open(const char *file); struct tracecmd_input *tracecmd_open_fd(int fd); void tracecmd_ref(struct tracecmd_input *handle); void tracecmd_close(struct tracecmd_input *handle); int tracecmd_read_headers(struct tracecmd_input *handle); int tracecmd_get_parsing_failures(struct tracecmd_input *handle); int tracecmd_long_size(struct tracecmd_input *handle); int tracecmd_page_size(struct tracecmd_input *handle); int tracecmd_cpus(struct tracecmd_input *handle); int tracecmd_copy_headers(struct tracecmd_input *handle, int fd); void tracecmd_set_flag(struct tracecmd_input *handle, int flag); void tracecmd_clear_flag(struct tracecmd_input *handle, int flag); unsigned long tracecmd_get_flags(struct tracecmd_input *handle); int tracecmd_make_pipe(struct tracecmd_input *handle, int cpu, int fd, int cpus); int tracecmd_buffer_instances(struct tracecmd_input *handle); const char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx); struct tracecmd_input *tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx); int tracecmd_is_buffer_instance(struct tracecmd_input *handle); void tracecmd_create_top_instance(char *name); void tracecmd_remove_instances(void); void tracecmd_set_ts_offset(struct tracecmd_input *handle, unsigned long long offset); void tracecmd_set_ts2secs(struct tracecmd_input *handle, unsigned long long hz); void tracecmd_print_events(struct tracecmd_input *handle, const char *regex); struct hook_list *tracecmd_hooks(struct tracecmd_input *handle); int tracecmd_init_data(struct tracecmd_input *handle); void tracecmd_print_stats(struct tracecmd_input *handle); void tracecmd_print_uname(struct tracecmd_input *handle); void tracecmd_print_version(struct tracecmd_input *handle); struct tep_record * tracecmd_read_page_record(struct tep_handle *pevent, void *page, int size, struct tep_record *last_record); struct tep_record * tracecmd_peek_data(struct tracecmd_input *handle, int cpu); static inline struct tep_record * tracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu) { struct tep_record *rec = tracecmd_peek_data(handle, cpu); if (rec) rec->ref_count++; return rec; } struct tep_record * tracecmd_read_data(struct tracecmd_input *handle, int cpu); struct tep_record * tracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record); struct tep_record * tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu); struct tep_record * tracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu); struct tep_record * tracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset, int *cpu); struct tep_record * tracecmd_translate_data(struct tracecmd_input *handle, void *ptr, int size); struct tep_record * tracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu); struct tep_record * tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu); int tracecmd_refresh_record(struct tracecmd_input *handle, struct tep_record *record); int tracecmd_set_cpu_to_timestamp(struct tracecmd_input *handle, int cpu, unsigned long long ts); void tracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle, unsigned long long time); int tracecmd_set_cursor(struct tracecmd_input *handle, int cpu, unsigned long long offset); unsigned long long tracecmd_get_cursor(struct tracecmd_input *handle, int cpu); int tracecmd_ftrace_overrides(struct tracecmd_input *handle, struct tracecmd_ftrace *finfo); struct tep_handle *tracecmd_get_pevent(struct tracecmd_input *handle); bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle); tracecmd_show_data_func tracecmd_get_show_data_func(struct tracecmd_input *handle); void tracecmd_set_show_data_func(struct tracecmd_input *handle, tracecmd_show_data_func func); char *tracecmd_get_tracing_file(const char *name); void tracecmd_put_tracing_file(char *name); int tracecmd_record_at_buffer_start(struct tracecmd_input *handle, struct tep_record *record); unsigned long long tracecmd_page_ts(struct tracecmd_input *handle, struct tep_record *record); unsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle, struct tep_record *record); #ifndef SWIG /* hack for function graph work around */ extern __thread struct tracecmd_input *tracecmd_curr_thread_handle; #endif /* --- Creating and Writing the trace.dat file --- */ struct tracecmd_event_list { struct tracecmd_event_list *next; const char *glob; }; struct tracecmd_option; struct tracecmd_msg_handle; struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus); struct tracecmd_output *tracecmd_create_file(const char *output_file, int cpus, char * const *cpu_data_files); struct tracecmd_output * tracecmd_create_file_glob(const char *output_file, int cpus, char * const *cpu_data_files, struct tracecmd_event_list *event_globs); struct tracecmd_output * tracecmd_create_init_file_glob(const char *output_file, struct tracecmd_event_list *list); struct tracecmd_output *tracecmd_create_init_fd(int fd); struct tracecmd_output * tracecmd_create_init_fd_glob(int fd, struct tracecmd_event_list *list); struct tracecmd_output * tracecmd_create_init_fd_msg(struct tracecmd_msg_handle *msg_handle, struct tracecmd_event_list *list); struct tracecmd_output *tracecmd_create_init_file(const char *output_file); struct tracecmd_output *tracecmd_create_init_file_override(const char *output_file, const char *tracing_dir, const char *kallsyms); struct tracecmd_option *tracecmd_add_option(struct tracecmd_output *handle, unsigned short id, int size, const void *data); struct tracecmd_option *tracecmd_add_buffer_option(struct tracecmd_output *handle, const char *name, int cpus); int tracecmd_write_cpus(struct tracecmd_output *handle, int cpus); int tracecmd_write_options(struct tracecmd_output *handle); int tracecmd_update_option(struct tracecmd_output *handle, struct tracecmd_option *option, int size, const void *data); void tracecmd_output_close(struct tracecmd_output *handle); void tracecmd_output_free(struct tracecmd_output *handle); struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file); int tracecmd_write_cpu_data(struct tracecmd_output *handle, int cpus, char * const *cpu_data_files); int tracecmd_append_cpu_data(struct tracecmd_output *handle, int cpus, char * const *cpu_data_files); int tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle, struct tracecmd_option *option, int cpus, char * const *cpu_data_files); struct tracecmd_output *tracecmd_get_output_handle_fd(int fd); /* --- Reading the Fly Recorder Trace --- */ enum { TRACECMD_RECORD_NOSPLICE = (1 << 0), /* Use read instead of splice */ TRACECMD_RECORD_SNAPSHOT = (1 << 1), /* extract from snapshot */ TRACECMD_RECORD_BLOCK = (1 << 2), /* Block on splice write */ }; void tracecmd_free_recorder(struct tracecmd_recorder *recorder); struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags); struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags); struct tracecmd_recorder *tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb); struct tracecmd_recorder *tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, const char *buffer); struct tracecmd_recorder *tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags, const char *buffer); struct tracecmd_recorder *tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags, const char *buffer, int maxkb); int tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep); void tracecmd_stop_recording(struct tracecmd_recorder *recorder); void tracecmd_stat_cpu(struct trace_seq *s, int cpu); long tracecmd_flush_recording(struct tracecmd_recorder *recorder); void tracecmd_filter_pid(int pid, int exclude); int tracecmd_add_event(const char *event_str, int stack); void tracecmd_enable_events(void); void tracecmd_disable_all_tracing(int disable_tracer); void tracecmd_disable_tracing(void); void tracecmd_enable_tracing(void); enum tracecmd_msg_flags { TRACECMD_MSG_FL_USE_TCP = 1 << 0, }; /* for both client and server */ struct tracecmd_msg_handle { int fd; short cpu_count; short version; /* Current protocol version */ unsigned long flags; bool done; }; struct tracecmd_msg_handle * tracecmd_msg_handle_alloc(int fd, unsigned long flags); /* Closes the socket and frees the handle */ void tracecmd_msg_handle_close(struct tracecmd_msg_handle *msg_handle); /* for clients */ int tracecmd_msg_send_init_data(struct tracecmd_msg_handle *msg_handle, unsigned int **client_ports); int tracecmd_msg_data_send(struct tracecmd_msg_handle *msg_handle, const char *buf, int size); int tracecmd_msg_finish_sending_data(struct tracecmd_msg_handle *msg_handle); int tracecmd_msg_send_close_msg(struct tracecmd_msg_handle *msg_handle); int tracecmd_msg_wait_close(struct tracecmd_msg_handle *msg_handle); /* for server */ int tracecmd_msg_initial_setting(struct tracecmd_msg_handle *msg_handle); int tracecmd_msg_send_port_array(struct tracecmd_msg_handle *msg_handle, int *ports); int tracecmd_msg_read_data(struct tracecmd_msg_handle *msg_handle, int ofd); int tracecmd_msg_collect_data(struct tracecmd_msg_handle *msg_handle, int ofd); bool tracecmd_msg_done(struct tracecmd_msg_handle *msg_handle); void tracecmd_msg_set_done(struct tracecmd_msg_handle *msg_handle); /* --- Plugin handling --- */ extern struct tep_plugin_option trace_ftrace_options[]; int trace_util_add_options(const char *name, struct tep_plugin_option *options); void trace_util_remove_options(struct tep_plugin_option *options); int trace_util_add_option(const char *name, const char *val); int trace_util_load_plugins(struct tep_handle *pevent, const char *suffix, int (*load_plugin)(struct tep_handle *pevent, const char *path, const char *name, void *data), void *data); struct tep_plugin_option *trace_util_read_plugin_options(void); void trace_util_free_options(struct tep_plugin_option *options); char **trace_util_find_plugin_files(const char *suffix); void trace_util_free_plugin_files(char **files); void trace_util_print_plugins(struct trace_seq *s, const char *prefix, const char *suffix, const struct tep_plugin_list *list); void trace_util_print_plugin_options(struct trace_seq *s); char **trace_util_list_plugin_options(void); void trace_util_free_plugin_options_list(char **list); const char *trace_util_plugin_option_value(const char *name); /* Used for trace-cmd list */ void tracecmd_ftrace_load_options(void); /* event hooks */ struct hook_list { struct hook_list *next; struct buffer_instance *instance; const char *hook; char *str; char *start_system; char *start_event; char *start_match; char *end_system; char *end_event; char *end_match; char *pid; int migrate; int global; int stack; }; struct hook_list *tracecmd_create_event_hook(const char *arg); void tracecmd_free_hooks(struct hook_list *hooks); /* --- Hack! --- */ int tracecmd_blk_hack(struct tracecmd_input *handle); /* --- Stack tracer functions --- */ int tracecmd_stack_tracer_status(int *status); /* --- Debugging --- */ struct kbuffer *tracecmd_record_kbuf(struct tracecmd_input *handle, struct tep_record *record); void *tracecmd_record_page(struct tracecmd_input *handle, struct tep_record *record); void *tracecmd_record_offset(struct tracecmd_input *handle, struct tep_record *record); #endif /* _TRACE_CMD_H */ trace-cmd-2.8.3/include/trace-cmd/trace-filter-hash.h000066400000000000000000000024171351617527000223330ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * Copyright (C) 2018 VMware Inc, Steven Rostedt * */ #ifndef _TRACE_FILTER_HASH_H #define _TRACE_FILTER_HASH_H #include struct tracecmd_filter_id_item { struct tracecmd_filter_id_item *next; int id; }; struct tracecmd_filter_id { struct tracecmd_filter_id_item **hash; int count; }; struct tracecmd_filter_id_item * tracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id); void tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id); void tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id); void tracecmd_filter_id_clear(struct tracecmd_filter_id *hash); struct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void); void tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash); struct tracecmd_filter_id * tracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash); int *tracecmd_filter_ids(struct tracecmd_filter_id *hash); int tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1, struct tracecmd_filter_id *hash2); static inline int tracecmd_filter_task_count(struct tracecmd_filter_id *hash) { return hash->count; } #endif /* _TRACE_FILTER_HASH_H */ trace-cmd-2.8.3/include/trace-cmd/trace-hash.h000066400000000000000000000027711351617527000210530ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2014 Red Hat Inc, Steven Rostedt * */ #ifndef _TRACE_HASH_H #define _TRACE_HASH_H struct trace_hash_item { struct trace_hash_item *next; struct trace_hash_item *prev; unsigned long long key; }; struct trace_hash { struct trace_hash_item **buckets; int nr_buckets; int power; }; int trace_hash_init(struct trace_hash *hash, int buckets); void trace_hash_free(struct trace_hash *hash); int trace_hash_add(struct trace_hash *hash, struct trace_hash_item *item); int trace_hash_empty(struct trace_hash *hash); static inline void trace_hash_del(struct trace_hash_item *item) { struct trace_hash_item *prev = item->prev; prev->next = item->next; if (item->next) item->next->prev = prev; } #define trace_hash_for_each_bucket(bucket, hash) \ for (bucket = (hash)->buckets; \ (bucket) < (hash)->buckets + (hash)->nr_buckets; (bucket)++) #define trace_hash_for_each_item(item, bucket) \ for ((item = *(bucket)); item; item = (item)->next) #define trace_hash_for_each_item_safe(item, n, bucket) \ for ((item = *(bucket)), n = item ? item->next : NULL; item; \ item = n, n = item ? (item)->next : NULL) #define trace_hash_while_item(item, bucket) \ while ((item = *(bucket))) typedef int (*trace_hash_func)(struct trace_hash_item *item, void *data); struct trace_hash_item * trace_hash_find(struct trace_hash *hash, unsigned long long key, trace_hash_func match, void *data); #endif /* _TRACE_HASH_H */ trace-cmd-2.8.3/include/traceevent/000077500000000000000000000000001351617527000171555ustar00rootroot00000000000000trace-cmd-2.8.3/include/traceevent/event-parse.h000066400000000000000000000522101351617527000215570ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #ifndef _PARSE_EVENTS_H #define _PARSE_EVENTS_H #include #include #include #include #include #include "traceevent/trace-seq.h" #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) #endif #ifndef DEBUG_RECORD #define DEBUG_RECORD 0 #endif struct tep_record { unsigned long long ts; unsigned long long offset; long long missed_events; /* buffer dropped events before */ int record_size; /* size of binary record */ int size; /* size of data */ void *data; int cpu; int ref_count; int locked; /* Do not free, even if ref_count is zero */ void *priv; #if DEBUG_RECORD struct tep_record *prev; struct tep_record *next; long alloc_addr; #endif }; /* ----------------------- tep ----------------------- */ struct tep_handle; struct tep_event; typedef int (*tep_event_handler_func)(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context); typedef int (*tep_plugin_load_func)(struct tep_handle *tep); typedef int (*tep_plugin_unload_func)(struct tep_handle *tep); struct tep_plugin_option { struct tep_plugin_option *next; void *handle; char *file; char *name; char *plugin_alias; char *description; const char *value; void *priv; int set; }; /* * Plugin hooks that can be called: * * TEP_PLUGIN_LOADER: (required) * The function name to initialized the plugin. * * int TEP_PLUGIN_LOADER(struct tep_handle *tep) * * TEP_PLUGIN_UNLOADER: (optional) * The function called just before unloading * * int TEP_PLUGIN_UNLOADER(struct tep_handle *tep) * * TEP_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading * * struct tep_plugin_option TEP_PLUGIN_OPTIONS[] = { * { * .name = "option-name", * .plugin_alias = "override-file-name", (optional) * .description = "description of option to show users", * }, * { * .name = NULL, * }, * }; * * Array must end with .name = NULL; * * * .plugin_alias is used to give a shorter name to access * the vairable. Useful if a plugin handles more than one event. * * If .value is not set, then it is considered a boolean and only * .set will be processed. If .value is defined, then it is considered * a string option and .set will be ignored. * * TEP_PLUGIN_ALIAS: (optional) * The name to use for finding options (uses filename if not defined) */ #define TEP_PLUGIN_LOADER tep_plugin_loader #define TEP_PLUGIN_UNLOADER tep_plugin_unloader #define TEP_PLUGIN_OPTIONS tep_plugin_options #define TEP_PLUGIN_ALIAS tep_plugin_alias #define _MAKE_STR(x) #x #define MAKE_STR(x) _MAKE_STR(x) #define TEP_PLUGIN_LOADER_NAME MAKE_STR(TEP_PLUGIN_LOADER) #define TEP_PLUGIN_UNLOADER_NAME MAKE_STR(TEP_PLUGIN_UNLOADER) #define TEP_PLUGIN_OPTIONS_NAME MAKE_STR(TEP_PLUGIN_OPTIONS) #define TEP_PLUGIN_ALIAS_NAME MAKE_STR(TEP_PLUGIN_ALIAS) enum tep_format_flags { TEP_FIELD_IS_ARRAY = 1, TEP_FIELD_IS_POINTER = 2, TEP_FIELD_IS_SIGNED = 4, TEP_FIELD_IS_STRING = 8, TEP_FIELD_IS_DYNAMIC = 16, TEP_FIELD_IS_LONG = 32, TEP_FIELD_IS_FLAG = 64, TEP_FIELD_IS_SYMBOLIC = 128, }; struct tep_format_field { struct tep_format_field *next; struct tep_event *event; char *type; char *name; char *alias; int offset; int size; unsigned int arraylen; unsigned int elementsize; unsigned long flags; }; struct tep_format { int nr_common; int nr_fields; struct tep_format_field *common_fields; struct tep_format_field *fields; }; struct tep_print_arg_atom { char *atom; }; struct tep_print_arg_string { char *string; int offset; }; struct tep_print_arg_bitmask { char *bitmask; int offset; }; struct tep_print_arg_field { char *name; struct tep_format_field *field; }; struct tep_print_flag_sym { struct tep_print_flag_sym *next; char *value; char *str; }; struct tep_print_arg_typecast { char *type; struct tep_print_arg *item; }; struct tep_print_arg_flags { struct tep_print_arg *field; char *delim; struct tep_print_flag_sym *flags; }; struct tep_print_arg_symbol { struct tep_print_arg *field; struct tep_print_flag_sym *symbols; }; struct tep_print_arg_hex { struct tep_print_arg *field; struct tep_print_arg *size; }; struct tep_print_arg_int_array { struct tep_print_arg *field; struct tep_print_arg *count; struct tep_print_arg *el_size; }; struct tep_print_arg_dynarray { struct tep_format_field *field; struct tep_print_arg *index; }; struct tep_print_arg; struct tep_print_arg_op { char *op; int prio; struct tep_print_arg *left; struct tep_print_arg *right; }; struct tep_function_handler; struct tep_print_arg_func { struct tep_function_handler *func; struct tep_print_arg *args; }; enum tep_print_arg_type { TEP_PRINT_NULL, TEP_PRINT_ATOM, TEP_PRINT_FIELD, TEP_PRINT_FLAGS, TEP_PRINT_SYMBOL, TEP_PRINT_HEX, TEP_PRINT_INT_ARRAY, TEP_PRINT_TYPE, TEP_PRINT_STRING, TEP_PRINT_BSTRING, TEP_PRINT_DYNAMIC_ARRAY, TEP_PRINT_OP, TEP_PRINT_FUNC, TEP_PRINT_BITMASK, TEP_PRINT_DYNAMIC_ARRAY_LEN, TEP_PRINT_HEX_STR, }; struct tep_print_arg { struct tep_print_arg *next; enum tep_print_arg_type type; union { struct tep_print_arg_atom atom; struct tep_print_arg_field field; struct tep_print_arg_typecast typecast; struct tep_print_arg_flags flags; struct tep_print_arg_symbol symbol; struct tep_print_arg_hex hex; struct tep_print_arg_int_array int_array; struct tep_print_arg_func func; struct tep_print_arg_string string; struct tep_print_arg_bitmask bitmask; struct tep_print_arg_op op; struct tep_print_arg_dynarray dynarray; }; }; struct tep_print_fmt { char *format; struct tep_print_arg *args; }; struct tep_event { struct tep_handle *tep; char *name; int id; int flags; struct tep_format format; struct tep_print_fmt print_fmt; char *system; tep_event_handler_func handler; void *context; }; enum { TEP_EVENT_FL_ISFTRACE = 0x01, TEP_EVENT_FL_ISPRINT = 0x02, TEP_EVENT_FL_ISBPRINT = 0x04, TEP_EVENT_FL_ISFUNCENT = 0x10, TEP_EVENT_FL_ISFUNCRET = 0x20, TEP_EVENT_FL_NOHANDLE = 0x40, TEP_EVENT_FL_PRINTRAW = 0x80, TEP_EVENT_FL_FAILED = 0x80000000 }; enum tep_event_sort_type { TEP_EVENT_SORT_ID, TEP_EVENT_SORT_NAME, TEP_EVENT_SORT_SYSTEM, }; enum tep_event_type { TEP_EVENT_ERROR, TEP_EVENT_NONE, TEP_EVENT_SPACE, TEP_EVENT_NEWLINE, TEP_EVENT_OP, TEP_EVENT_DELIM, TEP_EVENT_ITEM, TEP_EVENT_DQUOTE, TEP_EVENT_SQUOTE, }; typedef unsigned long long (*tep_func_handler)(struct trace_seq *s, unsigned long long *args); enum tep_func_arg_type { TEP_FUNC_ARG_VOID, TEP_FUNC_ARG_INT, TEP_FUNC_ARG_LONG, TEP_FUNC_ARG_STRING, TEP_FUNC_ARG_PTR, TEP_FUNC_ARG_MAX_TYPES }; enum tep_flag { TEP_NSEC_OUTPUT = 1, /* output in NSECS */ TEP_DISABLE_SYS_PLUGINS = 1 << 1, TEP_DISABLE_PLUGINS = 1 << 2, }; #define TEP_ERRORS \ _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \ _PE(PARSE_EVENT_FAILED, "failed to parse event"), \ _PE(READ_ID_FAILED, "failed to read event id"), \ _PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ _PE(INVALID_ARG_TYPE, "invalid argument type"), \ _PE(INVALID_EXP_TYPE, "invalid expression type"), \ _PE(INVALID_OP_TYPE, "invalid operator type"), \ _PE(INVALID_EVENT_NAME, "invalid event name"), \ _PE(EVENT_NOT_FOUND, "no event found"), \ _PE(SYNTAX_ERROR, "syntax error"), \ _PE(ILLEGAL_RVALUE, "illegal rvalue"), \ _PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \ _PE(INVALID_REGEX, "regex did not compute"), \ _PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \ _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \ _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ _PE(BAD_FILTER_ARG, "bad arg in filter tree"), \ _PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \ _PE(ILLEGAL_TOKEN, "illegal token"), \ _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ _PE(UNKNOWN_TOKEN, "unknown token"), \ _PE(FILTER_NOT_FOUND, "no filter found"), \ _PE(NOT_A_NUMBER, "must have number field"), \ _PE(NO_FILTER, "no filters exists"), \ _PE(FILTER_MISS, "record does not match to filter") #undef _PE #define _PE(__code, __str) TEP_ERRNO__ ## __code enum tep_errno { TEP_ERRNO__SUCCESS = 0, TEP_ERRNO__FILTER_MATCH = TEP_ERRNO__SUCCESS, /* * Choose an arbitrary negative big number not to clash with standard * errno since SUS requires the errno has distinct positive values. * See 'Issue 6' in the link below. * * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html */ __TEP_ERRNO__START = -100000, TEP_ERRORS, __TEP_ERRNO__END, }; #undef _PE struct tep_plugin_list; #define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1)) struct tep_plugin_list *tep_load_plugins(struct tep_handle *tep); void tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep); char **tep_plugin_list_options(void); void tep_plugin_free_options_list(char **list); int tep_plugin_add_options(const char *name, struct tep_plugin_option *options); void tep_plugin_remove_options(struct tep_plugin_option *options); void tep_print_plugins(struct trace_seq *s, const char *prefix, const char *suffix, const struct tep_plugin_list *list); /* tep_handle */ typedef char *(tep_func_resolver_t)(void *priv, unsigned long long *addrp, char **modp); void tep_set_flag(struct tep_handle *tep, int flag); void tep_clear_flag(struct tep_handle *tep, enum tep_flag flag); bool tep_test_flag(struct tep_handle *tep, enum tep_flag flags); static inline int tep_is_bigendian(void) { unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; unsigned int val; memcpy(&val, str, 4); return val == 0x01020304; } /* taken from kernel/trace/trace.h */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, TRACE_FLAG_IRQS_NOSUPPORT = 0x02, TRACE_FLAG_NEED_RESCHED = 0x04, TRACE_FLAG_HARDIRQ = 0x08, TRACE_FLAG_SOFTIRQ = 0x10, }; int tep_set_function_resolver(struct tep_handle *tep, tep_func_resolver_t *func, void *priv); void tep_reset_function_resolver(struct tep_handle *tep); int tep_register_comm(struct tep_handle *tep, const char *comm, int pid); int tep_override_comm(struct tep_handle *tep, const char *comm, int pid); int tep_register_trace_clock(struct tep_handle *tep, const char *trace_clock); int tep_register_function(struct tep_handle *tep, char *name, unsigned long long addr, char *mod); int tep_register_print_string(struct tep_handle *tep, const char *fmt, unsigned long long addr); bool tep_is_pid_registered(struct tep_handle *tep, int pid); void tep_print_event_task(struct tep_handle *tep, struct trace_seq *s, struct tep_event *event, struct tep_record *record); void tep_print_event_time(struct tep_handle *tep, struct trace_seq *s, struct tep_event *event, struct tep_record *record, bool use_trace_clock); void tep_print_event_data(struct tep_handle *tep, struct trace_seq *s, struct tep_event *event, struct tep_record *record); void tep_print_event(struct tep_handle *tep, struct trace_seq *s, struct tep_record *record, bool use_trace_clock); int tep_parse_header_page(struct tep_handle *tep, char *buf, unsigned long size, int long_size); enum tep_errno tep_parse_event(struct tep_handle *tep, const char *buf, unsigned long size, const char *sys); enum tep_errno tep_parse_format(struct tep_handle *tep, struct tep_event **eventp, const char *buf, unsigned long size, const char *sys); void *tep_get_field_raw(struct trace_seq *s, struct tep_event *event, const char *name, struct tep_record *record, int *len, int err); int tep_get_field_val(struct trace_seq *s, struct tep_event *event, const char *name, struct tep_record *record, unsigned long long *val, int err); int tep_get_common_field_val(struct trace_seq *s, struct tep_event *event, const char *name, struct tep_record *record, unsigned long long *val, int err); int tep_get_any_field_val(struct trace_seq *s, struct tep_event *event, const char *name, struct tep_record *record, unsigned long long *val, int err); int tep_print_num_field(struct trace_seq *s, const char *fmt, struct tep_event *event, const char *name, struct tep_record *record, int err); int tep_print_func_field(struct trace_seq *s, const char *fmt, struct tep_event *event, const char *name, struct tep_record *record, int err); enum tep_reg_handler { TEP_REGISTER_SUCCESS = 0, TEP_REGISTER_SUCCESS_OVERWRITE, }; int tep_register_event_handler(struct tep_handle *tep, int id, const char *sys_name, const char *event_name, tep_event_handler_func func, void *context); int tep_unregister_event_handler(struct tep_handle *tep, int id, const char *sys_name, const char *event_name, tep_event_handler_func func, void *context); int tep_register_print_function(struct tep_handle *tep, tep_func_handler func, enum tep_func_arg_type ret_type, char *name, ...); int tep_unregister_print_function(struct tep_handle *tep, tep_func_handler func, char *name); struct tep_format_field *tep_find_common_field(struct tep_event *event, const char *name); struct tep_format_field *tep_find_field(struct tep_event *event, const char *name); struct tep_format_field *tep_find_any_field(struct tep_event *event, const char *name); const char *tep_find_function(struct tep_handle *tep, unsigned long long addr); unsigned long long tep_find_function_address(struct tep_handle *tep, unsigned long long addr); unsigned long long tep_read_number(struct tep_handle *tep, const void *ptr, int size); int tep_read_number_field(struct tep_format_field *field, const void *data, unsigned long long *value); struct tep_event *tep_get_first_event(struct tep_handle *tep); struct tep_event *tep_get_event(struct tep_handle *tep, int index); int tep_get_events_count(struct tep_handle *tep); struct tep_event *tep_find_event(struct tep_handle *tep, int id); struct tep_event * tep_find_event_by_name(struct tep_handle *tep, const char *sys, const char *name); struct tep_event * tep_find_event_by_record(struct tep_handle *tep, struct tep_record *record); void tep_data_latency_format(struct tep_handle *tep, struct trace_seq *s, struct tep_record *record); int tep_data_type(struct tep_handle *tep, struct tep_record *rec); int tep_data_pid(struct tep_handle *tep, struct tep_record *rec); int tep_data_preempt_count(struct tep_handle *tep, struct tep_record *rec); int tep_data_flags(struct tep_handle *tep, struct tep_record *rec); const char *tep_data_comm_from_pid(struct tep_handle *tep, int pid); struct tep_cmdline; struct tep_cmdline *tep_data_pid_from_comm(struct tep_handle *tep, const char *comm, struct tep_cmdline *next); int tep_cmdline_pid(struct tep_handle *tep, struct tep_cmdline *cmdline); void tep_print_field(struct trace_seq *s, void *data, struct tep_format_field *field); void tep_print_fields(struct trace_seq *s, void *data, int size __maybe_unused, struct tep_event *event); void tep_event_info(struct trace_seq *s, struct tep_event *event, struct tep_record *record); int tep_strerror(struct tep_handle *tep, enum tep_errno errnum, char *buf, size_t buflen); struct tep_event **tep_list_events(struct tep_handle *tep, enum tep_event_sort_type); struct tep_event **tep_list_events_copy(struct tep_handle *tep, enum tep_event_sort_type); struct tep_format_field **tep_event_common_fields(struct tep_event *event); struct tep_format_field **tep_event_fields(struct tep_event *event); enum tep_endian { TEP_LITTLE_ENDIAN = 0, TEP_BIG_ENDIAN }; int tep_get_cpus(struct tep_handle *tep); void tep_set_cpus(struct tep_handle *tep, int cpus); int tep_get_long_size(struct tep_handle *tep); void tep_set_long_size(struct tep_handle *tep, int long_size); int tep_get_page_size(struct tep_handle *tep); void tep_set_page_size(struct tep_handle *tep, int _page_size); bool tep_is_file_bigendian(struct tep_handle *tep); void tep_set_file_bigendian(struct tep_handle *tep, enum tep_endian endian); bool tep_is_local_bigendian(struct tep_handle *tep); void tep_set_local_bigendian(struct tep_handle *tep, enum tep_endian endian); bool tep_is_latency_format(struct tep_handle *tep); void tep_set_latency_format(struct tep_handle *tep, int lat); int tep_get_header_page_size(struct tep_handle *tep); int tep_get_header_timestamp_size(struct tep_handle *tep); bool tep_is_old_format(struct tep_handle *tep); void tep_set_print_raw(struct tep_handle *tep, int print_raw); void tep_set_test_filters(struct tep_handle *tep, int test_filters); struct tep_handle *tep_alloc(void); void tep_free(struct tep_handle *tep); void tep_ref(struct tep_handle *tep); void tep_unref(struct tep_handle *tep); int tep_get_ref(struct tep_handle *tep); /* access to the internal parser */ void tep_buffer_init(const char *buf, unsigned long long size); enum tep_event_type tep_read_token(char **tok); void tep_free_token(char *token); int tep_peek_char(void); const char *tep_get_input_buf(void); unsigned long long tep_get_input_buf_ptr(void); /* for debugging */ void tep_print_funcs(struct tep_handle *tep); void tep_print_printk(struct tep_handle *tep); /* ----------------------- filtering ----------------------- */ enum tep_filter_boolean_type { TEP_FILTER_FALSE, TEP_FILTER_TRUE, }; enum tep_filter_op_type { TEP_FILTER_OP_AND = 1, TEP_FILTER_OP_OR, TEP_FILTER_OP_NOT, }; enum tep_filter_cmp_type { TEP_FILTER_CMP_NONE, TEP_FILTER_CMP_EQ, TEP_FILTER_CMP_NE, TEP_FILTER_CMP_GT, TEP_FILTER_CMP_LT, TEP_FILTER_CMP_GE, TEP_FILTER_CMP_LE, TEP_FILTER_CMP_MATCH, TEP_FILTER_CMP_NOT_MATCH, TEP_FILTER_CMP_REGEX, TEP_FILTER_CMP_NOT_REGEX, }; enum tep_filter_exp_type { TEP_FILTER_EXP_NONE, TEP_FILTER_EXP_ADD, TEP_FILTER_EXP_SUB, TEP_FILTER_EXP_MUL, TEP_FILTER_EXP_DIV, TEP_FILTER_EXP_MOD, TEP_FILTER_EXP_RSHIFT, TEP_FILTER_EXP_LSHIFT, TEP_FILTER_EXP_AND, TEP_FILTER_EXP_OR, TEP_FILTER_EXP_XOR, TEP_FILTER_EXP_NOT, }; enum tep_filter_arg_type { TEP_FILTER_ARG_NONE, TEP_FILTER_ARG_BOOLEAN, TEP_FILTER_ARG_VALUE, TEP_FILTER_ARG_FIELD, TEP_FILTER_ARG_EXP, TEP_FILTER_ARG_OP, TEP_FILTER_ARG_NUM, TEP_FILTER_ARG_STR, }; enum tep_filter_value_type { TEP_FILTER_NUMBER, TEP_FILTER_STRING, TEP_FILTER_CHAR }; struct tep_filter_arg; struct tep_filter_arg_boolean { enum tep_filter_boolean_type value; }; struct tep_filter_arg_field { struct tep_format_field *field; }; struct tep_filter_arg_value { enum tep_filter_value_type type; union { char *str; unsigned long long val; }; }; struct tep_filter_arg_op { enum tep_filter_op_type type; struct tep_filter_arg *left; struct tep_filter_arg *right; }; struct tep_filter_arg_exp { enum tep_filter_exp_type type; struct tep_filter_arg *left; struct tep_filter_arg *right; }; struct tep_filter_arg_num { enum tep_filter_cmp_type type; struct tep_filter_arg *left; struct tep_filter_arg *right; }; struct tep_filter_arg_str { enum tep_filter_cmp_type type; struct tep_format_field *field; char *val; char *buffer; regex_t reg; }; struct tep_filter_arg { enum tep_filter_arg_type type; union { struct tep_filter_arg_boolean boolean; struct tep_filter_arg_field field; struct tep_filter_arg_value value; struct tep_filter_arg_op op; struct tep_filter_arg_exp exp; struct tep_filter_arg_num num; struct tep_filter_arg_str str; }; }; struct tep_filter_type { int event_id; struct tep_event *event; struct tep_filter_arg *filter; }; #define TEP_FILTER_ERROR_BUFSZ 1024 struct tep_event_filter { struct tep_handle *tep; int filters; struct tep_filter_type *event_filters; char error_buffer[TEP_FILTER_ERROR_BUFSZ]; }; struct tep_event_filter *tep_filter_alloc(struct tep_handle *tep); /* for backward compatibility */ #define FILTER_NONE TEP_ERRNO__NO_FILTER #define FILTER_NOEXIST TEP_ERRNO__FILTER_NOT_FOUND #define FILTER_MISS TEP_ERRNO__FILTER_MISS #define FILTER_MATCH TEP_ERRNO__FILTER_MATCH enum tep_errno tep_filter_add_filter_str(struct tep_event_filter *filter, const char *filter_str); enum tep_errno tep_filter_match(struct tep_event_filter *filter, struct tep_record *record); int tep_filter_strerror(struct tep_event_filter *filter, enum tep_errno err, char *buf, size_t buflen); int tep_event_filtered(struct tep_event_filter *filter, int event_id); void tep_filter_reset(struct tep_event_filter *filter); void tep_filter_free(struct tep_event_filter *filter); char *tep_filter_make_string(struct tep_event_filter *filter, int event_id); int tep_filter_remove_event(struct tep_event_filter *filter, int event_id); int tep_filter_copy(struct tep_event_filter *dest, struct tep_event_filter *source); int tep_filter_compare(struct tep_event_filter *filter1, struct tep_event_filter *filter2); #endif /* _PARSE_EVENTS_H */ trace-cmd-2.8.3/include/traceevent/kbuffer.h000066400000000000000000000034551351617527000207610ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2012 Red Hat Inc, Steven Rostedt * */ #ifndef _KBUFFER_H #define _KBUFFER_H #ifndef TS_SHIFT #define TS_SHIFT 27 #endif enum kbuffer_endian { KBUFFER_ENDIAN_BIG, KBUFFER_ENDIAN_LITTLE, }; enum kbuffer_long_size { KBUFFER_LSIZE_4, KBUFFER_LSIZE_8, }; enum { KBUFFER_TYPE_PADDING = 29, KBUFFER_TYPE_TIME_EXTEND = 30, KBUFFER_TYPE_TIME_STAMP = 31, }; struct kbuffer; struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian); void kbuffer_free(struct kbuffer *kbuf); int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer); void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts); void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts); unsigned long long kbuffer_timestamp(struct kbuffer *kbuf); unsigned long long kbuffer_subbuf_timestamp(struct kbuffer *kbuf, void *subbuf); unsigned int kbuffer_ptr_delta(struct kbuffer *kbuf, void *ptr); void *kbuffer_translate_data(int swap, void *data, unsigned int *size); void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts); int kbuffer_curr_index(struct kbuffer *kbuf); int kbuffer_curr_offset(struct kbuffer *kbuf); int kbuffer_curr_size(struct kbuffer *kbuf); int kbuffer_event_size(struct kbuffer *kbuf); int kbuffer_missed_events(struct kbuffer *kbuf); int kbuffer_subbuffer_size(struct kbuffer *kbuf); void kbuffer_set_old_format(struct kbuffer *kbuf); int kbuffer_start_of_data(struct kbuffer *kbuf); /* Debugging */ struct kbuffer_raw_info { int type; int length; unsigned long long delta; void *next; }; /* Read raw data */ struct kbuffer_raw_info *kbuffer_raw_get(struct kbuffer *kbuf, void *subbuf, struct kbuffer_raw_info *info); #endif /* _K_BUFFER_H */ trace-cmd-2.8.3/include/traceevent/trace-seq.h000066400000000000000000000026331351617527000212160ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #ifndef _TRACE_SEQ_H #define _TRACE_SEQ_H #include #include /* ----------------------- trace_seq ----------------------- */ #ifndef TRACE_SEQ_BUF_SIZE #define TRACE_SEQ_BUF_SIZE 4096 #endif enum trace_seq_fail { TRACE_SEQ__GOOD, TRACE_SEQ__BUFFER_POISONED, TRACE_SEQ__MEM_ALLOC_FAILED, }; /* * Trace sequences are used to allow a function to call several other functions * to create a string of data to use (up to a max of PAGE_SIZE). */ struct trace_seq { char *buffer; unsigned int buffer_size; unsigned int len; unsigned int readpos; enum trace_seq_fail state; }; void trace_seq_init(struct trace_seq *s); void trace_seq_reset(struct trace_seq *s); void trace_seq_destroy(struct trace_seq *s); extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) __attribute__ ((format (printf, 2, 0))); extern int trace_seq_puts(struct trace_seq *s, const char *str); extern int trace_seq_putc(struct trace_seq *s, unsigned char c); extern void trace_seq_terminate(struct trace_seq *s); extern int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp); extern int trace_seq_do_printf(struct trace_seq *s); #endif /* _TRACE_SEQ_H */ trace-cmd-2.8.3/include/version.h000066400000000000000000000004211351617527000166500ustar00rootroot00000000000000#ifndef _VERSION_H #define _VERSION_H #define VERSION(a, b) (((a) << 8) + (b)) #ifdef BUILDGUI #include "ks_version.h" #else #include "tc_version.h" #endif #define _STR(x) #x #define STR(x) _STR(x) #define FILE_VERSION_STRING STR(FILE_VERSION) #endif /* _VERSION_H */ trace-cmd-2.8.3/kernel-shark/000077500000000000000000000000001351617527000157605ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/CMakeLists.txt000066400000000000000000000051671351617527000205310ustar00rootroot00000000000000# Check if cmake has the required version cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) # Set the name and version of the project project(kernel-shark) set(KS_APP_NAME "kernelshark") set(KS_VERSION_MAJOR 0) set(KS_VERSION_MINOR 9) set(KS_VERSION_PATCH 8) set(KS_VERSION_STRING ${KS_VERSION_MAJOR}.${KS_VERSION_MINOR}.${KS_VERSION_PATCH}) message("\n project: Kernel Shark: (version: ${KS_VERSION_STRING})\n") set(KS_DIR ${CMAKE_SOURCE_DIR}) include(${KS_DIR}/build/FindTraceCmd.cmake) include(${KS_DIR}/build/FindJSONC.cmake) find_package(Doxygen) set(OpenGL_GL_PREFERENCE LEGACY) find_package(OpenGL) find_package(GLUT) find_package(Qt5Widgets 5.7.1) find_package(Qt5Network) if (Qt5Widgets_FOUND) message(STATUS "Found Qt5Widgets: (version ${Qt5Widgets_VERSION})") endif (Qt5Widgets_FOUND) set(LIBRARY_OUTPUT_PATH "${KS_DIR}/lib") set(EXECUTABLE_OUTPUT_PATH "${KS_DIR}/bin") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -g -pthread -fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -std=c++11 -pthread -fPIC") if(NOT _INSTALL_PREFIX) set(_INSTALL_PREFIX "/usr/local") endif() set(KS_PLUGIN_INSTALL_PREFIX ${_INSTALL_PREFIX}/lib/${KS_APP_NAME}/plugins/) set(KS_ICON ksharkicon.png) if (NOT _DEBUG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") endif (NOT _DEBUG) SET(CMAKE_INSTALL_RPATH "${_INSTALL_PREFIX}/lib/${KS_APP_NAME}/") SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) include_directories(${KS_DIR}/src/ ${KS_DIR}/build/src/ ${JSONC_INCLUDE_DIR} ${TRACECMD_INCLUDE_DIR}) message("") message(STATUS "C flags : " ${CMAKE_C_FLAGS}) message(STATUS "CXX flags : " ${CMAKE_CXX_FLAGS}) message(STATUS "Linker flags : " ${CMAKE_EXE_LINKER_FLAGS}) add_subdirectory(${KS_DIR}/src) add_subdirectory(${KS_DIR}/examples) if (_DOXYGEN_DOC AND DOXYGEN_FOUND) message("\n doxygen documentation ...") add_custom_target(doc ALL) add_custom_command(TARGET doc COMMAND doxygen dox_config > dox_build.log WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/doc) set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${KS_DIR}/doc/dox_build.log" "${KS_DIR}/doc/html" "${KS_DIR}/doc/latex") endif () configure_file( ${KS_DIR}/build/ks.desktop.cmake ${KS_DIR}/${KS_APP_NAME}.desktop) configure_file( ${KS_DIR}/build/org.freedesktop.kshark-record.policy.cmake ${KS_DIR}/org.freedesktop.kshark-record.policy) message("") trace-cmd-2.8.3/kernel-shark/README000066400000000000000000000035701351617527000166450ustar00rootroot00000000000000 This directory contains the new Qt-based version of the KernelShark GUI. Third Party Software: ------------------------------------------------------------ KernelShark has the following external dependencies: Cmake, Json-C, OpenGL/Glut, Qt5Base. 1. In order to install the packages on Ubuntu do the following: sudo apt-get install build-essential git cmake libjson-c-dev -y sudo apt-get install freeglut3-dev libxmu-dev libxi-dev -y sudo apt-get install qtbase5-dev -y 1.1 I you want to be able to generate Doxygen documentation: sudo apt-get install graphviz doxygen-gui -y 2. In order to install the packages on Fedora, as root do the following: dnf install gcc gcc-c++ git cmake json-c-devel -y dnf install freeglut-devel redhat-rpm-config -y dnf install qt5-qtbase-devel -y 2.1 I you want to be able to generate Doxygen documentation: dnf install graphviz doxygen -y Building: ------------------------------------------------------------ 1. Follow the instructions given in trace-cmd/README and build the original trace-cmd end traceevent libraries. 2. Do: cd kernel-shark/build cmake ../ make 2.1.1 In order to create a Doxygen documentation add -D_DOXYGEN_DOC=1 as a CMake Command-Line option. 2.1.2 In order to generates complete debug information to be used by GDB add -D_DEBUG=1 as a CMake Command-Line option. 2.1.3 By default, installation prefix is "/usr/local". It can be changed using -D_INSTALL_PREFIX= as a CMake Command-Line option. Example: cmake -D_DOXYGEN_DOC=1 -D_DEBUG=1 -D_INSTALL_PREFIX=/usr ../ 2.2.1 Use "make clean" if you want to delete all already compiled objects. 2.2.2 Use the script "cmake_clean.sh" if you want to delete all already compiled objects and all files generated by CMake. 3. After building the code "kernel-shark/lib" will contain all libraries and "kernel-shark/bin" will contain all executables. trace-cmd-2.8.3/kernel-shark/bin/000077500000000000000000000000001351617527000165305ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/bin/kshark-su-record000077500000000000000000000002151351617527000216400ustar00rootroot00000000000000#!/bin/bash if [ $XDG_SESSION_TYPE = "wayland" ] then xhost +si:localuser:root &>/dev/null fi pkexec kshark-record -o ${PWD}/trace.dat trace-cmd-2.8.3/kernel-shark/build/000077500000000000000000000000001351617527000170575ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/build/FindJSONC.cmake000066400000000000000000000024171351617527000215420ustar00rootroot00000000000000# - Try to find json-c # https://cmake.org/Wiki/CMake:How_To_Find_Libraries # Once done this will define # JSONC_FOUND - System has json-c # JSONC_INCLUDE_DIRS - The json-c include directories # JSONC_LIBRARIES - The libraries needed to use json-c # JSONC_DEFINITIONS - Compiler switches required for using json-c find_package(PkgConfig) pkg_check_modules(PC_JSONC QUIET json-c) set(JSONC_DEFINITIONS ${PC_JSONC_CFLAGS_OTHER}) find_path(JSONC_INCLUDE_DIR json.h HINTS ${PC_JSONC_INCLUDEDIR} ${PC_JSONC_INCLUDE_DIRS} PATH_SUFFIXES json-c) find_library(JSONC_LIBRARY NAMES json-c libjson-c HINTS ${PC_JSONC_LIBDIR} ${PC_JSONC_LIBRARY_DIRS}) find_library(JSONC_LIBRARY NAMES json-c libjson-c HINTS ${PC_JSON-C_LIBDIR} ${PC_JSON-C_LIBRARY_DIRS}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set JSONC_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(JSONC DEFAULT_MSG JSONC_LIBRARY JSONC_INCLUDE_DIR) if (NOT JSONC_FOUND) message(FATAL_ERROR "Json-C is Required!\n") endif (NOT JSONC_FOUND) mark_as_advanced(JSONC_INCLUDE_DIR JSONC_LIBRARY) set(JSONC_LIBRARIES ${JSONC_LIBRARY}) set(JSONC_INCLUDE_DIRS ${JSONC_INCLUDE_DIR}) trace-cmd-2.8.3/kernel-shark/build/FindTraceCmd.cmake000066400000000000000000000045551351617527000223550ustar00rootroot00000000000000# Find traceevent and trace-cmd # This module finds an installed trace-cmd package. # # It sets the following variables: # TRACEEVENT_LIBRARY, traceevent the library. # TRACEEVENT_FOUND, If false, do not try to use traceevent. # # TRACECMD_INCLUDE_DIR, where to find trace-cmd header. # TRACECMD_LIBRARY, the trace-cmd library. # TRACECMD_FOUND, If false, do not try to use trace-cmd. # MESSAGE(" Looking for trace-cmd ...") # First search in the user provided paths. find_path(TRACECMD_BIN_DIR NAMES trace-cmd PATHS $ENV{TRACE_CMD}/tracecmd/ ${CMAKE_SOURCE_DIR}/../tracecmd/ NO_DEFAULT_PATH) find_path(TRACECMD_INCLUDE_DIR NAMES trace-cmd/trace-cmd.h PATHS $ENV{TRACE_CMD}/include/ ${CMAKE_SOURCE_DIR}/../include/ NO_DEFAULT_PATH) find_library(TRACECMD_LIBRARY NAMES trace-cmd/libtracecmd.a PATHS $ENV{TRACE_CMD}/lib/ ${CMAKE_SOURCE_DIR}/../lib/ NO_DEFAULT_PATH) find_library(TRACEEVENT_LIBRARY NAMES traceevent/libtraceevent.a PATHS $ENV{TRACE_CMD}/lib/ ${CMAKE_SOURCE_DIR}/../lib/ NO_DEFAULT_PATH) # If not found, search in the default system paths. Note that if the previous # search was successful "find_path" will do nothing this time. find_path(TRACECMD_BIN_DIR NAMES trace-cmd) find_path(TRACECMD_INCLUDE_DIR NAMES trace-cmd/trace-cmd.h) find_library(TRACECMD_LIBRARY NAMES trace-cmd/libtracecmd.so) find_library(TRACEEVENT_LIBRARY NAMES traceevent/libtraceevent.so) IF (TRACECMD_INCLUDE_DIR AND TRACECMD_LIBRARY) SET(TRACECMD_FOUND TRUE) ENDIF (TRACECMD_INCLUDE_DIR AND TRACECMD_LIBRARY) IF (TRACECMD_FOUND) MESSAGE(STATUS "Found trace-cmd: ${TRACECMD_LIBRARY}") ELSE (TRACECMD_FOUND) MESSAGE(FATAL_ERROR "\nCould not find trace-cmd!\n") ENDIF (TRACECMD_FOUND) IF (TRACEEVENT_LIBRARY) SET(TRACEEVENT_FOUND TRUE) ENDIF (TRACEEVENT_LIBRARY) IF (TRACEEVENT_FOUND) MESSAGE(STATUS "Found traceevent: ${TRACEEVENT_LIBRARY}") ELSE (TRACEEVENT_FOUND) MESSAGE(FATAL_ERROR "\nCould not find libtraceevent!\n") ENDIF (TRACEEVENT_FOUND) trace-cmd-2.8.3/kernel-shark/build/cmake_clean.sh000077500000000000000000000004301351617527000216350ustar00rootroot00000000000000make clean rm CMakeCache.txt rm cmake_install.cmake rm Makefile rm -rf CMakeFiles/ rm -rf src/ rm -rf examples/ rm -f ../lib/* rm ../kernelshark.desktop rm ../org.freedesktop.kshark-record.policy rm -f ../src/KsCmakeDef.hpp rm -f CMakeDoxyfile.in rm -f CMakeDoxygenDefaults.cmake trace-cmd-2.8.3/kernel-shark/build/cmake_uninstall.sh000077500000000000000000000004771351617527000225770ustar00rootroot00000000000000#!/bin/bash CYAN='\e[36m' PURPLE='\e[35m' NC='\e[0m' # No Color if [[ $EUID -ne 0 ]]; then echo -e "${PURPLE}Permission denied${NC}" 1>&2 exit 100 fi if [ -e install_manifest.txt ] then echo -e "${CYAN}Uninstall the project...${NC}" xargs rm -v < install_manifest.txt rm -f install_manifest.txt fi trace-cmd-2.8.3/kernel-shark/build/deff.h.cmake000066400000000000000000000016671351617527000212250ustar00rootroot00000000000000 /** * \file KsCmakeDef.hpp * \brief This File is generated by CMAKE */ /* ! -- Do Not Hand Edit - This File is generated by CMAKE -- ! */ #ifndef _KS_CONFIG_H #define _KS_CONFIG_H /** KernelShark Version number. */ #cmakedefine KS_VERSION_STRING "@KS_VERSION_STRING@" /** KernelShark installation prefix path. */ #cmakedefine _INSTALL_PREFIX "@_INSTALL_PREFIX@" /** KernelShark plugins installation prefix path. */ #cmakedefine KS_PLUGIN_INSTALL_PREFIX "@KS_PLUGIN_INSTALL_PREFIX@" /** Location of the trace-cmd executable. */ #cmakedefine TRACECMD_BIN_DIR "@TRACECMD_BIN_DIR@" /** "pkexec" executable. */ #cmakedefine DO_AS_ROOT "@DO_AS_ROOT@" #ifdef __cplusplus #include /** * String containing semicolon-separated list of plugin names. * The plugins to be loaded when KernelShark starts are tagged * with "default". */ const QString plugins = "@PLUGINS@"; #endif /* __cplusplus */ #endif // _KS_CONFIG_H trace-cmd-2.8.3/kernel-shark/build/ks.desktop.cmake000066400000000000000000000003671351617527000221540ustar00rootroot00000000000000[Desktop Entry] Version=@KS_VERSION_STRING@ Type=Application Name=Kernel Shark GenericName=Kernel Shark Comment= Exec=@_INSTALL_PREFIX@/bin/@KS_APP_NAME@ Icon=@_INSTALL_PREFIX@/share/icons/@KS_APP_NAME@/@KS_ICON@ Categories=System; Terminal=false trace-cmd-2.8.3/kernel-shark/build/org.freedesktop.kshark-record.policy.cmake000066400000000000000000000013551351617527000272220ustar00rootroot00000000000000 Authentication is required to run KernelShark Record auth_admin auth_admin auth_admin @_INSTALL_PREFIX@/bin/kshark-record true trace-cmd-2.8.3/kernel-shark/doc/000077500000000000000000000000001351617527000165255ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/doc/dox_config000066400000000000000000000010511351617527000205640ustar00rootroot00000000000000# For detailed description of all Doxygen Configuration Parameters see: # http://www.doxygen.org/manual/config.html DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "kernel-shark" PROJECT_BRIEF = "Kernel Shark is a front-end reader of the Linux kernel tracing data." INPUT = ../src/ ../src/plugins/ SOURCE_BROWSER = YES QT_AUTOBRIEF = YES TAB_SIZE = 8 OPTIMIZE_OUTPUT_FOR_C = YES CASE_SENSE_NAMES = YES SORT_MEMBER_DOCS = NO STRICT_PROTO_MATCHING = YES DOT_MULTI_TARGETS = YES trace-cmd-2.8.3/kernel-shark/examples/000077500000000000000000000000001351617527000175765ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/examples/CMakeLists.txt000066400000000000000000000013141351617527000223350ustar00rootroot00000000000000message("\n examples ...") message(STATUS "dataload") add_executable(dload dataload.c) target_link_libraries(dload kshark) message(STATUS "datafilter") add_executable(dfilter datafilter.c) target_link_libraries(dfilter kshark) message(STATUS "datahisto") add_executable(dhisto datahisto.c) target_link_libraries(dhisto kshark) message(STATUS "confogio") add_executable(confio configio.c) target_link_libraries(confio kshark) message(STATUS "dataplot") add_executable(dplot dataplot.cpp) target_link_libraries(dplot kshark-plot) message(STATUS "widgetdemo") add_executable(widgetdemo widgetdemo.cpp) target_link_libraries(widgetdemo kshark-gui) trace-cmd-2.8.3/kernel-shark/examples/configio.c000066400000000000000000000032411351617527000215370ustar00rootroot00000000000000#include #include #include "libkshark.h" int main(int argc, char **argv) { struct kshark_config_doc *conf, *filter, *hello; struct kshark_context *kshark_ctx; int *ids = NULL, i; /* Create a new kshark session. */ kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx)) return 1; if (argc == 1) { tracecmd_filter_id_add(kshark_ctx->show_task_filter, 314); tracecmd_filter_id_add(kshark_ctx->show_task_filter, 42); /* Create a new Confog. doc. */ conf = kshark_config_new("foo.bar.config", KS_CONFIG_JSON); /* Add filter's info. */ filter = kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON); kshark_config_doc_add(conf, "Filters" ,filter); /* Add "Hello Kernel" message. */ hello = kshark_string_config_alloc(); hello->conf_doc = "Hello Kernel"; kshark_config_doc_add(conf, "Message" ,hello); /* Save to file. */ kshark_save_config_file("conf.json", conf); } else { /* Open a Config. file. */ conf = kshark_open_config_file(argv[1], "foo.bar.config"); /* Retrieve the filter's info. */ filter = kshark_config_alloc(KS_CONFIG_JSON); if (kshark_config_doc_get(conf, "Filters" ,filter)) { kshark_import_all_filters(kshark_ctx, filter); /* Get the array of Ids to be fitered. */ ids = tracecmd_filter_ids(kshark_ctx->show_task_filter); for (i = 0; i < kshark_ctx->show_task_filter->count; ++i) printf("pid: %i\n", ids[i]); } /* Retrieve the message. */ hello = kshark_string_config_alloc(); if (kshark_config_doc_get(conf, "Message" ,hello)) puts((char *) hello->conf_doc); free(filter); free(hello); free(ids); } kshark_free_config_doc(conf); kshark_free(kshark_ctx); return 0; } trace-cmd-2.8.3/kernel-shark/examples/datafilter.c000066400000000000000000000066271351617527000220740ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ // C #include #include // KernelShark #include "libkshark.h" const char *default_file = "trace.dat"; int main(int argc, char **argv) { ssize_t i, n_rows, n_tasks, n_evts, count; struct kshark_context *kshark_ctx; struct kshark_entry **data = NULL; struct tep_event_filter *adv_filter; struct tep_event *event; char *entry_str; bool status; int *pids; /* Create a new kshark session. */ kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx)) return 1; /* Open a trace data file produced by trace-cmd. */ if (argc > 1) status = kshark_open(kshark_ctx, argv[1]); else status = kshark_open(kshark_ctx, default_file); if (!status) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ n_rows = kshark_load_data_entries(kshark_ctx, &data); if (n_rows < 1) { kshark_free(kshark_ctx); return 1; } /* Filter the trace data coming from trace-cmd. */ n_tasks = kshark_get_task_pids(kshark_ctx, &pids); for (i = 0; i < n_tasks; ++i) { const char *task_str = tep_data_comm_from_pid(kshark_ctx->pevent, pids[i]); if (strcmp(task_str, "trace-cmd") == 0) kshark_filter_add_id(kshark_ctx, KS_HIDE_TASK_FILTER, pids[i]); } free(pids); /* * Set the Filter Mask. In this case we want to avoid showing the * filterd entris in text format. */ kshark_ctx->filter_mask = KS_TEXT_VIEW_FILTER_MASK; kshark_filter_entries(kshark_ctx, data, n_rows); /* Print to the screen the first 10 visible entries. */ count = 0; i = 0; for (i = 0; i < n_rows; ++i) { if (data[i]->visible & KS_TEXT_VIEW_FILTER_MASK) { entry_str = kshark_dump_entry(data[i]); puts(entry_str); free(entry_str); if (++count > 10) break; } ++i; } puts("\n\n"); /* Show only "sched" events. */ n_evts = tep_get_events_count(kshark_ctx->pevent); for (i = 0; i < n_evts; ++i) { event = tep_get_event(kshark_ctx->pevent, i); if (strcmp(event->system, "sched") == 0) kshark_filter_add_id(kshark_ctx, KS_SHOW_EVENT_FILTER, event->id); } kshark_filter_entries(kshark_ctx, data, n_rows); /* Print to the screen the first 10 visible entries. */ count = 0; i = 0; for (i = 0; i < n_rows; ++i) { if (data[i]->visible & KS_TEXT_VIEW_FILTER_MASK) { entry_str = kshark_dump_entry(data[i]); puts(entry_str); free(entry_str); if (++count > 10) break; } ++i; } puts("\n\n"); /* Clear all filters. */ kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER); kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER); /* Use the Advanced filter to do event content based filtering. */ adv_filter = kshark_ctx->advanced_event_filter; tep_filter_add_filter_str(adv_filter, "sched/sched_wakeup:target_cpu==1"); /* The Advanced filter requires reloading the data. */ for (i = 0; i < n_rows; ++i) free(data[i]); n_rows = kshark_load_data_entries(kshark_ctx, &data); count = 0; for (i = 0; i < n_rows; ++i) { if (data[i]->visible & KS_EVENT_VIEW_FILTER_MASK) { entry_str = kshark_dump_entry(data[i]); puts(entry_str); free(entry_str); if (++count > 10) break; } } /* Free the memory. */ for (i = 0; i < n_rows; ++i) free(data[i]); free(data); /* Close the file. */ kshark_close(kshark_ctx); /* Close the session. */ kshark_free(kshark_ctx); return 0; } trace-cmd-2.8.3/kernel-shark/examples/datahisto.c000066400000000000000000000067501351617527000217320ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ // C #include #include // KernelShark #include "libkshark.h" #include "libkshark-model.h" #define N_BINS 5 const char *default_file = "trace.dat"; void dump_bin(struct kshark_trace_histo *histo, int bin, const char *type, int val) { const struct kshark_entry *e_front, *e_back; char *entry_str; ssize_t i_front, i_back; printf("bin %i {\n", bin); if (strcmp(type, "cpu") == 0) { e_front = ksmodel_get_entry_front(histo, bin, true, kshark_match_cpu, val, NULL, &i_front); e_back = ksmodel_get_entry_back(histo, bin, true, kshark_match_cpu, val, NULL, &i_back); } else if (strcmp(type, "task") == 0) { e_front = ksmodel_get_entry_front(histo, bin, true, kshark_match_pid, val, NULL, &i_front); e_back = ksmodel_get_entry_back(histo, bin, true, kshark_match_pid, val, NULL, &i_back); } else { i_front = ksmodel_first_index_at_bin(histo, bin); e_front = histo->data[i_front]; i_back = ksmodel_last_index_at_bin(histo, bin); e_back = histo->data[i_back]; } if (i_front == KS_EMPTY_BIN) { puts ("EMPTY BIN"); } else { entry_str = kshark_dump_entry(e_front); printf("%li -> %s\n", i_front, entry_str); free(entry_str); entry_str = kshark_dump_entry(e_back); printf("%li -> %s\n", i_back, entry_str); free(entry_str); } puts("}\n"); } void dump_histo(struct kshark_trace_histo *histo, const char *type, int val) { size_t bin; for (bin = 0; bin < histo->n_bins; ++bin) dump_bin(histo, bin, type, val); } int main(int argc, char **argv) { struct kshark_context *kshark_ctx; struct kshark_entry **data = NULL; struct kshark_trace_histo histo; ssize_t i, n_rows, n_tasks; bool status; int *pids; /* Create a new kshark session. */ kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx)) return 1; /* Open a trace data file produced by trace-cmd. */ if (argc > 1) status = kshark_open(kshark_ctx, argv[1]); else status = kshark_open(kshark_ctx, default_file); if (!status) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ n_rows = kshark_load_data_entries(kshark_ctx, &data); if (n_rows < 1) { kshark_free(kshark_ctx); return 1; } /* Get a list of all tasks. */ n_tasks = kshark_get_task_pids(kshark_ctx, &pids); /* Initialize the Visualization Model. */ ksmodel_init(&histo); ksmodel_set_bining(&histo, N_BINS, data[0]->ts, data[n_rows - 1]->ts); /* Fill the model with data and calculate its state. */ ksmodel_fill(&histo, data, n_rows); /* Dump the raw bins. */ dump_histo(&histo, "", 0); puts("\n...\n\n"); /* * Change the state of the model. Do 50% Zoom-In and dump only CPU 0. */ ksmodel_zoom_in(&histo, .50, -1); dump_histo(&histo, "cpu", 0); puts("\n...\n\n"); /* Shift forward by two bins and this time dump only CPU 1. */ ksmodel_shift_forward(&histo, 2); dump_histo(&histo, "cpu", 1); puts("\n...\n\n"); /* * Do 10% Zoom-Out, using the last bin as a focal point. Dump the last * Task. */ ksmodel_zoom_out(&histo, .10, N_BINS - 1); dump_histo(&histo, "task", pids[n_tasks - 1]); /* Reset (clear) the model. */ ksmodel_clear(&histo); /* Free the memory. */ for (i = 0; i < n_rows; ++i) free(data[i]); free(data); /* Close the file. */ kshark_close(kshark_ctx); /* Close the session. */ kshark_free(kshark_ctx); return 0; } trace-cmd-2.8.3/kernel-shark/examples/dataload.c000066400000000000000000000033731351617527000215210ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ // C #include #include // KernelShark #include "libkshark.h" const char *default_file = "trace.dat"; int main(int argc, char **argv) { struct kshark_context *kshark_ctx; struct kshark_entry **data = NULL; ssize_t r, n_rows, n_tasks; char *entry_str; bool status; int *pids; /* Create a new kshark session. */ kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx)) return 1; /* Open a trace data file produced by trace-cmd. */ if (argc > 1) status = kshark_open(kshark_ctx, argv[1]); else status = kshark_open(kshark_ctx, default_file); if (!status) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ n_rows = kshark_load_data_entries(kshark_ctx, &data); if (n_rows < 1) { kshark_free(kshark_ctx); return 1; } /* Print to the screen the list of all tasks. */ n_tasks = kshark_get_task_pids(kshark_ctx, &pids); for (r = 0; r < n_tasks; ++r) { const char *task_str = tep_data_comm_from_pid(kshark_ctx->pevent, pids[r]); printf("task: %s-%i\n", task_str, pids[r]); } free(pids); puts("\n\n"); /* Print to the screen the first 10 entries. */ for (r = 0; r < 10; ++r) { entry_str = kshark_dump_entry(data[r]); puts(entry_str); free(entry_str); } puts("\n...\n"); /* Print the last 10 entries. */ for (r = n_rows - 10; r < n_rows; ++r) { entry_str = kshark_dump_entry(data[r]); puts(entry_str); free(entry_str); } /* Free the memory. */ for (r = 0; r < n_rows; ++r) free(data[r]); free(data); /* Close the file. */ kshark_close(kshark_ctx); /* Close the session. */ kshark_free(kshark_ctx); return 0; } trace-cmd-2.8.3/kernel-shark/examples/dataplot.cpp000066400000000000000000000116371351617527000221220ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ // C #include // C++ #include #include // OpenGL #include // KernelShark #include "libkshark.h" #include "KsPlotTools.hpp" using namespace std; #define GRAPH_HEIGHT 40 // width of the graph in pixels #define GRAPH_H_MARGIN 50 // size of the white space surrounding the graph #define WINDOW_WIDTH 800 // width of the screen window in pixels #define WINDOW_HEIGHT 480 // height of the scrren window in pixels #define default_file (char*)"trace.dat" struct kshark_trace_histo histo; vector graphs; void usage(const char *prog) { cout << "Usage: " << prog << endl; cout << " -h Display this help message\n"; cout << " -s Draw shapes. This demonstrates how to draw simple " << "geom. shapes.\n"; cout << " -i Input file and draw animated graphs.\n"; cout << " No args. Import " << default_file << "and draw animated graphs.\n"; } /* An example function drawing something. */ void drawShapes() { /* Clear the screen. */ glClear(GL_COLOR_BUFFER_BIT); KsPlot::Triangle t; KsPlot::Point a(200, 100), b(200, 300), c(400, 100); t.setPoint(0, a); t.setPoint(1, b); t.setPoint(2, c); /* Set RGB color. */ t._color = {100, 200, 50}; t.draw(); KsPlot::Rectangle r; KsPlot::Point d(400, 200), e(400, 300), f(500, 300), g(500, 200); r.setPoint(0, d); r.setPoint(1, e); r.setPoint(2, f); r.setPoint(3, g); /* Set RGB color. */ r._color = {150, 50, 250}; r._size = 3; /* Do not fiil the rectangle. Draw the contour only. */ r.setFill(false); r.draw(); glFlush(); } /* An example function demonstrating Zoom In and Zoom Out. */ void play() { KsPlot::ColorTable taskColors = KsPlot::getTaskColorTable(); KsPlot::ColorTable cpuColors = KsPlot::getCPUColorTable(); vector::iterator it; vector CPUs, Tasks; bool zoomIn(true); int base; size_t i; CPUs = {3, 4, 6}; Tasks = {}; // Add valid pids here, if you want task plots. auto lamAddGraph = [&] (KsPlot::Graph *g) { /* Set the dimensions of the Graph. */ g->setHeight(GRAPH_HEIGHT); g->setHMargin(GRAPH_H_MARGIN); /* * Set the Y coordinate of the Graph's base. * Remember that the "Y" coordinate is inverted. */ base = 1.7 * GRAPH_HEIGHT * (i + 1); g->setBase(base); /* Add the Graph. */ graphs.push_back(g); }; for (i = 0; i < CPUs.size(); ++i) lamAddGraph(new KsPlot::Graph(&histo, &taskColors, &taskColors)); for (;i < CPUs.size() + Tasks.size(); ++i) lamAddGraph(new KsPlot::Graph(&histo, &taskColors, &cpuColors)); for (i = 1; i < 1000; ++i) { it = graphs.begin(); for (int const &cpu: CPUs) (*it++)->fillCPUGraph(cpu); for (int const &pid: Tasks) (*it++)->fillTaskGraph(pid); /* Clear the screen. */ glClear(GL_COLOR_BUFFER_BIT); /* Draw all graphs. */ for (auto &g: graphs) g->draw(); glFlush(); if (!(i % 250)) zoomIn = !zoomIn; if (zoomIn) ksmodel_zoom_in(&histo, .01, -1); else ksmodel_zoom_out(&histo, .01, -1); } } int main(int argc, char **argv) { struct kshark_context *kshark_ctx(nullptr); struct kshark_entry **data(nullptr); static char *input_file(nullptr); bool status, shapes(false); size_t r, nRows; int c, nBins; while ((c = getopt(argc, argv, "hsi:")) != -1) { switch(c) { case 'h': usage(argv[0]); return 1; case 'i': input_file = optarg; break; case 's': shapes = true; default: break; } } auto lamDraw = [&] (void (*func)(void)) { /* Initialize OpenGL/Glut. */ glutInit(&argc, argv); ksplot_make_scene(WINDOW_WIDTH, WINDOW_HEIGHT); ksplot_init_opengl(1); /* Display something. */ glutDisplayFunc(func); glutMainLoop(); }; if (shapes) { /* Draw simple shapes. */ lamDraw(drawShapes); return 0; } /* Create a new kshark session. */ if (!kshark_instance(&kshark_ctx)) return 1; /* Open a trace data file produced by trace-cmd. */ if (!input_file) input_file = default_file; status = kshark_open(kshark_ctx, input_file); if (!status) { kshark_free(kshark_ctx); usage(argv[0]); cerr << "\nFailed to open file " << input_file << endl; return 1; } /* Load the content of the file into an array of entries. */ nRows = kshark_load_data_entries(kshark_ctx, &data); /* Initialize the Visualization Model. */ ksmodel_init(&histo); nBins = WINDOW_WIDTH - 2 * GRAPH_HEIGHT; ksmodel_set_bining(&histo, nBins, data[0]->ts, data[nRows - 1]->ts); /* Fill the model with data and calculate its state. */ ksmodel_fill(&histo, data, nRows); /* Play animated Graph. */ lamDraw(play); /* Free the memory. */ for (auto &g: graphs) delete g; for (r = 0; r < nRows; ++r) free(data[r]); free(data); /* Reset (clear) the model. */ ksmodel_clear(&histo); /* Close the file. */ kshark_close(kshark_ctx); /* Close the session. */ kshark_free(kshark_ctx); return 0; } trace-cmd-2.8.3/kernel-shark/examples/widgetdemo.cpp000066400000000000000000000060211351617527000224310ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ // C #include #include #include // C++ #include // Qt #include // KernelShark #include "KsUtils.hpp" #include "KsWidgetsLib.hpp" #define default_input_file (char*)"trace.dat" static char *input_file = nullptr; using namespace std; void usage(const char *prog) { cout << "Usage: " << prog << endl << " -h Display this help message\n" << " -v Display version and exit\n" << " -i input_file, default is " << default_input_file << endl << " -p register plugin, use plugin name, absolute or relative path\n" << " -u unregister plugin, use plugin name or absolute path\n"; } struct TaskPrint : public QObject { tep_handle *_pevent; void print(QVector pids) { for (auto const &pid: pids) cout << "task: " << tep_data_comm_from_pid(_pevent, pid) << " pid: " << pid << endl; } }; int main(int argc, char **argv) { kshark_context *kshark_ctx(nullptr); QApplication a(argc, argv); KsPluginManager plugins; KsDataStore data; size_t nRows(0); int c; if (!kshark_instance(&kshark_ctx)) return 1; while ((c = getopt(argc, argv, "hvi:p:u:")) != -1) { switch(c) { case 'v': printf("kshark-gui %s\n", KS_VERSION_STRING); return 0; case 'i': input_file = optarg; break; case 'p': plugins.registerPlugin(QString(optarg)); break; case 'u': plugins.unregisterPlugin(QString(optarg)); break; case 'h': usage(argv[0]); return 0; } } if (!input_file) { struct stat st; if (stat(default_input_file, &st) == 0) input_file = default_input_file; } if (input_file) { data.loadDataFile(input_file); nRows = data.size(); } else { cerr << "No input file is provided.\n"; } cout << nRows << " entries loaded\n"; auto lamPrintPl = [&]() { kshark_plugin_list *pl; for (pl = kshark_ctx->plugins; pl; pl = pl->next) cout << pl->file << endl; }; cout << "\n\n"; lamPrintPl(); sleep(1); QVector registeredPlugins; QStringList pluginsList; pluginsList << plugins._ksPluginList << plugins._userPluginList; registeredPlugins << plugins._registeredKsPlugins << plugins._registeredUserPlugins; KsCheckBoxWidget *pluginCBD = new KsPluginCheckBoxWidget(pluginsList); pluginCBD->set(registeredPlugins); KsCheckBoxDialog *dialog1 = new KsCheckBoxDialog(pluginCBD); QObject::connect(dialog1, &KsCheckBoxDialog::apply, &plugins, &KsPluginManager::updatePlugins); dialog1->show(); a.exec(); cout << "\n\nYou selected\n"; lamPrintPl(); sleep(1); if (!nRows) return 1; KsCheckBoxWidget *tasks_cbd = new KsTasksCheckBoxWidget(data.tep(), true); tasks_cbd->setDefault(false); TaskPrint p; p._pevent = data.tep(); KsCheckBoxDialog *dialog2 = new KsCheckBoxDialog(tasks_cbd); QObject::connect(dialog2, &KsCheckBoxDialog::apply, &p, &TaskPrint::print); cout << "\n\nYou selected\n"; dialog2->show(); a.exec(); return 0; } trace-cmd-2.8.3/kernel-shark/icons/000077500000000000000000000000001351617527000170735ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/icons/ksharkicon.png000066400000000000000000002351321351617527000217430ustar00rootroot00000000000000PNG  IHDR' 7bKGD pHYs  tIME %fiTXtCommentCreated with GIMPd.e IDATxyUY{ gs ZOOHDA]HJ;E)SB%񐸂I%8ƔcJD B$_o oOcsv'9oZwmI)`<Ɨ0s<߱b5'v7G6;Õ`3Kqa47Mnݩ4cDDJ)F/$97B2cGMMqm7^ן}TW'< _CX/XyX݅ƻ}Auv‘5z>1Fю' >l޾[4;ST㠍h4ZkJ)#H-҅#'XydzeO-RHaç_m<?up{?;UeY)JuTU0_]ZS.z.ڶ(to7Hm@DS D % DHłxTsS,^tc4 HS~Y>2S2}L?NM)= |$;uy*?Xz}v_cyBMD"1'),.yTi ʣK\#BE Q!!^!Khh4)%B (fJP/"R*V׏к1{s4i Ӏߙҍ;BkgL))W^y ~ ߹Zע1oTih" JGDC۶RC"jA$ZbR4Z[LG-1F-(ĈI%Jc1Z)і&vK׎穋V"uoH< V,UUх:^{nE [*y/Q}ˉsQw|[c iCg<`RDvYVtH"tQHPT e#b=>MRGYZlYB")hkL7S m!D) )H`J UP:A\PlY4V+k OJ}P1aPSJܥ/`<XÏ~pIv1MMVD:z^Ghр"ER m 0'3Zm={T=\ wı#N=5EO$X]\GJ#1ĄE(SfZ[&ô5 RN1eBPմQ8"$N7u:aZԪ:/p !5S6G_&H;  iH QbH ,k=Qԕ%)Xx('(kcWFQ&oIc9Sd(%R@/X^?L*Q!H7fsmn`=X'%)S=]pѓpd;wi6wPЙx(a&|gCaPo 1( (+LYp'M\?K%v9g ,oscxשCq` @ $Ep$ gY9đ'NS N -ӫ;|9=ztdf!g6JB , D%B8,\g*xƻ{l]X0XZ?m׷^nM~xsyʃUrj6V7Dԏ!$ *kFLSǩG桷7|'s$mY9}# ;[Ѣfg{^x6bFGP!!Q4c|vÆ#g Y8}T+v[X1>!B)׆ tDx6$4D*-087ٺz.v"ӑ')~0"5GN(_K߼;"3[qWi~'uS|NcZ3 ɳ~kǎ0^3^ N"m 6a&vՓX;J[f缮H@pyc9֩(cc-(€ܾS8!'N`aTÒ(* "IL7 ~9ܼ·ӿ[ LImJ9Gp#Jm;'yFDבTi>_z~oDOiL4X꠱「[qJ*)g^E~eێ<|?echϝ~:Xsɨ7]to,jMDVѹ)fA?9dgSIcm&j؎iܔŶƇ^g1`(/:b/RVǤpt b=zc nt %7t?i <}O+;=LEA:#zJ!9x?Ͻ)w$и]e1z"?PA{ut6),%XoVhMTWٸ**DEcT!9OP}F[SO/z/qo]w(-ľw)D +k;HْO{OZLaA1"JI*#ʲa0B$ui""۔״F#X, 368!&%>|m=ۛ;|.@3a`I*ǜbyW$ "eؐFQc^܃.\1^w; 2,.QbQ 7|_O=X}C{?+"?XڃVd.kcڨjBM =D- GqnaUAPWKĪ ee]Pq"tz[-"[_3{PJMF'Kks#>]ZYmT򦈎lS=)3 dqШR, AME+?rٕ?E:ֿuֿ""߿0>-jfu֤l(B(!{(cBVǐ_/J m d*c09k=) -f+HQHIhvH_R92 kg$$wcZf3*{'$s/[; Z״-* b@ݯf1~@s -"ѧE4o[tL4.kWs[_-jn?iw $"†,dT+}69ͬYE"Dpsr*Ma:bmbhZk^fþU3e<8"= 2Jce*J$|tHXlmA7K ӑhbfcH4)*!C'48\tti(%E]6@p%%TBdI]"-)!<(QJe0 birJj೏ιth2h#".iw-V .<(=| PV DDBݳ?G;{=`ܻw8[|s #-"wЙEXOD! ,]8ޛnj$ܹV w DA2~L^âo_뱃sxk09u c$DL)`xͻOs? "y"[}op*ŨY|M|r7 ݼqvy.YOe染3O7usA%^l|wOsA.\z=ffs9٦%mZ$O_~ǡo?C~_m6f̨6@R 7$[$:Q=-&Kc}BQU"=DP".z5ęEq8aEC$ *Ȍ#"=y(qsab׌a>L$"̘umMfDPS񯾏CozϏ}!+>sHbL&}DkMիAu PRBYU$!Gms\Ô&B;v-QFrHk$i$GήKvfqFg Ѓ?1spϋOTd"GYT\5,,t/Za˜3!r7Щ{/h.HO{FѾkfa0fsK+PJ}Drf2ML(W4d0M Y_(Cuvo0FtDWm.L$IYVfg1v]o1=&?sf==B2фS꺦rȢdyf2m"7=ݶ?}g;׿g</k-4KmoP3p6*yRif23wYb7ۜ ){%ՠGAIBx& ۻL;U D>1s{?9uƜ?{+Ik3UUeOEbf!9`tqnIأ#RQV*u%f(;PbiG`e| )E f.v.'ΟA{ @m*Ɲ(+rބVL4Ǘ8~(`]P3˴8c z:؉¤;)DI=$uIGŒ2 K\(Q1:LaFN):e  PdsI,ZD;fB)BY T3ISϡƑ.P3Q#A7_e|πӧN@=,>~˴ڣ8 =D z'{GqV> X)8rljhAj:ЩC ңfʘ7nskn1ٝfAr狈f ! e^"0AjKO,IALjJKC蘀{u41=B(zqHL%RhC (h',)tn)fN0}nl_` MCӨ!J2 Ӭ9'X@+7-fF%Cǣp?ĉWEY"miB/hf*d(=)Zdx:%(U(J(TeO m4u.* uа3a\M-uѳGYXd5A1FNz6vz*qk$ OBJQaFWnqK챋'ЧsGY; faŇ8gV˴47xcϱw}+J :ABHQ ) W@uraѴcwĄ]zm@Ff A吥6Ħ٣4EAoeY]+b*R +ˋUtw0$%(:&$"TR5< 77HhWJa`PM"@2k :aqb/qIr)e2ia(-2H$jDJ JV"!D e)v9?賸@ݫhcC ,]aoc?;/m`:EjEJb.:҄PDaV : mZ'H(6m^|9^ L]M/Mf^d$JxEOX8H}eVfYV%a8гUUS ba=r UST!+Ey^*baes+ VX\]nw6hf8rV)G/+gVJM$P״Ȑc;#_:V:ȧ>in={ [t\0bɩ_~췿Xo?|tMo6;vn¾Ca=>CBm]rBӄXj]qǎJUl_Kh6vt*bDzEN@4˧s_} Д>7px vPbZV %eE#Aji vݲ~(qXg5Z"aݣ,+ IDATs \ٹ+ϾpW 7eSR)(H:^;.,pŞXb:3I*d ܚpHm9&[ڬۍx %fL,)F] 4 mF(Svt*+uŒۣZ3A-xӟ}#c; Yf-~Oݤ1Ō q7RFUhuBC7/ho_I%$JcCn 䃙g,)1PG1]$M4ii(&m,/& іS$j&S$F553\@(3*)pOp ŝ`s_b/;|:^UaJ&Vc %FP.&ðGvb+4s8bl(KFRk[˅"t44)wyLK :knA謧ZQ./`5BIYMFܾqCݢ] ԡƂ1*v.BG:VlhcC_B (FCO4C=[ Jt ʩ0)UjPW$9G__'?5]DR-eӪ@kM7hGmuܵ2kht"p2? .p혢.@{\t(b(i2"ҹ5LQhMQtՎpԡ&GSl^U&&ER4Qj< fڜ J*!2(si-W *bbGGc5M=pNF%B$$5IB.rp̎PCi,J)d;YqE߈@^δUP粕j߽?`j1'O!(hJ ÒdoH0쒂Bb$urhi *Vd6@o__>_=ݏ> jyo.R\𙀛P >S"LO҄r`%EvqۑG!yR?3p6ơB)ѡzdaYba}\d"L ,Vc2` k-mxBH.K#(0:xœ)4E$ih\C!7IFBj"-WY)3k Y=Dl޼vw0JQzD<ϩ UEAܼQ)OfR42M5"굀C[ @h)T3pchh '8gZ_}ْSȏ*I;:Y:y]cv7wq]\|bcJ + 7uq @KgVY>/}%""9X%8FBz8sG(18a-F7x2i}K*FHa&SF6$BgJ􏮱Yu#&;Ws6zHb҃bf$b.vTF`"tRQ W !ym[&SDazUϙu\V%qZJ!bt.5r/R_\8v4Wz˘4Sݭ-OdЦMk_EԂP9|~'){?/N)P,Y:~ŕelUP_TR"[9[b, tas#nu- V9taNR_HVRCNV%~άm?pѫ kii>:NsܚnGu͙s~9zGJṄ֢[G^Ac5AARb> O 5x_U1+\Ln:ˉX]]bӗyg9e(FڔHˤYYQ"kH\ZBi|B+C q_ s&zꠙн\S""i])^K%C4TuiDŽT%Gs.P=,Sh@&7Ɠ8SWeΊ)fKXPyOr+rRFLsnlc~k~䁻cǢ _]6g2\?D ] L}|iLF=5f&&zz c! _dS@3Qv+ j$Ԃz PH bGR_ІK);2ʵ|CG^$nUGxٯx%yHxΧ@a*wٕU@& A GYpDkן$>".u#gp)lYp[;Q*y) h$wȨ` Cia^ALr*gpl?>p[Qt'p#Oxx鯰xtso~Wn/Y !"I7k?xƑׂ >`qJMN08*KxB(kpoZkEeJsG.J͎9ee}YQ`F%p̘[N\<˩xCO32@.)MQ -^;BGAi=gX)ój&ĝKzg>t?8t(oevwXY;r1 MaSf.^,GUIAQq.|Xz ^Ї~*g9ʣ6Uc9zP~!y:?XAd2BV:ri< 0a9QhƎpu DW8rj7&,ufe6~fh8|~:%mL(J457iwvh *=ǥr.(qV a7_/ y!Fα7v~}&gΝ0-={s]U=&ERԘXb"LIC (#؉8.PHp D1 Ʌđ(Qd]CWx5biH6eU*Tg׷}kы'~ʿ[/\ŭP`-xT,GJCL֢|H5ʀJRSvYEjDpbG_ G9SOP1`" zJ$/xkԴQ<'w-]NcFA_*!;ι;\y9\__@#W>oȄGZ;&ܧcRޔB4gMk9,{㐏4l6ă`lt^ybȑ\e@1{{{vA%Zڄ쐨%R8 pP9[Z;VdHْi4E2@iLJpW%r47 ZZr/>ɳ<]^lݖ?E(,}ѠF9, J,&GbHzϹ1|Ē5%U+pQA~x_=ctJh}hYAU׼;qpt䘞T4H9@G.ua8.ӽ^rlR.S=Vt ALcKr͎H!=Z+z>:KsB;zL(E*Ae"fz=L5|^:p;FmIiEnrʲ( *~;!%bh>WNLͲ۷ 8|LL3U%2/QrnD`;5~Ig]ڄ?ejLoԿ{ ]πж-I%Ҿ$ۺp@% GvWKܐ8kO )@'$.xrF&x2!/$y @SغA5I>5@Ie SL)ZҢȐ$ "Wdz!ED枸 c@/ [5L *DwAsֶ=4iTR1Ȏ\DcQDe<3ۖ VV[td EQGr@.68*.Fj%)z9:B-Fl:9-5yAx>Cْue@EF#X&'R7誄hbt WdQdpe[pn]PMK4'.jO9A , ئŷtй!+5"Iӄt);㓰iNneoȧ]1.lH>_Gt?RIڨG0[ vęK] *mR٥J߇N.\3ʻ'w@*ǣ$20ZE]B_uq4wDğU~_?[?~)y'W<%Xo? P\ڑ@JAkAd?I_W2 ۙD*Cws+B.kR(R5Dol&5\@$232i-B *HtTQJYMw. LtJTQ$U !1w OLX2kru`kyz4 .#G̎̏*挮YUC@ӪMj-ާp:sz'$JF ߨTq!+-S"uȉ%RNrIcpv_%zm `W]g~oX@ԄBI֙μ:|'>7+ȿ^IE~٥5-b (!DuȅDiM)j&fN'iqt)wUu%!ӱCk- ~[j5*'BoWM1h?Q""ڳ<<ݛ׾\&\֧7({ȝyox&cy*c RT >@ Lj3s42Lc0A= 7#5QO" 4MwbC ǿS W~vۿ|OwsМ !NY!F2_74uM ɨfC}5RӜ"Rw_=x9nt1SEc*D܁RtsX!Ҳ֦Ɩs4HWuؤŻ^wedH pqP#tI5xA^|KDS4yx2)ӚZ"ΗԳ s$"wۃ^Mw8"`O!ω^1e۞5;k;g ^=yƫw1t+UޑŐe bEOQ-nw,\DBGT*'-E)l$#h2jJ$N *ADg-3uРE乡,2By}ӛ| Cn <9sMlN->cxyýOeP 2uWsE|AQ)A'4pܩ)jp&&"H:hn'Lp' =yݢL٥:%wp UqiF`IdTߎ;K_F9.SZi8t1$<фhafv'힫#&4 9T'B|2>s$73rh!!אXϔ|)׾ 7ކyDnAq W3]Dw~8XMs4Ml8xje')"B#?gpw>0.9빐$'U'G Soխ\N"E\jXX2=!9ǻX[+chp U /[:Z=#WWl˸~kgqR IDAT?#n\p`7PSWɖ(d ʣgj6 /8V|3gsU8x 'v3]=CWq|뉎ύ|j$+.P:]m)bHDE'p蚣I7w킸J((.$BkaNYBt|ZpӔrpWDޯ&!Z vz;gwf>^qe PUF:g< C[mU -NHN2N `Ig4Dy!%z N{'U*uFWIQ"I.՜6}vlE 1䬥^V Yd δBf$<W7ԋ%¥ս*W*]T7IE$jٝi`6ݮUEXvΠy8'sg)R87^qEWTK%˻77^y5qK *#srGF.1(޼/H0>}s/{@ ({9J;i5]e<[,|Il=;gBzѮ>əZU""HTF.0i*&S\0ihzٰ.pΣB8甫/EJQ{D% 2T2Z 4"b,hTf3̞ 5$[KWn7Zr{{Iהc"i!4r=ۜ łsshm!X+ۡsa- G}H$"hI[U6U[,(˒J"1_]w}/Tϟ_ҕmKheRJHg Z LĦ]VT%44RZi|$ )rV <(2L ]Tǝ/h Lqz$vqOmy.6kqxkn߰z+(TF/y@"a$!*6CH>QˣCpngnzاQg+K:ݑZ5RO"c?+S_[?G@? u#={cθާ=I°F):T¨+"JT/ܶ-mݠ:Z\BW^kq]>|_,*k) ȿ΍fcqu…"ڌdvd;o6y;p|$ 1#4Nm=.TxTd;I?dx2Ep4Mg5Uhn);ͲƠ2ӹiqJbK??~?Յ[ cSΟlU /rPmȋ>*3h o2#(E*2dRY:ԯb mpEAfR]t>AH'8RCc,l0A9)k!aJ(zH/aՙ-$US#e{s FώY箞ʥvkǷ5Bj*KԲ"'~=➦h dvt@5xɃ7Vx6`'(DHIs*aMbŘQ`ˏ*#<B\\K%׷%Iލ!wo?>bv 1Kr# zRn0TڥP  \"B!| ,3tPυ*IHdP %~Hb'bK,*cņ7AK2X+'̫t2SJC >-ʮუŠ1f,kn]._/1)~QM$z*O_֙cn~.8(mVԚS;'}\۳Ke&.F &RjE!Zb:5`3 k"v5DWp\Q-%T0E J)h2N$4*#M* =:E%9#yyH(#3)ssxz:# CB9@<<?&)j=I"jlo12#j~|0(tT~&/_|)o8#x%XL5(v)B -b0kn;hrJ+e7JBhJ^^)Ȟ s|Vod.,%yIPـ)*Z"6&hM:`-Oz"R%sQTJ;lEQ VrwSDuqF[d/&@D_Crpp}BD0t?wOOm]Ng}ɐr5u]c]%Upʢ ٰ G=bwpRS--!E?GoQ"5RK9ߺ3x7`6W&,sLQk׶陒ݢzOnJuNpJ"xp! dfǤ7kWX;`zkf@A>qY{7WoV@1gcʼnbcXqiñ=8$"7:䭯pdztB[rHoX"Z"id;ޱ1DooˊKyt<¢r؎,D u]Gvͥ:MGsL0u>X ̨`FT =֘{)B d ZD&^P/{>7ٹr\zssqvQHK>sl_&1z{=$,WHs剞 췲p|ŧ_ǿߜZ,Pb52| ! C+nL[7鱌Mwhby#Vs%VƤT!bgDyeBqe6?z,pxd.dfz#QdR#TJj"D$ $Ohr}pZh#un?wMтr|Q]4 ꕠ&,zJ/9w<{`q4*0嗞8.o5x:Is&Txq'f|R˅{|ʗ*<[ 23YFn4Z$Rw.ƩV25ewSg5.,Qd6ykW`e ySZ)G_iV1`3C. Pl^q9 dhe0`zkh:iڥD.SLyr-A`s=Ȭ#r*3 O.1ޢ{B^ ڀeB_i|]}o7b~;:x|a_ eL3Z_cm{W@SWJ`>&W^Ey~т)v\K\熸<`sɒ踦F#ȨXRȸZ}Mo?"[  Q@e2KOsSQ>"p[8娠z|ȣW^zWq,d' @ mgX"kWv@I6&chZ滇y6 ㆑-(A>chP>x͊jqñ8e 2k :hZµ̖ q2/5QuyNe(Z|ιn$c xkާUW^ +\xvMDaV2 |l;@5`5˃/8{;:{7s.m7tCAZ&W\u k2c* !aǽWoGm$V]lr;V*?wyAߒ-*o|D 9--s5D("JdOEyjtLjP U@FMU\Xʊ6Okjʰ ,4Ufu!}VJ3C[uMF{\ P=n5EӤ>ɒvٸDDf#LR:n 5Apro0!!ԆjZ64+XmvG /gzczYI[`mn<`sC AWHlETQQ,a}.|.NS{b.s8qY@(0EJ&Og鴾B^!Q.ZoZ2E{]y@_UyK;kA0(FVLXl}ZM,GyN,q9́d2ж-{!h рgύ5ח 5ww#:GaX3XqbLfHdˀwRU* i$sP9|Ӛk+k$-E-WCngߙc8$F}$6&\5jɠ();29fuJ\6G6aFЬl"I[UDdXMX8q޸gx=T I *(M UI E4DOwd_-cIT>(֨egN*sMUUQS6LI`.1*d p>bI3m| l6< =|KQ, q -F(x%"Uj4yZCRTRoL )b})CH >nO}`/+D2Ss*} #tvW!zT|9=1޲XZZ2zMI!5Ͳ&((tk,l=xDz)%MSN;YΗ '},K^ZYf )z9:SyM&NBLn(kQޣ}o$p*U/ܝK]n:KA"S([)MЀ.j)iA Yg$˄~QD~I.jЪtnv2%rR[RxH*хBi)2iDiNk. dNc&C1EԞ "]W*F*Yj`\U3Ac#t3FaɔB64Q&/p!YZ'AŖoyZQI^8ɵSQ#oW=' M娲CK$DM Aju6KyvsB4I44QA$ 5 =A{l) |`kH0'xfLmux*/˒pue#YRbȴȲҵmDT@fOи"UU1 ֚ xĐA%@)&62)&-@u%3DpRIT(DL)1hU&ku<&8 LEZnN~[Z6WeeBVR!H"N%*2Bv,Y(zh5mD@hls<=b32 KFE ī'ʦoő yCǸ2|9- 1rIdHء8e h*g;:DqQH2.0ĵ4nT<* V訄Z\@UZǑ|d_<{rm˫_x͚{w$9$X[E9&SLzNz+8g+>iR2佮iv*&Q:c*S~>@n`ɲʰBUp>CɄMUƒh82 b C^κTT'[HױC@ CF%J״(%ȄisH_8(=9YԨ 0r|5)[G]s /k|ږڡ$“9FD +cu>Oi mo 9z|#{L ց09L.S5tp]5t _[sqI8.Sy1%32,h-DӸt/nv~u{GQg\:n΅$kŤ1Q*cܪ ٗyKd»z^C#9!7$޵,5)%EFp @4$t!:敀 - Ao2,v F4 z!]3d9IQ9w5.B$lC WJ bHAZ'mx٤' YF:^V9HbU&*.tUZ%q"5])qed@4bcf ~ZZ">"@*Op-ށiҠ CW~OOok룟'GEP1`|ш:@DODv+GruU˗Ʉw1o9 /L!="ʎ=~UShRz+ /0\(Lz:.=&*5V3bP8eQ"g9_~a Mhog=F׶3\vM / ]br~voW/ph!'LkgqbDi{DI X-zϨ4&j D@AԔ qd"P54lp>Q֟:7or|xD]/ټ| /<+L.n]ȐuHS:(RMk`xe/4 Ho<`rask,ʒ7vӈs-JL5R&0whHrTeu6_ŗ13 &tOP <aI|m[Tq~Ob̏\ox’C& ީ8<*dHdpa\Һ9X&CA >j m)Vc1'Ew Βx1ڀǭ]n3J_};8xp.хu.\psJ ㌽;}.QA#҃$dZkr4tb1#@c41^_#؍wq;]C+4P k$h4%)#F9 ֆLt(C1SWXC>DdGܥ=0Z2rKAަ3/} 3|V-\@ȐĎ֐6WԄ!X6.'^3WXMr Z4 ˌd/Jr#-;Y'KF{E[W/w~1mF}d(D} ar':L'ˮ[,Du$ڦA+AhD˜ ׶/S\YÌKY\GSiOtO݌Le(YЦwq K3Jɲ8z߯KX<LdYik.ؚ8 IDATŘ@VmX[v3D)Ժ8F1-Zu u,E]y0+ +\T3n:}M⻇/mx?~*[ZRğt6F:0+ %d AD :G%]f%sܻy"w#gwؘldk_=DPPQ@E=\Fkmpm>-',G?d6{UΫԉ r*%@oPRfa+(c%.4>r 1ڊtF1*)hi בt #fet2շUDJz_x#+@%Ӆ5_y׾dRڡDmYS ""rc 8"EtѮM-ܴ="Q Xe/2ވ̔Àiνxm*[Sed_ޣ1X-`Rt1:6)3bmDٔ yUl멦sTёDW"5%VieJLhO=hI6dtqsLa;ːKֵԾ6`<ї/o, kՓ,;s>_77`2Ɠ`2]dVDÒa9A "EM /z!V68bJ o6#%hpmOuސ)s)L`! L 9^P1L)@HR(Zp22 "HM.EQ tPATiLZ&:΍m RPdq<:`lyI1*J]+Gu^,q5A\xjڃivĶ!G樝zg?55M/_ :d.!AT AYlɔ#= i2/46WGL 1eDiق &'?sDa#B{.x5NX|fYz>+Os7H0iDEJ4MmUG.j<̔mIHI 4ηo:a9vIuQ}{޿K0閠 eB{ T{PRf ,FvrOP{ }KOc9aHEt€39p :lsg7/|;1&}p`5bi}&au(46'"1C xlt?8"k~f8.m1f,z]IMH/)$J$5*^$LգmNeʼnznuQZQ`h`W#چ VȺb _~ qaBC{o#oqhmIsLhμ[jcˉDDd6v"Y.cppf4b0^!\/71{->[5[M .jRch=ހ3uozT`"pdhQ;#g#Ց 4-S4 q/ȅRY)g7нBtvSFfÀYiJ r+#a Lo-VXhk-ʚ|?x}FCz֙'.ՌGwܺo0!`̒$=!$Jh&! Y3$/Qdȥ`!datQbe_ɲ\D@d!)ob:0(9X.X'IRWDTJGrq-Gzvu]OpkHqLl!M%](zZpeqe5. הVBM`]iv>W45RG}J45[;H~`~t0 (?Қ=H7_5i/ <(%DuY'(}& %P {s:*A4AQ⫫]hI#Opx&Rj&Z,+#X#CcV|zW^S@CdldHpAh5SxDZ9t܇!eSt"Z㕫+qBu)B:N#{sCma4ajlQz|Z #u:oVF+œi !A>DLlhh Y/hChC+UVta7檭$RFȜO1dItv$P` Ne)FStSe~aX[k> CѶ-ucÑ̳|8Km-B!^x1~O+K=)5rN+#+hDep)0aȪ| @x!VӰ.]9F}QGR5z D apZ VJӒ1 ,'h&GM >Zbb6_F#ƈU[<ōWH~{o7zΛ}[Skئ:OSh >\!ETᆬr=HTmYI.@k% :'p"U6ҥD aph;R...ϗ3٘d ibyd\0-!8tNANJPV%ȗD)(|N1ɇm!fXYTT*ᤒX Y|V"D&L66.1~y !xWf\U"a'#2n5.>< %QJ롼fH ;jR(k/e)l)K s"3e2Y,+/[J-OwRbT !`d)bF]g.{_2|K1>]~]xnW_9+wi4*Q@ʈHy@Xj<$42iRY~Fd%!#R9$DCJ)DV.(R(P% RX_%VFHDߒ }ģ"‹X|qJ0ɉla#=r0 ªs Dɗo1&ay`rR2-YKH"DNAʭEAJJ%TMϯB𧕅z^">e\4'UZ3T 84>̯3,?_͟?V_')EyhNUS#$9=1ろ1[R+olkdٳJIT9(0!ڲrθқ$0(EOR|\uB}o~ᘳ+CwOKxx ?t(c::bWg8. JXTJX_+i4ѻ<ЊĐL'bAIe %I;D#$D;HԠ*MdU 숗VIْ,Pgp%^݅G N{bUpLV6' eLVl$W2+Igϭ$DHx?O B2(%x0 wp㿺\o7_q?R g_R;|< )U~QeLWf5 dТaMgzբ>u5@wst«XlJ'{e6x U`!_~p3s[o{^yIʏ,onudcQ&ggk(R(Q:e WwPBLR(qqZHg$:nrt._o3cF Nl'y^"$L*5"ʵX$z.|o|Z?(~Hɨ0҉/lf灾{ecrάl=V\%B>ښUXmʀx](޴D2*˥IkE$0 X "0321VD(OIU%Y.ga1=I E3էOĵEbX-gPQv D^NIdĎ\{f̵"9JS+kOn/^|̙Ϳ#׹}w[~wK^ќ1,%kzJSCLKaAR  Ri@DA!&RZ.CSӐDRYL;)'hD&"KY !R( x BX2Fл\Vk-Ir/s~5V$PBt}E6r+'THThz/LS#Nj1̔$OVeIEHx{y)t&H1ATheVH| T>ۮ)C4T9)N>!ا=Ls)N NyׯE3o\叾ux?~[|xHSKz)~ $dT9B*OYS$z4vQgA!B)+7y5,ZGU$x% BȇV/I D`IeW*K& jݲ^ ~js?*[>s߽؇r>8O\*T?G^_s.o_6֯ףȃ)JD[ϱhIB0+Z}LH!Wn2%|A(!We~m3\%g!Dy¯ Ìd r!rfs788xkoxc~c+Q$gLY \ ">Ʉ~6&IJ@\70yJJ$ѯH 7C,.~Hmel yRrN{ )L(3@㇀ *2IfPy`' vNKK2t6?aYJ)ՐUQZHA^EI!5Ze' ֖(Bn5*o=<,kn#s 4][ɨe$3q>8 n\},HZb樦 ?Х?}sj \ -1%ݒ#}^3$+3߯y&ݠxy3r2ݐ!gp"@XKҚ!,ݜ!, DkRA ĀeQ8,֛("%A8B$Ed}1㊝`9Ry.B&Ʉ{Sg=e_Cۭ-CU"9TxDeX ZN@G _Wݶ.y3z/YGW Og\%rQ dRZ2Nޓv>(!YkDvB[, |-_,SW*6+N@5 e B\Kt}]ˉML}=&Ue S6֬ݽzcH<if|ጟ;pښQF#D$,#V% )"zu >f QPyy-"t"MR ĘetS% -)-I."eL )h YUʘLPH-H"ӽq ,gse},Ȏ$Ƙ2t>q5+YÁk6U)m]?apt]mF\DtnٞT RP<0k ܯAN-޹\*)I4Y~=zXJ$ÜdzM EK 4)|"B`]r<ڽ-":K஖̆Oiٱߋ쥁knf< $, څGˆb4C§:I xSGf rȓ"`<ր?$V0Wyd IˡYTyX>AшoE>0O9}Ng܇q0KGe_*pיy4ٯ:oO 'a.vd]u\I.$>W~rj-rMIyAo rbAH߄=<Ҳz*(Et).xK]S=k+#{@6V? oFi턑5;cir8% fSipibCYA~:K,j[JcPH&id.;OS Ό$|PhtvVWPRxA&TAH etydBr[apjBJ3=a; qW2}Ȓ]O%$Z)}DJ"s.Ã3p /=*>y xi=ȘrAWUo)S>b̦uaÃyTzm=Moٯ}^?c|ɞ?JxZ_nyĭO+)*J̀P r ,2$ )bLůKEVG:T)Kd>+^nV$H?hFÅ #8B(OMeJ3o]\Ȟ"J2QS_6ulEg}iJTIy&$ !5&p}0kw46z玐aˤe(+ Qa+F^PE|&gs͕'gzqh)8?PF-;cϟ}k?tסwPl% GKdŤ :E:bP*uDof~bSgUh|^V(%p69D ;xzmG?yKX!EgF&*9u>k D`qp Qb2F|0afTc c8Lcfcc C'*b.ٍْۨ&bk"Yhk`y2V#\y& tFAUUΥ]RןlTc!#w(%߹O54f;Ϝa bD !2\(r-VBKYR$XZF=|9ftu{n`cj1#h[C,}?`%YOzTRJa5T[SZQièY5_FҜߡޝR5#cǨS}f]JWAEBbrzؐ&: IDATyD7Zkb<{Eמꑽk=?{-~:pE*P"h+6)yH?g6ATqȻ̽ DZJTb$1Jne戧o1ٽYFf˲yI`rn̠:#Guy#7 [Fd$cv *2ژubJsR4(;ɵڧJ2|QUDsv418!&)֔D`qpDwD\b>1r 9w?t4&i=3e|ee/Kn߹'BJkA[.rwr|.mߣF]bCC/*Мd#oHA[&81A9G}cΎrZkMå3 ۆo</݂͜[7+JgTD-D ER=|_N'쭂3]Ҫw4Hi.H*"eBJ,/)Ţ)AptwcIĹjc=8~[|c\f˗o G͹9G|]Iwu"bP>zԉaXKZ? f1e2`9G۶t)@c1fz‘!bZ,.1Y!A$Ikv>thJmzj9uhSDŽ#fxQVD)̹GnE,vwy'غ3Ó^̿w& H MhI $#p2 E"e$fIƐ Ķgcc6fb6j~iv4}kos{ $yIȾ;)y$?lLG#9hUL.p^DHS4ĩW<8CSeQZ9wr[¸H/>Fbh;x@Q'ecg ;11.z91L|=M_^z>f)ʁ'5HEBN1cSJgQ<5C Hkbswz;EgK g) ;!za_>3?8;ٿ~G&vL} =t@lm00҆o^eP[z" )u#g-âz""R*{h=6c^xk/P9tݚrtpĭ72]2#\Jd SI@Դwg鏳_ηhD])ڽc)%IbwR:ˌSY6 L.r/\Bo)T0e{w3rī#;d\'Rtc'*$؂?WF i^HD+{BY`S!ݩL}H=c$ &Vϻ?|˖ͫ繸smrY\|:{ϭk׹޽ǴטB. PF w kOI@YCuvdv0sރ_!亳?ZJGK 10{w?fv} ]{Hj4bc<Χ!=22I`ȸ2U+H{ .屯Hs:h~obJe`3ԌE(E(8Bt/ػv2^Bln*тru, ̢0V|zLX../n6gwRDd\ݜqx.iHV4x2?)Ĭ۳p7je ^n=,8{uW~u6Ȫ<ꉡGƁIcG# %%>-1!R̠!g2@?m);()Rή1I!fK>*ЩBZ^`I}&?w]v7wO{rÏww16IOW|txH6bҦRM !"?;?&5 DYGf(9Iy,K;ӹpߐ0r(|׈Vi"B$EE Bg_ X"ɺ4G"T|cg*bB EwIL灑L ~1wtsY!bb`ySKȌp@h"዇P2f]gSHeס'ˌim^n 0"NfB*IhC^'3fg\I$#ik euH:WDZ 38< 4BnrD-is|ѣZlI##Q!uRGJ*uiciJ+rVfzĴjXdzCd%.51I QM 2Yz$;ecsA5ьF+[ T ]>楤*yp1 $d?Hl(Ո2BEꠏa??ޚssO1аـ=޵D?`ƚ !k E*iY4)FhZR36Y /=A*blo#FOKθtDžC臈7zt2z*' .PKzవ!'lH&Cv"P`vhBQTi@VhE]lҐj)CTcupvcK3A*, \HDdkF$,TF`*5_4_l9}-~ F1hQcv3jDk|h!(0&1{Iuy^Ġ~CRʼn>fEL+D  fD Ɉ#Zjkq]A7Q;ߝpk ﴴCb1b1_@vBA((a%**!jg+F'A5dh"Ret"mQ'llT9*iB,bćV-D\$Ze2"F"cɝw0=PH:%4Ra̖Q=& a?d16` `H:pfNzcH%34x)'M&\UY}}ZSB@sK<IBLҗBeD$cfLw71xcqKOr3t]*%IRdrwEÇufՈ" &#zLuEZ╆>':xCqw1˜6>K&C 4hV=! $4ʻXYJ #RQ]M?4% d*"lG"B`XT#%jXGGgH_:'޸*+\f򜸞4tTP5 2\*BPɢAوM*)|Xt3P9Vcz1&zUҹ/X@H5ಘ/!ϳ }s6F[39*kdo)0(Mf 2[dDD =,ÀK=hFfs}xz|D:ljFVta,Hh/@i/ԕ]QtWxքkf-{|ϑ_`Ȇ`BJ2˫]y꜇myoD$5>xߛ=xK^{M]̎1tszD%n60Ktvs 2YXt,KD (S0!S6Қg:@I" h-d s@횫O=Z`UglM<I$ CW0 S627><T"ƬSef8 ͖$P % ò?hZ7u=*iTq"KCNK)aP [ "CCѭկa)Q)xH'h?Aw*z*g%ʭ VXW3NhY?WDfKzʚ ]Hۖ^ҋOp.[5QڐayqwG1@4('T#%D4$h\77y㵖qF`*3,"!D = RTH*%y?BE5^T $굆rc.ENbupG\UEi|yE[b r=2!VȠ-as ?xe/!J|#\{7;Hd2-3[' + j8# A0sx-7~,VVY.h]KeH<+p$z{nLv6pD1bK`j]Q! FB)}u*B$KfZtd5^||ǿp-T 4[cvsP#xn3*êL^[^\9Iך" Da6-_o1x~X?DÙqǹ'L#ígΡά$#e0 cIgxKճ=~z~^}5S z`хjA% . H(*r.ǹT0,[Or=C#lI* y'lܜ@kW"aMYV0JYd!!U;J3=f|U&U*QSsfG쓞u]9ޚr>yzw SMz#C N( /v23dعzz+?Az{M?x~"׵ RKd !dR57y[6v8d{[{0 Ξgg{s;T{ qmK- 6Jzq`dd3~fK|+_}j=A|>y-73 5?" _F-ڜ%~)nUIY)A꼫Zy"_u"19Dq !AsV)hF=Giy~,݇8Kow8x:rpHQJ+_ &R_;8?O.ȡ}+17qL?njjز{7yW܌g_asԖO\Wx{|ec<=_m8RPlY sh`ѱ*H688Y.4k$:5TS-Ǒ1I2EѺKs#^ꄐ|87zlؙnRHqgAGʀI9-I,ĽWeYz,qdDzo*uW7d aF# /_!B\.F4]lS]]ˤ&"[FDdV5!  Yg}E^!EamTϰFH91?x]HlU XCAiˌCJc "RYȣ͂GdpH8KAC N\#oxah.爨dwZWHx|j >ax"DF#e5n1+a`j9<,~I#hE%[݀;stIЂo3ztpbd ǬRB·^RmCZUBPZQCeN]̀\X/Qle+)iI󂦖hcIoA`@>p aKrTab^5BɓDi,rNq4\:/~9_|~Ã]¦g#^5 9WzCocQXk~e^#[}]ծ>.(]% IDATf6XZ}l*jz/^8)+vzex=x CۘꒆJcxWVok8=X[ )+#j"N*I6q%tNbVW"^x+o%\Qt"}ɜБiI`U8<(|->C(ktTuŚc &.A9EJ3)7NG<+lIܺ`ieqƼ\xiSVċ?{w5RϘ]RMGxׅԫ nT(v1wDèޚJ9w@h"+P FRmOxm6xdh0UeM|iCsVFi̧9j!%ZKd~/~ZԿ htۄIP ٬6,ǺkqFeĮM$6ՈvƔL@$W7ٷnj`c)gˏxZ1ܗt&Bx`lu#%,5H'PPxWupۻբB/vg\Ǣ d S|Yh%=#]ɵ74B˒(r5nHB[g1^Gx / rx8pNQڄjQ ˜i(Ւ8s#Y֗rW +M:4&l1.ƄF3exqrǿ@V ]Gx` I\%qh6QYB@hTv~!`A@x_!'Wh!ipgOqQ&$!86i[34څ jx*TR$ꗷkXw['iVCΚzTO3URLZ &=:֤&yXюd^78K9p@e חYodc UUrw]Ãu/Sv ɨoB\-^qe(fڴ ꡑ6:8@E6\UwUY_OaHR՟,"w;Sw[/\W$Ʌh_;!|=':P &mRO2uVԨ#(\'g%EirBiD1ltiVk AɳrNPl$ !$N7 !(H9T,2H )R8^hP^G8cD#/U Fc66 ٺf5f,B!Ɩ΅xX' Rm~pcIA!崊V` gmMGy'=rbgPz7(Ÿ{|j>|JE&JD2CJN 0f䐢,ylUJ^uq,1N)U?RH !P$VD`}uz@/u'J?l*!wܣGPW ej8z /^JN/NtOhlk;f|ٟg\xAҞQ INO9s}uph)f5=*FF7v5b>ZօToBo85pRWG}#i7/-:ߋRBx!YWwqQTum:p+ txkv_}Qoc.@BPyU ~+8"{, _K4% JThLn@|Z@,ң%끥9l E@>.-?)ܾ'X.h3SeEkyNО hcFHbiN]MyWVPj P0,HtP݃~Jƹ31oiy~Ms>Q{?>Qgq5}sǽz$F'̹r.v }P %RQnQprاjF|e>u\CZwZ=eY%05`-Gw֡}@C렪AWn}9]Yv= } -Up!xJ)7k>8UWՄK;*_TqQl;iL24CW 7y-:RЧ'&1ӏw+~ 7aqέێ+{C]b>Z64͸ڈl{ȋ9<}a2LQUiR&1$diY*XZ 5RҘeOW _>W z=--._7_krJD.&=w`vvcbע#tLf#iŶ*w)'fd)[V%܌8GK^۳S 򬪍2's݊!krJaY0,/yڱ' r4sC_+'hRHJ 3-EZ:h]ӻXȟTqœl=LF HRAmX;8{q38z3臨pN"C[OwÊx3 LE%E` ӑ'U*;o.k. h60KvLPDWu#1\)+!eUsm]DRRQ%rG9nZ:j2_Pf̣#_#\ȌsPf9޺_Wr Zhյ6o͋k^m?^NGr5I/ܹǝ3?.fa Rc(9:(ݘ{_#Οt uE=1Z5uJGpb6_Ÿ71,e` =dv'^Β9)wDbf8#9֖lG Ӏ'O\qb3 2x @'@)iSg/ FHU%#M=VFo)B Hgg <ߚOZu<8hÒc[Y4;;8Bᘍf:f8Hg%m ^ ¾<<[,LЋ)T/qCe㫛ү ֎G;]HG w݆w쾑s3N(4THeQ:X"8 +"ëoķH9Z tCБCi WT`;OoVҌg;&KQM< .\k_AP##sD鐕 EoL%qV]!*8+KX$&h֧uz{<߳|锟}~b{'`_!LPۓC3(Dhtxtv7`emwKm>:x# FHc$kUU'S5NZz0+ꪶ:\+rwqRڂbdT9t rv^ XYOw)՝4 8t I,_c /[A=fh=˓nܻ'(0*t+Kjダd8)dc;皋CW\Zn(wأb0 dZ 6/ H>Z=5 ->}vW2mŗwHu"3i֚C_+\;okEZŞhHY[+Ob_Tܻ-xxÌM9ɳZ #  QdI;{G3(:<3 ˇ8'OKKVO,qI:`@_+HS`6)&LG `[==Z^:Y]T1v{Ŕ9{)Wo9g0|Cɟ[vY2&N,),ᭊ!.^8}~ڰit IÓĒƙ-8+YV0?2ڽ#=sրϻ20ER: agZɛo'v~Y %+(w 9kN75rv)dy_}7Ō4ytb{0z*M;?c\,УϫpW[J3;HiΕtQVPy*£xReh>^,Z镘o_6eJTTEPێ|.Oŭ;ƒ{'4*"SbmJMA08{8yl95S&f(8,(J`RЌ9gMX06bgs5H'E2 gt l>g *cEbI#kؘN/[n=w*._IeU*~)8,℞8@)sv ;{; Eghu< MqhѪD.<̳7jfSF;37'l=g]v0^`DRb7;!_ 0=*pٔa fo'p(rMF"(+G{guDt$8LY(IԓK/[??ڝ Ƒ[k|2hXI@%tbh0^ADUp?EI>[oKng"&/2 >?{OmDi~A~1rv*67⥀ I-ɦg#hvlZt(F>y.3JE 512J(h0@GM(%"b !TļdKt=!/}h/Ⴊ(܌tI~s 7TV(p7qUtgmzdnF.^=kk Zm'ϜLJI/~>}go*1Nb`ʤ^2.4.]UNJsGQdYFcgx$ݕiEA`2f6b#5ȈšW֪ZI X唶B&?pw뫇v0[\Q!8!:!CS(b$o xᅯ}9%$Nt@7Hqfz0TL[c9jDs͂Lx`& a< Gq ǣg;{cP8Zc~A IDAT%k#xU{G㐩5V4hs8〥/ixnWsOQZ<^aqLPCIoXTPg2x[1:FU]cyRuET)IjNv:mO . ~)oBYբJ\Y6 5CԂF9&+CS "d"CGU:pıwX16 $DDH¸Dn(;o*7ފ8ݏ"&VlEL\575Itw/ SYhOݐ]pp`*9,YA~wQaŕ^;z%)8T(&*e mEdILS/_GqeDp87 h/xjIҁ祃cO_wǥ}O=gncXT-&G4"Zv:D4?E>xlLfXwq1{rJVT( 7Ȳ+02%%'OiΝU;r\ʅ '6vO ډe|'Sg@ߢ<[ 8=u nrܽ[1fBO C-ĕ`%KP%NIz,&/eU2D+i186h/ݸ`)&̧$8Mwŋ1䀏~O;JL5H#M6}J Li2{)$e0MT'TfSQXSsJ.]Q\t%KNy}Gw߹:^K9sO?sΌgL,z sA. }0$fվ ugPi(A3: ah2à*b^ RJf)C>y{|^$vTK!rO~vӯ_zo?'xmmCEQ(% Б>^G&!-J4BPK̲C^<E-9=`5T` 1VP#E:p2\r Οq$H &[%{$W6z4$i@9p$\dV{ď-y%BmZld1 a]GHi u]!,(ıDkPi!N;oIv3 uli(}ų9ɘuDž8լks:4D[Z+ޣ;s S*U u05n{AU(xmP@'V8+x^{#!^ '̢*L;T&y扂V >{O[{~fu-@j.IE_1̲kbL3;;Gn (SsdӔu:6|4dY\٠Z:0+6ڊ9Y%9v*6ZH`.qXQimx"pd_dӆ,( 8@'زb);O($hҰTq-˲.#gZ%D.R4&篅K3ឭGVJRQ8մZNKi4hEu++'=8MzH +^pri?9`,9ew/r-:#ml/F]X>K+3^ó'Ox`k+b{G080L&pmN dQ9v$-A'h4Rʙù '.@b T;/a Z(RЈbKV,K2Nnp/̙Ut]<r"ЏLfc؝ٛ޳7x%1KJӜcg L2.f6~NcDV? 88*e$钧߯r"f_y<,Sƭy4qA"``g*l0R0P, /,H[wvi k`J=V9IBY[4f3j.)+UAN9|59E9$h_)ْ9#KYj'i:KNWfBtڎaumPo0-g n >PCYNP~FIӘ_L[Y>O틂ׇlq{-`a{9ZTā#ѬmHO鋊 7bΜXImIp|dGO>Vd/ *P+*ZMKEdu_~o[\ NI]bM5iDQxʪFhGwIE$JXG7鱶zeCnN;uMlV+csko+AGtxtR2, LHC1Iڀ*x|L+Mrp18JY:g>uބNz4kj$7cu66G?{?h 2ɲRY:xM.R3E)4BX*dߒ5 i -nT -3SOiM& ԑ*!l' F)3bcF3Zmt[08wꒉX3 fH %wAmuRvLđ ^DsxX =Uo ò4* j:\KQY#3RcUſsVSW܉Vu¾Gw8}~rDG H V3-X"Y*TI[ K=Vzhg((%0 g ]n*NJGqɇv<,crаwpk̩St6i42,+Xg6g2ZtuTcTJdY[ 2x u+S{|YAi Qp5DH7{XKкR[co8$J#d&hJW+vQ]prsԡ tEч~⽇Nο~m3F|˟a] ٔeP"#YGˈJAn+|lO͍[?ݏ "~"ITM/| :9aQ:=ZHbFkMkCҕ ݌bz4h{pF=(QeD=qh4uqOZ$ܬPyq.J8NƒO B|,J'DkGצ%35!R3F+pZWI5f )Hr6kgs:g?f<8~yyV)ξCTt@31oyM;o{>f91CF)J!Cpi+S/,@/plY9Av+iָIΟ*uQRF96/* K+"/dsa&Gcv~qkSkء-2p$@Jp%ж4PR*O[T^q\z2KKHZ;~΍mnuWbGo! [xS1.#C 5 hJb$1E$Q@"75@6ί CC.2ESjnuoq1=ǹ &w–ԥ% UoݑvO}z6RX!c>wZ^{k7&2mE$ Nx/au%&;" l09_.Nl(WcF`g~$PW*I/7k0>ƎPENc5ϻ G[7vb Ҍ ,J&884sEY8B6K%;vdGkraA35 ׎0(:H,e@GiRLyd&q-%^ <{QGraujsx$x,tV9sjB&4u{cEV^ýVYD{Lys88>9?+xݘ"VWϷhZmsn:XeL&$r(Bqvc+/phu'9x>vXpg\bDT)G%I8p~eNlQ nutZ}w%SMgYFyN9ͱ"G|![ LY!Z֩>]&up`g,+}Noo.2>Cf1E"um 6sT *QA2~,[?J9AʥgKtחHbnQ+RTqi-i&>/-$Nӯ|ρ;)_t&j 㨄J/IHqBb{LMH6)Ϭq+,+>_{0<>|!™ ;`4muXhh>h$6+[A΁4,ui:8ZVK̼clic*'P\bi}2)-8·7y[o3;R/>=oy)jT<,W|͌_^is\ZdwX3X7GNg  vf\`w5є7o PR-::el53 [ZϷx)W ++yklQ1(v`pzt^9' )ˎ9 ^;28.@Xm||_Th[Ƃy_Wg~U3&~.)(% !;,͈C99ȃ. )Ik >*{*v8M08̱Kzi 1E/sxl`y7!!ʼnj2)gұrf˯ qTJ!T g9uvC1-x]DhʒΒdfٗ;W_ s;'0&1. ` fQ! ^6YXI{ew_%;& 2[\X-"Ξ[sgyg=SJKL"(i!3oTjT8ۅ{3hg(YbR{ #m\F/%)^|K!y^? vJ\czkw$#Oeh g$)^X?'IS=o{3`w?6\m [SRZJb 6kK ?uKQ Z\\f?ဇG5FU= } +ޯ+.-Sl^@!HDk-^J|[u}jRshkE[F$%m.3U]P썙DgDJSV [R >[/K8X~ӾG_~gȍ8 \9eY"$0?IGrt/shg EAe]ȱ,( ԆI>^Ělᘱ]_z!r\F-̓”8ɢDf`vՇbxjk"*28J`|? IDATXv/naӦlTd*&[K>1\^]gYglY%HG(c&:47^fs(H %}{qo|wK}T_Pւ*JJUڌq0fjo;n 纊~.?:~Ԕ \Vlմ:`ii/~Z ggraS.͈WC_#*N/a.vq6vw(J ȧ =$+6 G)f)%ݴ)NԢ&TǤAkFy1p(,o1=RU#L8e2me*%nIӬ$4oo=̉$"jH/R>zȅs烥I?f-n߻Mh78NPa%~o_߁<?˲y TX.Ȳ^,s JadehTeN"ިB5n-kL}S+d?yZ>: 8>*P$=.)1i>f:`aTS* kIlZ`$+B$ VH&P(؅uYx_!\ MĄA|(Z+++$qCd4 LiFM;h8G7[cKڭ:To2aakw?<9)fLOE ҌsK^xW^U*g/t8:7eUFǹE}W~>O~>҅/o_wbl DxZD᝚o}BI,,t:V7Rİeq\GYbI#.44Sɬqq'̤  I8^17N'wwsW)IîX&=N7>YhIi 6 s2Fʐ4E0{͉W^Kׁ/=t\0L(WJEX+P{/zHbc20OkGiB+ܚhY"q%8-dsʙ0Z.[d8$udfuMplZ~llbKݥ]>5gGlW~Ϟ{D%萫ԱJLjsK-/0cax(f%c:LGJtdtGU0B#8K;s(U' oNʹ<9ga ^4M:9Axd}f.fu]7'EP h :vy3;w^V-jP(l }Bd:-ϛ7³e#B5wRJ:Vl lZFaf[4تD{-ƕ<^B]$crBW0-g#= Bi,HӈV˲8{>ԩ6k]: ZOşb' nt9 :I'aZ| ?sv)o~hCy"cV?s#^lxӖ#8>ZHdSUD(YTX%glKZYWﭡM>|cߩj2Q; 39;(*> 8[@.mkCITnF΅YN v*偪,Cyc[³rzR8޵w4B{;Ӌᗜ)Q.ܡ8Q*p"_h$ 1V40ӚHYtu[D; 5%E #\(&Q97 (CF #6G(}LS)#T$I-h OMFJYfXD2!A;- EBL-iXd61=.]@Ej5UÝ)~y~Ef8kPhS~Z76PCߦ%亢;"0BѱBF#I9(Q3F3tRX5%QI\fgjUIkU#j N'1v^ع{' |HS{|b)0.q'B;4IV cX;8"`*UUS7n9*HD2BJ%}-IRaIغF($~wonyf\_~Iipv+"CkgQ"|-jjuiw~n?{b|N(\R,@l=xƂ?K/_D U[\ayS4ƢʒJ&UE _c[  'pR`j tJb#Cf2QyOzF jQFP]yJP6Ϋ"]S̾i{:<2}J5~a72wXeYK K;jgB,pH(-)уc|ߗ8uĢ 9)b+ B(754s2 t`Ԅ v ^RZN.`;0f8GaAzaj+0oEeOMH(p}4mD*vquMYp]\ʪ$6b~sz,f8RaNnuE] sq±]˺' ev>y~=\֓ZآZ|'3rath hьX$}~8gl ԾWoŭׁ pd %1q@ )Xh[j yG8$M>Ɯsn(>hnBRo)fq"! ^mūYlV,nR+v5;.T"`Z'Xb"g%Ji\ #o嬠6ɄQ'IU,* `Ar ^UQyuH`UHEw;Χge3YHDw̸`Z/SV!A̟WS4[r:9nBIlsUo+M$0MUQ{|Md<4\9Ex.MQZ%qm$Ky\ f ~ q/3gdƵ&p0im]8?|ѣiBc\$Ṍ Ix>8*PNIXl"ɄDYZHChA%$-ɸl#snqmsoU(vCl$B4襎c(6BYt !!m0^+RH5%Y:O(oO2N%;^$'w*BP^d޿au^yK's,$I 9,GX\G&|"e4 "Zh4 !` /qǝg(\JOKR*yO\UeQcm#FdwjCjeN2ttX<ۈtT&䪢YII&t\|Bz i(tE)*r_[)v%"D{ۻ䣜\*AԞnrVCptCbYrmfLM$Y#EB51 &Hv Wdqvo1m gNXS! CG-VZ Ùc.C;@A% t*PDVP^_ut3Gyօ-H2)Qh#-hM.GyRoq`mM%mζ$:Ĩ,AgI 4X(Ԣbtc5mRj)/"pl㷇_"?dj\#ZNJ ͷormJj^+[$"nINpˎ/e)-xu|*TT]XG(Xɖ[P*zCY(+=>yMVPH$2FX`p8΍c !MνXLSf+\YnP37ٹ4O@ Z7"vsL!'-.lq~2/9۷:wH刄#RR`_skf}YϬRkXzHqd3<wṵDa MeE5vN"c<'/Kjk@E[jC9m:WEI=<D+gO9G jzL=uFx8fHcz }1ޭegWY:D*(JlD=6%L>C{.-p3wVB/?Bτ74^JdZ٧;FMs\^b{ }ޛ1"B"/_}%5*DYLN).*@j sy Ctn%P852VX:Y:BF$|7sn/_O@Fcq}S"FphS:(VE$N#VϞ" abIT>e3yK!mp߿{]+0B bLk/%E9>!^J) SVyKY"2Fy0 d @+y Gl2l6iRZdYF׊dh8Z0r} F 6TD)B xg tO/q,8q;)DL,5%l wD.Z$":3^ D/i؋t֗ GV aW0`ϟߒR~ %z$Y7}nq.jC`&Bd$Vr$JU6."KWN[{Ȕt֚d) M_oȊWz̪!V(OUFia*TWC̋g9˜~"YDŽQ= IDAT*t0orM9B]%MdjtK*tέ牷,&OqeiR;nqt!w3cjT[wn=5pNg[XQt=&5!Ь]+omZ))8 7ʇc30h@-qV"QhIXw!pH"ʂ4(IYAFؘ0^JԥY h/q\f ڠ;QGSD%X?JgY>bbR .Ƿ="'3H|1qHTkJHt`͌g8Y:VH{$iʤ̙{o#jheiQbHI%4Qz(DL$Q[cKtK^\c)#Di9uQ< rA@GZaͨD6H+El:%@ʹGiF> IV{vަ&74>g{w[g @&-1&c 8 B\2)U!$hZ#1#>L}n,gy{{4F̨Oͽ}m RoV\=s6W3AfKQu/E~?!]q>S$4jB(n⩧Zw4 :8QDBQ}VqwEff^O "$z#zL@|->a!~uzsJ_ U T.UcBzqZC HrYa2=_kOPY\X¶-׷(Q@*츅ZIdF|J[Y2U+#) MMUh(X-q" S+V6M$[ZN?鸕I-Ӫ쭧L S,O S ߦM"`8%f_2'[>UPAb4Qu=aIsCj̤{T %/`,K4`:L]p%d~OU_cMDo*!geOpkgAY2]C@ # rk 2P!|\ IHHRv8~uNĹ3wQ7 ZiV89G,DI?/iZr%S@q1Z10eZ6pm!cA/k Ɠ)Hf F8XYZ.ۄXh+K5Eda6EN+TcJeoYćDIJ$P qM{m h1)S}+\\děΪq&UiQ"fg_%RE2!Df:砛x.za(qE%0%Y JZ%2eQB`;&MnRdXi1h(PV`H?+#(LP瑱nɅQK1/UH4mD#=H[ "Q;؛1b$xۭn~M :@eKM~":-AxͰmf6ٜ2z…4AS&#Y]&"SG߿l1t&c}:n'J#I|.T,7Ib]H9Sh2eh*f3\SdO*zwN7s^#Nsη?y(٠-m\M=%~ȣ$1R8?3ɔnQM)1R!JΩhqxIIiu&M9ίZ'.\vM& kÜGQloluq"Bq_҉{]O+q_]_Gi5Z$.p@ѯ^i5ѦCDbTa6[2oT-,>]GY:bA]O"$8I^qK\~,nݦUA&yp!mG;`]Lƞs(=%O~a%؈9Iﶬ;E+Z8{G\F}C}=ܳ7 mLQUDH:wЬiQhY6wu@AϘ)?ъu7Mش ;MRDz^H7P6( MSdBxஂM HoX2\>G jb [6qC_(B+>ǿԽ$K<>CB\*(&C269,BBt'/E"{Oru||W^COqÏo4PoP s!]hG XPDZºt,z9m³x}ܸz'aC爧F( 'x7{MGJU+@vq߽@=0;-^hhFȩS8ëqޤ dېA!Rƀ nX],I%Mx@~kX|Q4D4"&d"mySN73 UVq0?ݮ"}y*b%"s-"2RB;S,wlWכ?_HBs[sν|ӑۏۿB00i*7B N$|e í.+-پ!KwbϡUfs\㬆>Di45qrX;D[Ju-Mz DAaƅi ko#;f3܃rcg]SP>'γVW9v1TEDO!ts7[ҒV$BgnO6YP+ul^Byh|C=}10lUc 4FF(# F]KĨLW/y ޑ(Gp}t OIɐU9 ,u#{L$ S:(阒g%&P=L޺bj+Bbsy^"n}: %2fO;T#j7+#/T!oz?;p_Ա>gUYcX#ԁK^oV5!aU;S'7#roVK^K*05"Dи1 f{'vLh!iT3qIcDV=ʅkTצ bGmMsm>DVC|5F[YNh+vQ&HޠOabB,$=TX1r5JGtsCھO1҄^~ʖu ^:Y(CC߿#h/Nܠl%PnQm/'9rCG΃?AaJ"`&5Fjh\XL@.*Hў>$n\n[mJ$wf>$kW.3nQ19~1:de>UAҨ6T"L " +WѨtiΎ_}<0G+E4JzG%5+F2%w" g@HFRFSpN޵&]ڡ g>c}l`m}-_ew[|K%VWB;enwISy! O>(qjM݅Y'fqvligD&1!НiĸlMfЊgN䡍|\F'}~hc@'aP*ϾY/&>ٹ~v#Ua׮"еwGq+4yϽhl;wlALϊƘ_'~/_'~7[>cDh9w<{3[wBH"E3xxo $+ktx ƞM0K[l%3bj-VI&3FXٮH,k UP6\u"E !b jjt~ &V}~.ssד ~~oK<=-^f]C…'@ȑJ3 Qdb8^V@ #}Chb"T\ ^vTH ?, Ԯ"xuJQ 2*Ddf*ހ"b@ D%i8:TDJiJR(Hm Pnji+ꨡ(P\e.bW| Ul@D%&G:㺔I2ɱUē I4i l`˛ RTYJM!4ZnL>Cx@#^% ljia B_ɷǁ3geWD;j #fY7_R ep{: "Yb9䎠V5>8/#_/¿Os wbdrĘYCge QI!rf4Iz$bG׋ &LJTdHm51__=p_N/H\^]zvRtNL.6ƛVFԔN,J)2iQ.>i!ѡ"=Kn D%RL 4 ZE>t}[++`gŬ=p7/8ih@z֊~큳_ fz.B@ =6n`zyʊ_ERI,>S<5rඃ@+tg:Zd^6X>2*tV<7\c z6rc+=?F/&%tdSzېZOM6?rlwRIDATהcPffr_ADkrr'A@lJ&Z %؈B\Ax >x\G F=SH/RW "nX1%:0UATPbCNt Brp9ڍܮFM!6IB z{3M,{a$QIGM"c5 bÜՔPaNDsmJT%(Dcu9bv;牉R`V; 8Z 4 \/Ћ$!b̞GD:"uA_D ȅ>"m߰ K18 -5²!b"aGcchc:>CLj`$ܤm%bҞyReIQ3|}畴5xŔ7{;UTJ4urgUK8DoqIFX7E%Vc0,s'q7R )UN) skhipc˅O=+d䘬Nzb?kvdċ>p=9d:)mNkaI''J"bcj> q϶:zQ#vTNjtT WXΗh7=L}J # ӭ1qdS׽%WN r zEhF7Wx^4y,*y`)m3ǨRHf#mU3TlۖitHHhAS)MSQu)2HM*!ik~n܃†~o2/7U\}2,Vo;DvF~&7Z# pSm_?5J:&/5lΙ=Ngez m {&_賽c7\~3D۵H4wW>HP]d uɇ:FM*>T= O$}B&0Òf^z{h'@'1C[kۈʑGM=E~fih@zdPVNrSx" )sssCY&WiORP BJN'(tq1YVQjWP9&8":䢏(5'J=Wj!dN/)%~=,ݶBA5󑥥e O?(>iۑ"h|!STJ(AGE[7"'0 jOejCCNrr6So`5.q.|q3}X<0d`VY䴊;?M\Xe@zAh=-{ 6e:wK{h:Zp.B_Wi,L*^CpKe['29&%Sr޿:q>Ei=rii(Ȧ3<޿~1|kl"d0"OD@&C2]_ 豾ď\<+oK#?Ԏ#7έ񙃔,[ahJ(vs?.cVmP H]\5:2'8m-YOל#\>sS8}p/`ri}> |AUC%4$sn"h)d4*ιh-:Jdifm~L+=EaӖMQZdy F[D9NIMen i [2>)j(2Tg:D̑Ґ9CXoic>-<:Y(U$ SfO!Ddت>?\?y7?_ ojh=c`])%j`pZpP#,>vEžH)ɤBIb(z)&XL)&PH>/iƸ4uKRv5dHݾڅgL*{A|&YQQ D~Q׉ѳ+b0#Ltk tXdZDHx֮BꪘgvYQP`"E_cՖ~ĠqӖǷ @yj`ɽFAR%3]TǢFV w^j[WS䷟h_n) $"0zr>R6UnEt"jPJIIRʩ|AZLc@, :c\|U!GHӴz3ʳjöd)R{٥we4RJڶi%o^iiM/+y<Ƿ &Z2g[)4,$[]|Rf ):D5H-2C\*a=J@"DX۠(OA ,jڐd}H!mEsoU6īٿ׺O }P)p:1nBwQ}ᝅC(ڔw[&m C9Sraf%uF444Ո& \Ւ ڦkΓ94ʜBy7X$>jJ=C| mE6ürCdq$ôl Iiju[=ѴS2{L& D5}evsDn S˲ )O[(SZLjW[2U L&Jg󢗈&ls.FږLD'N?w]g~ժV>x_liQ+iKss:lGtZĠȵ0#G3! Pm1ҠOXh'0I(M} I$uZO"0+"@XLG {CzkK&l+$6~oP!#7XFL]K4DJ` `'a*bIfm;6䚍z0>H=Iٵs :?R+De96Nڣx^÷;z৯^XG?\h) #s(g2ٟ sI)ؠrkZaQ8qlG ٺtх5bwS ,˰ǵ-YQ;FJ'1E7}=<i2|c:WoЮoSxEQu4(RdXR#< /O?d~JyA^tR$Sy-Qڐj_'+=G:##Dq ?8?q߶Ct#rhP((\ܶIKD{wqwCZ=l]b|u'>7N LzJcg[dMb^Hj)Hȼ'x#_u85]{zpMlfB+mrg>`Brd}ZہgLtKiCI響ʗ_.oO?孃< =^TLvz'ZQ+Iρןய=ّ!/_C3Y!Ü.4]l5V M;J1 K"f`錄8s\'׷XZXd#yý,\+h',}f:X!Iw3b222YV-s+OS OcdTE I0;/k"SvJ[2%-Y,އ**ГT|țJu;$HħPsŗ_noxW A ?*" & |lECcQ\Bk9<>}מb˴[DSFtwr gJ DiX8ȉNo˗pGΪ\a2`,S8|,=9rikJH($N1zjA`=G8| %7u&f) с%41؛>g%LϥʜeI6ۊF9Rໄ< S۸5|'lH=_O?#i6M\<ž*t™yXիd6:&ֲ/]cϞCE>'q}:Hi46F|2XuجVci}`yn^cC3}z|O!/#tTH<}pX\^fXpWXDH޳;[g @*}>lLGl]fj+:QSfP)%!^ 5]oMo9?!yۭsn]`zfAS QXōg >IyX#\cثnJft7 ,(7Xܷ7f ]SO_etó8 6zO]Ӈٱ{r:# H¡s 7ƃ|gQn0]:~ALnlS*EȢ] s :Z- "=DG৖.:ijVB^xyksg|Fp_Ue:Ų$zt醅*g*W3^b't̃)57>MF[7]p`ϛW~J5qx]GNﵭ?E+q!$kUUu4EGGcTٹgX\]!+rx|N*pNO54 R:NDĝl|$KJ-P=6ty̐b!Zka<L]\wsDtUwkd5_wt4nevh*8$jE9,)jMz@8=reu23n'Ӕŝ*DLGcF B `!?w*A0BDcېӂLYVpq/4|k >1La?o]a&|k8?̿7u;b.@ii"<2#IE!_\HLm$*dXVjtVVN/1FT|EW4MAEL_Qew 4d"S۶sピ2 !8.]y֕tk~۟yWxPm>Gd0x"JtnHe4iG`mUKEBk|1=uhgQJbʂ.d%Ad)P p-4>&E'x'Ƈ߳g[W̭9W=w#1ƿ:F%c@d imRZ4EKXA0!!BDĐhm1iۤ4<AךyVIF֚Le4X;#:@F:{ːW xVЃ3럏>x­}ō|\ 8-q@_c|~!cw{bvQ]jmV]s'mѻ ݊;SbǏo GwxgQMB4λ&^|BD>|Ɵ]oMWx?x?ٿ.+wZޮIXDO $SeQSDMh=й-Df־@Ka,ґ2dn3 &p Go}|}5ql?X=nxW>#x8c'XQ.9P ! )D d׶I)50&6 7k2px]vn}O/!n]+ow`!IENDB`trace-cmd-2.8.3/kernel-shark/src/000077500000000000000000000000001351617527000165475ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/src/CMakeLists.txt000066400000000000000000000102201351617527000213020ustar00rootroot00000000000000message("\n src ...") message(STATUS "libkshark") add_library(kshark SHARED libkshark.c libkshark-model.c libkshark-plugin.c libkshark-configio.c libkshark-collection.c) target_link_libraries(kshark ${CMAKE_DL_LIBS} ${JSONC_LIBRARY} ${TRACEEVENT_LIBRARY} ${TRACECMD_LIBRARY}) set_target_properties(kshark PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") if (OPENGL_FOUND AND GLUT_FOUND) message(STATUS "libkshark-plot") add_library(kshark-plot SHARED libkshark-plot.c KsPlotTools.cpp) target_link_libraries(kshark-plot kshark ${OPENGL_LIBRARIES} ${GLUT_LIBRARY}) set_target_properties(kshark-plot PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") endif (OPENGL_FOUND AND GLUT_FOUND) if (Qt5Widgets_FOUND AND Qt5Network_FOUND) message(STATUS "libkshark-gui") set (ks-guiLib_hdr KsUtils.hpp KsModels.hpp KsGLWidget.hpp KsSearchFSM.hpp KsDualMarker.hpp KsWidgetsLib.hpp KsTraceGraph.hpp KsTraceViewer.hpp KsMainWindow.hpp KsCaptureDialog.hpp KsQuickContextMenu.hpp KsAdvFilteringDialog.hpp) QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr}) add_library(kshark-gui SHARED ${ks-guiLib_hdr_moc} KsUtils.cpp KsModels.cpp KsSession.cpp KsGLWidget.cpp KsSearchFSM.cpp KsDualMarker.cpp KsWidgetsLib.cpp KsTraceGraph.cpp KsTraceViewer.cpp KsMainWindow.cpp KsCaptureDialog.cpp KsQuickContextMenu.cpp KsAdvFilteringDialog.cpp) target_link_libraries(kshark-gui kshark-plot ${CMAKE_DL_LIBS} ${TRACEEVENT_LIBRARY} ${TRACECMD_LIBRARY} Qt5::Widgets Qt5::Network) set_target_properties(kshark-gui PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") message(STATUS ${KS_APP_NAME}) add_executable(${KS_APP_NAME} kernelshark.cpp) target_link_libraries(${KS_APP_NAME} kshark-gui) message(STATUS "kshark-record") add_executable(kshark-record kshark-record.cpp) target_link_libraries(kshark-record kshark-gui) install(TARGETS ${KS_APP_NAME} kshark-record kshark kshark-plot kshark-gui RUNTIME DESTINATION ${_INSTALL_PREFIX}/bin/ LIBRARY DESTINATION ${_INSTALL_PREFIX}/lib/${KS_APP_NAME}/) install(FILES "${KS_DIR}/${KS_APP_NAME}.desktop" DESTINATION ${_INSTALL_PREFIX}/share/applications/) install(FILES "${KS_DIR}/icons/${KS_ICON}" DESTINATION ${_INSTALL_PREFIX}/share/icons/${KS_APP_NAME}) install(FILES "${KS_DIR}/org.freedesktop.kshark-record.policy" DESTINATION ${_INSTALL_PREFIX}/share/polkit-1/actions/) install(PROGRAMS "${KS_DIR}/bin/kshark-su-record" DESTINATION ${_INSTALL_PREFIX}/bin/) endif (Qt5Widgets_FOUND AND Qt5Network_FOUND) add_subdirectory(plugins) find_program(DO_AS_ROOT pkexec) configure_file( ${KS_DIR}/build/deff.h.cmake ${KS_DIR}/src/KsCmakeDef.hpp) trace-cmd-2.8.3/kernel-shark/src/KsAdvFilteringDialog.cpp000066400000000000000000000242031351617527000232500ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsAdvFilteringDialog.cpp * @brief GUI Dialog for Advanced filtering settings. */ // KernelShark #include "KsAdvFilteringDialog.hpp" #include "libkshark.h" #include "KsUtils.hpp" /** Create dialog for Advanced Filtering. */ KsAdvFilteringDialog::KsAdvFilteringDialog(QWidget *parent) : QDialog(parent), _condToolBar1(this), _condToolBar2(this), _condToolBar3(this), _descrLabel(this), _sysEvLabel("System/Event: ", &_condToolBar1), _opsLabel("Operator: ", this), _fieldLabel("Field: ", this), _systemComboBox(&_condToolBar1), _eventComboBox(&_condToolBar1), _opsComboBox(&_condToolBar2), _fieldComboBox(&_condToolBar3), _filterEdit(this), _helpButton("Show Help", this), _insertEvtButton("Insert", this), _insertOpButton("Insert", this), _insertFieldButton("Insert", this), _applyButton("Apply", this), _cancelButton("Cancel", this) { struct kshark_context *kshark_ctx(NULL); int buttonWidth; if (!kshark_instance(&kshark_ctx)) return; auto lamAddLine = [&] { QFrame* line = new QFrame(); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); _topLayout.addWidget(line); }; setMinimumWidth(FONT_WIDTH * 80); buttonWidth = STRING_WIDTH("--Show Help--"); _helpButton.setFixedWidth(buttonWidth); _helpButton.setDefault(false); _topLayout.addWidget(&_helpButton); connect(&_helpButton, &QPushButton::pressed, this, &KsAdvFilteringDialog::_help); _descrLabel.setText(_description()); _topLayout.addWidget(&_descrLabel); /* * For the moment do not show the syntax description. It will be shown * only if the "Show Help" button is clicked. */ _descrLabel.hide(); lamAddLine(); _getFilters(kshark_ctx); if (_filters.count()) { _makeFilterTable(kshark_ctx); lamAddLine(); } _condToolBar1.addWidget(&_sysEvLabel); _condToolBar1.addWidget(&_systemComboBox); _condToolBar1.addWidget(&_eventComboBox); /* * Using the old Signal-Slot syntax because QComboBox::currentIndexChanged * has overloads. */ connect(&_systemComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(_systemChanged(const QString&))); connect(&_eventComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(_eventChanged(const QString&))); _setSystemCombo(kshark_ctx); _condToolBar1.addSeparator(); _condToolBar1.addWidget(&_insertEvtButton); _topLayout.addWidget(&_condToolBar1); _opsComboBox.addItems(_operators()); _condToolBar2.addWidget(&_opsLabel); _condToolBar2.addWidget(&_opsComboBox); _condToolBar2.addSeparator(); _condToolBar2.addWidget(&_insertOpButton); _topLayout.addWidget(&_condToolBar2); _condToolBar3.addWidget(&_fieldLabel); _condToolBar3.addWidget(&_fieldComboBox); _condToolBar3.addSeparator(); _condToolBar3.addWidget(&_insertFieldButton); _topLayout.addWidget(&_condToolBar3); lamAddLine(); _filterEdit.setMinimumWidth(50 * FONT_WIDTH); _topLayout.addWidget(&_filterEdit); this->setLayout(&_topLayout); buttonWidth = STRING_WIDTH("--Cancel--"); _applyButton.setFixedWidth(buttonWidth); _applyButton.setDefault(true); _cancelButton.setFixedWidth(buttonWidth); _buttonLayout.addWidget(&_applyButton); _buttonLayout.addWidget(&_cancelButton); _buttonLayout.setAlignment(Qt::AlignLeft); _topLayout.addLayout(&_buttonLayout); connect(&_insertEvtButton, &QPushButton::pressed, this, &KsAdvFilteringDialog::_insertEvt); connect(&_insertOpButton, &QPushButton::pressed, this, &KsAdvFilteringDialog::_insertOperator); connect(&_insertFieldButton, &QPushButton::pressed, this, &KsAdvFilteringDialog::_insertField); _applyButtonConnection = connect(&_applyButton, &QPushButton::pressed, this, &KsAdvFilteringDialog::_applyPress); connect(&_applyButton, &QPushButton::pressed, this, &QWidget::close); connect(&_cancelButton, &QPushButton::pressed, this, &QWidget::close); } void KsAdvFilteringDialog::_setSystemCombo(struct kshark_context *kshark_ctx) { QStringList sysList; tep_event **events; int i(0), nEvts(0); if (kshark_ctx->pevent) { nEvts = tep_get_events_count(kshark_ctx->pevent); events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); } while (i < nEvts) { QString sysName(events[i]->system); sysList << sysName; while (sysName == events[i]->system) { if (++i == nEvts) break; } } qSort(sysList); _systemComboBox.addItems(sysList); i = _systemComboBox.findText("ftrace"); if (i >= 0) _systemComboBox.setCurrentIndex(i); } QString KsAdvFilteringDialog::_description() { QString descrText = "Usage:\n"; descrText += " [,] : [!][(][)]"; descrText += "[&&/|| [(][)]]\n\n"; descrText += "Examples:\n\n"; descrText += " sched/sched_switch : next_prio < 100 && (prev_prio > 100"; descrText += "&& prev_pid != 0)\n\n"; descrText += " irq.* : irq != 38\n\n"; descrText += " .* : common_pid == 1234\n"; return descrText; } QStringList KsAdvFilteringDialog::_operators() { QStringList OpsList; OpsList << ":" << "," << "==" << "!=" << ">" << "<" << ">=" << "<="; OpsList << "=~" << "!~" << "!" << "(" << ")" << "+" << "-"; OpsList << "*" << "/" << "<<" << ">>" << "&&" << "||" << "&"; return OpsList; } void KsAdvFilteringDialog::_getFilters(struct kshark_context *kshark_ctx) { tep_event **events; char *str; events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); for (int i = 0; events[i]; i++) { str = tep_filter_make_string(kshark_ctx->advanced_event_filter, events[i]->id); if (!str) continue; _filters.insert(events[i]->id, QString("%1/%2:%3\n").arg(events[i]->system, events[i]->name, str)); free(str); } } void KsAdvFilteringDialog::_makeFilterTable(struct kshark_context *kshark_ctx) { QMapIterator f(_filters); QTableWidgetItem *i1, *i2, *i3; QStringList headers; int count(0); _table = new KsCheckBoxTable(this); _table->setSelectionMode(QAbstractItemView::SingleSelection); headers << "Delete" << "Event" << " Id" << "Filter"; _table->init(headers, _filters.count()); for(auto f : _filters.keys()) { QStringList thisFilter = _filters.value(f).split(":"); i1 = new QTableWidgetItem(thisFilter[0]); _table->setItem(count, 1, i1); i2 = new QTableWidgetItem(tr("%1").arg(f)); _table->setItem(count, 2, i2); i3 = new QTableWidgetItem(thisFilter[1]); _table->setItem(count, 3, i3); ++count; } _table->setVisible(false); _table->resizeColumnsToContents(); _table->setVisible(true); _topLayout.addWidget(_table); } void KsAdvFilteringDialog::_help() { if (_descrLabel.isVisible()) { _descrLabel.hide(); QApplication::processEvents(); _helpButton.setText("Show Help"); resize(width(), _noHelpHeight); } else { _helpButton.setText("Hide Help"); _noHelpHeight = height(); _descrLabel.show(); } } void KsAdvFilteringDialog::_systemChanged(const QString &sysName) { kshark_context *kshark_ctx(NULL); QStringList evtsList; tep_event **events; int i, nEvts; _eventComboBox.clear(); if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent) return; nEvts = tep_get_events_count(kshark_ctx->pevent); events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); for (i = 0; i < nEvts; ++i) { if (sysName == events[i]->system) evtsList << events[i]->name; } qSort(evtsList); _eventComboBox.addItems(evtsList); i = _eventComboBox.findText("function"); if (i >= 0) _eventComboBox.setCurrentIndex(i); } QStringList KsAdvFilteringDialog::_getEventFormatFields(struct tep_event *event) { tep_format_field *field, **fields = tep_event_fields(event); QStringList fieldList; for (field = *fields; field; field = field->next) fieldList << field->name; free(fields); qSort(fieldList); return fieldList; } void KsAdvFilteringDialog::_eventChanged(const QString &evtName) { QString sysName = _systemComboBox.currentText(); kshark_context *kshark_ctx(NULL); QStringList fieldList; tep_event **events; int nEvts; _fieldComboBox.clear(); if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent) return; nEvts = tep_get_events_count(kshark_ctx->pevent); events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); for (int i = 0; i < nEvts; ++i) { if (evtName == events[i]->name && sysName == events[i]->system) { fieldList = _getEventFormatFields(events[i]); _fieldComboBox.addItems(fieldList); return; } } } void KsAdvFilteringDialog::_insertEvt() { QString text = _filterEdit.text(); auto set_evt = [&] () { text += _systemComboBox.currentText(); text += "/"; text += _eventComboBox.currentText(); }; if (text == "") { set_evt(); text += ":"; } else { QString evt = text; text = ""; set_evt(); text += ","; text += evt; } _filterEdit.setText(text); } void KsAdvFilteringDialog::_insertOperator() { QString text = _filterEdit.text(); text += _opsComboBox.currentText(); _filterEdit.setText(text); } void KsAdvFilteringDialog::_insertField() { QString text = _filterEdit.text(); text += _fieldComboBox.currentText(); _filterEdit.setText(text); } void KsAdvFilteringDialog::_applyPress() { QMapIterator f(_filters); kshark_context *kshark_ctx(NULL); const char *text; tep_errno ret; char *filter; int i(0); if (!kshark_instance(&kshark_ctx)) return; while (f.hasNext()) { f.next(); if (_table->_cb[i]->checkState() == Qt::Checked) { tep_filter_remove_event(kshark_ctx->advanced_event_filter, f.key()); } ++i; } auto job_done = [&]() { /* * Disconnect Apply button. This is done in order to protect * against multiple clicks. */ disconnect(_applyButtonConnection); emit dataReload(); }; text = _filterEdit.text().toLocal8Bit().data(); if (strlen(text) == 0) { job_done(); return; } filter = (char*) malloc(strlen(text) + 1); strcpy(filter, text); ret = tep_filter_add_filter_str(kshark_ctx->advanced_event_filter, filter); if (ret < 0) { char error_str[200]; tep_strerror(kshark_ctx->pevent, ret, error_str, sizeof(error_str)); fprintf(stderr, "filter failed due to: %s\n", error_str); free(filter); return; } free(filter); job_done(); } trace-cmd-2.8.3/kernel-shark/src/KsAdvFilteringDialog.hpp000066400000000000000000000033601351617527000232560ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsAdvFilteringDialog.hpp * @brief GUI Dialog for Advanced filtering settings. */ #ifndef _KS_ADV_FILTERING_DIALOG_H #define _KS_ADV_FILTERING_DIALOG_H // Qt #include // KernelShark #include "KsWidgetsLib.hpp" /** * The KsAdvFilteringDialog class provides a dialog for Advanced filtering. */ class KsAdvFilteringDialog : public QDialog { Q_OBJECT public: explicit KsAdvFilteringDialog(QWidget *parent = nullptr); signals: /** Signal emitted when the _apply button of the dialog is pressed. */ void dataReload(); private: int _noHelpHeight; QMap _filters; KsCheckBoxTable *_table; QVBoxLayout _topLayout; QHBoxLayout _buttonLayout; QToolBar _condToolBar1, _condToolBar2, _condToolBar3; QLabel _descrLabel, _sysEvLabel, _opsLabel, _fieldLabel; QComboBox _systemComboBox, _eventComboBox; QComboBox _opsComboBox, _fieldComboBox; QLineEdit _filterEdit; QPushButton _helpButton; QPushButton _insertEvtButton, _insertOpButton, _insertFieldButton; QPushButton _applyButton, _cancelButton; QMetaObject::Connection _applyButtonConnection; void _help(); void _applyPress(); void _insertEvt(); void _insertOperator(); void _insertField(); QString _description(); QStringList _operators(); void _getFilters(struct kshark_context *kshark_ctx); void _makeFilterTable(struct kshark_context *kshark_ctx); QStringList _getEventFormatFields(struct tep_event *event); void _setSystemCombo(struct kshark_context *kshark_ctx); private slots: void _systemChanged(const QString&); void _eventChanged(const QString&); }; #endif // _KS_ADV_FILTERING_DIALOG_H trace-cmd-2.8.3/kernel-shark/src/KsCaptureDialog.cpp000066400000000000000000000342711351617527000223030ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsCaptureDialog.cpp * @brief Dialog for trace data recording. */ // Qt #include // KernelShark #include "libkshark.h" #include "KsUtils.hpp" #include "KsCmakeDef.hpp" #include "KsCaptureDialog.hpp" static inline tep_handle *local_events() { return tracecmd_local_events(tracecmd_get_tracing_dir()); } /** @brief Create KsCaptureControl widget. */ KsCaptureControl::KsCaptureControl(QWidget *parent) : QWidget(parent), _localTEP(local_events()), _eventsWidget(_localTEP, this), _pluginsLabel("Plugin: ", this), _outputLabel("Output file: ", this), _commandLabel("Command: ", this), _outputLineEdit("trace.dat", this), _commandLineEdit("sleep 0.1", this), _settingsToolBar(this), _controlToolBar(this), _pluginsComboBox(this), _importSettingsButton("Import Settings", this), _exportSettingsButton("Export Settings", this), _outputBrowseButton("Browse", this), _commandCheckBox("Display output", this), _captureButton("Capture", &_controlToolBar), _applyButton("Apply", &_controlToolBar), _closeButton("Close", &_controlToolBar) { QStringList pluginList = _getPlugins(); int row(0); auto lamAddLine = [&] { QFrame* line = new QFrame(); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); _topLayout.addWidget(line); }; if (pluginList.count() == 0) { /* * No plugins have been found. Most likely this is because * the process has no Root privileges. */ QString message("Error: No events or plugins found.\n"); message += "Root privileges are required."; QLabel *errorLabel = new QLabel(message); errorLabel->setStyleSheet("QLabel {color : red;}"); _topLayout.addWidget(errorLabel); lamAddLine(); } pluginList.prepend("nop"); _settingsToolBar.addWidget(&_importSettingsButton); _settingsToolBar.addSeparator(); _settingsToolBar.addWidget(&_exportSettingsButton); _topLayout.addWidget(&_settingsToolBar); lamAddLine(); _eventsWidget.setDefault(false); _eventsWidget.setMinimumHeight(25 * FONT_HEIGHT); _eventsWidget.removeSystem("ftrace"); _topLayout.addWidget(&_eventsWidget); _pluginsLabel.adjustSize(); _execLayout.addWidget(&_pluginsLabel, row, 0); _pluginsComboBox.addItems(pluginList); _execLayout.addWidget(&_pluginsComboBox, row++, 1); _outputLabel.adjustSize(); _execLayout.addWidget(&_outputLabel, row, 0); _outputLineEdit.setFixedWidth(FONT_WIDTH * 30); _execLayout.addWidget(&_outputLineEdit, row, 1); _outputBrowseButton.adjustSize(); _execLayout.addWidget(&_outputBrowseButton, row++, 2); _commandLabel.adjustSize(); _commandLabel.setFixedWidth(_outputLabel.width()); _execLayout.addWidget(&_commandLabel, row, 0); _commandLineEdit.setFixedWidth(FONT_WIDTH * 30); _execLayout.addWidget(&_commandLineEdit, row, 1); _commandCheckBox.setCheckState(Qt::Unchecked); _commandCheckBox.adjustSize(); _execLayout.addWidget(&_commandCheckBox, row++, 2); _topLayout.addLayout(&_execLayout); lamAddLine(); _captureButton.setFixedWidth(STRING_WIDTH("_Capture_") + FONT_WIDTH * 2); _applyButton.setFixedWidth(_captureButton.width()); _closeButton.setFixedWidth(_captureButton.width()); _controlToolBar.addWidget(&_captureButton); _controlToolBar.addWidget(&_applyButton); _controlToolBar.addWidget(&_closeButton); _topLayout.addWidget(&_controlToolBar); setLayout(&_topLayout); connect(&_importSettingsButton, &QPushButton::pressed, this, &KsCaptureControl::_importSettings); connect(&_exportSettingsButton, &QPushButton::pressed, this, &KsCaptureControl::_exportSettings); connect(&_outputBrowseButton, &QPushButton::pressed, this, &KsCaptureControl::_browse); connect(&_applyButton, &QPushButton::pressed, this, &KsCaptureControl::_apply); } /** * Use the settings of the Control panel to generate a list of command line * arguments for trace-cmd. */ QStringList KsCaptureControl::getArgs() { QStringList argv; argv << "record"; argv << "-p" << _pluginsComboBox.currentText(); if (_eventsWidget.all()) { argv << "-e" << "all"; } else { QVector evtIds = _eventsWidget.getCheckedIds(); tep_event *event; for (auto const &id: evtIds) { event = tep_find_event(_localTEP, id); if (!event) continue; argv << "-e" + QString(event->system) + ":" + QString(event->name); } } argv << "-o" << outputFileName(); argv << _commandLineEdit.text().split(" "); return argv; } QStringList KsCaptureControl::_getPlugins() { QStringList pluginList; char **all_plugins; all_plugins = tracecmd_local_plugins(tracecmd_get_tracing_dir()); if (!all_plugins) return pluginList; for (int i = 0; all_plugins[i]; ++i) { /* * TODO plugin selection here. * printf("plugin %i %s\n", i, all_plugins[i]); */ pluginList << all_plugins[i]; free(all_plugins[i]); } free (all_plugins); qSort(pluginList); return pluginList; } void KsCaptureControl::_importSettings() { int nEvts = tep_get_events_count(_localTEP); kshark_config_doc *conf, *jevents, *temp; QVector v(nEvts, false); tracecmd_filter_id *eventHash; tep_event **events; QString fileName; /** Get all available events. */ events = tep_list_events(_localTEP, TEP_EVENT_SORT_SYSTEM); /* Get the configuration document. */ fileName = KsUtils::getFile(this, "Import from Filter", "Kernel Shark Config files (*.json);;", _lastFilePath); if (fileName.isEmpty()) return; conf = kshark_open_config_file(fileName.toStdString().c_str(), "kshark.config.record"); if (!conf) return; /* * Load the hash table of selected events from the configuration * document. */ jevents = kshark_config_alloc(KS_CONFIG_JSON); if (!kshark_config_doc_get(conf, "Events", jevents)) return; eventHash = tracecmd_filter_id_hash_alloc(); kshark_import_event_filter(_localTEP, eventHash, "Events", jevents); for (int i = 0; i < nEvts; ++i) { if (tracecmd_filter_id_find(eventHash, events[i]->id)) v[i] = true; } _eventsWidget.set(v); tracecmd_filter_id_hash_free(eventHash); /** Get all available plugins. */ temp = kshark_string_config_alloc(); if (kshark_config_doc_get(conf, "Plugin", temp)) _pluginsComboBox.setCurrentText(KS_C_STR_CAST(temp->conf_doc)); if (kshark_config_doc_get(conf, "Output", temp)) _outputLineEdit.setText(KS_C_STR_CAST(temp->conf_doc)); if (kshark_config_doc_get(conf, "Command", temp)) _commandLineEdit.setText(KS_C_STR_CAST(temp->conf_doc)); } void KsCaptureControl::_exportSettings() { kshark_config_doc *conf, *events; json_object *jplugin; QString plugin, out, comm; QVector ids; QString fileName; fileName = KsUtils::getSaveFile(this, "Export to File", "Kernel Shark Config files (*.json);;", ".json", _lastFilePath); if (fileName.isEmpty()) return; /* Create a configuration document. */ conf = kshark_record_config_new(KS_CONFIG_JSON); events = kshark_filter_config_new(KS_CONFIG_JSON); /* * Use the tracecmd_filter_id to save all selected events in the * configuration file. */ ids = _eventsWidget.getCheckedIds(); tracecmd_filter_id *eventHash = tracecmd_filter_id_hash_alloc(); for (auto const &id: ids) tracecmd_filter_id_add(eventHash, id); kshark_export_event_filter(_localTEP, eventHash, "Events", events); kshark_config_doc_add(conf, "Events", events); tracecmd_filter_id_hash_free(eventHash); /* Save the plugin. */ plugin = _pluginsComboBox.currentText(); jplugin = json_object_new_string(plugin.toStdString().c_str()); kshark_config_doc_add(conf, "Plugin", kshark_json_to_conf(jplugin)); /* Save the output file. */ out = outputFileName(); json_object *jout = json_object_new_string(out.toStdString().c_str()); kshark_config_doc_add(conf, "Output", kshark_json_to_conf(jout)); /* Save the command. */ comm = _commandLineEdit.text(); json_object *jcomm = json_object_new_string(comm.toStdString().c_str()); kshark_config_doc_add(conf, "Command", kshark_json_to_conf(jcomm)); kshark_save_config_file(fileName.toStdString().c_str(), conf); } void KsCaptureControl::_browse() { QString fileName = KsUtils::getSaveFile(this, "Save File", "trace-cmd files (*.dat);;All files (*)", ".dat", _lastFilePath); if (!fileName.isEmpty()) _outputLineEdit.setText(fileName); } void KsCaptureControl::_apply() { emit argsReady(getArgs().join(" ")); } /** @brief Create KsCaptureMonitor widget. */ KsCaptureMonitor::KsCaptureMonitor(QWidget *parent) : QWidget(parent), _mergedChannels(false), _argsModified(false), _panel(this), _name("Output display", this), _space("max size ", this), _readOnlyCB("read only", this), _maxLinNumEdit(QString("%1").arg(KS_CAP_MON_MAX_LINE_NUM), this), _consolOutput("", this) { _panel.setMaximumHeight(FONT_HEIGHT * 1.75); _panel.addWidget(&_name); _space.setAlignment(Qt::AlignRight); _panel.addWidget(&_space); _maxLinNumEdit.setFixedWidth(FONT_WIDTH * 7); _panel.addWidget(&_maxLinNumEdit); _panel.addSeparator(); _readOnlyCB.setCheckState(Qt::Checked); _panel.addWidget(&_readOnlyCB); _layout.addWidget(&_panel); _consolOutput.setStyleSheet("QLabel {background-color : white;}"); _consolOutput.setMinimumWidth(FONT_WIDTH * 60); _consolOutput.setMinimumHeight(FONT_HEIGHT * 10); _consolOutput.setMaximumBlockCount(KS_CAP_MON_MAX_LINE_NUM); _space.setMinimumWidth(FONT_WIDTH * 50 - _name.width() - _readOnlyCB.width()); _consolOutput.setReadOnly(true); _layout.addWidget(&_consolOutput); this->setLayout(&_layout); connect(&_maxLinNumEdit, &QLineEdit::textChanged, this, &KsCaptureMonitor::_maxLineNumber); connect(&_readOnlyCB, &QCheckBox::stateChanged, this, &KsCaptureMonitor::_readOnly); connect(&_consolOutput, &QPlainTextEdit::textChanged, this, &KsCaptureMonitor::_argVModified); this->show(); } void KsCaptureMonitor::_maxLineNumber(const QString &test) { bool ok; int max = test.toInt(&ok); if (ok) _consolOutput.setMaximumBlockCount(max); } void KsCaptureMonitor::_readOnly(int state) { if (state == Qt::Checked) _consolOutput.setReadOnly(true); else _consolOutput.setReadOnly(false); } void KsCaptureMonitor::_argsReady(const QString &args) { _name.setText("Capture options:"); _consolOutput.setPlainText(args); _argsModified = false; } void KsCaptureMonitor::_argVModified() { _argsModified = true; } void KsCaptureMonitor::_printAllStandardError() { QProcess *_capture = (QProcess*) sender(); _consolOutput.moveCursor(QTextCursor::End); _consolOutput.insertPlainText(_capture->readAllStandardError()); _consolOutput.moveCursor(QTextCursor::End); QCoreApplication::processEvents(); } void KsCaptureMonitor::_printAllStandardOutput() { QProcess *_capture = (QProcess*) sender(); if (!_mergedChannels) return; _consolOutput.appendPlainText(_capture->readAllStandardOutput()); QCoreApplication::processEvents(); } /** * Connect the Capture monitor widget to the signals of the recording process. */ void KsCaptureMonitor::connectMe(QProcess *proc, KsCaptureControl *ctrl) { connect(proc, &QProcess::started, this, &KsCaptureMonitor::_captureStarted); /* Using the old Signal-Slot syntax because QProcess::finished has overloads. */ connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(_captureFinished(int, QProcess::ExitStatus))); connect(proc, &QProcess::readyReadStandardError, this, &KsCaptureMonitor::_printAllStandardError); connect(proc, &QProcess::readyReadStandardOutput, this, &KsCaptureMonitor::_printAllStandardOutput); connect(ctrl, &KsCaptureControl::argsReady, this, &KsCaptureMonitor::_argsReady); } void KsCaptureMonitor::_captureStarted() { _name.setText("Terminal output:"); _readOnlyCB.setCheckState(Qt::Checked); QCoreApplication::processEvents(); } void KsCaptureMonitor::_captureFinished(int exit, QProcess::ExitStatus status) { QProcess *_capture = (QProcess *)sender(); if (exit != 0 || status != QProcess::NormalExit) { QString errMessage("Capture process failed: "); errMessage += _capture->errorString(); _consolOutput.appendPlainText(errMessage); QCoreApplication::processEvents(); } } /** Print a message. */ void KsCaptureMonitor::print(const QString &message) { _consolOutput.appendPlainText(message); } /** @brief Create KsCaptureDialog widget. */ KsCaptureDialog::KsCaptureDialog(QWidget *parent) : QWidget(parent), _captureCtrl(this), _captureMon(this), _captureProc(this) { QString captureExe; this->setWindowTitle("Capture"); _layout.addWidget(&_captureCtrl); _layout.addWidget(&_captureMon); this->setLayout(&_layout); connect(&_captureCtrl._commandCheckBox, &QCheckBox::stateChanged, this, &KsCaptureDialog::_setChannelMode); connect(&_captureCtrl._captureButton, &QPushButton::pressed, this, &KsCaptureDialog::_capture); connect(&_captureCtrl._closeButton, &QPushButton::pressed, this, &KsCaptureDialog::close); if (KsUtils::isInstalled()) captureExe = QString(_INSTALL_PREFIX) + QString("/bin"); else captureExe = TRACECMD_BIN_DIR; captureExe += "/trace-cmd"; _captureProc.setProgram(captureExe); _captureMon.connectMe(&_captureProc, &_captureCtrl); } void KsCaptureDialog::_capture() { QStringList argv; int argc; if(_captureMon._argsModified) { argv = _captureMon.text().split(" "); } else { argv = _captureCtrl.getArgs(); } _captureMon.print("\n"); _captureMon.print(QString("trace-cmd " + argv.join(" ") + "\n")); _captureProc.setArguments(argv); _captureProc.start(); _captureProc.waitForFinished(); argc = argv.count(); for (int i = 0; i < argc; ++i) { if (argv[i] == "-o") { _sendOpenReq(argv[i + 1]); break; } } /* Reset the _argsModified flag. */ _captureMon._argsModified = false; } void KsCaptureDialog::_setChannelMode(int state) { if (state > 0) { _captureMon._mergedChannels = true; } else { _captureMon._mergedChannels = false; } } void KsCaptureDialog::_sendOpenReq(const QString &fileName) { QLocalSocket *socket = new QLocalSocket(this); socket->connectToServer("KSCapture", QIODevice::WriteOnly); if (socket->waitForConnected()) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); const QString message = fileName; out << quint32(message.size()); out << message; socket->write(block); socket->flush(); socket->disconnectFromServer(); } else { _captureMon.print(socket->errorString()); } } trace-cmd-2.8.3/kernel-shark/src/KsCaptureDialog.hpp000066400000000000000000000072631351617527000223110ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsCaptureDialog.hpp * @brief Dialog for trace data recording. */ #ifndef _KS_CAPTURE_H #define _KS_CAPTURE_H // Qt #include // KernelShark #include "KsWidgetsLib.hpp" /** * The KsCaptureControl class provides a control panel for the KernelShark * Capture dialog. */ class KsCaptureControl : public QWidget { Q_OBJECT public: explicit KsCaptureControl(QWidget *parent = 0); QStringList getArgs(); /** Get the name of the tracing data output file. */ QString outputFileName() const {return _outputLineEdit.text();} /** Set the name of the tracing data output file. */ void setOutputFileName(const QString &f) {_outputLineEdit.setText(f);} signals: /** This signal is emitted when the "Apply" button is pressed. */ void argsReady(const QString &args); private: tep_handle *_localTEP; KsEventsCheckBoxWidget _eventsWidget; QVBoxLayout _topLayout; QGridLayout _execLayout; QLabel _pluginsLabel, _outputLabel, _commandLabel; QLineEdit _outputLineEdit, _commandLineEdit; QToolBar _settingsToolBar, _controlToolBar; QComboBox _pluginsComboBox; QPushButton _importSettingsButton, _exportSettingsButton; QPushButton _outputBrowseButton; QString _lastFilePath; QStringList _getPlugins(); void _importSettings(); void _exportSettings(); void _browse(); void _apply(); public: /** * A Check box used to indicate if the output of the command needs to * be shown by the KsCaptureMonitor widget. */ QCheckBox _commandCheckBox; /** Capture button for the control panel. */ QPushButton _captureButton; /** Apply button for the control panel. */ QPushButton _applyButton; /** Close button for the control panel. */ QPushButton _closeButton; }; /** * The KsCaptureMonitor class provides a terminal-like widget for monitoring * the tracing data recording process. */ class KsCaptureMonitor : public QWidget { Q_OBJECT public: explicit KsCaptureMonitor(QWidget *parent = 0); /** Get the text shown by the widget. */ QString text() const {return _consolOutput.toPlainText();} /** Clear the text shown by the widget. */ void clear() {_consolOutput.clear();} void print(const QString &message); void connectMe(QProcess *proc, KsCaptureControl *ctrl); /** A flag indicating if the stdout and stderr channels are _merged. */ bool _mergedChannels; /** * A flag indicating, if the list of the command line arguments for trace-cmd * has been edited by the user. */ bool _argsModified; private: QVBoxLayout _layout; QToolBar _panel; QLabel _name, _space; QCheckBox _readOnlyCB; QLineEdit _maxLinNumEdit; QPlainTextEdit _consolOutput; void _argsReady(const QString &test); void _maxLineNumber(const QString &test); void _readOnly(int); void _argVModified(); void _captureStarted(); void _printAllStandardError(); void _printAllStandardOutput(); private slots: void _captureFinished(int, QProcess::ExitStatus); }; /** Default number of lines shown by the KsCaptureMonitor widget. */ #define KS_CAP_MON_MAX_LINE_NUM 200 /** * The KsCaptureDialog class provides a dialog for recording of tracing data. */ class KsCaptureDialog : public QWidget { Q_OBJECT public: explicit KsCaptureDialog(QWidget *parent = 0); /** Set the name of the tracing data output file. */ void setOutputFileName(const QString &f) { _captureCtrl.setOutputFileName(f); } private: QHBoxLayout _layout; KsCaptureControl _captureCtrl; KsCaptureMonitor _captureMon; QProcess _captureProc; void _capture(); void _setChannelMode(int state); void _sendOpenReq(const QString &fileName); }; #endif // _KS_CAPTURE_H trace-cmd-2.8.3/kernel-shark/src/KsDualMarker.cpp000066400000000000000000000211241351617527000216000ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsDualMarker.cpp * @brief KernelShark Dual Marker. */ #include "KsDualMarker.hpp" #include "KsGLWidget.hpp" /** * Reimplemented handler for mouse press events. Right mouse click events will * deselect the corresponding marker. */ void KsMarkerButton::mousePressEvent(QMouseEvent *e) { if(e->button() == Qt::RightButton) { emit deselect(); return; } QPushButton::mousePressEvent(e); } /** * @brief Create KsGraphMark object. * * @param s: The Identifier of the marker (state A or state B). */ KsGraphMark::KsGraphMark(DualMarkerState s) : _color(Qt::darkGreen), _state(s) { reset(); _mark._color << _color; } /** * @brief Create KsGraphMark object. * * @param s: The Identifier of the marker (state A or state B). * @param col: The color of the marker. */ KsGraphMark::KsGraphMark(DualMarkerState s, QColor col) : _color(col), _state(s) { reset(); _mark._color << _color; } /** Reset the marker. */ void KsGraphMark::reset() { _isSet = false; _bin = -1; _cpu = -1; _task = -1; _pos = 0; _mark._visible = false; } /** * @brief Set the marker. * * @param data: Input location for the Data Store object. * @param histo: Input location for the model descriptor. * @param pos: The index inside the data array this marker will points to. * @param cpuGraph: The index of the CPU Graph this marker points to. * @param taskGraph: The index of the Task Graph this marker points to. */ bool KsGraphMark::set(const KsDataStore &data, kshark_trace_histo *histo, size_t pos, int cpuGraph, int taskGraph) { uint8_t visFlags; _isSet = true; _pos = pos; _ts = data.rows()[_pos]->ts; visFlags = data.rows()[_pos]->visible; if ((visFlags & KS_TEXT_VIEW_FILTER_MASK) && (visFlags & KS_GRAPH_VIEW_FILTER_MASK)) _mark.setDashed(false); else _mark.setDashed(true); _cpu = cpuGraph; _task = taskGraph; if (_ts > histo->max || _ts < histo->min) { _bin = -1; _mark._visible = false; return false; } _bin = (_ts - histo->min)/histo->bin_size; setVisible(true); return true; } /** * @brief Use this function to update the marker when the state of the model * has changed. * * @param data: Input location for the Data Store object. * @param histo: Input location for the model descriptor. */ bool KsGraphMark::update(const KsDataStore &data, kshark_trace_histo *histo) { if (!_isSet) return false; return set(data, histo, this->_pos, this->_cpu, this->_task); } /** Unset the Marker and make it invisible. */ void KsGraphMark::remove() { _isSet = false; setVisible(false); } /** An operator for getting the opposite state of the marker identifier. */ DualMarkerState operator!(const DualMarkerState &state) { if (state == DualMarkerState::B) return DualMarkerState::A; return DualMarkerState::B; } /** @brief Create a Dual Marker State Machine. */ KsDualMarkerSM::KsDualMarkerSM(QWidget *parent) : QWidget(parent), _buttonA("Marker A", this), _buttonB("Marker B", this), _labelDeltaDescr(" A,B Delta: ", this), _markA(DualMarkerState::A, Qt::darkGreen), _markB(DualMarkerState::B, Qt::darkCyan), _scCtrlA(this), _scCtrlB(this) { QString styleSheetA, styleSheetB; _buttonA.setFixedWidth(STRING_WIDTH(" Marker A ")); _buttonB.setFixedWidth(STRING_WIDTH(" Marker B ")); for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) { l->setFrameStyle(QFrame::Panel | QFrame::Sunken); l->setStyleSheet("QLabel {background-color : white;}"); l->setTextInteractionFlags(Qt::TextSelectableByMouse); l->setFixedWidth(FONT_WIDTH * 16); } styleSheetA = "background : " + _markA._color.name() + "; color : white"; _stateA = new QState; _stateA->setObjectName("A"); _stateA->assignProperty(&_buttonA, "styleSheet", styleSheetA); _stateA->assignProperty(&_buttonB, "styleSheet", "color : rgb(70, 70, 70)"); styleSheetB = "background : " + _markB._color.name() + "; color : white"; _stateB = new QState; _stateB->setObjectName("B"); _stateB->assignProperty(&_buttonA, "styleSheet", "color : rgb(70, 70, 70)"); _stateB->assignProperty(&_buttonB, "styleSheet", styleSheetB); /* Define transitions from State A to State B. */ _stateA->addTransition(this, &KsDualMarkerSM::machineToB, _stateB); _scCtrlA.setKey(Qt::CTRL + Qt::Key_A); _stateA->addTransition(&_scCtrlB, &QShortcut::activated, _stateB); connect(&_scCtrlA, &QShortcut::activated, this, &KsDualMarkerSM::_doStateA); _stateA->addTransition(&_buttonB, &QPushButton::clicked, _stateB); connect(&_buttonB, &QPushButton::clicked, this, &KsDualMarkerSM::_doStateB); connect(&_buttonB, &KsMarkerButton::deselect, this, &KsDualMarkerSM::deselectB); /* Define transitions from State B to State A. */ _stateB->addTransition(this, &KsDualMarkerSM::machineToA, _stateA); _scCtrlB.setKey(Qt::CTRL + Qt::Key_B); _stateB->addTransition(&_scCtrlA, &QShortcut::activated, _stateA); connect(&_scCtrlB, &QShortcut::activated, this, &KsDualMarkerSM::_doStateB); _stateB->addTransition(&_buttonA, &QPushButton::clicked, _stateA); connect(&_buttonA, &QPushButton::clicked, this, &KsDualMarkerSM::_doStateA); connect(&_buttonA, &KsMarkerButton::deselect, this, &KsDualMarkerSM::deselectA); _machine.addState(_stateA); _machine.addState(_stateB); _machine.setInitialState(_stateA); _markState = DualMarkerState::A; _machine.start(); } /** * Reset the Mark A and Mark B and clear the information shown by the Marker's * toolbar. */ void KsDualMarkerSM::reset() { _markA.reset(); _markB.reset(); _labelMA.setText(""); _labelMB.setText(""); _labelDelta.setText(""); } /** Restart the Dual Marker State Machine. */ void KsDualMarkerSM::restart() { _machine.stop(); reset(); _markState = DualMarkerState::A; _machine.start(); } void KsDualMarkerSM::_doStateA() { if (_markState != DualMarkerState::A) { _markState = DualMarkerState::A; emit markSwitchForView(); } else if (activeMarker()._isSet) { emit updateView(activeMarker()._pos, true); emit updateGraph(activeMarker()._pos); } } void KsDualMarkerSM::_doStateB() { if (_markState != DualMarkerState::B) { _markState = DualMarkerState::B; emit markSwitchForView(); } else if (activeMarker()._isSet) { emit updateView(activeMarker()._pos, true); emit updateGraph(activeMarker()._pos); } } /** Get the Graph marker associated with a given state. */ KsGraphMark &KsDualMarkerSM::getMarker(DualMarkerState s) { if (s == DualMarkerState::A) return _markA; return _markB; } /** Position all buttons and labels of the Dual Marker in a toolbar. */ void KsDualMarkerSM::placeInToolBar(QToolBar *tb) { tb->addWidget(new QLabel(" ")); tb->addWidget(&_buttonA); tb->addWidget(&_labelMA); tb->addSeparator(); tb->addWidget(new QLabel(" ")); tb->addWidget(&_buttonB); tb->addWidget(&_labelMB); tb->addSeparator(); tb->addWidget(&_labelDeltaDescr); tb->addWidget(&_labelDelta); } /** Set the state of the Dual Marker State Machine. */ void KsDualMarkerSM::setState(DualMarkerState st) { if (st == _markState) return; if (st == DualMarkerState::A) { emit machineToA(); _doStateA(); } if (st == DualMarkerState::B) { emit machineToB(); _doStateB(); } } /** * @brief Use this function to update the two marker when the state of the * model has changed. * * @param data: Input location for the Data Store object. * @param glw: Input location for the OpenGL widget object. */ void KsDualMarkerSM::updateMarkers(const KsDataStore &data, KsGLWidget *glw) { if(_markA.update(data, glw->model()->histo())) glw->setMark(&_markA); if(_markB.update(data, glw->model()->histo())) glw->setMark(&_markB); updateLabels(); } /** * @brief Use this function to update the labels when the state of the model * has changed. */ void KsDualMarkerSM::updateLabels() { char separator(' '); int precision(6); // 1 microsecond precision. auto lamSetTimeLabel = [&precision, &separator] (QLabel &l, int64_t t) { QString time = KsUtils::Ts2String(t, precision); int i = time.indexOf('.') + 4; /* Insert separators for milliseconds amd microseconds. */ while (i < time.size()) { time.insert(i, separator); i = i + 4; } l.setText(time); }; // Marker A if (_markA._isSet) lamSetTimeLabel(_labelMA, _markA._ts); else _labelMA.clear(); // Marker B if (_markB._isSet) lamSetTimeLabel(_labelMB, _markB._ts); else _labelMB.clear(); // Delta if (_markA._isSet && _markB._isSet) { precision = 9; // 1 nanoseconds precision. lamSetTimeLabel(_labelDelta, _markB._ts - _markA._ts); } else { _labelDelta.clear(); } } trace-cmd-2.8.3/kernel-shark/src/KsDualMarker.hpp000066400000000000000000000110321351617527000216020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsDualMarker.hpp * @brief KernelShark Dual Marker. */ #ifndef _KS_DUAL_MARKER_H #define _KS_DUAL_MARKER_H // Qt #include // KernelShark #include "KsUtils.hpp" #include "KsPlotTools.hpp" /** * The Marker Button class provides a button that deselect the corresponding * marker in the case of a Right mouse click. */ class KsMarkerButton : public QPushButton { Q_OBJECT public: /** * @brief Create a default button. */ explicit KsMarkerButton(QWidget *parent = nullptr) : QPushButton(parent) {} /** * @brief Create a button with text. */ explicit KsMarkerButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent) {} void mousePressEvent(QMouseEvent *e); signals: /** * This signal is emitted when the button is click by the Right mouse * button. */ void deselect(); }; class KsGLWidget; /** The KsGraphMark represents a marker for KernelShark GUI. */ class KsGraphMark : public QObject { Q_OBJECT public: KsGraphMark() = delete; KsGraphMark(DualMarkerState s); KsGraphMark(DualMarkerState s, QColor col); void reset(); bool set(const KsDataStore &data, kshark_trace_histo *histo, size_t pos, int cpuGraph, int taskGraph); bool update(const KsDataStore &data, kshark_trace_histo *histo); /** Is this marker visible. */ bool isVisible() const {return _mark._visible;} /** Draw this marker. */ void draw() const {_mark.draw();} /** Set the visiblity of the marker. */ void setVisible(bool v) {_mark._visible = v;} void remove(); public: /** Is this marker set. */ bool _isSet; /** The number of the bin this marker points to. */ int _bin; /** The index of the CPU Graph this marker points to. */ int _cpu; /** The index of the Task Graph this marker points to. */ int _task; /** The index inside the data array this marker points to. */ size_t _pos; /** The timestamp of the marker. */ uint64_t _ts; /** The RGB color of the marker. */ QColor _color; /** The Identifier of the marker (A or B). */ const DualMarkerState _state; /** The graphical element of this marker. */ KsPlot::Mark _mark; }; DualMarkerState operator !(const DualMarkerState &state); /** * The DualMarkerState represents the State Machine of the KernelShark GUI * Dual Marker. */ class KsDualMarkerSM : public QWidget { Q_OBJECT public: explicit KsDualMarkerSM(QWidget *parent = nullptr); void reset(); void restart(); void placeInToolBar(QToolBar *tb); /** Get the Identifier of the current state of the State Machine. */ DualMarkerState getState() const {return _markState;} void setState(DualMarkerState st); KsGraphMark &getMarker(DualMarkerState s); /** Get the active marker. */ KsGraphMark &activeMarker() {return getMarker(_markState);} /** Get the passive marker. */ KsGraphMark &passiveMarker() {return getMarker(!_markState);} /** Get the marker A. */ KsGraphMark &markerA() {return _markA;} /** Get the marker B. */ KsGraphMark &markerB() {return _markB;} /** Get a pointer to the State A object. */ QState *stateAPtr() {return _stateA;} /** Get a pointer to the State B object. */ QState *stateBPtr() {return _stateB;} void updateMarkers(const KsDataStore &data, KsGLWidget *glw); void updateLabels(); signals: /** * This signal is emitted when the Table View has to switch the color * of the selected row. */ void markSwitchForView(); /** * This signal is emitted when the Table View has to show a different * entry (row). */ void updateView(size_t pos, bool mark); /** * This signal is emitted when the Graph mark has to show a different * entry. */ void updateGraph(size_t pos); /** * This signal is emitted when the State Machine has to switch to * state A. */ void machineToA(); /** * This signal is emitted when the State Machine has to switch to * state B. */ void machineToB(); /** * This signal is used to re-emitted the deselect signal of the * Marker A button. */ void deselectA(); /** * This signal is used to re-emitted the deselect signal of the * Marker B button. */ void deselectB(); private: KsMarkerButton _buttonA; KsMarkerButton _buttonB; QLabel _labelMA, _labelMB, _labelDelta; QLabel _labelDeltaDescr; QState *_stateA; QState *_stateB; QStateMachine _machine; DualMarkerState _markState; KsGraphMark _markA, _markB; QShortcut _scCtrlA, _scCtrlB; void _doStateA(); void _doStateB(); }; #endif trace-cmd-2.8.3/kernel-shark/src/KsGLWidget.cpp000066400000000000000000000523121351617527000212220ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsGLWidget.cpp * @brief OpenGL widget for plotting trace graphs. */ // OpenGL #include #include // KernelShark #include "KsGLWidget.hpp" #include "KsUtils.hpp" #include "KsPlugins.hpp" #include "KsDualMarker.hpp" /** Create a default (empty) OpenGL widget. */ KsGLWidget::KsGLWidget(QWidget *parent) : QOpenGLWidget(parent), _hMargin(20), _vMargin(30), _vSpacing(20), _mState(nullptr), _data(nullptr), _rubberBand(QRubberBand::Rectangle, this), _rubberBandOrigin(0, 0), _dpr(1) { setMouseTracking(true); /* * Using the old Signal-Slot syntax because QWidget::update has * overloads. */ connect(&_model, SIGNAL(modelReset()), this, SLOT(update())); } KsGLWidget::~KsGLWidget() { for (auto &g: _graphs) delete g; } /** Reimplemented function used to set up all required OpenGL resources. */ void KsGLWidget::initializeGL() { _dpr = QApplication::desktop()->devicePixelRatio(); ksplot_init_opengl(_dpr); } /** * Reimplemented function used to reprocess all graphs whene the widget has * been resized. */ void KsGLWidget::resizeGL(int w, int h) { ksplot_resize_opengl(w, h); if(!_data) return; /* * From the size of the widget, calculate the number of bins. * One bin will correspond to one pixel. */ int nBins = width() - _hMargin * 2; /* * Reload the data. The range of the histogram is the same * but the number of bins changes. */ ksmodel_set_bining(_model.histo(), nBins, _model.histo()->min, _model.histo()->max); _model.fill(_data->rows(), _data->size()); } /** Reimplemented function used to plot trace graphs. */ void KsGLWidget::paintGL() { float size = 1.5 * _dpr; glClear(GL_COLOR_BUFFER_BIT); /* Draw the time axis. */ if(_data) _drawAxisX(size); /* Process and draw all graphs by using the built-in logic. */ _makeGraphs(_cpuList, _taskList); for (auto const &g: _graphs) g->draw(size); /* Process and draw all plugin-specific shapes. */ _makePluginShapes(_cpuList, _taskList); while (!_shapes.empty()) { auto s = _shapes.front(); _shapes.pop_front(); s->_size = size; s->draw(); delete s; } /* * Update and draw the markers. Make sure that the active marker * is drawn on top. */ _mState->updateMarkers(*_data, this); _mState->passiveMarker().draw(); _mState->activeMarker().draw(); } /** Reset (empty) the widget. */ void KsGLWidget::reset() { _cpuList = {}; _taskList = {}; _data = nullptr; _model.reset(); } /** Reimplemented event handler used to receive mouse press events. */ void KsGLWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { _posMousePress = _posInRange(event->pos().x()); _rangeBoundInit(_posMousePress); } } int KsGLWidget::_getLastTask(struct kshark_trace_histo *histo, int bin, int cpu) { kshark_context *kshark_ctx(nullptr); kshark_entry_collection *col; int pid; if (!kshark_instance(&kshark_ctx)) return KS_EMPTY_BIN; col = kshark_find_data_collection(kshark_ctx->collections, KsUtils::matchCPUVisible, cpu); for (int b = bin; b >= 0; --b) { pid = ksmodel_get_pid_back(histo, b, cpu, false, col, nullptr); if (pid >= 0) return pid; } return ksmodel_get_pid_back(histo, LOWER_OVERFLOW_BIN, cpu, false, col, nullptr); } int KsGLWidget::_getLastCPU(struct kshark_trace_histo *histo, int bin, int pid) { kshark_context *kshark_ctx(nullptr); kshark_entry_collection *col; int cpu; if (!kshark_instance(&kshark_ctx)) return KS_EMPTY_BIN; col = kshark_find_data_collection(kshark_ctx->collections, kshark_match_pid, pid); for (int b = bin; b >= 0; --b) { cpu = ksmodel_get_cpu_back(histo, b, pid, false, col, nullptr); if (cpu >= 0) return cpu; } return ksmodel_get_cpu_back(histo, LOWER_OVERFLOW_BIN, pid, false, col, nullptr); } /** Reimplemented event handler used to receive mouse move events. */ void KsGLWidget::mouseMoveEvent(QMouseEvent *event) { int bin, cpu, pid; size_t row; bool ret; if (_rubberBand.isVisible()) _rangeBoundStretched(_posInRange(event->pos().x())); bin = event->pos().x() - _hMargin; cpu = getPlotCPU(event->pos()); pid = getPlotPid(event->pos()); ret = _find(bin, cpu, pid, 5, false, &row); if (ret) { emit found(row); } else { if (cpu >= 0) { pid = _getLastTask(_model.histo(), bin, cpu); } if (pid > 0) { cpu = _getLastCPU(_model.histo(), bin, pid); } emit notFound(ksmodel_bin_ts(_model.histo(), bin), cpu, pid); } } /** Reimplemented event handler used to receive mouse release events. */ void KsGLWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { size_t posMouseRel = _posInRange(event->pos().x()); int min, max; if (_posMousePress < posMouseRel) { min = _posMousePress - _hMargin; max = posMouseRel - _hMargin; } else { max = _posMousePress - _hMargin; min = posMouseRel - _hMargin; } _rangeChanged(min, max); } } /** Reimplemented event handler used to receive mouse double click events. */ void KsGLWidget::mouseDoubleClickEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) _findAndSelect(event); } /** Reimplemented event handler used to receive mouse wheel events. */ void KsGLWidget::wheelEvent(QWheelEvent * event) { int zoomFocus; if (_mState->activeMarker()._isSet && _mState->activeMarker().isVisible()) { /* * Use the position of the marker as a focus point for the * zoom. */ zoomFocus = _mState->activeMarker()._bin; } else { /* * Use the position of the mouse as a focus point for the * zoom. */ zoomFocus = event->pos().x() - _hMargin; } if (event->delta() > 0) { _model.zoomIn(.05, zoomFocus); } else { _model.zoomOut(.05, zoomFocus); } _mState->updateMarkers(*_data, this); } /** Reimplemented event handler used to receive key press events. */ void KsGLWidget::keyPressEvent(QKeyEvent *event) { if (event->isAutoRepeat()) return; switch (event->key()) { case Qt::Key_Plus: emit zoomIn(); return; case Qt::Key_Minus: emit zoomOut(); return; case Qt::Key_Left: emit scrollLeft(); return; case Qt::Key_Right: emit scrollRight(); return; default: QOpenGLWidget::keyPressEvent(event); return; } } /** Reimplemented event handler used to receive key release events. */ void KsGLWidget::keyReleaseEvent(QKeyEvent *event) { if (event->isAutoRepeat()) return; if(event->key() == Qt::Key_Plus || event->key() == Qt::Key_Minus || event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) { emit stopUpdating(); return; } QOpenGLWidget::keyPressEvent(event); return; } /** * @brief Load and show trace data. * * @param data: Input location for the KsDataStore object. * KsDataStore::loadDataFile() must be called first. */ void KsGLWidget::loadData(KsDataStore *data) { uint64_t tMin, tMax; int nCPUs, nBins; _data = data; /* * From the size of the widget, calculate the number of bins. * One bin will correspond to one pixel. */ nBins = width() - _hMargin * 2; nCPUs = tep_get_cpus(_data->tep()); _model.reset(); /* Now load the entire set of trace data. */ tMin = _data->rows()[0]->ts; tMax = _data->rows()[_data->size() - 1]->ts; ksmodel_set_bining(_model.histo(), nBins, tMin, tMax); _model.fill(_data->rows(), _data->size()); /* Make a default CPU list. All CPUs will be plotted. */ _cpuList = {}; for (int i = 0; i < nCPUs; ++i) _cpuList.append(i); /* Make a default task list. No tasks will be plotted. */ _taskList = {}; loadColors(); _makeGraphs(_cpuList, _taskList); } /** * Create a Hash table of Rainbow colors. The sorted Pid values are mapped to * the palette of Rainbow colors. */ void KsGLWidget::loadColors() { _pidColors.clear(); _pidColors = KsPlot::getTaskColorTable(); _cpuColors.clear(); _cpuColors = KsPlot::getCPUColorTable(); } /** * Position the graphical elements of the marker according to the current * position of the graphs inside the GL widget. */ void KsGLWidget::setMark(KsGraphMark *mark) { mark->_mark.setDPR(_dpr); mark->_mark.setX(mark->_bin + _hMargin); mark->_mark.setY(_vMargin / 2 + 2, height() - _vMargin); if (mark->_cpu >= 0) { mark->_mark.setCPUY(_graphs[mark->_cpu]->getBase()); mark->_mark.setCPUVisible(true); } else { mark->_mark.setCPUVisible(false); } if (mark->_task >= 0) { mark->_mark.setTaskY(_graphs[mark->_task]->getBase()); mark->_mark.setTaskVisible(true); } else { mark->_mark.setTaskVisible(false); } } /** * @brief Check if a given KernelShark entry is ploted. * * @param e: Input location for the KernelShark entry. * @param graphCPU: Output location for index of the CPU graph to which this * entry belongs. If such a graph does not exist the outputted * value is "-1". * @param graphTask: Output location for index of the Task graph to which this * entry belongs. If such a graph does not exist the * outputted value is "-1". */ void KsGLWidget::findGraphIds(const kshark_entry &e, int *graphCPU, int *graphTask) { int graph(0); bool cpuFound(false), taskFound(false); /* * Loop over all CPU graphs and try to find the one that * contains the entry. */ for (auto const &c: _cpuList) { if (c == e.cpu) { cpuFound = true; break; } ++graph; } if (cpuFound) *graphCPU = graph; else *graphCPU = -1; /* * Loop over all Task graphs and try to find the one that * contains the entry. */ graph = _cpuList.count(); for (auto const &p: _taskList) { if (p == e.pid) { taskFound = true; break; } ++graph; } if (taskFound) *graphTask = graph; else *graphTask = -1; } void KsGLWidget::_drawAxisX(float size) { KsPlot::Point a0(_hMargin, _vMargin / 4), a1(_hMargin, _vMargin / 2); KsPlot::Point b0(width() / 2, _vMargin / 4), b1(width() / 2, _vMargin / 2); KsPlot::Point c0(width() - _hMargin, _vMargin / 4), c1(width() - _hMargin, _vMargin / 2); a0._size = c0._size = _dpr; a0.draw(); c0.draw(); KsPlot::drawLine(a0, a1, {}, size); KsPlot::drawLine(b0, b1, {}, size); KsPlot::drawLine(c0, c1, {}, size); KsPlot::drawLine(a0, c0, {}, size); } void KsGLWidget::_makeGraphs(QVector cpuList, QVector taskList) { /* The very first thing to do is to clean up. */ for (auto &g: _graphs) delete g; _graphs.resize(0); if (!_data || !_data->size()) return; auto lamAddGraph = [&](KsPlot::Graph *graph) { /* * Calculate the base level of the CPU graph inside the widget. * Remember that the "Y" coordinate is inverted. */ if (!graph) return; int base = _vMargin + _vSpacing * _graphs.count() + KS_GRAPH_HEIGHT * (_graphs.count() + 1); graph->setBase(base); _graphs.append(graph); }; /* Create CPU graphs according to the cpuList. */ for (auto const &cpu: cpuList) lamAddGraph(_newCPUGraph(cpu)); /* Create Task graphs taskList to the taskList. */ for (auto const &pid: taskList) lamAddGraph(_newTaskGraph(pid)); } void KsGLWidget::_makePluginShapes(QVector cpuList, QVector taskList) { kshark_context *kshark_ctx(nullptr); kshark_event_handler *evt_handlers; KsCppArgV cppArgv; if (!kshark_instance(&kshark_ctx)) return; cppArgv._histo = _model.histo(); cppArgv._shapes = &_shapes; for (int g = 0; g < cpuList.count(); ++g) { cppArgv._graph = _graphs[g]; evt_handlers = kshark_ctx->event_handlers; while (evt_handlers) { evt_handlers->draw_func(cppArgv.toC(), cpuList[g], KSHARK_PLUGIN_CPU_DRAW); evt_handlers = evt_handlers->next; } } for (int g = 0; g < taskList.count(); ++g) { cppArgv._graph = _graphs[cpuList.count() + g]; evt_handlers = kshark_ctx->event_handlers; while (evt_handlers) { evt_handlers->draw_func(cppArgv.toC(), taskList[g], KSHARK_PLUGIN_TASK_DRAW); evt_handlers = evt_handlers->next; } } } KsPlot::Graph *KsGLWidget::_newCPUGraph(int cpu) { /* The CPU graph needs to know only the colors of the tasks. */ KsPlot::Graph *graph = new KsPlot::Graph(_model.histo(), &_pidColors, &_pidColors); graph->setZeroSuppressed(true); kshark_context *kshark_ctx(nullptr); kshark_entry_collection *col; if (!kshark_instance(&kshark_ctx)) return nullptr; graph->setHMargin(_hMargin); graph->setHeight(KS_GRAPH_HEIGHT); col = kshark_find_data_collection(kshark_ctx->collections, KsUtils::matchCPUVisible, cpu); graph->setDataCollectionPtr(col); graph->fillCPUGraph(cpu); return graph; } KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid) { /* * The Task graph needs to know the colors of the tasks and the colors * of the CPUs. */ KsPlot::Graph *graph = new KsPlot::Graph(_model.histo(), &_pidColors, &_cpuColors); kshark_context *kshark_ctx(nullptr); kshark_entry_collection *col; if (!kshark_instance(&kshark_ctx)) return nullptr; graph->setHMargin(_hMargin); graph->setHeight(KS_GRAPH_HEIGHT); col = kshark_find_data_collection(kshark_ctx->collections, kshark_match_pid, pid); if (!col) { /* * If a data collection for this task does not exist, * register a new one. */ col = kshark_register_data_collection(kshark_ctx, _data->rows(), _data->size(), kshark_match_pid, pid, 25); } /* * Data collections are efficient only when used on graphs, having a * lot of empty bins. * TODO: Determine the optimal criteria to decide whether to use or * not use data collection for this graph. */ if (_data->size() < 1e6 && col && col->size && _data->size() / col->size < 100) { /* * No need to use collection in this case. Free the collection * data, but keep the collection registered. This will prevent * from recalculating the same collection next time when this * task is ploted. */ kshark_reset_data_collection(col); } graph->setDataCollectionPtr(col); graph->fillTaskGraph(pid); return graph; } /** * @brief Find the KernelShark entry under the the cursor. * * @param point: The position of the cursor. * @param variance: The variance of the position (range) in which an entry will * be searched. * @param joined: It True, search also in the associated CPU/Task graph. * @param index: Output location for the index of the entry under the cursor. * If no entry has been found, the outputted value is zero. * * @returns True, if an entry has been found, otherwise False. */ bool KsGLWidget::find(const QPoint &point, int variance, bool joined, size_t *index) { /* * Get the bin, pid and cpu numbers. * Remember that one bin corresponds to one pixel. */ int bin = point.x() - _hMargin; int cpu = getPlotCPU(point); int pid = getPlotPid(point); return _find(bin, cpu, pid, variance, joined, index); } int KsGLWidget::_getNextCPU(int pid, int bin) { kshark_context *kshark_ctx(nullptr); kshark_entry_collection *col; int cpu; if (!kshark_instance(&kshark_ctx)) return KS_EMPTY_BIN; col = kshark_find_data_collection(kshark_ctx->collections, kshark_match_pid, pid); if (!col) return KS_EMPTY_BIN; for (int i = bin; i < _model.histo()->n_bins; ++i) { cpu = ksmodel_get_cpu_front(_model.histo(), i, pid, false, col, nullptr); if (cpu >= 0) return cpu; } return KS_EMPTY_BIN; } bool KsGLWidget::_find(int bin, int cpu, int pid, int variance, bool joined, size_t *row) { int hSize = _model.histo()->n_bins; ssize_t found; if (bin < 0 || bin > hSize || (cpu < 0 && pid < 0)) { /* * The click is outside of the range of the histogram. * Do nothing. */ *row = 0; return false; } auto lamGetEntryByCPU = [&](int b) { /* Get the first data entry in this bin. */ found = ksmodel_first_index_at_cpu(_model.histo(), b, cpu); if (found < 0) { /* * The bin is empty or the entire connect of the bin * has been filtered. */ return false; } *row = found; return true; }; auto lamGetEntryByPid = [&](int b) { /* Get the first data entry in this bin. */ found = ksmodel_first_index_at_pid(_model.histo(), b, pid); if (found < 0) { /* * The bin is empty or the entire connect of the bin * has been filtered. */ return false; } *row = found; return true; }; auto lamFindEntryByCPU = [&](int b) { /* * The click is over the CPU graphs. First try the exact * match. */ if (lamGetEntryByCPU(bin)) return true; /* Now look for a match, nearby the position of the click. */ for (int i = 1; i < variance; ++i) { if (bin + i <= hSize && lamGetEntryByCPU(bin + i)) return true; if (bin - i >= 0 && lamGetEntryByCPU(bin - i)) return true; } *row = 0; return false; }; auto lamFindEntryByPid = [&](int b) { /* * The click is over the Task graphs. First try the exact * match. */ if (lamGetEntryByPid(bin)) return true; /* Now look for a match, nearby the position of the click. */ for (int i = 1; i < variance; ++i) { if ((bin + i <= hSize) && lamGetEntryByPid(bin + i)) return true; if ((bin - i >= 0) && lamGetEntryByPid(bin - i)) return true; } *row = 0; return false; }; if (cpu >= 0) return lamFindEntryByCPU(bin); if (pid >= 0) { bool ret = lamFindEntryByPid(bin); /* * If no entry has been found and we have a joined search, look * for an entry on the next CPU used by this task. */ if (!ret && joined) { cpu = _getNextCPU(pid, bin); ret = lamFindEntryByCPU(bin); } return ret; } *row = 0; return false; } bool KsGLWidget::_findAndSelect(QMouseEvent *event) { size_t row; bool found = find(event->pos(), 10, true, &row); if (found) { emit select(row); emit updateView(row, true); } return found; } void KsGLWidget::_rangeBoundInit(int x) { /* * Set the origin of the rubber band that shows the new range. Only * the X coordinate of the origin matters. The Y coordinate will be * set to zero. */ _rubberBandOrigin.rx() = x; _rubberBandOrigin.ry() = 0; _rubberBand.setGeometry(_rubberBandOrigin.x(), _rubberBandOrigin.y(), 0, 0); /* Make the rubber band visible, although its size is zero. */ _rubberBand.show(); } void KsGLWidget::_rangeBoundStretched(int x) { QPoint pos; pos.rx() = x; pos.ry() = this->height(); /* * Stretch the rubber band between the origin position and the current * position of the mouse. Only the X coordinate matters. The Y * coordinate will be the height of the widget. */ if (_rubberBandOrigin.x() < pos.x()) { _rubberBand.setGeometry(QRect(_rubberBandOrigin.x(), _rubberBandOrigin.y(), pos.x() - _rubberBandOrigin.x(), pos.y() - _rubberBandOrigin.y())); } else { _rubberBand.setGeometry(QRect(pos.x(), _rubberBandOrigin.y(), _rubberBandOrigin.x() - pos.x(), pos.y() - _rubberBandOrigin.y())); } } void KsGLWidget::_rangeChanged(int binMin, int binMax) { size_t nBins = _model.histo()->n_bins; int binMark = _mState->activeMarker()._bin; uint64_t min, max; /* The rubber band is no longer needed. Make it invisible. */ _rubberBand.hide(); if ( (binMax - binMin) < 4) { /* Most likely this is an accidental click. Do nothing. */ return; } /* * Calculate the new range of the histogram. The number of bins will * stay the same. */ min = ksmodel_bin_ts(_model.histo(), binMin); max = ksmodel_bin_ts(_model.histo(), binMax); if (max - min < nBins) { /* * The range cannot be smaller than the number of bins. * Do nothing. */ return; } /* Recalculate the model and update the markers. */ ksmodel_set_bining(_model.histo(), nBins, min, max); _model.fill(_data->rows(), _data->size()); _mState->updateMarkers(*_data, this); /* * If the Marker is inside the new range, make sure that it will * be visible in the table. Note that for this check we use the * bin number of the marker, retrieved before its update. */ if (_mState->activeMarker()._isSet && binMark < binMax && binMark > binMin) { emit updateView(_mState->activeMarker()._pos, true); return; } /* * Find the first bin which contains unfiltered data and send a signal * to the View widget to make this data visible. */ for (int bin = 0; bin < _model.histo()->n_bins; ++bin) { int64_t row = ksmodel_first_index_at_bin(_model.histo(), bin); if (row != KS_EMPTY_BIN && (_data->rows()[row]->visible & KS_TEXT_VIEW_FILTER_MASK)) { emit updateView(row, false); return; } } } int KsGLWidget::_posInRange(int x) { int posX; if (x < _hMargin) posX = _hMargin; else if (x > (width() - _hMargin)) posX = width() - _hMargin; else posX = x; return posX; } /** Get the CPU Id of the Graph plotted at given position. */ int KsGLWidget::getPlotCPU(const QPoint &point) { int cpuId, y = point.y(); if (_cpuList.count() == 0) return -1; cpuId = (y - _vMargin + _vSpacing / 2) / (_vSpacing + KS_GRAPH_HEIGHT); if (cpuId < 0 || cpuId >= _cpuList.count()) return -1; return _cpuList[cpuId]; } /** Get the CPU Id of the Graph plotted at given position. */ int KsGLWidget::getPlotPid(const QPoint &point) { int pidId, y = point.y(); if (_taskList.count() == 0) return -1; pidId = (y - _vMargin - _cpuList.count()*(KS_GRAPH_HEIGHT + _vSpacing) + _vSpacing / 2) / (_vSpacing + KS_GRAPH_HEIGHT); if (pidId < 0 || pidId >= _taskList.count()) return -1; return _taskList[pidId]; } trace-cmd-2.8.3/kernel-shark/src/KsGLWidget.hpp000066400000000000000000000112301351617527000212210ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsGLWidget.hpp * @brief OpenGL widget for plotting trace graphs. */ #ifndef _KS_GLWIDGET_H #define _KS_GLWIDGET_H // Qt #include // KernelShark #include "KsUtils.hpp" #include "KsPlotTools.hpp" #include "KsModels.hpp" #include "KsDualMarker.hpp" /** * The KsGLWidget class provides a widget for rendering OpenGL graphics used * to plot trace graphs. */ class KsGLWidget : public QOpenGLWidget { Q_OBJECT public: explicit KsGLWidget(QWidget *parent = NULL); ~KsGLWidget(); void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; void reset(); /** Reprocess all graphs. */ void update() {resizeGL(width(), height());} void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent *event); void wheelEvent(QWheelEvent * event); void keyPressEvent(QKeyEvent *event); void keyReleaseEvent(QKeyEvent *event); void loadData(KsDataStore *data); void loadColors(); /** * Provide the widget with a pointer to the Dual Marker state machine * object. */ void setMarkerSM(KsDualMarkerSM *m) {_mState = m;} /** Get the KsGraphModel object. */ KsGraphModel *model() {return &_model;} /** Get the number of CPU graphs. */ int cpuGraphCount() const {return _cpuList.count();} /** Get the number of Task graphs. */ int taskGraphCount() const {return _taskList.count();} /** Get the total number of graphs. */ int graphCount() const {return _cpuList.count() + _taskList.count();} /** Get the height of the widget. */ int height() const { return graphCount() * (KS_GRAPH_HEIGHT + _vSpacing) + _vMargin * 2; } /** Get the device pixel ratio. */ int dpr() const {return _dpr;} /** Get the size of the horizontal margin space. */ int hMargin() const {return _hMargin;} /** Get the size of the vertical margin space. */ int vMargin() const {return _vMargin;} /** Get the size of the vertical spaceing between the graphs. */ int vSpacing() const {return _vSpacing;} void setMark(KsGraphMark *mark); void findGraphIds(const kshark_entry &e, int *graphCPU, int *graphTask); bool find(const QPoint &point, int variance, bool joined, size_t *index); int getPlotCPU(const QPoint &point); int getPlotPid(const QPoint &point); /** CPUs to be plotted. */ QVector _cpuList; /** Tasks to be plotted. */ QVector _taskList; signals: /** * This signal is emitted when the mouse moves over a visible * KernelShark entry. */ void found(size_t pos); /** * This signal is emitted when the mouse moves but there is no visible * KernelShark entry under the cursor. */ void notFound(uint64_t ts, int cpu, int pid); /** This signal is emitted when the Plus key is pressed. */ void zoomIn(); /** This signal is emitted when the Minus key is pressed. */ void zoomOut(); /** This signal is emitted when the Left Arrow key is pressed. */ void scrollLeft(); /** This signal is emitted when the Right Arrow key is pressed. */ void scrollRight(); /** * This signal is emitted when one of the 4 Action keys is release * (after being pressed). */ void stopUpdating(); /** * This signal is emitted in the case of a double click over a visible * KernelShark entry. */ void select(size_t pos); /** * This signal is emitted when the KsTraceViewer widget needs to be * updated. */ void updateView(size_t pos, bool mark); private: QVector _graphs; KsPlot::PlotObjList _shapes; KsPlot::ColorTable _pidColors; KsPlot::ColorTable _cpuColors; int _hMargin, _vMargin; unsigned int _vSpacing; KsGraphModel _model; KsDualMarkerSM *_mState; KsDataStore *_data; QRubberBand _rubberBand; QPoint _rubberBandOrigin; size_t _posMousePress; bool _keyPressed; int _dpr; void _drawAxisX(float size); void _makeGraphs(QVector cpuMask, QVector taskMask); KsPlot::Graph *_newCPUGraph(int cpu); KsPlot::Graph *_newTaskGraph(int pid); void _makePluginShapes(QVector cpuMask, QVector taskMask); int _posInRange(int x); void _rangeBoundInit(int x); void _rangeBoundStretched(int x); void _rangeChanged(int binMin, int binMax); bool _findAndSelect(QMouseEvent *event); bool _find(int bin, int cpu, int pid, int variance, bool joined, size_t *row); int _getNextCPU(int pid, int bin); int _getLastTask(struct kshark_trace_histo *histo, int bin, int cpu); int _getLastCPU(struct kshark_trace_histo *histo, int bin, int pid); void _deselect(); }; #endif trace-cmd-2.8.3/kernel-shark/src/KsMainWindow.cpp000066400000000000000000000731261351617527000216360ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsMainWindow.cpp * @brief KernelShark GUI main window. */ // C #include #include #include // C++11 #include // Qt #include #include #include #include #include // KernelShark #include "libkshark.h" #include "KsCmakeDef.hpp" #include "KsMainWindow.hpp" #include "KsCaptureDialog.hpp" #include "KsAdvFilteringDialog.hpp" /** Create KernelShark Main window. */ KsMainWindow::KsMainWindow(QWidget *parent) : QMainWindow(parent), _splitter(Qt::Vertical, this), _data(this), _view(this), _graph(this), _mState(this), _plugins(this), _capture(this), _captureLocalServer(this), _openAction("Open", this), _restoreSessionAction("Restore Last Session", this), _importSessionAction("Import Session", this), _exportSessionAction("Export Sassion", this), _quitAction("Quit", this), _importFilterAction("Import Filter", this), _exportFilterAction("Export Filter", this), _graphFilterSyncCBox(nullptr), _listFilterSyncCBox(nullptr), _showEventsAction("Show events", this), _showTasksAction("Show tasks", this), _showCPUsAction("Show CPUs", this), _advanceFilterAction("Advance Filtering", this), _clearAllFilters("Clear all filters", this), _cpuSelectAction("CPUs", this), _taskSelectAction("Tasks", this), _managePluginsAction("Manage plugins", this), _addPluginsAction("Add plugins", this), _captureAction("Record", this), _colorAction(this), _colSlider(this), _colorPhaseSlider(Qt::Horizontal, this), _fullScreenModeAction("Full Screen Mode", this), _aboutAction("About", this), _contentsAction("Contents", this), _bugReportAction("Report a bug", this), _deselectShortcut(this), _settings("kernelshark.org", "Kernel Shark") // organization , application { setWindowTitle("Kernel Shark"); _createActions(); _createMenus(); _initCapture(); _splitter.addWidget(&_graph); _splitter.addWidget(&_view); setCentralWidget(&_splitter); connect(&_splitter, &QSplitter::splitterMoved, this, &KsMainWindow::_splitterMoved); _view.setMarkerSM(&_mState); connect(&_mState, &KsDualMarkerSM::markSwitchForView, &_view, &KsTraceViewer::markSwitch); _graph.setMarkerSM(&_mState); connect(&_mState, &KsDualMarkerSM::updateGraph, &_graph, &KsTraceGraph::markEntry); connect(&_mState, &KsDualMarkerSM::updateView, &_view, &KsTraceViewer::showRow); connect(&_view, &KsTraceViewer::select, &_graph, &KsTraceGraph::markEntry); connect(&_view, &KsTraceViewer::addTaskPlot, &_graph, &KsTraceGraph::addTaskPlot); connect(_graph.glPtr(), &KsGLWidget::updateView, &_view, &KsTraceViewer::showRow); connect(&_graph, &KsTraceGraph::deselect, this, &KsMainWindow::_deselectActive); connect(&_view, &KsTraceViewer::deselect, this, &KsMainWindow::_deselectActive); connect(&_data, &KsDataStore::updateWidgets, &_view, &KsTraceViewer::update); connect(&_data, &KsDataStore::updateWidgets, &_graph, &KsTraceGraph::update); connect(&_plugins, &KsPluginManager::dataReload, &_data, &KsDataStore::reload); _deselectShortcut.setKey(Qt::CTRL + Qt::Key_D); connect(&_deselectShortcut, &QShortcut::activated, this, &KsMainWindow::_deselectActive); connect(&_mState, &KsDualMarkerSM::deselectA, this, &KsMainWindow::_deselectA); connect(&_mState, &KsDualMarkerSM::deselectB, this, &KsMainWindow::_deselectB); _lastDataFilePath = _settings.value("dataPath").toString(); _lastConfFilePath = _settings.value("confPath").toString(); _lastPluginFilePath = _settings.value("pluginPath").toString(); _resizeEmpty(); } /** Destroy KernelShark Main window. */ KsMainWindow::~KsMainWindow() { kshark_context *kshark_ctx(nullptr); QString file = lastSessionFile(); if (!file.isEmpty()) { QByteArray fileBA = file.toLocal8Bit(); _updateSession(); kshark_save_config_file(fileBA.data(), _session.getConfDocPtr()); } _settings.setValue("dataPath", _lastDataFilePath); _settings.setValue("confPath", _lastConfFilePath); _settings.setValue("pluginPath", _lastPluginFilePath); _data.clear(); if (kshark_instance(&kshark_ctx)) kshark_free(kshark_ctx); } /** * Reimplemented event handler used to update the geometry of the window on * resize events. */ void KsMainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); _session.saveMainWindowSize(*this); _session.saveSplitterSize(_splitter); } void KsMainWindow::_createActions() { /* File menu */ _openAction.setIcon(QIcon::fromTheme("document-open")); _openAction.setShortcut(tr("Ctrl+O")); _openAction.setStatusTip("Open an existing data file"); connect(&_openAction, &QAction::triggered, this, &KsMainWindow::_open); _restoreSessionAction.setIcon(QIcon::fromTheme("document-open-recent")); connect(&_restoreSessionAction, &QAction::triggered, this, &KsMainWindow::_restoreSession); _importSessionAction.setIcon(QIcon::fromTheme("document-send")); _importSessionAction.setStatusTip("Load a session"); connect(&_importSessionAction, &QAction::triggered, this, &KsMainWindow::_importSession); _exportSessionAction.setIcon(QIcon::fromTheme("document-revert")); _exportSessionAction.setStatusTip("Export this session"); connect(&_exportSessionAction, &QAction::triggered, this, &KsMainWindow::_exportSession); _quitAction.setIcon(QIcon::fromTheme("window-close")); _quitAction.setShortcut(tr("Ctrl+Q")); _quitAction.setStatusTip("Exit KernelShark"); connect(&_quitAction, &QAction::triggered, this, &KsMainWindow::close); /* Filter menu */ _importFilterAction.setIcon(QIcon::fromTheme("document-send")); _importFilterAction.setStatusTip("Load a filter"); connect(&_importFilterAction, &QAction::triggered, this, &KsMainWindow::_importFilter); _exportFilterAction.setIcon(QIcon::fromTheme("document-revert")); _exportFilterAction.setStatusTip("Export a filter"); connect(&_exportFilterAction, &QAction::triggered, this, &KsMainWindow::_exportFilter); connect(&_showEventsAction, &QAction::triggered, this, &KsMainWindow::_showEvents); connect(&_showTasksAction, &QAction::triggered, this, &KsMainWindow::_showTasks); connect(&_showCPUsAction, &QAction::triggered, this, &KsMainWindow::_showCPUs); connect(&_advanceFilterAction, &QAction::triggered, this, &KsMainWindow::_advancedFiltering); connect(&_clearAllFilters, &QAction::triggered, this, &KsMainWindow::_clearFilters); /* Plot menu */ connect(&_cpuSelectAction, &QAction::triggered, this, &KsMainWindow::_cpuSelect); connect(&_taskSelectAction, &QAction::triggered, this, &KsMainWindow::_taskSelect); /* Tools menu */ _managePluginsAction.setShortcut(tr("Ctrl+P")); _managePluginsAction.setIcon(QIcon::fromTheme("preferences-system")); _managePluginsAction.setStatusTip("Manage plugins"); connect(&_managePluginsAction, &QAction::triggered, this, &KsMainWindow::_pluginSelect); _addPluginsAction.setIcon(QIcon::fromTheme("applications-engineering")); _addPluginsAction.setStatusTip("Add plugins"); connect(&_addPluginsAction, &QAction::triggered, this, &KsMainWindow::_pluginAdd); _captureAction.setIcon(QIcon::fromTheme("media-record")); _captureAction.setShortcut(tr("Ctrl+R")); _captureAction.setStatusTip("Capture trace data"); connect(&_captureAction, &QAction::triggered, this, &KsMainWindow::_record); _colorPhaseSlider.setMinimum(20); _colorPhaseSlider.setMaximum(180); _colorPhaseSlider.setValue(KsPlot::Color::getRainbowFrequency() * 100); _colorPhaseSlider.setFixedWidth(FONT_WIDTH * 15); connect(&_colorPhaseSlider, &QSlider::valueChanged, this, &KsMainWindow::_setColorPhase); _colSlider.setLayout(new QHBoxLayout); _colSlider.layout()->addWidget(new QLabel("Color scheme", this)); _colSlider.layout()->addWidget(&_colorPhaseSlider); _colorAction.setDefaultWidget(&_colSlider); _fullScreenModeAction.setIcon(QIcon::fromTheme("view-fullscreen")); _fullScreenModeAction.setShortcut(tr("Ctrl+Shift+F")); _fullScreenModeAction.setStatusTip("Full Screen Mode"); connect(&_fullScreenModeAction, &QAction::triggered, this, &KsMainWindow::_changeScreenMode); /* Help menu */ _aboutAction.setIcon(QIcon::fromTheme("help-about")); connect(&_aboutAction, &QAction::triggered, this, &KsMainWindow::_aboutInfo); _contentsAction.setIcon(QIcon::fromTheme("help-contents")); connect(&_contentsAction, &QAction::triggered, this, &KsMainWindow::_contents); connect(&_bugReportAction, &QAction::triggered, this, &KsMainWindow::_bugReport); } void KsMainWindow::_createMenus() { QMenu *file, *sessions, *filter, *plots, *tools, *help; kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; /* File menu */ file = menuBar()->addMenu("File"); file->addAction(&_openAction); sessions = file->addMenu("Sessions"); sessions->setIcon(QIcon::fromTheme("document-properties")); sessions->addAction(&_restoreSessionAction); sessions->addAction(&_importSessionAction); sessions->addAction(&_exportSessionAction); file->addAction(&_quitAction); /* Filter menu */ filter = menuBar()->addMenu("Filter"); connect(filter, &QMenu::aboutToShow, this, &KsMainWindow::_updateFilterMenu); filter->addAction(&_importFilterAction); filter->addAction(&_exportFilterAction); /* * Set the default filter mask. Filter will apply to both View and * Graph. */ kshark_ctx->filter_mask = KS_TEXT_VIEW_FILTER_MASK | KS_GRAPH_VIEW_FILTER_MASK; kshark_ctx->filter_mask |= KS_EVENT_VIEW_FILTER_MASK; _graphFilterSyncCBox = KsUtils::addCheckBoxToMenu(filter, "Apply filters to Graph"); _graphFilterSyncCBox->setChecked(true); connect(_graphFilterSyncCBox, &QCheckBox::stateChanged, this, &KsMainWindow::_graphFilterSync); _listFilterSyncCBox = KsUtils::addCheckBoxToMenu(filter, "Apply filters to List"); _listFilterSyncCBox->setChecked(true); connect(_listFilterSyncCBox, &QCheckBox::stateChanged, this, &KsMainWindow::_listFilterSync); filter->addAction(&_showEventsAction); filter->addAction(&_showTasksAction); filter->addAction(&_showCPUsAction); filter->addAction(&_advanceFilterAction); filter->addAction(&_clearAllFilters); /* Plot menu */ plots = menuBar()->addMenu("Plots"); plots->addAction(&_cpuSelectAction); plots->addAction(&_taskSelectAction); /* Tools menu */ tools = menuBar()->addMenu("Tools"); tools->addAction(&_managePluginsAction); tools->addAction(&_addPluginsAction); tools->addAction(&_captureAction); tools->addSeparator(); tools->addAction(&_colorAction); tools->addAction(&_fullScreenModeAction); /* Help menu */ help = menuBar()->addMenu("Help"); help->addAction(&_aboutAction); help->addAction(&_contentsAction); help->addAction(&_bugReportAction); } void KsMainWindow::_open() { QString fileName; fileName = KsUtils::getFile(this, "Open File", "trace-cmd files (*.dat);;All files (*)", _lastDataFilePath); if (!fileName.isEmpty()) loadDataFile(fileName); } QString KsMainWindow::_getCacheDir() { QString dir; auto lamMakePath = [&] (bool ask) { if (ask) { QMessageBox::StandardButton reply; QString err("KernelShark cache directory not found!\n"); QString question = QString("Do you want to create %1").arg(dir); reply = QMessageBox::question(this, "KernelShark", err + question, QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::No) { dir.clear(); return; } } QDir().mkpath(dir); }; dir = getenv("KS_USER_CACHE_DIR"); if (!dir.isEmpty()) { if (!QDir(dir).exists()) lamMakePath(true); } else { auto appCachePath = QStandardPaths::GenericCacheLocation; dir = QStandardPaths::writableLocation(appCachePath); dir += "/kernelshark"; if (!QDir(dir).exists()) lamMakePath(false); } return dir; } /** Get the description file of the last session. */ QString KsMainWindow::lastSessionFile() { QString file; file = _getCacheDir(); if (!file.isEmpty()) file += "/lastsession.json"; return file; } void KsMainWindow::_restoreSession() { loadSession(lastSessionFile()); } void KsMainWindow::_importSession() { QString fileName; fileName = KsUtils::getFile(this, "Import Session", "Kernel Shark Config files (*.json);;", _lastConfFilePath); if (fileName.isEmpty()) return; loadSession(fileName); } void KsMainWindow::_updateSession() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; _session.saveGraphs(*_graph.glPtr()); _session.saveVisModel(_graph.glPtr()->model()->histo()); _session.saveFilters(kshark_ctx); _session.saveDualMarker(&_mState); _session.saveTable(_view); _session.saveColorScheme(); _session.savePlugins(_plugins); } void KsMainWindow::_exportSession() { QString fileName; fileName = KsUtils::getSaveFile(this, "Export Filter", "Kernel Shark Config files (*.json);;", ".json", _lastConfFilePath); if (fileName.isEmpty()) return; _updateSession(); _session.exportToFile(fileName); } void KsMainWindow::_filterSyncCBoxUpdate(kshark_context *kshark_ctx) { if (kshark_ctx->filter_mask & KS_TEXT_VIEW_FILTER_MASK) _listFilterSyncCBox->setChecked(true); else _listFilterSyncCBox->setChecked(false); if (kshark_ctx->filter_mask & (KS_GRAPH_VIEW_FILTER_MASK | KS_EVENT_VIEW_FILTER_MASK)) _graphFilterSyncCBox->setChecked(true); else _graphFilterSyncCBox->setChecked(false); } void KsMainWindow::_updateFilterMenu() { kshark_context *kshark_ctx(nullptr); if (kshark_instance(&kshark_ctx)) _filterSyncCBoxUpdate(kshark_ctx); } void KsMainWindow::_importFilter() { kshark_context *kshark_ctx(nullptr); kshark_config_doc *conf; QString fileName; if (!kshark_instance(&kshark_ctx) || _data.size() < 1) return; fileName = KsUtils::getFile(this, "Import Filter", "Kernel Shark Config files (*.json);;", _lastConfFilePath); if (fileName.isEmpty()) return; conf = kshark_open_config_file(fileName.toStdString().c_str(), "kshark.config.filter"); if (!conf) return; kshark_import_all_event_filters(kshark_ctx, conf); kshark_free_config_doc(conf); kshark_filter_entries(kshark_ctx, _data.rows(), _data.size()); _filterSyncCBoxUpdate(kshark_ctx); emit _data.updateWidgets(&_data); } void KsMainWindow::_exportFilter() { kshark_context *kshark_ctx(nullptr); kshark_config_doc *conf(nullptr); QString fileName; if (!kshark_instance(&kshark_ctx)) return; fileName = KsUtils::getSaveFile(this, "Export Filter", "Kernel Shark Config files (*.json);;", ".json", _lastConfFilePath); if (fileName.isEmpty()) return; kshark_export_all_event_filters(kshark_ctx, &conf); kshark_save_config_file(fileName.toStdString().c_str(), conf); kshark_free_config_doc(conf); } void KsMainWindow::_listFilterSync(int state) { KsUtils::listFilterSync(state); _data.update(); } void KsMainWindow::_graphFilterSync(int state) { KsUtils::graphFilterSync(state); _data.update(); } void KsMainWindow::_showEvents() { kshark_context *kshark_ctx(nullptr); KsCheckBoxWidget *events_cb; KsCheckBoxDialog *dialog; if (!kshark_instance(&kshark_ctx)) return; events_cb = new KsEventsCheckBoxWidget(_data.tep(), this); dialog = new KsCheckBoxDialog(events_cb, this); if (!kshark_ctx->show_event_filter || !kshark_ctx->show_event_filter->count) { events_cb->setDefault(true); } else { /* * The event filter contains IDs. Make this visible in the * CheckBox Widget. */ tep_event **events = tep_list_events(_data.tep(), TEP_EVENT_SORT_SYSTEM); int nEvts = tep_get_events_count(_data.tep()); QVector v(nEvts, false); for (int i = 0; i < nEvts; ++i) { if (tracecmd_filter_id_find(kshark_ctx->show_event_filter, events[i]->id)) v[i] = true; } events_cb->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_data, &KsDataStore::applyPosEventFilter); dialog->show(); } void KsMainWindow::_showTasks() { kshark_context *kshark_ctx(nullptr); KsCheckBoxWidget *tasks_cbd; KsCheckBoxDialog *dialog; if (!kshark_instance(&kshark_ctx)) return; tasks_cbd = new KsTasksCheckBoxWidget(_data.tep(), true, this); dialog = new KsCheckBoxDialog(tasks_cbd, this); if (!kshark_ctx->show_task_filter || !kshark_ctx->show_task_filter->count) { tasks_cbd->setDefault(true); } else { QVector pids = KsUtils::getPidList(); int nPids = pids.count(); QVector v(nPids, false); for (int i = 0; i < nPids; ++i) { if (tracecmd_filter_id_find(kshark_ctx->show_task_filter, pids[i])) v[i] = true; } tasks_cbd->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_data, &KsDataStore::applyPosTaskFilter); dialog->show(); } void KsMainWindow::_hideTasks() { kshark_context *kshark_ctx(nullptr); KsCheckBoxWidget *tasks_cbd; KsCheckBoxDialog *dialog; if (!kshark_instance(&kshark_ctx)) return; tasks_cbd = new KsTasksCheckBoxWidget(_data.tep(), false, this); dialog = new KsCheckBoxDialog(tasks_cbd, this); if (!kshark_ctx->hide_task_filter || !kshark_ctx->hide_task_filter->count) { tasks_cbd->setDefault(false); } else { QVector pids = KsUtils::getPidList(); int nPids = pids.count(); QVector v(nPids, false); for (int i = 0; i < nPids; ++i) { if (tracecmd_filter_id_find(kshark_ctx->hide_task_filter, pids[i])) v[i] = true; } tasks_cbd->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_data, &KsDataStore::applyNegTaskFilter); dialog->show(); } void KsMainWindow::_showCPUs() { kshark_context *kshark_ctx(nullptr); KsCheckBoxWidget *cpu_cbd; KsCheckBoxDialog *dialog; if (!kshark_instance(&kshark_ctx)) return; cpu_cbd = new KsCPUCheckBoxWidget(_data.tep(), this); dialog = new KsCheckBoxDialog(cpu_cbd, this); if (!kshark_ctx->show_cpu_filter || !kshark_ctx->show_cpu_filter->count) { cpu_cbd->setDefault(true); } else { int nCPUs = tep_get_cpus(_data.tep()); QVector v(nCPUs, false); for (int i = 0; i < nCPUs; ++i) { if (tracecmd_filter_id_find(kshark_ctx->show_cpu_filter, i)) v[i] = true; } cpu_cbd->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_data, &KsDataStore::applyPosCPUFilter); dialog->show(); } void KsMainWindow::_hideCPUs() { kshark_context *kshark_ctx(nullptr); KsCheckBoxWidget *cpu_cbd; KsCheckBoxDialog *dialog; if (!kshark_instance(&kshark_ctx)) return; cpu_cbd = new KsCPUCheckBoxWidget(_data.tep(), this); dialog = new KsCheckBoxDialog(cpu_cbd, this); if (!kshark_ctx->hide_cpu_filter || !kshark_ctx->hide_cpu_filter->count) { cpu_cbd->setDefault(false); } else { int nCPUs = tep_get_cpus(_data.tep()); QVector v(nCPUs, false); for (int i = 0; i < nCPUs; ++i) { if (tracecmd_filter_id_find(kshark_ctx->hide_cpu_filter, i)) v[i] = true; } cpu_cbd->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_data, &KsDataStore::applyNegCPUFilter); dialog->show(); } void KsMainWindow::_advancedFiltering() { KsAdvFilteringDialog *dialog; if (!_data.tep()) { QErrorMessage *em = new QErrorMessage(this); QString text("Unable to open Advanced filtering dialog."); text += " Tracing data has to be loaded first."; em->showMessage(text, "advancedFiltering"); qCritical() << "ERROR: " << text; return; } dialog = new KsAdvFilteringDialog(this); connect(dialog, &KsAdvFilteringDialog::dataReload, &_data, &KsDataStore::reload); dialog->show(); } void KsMainWindow::_clearFilters() { _data.clearAllFilters(); } void KsMainWindow::_cpuSelect() { KsCheckBoxWidget *cpus_cbd = new KsCPUCheckBoxWidget(_data.tep(), this); KsCheckBoxDialog *dialog = new KsCheckBoxDialog(cpus_cbd, this); if(_data.tep()) { int nCPUs = tep_get_cpus(_data.tep()); if (nCPUs == _graph.glPtr()->cpuGraphCount()) { cpus_cbd->setDefault(true); } else { QVector v(nCPUs, false); for (auto const &cpu: _graph.glPtr()->_cpuList) v[cpu] = true; cpus_cbd->set(v); } } connect(dialog, &KsCheckBoxDialog::apply, &_graph, &KsTraceGraph::cpuReDraw); dialog->show(); } void KsMainWindow::_taskSelect() { KsCheckBoxWidget *tasks_cbd = new KsTasksCheckBoxWidget(_data.tep(), true, this); KsCheckBoxDialog *dialog = new KsCheckBoxDialog(tasks_cbd, this); QVector pids = KsUtils::getPidList(); int nPids = pids.count(); if (nPids == _graph.glPtr()->taskGraphCount()) { tasks_cbd->setDefault(true); } else { QVector v(nPids, false); for (int i = 0; i < nPids; ++i) { for (auto const &pid: _graph.glPtr()->_taskList) { if (pids[i] == pid) { v[i] = true; break; } } } tasks_cbd->set(v); } connect(dialog, &KsCheckBoxDialog::apply, &_graph, &KsTraceGraph::taskReDraw); dialog->show(); } void KsMainWindow::_pluginSelect() { KsCheckBoxWidget *plugin_cbd; KsCheckBoxDialog *dialog; QVector registeredPlugins; QStringList plugins; plugins << _plugins._ksPluginList << _plugins._userPluginList; registeredPlugins << _plugins._registeredKsPlugins << _plugins._registeredUserPlugins; plugin_cbd = new KsPluginCheckBoxWidget(plugins, this); plugin_cbd->set(registeredPlugins); dialog = new KsCheckBoxDialog(plugin_cbd, this); connect(dialog, &KsCheckBoxDialog::apply, &_plugins, &KsPluginManager::updatePlugins); dialog->show(); } void KsMainWindow::_pluginAdd() { QStringList fileNames; fileNames = KsUtils::getFiles(this, "Add KernelShark plugins", "KernelShark Plugins (*.so);;", _lastPluginFilePath); if (!fileNames.isEmpty()) _plugins.addPlugins(fileNames); } void KsMainWindow::_record() { #ifndef DO_AS_ROOT QErrorMessage *em = new QErrorMessage(this); QString message; message = "Record is currently not supported."; message += " Install \"pkexec\" and then do:
    "; message += " cd build
    sudo ./cmake_uninstall.sh
    "; message += " ./cmake_clean.sh
    cmake ..
    make
    "; message += " sudo make install"; em->showMessage(message); qCritical() << "ERROR: " << message; return; #endif _capture.start(); } void KsMainWindow::_setColorPhase(int f) { KsPlot::Color::setRainbowFrequency(f / 100.); _graph.glPtr()->model()->update(); } void KsMainWindow::_changeScreenMode() { if (isFullScreen()) { _fullScreenModeAction.setText("Full Screen Mode"); _fullScreenModeAction.setIcon(QIcon::fromTheme("view-fullscreen")); showNormal(); } else { _fullScreenModeAction.setText("Exit Full Screen Mode"); _fullScreenModeAction.setIcon(QIcon::fromTheme("view-restore")); showFullScreen(); } } void KsMainWindow::_aboutInfo() { KsMessageDialog *message; QString text; text.append(" KernelShark\n\n version: "); text.append(KS_VERSION_STRING); text.append("\n"); message = new KsMessageDialog(text); message->setWindowTitle("About"); message->show(); } void KsMainWindow::_contents() { QDesktopServices::openUrl(QUrl("http://kernelshark.org/", QUrl::TolerantMode)); } void KsMainWindow::_bugReport() { QUrl bugs("https://bugzilla.kernel.org/buglist.cgi?component=Trace-cmd%2FKernelshark&product=Tools&resolution=---", QUrl::TolerantMode); QDesktopServices::openUrl(bugs); } /** Load trace data for file. */ void KsMainWindow::loadDataFile(const QString& fileName) { char buff[FILENAME_MAX]; QString pbLabel("Loading "); bool loadDone = false; struct stat st; int ret; ret = stat(fileName.toStdString().c_str(), &st); if (ret != 0) { QString text("Unable to find file "); text.append(fileName); text.append("."); _error(text, "loadDataErr1", true, true); return; } qInfo() << "Loading " << fileName; _mState.reset(); _view.reset(); _graph.reset(); if (fileName.size() < 40) { pbLabel += fileName; } else { pbLabel += "..."; pbLabel += fileName.mid(fileName.size() - 37, 37); } setWindowTitle("Kernel Shark"); KsProgressBar pb(pbLabel); QApplication::processEvents(); auto lamLoadJob = [&](KsDataStore *d) { d->loadDataFile(fileName); loadDone = true; }; std::thread tload(lamLoadJob, &_data); for (int i = 0; i < 160; ++i) { /* * TODO: The way this progress bar gets updated here is a pure * cheat. See if this can be implemented better. */ if (loadDone) break; pb.setValue(i); usleep(150000); } tload.join(); if (_data.size() < 1) { QString text("No data was loaded from file "); text.append(fileName + "."); _error(text, "loadDataErr2", true, true); return; } pb.setValue(165); _view.loadData(&_data); pb.setValue(180); _graph.loadData(&_data); pb.setValue(195); setWindowTitle("Kernel Shark (" + fileName + ")"); if (realpath(fileName.toStdString().c_str(), buff)) { QString path(buff); _session.saveDataFile(path); } } void KsMainWindow::_error(const QString &text, const QString &errCode, bool resize, bool unloadPlugins) { QErrorMessage *em = new QErrorMessage(this); if (resize) _resizeEmpty(); if (unloadPlugins) _plugins.unloadAll(); qCritical().noquote() << "ERROR: " << text; em->showMessage(text, errCode); em->exec(); } /** * @brief Load user session. * * @param fileName: Json file containing the description of the session. */ void KsMainWindow::loadSession(const QString &fileName) { kshark_context *kshark_ctx(nullptr); struct stat st; int ret; if (!kshark_instance(&kshark_ctx)) return; ret = stat(fileName.toStdString().c_str(), &st); if (ret != 0) { QString text("Unable to find session file "); text.append(fileName); text.append("\n"); _error(text, "loadSessErr0", true, true); return; } if (!_session.importFromFile(fileName)) { QString text("Unable to open session description file "); text.append(fileName); text.append(".\n"); _error(text, "loadSessErr1", true, true); return; } _session.loadPlugins(kshark_ctx, &_plugins); QString dataFile(_session.getDataFile(kshark_ctx)); if (dataFile.isEmpty()) { QString text("Unable to open trace data file for session "); text.append(fileName); text.append("\n"); _error(text, "loadSessErr1", true, true); return; } loadDataFile(dataFile); if (!_data.tep()) { _plugins.unloadAll(); return; } KsProgressBar pb("Loading session settings ..."); pb.setValue(10); _session.loadGraphs(&_graph); pb.setValue(20); _session.loadFilters(kshark_ctx, &_data); _filterSyncCBoxUpdate(kshark_ctx); pb.setValue(130); _session.loadSplitterSize(&_splitter); _session.loadMainWindowSize(this); this->show(); pb.setValue(140); _session.loadDualMarker(&_mState, &_graph); _session.loadVisModel(_graph.glPtr()->model()); _mState.updateMarkers(_data, _graph.glPtr()); pb.setValue(170); _session.loadTable(&_view); _colorPhaseSlider.setValue(_session.getColorScheme() * 100); _graph.updateGeom(); } void KsMainWindow::_initCapture() { #ifdef DO_AS_ROOT _capture.setProgram("kshark-su-record"); connect(&_capture, &QProcess::started, this, &KsMainWindow::_captureStarted); /* * Using the old Signal-Slot syntax because QProcess::finished has * overloads. */ connect(&_capture, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(_captureFinished(int, QProcess::ExitStatus))); connect(&_capture, &QProcess::errorOccurred, this, &KsMainWindow::_captureError); connect(&_captureLocalServer, &QLocalServer::newConnection, this, &KsMainWindow::_readSocket); #endif } void KsMainWindow::_captureStarted() { _captureLocalServer.listen("KSCapture"); } /** * If the authorization could not be obtained because the user dismissed * the authentication dialog (clicked Cancel), pkexec exits with a return * value of 126. */ #define PKEXEC_DISMISS_RET 126 void KsMainWindow::_captureFinished(int ret, QProcess::ExitStatus st) { QProcess *capture = (QProcess *)sender(); _captureLocalServer.close(); if (ret == PKEXEC_DISMISS_RET) { /* * Authorization could not be obtained because the user * dismissed the authentication dialog. */ return; } if (ret != 0 || st != QProcess::NormalExit) { QString message = "Capture process failed:
    "; message += capture->errorString(); message += "
    Try doing:
    sudo make install"; _error(message, "captureFinishedErr", false, false); } } void KsMainWindow::_captureError(QProcess::ProcessError error) { QProcess *capture = (QProcess *)sender(); QString message = "Capture process failed:
    "; message += capture->errorString(); message += "
    Try doing:
    sudo make install"; _error(message, "captureFinishedErr", false, false); } void KsMainWindow::_readSocket() { QLocalSocket *socket; quint32 blockSize; QString fileName; auto lamSocketError = [&](QString message) { message = "ERROR from Local Server: " + message; _error(message, "readSocketErr", false, false); }; socket = _captureLocalServer.nextPendingConnection(); if (!socket) { lamSocketError("Pending connectio not found!"); return; } QDataStream in(socket); socket->waitForReadyRead(); if (socket->bytesAvailable() < (int)sizeof(quint32)) { lamSocketError("Message size is corrupted!"); return; }; in >> blockSize; if (socket->bytesAvailable() < blockSize || in.atEnd()) { lamSocketError("Message is corrupted!"); return; } in >> fileName; loadDataFile(fileName); } void KsMainWindow::_splitterMoved(int pos, int index) { _session.saveSplitterSize(_splitter); } void KsMainWindow::_deselectActive() { _view.clearSelection(); _mState.activeMarker().remove(); _mState.updateLabels(); _graph.glPtr()->model()->update(); } void KsMainWindow::_deselectA() { if (_mState.getState() == DualMarkerState::A) _view.clearSelection(); else _view.passiveMarkerSelectRow(KS_NO_ROW_SELECTED); _mState.markerA().remove(); _mState.updateLabels(); _graph.glPtr()->model()->update(); } void KsMainWindow::_deselectB() { if (_mState.getState() == DualMarkerState::B) _view.clearSelection(); else _view.passiveMarkerSelectRow(KS_NO_ROW_SELECTED); _mState.markerB().remove(); _mState.updateLabels(); _graph.glPtr()->model()->update(); } trace-cmd-2.8.3/kernel-shark/src/KsMainWindow.hpp000066400000000000000000000102271351617527000216340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsMainWindow.hpp * @brief KernelShark GUI main window. */ #ifndef _KS_MAINWINDOW_H #define _KS_MAINWINDOW_H // Qt #include #include // KernelShark #include "KsTraceViewer.hpp" #include "KsTraceGraph.hpp" #include "KsSession.hpp" #include "KsUtils.hpp" /** * The KsMainWindow class provides Main window for the KernelShark GUI. */ class KsMainWindow : public QMainWindow { Q_OBJECT public: explicit KsMainWindow(QWidget *parent = nullptr); ~KsMainWindow(); void loadDataFile(const QString &fileName); void loadSession(const QString &fileName); QString lastSessionFile(); /** * @brief * * @param plugin: can be the name of the plugin or the plugin's library * file (including absolute or relative path). */ void registerPlugin(const QString &plugin) { _plugins.registerPlugin(plugin); } /** * @brief * * @param plugin: can be the name of the plugin or the plugin's library * file (including absolute path). */ void unregisterPlugin(const QString &plugin) { _plugins.unregisterPlugin(plugin); } void resizeEvent(QResizeEvent* event); /** Set the Full Screen mode. */ void setFullScreenMode(bool f) { if ((!isFullScreen() && f) || (isFullScreen() && !f) ) _changeScreenMode(); } private: QSplitter _splitter; /** GUI session. */ KsSession _session; /** Data Manager. */ KsDataStore _data; /** Widget for reading and searching in the trace data. */ KsTraceViewer _view; /** Widget for graphical visualization of the trace data. */ KsTraceGraph _graph; /** Dual Marker State Machine. */ KsDualMarkerSM _mState; /** Plugin manager. */ KsPluginManager _plugins; /** The process used to record trace data. */ QProcess _capture; /** Local Server used for comunucation with the Capture process. */ QLocalServer _captureLocalServer; // File menu. QAction _openAction; QAction _restoreSessionAction; QAction _importSessionAction; QAction _exportSessionAction; QAction _quitAction; // Filter menu. QAction _importFilterAction; QAction _exportFilterAction; QCheckBox *_graphFilterSyncCBox; QCheckBox *_listFilterSyncCBox; QAction _showEventsAction; QAction _showTasksAction; QAction _showCPUsAction; QAction _advanceFilterAction; QAction _clearAllFilters; // Plots menu. QAction _cpuSelectAction; QAction _taskSelectAction; // Tools menu. QAction _managePluginsAction; QAction _addPluginsAction; QAction _captureAction; QWidgetAction _colorAction; QWidget _colSlider; QSlider _colorPhaseSlider; QAction _fullScreenModeAction; // Help menu. QAction _aboutAction; QAction _contentsAction; QAction _bugReportAction; QShortcut _deselectShortcut; QString _lastDataFilePath, _lastConfFilePath, _lastPluginFilePath; QSettings _settings; void _open(); void _restoreSession(); void _importSession(); void _exportSession(); void _importFilter(); void _exportFilter(); void _listFilterSync(int state); void _graphFilterSync(int state); void _showEvents(); void _showTasks(); void _hideTasks(); void _showCPUs(); void _hideCPUs(); void _advancedFiltering(); void _clearFilters(); void _cpuSelect(); void _taskSelect(); void _pluginSelect(); void _pluginAdd(); void _record(); void _setColorPhase(int); void _changeScreenMode(); void _aboutInfo(); void _contents(); void _bugReport(); void _captureStarted(); void _captureError(QProcess::ProcessError error); void _readSocket(); void _splitterMoved(int pos, int index); void _createActions(); void _createMenus(); void _initCapture(); void _updateSession(); inline void _resizeEmpty() {resize(SCREEN_WIDTH * .5, FONT_HEIGHT * 3);} void _error(const QString &text, const QString &errCode, bool resize, bool unloadPlugins); void _deselectActive(); void _deselectA(); void _deselectB(); void _updateFilterMenu(); void _filterSyncCBoxUpdate(kshark_context *kshark_ctx); QString _getCacheDir(); private slots: void _captureFinished(int, QProcess::ExitStatus); }; #endif // _KS_MAINWINDOW_H trace-cmd-2.8.3/kernel-shark/src/KsModels.cpp000066400000000000000000000303751351617527000210040ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsModels.cpp * @brief Models for data representation. */ // KernelShark #include "KsModels.hpp" #include "KsWidgetsLib.hpp" #include "KsUtils.hpp" /** Create a default (empty) KsFilterProxyModel object. */ KsFilterProxyModel::KsFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), _searchStop(false), _source(nullptr) {} /** * Returns False if the item in the row indicated by the sourceRow and * sourceParentshould be filtered out. Otherwise returns True. */ bool KsFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if (_data[sourceRow]->visible & KS_TEXT_VIEW_FILTER_MASK) return true; return false; } /** Provide the Proxy model with data. */ void KsFilterProxyModel::fill(KsDataStore *data) { _data = data->rows(); } /** Set the source model for this Proxy model. */ void KsFilterProxyModel::setSource(KsViewModel *s) { QSortFilterProxyModel::setSourceModel(s); _source = s; } size_t KsFilterProxyModel::_search(int column, const QString &searchText, search_condition_func cond, QList *matchList, int first, int last, QProgressBar *pb, QLabel *l, bool notify) { int index, row, nRows(last - first + 1); int pbCount(1); QString item; if (nRows > KS_PROGRESS_BAR_MAX) pbCount = nRows / (KS_PROGRESS_BAR_MAX - _searchProgress); else _searchProgress = KS_PROGRESS_BAR_MAX - nRows; /* Loop over the items of the proxy model. */ for (index = first; index <= last; ++index) { /* * Use the index of the proxy model to retrieve the value * of the row number in the base model. */ row = mapRowFromSource(index); item = _source->getValueStr(column, row); if (cond(searchText, item)) matchList->append(row); if (_searchStop) { if (notify) { _searchProgress = KS_PROGRESS_BAR_MAX; _pbCond.notify_one(); } break; } /* Deal with the Progress bar of the seatch. */ if ((index - first) % pbCount == 0) { if (notify) { std::lock_guard lk(_mutex); ++_searchProgress; _pbCond.notify_one(); } else { if (pb) { pb->setValue(pb->value() + 1); ++_searchProgress; } if (l) l->setText(QString(" %1").arg(matchList->count())); QApplication::processEvents(); } } } return index; } /** @brief Search the content of the table for a data satisfying an abstract * condition. * * @param column: The number of the column to search in. * @param searchText: The text to search for. * @param cond: Matching condition function. * @param matchList: Output location for a list containing the row indexes of * the cells satisfying matching condition. * @param pb: Input location for a Progressbar used to visualize the progress * of the search. * @param l: Input location for a label used to show the number of cells found. * * @returns The number of cells satisfying the matching condition. */ size_t KsFilterProxyModel::search(int column, const QString &searchText, search_condition_func cond, QList *matchList, QProgressBar *pb, QLabel *l) { int nRows = rowCount({}); _search(column, searchText, cond, matchList, 0, nRows - 1, pb, l, false); return matchList->count(); } /** @brief Search the content of the table for a data satisfying an abstract * condition. * * @param sm: Input location for the Search State machine object. * @param matchList: Output location for a list containing the row indexes of * * @returns The number of cells satisfying the matching condition. */ size_t KsFilterProxyModel::search(KsSearchFSM *sm, QList *matchList) { int nRows = rowCount({}); sm->_lastRowSearched = _search(sm->column(), sm->searchText(), sm->condition(), matchList, sm->_lastRowSearched + 1, nRows - 1, &sm->_searchProgBar, &sm->_searchCountLabel, false); return matchList->count(); } /** @brief Search the content of the table for a data satisfying an abstract * condition. * * @param column: The number of the column to search in. * @param searchText: The text to search for. * @param cond: Matching condition function. * @param first: Row index specifying the position inside the table from * where the search starts. * @param last: Row index specifying the position inside the table from * where the search ends. * @param notify: Input location for flag specifying if the search has to * notify the main thread when to update the progress bar. * * @returns A list containing the row indexes of the cells satisfying matching * condition. */ QList KsFilterProxyModel::searchMap(int column, const QString &searchText, search_condition_func cond, int first, int last, bool notify) { QList matchList; _search(column, searchText, cond, &matchList, first, last, nullptr, nullptr, notify); return matchList; } /** Create default (empty) KsViewModel object. */ KsViewModel::KsViewModel(QObject *parent) : QAbstractTableModel(parent), _data(nullptr), _nRows(0), _header({"#", "CPU", "Time Stamp", "Task", "PID", "Latency", "Event", "Info"}), _markA(KS_NO_ROW_SELECTED), _markB(KS_NO_ROW_SELECTED) {} /** * Get the data stored under the given role for the item referred to by * the index. This is an implementation of the pure virtual method of the * abstract model class. */ QVariant KsViewModel::data(const QModelIndex &index, int role) const { if (role == Qt::ForegroundRole) { if (index.row() == _markA) return QVariant::fromValue(QColor(Qt::white)); if (index.row() == _markB) return QVariant::fromValue(QColor(Qt::white)); } if (role == Qt::BackgroundRole) { if (index.row() == _markA) return QVariant::fromValue(QColor(_colorMarkA)); if (index.row() == _markB) return QVariant::fromValue(QColor(_colorMarkB)); } if (role == Qt::DisplayRole) return this->getValue(index.column(), index.row()); return {}; } /** Get the string data stored in a given cell of the table. */ QString KsViewModel::getValueStr(int column, int row) const { int pid; switch (column) { case TRACE_VIEW_COL_INDEX : return QString("%1").arg(row); case TRACE_VIEW_COL_CPU: return QString("%1").arg(_data[row]->cpu); case TRACE_VIEW_COL_TS: return KsUtils::Ts2String(_data[row]->ts, 6); case TRACE_VIEW_COL_COMM: return kshark_get_task_easy(_data[row]); case TRACE_VIEW_COL_PID: pid = kshark_get_pid_easy(_data[row]); return QString("%1").arg(pid); case TRACE_VIEW_COL_LAT: return kshark_get_latency_easy(_data[row]); case TRACE_VIEW_COL_EVENT: return kshark_get_event_name_easy(_data[row]); case TRACE_VIEW_COL_INFO : return kshark_get_info_easy(_data[row]); default: return {}; } } /** Get the data stored in a given cell of the table. */ QVariant KsViewModel::getValue(int column, int row) const { return getValueStr(column, row); } /** * Get the header of a column. This is an implementation of the pure virtual * method of the abstract model class. */ QVariant KsViewModel::headerData(int column, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) return {}; if (column < _header.count()) return _header.at(column); return {}; } /** Provide the model with data. */ void KsViewModel::fill(KsDataStore *data) { beginInsertRows(QModelIndex(), 0, data->size() - 1); _data = data->rows(); _nRows = data->size(); endInsertRows(); } /** @brief Select a row in the table. * * @param state: Identifier of the marker used to select the row. * @param row: Row index. */ void KsViewModel::selectRow(DualMarkerState state, int row) { if (state == DualMarkerState::A) { _markA = row; _markB = KS_NO_ROW_SELECTED; } else { _markB = row; _markA = KS_NO_ROW_SELECTED; } } /** Reset the model. */ void KsViewModel::reset() { beginResetModel(); _data = nullptr; _nRows = 0; endResetModel(); } /** Update the model. Use this function if the data has changed. */ void KsViewModel::update(KsDataStore *data) { /* * Do not try to skip the reset(). The row count has to be set to * zero before you full. */ reset(); fill(data); } /** @brief Search the content of the table for a data satisfying an abstract * condition. * * @param column: The number of the column to search in. * @param searchText: The text to search for. * @param cond: Matching condition function. * @param matchList: Output location for a list containing the row indexes of * the cells satisfying the matching condition. * * @returns The number of cells satisfying the matching condition. */ size_t KsViewModel::search(int column, const QString &searchText, search_condition_func cond, QList *matchList) { int nRows = rowCount({}); QVariant item; for (int r = 0; r < nRows; ++r) { item = getValue(r, column); if (cond(searchText, item.toString())) { matchList->append(r); } } return matchList->count(); } /** Create a default (empty) KsFilterProxyModel object. */ KsGraphModel::KsGraphModel(QObject *parent) : QAbstractTableModel(parent) { ksmodel_init(&_histo); } /** Destroy KsFilterProxyModel object. */ KsGraphModel::~KsGraphModel() { ksmodel_clear(&_histo); } /** * @brief Provide the Visualization model with data. Calculate the current * state of the model. * * @param entries: Input location for the trace data. * @param n: Number of bins. */ void KsGraphModel::fill(kshark_entry **entries, size_t n) { if (n == 0) return; beginResetModel(); if (_histo.n_bins == 0) ksmodel_set_bining(&_histo, KS_DEFAULT_NBUNS, entries[0]->ts, entries[n-1]->ts); ksmodel_fill(&_histo, entries, n); endResetModel(); } /** * @brief Shift the time-window of the model forward. Recalculate the current * state of the model. * * @param n: Number of bins to shift. */ void KsGraphModel::shiftForward(size_t n) { beginResetModel(); ksmodel_shift_forward(&_histo, n); endResetModel(); } /** * @brief Shift the time-window of the model backward. Recalculate the current * state of the model. * * @param n: Number of bins to shift. */ void KsGraphModel::shiftBackward(size_t n) { beginResetModel(); ksmodel_shift_backward(&_histo, n); endResetModel(); } /** * @brief Move the time-window of the model to a given location. Recalculate * the current state of the model. * * @param ts: position in time to be visualized. */ void KsGraphModel::jumpTo(size_t ts) { beginResetModel(); ksmodel_jump_to(&_histo, ts); endResetModel(); } /** * @brief Extend the time-window of the model. Recalculate the current state * of the model. * * @param r: Scale factor of the zoom-out. * @param mark: Focus point of the zoom-out. */ void KsGraphModel::zoomOut(double r, int mark) { beginResetModel(); ksmodel_zoom_out(&_histo, r, mark); endResetModel(); } /** * @brief Shrink the time-window of the model. Recalculate the current state * of the model. * * @param r: Scale factor of the zoom-in. * @param mark: Focus point of the zoom-in. */ void KsGraphModel::zoomIn(double r, int mark) { beginResetModel(); ksmodel_zoom_in(&_histo, r, mark); endResetModel(); } /** Quick zoom out. The entire data-set will be visualized. */ void KsGraphModel::quickZoomOut() { beginResetModel(); ksmodel_set_bining(&_histo, _histo.n_bins, _histo.data[0]->ts, _histo.data[_histo.data_size - 1]->ts); ksmodel_fill(&_histo, _histo.data, _histo.data_size); endResetModel(); } /** * @brief Quick zoom in to a state of the Visualization model which has the * given bin size. The actual value of the bin size may en up being slightly * different because of the fine tuning performed by the model. * * @param binSize: an approximate value for the new size of the bins. */ void KsGraphModel::quickZoomIn(uint64_t binSize) { double range, r; range = _histo.max - _histo.min; r = 1 - (binSize * _histo.n_bins) / range; zoomIn(r); } /** Reset the model. */ void KsGraphModel::reset() { beginResetModel(); ksmodel_clear(&_histo); endResetModel(); } /** Update the model. Use this function if the data has changed. */ void KsGraphModel::update(KsDataStore *data) { beginResetModel(); if (data) ksmodel_fill(&_histo, data->rows(), data->size()); endResetModel(); } trace-cmd-2.8.3/kernel-shark/src/KsModels.hpp000066400000000000000000000151071351617527000210050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsModels.hpp * @brief Models for data representation. */ #ifndef _KS_MODELS_H #define _KS_MODELS_H // C++11 #include #include // Qt #include #include #include #include #include // KernelShark #include "libkshark.h" #include "libkshark-model.h" #include "KsSearchFSM.hpp" /** A negative row index, to be used for deselecting the Passive Marker. */ #define KS_NO_ROW_SELECTED -1 enum class DualMarkerState; class KsDataStore; /** * Class KsViewModel provides models for trace data representation in a * table view. */ class KsViewModel : public QAbstractTableModel { public: explicit KsViewModel(QObject *parent = nullptr); /** Set the colors of the two markers. */ void setColors(const QColor &colA, const QColor &colB) { _colorMarkA = colA; _colorMarkB = colB; }; /** * Get the number of rows. This is an implementation of the pure * virtual method of the abstract model class. */ int rowCount(const QModelIndex &) const override {return _nRows;} /** * Get the number of columns. This is an implementation of the pure * virtual method of the abstract model class. */ int columnCount(const QModelIndex &) const override { return _header.count(); } QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex &index, int role) const override; void fill(KsDataStore *data); void selectRow(DualMarkerState state, int row); void reset(); void update(KsDataStore *data); /** Get the list of column's headers. */ QStringList header() const {return _header;} QString getValueStr(int column, int row) const; QVariant getValue(int column, int row) const; size_t search(int column, const QString &searchText, search_condition_func cond, QList *matchList); /** Table columns Identifiers. */ enum { /** Identifier of the Index column. */ TRACE_VIEW_COL_INDEX, /** Identifier of the CPU column. */ TRACE_VIEW_COL_CPU, /** Identifier of the Timestamp column. */ TRACE_VIEW_COL_TS, /** Identifier of the Task name (command) column. */ TRACE_VIEW_COL_COMM, /** Identifier of the Process Id column. */ TRACE_VIEW_COL_PID, /** Identifier of the Latency Id column. */ TRACE_VIEW_COL_LAT, /** Identifier of the Event name Id column. */ TRACE_VIEW_COL_EVENT, /** Identifier of the Event name Id column. */ TRACE_VIEW_COL_INFO, /** Number of column. */ TRACE_VIEW_N_COLUMNS, }; private: /** Trace data array. */ kshark_entry **_data; /** The size of the data array. */ size_t _nRows; /** The headers of the individual columns. */ QStringList _header; /** The index of marker A inside the data array. */ int _markA; /** The index of marker A inside the data array. */ int _markB; /** The color of the row selected by marker A. */ QColor _colorMarkA; /** The color of the row selected by marker B. */ QColor _colorMarkB; }; /** * Class KsFilterProxyModel provides support for filtering trace data in * table view. */ class KsFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit KsFilterProxyModel(QObject *parent = nullptr); bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; void fill(KsDataStore *data); void setSource(KsViewModel *s); size_t search(int column, const QString &searchText, search_condition_func cond, QList *matchList, QProgressBar *pb = nullptr, QLabel *l = nullptr); size_t search(KsSearchFSM *sm, QList *matchList); QList searchMap(int column, const QString &searchText, search_condition_func cond, int first, int last, bool notify); /** Get the progress of the search. */ int searchProgress() const {return _searchProgress;} /** Reset the progress value of the search. */ void searchReset() { _searchProgress = 0; _searchStop = false; } /** * Use the "row" index in the Proxy model to retrieve the "row" index * in the source model. */ int mapRowFromSource(int r) const { /*This works because the row number is shown in column "0". */ return this->data(this->index(r, 0)).toInt(); } /** Get the source model. */ KsViewModel *source() {return _source;} /** * A condition variable used to notify the main thread to update the * search progressbar. */ std::condition_variable _pbCond; /** A mutex used by the condition variable. */ std::mutex _mutex; /** A flag used to stop the serch for all threads. */ bool _searchStop; private: int _searchProgress; /** Trace data array. */ kshark_entry **_data; KsViewModel *_source; size_t _search(int column, const QString &searchText, search_condition_func cond, QList *matchList, int first, int last, QProgressBar *pb, QLabel *l, bool notify); }; /** * Class KsGraphModel provides a model for visualization of trace data. This * class is a wrapper of kshark_trace_histo and is needed only because we want * to use the signals defined in QAbstractTableModel. */ class KsGraphModel : public QAbstractTableModel { public: explicit KsGraphModel(QObject *parent = nullptr); virtual ~KsGraphModel(); /** * This dummy function is an implementation of the pure * virtual method of the abstract model class. */ int rowCount(const QModelIndex &) const override { return _histo.n_bins; } /** * This dummy function is an implementation of the pure * virtual method of the abstract model class. */ int columnCount(const QModelIndex &) const override {return 0;} /** * This dummy function is an implementation of the pure * virtual method of the abstract model class. */ QVariant data(const QModelIndex &index, int role) const override { return {}; } /** Get the kshark_trace_histo object. */ kshark_trace_histo *histo() {return &_histo;} void fill(kshark_entry **entries, size_t n); void shiftForward(size_t n); void shiftBackward(size_t n); void jumpTo(size_t ts); void zoomOut(double r, int mark = -1); void zoomIn(double r, int mark = -1); void quickZoomOut(); void quickZoomIn(uint64_t binSize); void reset(); void update(KsDataStore *data = nullptr); private: kshark_trace_histo _histo; }; /** Defines a default number of bins to be used by the visualization model. */ #define KS_DEFAULT_NBUNS 1024 #endif // _KS_MODELS_H trace-cmd-2.8.3/kernel-shark/src/KsPlotTools.cpp000066400000000000000000000621731351617527000215210ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsPlotTools.cpp * @brief KernelShark Plot tools. */ // C #include // C++ #include #include // OpenGL #include #include // KernelShark #include "KsPlotTools.hpp" namespace KsPlot { float Color::_frequency = .75; /** * @brief Create a default color (black). */ Color::Color() { _col_c.red = _col_c.green = _col_c.blue = 0; } /** * @brief Constructs a RGB color object. * * @param r: The red component of the color. * @param g: The green component of the color. * @param b: The blue component of the color */ Color::Color(uint8_t r, uint8_t g, uint8_t b) { set(r, g, b); } /** * @brief Constructs a RGB color object. * * @param rgb: RGB value. */ Color::Color(int rgb) { set(rgb); } /** * @brief Sets the color. * * @param r: The red component of the color. * @param g: The green component of the color. * @param b: The blue component of the color */ void Color::set(uint8_t r, uint8_t g, uint8_t b) { _col_c.red = r; _col_c.green = g; _col_c.blue = b; } /** * @brief Sets the color. * * @param rgb: RGB value. */ void Color::set(int rgb) { int r = rgb & 0xFF; int g = (rgb >> 8) & 0xFF; int b = (rgb >> 16) & 0xFF; set(r, g, b); } /** * @brief The color is selected from the Rainbow palette. * * @param n: index of the color inside the Rainbow palette. */ void Color::setRainbowColor(int n) { int r = sin(_frequency * n + 0) * 127 + 128; int g = sin(_frequency * n + 2) * 127 + 128; int b = sin(_frequency * n + 4) * 127 + 128; set(r, g, b); } /** * @brief Create a Hash table of Rainbow colors. The sorted Pid values are * mapped to the palette of Rainbow colors. * * @returns ColorTable instance. */ ColorTable getTaskColorTable() { struct kshark_context *kshark_ctx(nullptr); ColorTable colors; int nTasks, pid, *pids, i(0); if (!kshark_instance(&kshark_ctx)) return colors; nTasks = kshark_get_task_pids(kshark_ctx, &pids); if (!nTasks) return colors; std::vector temp_pids(pids, pids + nTasks); std::sort(temp_pids.begin(), temp_pids.end()); free(pids); if (temp_pids[i] == 0) { /* The "Idle" process (pid = 0) will be plotted in black. */ colors[i++] = {}; } for (; i < nTasks; ++i) { pid = temp_pids[i]; colors[pid].setRainbowColor(i - 1); } return colors; } /** * @brief Create a Hash table of Rainbow colors. The CPU Ids are * mapped to the palette of Rainbow colors. * * @returns ColorTable instance. */ ColorTable getCPUColorTable() { struct kshark_context *kshark_ctx(nullptr); ColorTable colors; int nCPUs; if (!kshark_instance(&kshark_ctx)) return colors; nCPUs = tep_get_cpus(kshark_ctx->pevent); for (int i = 0; i < nCPUs; ++i) colors[i].setRainbowColor(i); return colors; } /** * @brief Search the Hash table of Rainbow colors for a particular key (pid). * * @param colors: Input location for the ColorTable instance. * @param pid: the Process Id to search for. * * @returns The Rainbow color of the key "pid". If "pid" does not exist, the * returned color is Black. */ Color getColor(ColorTable *colors, int pid) { auto item = colors->find(pid); if (item != colors->end()) return item->second; return {}; } /** * @brief Create a default Shape. */ Shape::Shape() : _nPoints(0), _points(nullptr) {} /** * @brief Create a Shape defined by "n" points. All points are initialized * at (0, 0). */ Shape::Shape(int n) : _nPoints(n), _points(new(std::nothrow) ksplot_point[n]()) { if (!_points) { _size = 0; fprintf(stderr, "Failed to allocate memory for ksplot_points.\n"); } } /** Copy constructor. */ Shape::Shape(const Shape &s) : _nPoints(0), _points(nullptr) { *this = s; } /** Move constructor. */ Shape::Shape(Shape &&s) : _nPoints(s._nPoints), _points(s._points) { s._nPoints = 0; s._points = nullptr; } /** * @brief Destroy the Shape object. */ Shape::~Shape() { delete[] _points; } /** Assignment operator. */ void Shape::operator=(const Shape &s) { PlotObject::operator=(s); if (s._nPoints != _nPoints) { delete[] _points; _points = new(std::nothrow) ksplot_point[s._nPoints]; } if (_points) { _nPoints = s._nPoints; memcpy(_points, s._points, sizeof(_points) * _nPoints); } else { _nPoints = 0; fprintf(stderr, "Failed to allocate memory for ksplot_points.\n"); } } /** * @brief Set the point of the polygon indexed by "i". * * @param i: the index of the point to be set. * @param x: X coordinate of the point in pixels. * @param y: Y coordinate of the point in pixels. */ void Shape::setPoint(size_t i, int x, int y) { if (i < _nPoints) { _points[i].x = x; _points[i].y = y; } } /** * @brief Set the point of the polygon indexed by "i". * * @param i: the index of the point to be set. * @param p: A ksplot_point object used to provide coordinate values. */ void Shape::setPoint(size_t i, const ksplot_point &p) { setPoint(i, p.x, p.y); } /** * @brief Set the point of the polygon indexed by "i". * * @param i: the index of the point to be set. * @param p: A Point object used to provide coordinate values. */ void Shape::setPoint(size_t i, const Point &p) { setPoint(i, p.x(), p.y()); } /** * @brief Get the point "i". If the point does not exist, the function returns * nullptr. */ const ksplot_point *Shape::getPoint(size_t i) const { if (i < _nPoints) return &_points[i]; return nullptr; } /** * @brief Set the horizontal coordinate of the point "i". * * @param i: the index of the point to be set. * @param x: X coordinate of the point in pixels. */ void Shape::setPointX(size_t i, int x) { if (i < _nPoints) _points[i].x = x; } /** * @brief Set the vertical coordinate of the point "i". * * @param i: the index of the point to be set. * @param y: Y coordinate of the point in pixels. */ void Shape::setPointY(size_t i, int y) { if (i < _nPoints) _points[i].y = y; } /** * @brief Get the horizontal coordinate of the point "i". If the point does * not exist, the function returns 0. */ int Shape::getPointX(size_t i) const { if (i < _nPoints) return _points[i].x; return 0; } /** * @brief Get the vertical coordinate of the point "i". If the point does * not exist, the function returns 0. */ int Shape::getPointY(size_t i) const { if (i < _nPoints) return _points[i].y; return 0; } /** @brief Create a default Point. The point is initialized at (0, 0). */ Point::Point() : Shape(1) {} /** * @brief Create a point. * * @param x: X coordinate of the point in pixels. * @param y: Y coordinate of the point in pixels. */ Point::Point(int x, int y) : Shape(1) { setPoint(0, x, y); } void Point::_draw(const Color &col, float size) const { if (_nPoints == 1) ksplot_draw_point(_points, col.color_c_ptr(), size); } /** * @brief Draw a line between point "a" and point "b". * * @param a: The first finishing point of the line. * @param b: The second finishing point of the line. * @param col: The color of the line. * @param size: The size of the line. */ void drawLine(const Point &a, const Point &b, const Color &col, float size) { ksplot_draw_line(a.point_c_ptr(), b.point_c_ptr(), col.color_c_ptr(), size); } /** * @brief Draw a dashed line between point "a" and point "b". * * @param a: The first finishing point of the line. * @param b: The second finishing point of the line. * @param col: The color of the line. * @param size: The size of the line. * @param period: The period of the dashed line. */ void drawDashedLine(const Point &a, const Point &b, const Color &col, float size, float period) { int dx = b.x() - a.x(), dy = b.y() - a.y(); float mod = sqrt(dx * dx + dy * dy); int n = mod / period; Point p1, p2; for (int i = 0; i < n; ++i) { p1.setX(a.x() + (i + .25) * dx / n); p1.setY(a.y() + (i + .25) * dy / n); p2.setX(a.x() + (i + .75) * dx / n); p2.setY(a.y() + (i + .75) * dy / n); drawLine(p1, p2, col, size); } } /** @brief Create a default line. The two points are initialized at (0, 0). */ Line::Line() : Shape(2) {} /** * @brief Create a line between the point "a" and point "b". * * @param a: first finishing point of the line. * @param b: second finishing point of the line. */ Line::Line(const Point &a, const Point &b) : Shape(2) { setPoint(0, a.x(), a.y()); setPoint(1, b.x(), b.y()); } void Line::_draw(const Color &col, float size) const { if (_nPoints == 2) ksplot_draw_line(&_points[0], &_points[1], col.color_c_ptr(), size); } /** * @brief Create a default polygon. All points are initialized at (0, 0). * * @param n: The number of edges of the polygon. */ Polygon::Polygon(size_t n) : Shape(n), _fill(true) {} void Polygon::_draw(const Color &col, float size) const { if (_fill) ksplot_draw_polygon(_points, _nPoints, col.color_c_ptr(), size); else ksplot_draw_polygon_contour(_points, _nPoints, col.color_c_ptr(), size); } /** * @brief Create a default Mark. */ Mark::Mark() : _dashed(false) { _visible = false; _cpu._color = Color(225, 255, 100); _cpu._size = 5.5f; _task._color = Color(0, 255, 0); _task._size = 5.5f; } void Mark::_draw(const Color &col, float size) const { if (_dashed) drawDashedLine(_a, _b, col, size, 3 * _cpu._size / size); else drawLine(_a, _b, col, size); _cpu.draw(); _task.draw(); } /** * @brief Set the device pixel ratio. * * @param dpr: device pixel ratio value. */ void Mark::setDPR(int dpr) { _size = 1.5 * dpr; _task._size = _cpu._size = 1.5 + 4.0 * dpr; } /** * @brief Set the X coordinate (horizontal) of the Mark. * * @param x: X coordinate of the Makr in pixels. */ void Mark::setX(int x) { _a.setX(x); _b.setX(x); _cpu.setX(x); _task.setX(x); } /** * @brief Set the Y coordinates (vertical) of the Mark's finishing points. * * @param yA: Y coordinate of the first finishing point of the Mark's line. * @param yB: Y coordinate of the second finishing point of the Mark's line. */ void Mark::setY(int yA, int yB) { _a.setY(yA); _b.setY(yB); } /** * @brief Set the Y coordinates (vertical) of the Mark's CPU points. * * @param yCPU: Y coordinate of the Mark's CPU point. */ void Mark::setCPUY(int yCPU) { _cpu.setY(yCPU); } /** * @brief Set the visiblity of the Mark's CPU points. * * @param v: If True, the CPU point will be visible. */ void Mark::setCPUVisible(bool v) { _cpu._visible = v; } /** * @brief Set the Y coordinates (vertical) of the Mark's Task points. * * @param yTask: Y coordinate of the Mark's Task point. */ void Mark::setTaskY(int yTask) { _task.setY(yTask); } /** * @brief Set the visiblity of the Mark's Task points. * * @param v: If True, the Task point will be visible. */ void Mark::setTaskVisible(bool v) { _task._visible = v; } /** * @brief Create a default Bin. */ Bin::Bin() : _idFront(KS_EMPTY_BIN), _idBack(KS_EMPTY_BIN) {} void Bin::_draw(const Color &col, float size) const { drawLine(_base, _val, col, size); } /** * @brief Draw only the "val" Point og the Bin. * * @param size: The size of the point. */ void Bin::drawVal(float size) { _val._size = size; _val.draw(); } /** * @brief Create a default (empty) Graph. */ Graph::Graph() : _histoPtr(nullptr), _bins(nullptr), _size(0), _hMargin(30), _collectionPtr(nullptr), _binColors(nullptr), _ensembleColors(nullptr), _zeroSuppress(false) {} /** * @brief Create a Graph to represent the state of the Vis. model. * * @param histo: Input location for the model descriptor. * @param bct: Input location for the Hash table of bin's colors. * @param ect: Input location for the Hash table of ensemble's colors. */ Graph::Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, KsPlot::ColorTable *ect) : _histoPtr(histo), _bins(new(std::nothrow) Bin[histo->n_bins]), _size(histo->n_bins), _hMargin(30), _collectionPtr(nullptr), _binColors(bct), _ensembleColors(ect), _zeroSuppress(false) { if (!_bins) { _size = 0; fprintf(stderr, "Failed to allocate memory graph's bins.\n"); } _initBins(); } /** * @brief Destroy the Graph object. */ Graph::~Graph() { delete[] _bins; } void Graph::_initBins() { for (int i = 0; i < _size; ++i) { _bins[i]._base.setX(i + _hMargin); _bins[i]._base.setY(0); _bins[i]._val.setX(_bins[i]._base.x()); _bins[i]._val.setY(_bins[i]._base.y()); } } /** * Get the number of bins. */ int Graph::size() { return _size; } /** * @brief Reinitialize the Graph according to the Vis. model. * * @param histo: Input location for the model descriptor. */ void Graph::setModelPtr(kshark_trace_histo *histo) { if (_size != histo->n_bins) { delete[] _bins; _size = histo->n_bins; _bins = new(std::nothrow) Bin[_size]; if (!_bins) { _size = 0; fprintf(stderr, "Failed to allocate memory graph's bins.\n"); } } _histoPtr = histo; _initBins(); } /** * @brief This function will set the Y (vertical) coordinate of the Graph's * base. It is safe to use this function even if the Graph contains * data. * * @param b: Y coordinate of the Graph's base in pixels. */ void Graph::setBase(int b) { int mod; if (!_size) return; if (b == _bins[0]._base.y()) // Nothing to do. return; for (int i = 0; i < _size; ++i) { mod = _bins[i].mod(); _bins[i]._base.setY(b); _bins[i]._val.setY(b + mod); } } /** * @brief Set the vertical size (height) of the Graph. * * @param h: the height of the Graph in pixels. */ void Graph::setHeight(int h) { _height = h; } /** * @brief Set the size of the white space added on both sides of the Graph. * * @param hMargin: the size of the white space in pixels. */ void Graph::setHMargin(int hMargin) { if (!_size) return; if (hMargin == _bins[0]._base.x()) // Nothing to do. return; for (int i = 0; i < _size; ++i) { _bins[i]._base.setX(i + hMargin); _bins[i]._val.setX(_bins[i]._base.x()); } _hMargin = hMargin; } /** * @brief Set the value of a given bin. * * @param bin: Bin Id. * @param val: Bin height in pixels. */ void Graph::setBinValue(int bin, int val) { _bins[bin].setVal(val); } /** * @brief Set the Process Id (Front and Back) a given bin. * * @param bin: Bin Id. * @param pidF: The Process Id detected at the from (first in time) edge of * the bin. * @param pidB: The Process Id detected at the back (last in time) edge of * the bin. */ void Graph::setBinPid(int bin, int pidF, int pidB) { _bins[bin]._idFront = pidF; _bins[bin]._idBack = pidB; } /** * @brief Set the color of a given bin. * * @param bin: Bin Id. * @param col: the color of the bin. */ void Graph::setBinColor(int bin, const Color &col) { _bins[bin]._color = col; } /** * @brief Set the visiblity mask of a given bin. * * @param bin: Bin Id. * @param m: the visiblity mask. */ void Graph::setBinVisMask(int bin, uint8_t m) { _bins[bin]._visMask = m; } /** * @brief Set all fields of a given bin. * * @param bin: Bin Id. * @param pidF: The Process Id detected at the from (first in time) edge of * the bin. * @param pidB: The Process Id detected at the back (last in time) edge of * the bin. * @param col: the color of the bin. * @param m: the visiblity mask. */ void Graph::setBin(int bin, int pidF, int pidB, const Color &col, uint8_t m) { setBinPid(bin, pidF, pidB); setBinValue(bin, _height * .7); setBinColor(bin, col); setBinVisMask(bin, m); } /** * @brief Process a CPU Graph. * * @param cpu: The CPU core. */ void Graph::fillCPUGraph(int cpu) { struct kshark_entry *eFront; int pidFront(0), pidBack(0); int pidBackNoFilter; uint8_t visMask; ssize_t index; int bin; auto lamGetPid = [&] (int bin) { eFront = nullptr; pidFront = ksmodel_get_pid_front(_histoPtr, bin, cpu, true, _collectionPtr, &index); if (index >= 0) eFront = _histoPtr->data[index]; pidBack = ksmodel_get_pid_back(_histoPtr, bin, cpu, true, _collectionPtr, nullptr); pidBackNoFilter = ksmodel_get_pid_back(_histoPtr, bin, cpu, false, _collectionPtr, nullptr); if (pidBack != pidBackNoFilter) pidBack = KS_FILTERED_BIN; visMask = 0x0; if (ksmodel_cpu_visible_event_exist(_histoPtr, bin, cpu, _collectionPtr, &index)) visMask = _histoPtr->data[index]->visible; else if (eFront) visMask = eFront->visible; }; auto lamSetBin = [&] (int bin) { if (pidFront != KS_EMPTY_BIN || pidBack != KS_EMPTY_BIN) { /* This is a regular process. */ setBin(bin, pidFront, pidBack, getColor(_binColors, pidFront), visMask); } else { /* The bin contens no data from this CPU. */ setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN); } }; /* * Check the content of the very first bin and see if the CPU is * active. */ bin = 0; lamGetPid(bin); if (pidFront >= 0) { /* * The CPU is active and this is a regular process. * Set this bin. */ lamSetBin(bin); } else { /* * No data from this CPU in the very first bin. Use the Lower * Overflow Bin to retrieve the Process Id (if any). First * get the Pid back, ignoring the filters. */ pidBackNoFilter = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, cpu, false, _collectionPtr, nullptr); /* Now get the Pid back, applying filters. */ pidBack = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, cpu, true, _collectionPtr, nullptr); if (pidBack != pidBackNoFilter) { /* The Lower Overflow Bin ends with filtered data. */ setBinPid(bin, KS_FILTERED_BIN, KS_FILTERED_BIN); } else { /* * The Lower Overflow Bin ends with data which has * to be plotted. */ setBinPid(bin, pidBack, pidBack); } } /* * The first bin is already processed. The loop starts from the second * bin. */ for (bin = 1; bin < _histoPtr->n_bins; ++bin) { /* * Check the content of this bin and see if the CPU is active. * If yes, retrieve the Process Id. */ lamGetPid(bin); lamSetBin(bin); } } /** * @brief Process a Task Graph. * * @param pid: The Process Id of the Task. */ void Graph::fillTaskGraph(int pid) { int cpuFront, cpuBack(0), pidFront(0), pidBack(0), lastCpu(-1), bin(0); uint8_t visMask; ssize_t index; auto lamSetBin = [&] (int bin) { if (cpuFront >= 0) { KsPlot::Color col = getColor(_binColors, pid); /* Data from the Task has been found in this bin. */ if (pid == pidFront && pid == pidBack) { /* No data from other tasks in this bin. */ setBin(bin, cpuFront, cpuBack, col, visMask); } else if (pid != pidFront && pid != pidBack) { /* * There is some data from other tasks at both * front and back sides of this bin. But we * still want to see this bin drawn. */ setBin(bin, cpuFront, KS_FILTERED_BIN, col, visMask); } else { if (pidFront != pid) { /* * There is some data from another * task at the front side of this bin. */ cpuFront = KS_FILTERED_BIN; } if (pidBack != pid) { /* * There is some data from another * task at the back side of this bin. */ cpuBack = KS_FILTERED_BIN; } setBin(bin, cpuFront, cpuBack, col, visMask); } lastCpu = cpuBack; } else { /* * No data from the Task in this bin. Check the CPU, * previously used by the task. We are looking for * data from another task running on the same CPU, * hence we cannot use the collection of this task. */ int cpuPid = ksmodel_get_pid_back(_histoPtr, bin, lastCpu, false, nullptr, // No collection nullptr); if (cpuPid != KS_EMPTY_BIN) { /* * If the CPU is active and works on another * task break the graph here. */ setBinPid(bin, KS_FILTERED_BIN, KS_EMPTY_BIN); } else { /* * No data from this CPU in the bin. * Continue the graph. */ setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN); } } }; auto lamGetPidCPU = [&] (int bin) { /* Get the CPU used by this task. */ cpuFront = ksmodel_get_cpu_front(_histoPtr, bin, pid, false, _collectionPtr, nullptr); cpuBack = ksmodel_get_cpu_back(_histoPtr, bin, pid, false, _collectionPtr, nullptr); if (cpuFront < 0) { pidFront = pidBack = cpuFront; } else { /* * Get the process Id at the begining and at the end * of the bin. */ pidFront = ksmodel_get_pid_front(_histoPtr, bin, cpuFront, false, _collectionPtr, nullptr); pidBack = ksmodel_get_pid_back(_histoPtr, bin, cpuBack, false, _collectionPtr, nullptr); visMask = 0x0; if (ksmodel_task_visible_event_exist(_histoPtr, bin, pid, _collectionPtr, &index)) { visMask = _histoPtr->data[index]->visible; } } }; /* * Check the content of the very first bin and see if the Task is * active. */ lamGetPidCPU(bin); if (cpuFront >= 0) { /* The Task is active. Set this bin. */ lamSetBin(bin); } else { /* * No data from this Task in the very first bin. Use the Lower * Overflow Bin to retrieve the CPU used by the task (if any). */ cpuFront = ksmodel_get_cpu_back(_histoPtr, LOWER_OVERFLOW_BIN, pid, false, _collectionPtr, nullptr); if (cpuFront >= 0) { /* * The Lower Overflow Bin contains data from this Task. * Now look again in the Lower Overflow Bin and Bim 0 * and find the Pid of the last active task on the same * CPU. */ int pidCpu0, pidCpuLOB; pidCpu0 = ksmodel_get_pid_back(_histoPtr, 0, cpuFront, false, _collectionPtr, nullptr); pidCpuLOB = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, cpuFront, false, _collectionPtr, nullptr); if (pidCpu0 < 0 && pidCpuLOB == pid) { /* * The Task is the last one running on this * CPU. Set the Pid of the bin. In this case * the very first bin is empty but we derive * the Process Id from the Lower Overflow Bin. */ setBinPid(bin, cpuFront, cpuFront); lastCpu = cpuFront; } else { setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN); } } } /* * The first bin is already processed. The loop starts from the second * bin. */ for (bin = 1; bin < _histoPtr->n_bins; ++bin) { lamGetPidCPU(bin); /* Set the bin accordingly. */ lamSetBin(bin); } } /** * @brief Draw the Graph * * @param size: The size of the lines of the individual Bins. */ void Graph::draw(float size) { int lastPid(-1), b(0), boxH(_height * .3); Rectangle taskBox; /* * Start by drawing a line between the base points of the first and * the last bin. */ drawLine(_bins[0]._base, _bins[_size - 1]._base, {}, size); /* Draw as vartical lines all bins containing data. */ for (int i = 0; i < _size; ++i) if (_bins[i]._idFront >= 0 || _bins[i]._idBack >= 0) if (_bins[i]._visMask & KS_EVENT_VIEW_FILTER_MASK) { _bins[i]._size = size; _bins[i].draw(); } auto lamCheckEnsblVal = [this] (int v) { return v > 0 || (v == 0 && !this->_zeroSuppress); }; /* * Draw colored boxes for processes. First find the first bin, which * contains data and determine its PID. */ for (; b < _size; ++b) { if (lamCheckEnsblVal(_bins[b]._idBack)) { lastPid = _bins[b]._idFront; /* * Initialize a box starting from this bin. * The color of the taskBox corresponds to the Pid * of the process. */ taskBox._color = getColor(_ensembleColors, lastPid); taskBox.setPoint(0, _bins[b]._base.x(), _bins[b]._base.y() - boxH); taskBox.setPoint(1, _bins[b]._base.x(), _bins[b]._base.y()); break; } } for (; b < _size; ++b) { if (_bins[b]._idFront == KS_EMPTY_BIN && _bins[b]._idBack == KS_EMPTY_BIN) { /* * This bin is empty. If a colored taskBox is already * initialized, it will be extended. */ continue; } if (_bins[b]._idFront != _bins[b]._idBack || _bins[b]._idFront != lastPid || _bins[b]._idBack != lastPid) { /* A new process starts here. */ if (b > 0 && lamCheckEnsblVal(lastPid)) { /* * There is another process running up to this * point. Close its colored box here and draw. */ taskBox.setPoint(3, _bins[b]._base.x() - 1, _bins[b]._base.y() - boxH); taskBox.setPoint(2, _bins[b]._base.x() - 1, _bins[b]._base.y()); taskBox.draw(); } if (lamCheckEnsblVal(_bins[b]._idBack)) { /* * This is a regular process. Initialize * colored box starting from this bin. */ taskBox._color = getColor(_ensembleColors, _bins[b]._idBack); taskBox.setPoint(0, _bins[b]._base.x() - 1, _bins[b]._base.y() - boxH); taskBox.setPoint(1, _bins[b]._base.x() - 1, _bins[b]._base.y()); } lastPid = _bins[b]._idBack; } } if (lamCheckEnsblVal(lastPid) > 0) { /* * This is the end of the Graph and we have a process running. * Close its colored box and draw. */ taskBox.setPoint(3, _bins[_size - 1]._base.x(), _bins[_size - 1]._base.y() - boxH); taskBox.setPoint(2, _bins[_size - 1]._base.x(), _bins[_size - 1]._base.y()); taskBox.draw(); } } }; // KsPlot trace-cmd-2.8.3/kernel-shark/src/KsPlotTools.hpp000066400000000000000000000262701351617527000215240ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsPlotTools.hpp * @brief KernelShark Plot tools. */ #ifndef _KS_PLOT_TOOLS_H #define _KS_PLOT_TOOLS_H // C++ #include #include // KernelShark #include "libkshark.h" #include "libkshark-plot.h" #include "libkshark-model.h" namespace KsPlot { /** This class represents a RGB color. */ class Color { public: Color(); Color(uint8_t r, uint8_t g, uint8_t b); Color(int rgb); /** @brief Get the Red coordinate of the color. */ uint8_t r() const {return _col_c.red;} /** @brief Get the Green coordinate of the color. */ uint8_t g() const {return _col_c.green;} /** @brief Get the Blue coordinate of the color. */ uint8_t b() const {return _col_c.blue;} void set(uint8_t r, uint8_t g, uint8_t b); void set(int rgb); void setRainbowColor(int n); /** * @brief Get the C struct defining the RGB color. */ const ksplot_color *color_c_ptr() const {return &_col_c;} /** * @brief Set the frequency value used to generate the Rainbow * palette. */ static void setRainbowFrequency(float f) {_frequency = f;} /** * @brief Get the frequency value used to generate the Rainbow * palette. */ static float getRainbowFrequency() {return _frequency;} private: ksplot_color _col_c; /** The frequency value used to generate the Rainbow palette. */ static float _frequency; }; /** Hash table of colors. */ typedef std::unordered_map ColorTable; ColorTable getTaskColorTable(); ColorTable getCPUColorTable(); Color getColor(ColorTable *colors, int pid); /** Represents an abstract graphical element. */ class PlotObject { public: /** * @brief Create a default object. */ PlotObject() : _visible(true), _size(2.) {} /** * @brief Destroy the object. Keep this destructor virtual. */ virtual ~PlotObject() {} /** Generic function used to draw different objects. */ void draw() const { if (_visible) _draw(_color, _size); } /** Is this object visible. */ bool _visible; /** The color of the object. */ Color _color; /** The size of the object. */ float _size; private: virtual void _draw(const Color &col, float s) const = 0; }; /** List of graphical element. */ typedef std::forward_list PlotObjList; class Point; /** Represents an abstract shape. */ class Shape : public PlotObject { public: Shape(); Shape(int n); Shape(const Shape &); Shape(Shape &&); /* Keep this destructor virtual. */ virtual ~Shape(); void operator=(const Shape &s); void setPoint(size_t i, int x, int y); void setPoint(size_t i, const ksplot_point &p); void setPoint(size_t i, const Point &p); const ksplot_point *getPoint(size_t i) const; void setPointX(size_t i, int x); void setPointY(size_t i, int y); int getPointX(size_t i) const; int getPointY(size_t i) const; /** * @brief Get the number of point used to define the polygon. */ size_t pointCount() const {return _nPoints;} protected: /** The number of point used to define the polygon. */ size_t _nPoints; /** The array of point used to define the polygon. */ ksplot_point *_points; }; /** This class represents a 2D poin. */ class Point : public Shape { public: Point(); Point(int x, int y); /** * @brief Destroy the Point object. Keep this destructor virtual. */ virtual ~Point() {} /** @brief Get the horizontal coordinate of the point. */ int x() const {return getPointX(0);} /** @brief Get the vertical coordinate of the point. */ int y() const {return getPointY(0);} /** @brief Set the horizontal coordinate of the point. */ void setX(int x) {setPointX(0, x);} /** @brief Set the vertical coordinate of the point. */ void setY(int y) {setPointY(0, y);} /** * @brief Set the coordinats of the point. * * @param x: horizontal coordinate of the point in pixels. * @param y: vertical coordinate of the point in pixels. */ void set(int x, int y) {setPoint(0, x, y);} /** * @brief Get the C struct defining the point. */ const ksplot_point *point_c_ptr() const {return getPoint(0);} private: void _draw(const Color &col, float size = 1.) const override; }; void drawLine(const Point &a, const Point &b, const Color &col, float size); void drawDashedLine(const Point &a, const Point &b, const Color &col, float size, float period); /** This class represents a straight line. */ class Line : public Shape { public: Line(); Line(const Point &a, const Point &b); /** * @brief Destroy the Line object. Keep this destructor virtual. */ virtual ~Line() {} /** * @brief Set the coordinats of the first finishing point of the * line. * * @param x: horizontal coordinate of the point in pixels. * @param y: vertical coordinate of the point in pixels. */ void setA(int x, int y) { setPoint(0, x, y);} /** @brief Get the first finishing point of the line. */ const ksplot_point *getA() const {return getPoint(0);} /** * @brief Set the coordinats of the second finishing point of the * line. * * @param x: horizontal coordinate of the point in pixels. * @param y: vertical coordinate of the point in pixels. */ void setB(int x, int y) {setPoint(1, x, y);} /** @brief Get the second finishing point of the line. */ const ksplot_point *getB() const {return getPoint(1);} private: void _draw(const Color &col, float size = 1.) const override; }; /** This class represents a polygon. */ class Polygon : public Shape { public: Polygon(size_t n); /** * @brief Destroy the polygon object. Keep this destructor virtual. */ virtual ~Polygon() {} /** * @brief Specify the way the polygon will be drawn. * * @param f: If True, the area of the polygon will be colored. * Otherwise only the contour of the polygon will be plotted. */ void setFill(bool f) {_fill = f;} private: Polygon() = delete; void _draw(const Color &, float size = 1.) const override; /** * If True, the area of the polygon will be colored. Otherwise only * the contour of the polygon will be plotted. */ bool _fill; }; /** This class represents a triangle. */ class Triangle : public Polygon { public: /** * Create a default triangle. All points are initialized at (0, 0). */ Triangle() : Polygon(3) {} /** Destroy the Triangle object. Keep this destructor virtual. */ virtual ~Triangle() {} }; /** This class represents a rectangle. */ class Rectangle : public Polygon { public: /** * Create a default Rectangle. All points are initialized at (0, 0). */ Rectangle() : Polygon(4) {} /** Destroy the Rectangle object. Keep this destructor virtual. */ virtual ~Rectangle() {} }; /** * This class represents the graphical element of the KernelShark GUI marker. */ class Mark : public PlotObject { public: Mark(); /** * @brief Destroy the Mark object. Keep this destructor virtual. */ virtual ~Mark() {} void setDPR(int dpr); void setX(int x); void setY(int yA, int yB); void setCPUY(int yCPU); void setCPUVisible(bool v); void setTaskY(int yTask); void setTaskVisible(bool v); /** If True, the Mark will be plotted as a dashed line. */ void setDashed(bool d) {_dashed = d;} private: void _draw(const Color &col, float size = 1.) const override; /** First finishing point of the Mark's line. */ Point _a; /** Second finishing point of the Mark's line. */ Point _b; /** A point indicating the position of the Mark in a CPU graph. */ Point _cpu; /** A point indicating the position of the Mark in a Task graph. */ Point _task; /* If True, plot the Mark as a dashed line. */ bool _dashed; }; /** This class represents a KernelShark graph's bin. */ class Bin : public PlotObject { public: Bin(); /** * @brief Destroy the Bin object. Keep this destructor virtual. */ virtual ~Bin() {} void drawVal(float size = 2.); /** Get the height (module) of the line, representing the Bin. */ int mod() {return _val.y() - _base.y();} /** @brief Set the vertical coordinate of the "val" Point. */ void setVal(int v) {_val.setY(_base.y() - v); } /** * The Id value (pid or cpu) detected at the front (first in time) edge * of the bin. */ int _idFront; /** * The Id value (pid or cpu) detected at the back (last in time) edge * of the bin. */ int _idBack; /** Lower finishing point of the line, representing the Bin. */ Point _base; /** Upper finishing point of the line, representing the Bin. */ Point _val; /** A bit mask controlling the visibility of the Bin. */ uint8_t _visMask; private: void _draw(const Color &col, float size = 1.) const override; }; /** This class represents a KernelShark graph. */ class Graph { public: Graph(); /* * Disable copying. We can enable the Copy Constructor in the future, * but only if we really need it for some reason. */ Graph(const Graph &) = delete; /* Disable moving. Same as copying.*/ Graph(Graph &&) = delete; Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, KsPlot::ColorTable *ect); /* Keep this destructor virtual. */ virtual ~Graph(); int size(); void setModelPtr(kshark_trace_histo *histo); /** * @brief Provide the Graph with a Data Collection. The collection * will be used to optimise the processing of the content of * the bins. * * @param col: Input location for the data collection descriptor. */ void setDataCollectionPtr(kshark_entry_collection *col) { _collectionPtr = col; } /** @brief Set the Hash table of Task's colors. */ void setBinColorTablePtr(KsPlot::ColorTable *ct) {_binColors = ct;} void fillCPUGraph(int cpu); void fillTaskGraph(int pid); void draw(float s = 1); void setBase(int b); /** @brief Get the vertical coordinate of the Graph's base. */ int getBase() const {return _bins[0]._base.y();} void setHeight(int h); /** @brief Get the vertical size (height) of the Graph. */ int getHeight() const {return _height;} void setBinValue(int bin, int val); void setBinPid(int bin, int pidF, int pidB); void setBinColor(int bin, const Color &col); void setBinVisMask(int bin, uint8_t m); void setBin(int bin, int pidF, int pidB, const Color &col, uint8_t m); /** @brief Get a particular bin. */ const Bin &getBin(int bin) const {return _bins[bin];} void setHMargin(int hMargin); /** * Check if this graph is Zero Suppressed. Zero Suppressed means that * bins having Id value = 0 (Idle task records) are not grouped * together. */ bool zeroSuppressed(bool zs) {return _zeroSuppress;} /** * Set Zero Suppression. If True, the bins having Id value = 0 (Idle * task records) are not grouped together. */ void setZeroSuppressed(bool zs) {_zeroSuppress = zs;} private: /** Pointer to the model descriptor object. */ kshark_trace_histo *_histoPtr; /** An array of Bins. */ Bin *_bins; /** The number of Bins. */ int _size; /** * The size (in pixels) of the white space added on both sides of * the Graph. */ int _hMargin; /** The vertical size (height) of the Graph. */ int _height; /** Pointer to the data collection object. */ kshark_entry_collection *_collectionPtr; /** Hash table of bin's colors. */ ColorTable *_binColors; /** Hash table of ensemble's colors. */ ColorTable *_ensembleColors; bool _zeroSuppress; void _initBins(); }; }; // KsPlot #endif /* _KS_PLOT_TOOLS_H */ trace-cmd-2.8.3/kernel-shark/src/KsPlugins.hpp000066400000000000000000000020471351617527000212020ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file KsPlugins.hpp * @brief KernelShark C++ plugin declarations. */ #ifndef _KS_PLUGINS_H #define _KS_PLUGINS_H // KernelShark #include "libkshark-model.h" #include "KsPlotTools.hpp" /** * Structure representing the vector of C++ arguments of the drawing function * of a plugin. */ struct KsCppArgV { /** Pointer to the model descriptor object. */ kshark_trace_histo *_histo; /** Pointer to the graph object. */ KsPlot::Graph *_graph; /** * Pointer to the list of shapes. All shapes created by the plugin * will be added to this list. */ KsPlot::PlotObjList *_shapes; /** * Convert the "this" pointer of the C++ argument vector into a * C pointer. */ kshark_cpp_argv *toC() { return reinterpret_cast(this); } }; /** * Macro used to convert a C pointer into a pointer to KsCppArgV (C++ struct). */ #define KS_ARGV_TO_CPP(a) (reinterpret_cast(a)) #endif trace-cmd-2.8.3/kernel-shark/src/KsQuickContextMenu.cpp000066400000000000000000000173741351617527000230330ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file KsQuickContextMenu.cpp * @brief Quick Context Menus for KernelShark. */ #include "KsQuickContextMenu.hpp" #include "KsTraceGraph.hpp" /** * @brief Create KsQuickMarkerMenu. * * @param dm: The State machine of the Dual marker. * @param parent: The parent of this widget. */ KsQuickMarkerMenu::KsQuickMarkerMenu(KsDualMarkerSM *dm, QWidget *parent) : QMenu("Context Menu", parent), _dm(dm), _deselectAction(this) { if (dm->activeMarker()._isSet) { addSection("Marker menu"); _deselectAction.setText("Deselect"); _deselectAction.setShortcut(tr("Ctrl+D")); _deselectAction.setStatusTip(tr("Deselect marker")); connect(&_deselectAction, &QAction::triggered, this, &KsQuickMarkerMenu::deselect); addAction(&_deselectAction); } } /** * @brief Create KsQuickContextMenu. * * @param data: Input location for the KsDataStore object. * @param row: The index of the entry used to initialize the menu. * @param dm: The State machine of the Dual marker. * @param parent: The parent of this widget. */ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row, KsDualMarkerSM *dm, QWidget *parent) : KsQuickMarkerMenu(dm, parent), _data(data), _row(row), _graphSyncCBox(nullptr), _listSyncCBox(nullptr), _hideTaskAction(this), _showTaskAction(this), _hideEventAction(this), _showEventAction(this), _hideCPUAction(this), _showCPUAction(this), _addCPUPlotAction(this), _addTaskPlotAction(this), _removeCPUPlotAction(this), _removeTaskPlotAction(this), _clearAllFilters(this) { typedef void (KsQuickContextMenu::*mfp)(); QString taskName, parentName, descr; KsTraceGraph *graphs; int pid, cpu; if (!parent || !_data) return; taskName = kshark_get_task_easy(_data->rows()[_row]); pid = kshark_get_pid_easy(_data->rows()[_row]); cpu = _data->rows()[_row]->cpu; auto lamAddAction = [this, &descr] (QAction *action, mfp mf) { action->setText(descr); connect(action, &QAction::triggered, this, mf); addAction(action); }; parentName = parent->metaObject()->className(); addSection("Pointer filter menu"); descr = "Show task ["; descr += taskName; descr += "-"; descr += QString("%1").arg(pid); descr += "] only"; lamAddAction(&_showTaskAction, &KsQuickContextMenu::_showTask); descr = "Hide task ["; descr += taskName; descr += "-"; descr += QString("%1").arg(pid); descr += "]"; lamAddAction(&_hideTaskAction, &KsQuickContextMenu::_hideTask); descr = "Show event ["; descr += kshark_get_event_name_easy(_data->rows()[_row]); descr += "] only"; lamAddAction(&_showEventAction, &KsQuickContextMenu::_showEvent); descr = "Hide event ["; descr += kshark_get_event_name_easy(_data->rows()[_row]); descr += "]"; lamAddAction(&_hideEventAction, &KsQuickContextMenu::_hideEvent); if (parentName == "KsTraceViewer") { descr = QString("Show CPU [%1] only").arg(cpu); lamAddAction(&_showCPUAction, &KsQuickContextMenu::_showCPU); } descr = QString("Hide CPU [%1]").arg(_data->rows()[_row]->cpu); lamAddAction(&_hideCPUAction, &KsQuickContextMenu::_hideCPU); descr = "Clear all filters"; lamAddAction(&_clearAllFilters, &KsQuickContextMenu::_clearFilters); addSection("Pointer plot menu"); if (parentName == "KsTraceViewer") { descr = "Add ["; descr += taskName; descr += "-"; descr += QString("%1").arg(pid); descr += "] plot"; lamAddAction(&_addTaskPlotAction, &KsQuickContextMenu::_addTaskPlot); } if (parentName == "KsTraceGraph" && (graphs = dynamic_cast(parent))) { if (graphs->glPtr()->_taskList.contains(pid)) { descr = "Remove ["; descr += taskName; descr += "-"; descr += QString("%1").arg(_data->rows()[_row]->pid); descr += "] plot"; lamAddAction(&_removeTaskPlotAction, &KsQuickContextMenu::_removeTaskPlot); } else { descr = "Add ["; descr += taskName; descr += "-"; descr += QString("%1").arg(_data->rows()[_row]->pid); descr += "] plot"; lamAddAction(&_addTaskPlotAction, &KsQuickContextMenu::_addTaskPlot); } if (graphs->glPtr()->_cpuList.contains(cpu)) { descr = "Remove [CPU "; descr += QString("%1").arg(cpu); descr += "] plot"; lamAddAction(&_removeCPUPlotAction, &KsQuickContextMenu::_removeCPUPlot); } else { descr = "Add [CPU "; descr += QString("%1").arg(cpu); descr += "] plot"; lamAddAction(&_addCPUPlotAction, &KsQuickContextMenu::_addCPUPlot); } } } void KsQuickContextMenu::_hideTask() { int pid = kshark_get_pid_easy(_data->rows()[_row]); kshark_context *kshark_ctx(nullptr); QVector vec; if (!kshark_instance(&kshark_ctx)) return; vec =_getFilterVector(kshark_ctx->hide_task_filter, pid); _data->applyNegTaskFilter(vec); } void KsQuickContextMenu::_showTask() { int pid = kshark_get_pid_easy(_data->rows()[_row]); _data->applyPosTaskFilter(QVector(1, pid)); } void KsQuickContextMenu::_hideEvent() { int eventId = kshark_get_event_id_easy(_data->rows()[_row]); kshark_context *kshark_ctx(nullptr); QVector vec; if (!kshark_instance(&kshark_ctx)) return; vec =_getFilterVector(kshark_ctx->hide_event_filter, eventId); _data->applyNegEventFilter(vec); } void KsQuickContextMenu::_showEvent() { int eventId = kshark_get_event_id_easy(_data->rows()[_row]); _data->applyPosEventFilter(QVector(1, eventId)); } void KsQuickContextMenu::_showCPU() { int cpu = _data->rows()[_row]->cpu; _data->applyPosCPUFilter(QVector(1, cpu)); } void KsQuickContextMenu::_hideCPU() { kshark_context *kshark_ctx(nullptr); QVector vec; if (!kshark_instance(&kshark_ctx)) return; vec =_getFilterVector(kshark_ctx->hide_cpu_filter, _data->rows()[_row]->cpu); _data->applyNegCPUFilter(vec); } QVector KsQuickContextMenu::_getFilterVector(tracecmd_filter_id *filter, int newId) { QVector vec = KsUtils::getFilterIds(filter); if (!vec.contains(newId)) vec.append(newId); return vec; } void KsQuickContextMenu::_addTaskPlot() { int pid = kshark_get_pid_easy(_data->rows()[_row]); emit addTaskPlot(pid); } void KsQuickContextMenu::_addCPUPlot() { emit addCPUPlot(_data->rows()[_row]->cpu); } void KsQuickContextMenu::_removeTaskPlot() { int pid = kshark_get_pid_easy(_data->rows()[_row]); emit removeTaskPlot(pid); } void KsQuickContextMenu::_removeCPUPlot() { emit removeCPUPlot(_data->rows()[_row]->cpu); } /** * @brief Create KsRmPlotContextMenu. * * @param dm: The State machine of the Dual marker. * @param parent: The parent of this widget. */ KsRmPlotContextMenu::KsRmPlotContextMenu(KsDualMarkerSM *dm, QWidget *parent) : KsQuickMarkerMenu(dm, parent), _removePlotAction(this) { addSection("Plots"); connect(&_removePlotAction, &QAction::triggered, this, &KsRmPlotContextMenu::removePlot); addAction(&_removePlotAction); } /** * @brief Create KsRmCPUPlotMenu. * * @param dm: The State machine of the Dual marker. * @param cpu : CPU Id. * @param parent: The parent of this widget. */ KsRmCPUPlotMenu::KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu, QWidget *parent) : KsRmPlotContextMenu(dm, parent) { _removePlotAction.setText(QString("Remove [CPU %1]").arg(cpu)); } /** * @brief Create KsRmTaskPlotMenu. * * @param dm: The State machine of the Dual marker. * @param pid: Process Id. * @param parent: The parent of this widget. */ KsRmTaskPlotMenu::KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid, QWidget *parent) : KsRmPlotContextMenu(dm, parent) { kshark_context *kshark_ctx(nullptr); QString descr("Remove [ "); if (!kshark_instance(&kshark_ctx)) return; descr += tep_data_comm_from_pid(kshark_ctx->pevent, pid); descr += "-"; descr += QString("%1").arg(pid); descr += "] plot"; _removePlotAction.setText(descr); } trace-cmd-2.8.3/kernel-shark/src/KsQuickContextMenu.hpp000066400000000000000000000055761351617527000230410ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file KsQuickContextMenu.hpp * @brief Quick Context Menus for KernelShark. */ #ifndef _KS_QUICK_CTX_MENU_H #define _KS_QUICK_CTX_MENU_H #include "KsDualMarker.hpp" #include "KsUtils.hpp" #include "KsGLWidget.hpp" /** * The KsQuickMarkerMenu class provides menu for quick Dual Marker related * actions. */ class KsQuickMarkerMenu : public QMenu { Q_OBJECT public: KsQuickMarkerMenu(KsDualMarkerSM *dm, QWidget *parent = nullptr); signals: /** Signal to deselect the active marker. */ void deselect(); private: KsDualMarkerSM *_dm; QAction _deselectAction; }; /** * The KsQuickFilterMenu class provides a menu for easy filtering and plotting. * The menu is initialized from a single kshark_entry and uses the content of * this entry to provides quick actions for filtering and plottin. */ class KsQuickContextMenu : public KsQuickMarkerMenu { Q_OBJECT public: KsQuickContextMenu() = delete; KsQuickContextMenu(KsDataStore *data, size_t row, KsDualMarkerSM *dm, QWidget *parent = nullptr); signals: /** Signal to add a task plot. */ void addTaskPlot(int); /** Signal to add a CPU plot. */ void addCPUPlot(int); /** Signal to remove a task plot. */ void removeTaskPlot(int); /** Signal to remove a CPU plot. */ void removeCPUPlot(int); private: void _hideTask(); void _showTask(); void _hideEvent(); void _showEvent(); void _showCPU(); void _hideCPU(); void _addCPUPlot(); void _addTaskPlot(); void _removeCPUPlot(); void _removeTaskPlot(); QVector _getFilterVector(tracecmd_filter_id *filter, int newId); void _clearFilters() {_data->clearAllFilters();} KsDataStore *_data; size_t _row; QCheckBox *_graphSyncCBox, *_listSyncCBox; QAction _hideTaskAction, _showTaskAction; QAction _hideEventAction, _showEventAction; QAction _hideCPUAction, _showCPUAction; QAction _addCPUPlotAction; QAction _addTaskPlotAction; QAction _removeCPUPlotAction; QAction _removeTaskPlotAction; QAction _clearAllFilters; }; /** * The KsQuickMarkerMenu is a baser class for Remove Plot menus. */ class KsRmPlotContextMenu : public KsQuickMarkerMenu { Q_OBJECT public: KsRmPlotContextMenu() = delete; KsRmPlotContextMenu(KsDualMarkerSM *dm, QWidget *parent = nullptr); signals: /** Signal to remove a plot. */ void removePlot(int); protected: /** Menu action. */ QAction _removePlotAction; }; /** * The KsQuickMarkerMenu class provides CPU Plot remove menus. */ struct KsRmCPUPlotMenu : public KsRmPlotContextMenu { KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu, QWidget *parent = nullptr); }; /** * The KsQuickMarkerMenu class provides Task Plot remove menus. */ struct KsRmTaskPlotMenu : public KsRmPlotContextMenu { KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid, QWidget *parent = nullptr); }; #endif trace-cmd-2.8.3/kernel-shark/src/KsSearchFSM.cpp000066400000000000000000000125361351617527000213330ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file KsSearchFSM.cpp * @brief Finite-state machine for searching in trace data. */ // KernelShark #include "KsSearchFSM.hpp" #include "KsUtils.hpp" #include "KsTraceViewer.hpp" #include "KsWidgetsLib.hpp" static bool notHaveCond(const QString &searchText, const QString &itemText) { return !itemText.contains(searchText, Qt::CaseInsensitive); } static bool containsCond(const QString &searchText, const QString &itemText) { return itemText.contains(searchText, Qt::CaseInsensitive); } static bool matchCond(const QString &searchText, const QString &itemText) { return (itemText.compare(searchText, Qt::CaseInsensitive) == 0); } static bool noCond(const QString &searchText, const QString &itemText) { return false; } /** Create a Finite-state machine for searching. */ KsSearchFSM::KsSearchFSM(QWidget *parent) : _currentState(new NotDone), _lastRowSearched(0), _searchProgBar(parent), _searchCountLabel("", parent), _columnComboBox(parent), _selectComboBox(parent), _searchLineEdit(parent), _prevButton("Prev", parent), _nextButton("Next", parent), _searchRestartButton(QIcon::fromTheme("media-playback-start"), "", parent), // _searchStopButton(QIcon::fromTheme("media-playback-pause"), "", parent), _searchStopButton(QIcon::fromTheme("process-stop"), "", parent), _cond(nullptr), _pbAction(nullptr), _searchStopAction(nullptr), _searchRestartAction(nullptr) { int bWidth = FONT_WIDTH * 6; _nextButton.setFixedWidth(bWidth); _prevButton.setFixedWidth(bWidth); _searchProgBar.setMaximumWidth(FONT_WIDTH * 10); _searchProgBar.setRange(0, KS_PROGRESS_BAR_MAX); _selectComboBox.addItem("contains"); _selectComboBox.addItem("full match"); _selectComboBox.addItem("does not have"); updateCondition(); } /** * Position all buttons and labels of the Finite-state machine for searching * in a toolbar. */ void KsSearchFSM::placeInToolBar(QToolBar *tb) { tb->addWidget(&_columnComboBox); tb->addWidget(&_selectComboBox); tb->addWidget(&_searchLineEdit); tb->addSeparator(); tb->addWidget(&_nextButton); tb->addWidget(&_prevButton); tb->addSeparator(); _pbAction = tb->addWidget(&_searchProgBar); _pbAction->setVisible(false); tb->addWidget(&_searchCountLabel); _searchStopAction = tb->addWidget(&_searchStopButton); _searchStopAction->setVisible(false); _searchRestartAction = tb->addWidget(&_searchRestartButton); _searchRestartAction->setVisible(false); tb->addSeparator(); } /** * Update the Matching condition function of the search according to the user * input. */ void KsSearchFSM::updateCondition() { int xSelect = _selectComboBox.currentIndex(); switch (xSelect) { case Condition::Containes: _cond = containsCond; return; case Condition::Match: _cond = matchCond; return; case Condition::NotHave: _cond = notHaveCond; return; default: _cond = noCond; return; } } void KsSearchFSM ::_lockSearchPanel(bool lock) { _columnComboBox.setEnabled(!lock); _selectComboBox.setEnabled(!lock); _searchLineEdit.setReadOnly(lock); _prevButton.setEnabled(!lock); _nextButton.setEnabled(!lock); // _graphFollowsCheckBox.setEnabled(!lock); } /** Act according to the provided input. */ void NotDone::handleInput(KsSearchFSM* sm, sm_input_t input) { switch(input) { case sm_input_t::Start: sm->_lastRowSearched = -1; sm->lockSearchPanel(); sm->updateCondition(); sm->progressBarVisible(true); if (sm->column() == KsViewModel::TRACE_VIEW_COL_INFO || sm->column() == KsViewModel::TRACE_VIEW_COL_LAT) sm->searchStopVisible(true); sm->changeState(std::shared_ptr(new InProgress)); break; case sm_input_t::Finish: sm->changeState(std::shared_ptr(new Done)); break; default: /* Ignore the input. */ break; } } /** Act according to the provided input. */ void Paused::handleInput(KsSearchFSM* sm, sm_input_t input) { switch(input) { case sm_input_t::Start: sm->lockSearchPanel(); sm->searchStopVisible(true); sm->searchRestartVisible(false); sm->changeState(std::shared_ptr(new InProgress)); break; case sm_input_t::Change: sm->_searchProgBar.setValue(0); sm->_searchCountLabel.setText(""); sm->progressBarVisible(false); sm->searchRestartVisible(false); sm->changeState(std::shared_ptr(new NotDone)); break; default: /* Ignore the input. */ break; } } /** Act according to the provided input. */ void InProgress::handleInput(KsSearchFSM* sm, sm_input_t input) { auto lamUnlock = [&sm] () { sm->searchStopVisible(false); sm->unlockSearchPanel(); }; switch(input) { case sm_input_t::Stop: lamUnlock(); sm->searchRestartVisible(true); sm->changeState(std::shared_ptr(new Paused)); break; case sm_input_t::Finish: lamUnlock(); sm->progressBarVisible(false); sm->changeState(std::shared_ptr(new Done)); break; default: /* Ignore the input. */ break; } } /** Act according to the provided input. */ void Done::handleInput(KsSearchFSM* sm, sm_input_t i) { switch(i) { case sm_input_t::Change: sm->_searchProgBar.setValue(0); sm->progressBarVisible(false); sm->_searchCountLabel.setText(""); sm->searchStopVisible(false); sm->searchRestartVisible(false); sm->changeState(std::shared_ptr(new NotDone)); break; default: /* Ignore the input. */ break; } } trace-cmd-2.8.3/kernel-shark/src/KsSearchFSM.hpp000066400000000000000000000111461351617527000213340ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file KsSearchFSM.hpp * @brief Finite-state machine for searching in trace data. */ #ifndef _KS_SEARCH_FSM_H #define _KS_SEARCH_FSM_H // C++11 #include // Qt #include /** Matching condition function type. To be user for searching. */ typedef bool (*search_condition_func)(const QString &, const QString &); /** State Identifiers of the Finite-state machine for searching. */ enum class search_state_t { /** Identifier of the "NotDone" state. */ NotDone_s = 0, /** Identifier of the "InProgress" state. */ InProgress_s = 1, /** Identifier of the "Paused" state. */ Paused_s = 2, /** Identifier of the "Done" state. */ Done_s = 3 }; /** Inputs of the Finite-state machine for searching. */ enum class sm_input_t { Start = 0, Stop = 1, Finish = 2, Change = 3 }; class KsSearchFSM; /** * State provides a base class for the states of the Finite-state machine for * searching. */ struct State { /** Virtual destructor. */ virtual ~State() {} /** * Act according to the provided input. This is a pure virtual * function. */ virtual void handleInput(KsSearchFSM* sm, sm_input_t i) = 0; /** * Get the identifier of this state. This is a pure virtual function. */ virtual search_state_t id() = 0; }; /** "NotDone" state. */ struct NotDone : public State { void handleInput(KsSearchFSM* sm, sm_input_t i) override; search_state_t id() override {return search_state_t::NotDone_s;} }; /** "InProgress" state. */ struct InProgress : public State { void handleInput(KsSearchFSM* sm, sm_input_t i) override; /** Get the identifier of this state. */ search_state_t id() override {return search_state_t::InProgress_s;} }; /** "Paused" state. */ struct Paused : public State { void handleInput(KsSearchFSM* sm, sm_input_t i) override; /** Get the identifier of this state. */ search_state_t id() override {return search_state_t::Paused_s;} }; /** "Done" state. */ struct Done : public State { void handleInput(KsSearchFSM* sm, sm_input_t i) override; /** Get the identifier of this state. */ search_state_t id() override {return search_state_t::Done_s;} }; /** Finite-state machine for searching. */ class KsSearchFSM : public QWidget { Q_OBJECT public: explicit KsSearchFSM(QWidget *parent = nullptr); void placeInToolBar(QToolBar *tb); /** Act according to the provided input. */ void handleInput(sm_input_t input) { _currentState->handleInput(this, input); } /** Switch the state. */ void changeState(std::shared_ptr newState) { _currentState = newState; } /** Get the identifier of the Current state. */ search_state_t getState() const {return _currentState->id();} /** Get the data column to search in. */ int column() const {return _columnComboBox.currentIndex();} /** Get the Matching condition function. */ search_condition_func condition() const {return _cond;} /** Get the text to search for. */ QString searchText() const {return _searchLineEdit.text();} /** Set the value of the Search Progress Bar. */ void setProgress(int v) {_searchProgBar.setValue(v);} /** Increment the value of the Search Progress Bar. */ void incrementProgress() { _searchProgBar.setValue(_searchProgBar.value() + 1); } void updateCondition(); /** Disable the user searching input (lock the panel). */ void lockSearchPanel() {_lockSearchPanel(true);} /** Enable the user searching input (unlock the panel). */ void unlockSearchPanel() {_lockSearchPanel(false);} /** Set the visibility of the Search Progress Bar. */ void progressBarVisible(bool v) {_pbAction->setVisible(v);} /** Set the visibility of the Search Stop button. */ void searchStopVisible(bool v) {_searchStopAction->setVisible(v);} /** Set the visibility of the Search Restart button. */ void searchRestartVisible(bool v) {_searchRestartAction->setVisible(v);} /** Current State of the Finite-state machine for searching. */ std::shared_ptr _currentState; /** * Last row, tested for matching. To be used when restarting the * search. */ ssize_t _lastRowSearched; //! @cond Doxygen_Suppress QProgressBar _searchProgBar; QLabel _searchCountLabel; QComboBox _columnComboBox; QComboBox _selectComboBox; QLineEdit _searchLineEdit; QPushButton _prevButton, _nextButton; QPushButton _searchRestartButton, _searchStopButton; //! @endcond private: search_condition_func _cond; QAction *_pbAction, *_searchStopAction, *_searchRestartAction; void _lockSearchPanel(bool lock); enum Condition { Containes = 0, Match = 1, NotHave = 2 }; }; #endif trace-cmd-2.8.3/kernel-shark/src/KsSession.cpp000066400000000000000000000362651351617527000212100ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsSession.cpp * @brief KernelShark Session. */ // KernelShark #include "libkshark.h" #include "KsSession.hpp" #include "KsMainWindow.hpp" /** Create a KsSession object. */ KsSession::KsSession() { _config = kshark_config_new("kshark.config.session", KS_CONFIG_JSON); } /** Destroy a KsSession object. */ KsSession::~KsSession() { kshark_free_config_doc(_config); } /** Import a user session from a Json file. */ bool KsSession::importFromFile(QString jfileName) { kshark_config_doc *configTmp = kshark_open_config_file(jfileName.toStdString().c_str(), "kshark.config.session"); if (configTmp) { kshark_free_config_doc(_config); _config = configTmp; return true; } return false; } /** Export the current user session from a Json file. */ void KsSession::exportToFile(QString jfileName) { kshark_save_config_file(jfileName.toStdString().c_str(), _config); } /** * @brief Save the state of the visualization model. * * @param histo: Input location for the model descriptor. */ void KsSession::saveVisModel(kshark_trace_histo *histo) { kshark_config_doc *model = kshark_export_model(histo, KS_CONFIG_JSON); kshark_config_doc_add(_config, "Model", model); } /** * @brief Load the state of the visualization model. * * @param model: Input location for the KsGraphModel object. */ void KsSession::loadVisModel(KsGraphModel *model) { kshark_config_doc *modelConf = kshark_config_alloc(KS_CONFIG_JSON); if (!kshark_config_doc_get(_config, "Model", modelConf)) return; kshark_import_model(model->histo(), modelConf); model->update(); } /** Save the trace data file. */ void KsSession::saveDataFile(QString fileName) { kshark_config_doc *file = kshark_export_trace_file(fileName.toStdString().c_str(), KS_CONFIG_JSON); kshark_config_doc_add(_config, "Data", file); } /** Get the trace data file. */ QString KsSession::getDataFile(kshark_context *kshark_ctx) { kshark_config_doc *file = kshark_config_alloc(KS_CONFIG_JSON); const char *file_str; if (!kshark_config_doc_get(_config, "Data", file)) return QString(); file_str = kshark_import_trace_file(kshark_ctx, file); if (file_str) return QString(file_str); return QString(); } /** * @brief Save the configuration of the filters. * * @param kshark_ctx: Input location for context pointer. */ void KsSession::saveFilters(kshark_context *kshark_ctx) { kshark_config_doc *filters = kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON); kshark_config_doc_add(_config, "Filters", filters); } /** * @brief Load the configuration of the filters and filter the data. * * @param kshark_ctx: Input location for context pointer. * @param data: Input location for KsDataStore object; */ void KsSession::loadFilters(kshark_context *kshark_ctx, KsDataStore *data) { kshark_config_doc *filters = kshark_config_alloc(KS_CONFIG_JSON); if (!kshark_config_doc_get(_config, "Filters", filters)) return; kshark_import_all_filters(kshark_ctx, filters); if (kshark_ctx->advanced_event_filter->filters) data->reload(); else kshark_filter_entries(kshark_ctx, data->rows(), data->size()); data->registerCPUCollections(); emit data->updateWidgets(data); } /** * @brief Save the state of the table. * * @param view: Input location for the KsTraceViewer widget. */ void KsSession::saveTable(const KsTraceViewer &view) { kshark_config_doc *topRow = kshark_config_alloc(KS_CONFIG_JSON); int64_t r = view.getTopRow(); topRow->conf_doc = json_object_new_int64(r); kshark_config_doc_add(_config, "ViewTop",topRow); } /** * @brief Load the state of the table. * * @param view: Input location for the KsTraceViewer widget. */ void KsSession::loadTable(KsTraceViewer *view) { kshark_config_doc *topRow = kshark_config_alloc(KS_CONFIG_JSON); size_t r = 0; if (!kshark_config_doc_get(_config, "ViewTop", topRow)) return; if (_config->format == KS_CONFIG_JSON) r = json_object_get_int64(KS_JSON_CAST(topRow->conf_doc)); view->setTopRow(r); } /** * @brief Save the KernelShark Main window size. * * @param window: Input location for the KsMainWindow widget. */ void KsSession::saveMainWindowSize(const QMainWindow &window) { kshark_config_doc *windowConf = kshark_config_alloc(KS_CONFIG_JSON); int width = window.width(), height = window.height(); json_object *jwindow; if (window.isFullScreen()) { jwindow = json_object_new_string("FullScreen"); } else { jwindow = json_object_new_array(); json_object_array_put_idx(jwindow, 0, json_object_new_int(width)); json_object_array_put_idx(jwindow, 1, json_object_new_int(height)); } windowConf->conf_doc = jwindow; kshark_config_doc_add(_config, "MainWindow", windowConf); } /** * @brief Load the KernelShark Main window size. * * @param window: Input location for the KsMainWindow widget. */ void KsSession::loadMainWindowSize(KsMainWindow *window) { kshark_config_doc *windowConf = kshark_config_alloc(KS_CONFIG_JSON); json_object *jwindow, *jwidth, *jheight; int width, height; if (!kshark_config_doc_get(_config, "MainWindow", windowConf)) return; if (_config->format == KS_CONFIG_JSON) { jwindow = KS_JSON_CAST(windowConf->conf_doc); if (json_object_get_type(jwindow) == json_type_string && QString(json_object_get_string(jwindow)) == "FullScreen") { window->setFullScreenMode(true); return; } jwidth = json_object_array_get_idx(jwindow, 0); jheight = json_object_array_get_idx(jwindow, 1); width = json_object_get_int(jwidth); height = json_object_get_int(jheight); window->setFullScreenMode(false); window->resize(width, height); } } /** * @brief Save the state of the Main window spliter. * * @param splitter: Input location for the splitter widget. */ void KsSession::saveSplitterSize(const QSplitter &splitter) { kshark_config_doc *spl = kshark_config_alloc(KS_CONFIG_JSON); json_object *jspl = json_object_new_array(); QList sizes = splitter.sizes(); json_object_array_put_idx(jspl, 0, json_object_new_int(sizes[0])); json_object_array_put_idx(jspl, 1, json_object_new_int(sizes[1])); spl->conf_doc = jspl; kshark_config_doc_add(_config, "Splitter", spl); } /** * @brief Load the state of the Main window spliter. * * @param splitter: Input location for the splitter widget. */ void KsSession::loadSplitterSize(QSplitter *splitter) { kshark_config_doc *spl = kshark_config_alloc(KS_CONFIG_JSON); json_object *jspl, *jgraphsize, *jviewsize; int graphSize(1), viewSize(1); QList sizes; if (!kshark_config_doc_get(_config, "Splitter", spl)) return; if (_config->format == KS_CONFIG_JSON) { jspl = KS_JSON_CAST(spl->conf_doc); jgraphsize = json_object_array_get_idx(jspl, 0); jviewsize = json_object_array_get_idx(jspl, 1); graphSize = json_object_get_int(jgraphsize); viewSize = json_object_get_int(jviewsize); if (graphSize == 0 && viewSize == 0) { /* 0/0 spliter ratio is undefined. Make it 1/1. */ viewSize = graphSize = 1; } } sizes << graphSize << viewSize; splitter->setSizes(sizes); } /** @brief Save the Color scheme used. */ void KsSession::saveColorScheme() { kshark_config_doc *colSch = kshark_config_alloc(KS_CONFIG_JSON); double s = KsPlot::Color::getRainbowFrequency(); colSch->conf_doc = json_object_new_double(s); kshark_config_doc_add(_config, "ColorScheme", colSch); } /** @brief Get the Color scheme used. */ float KsSession::getColorScheme() { kshark_config_doc *colSch = kshark_config_alloc(KS_CONFIG_JSON); /* Default color scheme. */ float s = 0.75; if (!kshark_config_doc_get(_config, "ColorScheme", colSch)) return s; if (_config->format == KS_CONFIG_JSON) s = json_object_get_double(KS_JSON_CAST(colSch->conf_doc)); return s; } /** * @brief Save the list of the graphs plotted. * * @param glw: Input location for the KsGLWidget widget. */ void KsSession::saveGraphs(const KsGLWidget &glw) { _saveCPUPlots(glw._cpuList); _saveTaskPlots(glw._taskList); } /** * @brief Load the list of the graphs and plot. * * @param graphs: Input location for the KsTraceGraph widget. */ void KsSession::loadGraphs(KsTraceGraph *graphs) { graphs->cpuReDraw(_getCPUPlots()); graphs->taskReDraw(_getTaskPlots()); } void KsSession::_saveCPUPlots(const QVector &cpus) { kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON); json_object *jcpus = json_object_new_array(); for (int i = 0; i < cpus.count(); ++i) { json_object *jcpu = json_object_new_int(cpus[i]); json_object_array_put_idx(jcpus, i, jcpu); } cpuPlts->conf_doc = jcpus; kshark_config_doc_add(_config, "CPUPlots", cpuPlts); } QVector KsSession::_getCPUPlots() { kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON); json_object *jcpus; QVector cpus; size_t length; if (!kshark_config_doc_get(_config, "CPUPlots", cpuPlts)) return cpus; if (_config->format == KS_CONFIG_JSON) { jcpus = KS_JSON_CAST(cpuPlts->conf_doc); length = json_object_array_length(jcpus); for (size_t i = 0; i < length; ++i) { int cpu = json_object_get_int(json_object_array_get_idx(jcpus, i)); cpus.append(cpu); } } return cpus; } void KsSession::_saveTaskPlots(const QVector &tasks) { kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON); json_object *jtasks = json_object_new_array(); for (int i = 0; i < tasks.count(); ++i) { json_object *jtask = json_object_new_int(tasks[i]); json_object_array_put_idx(jtasks, i, jtask); } taskPlts->conf_doc = jtasks; kshark_config_doc_add(_config, "TaskPlots", taskPlts); } QVector KsSession::_getTaskPlots() { kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON); json_object *jtasks; QVector tasks; size_t length; if (!kshark_config_doc_get(_config, "TaskPlots", taskPlts)) return tasks; if (_config->format == KS_CONFIG_JSON) { jtasks = KS_JSON_CAST(taskPlts->conf_doc); length = json_object_array_length(jtasks); for (size_t i = 0; i < length; ++i) { int pid = json_object_get_int(json_object_array_get_idx(jtasks, i)); tasks.append(pid); } } return tasks; } /** * @brief Save the state of the Dual marker. * * @param dm: Input location for the KsDualMarkerSM object. */ void KsSession::saveDualMarker(KsDualMarkerSM *dm) { struct kshark_config_doc *markers = kshark_config_new("kshark.config.markers", KS_CONFIG_JSON); json_object *jd_mark = KS_JSON_CAST(markers->conf_doc); auto save_mark = [&jd_mark] (KsGraphMark *m, const char *name) { json_object *jmark = json_object_new_object(); if (m->_isSet) { json_object_object_add(jmark, "isSet", json_object_new_boolean(true)); json_object_object_add(jmark, "row", json_object_new_int(m->_pos)); } else { json_object_object_add(jmark, "isSet", json_object_new_boolean(false)); } json_object_object_add(jd_mark, name, jmark); }; save_mark(&dm->markerA(), "markA"); save_mark(&dm->markerB(), "markB"); if (dm->getState() == DualMarkerState::A) json_object_object_add(jd_mark, "Active", json_object_new_string("A")); else json_object_object_add(jd_mark, "Active", json_object_new_string("B")); kshark_config_doc_add(_config, "Markers", markers); } /** * @brief Load the state of the Dual marker. * * @param dm: Input location for the KsDualMarkerSM object. * @param graphs: Input location for the KsTraceGraph widget. */ void KsSession::loadDualMarker(KsDualMarkerSM *dm, KsTraceGraph *graphs) { size_t pos; dm->reset(); dm->setState(DualMarkerState::A); if (_getMarker("markA", &pos)) { graphs->markEntry(pos); } else { dm->markerA().remove(); } dm->setState(DualMarkerState::B); if (_getMarker("markB", &pos)) { graphs->markEntry(pos); } else { dm->markerB().remove(); } dm->setState(_getMarkerState()); if (dm->activeMarker()._isSet) { pos = dm->activeMarker()._pos; emit graphs->glPtr()->updateView(pos, true); } } json_object *KsSession::_getMarkerJson() { struct kshark_config_doc *markers = kshark_config_alloc(KS_CONFIG_JSON); if (!kshark_config_doc_get(_config, "Markers", markers) || !kshark_type_check(markers, "kshark.config.markers")) return nullptr; return KS_JSON_CAST(markers->conf_doc); } bool KsSession::_getMarker(const char* name, size_t *pos) { json_object *jd_mark, *jmark; *pos = 0; jd_mark = _getMarkerJson(); if (!jd_mark) return false; if (json_object_object_get_ex(jd_mark, name, &jmark)) { json_object *jis_set; json_object_object_get_ex(jmark, "isSet", &jis_set); if (!json_object_get_boolean(jis_set)) return false; json_object *jpos; json_object_object_get_ex(jmark, "row", &jpos); *pos = json_object_get_int64(jpos); } return true; } DualMarkerState KsSession::_getMarkerState() { json_object *jd_mark, *jstate; const char* state; jd_mark = _getMarkerJson(); json_object_object_get_ex(jd_mark, "Active", &jstate); state = json_object_get_string(jstate); if (strcmp(state, "A") == 0) return DualMarkerState::A; return DualMarkerState::B; } /** * @brief Save the configuration of the plugins. * * @param pm: Input location for the KsPluginManager object. */ void KsSession::savePlugins(const KsPluginManager &pm) { struct kshark_config_doc *plugins = kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON); json_object *jplugins = KS_JSON_CAST(plugins->conf_doc); const QVector ®isteredPlugins = pm._registeredKsPlugins; const QStringList &pluginList = pm._ksPluginList; int nPlugins = pluginList.length(); json_object *jlist, *jpl; QByteArray array; char* buffer; bool active; jlist = json_object_new_array(); for (int i = 0; i < nPlugins; ++i) { array = pluginList[i].toLocal8Bit(); buffer = array.data(); jpl = json_object_new_array(); json_object_array_put_idx(jpl, 0, json_object_new_string(buffer)); active = registeredPlugins[i]; json_object_array_put_idx(jpl, 1, json_object_new_boolean(active)); json_object_array_put_idx(jlist, i, jpl); } json_object_object_add(jplugins, "Plugin List", jlist); kshark_config_doc_add(_config, "Plugins", plugins); } /** * @brief Load the configuration of the plugins. * * @param kshark_ctx: Input location for context pointer. * @param pm: Input location for the KsPluginManager object. */ void KsSession::loadPlugins(kshark_context *kshark_ctx, KsPluginManager *pm) { kshark_config_doc *plugins = kshark_config_alloc(KS_CONFIG_JSON); json_object *jplugins, *jlist, *jpl; const char *pluginName; QVector pluginIds; int length, index; bool loaded; if (!kshark_config_doc_get(_config, "Plugins", plugins) || !kshark_type_check(plugins, "kshark.config.plugins")) return; if (plugins->format == KS_CONFIG_JSON) { jplugins = KS_JSON_CAST(plugins->conf_doc); json_object_object_get_ex(jplugins, "Plugin List", &jlist); if (!jlist || json_object_get_type(jlist) != json_type_array || !json_object_array_length(jlist)) return; length = json_object_array_length(jlist); for (int i = 0; i < length; ++i) { jpl = json_object_array_get_idx(jlist, i); pluginName = json_object_get_string(json_object_array_get_idx(jpl, 0)); index = pm->_ksPluginList.indexOf(pluginName); loaded = json_object_get_boolean(json_object_array_get_idx(jpl, 1)); if (index >= 0 && loaded) pluginIds.append(index); } } pm->updatePlugins(pluginIds); } trace-cmd-2.8.3/kernel-shark/src/KsSession.hpp000066400000000000000000000041261351617527000212040ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsSession.hpp * @brief KernelShark Session. */ #ifndef _KS_SESSION_H #define _KS_SESSION_H // Qt #include // KernelShark #include "KsDualMarker.hpp" #include "KsTraceGraph.hpp" #include "KsTraceViewer.hpp" class KsMainWindow; /** * The KsSession class provides instruments for importing/exporting the state * of the different components of the GUI from/to Json documents. These * instruments are used to save/load user session in the GUI. */ class KsSession { public: KsSession(); virtual ~KsSession(); /** Get the configuration document object. */ kshark_config_doc *getConfDocPtr() const {return _config;} bool importFromFile(QString jfileName); void exportToFile(QString jfileName); void saveDataFile(QString fileName); QString getDataFile(kshark_context *kshark_ctx); void saveVisModel(kshark_trace_histo *histo); void loadVisModel(KsGraphModel *model); void saveGraphs(const KsGLWidget &glw); void loadGraphs(KsTraceGraph *graphs); void saveFilters(kshark_context *kshark_ctx); void loadFilters(kshark_context *kshark_ctx, KsDataStore *data); void saveMainWindowSize(const QMainWindow &window); void loadMainWindowSize(KsMainWindow *window); void saveSplitterSize(const QSplitter &splitter); void loadSplitterSize(QSplitter *splitter); void saveDualMarker(KsDualMarkerSM *dm); void loadDualMarker(KsDualMarkerSM *dmm, KsTraceGraph *graphs); void savePlugins(const KsPluginManager &pm); void loadPlugins(kshark_context *kshark_ctx, KsPluginManager *pm); void saveTable(const KsTraceViewer &view); void loadTable(KsTraceViewer *view); void saveColorScheme(); float getColorScheme(); private: kshark_config_doc *_config; json_object *_getMarkerJson(); void _saveCPUPlots(const QVector &cpus); QVector _getCPUPlots(); void _saveTaskPlots(const QVector &tasks); QVector _getTaskPlots(); bool _getMarker(const char* name, size_t *pos); DualMarkerState _getMarkerState(); }; #endif trace-cmd-2.8.3/kernel-shark/src/KsTraceGraph.cpp000066400000000000000000000502551351617527000216000ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsTraceGraph.cpp * @brief KernelShark Trace Graph widget. */ // KernelShark #include "KsUtils.hpp" #include "KsDualMarker.hpp" #include "KsTraceGraph.hpp" #include "KsQuickContextMenu.hpp" /** Create a default (empty) Trace graph widget. */ KsTraceGraph::KsTraceGraph(QWidget *parent) : QWidget(parent), _pointerBar(this), _navigationBar(this), _zoomInButton("+", this), _quickZoomInButton("++", this), _zoomOutButton("-", this), _quickZoomOutButton("- -", this), _scrollLeftButton("<", this), _scrollRightButton(">", this), _labelP1("Pointer: ", this), _labelP2("", this), _labelI1("", this), _labelI2("", this), _labelI3("", this), _labelI4("", this), _labelI5("", this), _scrollArea(this), _drawWindow(&_scrollArea), _legendWindow(&_drawWindow), _legendAxisX(&_drawWindow), _labelXMin("", &_legendAxisX), _labelXMid("", &_legendAxisX), _labelXMax("", &_legendAxisX), _glWindow(&_drawWindow), _mState(nullptr), _data(nullptr), _keyPressed(false) { auto lamMakeNavButton = [&](QPushButton *b) { b->setMaximumWidth(FONT_WIDTH * 5); connect(b, &QPushButton::released, this, &KsTraceGraph::_stopUpdating); _navigationBar.addWidget(b); }; _pointerBar.setMaximumHeight(FONT_HEIGHT * 1.75); _pointerBar.setOrientation(Qt::Horizontal); _navigationBar.setMaximumHeight(FONT_HEIGHT * 1.75); _navigationBar.setMinimumWidth(FONT_WIDTH * 90); _navigationBar.setOrientation(Qt::Horizontal); _pointerBar.addWidget(&_labelP1); _labelP2.setFrameStyle(QFrame::Panel | QFrame::Sunken); _labelP2.setStyleSheet("QLabel { background-color : white;}"); _labelP2.setTextInteractionFlags(Qt::TextSelectableByMouse); _labelP2.setFixedWidth(FONT_WIDTH * 16); _pointerBar.addWidget(&_labelP2); _pointerBar.addSeparator(); _labelI1.setStyleSheet("QLabel {color : blue;}"); _labelI2.setStyleSheet("QLabel {color : green;}"); _labelI3.setStyleSheet("QLabel {color : red;}"); _labelI4.setStyleSheet("QLabel {color : blue;}"); _labelI5.setStyleSheet("QLabel {color : green;}"); _pointerBar.addWidget(&_labelI1); _pointerBar.addSeparator(); _pointerBar.addWidget(&_labelI2); _pointerBar.addSeparator(); _pointerBar.addWidget(&_labelI3); _pointerBar.addSeparator(); _pointerBar.addWidget(&_labelI4); _pointerBar.addSeparator(); _pointerBar.addWidget(&_labelI5); _legendAxisX.setFixedHeight(FONT_HEIGHT * 1.5); _legendAxisX.setLayout(new QHBoxLayout); _legendAxisX.layout()->setSpacing(0); _legendAxisX.layout()->setContentsMargins(0, 0, FONT_WIDTH, 0); _labelXMin.setAlignment(Qt::AlignLeft); _labelXMid.setAlignment(Qt::AlignHCenter); _labelXMax.setAlignment(Qt::AlignRight); _legendAxisX.layout()->addWidget(&_labelXMin); _legendAxisX.layout()->addWidget(&_labelXMid); _legendAxisX.layout()->addWidget(&_labelXMax); _drawWindow.setMinimumSize(100, 100); _drawWindow.setStyleSheet("QWidget {background-color : white;}"); _drawLayout.setContentsMargins(0, 0, 0, 0); _drawLayout.setSpacing(0); _drawLayout.addWidget(&_legendAxisX, 0, 1); _drawLayout.addWidget(&_legendWindow, 1, 0); _drawLayout.addWidget(&_glWindow, 1, 1); _drawWindow.setLayout(&_drawLayout); _drawWindow.installEventFilter(this); connect(&_glWindow, &KsGLWidget::select, this, &KsTraceGraph::markEntry); connect(&_glWindow, &KsGLWidget::found, this, &KsTraceGraph::_setPointerInfo); connect(&_glWindow, &KsGLWidget::notFound, this, &KsTraceGraph::_resetPointer); connect(&_glWindow, &KsGLWidget::zoomIn, this, &KsTraceGraph::_zoomIn); connect(&_glWindow, &KsGLWidget::zoomOut, this, &KsTraceGraph::_zoomOut); connect(&_glWindow, &KsGLWidget::scrollLeft, this, &KsTraceGraph::_scrollLeft); connect(&_glWindow, &KsGLWidget::scrollRight, this, &KsTraceGraph::_scrollRight); connect(&_glWindow, &KsGLWidget::stopUpdating, this, &KsTraceGraph::_stopUpdating); connect(_glWindow.model(), &KsGraphModel::modelReset, this, &KsTraceGraph::_updateTimeLegends); _glWindow.setContextMenuPolicy(Qt::CustomContextMenu); connect(&_glWindow, &QWidget::customContextMenuRequested, this, &KsTraceGraph::_onCustomContextMenu); _scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); _scrollArea.setWidget(&_drawWindow); lamMakeNavButton(&_scrollLeftButton); connect(&_scrollLeftButton, &QPushButton::pressed, this, &KsTraceGraph::_scrollLeft); lamMakeNavButton(&_zoomInButton); connect(&_zoomInButton, &QPushButton::pressed, this, &KsTraceGraph::_zoomIn); lamMakeNavButton(&_zoomOutButton); connect(&_zoomOutButton, &QPushButton::pressed, this, &KsTraceGraph::_zoomOut); lamMakeNavButton(&_scrollRightButton); connect(&_scrollRightButton, &QPushButton::pressed, this, &KsTraceGraph::_scrollRight); _navigationBar.addSeparator(); lamMakeNavButton(&_quickZoomInButton); connect(&_quickZoomInButton, &QPushButton::pressed, this, &KsTraceGraph::_quickZoomIn); lamMakeNavButton(&_quickZoomOutButton); connect(&_quickZoomOutButton, &QPushButton::pressed, this, &KsTraceGraph::_quickZoomOut); _layout.addWidget(&_pointerBar); _layout.addWidget(&_navigationBar); _layout.addWidget(&_scrollArea); this->setLayout(&_layout); updateGeom(); } /** * @brief Load and show trace data. * * @param data: Input location for the KsDataStore object. * KsDataStore::loadDataFile() must be called first. */ void KsTraceGraph::loadData(KsDataStore *data) { _data = data; _glWindow.loadData(data); _updateGraphLegends(); updateGeom(); } /** Connect the KsGLWidget widget and the State machine of the Dual marker. */ void KsTraceGraph::setMarkerSM(KsDualMarkerSM *m) { _mState = m; _navigationBar.addSeparator(); _mState->placeInToolBar(&_navigationBar); _glWindow.setMarkerSM(m); } /** Reset (empty) the widget. */ void KsTraceGraph::reset() { /* Reset (empty) the OpenGL widget. */ _glWindow.reset(); _labelP2.setText(""); for (auto l1: {&_labelI1, &_labelI2, &_labelI3, &_labelI4, &_labelI5}) l1->setText(""); _selfUpdate(); for (auto l2: {&_labelXMin, &_labelXMid, &_labelXMax}) l2->setText(""); } void KsTraceGraph::_selfUpdate() { _updateGraphLegends(); _updateTimeLegends(); _markerReDraw(); _glWindow.model()->update(); updateGeom(); } void KsTraceGraph::_zoomIn() { _updateGraphs(GraphActions::ZoomIn); } void KsTraceGraph::_zoomOut() { _updateGraphs(GraphActions::ZoomOut); } void KsTraceGraph::_quickZoomIn() { /* Bin size will be 100 ns. */ _glWindow.model()->quickZoomIn(100); if (_mState->activeMarker()._isSet && _mState->activeMarker().isVisible()) { /* * Use the position of the active marker as * a focus point of the zoom. */ uint64_t ts = _mState->activeMarker()._ts; _glWindow.model()->jumpTo(ts); } } void KsTraceGraph::_quickZoomOut() { _glWindow.model()->quickZoomOut(); } void KsTraceGraph::_scrollLeft() { _updateGraphs(GraphActions::ScrollLeft); } void KsTraceGraph::_scrollRight() { _updateGraphs(GraphActions::ScrollRight); } void KsTraceGraph::_stopUpdating() { /* * The user is no longer pressing the action button. Reset the * "Key Pressed" flag. This will stop the ongoing user action. */ _keyPressed = false; } void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid) { uint64_t sec, usec; QString pointer; kshark_convert_nano(ts, &sec, &usec); pointer.sprintf("%lu.%06lu", sec, usec); _labelP2.setText(pointer); if (pid > 0 && cpu >= 0) { struct kshark_context *kshark_ctx(NULL); if (!kshark_instance(&kshark_ctx)) return; QString comm(tep_data_comm_from_pid(kshark_ctx->pevent, pid)); comm.append("-"); comm.append(QString("%1").arg(pid)); _labelI1.setText(comm); _labelI2.setText(QString("CPU %1").arg(cpu)); } else { _labelI1.setText(""); _labelI2.setText(""); } for (auto const &l: {&_labelI3, &_labelI4, &_labelI5}) { l->setText(""); } } void KsTraceGraph::_setPointerInfo(size_t i) { kshark_entry *e = _data->rows()[i]; QString event(kshark_get_event_name_easy(e)); QString lat(kshark_get_latency_easy(e)); QString info(kshark_get_info_easy(e)); QString comm(kshark_get_task_easy(e)); QString pointer, elidedText; int labelWidth, width; uint64_t sec, usec; kshark_convert_nano(e->ts, &sec, &usec); pointer.sprintf("%lu.%06lu", sec, usec); _labelP2.setText(pointer); comm.append("-"); comm.append(QString("%1").arg(kshark_get_pid_easy(e))); _labelI1.setText(comm); _labelI2.setText(QString("CPU %1").arg(e->cpu)); _labelI3.setText(lat); _labelI4.setText(event); _labelI5.setText(info); QCoreApplication::processEvents(); labelWidth = _pointerBar.geometry().right() - _labelI4.geometry().right(); if (labelWidth > STRING_WIDTH(info) + FONT_WIDTH * 5) return; /* * The Info string is too long and cannot be displayed on the toolbar. * Try to fit the text in the available space. */ QFontMetrics metrix(_labelI5.font()); width = labelWidth - FONT_WIDTH * 3; elidedText = metrix.elidedText(info, Qt::ElideRight, width); while(labelWidth < STRING_WIDTH(elidedText) + FONT_WIDTH * 5) { width -= FONT_WIDTH * 3; elidedText = metrix.elidedText(info, Qt::ElideRight, width); } _labelI5.setText(elidedText); _labelI5.setVisible(true); QCoreApplication::processEvents(); } /** * @brief Use the active marker to select particular entry. * * @param row: The index of the entry to be selected by the marker. */ void KsTraceGraph::markEntry(size_t row) { int graph, cpuGrId, taskGrId; _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); /* * If a Task graph has been found, this Task graph will be * visible. If no Task graph has been found, make visible * the corresponding CPU graph. */ if (taskGrId >= 0) graph = taskGrId; else graph = cpuGrId; _scrollArea.ensureVisible(0, _legendAxisX.height() + _glWindow.vMargin() + KS_GRAPH_HEIGHT / 2 + graph*(KS_GRAPH_HEIGHT + _glWindow.vSpacing()), 50, KS_GRAPH_HEIGHT / 2 + _glWindow.vSpacing() / 2); _glWindow.model()->jumpTo(_data->rows()[row]->ts); _mState->activeMarker().set(*_data, _glWindow.model()->histo(), row, cpuGrId, taskGrId); _mState->updateMarkers(*_data, &_glWindow); } void KsTraceGraph::_markerReDraw() { int cpuGrId, taskGrId; size_t row; if (_mState->markerA()._isSet) { row = _mState->markerA()._pos; _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); _mState->markerA().set(*_data, _glWindow.model()->histo(), row, cpuGrId, taskGrId); } if (_mState->markerB()._isSet) { row = _mState->markerB()._pos; _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId); _mState->markerB().set(*_data, _glWindow.model()->histo(), row, cpuGrId, taskGrId); } } /** * @brief Redreaw all CPU graphs. * * @param v: CPU ids to be plotted. */ void KsTraceGraph::cpuReDraw(QVector v) { _glWindow._cpuList = v; _selfUpdate(); } /** * @brief Redreaw all Task graphs. * * @param v: Process ids of the tasks to be plotted. */ void KsTraceGraph::taskReDraw(QVector v) { _glWindow._taskList = v; _selfUpdate(); } /** Add (and plot) a CPU graph to the existing list of CPU graphs. */ void KsTraceGraph::addCPUPlot(int cpu) { if (_glWindow._cpuList.contains(cpu)) return; _glWindow._cpuList.append(cpu); qSort(_glWindow._cpuList); _selfUpdate(); } /** Add (and plot) a Task graph to the existing list of Task graphs. */ void KsTraceGraph::addTaskPlot(int pid) { if (_glWindow._taskList.contains(pid)) return; _glWindow._taskList.append(pid); qSort(_glWindow._taskList); _selfUpdate(); } /** Remove a CPU graph from the existing list of CPU graphs. */ void KsTraceGraph::removeCPUPlot(int cpu) { if (!_glWindow._cpuList.contains(cpu)) return; _glWindow._cpuList.removeAll(cpu); _selfUpdate(); } /** Remove a Task graph from the existing list of Task graphs. */ void KsTraceGraph::removeTaskPlot(int pid) { if (!_glWindow._taskList.contains(pid)) return; _glWindow._taskList.removeAll(pid); _selfUpdate(); } /** Update the content of all graphs. */ void KsTraceGraph::update(KsDataStore *data) { _glWindow.model()->update(data); _selfUpdate(); } /** Update the geometry of the widget. */ void KsTraceGraph::updateGeom() { int saWidth, saHeight, dwWidth, hMin; /* Set the size of the Scroll Area. */ saWidth = width() - _layout.contentsMargins().left() - _layout.contentsMargins().right(); saHeight = height() - _pointerBar.height() - _navigationBar.height() - _layout.spacing() * 2 - _layout.contentsMargins().top() - _layout.contentsMargins().bottom(); _scrollArea.resize(saWidth, saHeight); /* * Calculate the width of the Draw Window, taking into account the size * of the scroll bar. */ dwWidth = _scrollArea.width(); if (_glWindow.height() + _legendAxisX.height() > _scrollArea.height()) dwWidth -= qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); /* * Set the height of the Draw window according to the number of * plotted graphs. */ _drawWindow.resize(dwWidth, _glWindow.height() + _legendAxisX.height()); /* Set the minimum height of the Graph widget. */ hMin = _drawWindow.height() + _pointerBar.height() + _navigationBar.height() + _layout.contentsMargins().top() + _layout.contentsMargins().bottom(); if (hMin > KS_GRAPH_HEIGHT * 8) hMin = KS_GRAPH_HEIGHT * 8; setMinimumHeight(hMin); /* * Now use the height of the Draw Window to fix the maximum height * of the Graph widget. */ setMaximumHeight(_drawWindow.height() + _pointerBar.height() + _navigationBar.height() + _layout.spacing() * 2 + _layout.contentsMargins().top() + _layout.contentsMargins().bottom() + 2); /* Just a little bit of extra space. This will * allow the scroll bar to disappear when the * widget is extended to maximum. */ _glWindow.update(); } void KsTraceGraph::_updateGraphLegends() { QString graphLegends, graphName; QVBoxLayout *layout; int width = 0; if (_legendWindow.layout()) { /* * Remove and delete the existing layout of the legend window. */ QLayoutItem *child; while ((child = _legendWindow.layout()->takeAt(0)) != 0) { delete child->widget(); delete child; } delete _legendWindow.layout(); } layout = new QVBoxLayout; layout->setContentsMargins(FONT_WIDTH, 0, 0, 0); layout->setSpacing(_glWindow.vSpacing()); layout->setAlignment(Qt::AlignTop); layout->addSpacing(_glWindow.vMargin()); auto lamMakeName = [&]() { QLabel *name = new QLabel(graphName); if (width < STRING_WIDTH(graphName)) width = STRING_WIDTH(graphName); name->setAlignment(Qt::AlignBottom); name->setStyleSheet("QLabel {background-color : white;}"); name->setFixedHeight(KS_GRAPH_HEIGHT); layout->addWidget(name); }; for (auto const &cpu: _glWindow._cpuList) { graphName = QString("CPU %1").arg(cpu); lamMakeName(); } for (auto const &pid: _glWindow._taskList) { graphName = QString(tep_data_comm_from_pid(_data->tep(), pid)); graphName.append(QString("-%1").arg(pid)); lamMakeName(); } _legendWindow.setLayout(layout); _legendWindow.setMaximumWidth(width + FONT_WIDTH); } void KsTraceGraph::_updateTimeLegends() { uint64_t sec, usec, tsMid; QString tMin, tMid, tMax; kshark_convert_nano(_glWindow.model()->histo()->min, &sec, &usec); tMin.sprintf("%lu.%06lu", sec, usec); _labelXMin.setText(tMin); tsMid = (_glWindow.model()->histo()->min + _glWindow.model()->histo()->max) / 2; kshark_convert_nano(tsMid, &sec, &usec); tMid.sprintf("%lu.%06lu", sec, usec); _labelXMid.setText(tMid); kshark_convert_nano(_glWindow.model()->histo()->max, &sec, &usec); tMax.sprintf("%lu.%06lu", sec, usec); _labelXMax.setText(tMax); } /** * Reimplemented event handler used to update the geometry of the widget on * resize events. */ void KsTraceGraph::resizeEvent(QResizeEvent* event) { updateGeom(); } /** * Reimplemented event handler (overriding a virtual function from QObject) * used to detect the position of the mouse with respect to the Draw window and * according to this position to grab / release the focus of the keyboard. The * function has nothing to do with the filtering of the trace events. */ bool KsTraceGraph::eventFilter(QObject* obj, QEvent* evt) { if (obj == &_drawWindow && evt->type() == QEvent::Enter) _glWindow.setFocus(); if (obj == &_drawWindow && evt->type() == QEvent::Leave) _glWindow.clearFocus(); return QWidget::eventFilter(obj, evt); } void KsTraceGraph::_updateGraphs(GraphActions action) { double k; int bin; /* * Set the "Key Pressed" flag. The flag will stay set as long as the user * keeps the corresponding action button pressed. */ _keyPressed = true; /* Initialize the zooming factor with a small value. */ k = .01; while (_keyPressed) { switch (action) { case GraphActions::ZoomIn: if (_mState->activeMarker()._isSet && _mState->activeMarker().isVisible()) { /* * Use the position of the active marker as * a focus point of the zoom. */ bin = _mState->activeMarker()._bin; _glWindow.model()->zoomIn(k, bin); } else { /* * The default focus point is the center of the * range interval of the model. */ _glWindow.model()->zoomIn(k); } break; case GraphActions::ZoomOut: if (_mState->activeMarker()._isSet && _mState->activeMarker().isVisible()) { /* * Use the position of the active marker as * a focus point of the zoom. */ bin = _mState->activeMarker()._bin; _glWindow.model()->zoomOut(k, bin); } else { /* * The default focus point is the center of the * range interval of the model. */ _glWindow.model()->zoomOut(k); } break; case GraphActions::ScrollLeft: _glWindow.model()->shiftBackward(10); break; case GraphActions::ScrollRight: _glWindow.model()->shiftForward(10); break; } /* * As long as the action button is pressed, the zooming factor * will grow smoothly until it reaches a maximum value. This * will have a visible effect of an accelerating zoom. */ if (k < .25) k *= 1.02; _mState->updateMarkers(*_data, &_glWindow); _updateTimeLegends(); QCoreApplication::processEvents(); } } void KsTraceGraph::_onCustomContextMenu(const QPoint &point) { KsQuickMarkerMenu *menu(nullptr); int cpu, pid; size_t row; bool found; found = _glWindow.find(point, 20, true, &row); if (found) { /* KernelShark entry has been found under the cursor. */ KsQuickContextMenu *entryMenu; menu = entryMenu = new KsQuickContextMenu(_data, row, _mState, this); connect(entryMenu, &KsQuickContextMenu::addTaskPlot, this, &KsTraceGraph::addTaskPlot); connect(entryMenu, &KsQuickContextMenu::addCPUPlot, this, &KsTraceGraph::addCPUPlot); connect(entryMenu, &KsQuickContextMenu::removeTaskPlot, this, &KsTraceGraph::removeTaskPlot); connect(entryMenu, &KsQuickContextMenu::removeCPUPlot, this, &KsTraceGraph::removeCPUPlot); } else { cpu = _glWindow.getPlotCPU(point); if (cpu >= 0) { /* * This is a CPU plot, but we do not have an entry * under the cursor. */ KsRmCPUPlotMenu *rmMenu; menu = rmMenu = new KsRmCPUPlotMenu(_mState, cpu, this); auto lamRmPlot = [&cpu, this] () { removeCPUPlot(cpu); }; connect(rmMenu, &KsRmPlotContextMenu::removePlot, lamRmPlot); } pid = _glWindow.getPlotPid(point); if (pid >= 0) { /* * This is a Task plot, but we do not have an entry * under the cursor. */ KsRmTaskPlotMenu *rmMenu; menu = rmMenu = new KsRmTaskPlotMenu(_mState, pid, this); auto lamRmPlot = [&pid, this] () { removeTaskPlot(pid); }; connect(rmMenu, &KsRmPlotContextMenu::removePlot, lamRmPlot); } } if (menu) { connect(menu, &KsQuickMarkerMenu::deselect, this, &KsTraceGraph::deselect); /* * Note that this slot was connected to the * customContextMenuRequested signal of the OpenGL widget. * Because of this the coordinates of the point are given with * respect to the frame of this widget. */ QPoint global = _glWindow.mapToGlobal(point); global.ry() -= menu->sizeHint().height() / 2; /* * Shift the menu so that it is not positioned under the mouse. * This will prevent from an accidental selection of the menu * item under the mouse. */ global.rx() += FONT_WIDTH; menu->exec(global); } } trace-cmd-2.8.3/kernel-shark/src/KsTraceGraph.hpp000066400000000000000000000054171351617527000216050ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsTraceGraph.hpp * @brief KernelShark Trace Graph widget. */ #ifndef _KS_TRACEGRAPH_H #define _KS_TRACEGRAPH_H // KernelShark #include "KsGLWidget.hpp" /** * Scroll Area class, needed in order to reimplemented the handler for mouse * wheel events. */ class KsGraphScrollArea : public QScrollArea { public: /** Create a default Scroll Area. */ explicit KsGraphScrollArea(QWidget *parent = nullptr) : QScrollArea(parent) {} /** * Reimplemented handler for mouse wheel events. All mouse wheel * events will be ignored. */ void wheelEvent(QWheelEvent *evt) {evt->ignore();} }; /** * The KsTraceViewer class provides a widget for interactive visualization of * trace data shown as time-series. */ class KsTraceGraph : public QWidget { Q_OBJECT public: explicit KsTraceGraph(QWidget *parent = nullptr); void loadData(KsDataStore *data); void setMarkerSM(KsDualMarkerSM *m); void reset(); /** Get the KsGLWidget object. */ KsGLWidget *glPtr() {return &_glWindow;} void markEntry(size_t); void cpuReDraw(QVector); void taskReDraw(QVector); void addCPUPlot(int); void addTaskPlot(int); void removeCPUPlot(int); void removeTaskPlot(int); void update(KsDataStore *data); void updateGeom(); void resizeEvent(QResizeEvent* event) override; bool eventFilter(QObject* obj, QEvent* evt) override; signals: /** * This signal is emitted in the case of a right mouse button click or * in the case of a double click over an empty area (no visible * KernelShark entries). */ void deselect(); private: void _zoomIn(); void _zoomOut(); void _quickZoomIn(); void _quickZoomOut(); void _scrollLeft(); void _scrollRight(); void _stopUpdating(); void _resetPointer(uint64_t ts, int cpu, int pid); void _setPointerInfo(size_t); void _updateTimeLegends(); void _updateGraphLegends(); void _selfUpdate(); void _markerReDraw(); enum class GraphActions { ZoomIn, ZoomOut, ScrollLeft, ScrollRight }; void _updateGraphs(GraphActions action); void _onCustomContextMenu(const QPoint &point); QToolBar _pointerBar, _navigationBar; QPushButton _zoomInButton, _quickZoomInButton; QPushButton _zoomOutButton, _quickZoomOutButton; QPushButton _scrollLeftButton, _scrollRightButton; QLabel _labelP1, _labelP2, // Pointer _labelI1, _labelI2, _labelI3, _labelI4, _labelI5; // Proc. info KsGraphScrollArea _scrollArea; QWidget _drawWindow, _legendWindow, _legendAxisX; QLabel _labelXMin, _labelXMid, _labelXMax; KsGLWidget _glWindow; QGridLayout _drawLayout; QVBoxLayout _layout; KsDualMarkerSM *_mState; KsDataStore *_data; bool _keyPressed; }; #endif // _KS_TRACEGRAPH_H trace-cmd-2.8.3/kernel-shark/src/KsTraceViewer.cpp000066400000000000000000000453761351617527000220100ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsTraceViewer.cpp * @brief KernelShark Trace Viewer widget. */ // C++11 #include #include // KernelShark #include "KsQuickContextMenu.hpp" #include "KsTraceViewer.hpp" #include "KsWidgetsLib.hpp" /** * Reimplemented handler for mouse press events. Right mouse click events will * be ignored. This is done because we want the Right click is being used to * open a Context menu. */ void KsTableView::mousePressEvent(QMouseEvent *e) { if(e->button() == Qt::RightButton) return; QTableView::mousePressEvent(e); } /** * Reimplemented the handler for Auto-scrolling. With this we disable * the Horizontal Auto-scrolling. */ void KsTableView::scrollTo(const QModelIndex &index, ScrollHint hint) { int bottomMargin(2); if (hint == QAbstractItemView::EnsureVisible && index.row() > indexAt(rect().topLeft()).row() && index.row() < indexAt(rect().bottomLeft()).row() - bottomMargin) return; QTableView::scrollTo(index, hint); } /** Create a default (empty) Trace viewer widget. */ KsTraceViewer::KsTraceViewer(QWidget *parent) : QWidget(parent), _view(this), _model(this), _proxyModel(this), _tableHeader(_model.header()), _toolbar(this), _labelSearch("Search: Column", this), _labelGrFollows("Graph follows ", this), _searchFSM(this), _graphFollowsCheckBox(this), _graphFollows(true), _mState(nullptr), _data(nullptr) { this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); /* Make a search toolbar. */ _toolbar.setOrientation(Qt::Horizontal); _toolbar.setMaximumHeight(FONT_HEIGHT * 1.75); /* On the toolbar make two Combo boxes for the search settings. */ _toolbar.addWidget(&_labelSearch); _searchFSM._columnComboBox.addItems(_tableHeader); /* * Using the old Signal-Slot syntax because * QComboBox::currentIndexChanged has overloads. */ connect(&_searchFSM._columnComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_searchEdit(int))); /* * Using the old Signal-Slot syntax because * QComboBox::currentIndexChanged has overloads. */ connect(&_searchFSM._selectComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(_searchEdit(int))); /* On the toolbar, make a Line edit field for search. */ _searchFSM._searchLineEdit.setMaximumWidth(FONT_WIDTH * 20); connect(&_searchFSM._searchLineEdit, &QLineEdit::returnPressed, this, &KsTraceViewer::_search); connect(&_searchFSM._searchLineEdit, &QLineEdit::textEdited, this, &KsTraceViewer::_searchEditText); /* On the toolbar, add Prev & Next buttons. */ connect(&_searchFSM._nextButton, &QPushButton::pressed, this, &KsTraceViewer::_next); connect(&_searchFSM._prevButton, &QPushButton::pressed, this, &KsTraceViewer::_prev); connect(&_searchFSM._searchStopButton, &QPushButton::pressed, this, &KsTraceViewer::_searchStop); connect(&_searchFSM._searchRestartButton, &QPushButton::pressed, this, &KsTraceViewer::_searchContinue); _searchFSM.placeInToolBar(&_toolbar); /* * On the toolbar, make a Check box for connecting the search pannel * to the Graph widget. */ _toolbar.addWidget(&_graphFollowsCheckBox); _toolbar.addWidget(&_labelGrFollows); _graphFollowsCheckBox.setCheckState(Qt::Checked); connect(&_graphFollowsCheckBox, &QCheckBox::stateChanged, this, &KsTraceViewer::_graphFollowsChanged); /* Initialize the trace viewer. */ _view.horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); _view.verticalHeader()->setVisible(false); _view.setEditTriggers(QAbstractItemView::NoEditTriggers); _view.setSelectionBehavior(QAbstractItemView::SelectRows); _view.setSelectionMode(QAbstractItemView::SingleSelection); _view.verticalHeader()->setDefaultSectionSize(FONT_HEIGHT * 1.25); _proxyModel.setSource(&_model); _view.setModel(&_proxyModel); connect(&_proxyModel, &QAbstractItemModel::modelReset, this, &KsTraceViewer::_searchReset); _view.setContextMenuPolicy(Qt::CustomContextMenu); connect(&_view, &QWidget::customContextMenuRequested, this, &KsTraceViewer::_onCustomContextMenu); connect(&_view, &QTableView::clicked, this, &KsTraceViewer::_clicked); /* Set the layout. */ _layout.addWidget(&_toolbar); _layout.addWidget(&_view); this->setLayout(&_layout); } /** * @brief Load and show trace data. * * @param data: Input location for the KsDataStore object. * KsDataStore::loadDataFile() must be called first. */ void KsTraceViewer::loadData(KsDataStore *data) { _data = data; _model.reset(); _proxyModel.fill(data); _model.fill(data); this->_resizeToContents(); this->setMinimumHeight(SCREEN_HEIGHT / 5); } /** Connect the QTableView widget and the State machine of the Dual marker. */ void KsTraceViewer::setMarkerSM(KsDualMarkerSM *m) { QString styleSheetA, styleSheetB; _mState = m; _model.setColors(_mState->markerA()._color, _mState->markerB()._color); /* * Assign a property to State A of the Dual marker state machine. When * the marker is in State A the background color of the selected row * will be the same as the color of Marker A. */ styleSheetA = "selection-background-color : " + _mState->markerA()._color.name() + ";"; _mState->stateAPtr()->assignProperty(&_view, "styleSheet", styleSheetA); /* * Assign a property to State B. When the marker is in State B the * background color of the selected row will be the same as the color * of Marker B. */ styleSheetB = "selection-background-color : " + _mState->markerB()._color.name() + ";"; _mState->stateBPtr()->assignProperty(&_view, "styleSheet", styleSheetB); } /** Reset (empty) the table. */ void KsTraceViewer::reset() { this->setMinimumHeight(FONT_HEIGHT * 10); _model.reset(); _resizeToContents(); } void KsTraceViewer::_searchReset() { _searchFSM.handleInput(sm_input_t::Change); _proxyModel.searchReset(); } /** Get the index of the first (top) visible row. */ size_t KsTraceViewer::getTopRow() const { return _view.indexAt(_view.rect().topLeft()).row(); } /** Position given row at the top of the table. */ void KsTraceViewer::setTopRow(size_t r) { _view.scrollTo(_proxyModel.index(r, 0), QAbstractItemView::PositionAtTop); } /** Update the content of the table. */ void KsTraceViewer::update(KsDataStore *data) { /* The Proxy model has to be updated first! */ _proxyModel.fill(data); _model.update(data); _data = data; if (_mState->activeMarker()._isSet) showRow(_mState->activeMarker()._pos, true); } void KsTraceViewer::_onCustomContextMenu(const QPoint &point) { QModelIndex i = _view.indexAt(point); if (i.isValid()) { /* * Use the index of the proxy model to retrieve the value * of the row number in the source model. */ size_t row = _proxyModel.mapRowFromSource(i.row()); KsQuickContextMenu menu(_data, row, _mState, this); /* * Note that this slot was connected to the * customContextMenuRequested signal of the Table widget. * Because of this the coordinates of the point are given with * respect to the frame of this widget. */ QPoint global = _view.mapToGlobal(point); global.ry() -= menu.sizeHint().height() / 2; /* * Shift the menu so that it is not positioned under the mouse. * This will prevent from an accidental selection of the menu * item under the mouse. */ global.rx() += FONT_WIDTH; connect(&menu, &KsQuickContextMenu::addTaskPlot, this, &KsTraceViewer::addTaskPlot); connect(&menu, &KsQuickMarkerMenu::deselect, this, &KsTraceViewer::deselect); menu.exec(global); } } void KsTraceViewer::_searchEdit(int index) { _searchReset(); // The search has been modified. } void KsTraceViewer::_searchEditText(const QString &text) { _searchReset(); // The search has been modified. } void KsTraceViewer::_graphFollowsChanged(int state) { _graphFollows = (bool) state; if (_graphFollows && _searchDone()) emit select(*_it); // Send a signal to the Graph widget. } void KsTraceViewer::_search() { if (!_searchDone()) { /* * The search is not done. This means that the search settings * have been modified since the last time we searched. */ _matchList.clear(); _searchItems(); if (!_matchList.empty()) { showRow(*_it, true); if (_graphFollows) emit select(*_it); // Send a signal to the Graph widget. } } else { /* * If the search is done, pressing "Enter" is equivalent * to pressing "Next" button. */ this->_next(); } } void KsTraceViewer::_next() { if (!_searchDone()) { _search(); return; } if (!_matchList.empty()) { // Items have been found. int row = selectedRow(); /* * The iterator can only be at the selected row or if the * selected row is not a match at the first matching row after * the selected one. */ if (*_it == row) { ++_it; // Move the iterator. if (_it == _matchList.end()) { /* * This is the last item of the list. * Go back to the beginning. */ _it = _matchList.begin(); } } // Select the row of the item. showRow(*_it, true); if (_graphFollows) emit select(*_it); // Send a signal to the Graph widget. } _updateSearchCount(); } void KsTraceViewer::_prev() { if (!_searchDone()) { _search(); return; } if (!_matchList.empty()) { // Items have been found. if (_it == _matchList.begin()) { // This is the first item of the list. Go to the last item. _it = _matchList.end() - 1; } else { --_it; // Move the iterator. } // Select the row of the item. showRow(*_it, true); if (_graphFollows) emit select(*_it); // Send a signal to the Graph widget. } _updateSearchCount(); } void KsTraceViewer::_updateSearchCount() { int index, total; QString countText; if (_matchList.isEmpty()) return; index = _it - _matchList.begin(); total =_matchList.count(); countText = QString(" %1 / %2").arg(index + 1).arg(total); _searchFSM._searchCountLabel.setText(countText); } void KsTraceViewer::_searchStop() { _proxyModel._searchStop = true; _searchFSM.handleInput(sm_input_t::Stop); } void KsTraceViewer::_searchContinue() { _proxyModel._searchStop = false; _searchItems(); } void KsTraceViewer::_clicked(const QModelIndex& i) { /* * Use the index of the proxy model to retrieve the value * of the row number in the base model. */ size_t row = _proxyModel.mapRowFromSource(i.row()); if (_searchDone() && _matchList.count()) { _setSearchIterator(row); _updateSearchCount(); } if (_graphFollows) emit select(row); // Send a signal to the Graph widget. } /** Make a given row of the table visible. */ void KsTraceViewer::showRow(size_t r, bool mark) { /* * Use the index in the source model to retrieve the value of the row number * in the proxy model. */ QModelIndex index = _proxyModel.mapFromSource(_model.index(r, 0)); if (mark) { // The row will be selected (colored). /* Get the first and the last visible rows of the table. */ int visiTot = _view.indexAt(_view.rect().topLeft()).row(); int visiBottom = _view.indexAt(_view.rect().bottomLeft()).row() - 2; /* Scroll only if the row to be shown in not vizible. */ if (index.row() < visiTot || index.row() > visiBottom) _view.scrollTo(index, QAbstractItemView::PositionAtCenter); _view.selectRow(index.row()); } else { /* * Just make sure that the row is visible. It will show up at * the top of the visible part of the table. */ _view.scrollTo(index, QAbstractItemView::PositionAtTop); } } /** Deselects the selected items (row) if any. */ void KsTraceViewer::clearSelection() { _view.clearSelection(); } /** Switch the Dual marker. */ void KsTraceViewer::markSwitch() { int row; /* The state of the Dual marker has changed. Get the new active marker. */ DualMarkerState state = _mState->getState(); /* First deal with the passive marker. */ if (_mState->getMarker(!state)._isSet) { /* * The passive marker is set. Use the model to color the row of * the passive marker. */ _model.selectRow(!state, _mState->getMarker(!state)._pos); } else { /* * The passive marker is not set. * Make sure that the model colors nothing. */ _model.selectRow(!state, KS_NO_ROW_SELECTED); } /* * Now deal with the active marker. This has to be done after dealing * with the model, because changing the model clears the selection. */ if (_mState->getMarker(state)._isSet) { /* * The active marker is set. Use QTableView to select its row. * The index in the source model is used to retrieve the value * of the row number in the proxy model. */ size_t row =_mState->getMarker(state)._pos; QModelIndex index = _proxyModel.mapFromSource(_model.index(row, 0)); if (index.isValid()) { /* * The row of the active marker will be colored according to * the assigned property of the current state of the Dual * marker. Auto-scrolling is temporarily disabled because we * do not want to scroll to the position of the marker yet. */ _view.setAutoScroll(false); _view.selectRow(index.row()); _view.setAutoScroll(true); } else { _view.clearSelection(); } } else { _view.clearSelection(); } row = selectedRow(); if (row >= 0) { _setSearchIterator(row); _updateSearchCount(); } } /** * Reimplemented event handler used to update the geometry of the widget on * resize events. */ void KsTraceViewer::resizeEvent(QResizeEvent* event) { int nColumns = _tableHeader.count(); int tableSize(0), viewSize, freeSpace; _resizeToContents(); for (int c = 0; c < nColumns; ++c) { tableSize += _view.columnWidth(c); } viewSize = _view.width() - qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); if ((freeSpace = viewSize - tableSize) > 0) { _view.setColumnWidth(nColumns - 1, _view.columnWidth(nColumns - 1) + freeSpace - 2); /* Just a little bit less space. * This will allow the scroll bar * to disappear when the widget * is extended to maximum. */ } } /** * Reimplemented event handler used to move the active marker. */ void KsTraceViewer::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) { int row = selectedRow(); if (row >= 0) emit select(row); // Send a signal to the Graph widget. return; } QWidget::keyReleaseEvent(event); } void KsTraceViewer::_resizeToContents() { int rows, columnSize; _view.setVisible(false); _view.resizeColumnsToContents(); _view.setVisible(true); /* * Because of some unknown reason the first column doesn't get * resized properly by the code above. We will resize this * column by hand. */ rows = _model.rowCount({}); columnSize = STRING_WIDTH(QString("%1").arg(rows)) + FONT_WIDTH; _view.setColumnWidth(0, columnSize); } //! @cond Doxygen_Suppress #define KS_SEARCH_SHOW_PROGRESS_MIN 100000 //! @endcond size_t KsTraceViewer::_searchItems() { int column = _searchFSM._columnComboBox.currentIndex(); QString searchText = _searchFSM._searchLineEdit.text(); int count, dataRow; if (searchText.isEmpty()) { /* * No text is provided by the user. Most probably this * is an accidental key press. */ return 0; } if (_proxyModel.rowCount({}) < KS_SEARCH_SHOW_PROGRESS_MIN) { /* * This is a small data-set. Do a single-threaded search * without showing the progress. We will bypass the state * switching, hence the search condition has to be updated * by hand. */ _searchFSM.updateCondition(); _proxyModel.search(column, searchText, _searchFSM.condition(), &_matchList, nullptr, nullptr); } else { _searchFSM.handleInput(sm_input_t::Start); if (column == KsViewModel::TRACE_VIEW_COL_INFO || column == KsViewModel::TRACE_VIEW_COL_LAT) _proxyModel.search(&_searchFSM, &_matchList); else _searchItemsMapReduce(column, searchText, _searchFSM.condition()); } count = _matchList.count(); _searchFSM.handleInput(sm_input_t::Finish); if (count == 0) // No items have been found. Do nothing. return 0; dataRow = selectedRow(); if (dataRow >= 0) { _view.clearSelection(); _setSearchIterator(dataRow); showRow(*_it, true); if (_graphFollows) emit select(*_it); // Send a signal to the Graph widget. } else { /* Move the iterator to the beginning of the match list. */ _it = _matchList.begin(); } _updateSearchCount(); return count; } void KsTraceViewer::_setSearchIterator(int row) { _it = _matchList.begin(); if (_matchList.isEmpty()) return; /* * Move the iterator to the first element of the match list * after the selected one. */ while (*_it < row) { ++_it; // Move the iterator. if (_it == _matchList.end()) { /* * This is the last item of the list. Go back * to the beginning. */ _it = _matchList.begin(); break; } } } void KsTraceViewer::_searchItemsMapReduce(int column, const QString &searchText, search_condition_func cond) { int nThreads = std::thread::hardware_concurrency(); std::vector> ranges(nThreads); std::vector>> maps; int i(0), nRows(_proxyModel.rowCount({})); int delta(nRows / nThreads); auto lamSearchMap = [&] (const QPair &range, bool notify) { return _proxyModel.searchMap(column, searchText, cond, range.first, range.second, notify); }; auto lamSearchReduce = [&] (QList &resultList, const QList &mapList) { resultList << mapList; _searchFSM.incrementProgress(); }; for (auto &r: ranges) { r.first = (i++) * delta; r.second = r.first + delta - 1; } /* * If the range is not multiple of the number of threads, adjust * the last range interval. */ ranges.back().second = nRows - 1; maps.push_back(std::async(lamSearchMap, ranges[0], true)); for (int r = 1; r < nThreads; ++r) maps.push_back(std::async(lamSearchMap, ranges[r], false)); while (_proxyModel.searchProgress() < KS_PROGRESS_BAR_MAX - nThreads) { std::unique_lock lk(_proxyModel._mutex); _proxyModel._pbCond.wait(lk); _searchFSM.setProgress(_proxyModel.searchProgress()); QApplication::processEvents(); } for (auto &m: maps) lamSearchReduce(_matchList, m.get()); } /** * @brief Color (select) the given row in the table, by using the color of the * Passive marker. * * @param row: The row index. If the Passive marker is selected and the input * value is negative, the Passive marker will be deselected. */ void KsTraceViewer::passiveMarkerSelectRow(int row) { DualMarkerState state = _mState->getState(); _view.setVisible(false); _model.selectRow(!state, row); _view.setVisible(true); } /** * Get the currently selected row. If no row is selected the function * returns KS_NO_ROW_SELECTED (-1). */ int KsTraceViewer::selectedRow() { QItemSelectionModel *sm = _view.selectionModel(); int dataRow = KS_NO_ROW_SELECTED; if (sm->hasSelection()) { /* Only one row at the time can be selected. */ QModelIndex i = sm->selectedRows()[0]; dataRow = _proxyModel.mapRowFromSource(i.row()); } return dataRow; } trace-cmd-2.8.3/kernel-shark/src/KsTraceViewer.hpp000066400000000000000000000056321351617527000220040ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsTraceViewer.hpp * @brief KernelShark Trace Viewer widget. */ #ifndef _KS_TRACEVIEW_H #define _KS_TRACEVIEW_H // Qt #include // KernelShark #include "KsUtils.hpp" #include "KsModels.hpp" #include "KsSearchFSM.hpp" #include "KsDualMarker.hpp" /** * Table View class, needed in order to reimplemented the handler for mouse * press events. */ class KsTableView : public QTableView { Q_OBJECT public: /** Create KsTableView. */ explicit KsTableView(QWidget *parent = nullptr) : QTableView(parent) {}; void mousePressEvent(QMouseEvent *event) override; void scrollTo(const QModelIndex &index, ScrollHint hint) override; }; /** * The KsTraceViewer class provides a widget for browsing in the trace data * shown in a text form. */ class KsTraceViewer : public QWidget { Q_OBJECT public: explicit KsTraceViewer(QWidget *parent = nullptr); void loadData(KsDataStore *data); void setMarkerSM(KsDualMarkerSM *m); void reset(); size_t getTopRow() const; void setTopRow(size_t r); void resizeEvent(QResizeEvent* event) override; void keyReleaseEvent(QKeyEvent *event); void markSwitch(); void showRow(size_t r, bool mark); void clearSelection(); void passiveMarkerSelectRow(int row); int selectedRow(); void update(KsDataStore *data); signals: /** Signal emitted when new row is selected. */ void select(size_t); /** * This signal is used to re-emitted the addTaskPlot signal of the * KsQuickContextMenu. */ void addTaskPlot(int pid); /** * This signal is used to re-emitted the deselect signal of the * KsQuickMarkerMenu. */ void deselect(); private: QVBoxLayout _layout; KsTableView _view; KsViewModel _model; KsFilterProxyModel _proxyModel; QStringList _tableHeader; QToolBar _toolbar; QLabel _labelSearch, _labelGrFollows; KsSearchFSM _searchFSM; QCheckBox _graphFollowsCheckBox; bool _graphFollows; QList _matchList; QList::iterator _it; KsDualMarkerSM *_mState; KsDataStore *_data; enum Condition { Containes = 0, Match = 1, NotHave = 2 }; void _searchReset(); void _resizeToContents(); size_t _searchItems(); void _searchItemsMapReduce(int column, const QString &searchText, search_condition_func cond); void _searchEditText(const QString &); void _graphFollowsChanged(int); void _lockSearchPanel(bool lock); void _search(); void _next(); void _prev(); void _updateSearchCount(); void _searchStop(); void _searchContinue(); void _clicked(const QModelIndex& i); void _onCustomContextMenu(const QPoint &); void _setSearchIterator(int row); bool _searchDone() { return _searchFSM.getState() == search_state_t::Done_s || _searchFSM.getState() == search_state_t::Paused_s; } private slots: void _searchEdit(int); }; #endif // _KS_TRACEVIEW_H trace-cmd-2.8.3/kernel-shark/src/KsUtils.cpp000066400000000000000000000454361351617527000206650ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsUtils.cpp * @brief KernelShark Utils. */ // KernelShark #include "KsUtils.hpp" #include "KsWidgetsLib.hpp" namespace KsUtils { /** @brief Get a sorted vector of Task's Pids. */ QVector getPidList() { kshark_context *kshark_ctx(nullptr); int nTasks, *tempPids; QVector pids; if (!kshark_instance(&kshark_ctx)) return pids; nTasks = kshark_get_task_pids(kshark_ctx, &tempPids); for (int r = 0; r < nTasks; ++r) { pids.append(tempPids[r]); } free(tempPids); qSort(pids); return pids; } /** @brief Get a sorted vector of Id values of a filter. */ QVector getFilterIds(tracecmd_filter_id *filter) { kshark_context *kshark_ctx(nullptr); int *cpuFilter, n; QVector v; if (!kshark_instance(&kshark_ctx)) return v; cpuFilter = tracecmd_filter_ids(filter); n = filter->count; for (int i = 0; i < n; ++i) v.append(cpuFilter[i]); qSort(v); free(cpuFilter); return v; } /** * Set the bit of the filter mask of the kshark session context responsible * for the visibility of the events in the Table View. */ void listFilterSync(bool state) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; if (state) { kshark_ctx->filter_mask |= KS_TEXT_VIEW_FILTER_MASK; } else { kshark_ctx->filter_mask &= ~KS_TEXT_VIEW_FILTER_MASK; } } /** * Set the bit of the filter mask of the kshark session context responsible * for the visibility of the events in the Graph View. */ void graphFilterSync(bool state) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; if (state) { kshark_ctx->filter_mask |= KS_GRAPH_VIEW_FILTER_MASK; kshark_ctx->filter_mask |= KS_EVENT_VIEW_FILTER_MASK; } else { kshark_ctx->filter_mask &= ~KS_GRAPH_VIEW_FILTER_MASK; kshark_ctx->filter_mask &= ~KS_EVENT_VIEW_FILTER_MASK; } } /** * @brief Add a checkbox to a menu. * * @param menu: Input location for the menu object, to which the checkbox will be added. * @param name: The name of the checkbox. * * @returns The checkbox object; */ QCheckBox *addCheckBoxToMenu(QMenu *menu, QString name) { QWidget *containerWidget = new QWidget(menu); containerWidget->setLayout(new QHBoxLayout()); containerWidget->layout()->setContentsMargins(FONT_WIDTH, FONT_HEIGHT/5, FONT_WIDTH, FONT_HEIGHT/5); QCheckBox *checkBox = new QCheckBox(name, menu); containerWidget->layout()->addWidget(checkBox); QWidgetAction *action = new QWidgetAction(menu); action->setDefaultWidget(containerWidget); menu->addAction(action); return checkBox; } /** * @brief Simple CPU matching function to be user for data collections. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param cpu: Matching condition value. * * @returns True if the CPU of the entry matches the value of "cpu" and * the entry is visibility in Graph. Otherwise false. */ bool matchCPUVisible(struct kshark_context *kshark_ctx, struct kshark_entry *e, int cpu) { return (e->cpu == cpu && (e->visible & KS_GRAPH_VIEW_FILTER_MASK)); } /** * @brief Check if the application runs from its installation location. */ bool isInstalled() { QString appPath = QCoreApplication::applicationDirPath(); QString installPath(_INSTALL_PREFIX); installPath += "/bin"; installPath = QDir::cleanPath(installPath); return appPath == installPath; } static QString getFileDialog(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath, bool forSave) { QString fileName; if (lastFilePath.isEmpty()) { lastFilePath = isInstalled() ? QDir::homePath() : QDir::currentPath(); } if (forSave) { fileName = QFileDialog::getSaveFileName(parent, windowName, lastFilePath, filter); } else { fileName = QFileDialog::getOpenFileName(parent, windowName, lastFilePath, filter); } if (!fileName.isEmpty()) lastFilePath = QFileInfo(fileName).path(); return fileName; } static QStringList getFilesDialog(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath) { QStringList fileNames; if (lastFilePath.isEmpty()) { lastFilePath = isInstalled() ? QDir::homePath() : QDir::currentPath(); } fileNames = QFileDialog::getOpenFileNames(parent, windowName, lastFilePath, filter); if (!fileNames.isEmpty()) lastFilePath = QFileInfo(fileNames[0]).path(); return fileNames; } /** * @brief Open a standard Qt getFileName dialog and return the name of the * selected file. Only one file can be selected. */ QString getFile(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath) { return getFileDialog(parent, windowName, filter, lastFilePath, false); } /** * @brief Open a standard Qt getFileName dialog and return the names of the * selected files. Multiple files can be selected. */ QStringList getFiles(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath) { return getFilesDialog(parent, windowName, filter, lastFilePath); } /** * @brief Open a standard Qt getFileName dialog and return the name of the * selected file. Only one file can be selected. */ QString getSaveFile(QWidget *parent, const QString &windowName, const QString &filter, const QString &extension, QString &lastFilePath) { QString fileName = getFileDialog(parent, windowName, filter, lastFilePath, true); if (!fileName.isEmpty() && !fileName.endsWith(extension)) { fileName += extension; if (QFileInfo(fileName).exists()) { if (!KsWidgetsLib::fileExistsDialog(fileName)) fileName.clear(); } } return fileName; } }; // KsUtils /** A stream operator for converting QColor into KsPlot::Color. */ KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c) { thisColor.set(c.red(), c.green(), c.blue()); return thisColor; } /** Create a default (empty) KsDataStore. */ KsDataStore::KsDataStore(QWidget *parent) : QObject(parent), _tep(nullptr), _rows(nullptr), _dataSize(0) {} /** Destroy the KsDataStore object. */ KsDataStore::~KsDataStore() {} /** Load trace data for file. */ void KsDataStore::loadDataFile(const QString &file) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; clear(); if (!kshark_open(kshark_ctx, file.toStdString().c_str())) { qCritical() << "ERROR Loading file " << file; return; } _tep = kshark_ctx->pevent; if (kshark_ctx->event_handlers == nullptr) kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT); else kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_UPDATE); _dataSize = kshark_load_data_entries(kshark_ctx, &_rows); } void KsDataStore::_freeData() { if (_dataSize > 0) { for (ssize_t r = 0; r < _dataSize; ++r) free(_rows[r]); free(_rows); } _rows = nullptr; _dataSize = 0; } /** Reload the trace data. */ void KsDataStore::reload() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; _freeData(); _dataSize = kshark_load_data_entries(kshark_ctx, &_rows); _tep = kshark_ctx->pevent; emit updateWidgets(this); } /** Free the loaded trace data and close the file. */ void KsDataStore::clear() { kshark_context *kshark_ctx(nullptr); _freeData(); _tep = nullptr; if (kshark_instance(&kshark_ctx) && kshark_ctx->handle) kshark_close(kshark_ctx); } /** Update the visibility of the entries (filter). */ void KsDataStore::update() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; _unregisterCPUCollections(); if (kshark_filter_is_set(kshark_ctx)) { kshark_filter_entries(kshark_ctx, _rows, _dataSize); emit updateWidgets(this); } registerCPUCollections(); } /** Register a collection of visible entries for each CPU. */ void KsDataStore::registerCPUCollections() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx) || !kshark_filter_is_set(kshark_ctx)) return; int nCPUs = tep_get_cpus(_tep); for (int cpu = 0; cpu < nCPUs; ++cpu) { kshark_register_data_collection(kshark_ctx, _rows, _dataSize, KsUtils::matchCPUVisible, cpu, 0); } } void KsDataStore::_unregisterCPUCollections() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; int nCPUs = tep_get_cpus(_tep); for (int cpu = 0; cpu < nCPUs; ++cpu) { kshark_unregister_data_collection(&kshark_ctx->collections, KsUtils::matchCPUVisible, cpu); } } void KsDataStore::_applyIdFilter(int filterId, QVector vec) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; switch (filterId) { case KS_SHOW_EVENT_FILTER: case KS_HIDE_EVENT_FILTER: kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_EVENT_FILTER); break; case KS_SHOW_TASK_FILTER: case KS_HIDE_TASK_FILTER: kshark_filter_clear(kshark_ctx, KS_SHOW_TASK_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER); break; case KS_SHOW_CPU_FILTER: case KS_HIDE_CPU_FILTER: kshark_filter_clear(kshark_ctx, KS_SHOW_CPU_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_CPU_FILTER); break; default: return; } for (auto &&val: vec) kshark_filter_add_id(kshark_ctx, filterId, val); if (!_tep) return; _unregisterCPUCollections(); /* * If the advanced event filter is set, the data has to be reloaded, * because the advanced filter uses tep_records. */ if (kshark_ctx->advanced_event_filter->filters) reload(); else kshark_filter_entries(kshark_ctx, _rows, _dataSize); registerCPUCollections(); emit updateWidgets(this); } /** Apply Show Task filter. */ void KsDataStore::applyPosTaskFilter(QVector vec) { _applyIdFilter(KS_SHOW_TASK_FILTER, vec); } /** Apply Hide Task filter. */ void KsDataStore::applyNegTaskFilter(QVector vec) { _applyIdFilter(KS_HIDE_TASK_FILTER, vec); } /** Apply Show Event filter. */ void KsDataStore::applyPosEventFilter(QVector vec) { _applyIdFilter(KS_SHOW_EVENT_FILTER, vec); } /** Apply Hide Event filter. */ void KsDataStore::applyNegEventFilter(QVector vec) { _applyIdFilter(KS_HIDE_EVENT_FILTER, vec); } /** Apply Show CPU filter. */ void KsDataStore::applyPosCPUFilter(QVector vec) { _applyIdFilter(KS_SHOW_CPU_FILTER, vec); } /** Apply Hide CPU filter. */ void KsDataStore::applyNegCPUFilter(QVector vec) { _applyIdFilter(KS_HIDE_CPU_FILTER, vec); } /** Disable all filters. */ void KsDataStore::clearAllFilters() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx) || !_tep) return; _unregisterCPUCollections(); kshark_filter_clear(kshark_ctx, KS_SHOW_TASK_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER); kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_EVENT_FILTER); kshark_filter_clear(kshark_ctx, KS_SHOW_CPU_FILTER); kshark_filter_clear(kshark_ctx, KS_HIDE_CPU_FILTER); tep_filter_reset(kshark_ctx->advanced_event_filter); kshark_clear_all_filters(kshark_ctx, _rows, _dataSize); emit updateWidgets(this); } /** * @brief Create Plugin Manager. Use list of plugins declared in the * CMake-generated header file. */ KsPluginManager::KsPluginManager(QWidget *parent) : QObject(parent) { kshark_context *kshark_ctx(nullptr); _parsePluginList(); if (!kshark_instance(&kshark_ctx)) return; registerFromList(kshark_ctx); } /** Parse the plugin list declared in the CMake-generated header file. */ void KsPluginManager::_parsePluginList() { _ksPluginList = KsUtils::getPluginList(); int nPlugins = _ksPluginList.count(); _registeredKsPlugins.resize(nPlugins); for (int i = 0; i < nPlugins; ++i) { if (_ksPluginList[i].contains(" default", Qt::CaseInsensitive)) { _ksPluginList[i].remove(" default", Qt::CaseInsensitive); _registeredKsPlugins[i] = true; } else { _registeredKsPlugins[i] = false; } } } /** * Register the plugins by using the information in "_ksPluginList" and * "_registeredKsPlugins". */ void KsPluginManager::registerFromList(kshark_context *kshark_ctx) { auto lamRegBuiltIn = [&kshark_ctx, this](const QString &plugin) { char *lib; int n; lib = _pluginLibFromName(plugin, n); if (n <= 0) return; kshark_register_plugin(kshark_ctx, lib); free(lib); }; auto lamRegUser = [&kshark_ctx](const QString &plugin) { std::string lib = plugin.toStdString(); kshark_register_plugin(kshark_ctx, lib.c_str()); }; _forEachInList(_ksPluginList, _registeredKsPlugins, lamRegBuiltIn); _forEachInList(_userPluginList, _registeredUserPlugins, lamRegUser); } /** * Unegister the plugins by using the information in "_ksPluginList" and * "_registeredKsPlugins". */ void KsPluginManager::unregisterFromList(kshark_context *kshark_ctx) { auto lamUregBuiltIn = [&kshark_ctx, this](const QString &plugin) { char *lib; int n; lib = _pluginLibFromName(plugin, n); if (n <= 0) return; kshark_unregister_plugin(kshark_ctx, lib); free(lib); }; auto lamUregUser = [&kshark_ctx](const QString &plugin) { std::string lib = plugin.toStdString(); kshark_unregister_plugin(kshark_ctx, lib.c_str()); }; _forEachInList(_ksPluginList, _registeredKsPlugins, lamUregBuiltIn); _forEachInList(_userPluginList, _registeredUserPlugins, lamUregUser); } char *KsPluginManager::_pluginLibFromName(const QString &plugin, int &n) { QString appPath = QCoreApplication::applicationDirPath(); QString libPath = appPath + "/../../kernel-shark/lib"; std::string pluginStr = plugin.toStdString(); char *lib; libPath = QDir::cleanPath(libPath); if (!KsUtils::isInstalled() && QDir(libPath).exists()) { std::string pathStr = libPath.toStdString(); n = asprintf(&lib, "%s/plugin-%s.so", pathStr.c_str(), pluginStr.c_str()); } else { n = asprintf(&lib, "%s/plugin-%s.so", KS_PLUGIN_INSTALL_PREFIX, pluginStr.c_str()); } return lib; } /** * @brief Register a Plugin. * * @param plugin: provide here the name of the plugin (as in the CMake-generated * header file) of a name of the plugin's library file (.so). */ void KsPluginManager::registerPlugin(const QString &plugin) { kshark_context *kshark_ctx(nullptr); char *lib; int n; if (!kshark_instance(&kshark_ctx)) return; for (int i = 0; i < _ksPluginList.count(); ++i) { if (_ksPluginList[i] == plugin) { /* * The argument is the name of the plugin. From the * name get the library .so file. */ lib = _pluginLibFromName(plugin, n); if (n > 0) { kshark_register_plugin(kshark_ctx, lib); _registeredKsPlugins[i] = true; free(lib); } return; } else if (plugin.contains("/lib/plugin-" + _ksPluginList[i], Qt::CaseInsensitive)) { /* * The argument is the name of the library .so file. */ n = asprintf(&lib, "%s", plugin.toStdString().c_str()); if (n > 0) { kshark_register_plugin(kshark_ctx, lib); _registeredKsPlugins[i] = true; free(lib); } return; } } /* No plugin with this name in the list. Try to add it anyway. */ if (plugin.endsWith(".so") && QFileInfo::exists(plugin)) { kshark_register_plugin(kshark_ctx, plugin.toStdString().c_str()); _userPluginList.append(plugin); _registeredUserPlugins.append(true); } else { qCritical() << "ERROR: " << plugin << "cannot be registered!"; } } /** @brief Unregister a Built in KernelShark plugin. *
    * WARNING: Do not use this function to unregister User plugins. * Instead use directly kshark_unregister_plugin(). * * @param plugin: provide here the name of the plugin (as in the CMake-generated * header file) or a name of the plugin's library file (.so). * */ void KsPluginManager::unregisterPlugin(const QString &plugin) { kshark_context *kshark_ctx(nullptr); char *lib; int n; if (!kshark_instance(&kshark_ctx)) return; for (int i = 0; i < _ksPluginList.count(); ++i) { if (_ksPluginList[i] == plugin) { /* * The argument is the name of the plugin. From the * name get the library .so file. */ lib = _pluginLibFromName(plugin, n); if (n > 0) { kshark_unregister_plugin(kshark_ctx, lib); _registeredKsPlugins[i] = false; free(lib); } return; } else if (plugin.contains("/lib/plugin-" + _ksPluginList[i], Qt::CaseInsensitive)) { /* * The argument is the name of the library .so file. */ n = asprintf(&lib, "%s", plugin.toStdString().c_str()); if (n > 0) { kshark_unregister_plugin(kshark_ctx, lib); _registeredKsPlugins[i] = false; free(lib); } return; } } } /** @brief Add to the list and initialize user-provided plugins. All other * previously loaded plugins will be reinitialized and the data will be * reloaded. * * @param fileNames: the library files (.so) of the plugins. */ void KsPluginManager::addPlugins(const QStringList &fileNames) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE); for (auto const &p: fileNames) registerPlugin(p); kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT); emit dataReload(); } /** Unload all plugins. */ void KsPluginManager::unloadAll() { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE); kshark_free_plugin_list(kshark_ctx->plugins); kshark_ctx->plugins = nullptr; kshark_free_event_handler_list(kshark_ctx->event_handlers); unregisterFromList(kshark_ctx); } /** @brief Update (change) the Plugins. * * @param pluginIds: The indexes of the plugins to be loaded. */ void KsPluginManager::updatePlugins(QVector pluginIds) { kshark_context *kshark_ctx(nullptr); if (!kshark_instance(&kshark_ctx)) return; auto register_plugins = [&] (QVector ids) { int nKsPlugins = _registeredKsPlugins.count(); /* First clear all registered plugins. */ for (auto &p: _registeredKsPlugins) p = false; for (auto &p: _registeredUserPlugins) p = false; /* The vector contains the indexes of those to register. */ for (auto const &p: ids) { if (p < nKsPlugins) _registeredKsPlugins[p] = true; else _registeredUserPlugins[p - nKsPlugins] = true; } registerFromList(kshark_ctx); }; if (!kshark_ctx->pevent) { kshark_free_plugin_list(kshark_ctx->plugins); kshark_ctx->plugins = nullptr; /* * No data is loaded. For the moment, just register the * plugins. Handling of the plugins will be done after * we load a data file. */ register_plugins(pluginIds); return; } /* Clean up all old plugins first. */ unloadAll(); /* Now load. */ register_plugins(pluginIds); kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT); emit dataReload(); } trace-cmd-2.8.3/kernel-shark/src/KsUtils.hpp000066400000000000000000000131361351617527000206620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsUtils.hpp * @brief KernelShark Utils. */ #ifndef _KS_UTILS_H #define _KS_UTILS_H // C++ 11 #include // Qt #include // KernelShark #include "libkshark.h" #include "libkshark-model.h" #include "KsCmakeDef.hpp" #include "KsPlotTools.hpp" /** Macro providing the height of the screen in pixels. */ #define SCREEN_HEIGHT QApplication::desktop()->screenGeometry().height() /** Macro providing the width of the screen in pixels. */ #define SCREEN_WIDTH QApplication::desktop()->screenGeometry().width() //! @cond Doxygen_Suppress auto fontHeight = []() { QFont font; QFontMetrics fm(font); return fm.height(); }; auto stringWidth = [](QString s) { QFont font; QFontMetrics fm(font); return fm.width(s); }; //! @endcond /** Macro providing the height of the font in pixels. */ #define FONT_HEIGHT fontHeight() /** Macro providing the width of the font in pixels. */ #define FONT_WIDTH stringWidth("4") /** Macro providing the width of a string in pixels. */ #define STRING_WIDTH(s) stringWidth(s) /** Macro providing the height of the KernelShark graphs in pixels. */ #define KS_GRAPH_HEIGHT (FONT_HEIGHT*2) //! @cond Doxygen_Suppress #define KS_JSON_CAST(doc) \ reinterpret_cast(doc) #define KS_C_STR_CAST(doc) \ reinterpret_cast(doc) typedef std::chrono::high_resolution_clock::time_point hd_time; #define GET_TIME std::chrono::high_resolution_clock::now() #define GET_DURATION(t0) \ std::chrono::duration_cast>( \ std::chrono::high_resolution_clock::now() - t0).count() //! @endcond namespace KsUtils { QVector getPidList(); QVector getFilterIds(tracecmd_filter_id *filter); /** @brief Geat the list of plugins. */ inline QStringList getPluginList() {return plugins.split(";");} void listFilterSync(bool state); void graphFilterSync(bool state); QCheckBox *addCheckBoxToMenu(QMenu *menu, QString name); /** @brief Convert the timestamp of the trace record into a string showing * the time in seconds. * * @param ts: Input location for the timestamp. * @param prec: the number of digits after the decimal point in the return * string. * * @returns String showing the time in seconds. */ inline QString Ts2String(int64_t ts, int prec) { return QString::number(ts * 1e-9, 'f', prec); } bool matchCPUVisible(struct kshark_context *kshark_ctx, struct kshark_entry *e, int cpu); bool isInstalled(); QString getFile(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath); QStringList getFiles(QWidget *parent, const QString &windowName, const QString &filter, QString &lastFilePath); QString getSaveFile(QWidget *parent, const QString &windowName, const QString &filter, const QString &extension, QString &lastFilePath); }; // KsUtils /** Identifier of the Dual Marker active state. */ enum class DualMarkerState { A, B }; /** * The KsDataStore class provides the access to trace data for all KernelShark * widgets. */ class KsDataStore : public QObject { Q_OBJECT public: explicit KsDataStore(QWidget *parent = nullptr); ~KsDataStore(); void loadDataFile(const QString &file); void clear(); /** Get the trace event parser. */ tep_handle *tep() const {return _tep;} /** Get the trace data array.. */ struct kshark_entry **rows() const {return _rows;} /** Get the size of the data array. */ ssize_t size() const {return _dataSize;} void reload(); void update(); void registerCPUCollections(); void applyPosTaskFilter(QVector); void applyNegTaskFilter(QVector); void applyPosEventFilter(QVector); void applyNegEventFilter(QVector); void applyPosCPUFilter(QVector); void applyNegCPUFilter(QVector); void clearAllFilters(); signals: /** * This signal is emitted when the data has changed and the View * widgets have to update. */ void updateWidgets(KsDataStore *); private: /** Page event used to parse the page. */ tep_handle *_tep; /** Trace data array. */ struct kshark_entry **_rows; /** The size of the data array. */ ssize_t _dataSize; void _freeData(); void _unregisterCPUCollections(); void _applyIdFilter(int filterId, QVector vec); }; /** A Plugin Manage class. */ class KsPluginManager : public QObject { Q_OBJECT public: explicit KsPluginManager(QWidget *parent = nullptr); /** A list of available built-in plugins. */ QStringList _ksPluginList; /** A list of registered built-in plugins. */ QVector _registeredKsPlugins; /** A list of available user plugins. */ QStringList _userPluginList; /** A list of registered user plugins. */ QVector _registeredUserPlugins; void registerFromList(kshark_context *kshark_ctx); void unregisterFromList(kshark_context *kshark_ctx); void registerPlugin(const QString &plugin); void unregisterPlugin(const QString &plugin); void addPlugins(const QStringList &fileNames); void unloadAll(); void updatePlugins(QVector pluginId); signals: /** This signal is emitted when a plugin is loaded or unloaded. */ void dataReload(); private: void _parsePluginList(); char *_pluginLibFromName(const QString &plugin, int &n); template void _forEachInList(const QStringList &pl, const QVector ®, T action) { int nPlugins; nPlugins = pl.count(); for (int i = 0; i < nPlugins; ++i) { if (reg[i]) { action(pl[i]); } } } }; KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c); #endif trace-cmd-2.8.3/kernel-shark/src/KsWidgetsLib.cpp000066400000000000000000000443321351617527000216140ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsWidgetsLib.cpp * @brief Defines small widgets and dialogues used by the KernelShark GUI. */ // KernelShark #include "libkshark.h" #include "KsUtils.hpp" #include "KsCmakeDef.hpp" #include "KsPlotTools.hpp" #include "KsWidgetsLib.hpp" /** * @brief Create KsProgressBar. * * @param message: Text to be shown. * @param parent: The parent of this widget. */ KsProgressBar::KsProgressBar(QString message, QWidget *parent) : QWidget(parent), _sb(this), _pb(&_sb) { resize(KS_BROGBAR_WIDTH, KS_BROGBAR_HEIGHT); setWindowTitle("KernelShark"); setLayout(new QVBoxLayout); _pb.setOrientation(Qt::Horizontal); _pb.setTextVisible(false); _pb.setRange(0, KS_PROGRESS_BAR_MAX); _pb.setValue(1); _sb.addPermanentWidget(&_pb, 1); layout()->addWidget(new QLabel(message)); layout()->addWidget(&_sb); setWindowFlags(Qt::WindowStaysOnTopHint); show(); } /** @brief Set the state of the progressbar. * * @param i: A value ranging from 0 to KS_PROGRESS_BAR_MAX. */ void KsProgressBar::setValue(int i) { _pb.setValue(i); QApplication::processEvents(); } /** * @brief Create KsMessageDialog. * * @param message: Text to be shown. * @param parent: The parent of this widget. */ KsMessageDialog::KsMessageDialog(QString message, QWidget *parent) : QDialog(parent), _text(message, this), _closeButton("Close", this) { resize(KS_MSG_DIALOG_WIDTH, KS_MSG_DIALOG_HEIGHT); _layout.addWidget(&_text); _layout.addWidget(&_closeButton); connect(&_closeButton, &QPushButton::pressed, this, &QWidget::close); this->setLayout(&_layout); } namespace KsWidgetsLib { /** * @brief Launch a File exists dialog. Use this function to ask the user * before overwriting an existing file. * * @param fileName: the name of the file. * * @returns True if the user wants to overwrite the file. Otherwise */ bool fileExistsDialog(QString fileName) { QString msg("A file "); QMessageBox msgBox; msg += fileName; msg += " already exists."; msgBox.setText(msg); msgBox.setInformativeText("Do you want to replace it?"); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel); return (msgBox.exec() == QMessageBox::Save); } }; // KsWidgetsLib /** * @brief Create KsCheckBoxWidget. * * @param name: The name of this widget. * @param parent: The parent of this widget. */ KsCheckBoxWidget::KsCheckBoxWidget(const QString &name, QWidget *parent) : QWidget(parent), _tb(this), _allCb("all", &_tb), _cbWidget(this), _cbLayout(&_cbWidget), _topLayout(this), _name(name), _nameLabel(name + ": ",&_tb) { setWindowTitle(_name); setMinimumHeight(SCREEN_HEIGHT / 2); connect(&_allCb, &QCheckBox::clicked, this, &KsCheckBoxWidget::_checkAll); _cbWidget.setLayout(&_cbLayout); _tb.addWidget(&_nameLabel); _tb.addWidget(&_allCb); _topLayout.addWidget(&_tb); _topLayout.addWidget(&_cbWidget); _topLayout.setContentsMargins(0, 0, 0, 0); setLayout(&_topLayout); _allCb.setCheckState(Qt::Checked); } /** * Set the default state for all checkboxes (including the "all" checkbox). */ void KsCheckBoxWidget::setDefault(bool st) { Qt::CheckState state = Qt::Unchecked; if (st) state = Qt::Checked; _allCb.setCheckState(state); _checkAll(state); } /** Get a vector containing the indexes of all checked boxes. */ QVector KsCheckBoxWidget::getCheckedIds() { QVector vec; int n = _id.size(); for (int i = 0; i < n; ++i) if (_checkState(i) == Qt::Checked) vec.append(_id[i]); return vec; } /** * @brief Set the state of the checkboxes. * * @param v: Vector containing the bool values for all checkboxes. */ void KsCheckBoxWidget::set(QVector v) { Qt::CheckState state; int nChecks; nChecks = (v.size() < _id.size()) ? v.size() : _id.size(); /* Start with the "all" checkbox being checked. */ _allCb.setCheckState(Qt::Checked); for (int i = 0; i < nChecks; ++i) { if (v[i]) { state = Qt::Checked; } else { /* * At least one checkbox is unchecked. Uncheck * "all" as well. */ state = Qt::Unchecked; _allCb.setCheckState(state); } _setCheckState(i, state); } _verify(); } void KsCheckBoxWidget::_checkAll(bool st) { Qt::CheckState state = Qt::Unchecked; int n = _id.size(); if (st) state = Qt::Checked; for (int i = 0; i < n; ++i) { _setCheckState(i, state); } _verify(); } /** * @brief Create KsCheckBoxDialog. * * @param cbw: A KsCheckBoxWidget to be nested in this dialog. * @param parent: The parent of this widget. */ KsCheckBoxDialog::KsCheckBoxDialog(KsCheckBoxWidget *cbw, QWidget *parent) : QDialog(parent), _checkBoxWidget(cbw), _applyButton("Apply", this), _cancelButton("Cancel", this) { int buttonWidth; setWindowTitle(cbw->name()); _topLayout.addWidget(_checkBoxWidget); buttonWidth = STRING_WIDTH("--Cancel--"); _applyButton.setFixedWidth(buttonWidth); _cancelButton.setFixedWidth(buttonWidth); _buttonLayout.addWidget(&_applyButton); _applyButton.setAutoDefault(false); _buttonLayout.addWidget(&_cancelButton); _cancelButton.setAutoDefault(false); _buttonLayout.setAlignment(Qt::AlignLeft); _topLayout.addLayout(&_buttonLayout); _applyButtonConnection = connect(&_applyButton, &QPushButton::pressed, this, &KsCheckBoxDialog::_applyPress); connect(&_applyButton, &QPushButton::pressed, this, &QWidget::close); connect(&_cancelButton, &QPushButton::pressed, this, &QWidget::close); this->setLayout(&_topLayout); } void KsCheckBoxDialog::_applyPress() { QVector vec = _checkBoxWidget->getCheckedIds(); emit apply(vec); /* * Disconnect _applyButton. This is done in order to protect * against multiple clicks. */ disconnect(_applyButtonConnection); } /** * @brief Create KsCheckBoxTable. * * @param parent: The parent of this widget. */ KsCheckBoxTable::KsCheckBoxTable(QWidget *parent) : QTableWidget(parent) { setShowGrid(false); horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); horizontalHeader()->setStretchLastSection(true); setSelectionBehavior(QAbstractItemView::SelectRows); setEditTriggers(QAbstractItemView::NoEditTriggers); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); verticalHeader()->setVisible(false); connect(this, &QTableWidget::cellDoubleClicked, this, &KsCheckBoxTable::_doubleClicked); } /** * @brief Initialize the table. * * @param headers: The headers of the individual columns. * @param size: The number of rows. */ void KsCheckBoxTable::init(QStringList headers, int size) { QHBoxLayout *cbLayout; QWidget *cbWidget; setColumnCount(headers.count()); setRowCount(size); setHorizontalHeaderLabels(headers); _cb.resize(size); for (int i = 0; i < size; ++i) { cbWidget = new QWidget(); _cb[i] = new QCheckBox(cbWidget); cbLayout = new QHBoxLayout(cbWidget); cbLayout->addWidget(_cb[i]); cbLayout->setAlignment(Qt::AlignCenter); cbLayout->setContentsMargins(0, 0, 0, 0); cbWidget->setLayout(cbLayout); setCellWidget(i, 0, cbWidget); } } /** Reimplemented event handler used to receive key press events. */ void KsCheckBoxTable::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return) { for (auto &s: selectedItems()) { if (s->column() == 1) emit changeState(s->row()); } } QApplication::processEvents(); QTableWidget::keyPressEvent(event); } /** Reimplemented event handler used to receive mouse press events. */ void KsCheckBoxTable::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { for (auto &i: selectedItems()) i->setSelected(false); return; } QApplication::processEvents(); QTableWidget::mousePressEvent(event); } void KsCheckBoxTable::_doubleClicked(int row, int col) { emit changeState(row); for (auto &i: selectedItems()) i->setSelected(false); } /** * @brief Create KsCheckBoxTableWidget. * * @param name: The name of this widget. * @param parent: The parent of this widget. */ KsCheckBoxTableWidget::KsCheckBoxTableWidget(const QString &name, QWidget *parent) : KsCheckBoxWidget(name, parent), _table(this) { connect(&_table, &KsCheckBoxTable::changeState, this, &KsCheckBoxTableWidget::_changeState); } /** Initialize the KsCheckBoxTable and its layout. */ void KsCheckBoxTableWidget::_initTable(QStringList headers, int size) { _table.init(headers, size); for (auto const & cb: _table._cb) { connect(cb, &QCheckBox::clicked, this, &KsCheckBoxTableWidget::_update); } _cbLayout.setContentsMargins(1, 1, 1, 1); _cbLayout.addWidget(&_table); } /** Adjust the size of this widget according to its content. */ void KsCheckBoxTableWidget::_adjustSize() { int width; _table.setVisible(false); _table.resizeColumnsToContents(); _table.setVisible(true); width = _table.horizontalHeader()->length() + FONT_WIDTH * 3 + style()->pixelMetric(QStyle::PM_ScrollBarExtent); _cbWidget.resize(width, _cbWidget.height()); setMinimumWidth(_cbWidget.width() + _cbLayout.contentsMargins().left() + _cbLayout.contentsMargins().right() + _topLayout.contentsMargins().left() + _topLayout.contentsMargins().right()); } void KsCheckBoxTableWidget::_update(bool state) { /* If a Checkbox is being unchecked. Unchecked "all" as well. */ if (!state) _allCb.setCheckState(Qt::Unchecked); } void KsCheckBoxTableWidget::_changeState(int row) { if (_table._cb[row]->checkState() == Qt::Checked) _table._cb[row]->setCheckState(Qt::Unchecked); else _table._cb[row]->setCheckState(Qt::Checked); _allCb.setCheckState(Qt::Checked); for (auto &c: _table._cb) { if (c->checkState() == Qt::Unchecked) { _allCb.setCheckState(Qt::Unchecked); break; } } } static void update_r(QTreeWidgetItem *item, Qt::CheckState state) { int n; item->setCheckState(0, state); n = item->childCount(); for (int i = 0; i < n; ++i) update_r(item->child(i), state); } /** * @brief Create KsCheckBoxTree. * * @param parent: The parent of this widget. */ KsCheckBoxTree::KsCheckBoxTree(QWidget *parent) : QTreeWidget(parent) { setColumnCount(2); setHeaderHidden(true); setSelectionBehavior(QAbstractItemView::SelectRows); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect(this, &KsCheckBoxTree::itemDoubleClicked, this, &KsCheckBoxTree::_doubleClicked); } /** Reimplemented event handler used to receive key press events. */ void KsCheckBoxTree::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return) { /* Loop over all selected child items and change * there states. */ for (auto &s: selectedItems()) { if(s->childCount()) { if (s->isExpanded()) continue; } if (s->checkState(0) == Qt::Unchecked) s->setCheckState(0, Qt::Checked); else s->setCheckState(0, Qt::Unchecked); if(s->childCount()) { update_r(s, s->checkState(0)); } } } emit verify(); QTreeWidget::keyPressEvent(event); } void KsCheckBoxTree::_doubleClicked(QTreeWidgetItem *item, int col) { if (item->checkState(0) == Qt::Unchecked) item->setCheckState(0, Qt::Checked); else item->setCheckState(0, Qt::Unchecked); for (auto &i: selectedItems()) i->setSelected(false); emit itemClicked(item, col); } /** Reimplemented event handler used to receive mouse press events. */ void KsCheckBoxTree::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { for (auto &i: selectedItems()) i->setSelected(false); return; } QApplication::processEvents(); QTreeWidget::mousePressEvent(event); } /** * @brief Create KsCheckBoxTreeWidget. * * @param name: The name of this widget. * @param parent: The parent of this widget. */ KsCheckBoxTreeWidget::KsCheckBoxTreeWidget(const QString &name, QWidget *parent) : KsCheckBoxWidget(name, parent), _tree(this) { connect(&_tree, &KsCheckBoxTree::verify, this, &KsCheckBoxTreeWidget::_verify); } /** Initialize the KsCheckBoxTree and its layout. */ void KsCheckBoxTreeWidget::_initTree() { _tree.setSelectionMode(QAbstractItemView::MultiSelection); connect(&_tree, &QTreeWidget::itemClicked, this, &KsCheckBoxTreeWidget::_update); _cbLayout.setContentsMargins(1, 1, 1, 1); _cbLayout.addWidget(&_tree); } /** Adjust the size of this widget according to its content. */ void KsCheckBoxTreeWidget::_adjustSize() { int width, n = _tree.topLevelItemCount(); if (n == 0) return; for (int i = 0; i < n; ++i) _tree.topLevelItem(i)->setExpanded(true); _tree.resizeColumnToContents(0); if (_tree.topLevelItem(0)->child(0)) { width = _tree.visualItemRect(_tree.topLevelItem(0)->child(0)).width(); } else { width = _tree.visualItemRect(_tree.topLevelItem(0)).width(); } width += FONT_WIDTH*3 + style()->pixelMetric(QStyle::PM_ScrollBarExtent); _cbWidget.resize(width, _cbWidget.height()); for (int i = 0; i < n; ++i) _tree.topLevelItem(i)->setExpanded(false); setMinimumWidth(_cbWidget.width() + _cbLayout.contentsMargins().left() + _cbLayout.contentsMargins().right() + _topLayout.contentsMargins().left() + _topLayout.contentsMargins().right()); } void KsCheckBoxTreeWidget::_update(QTreeWidgetItem *item, int column) { /* Get the new state of the item. */ Qt::CheckState state = item->checkState(0); /* Recursively update all items below this one. */ update_r(item, state); /* * Update all items above this one including the "all" * check box. */ _verify(); } void KsCheckBoxTreeWidget::_verify() { /* * Set the state of the top level items according to the * state of the childs. */ QTreeWidgetItem *topItem, *childItem; for(int t = 0; t < _tree.topLevelItemCount(); ++t) { topItem = _tree.topLevelItem(t); if (topItem->childCount() == 0) continue; topItem->setCheckState(0, Qt::Checked); for (int c = 0; c < topItem->childCount(); ++c) { childItem = topItem->child(c); if (childItem->checkState(0) == Qt::Unchecked) topItem->setCheckState(0, Qt::Unchecked); } } _allCb.setCheckState(Qt::Checked); for (auto &c: _cb) { if (c->checkState(0) == Qt::Unchecked) { _allCb.setCheckState(Qt::Unchecked); break; } } } /** * @brief Create KsCPUCheckBoxWidget. * * @param tep: Trace event parseer. * @param parent: The parent of this widget. */ KsCPUCheckBoxWidget::KsCPUCheckBoxWidget(struct tep_handle *tep, QWidget *parent) : KsCheckBoxTreeWidget("CPUs", parent) { int nCPUs(0), height(FONT_HEIGHT * 1.5); KsPlot::ColorTable colors; QString style; style = QString("QTreeView::item { height: %1 ;}").arg(height); _tree.setStyleSheet(style); _initTree(); if (tep) nCPUs = tep_get_cpus(tep); _id.resize(nCPUs); _cb.resize(nCPUs); colors = KsPlot::getCPUColorTable(); for (int i = 0; i < nCPUs; ++i) { QTreeWidgetItem *cpuItem = new QTreeWidgetItem; cpuItem->setText(0, " "); cpuItem->setText(1, QString("CPU %1").arg(i)); cpuItem->setCheckState(0, Qt::Checked); cpuItem->setBackgroundColor(0, QColor(colors[i].r(), colors[i].g(), colors[i].b())); _tree.addTopLevelItem(cpuItem); _id[i] = i; _cb[i] = cpuItem; } _adjustSize(); } /** * @brief Create KsEventsCheckBoxWidget. * * @param tep: Trace event parseer. * @param parent: The parent of this widget. */ KsEventsCheckBoxWidget::KsEventsCheckBoxWidget(struct tep_handle *tep, QWidget *parent) : KsCheckBoxTreeWidget("Events", parent) { QTreeWidgetItem *sysItem, *evtItem; tep_event **events(nullptr); QString sysName, evtName; int nEvts(0), i(0); if (tep) { nEvts = tep_get_events_count(tep); events = tep_list_events(tep, TEP_EVENT_SORT_SYSTEM); } _initTree(); _id.resize(nEvts); _cb.resize(nEvts); while (i < nEvts) { sysName = events[i]->system; sysItem = new QTreeWidgetItem; sysItem->setText(0, sysName); sysItem->setCheckState(0, Qt::Checked); _tree.addTopLevelItem(sysItem); while (sysName == events[i]->system) { evtName = events[i]->name; evtItem = new QTreeWidgetItem; evtItem->setText(0, evtName); evtItem->setCheckState(0, Qt::Checked); evtItem->setFlags(evtItem->flags() | Qt::ItemIsUserCheckable); sysItem->addChild(evtItem); _id[i] = events[i]->id; _cb[i] = evtItem; if (++i == nEvts) break; } } _tree.sortItems(0, Qt::AscendingOrder); _adjustSize(); } /** Remove a System from the Checkbox tree. */ void KsEventsCheckBoxWidget::removeSystem(QString name) { QTreeWidgetItem *item = _tree.findItems(name, Qt::MatchFixedString, 0)[0]; int index = _tree.indexOfTopLevelItem(item); if (index >= 0) _tree.takeTopLevelItem(index); } /** * @brief Create KsTasksCheckBoxWidget. * * @param pevent: Page event used to parse the page. * @param cond: If True make a "Show Task" widget. Otherwise make "Hide Task". * @param parent: The parent of this widget. */ KsTasksCheckBoxWidget::KsTasksCheckBoxWidget(struct tep_handle *pevent, bool cond, QWidget *parent) : KsCheckBoxTableWidget("Tasks", parent) { kshark_context *kshark_ctx(nullptr); QTableWidgetItem *pidItem, *comItem; KsPlot::ColorTable colors; QStringList headers; const char *comm; int nTasks, pid; if (!kshark_instance(&kshark_ctx)) return; if (_cond) headers << "Show" << "Pid" << "Task"; else headers << "Hide" << "Pid" << "Task"; _id = KsUtils::getPidList(); nTasks = _id.count(); _initTable(headers, nTasks); colors = KsPlot::getTaskColorTable(); for (int i = 0; i < nTasks; ++i) { pid = _id[i]; pidItem = new QTableWidgetItem(tr("%1").arg(pid)); _table.setItem(i, 1, pidItem); comm = tep_data_comm_from_pid(kshark_ctx->pevent, pid); comItem = new QTableWidgetItem(tr(comm)); pidItem->setBackgroundColor(QColor(colors[pid].r(), colors[pid].g(), colors[pid].b())); if (_id[i] == 0) pidItem->setTextColor(Qt::white); _table.setItem(i, 2, comItem); } _adjustSize(); } /** * @brief Create KsPluginCheckBoxWidget. * * @param pluginList: A list of plugin names. * @param parent: The parent of this widget. */ KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(QStringList pluginList, QWidget *parent) : KsCheckBoxTableWidget("Plugins", parent) { QTableWidgetItem *nameItem, *infoItem; QStringList headers; int nPlgins; headers << "Load" << "Name" << "Info"; nPlgins = pluginList.count(); _initTable(headers, nPlgins); _id.resize(nPlgins); for (int i = 0; i < nPlgins; ++i) { nameItem = new QTableWidgetItem(pluginList[i]); _table.setItem(i, 1, nameItem); infoItem = new QTableWidgetItem(" -- "); _table.setItem(i, 2, infoItem); _id[i] = i; } _adjustSize(); } trace-cmd-2.8.3/kernel-shark/src/KsWidgetsLib.hpp000066400000000000000000000160211351617527000216130ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file KsWidgetsLib.hpp * @brief Defines small widgets and dialogues used by the KernelShark GUI. */ #ifndef _KS_WIDGETS_LIB_H #define _KS_WIDGETS_LIB_H // Qt #include /** * The KsProgressBar class provides a visualization of the progress of a * running job. */ class KsProgressBar : public QWidget { Q_OBJECT QStatusBar _sb; QProgressBar _pb; public: KsProgressBar(QString message, QWidget *parent = nullptr); void setValue(int i); }; /** Defines the progress bar's maximum value. */ #define KS_PROGRESS_BAR_MAX 200 /** The height of the KsProgressBar widget. */ #define KS_BROGBAR_HEIGHT (FONT_HEIGHT * 5) /** The width of the KsProgressBar widget. */ #define KS_BROGBAR_WIDTH (FONT_WIDTH * 50) /** * The KsMessageDialog class provides a widget showing a message and having * a "Close" button. */ class KsMessageDialog : public QDialog { QVBoxLayout _layout; QLabel _text; QPushButton _closeButton; public: explicit KsMessageDialog(QWidget *parent) = delete; KsMessageDialog(QString message, QWidget *parent = nullptr); }; /** The height of the KsMessageDialog widget. */ #define KS_MSG_DIALOG_HEIGHT (FONT_HEIGHT * 8) /** The width of the KsMessageDialog widget. */ #define KS_MSG_DIALOG_WIDTH (SCREEN_WIDTH / 10) namespace KsWidgetsLib { bool fileExistsDialog(QString fileName); }; // KsWidgetsLib /** * The KsCheckBoxWidget class is the base class of all CheckBox widget used * by KernelShark. */ class KsCheckBoxWidget : public QWidget { Q_OBJECT public: KsCheckBoxWidget(const QString &name = "", QWidget *parent = nullptr); /** Get the name of the widget. */ QString name() const {return _name;} /** Get the state of the "all" checkboxe. */ bool all() const { if(_allCb.checkState() == Qt::Checked) return true; return false; } void setDefault(bool); void set(QVector v); QVector getCheckedIds(); private: QToolBar _tb; protected: /** The "all" checkboxe. */ QCheckBox _allCb; /** A vector of Id numbers coupled to each checkboxe. */ QVector _id; /** A nested widget used to position the checkboxes. */ QWidget _cbWidget; /** The layout of the nested widget. */ QVBoxLayout _cbLayout; /** The top level layout of this widget. */ QVBoxLayout _topLayout; /** The name of this widget. */ QString _name; /** A label to show the name of the widget. */ QLabel _nameLabel; private: virtual void _setCheckState(int i, Qt::CheckState st) = 0; virtual Qt::CheckState _checkState(int i) const = 0; virtual void _verify() {}; void _checkAll(bool); }; /** * The KsCheckBoxDialog class is the base class of all CheckBox dialogs * used by KernelShark. */ class KsCheckBoxDialog : public QDialog { Q_OBJECT public: KsCheckBoxDialog() = delete; KsCheckBoxDialog(KsCheckBoxWidget *cbw, QWidget *parent = nullptr); signals: /** Signal emitted when the "Apply" button is pressed. */ void apply(QVector); private: void _applyPress(); QVBoxLayout _topLayout; QHBoxLayout _buttonLayout; KsCheckBoxWidget *_checkBoxWidget; QPushButton _applyButton, _cancelButton; QMetaObject::Connection _applyButtonConnection; }; /** The KsCheckBoxTable class provides a table of checkboxes. */ class KsCheckBoxTable : public QTableWidget { Q_OBJECT public: explicit KsCheckBoxTable(QWidget *parent = nullptr); void init(QStringList headers, int size); /** A vector of checkboxes. */ QVector _cb; signals: /** Signal emitted when a checkboxes changes state. */ void changeState(int row); protected: void keyPressEvent(QKeyEvent *event) override; void mousePressEvent(QMouseEvent *event) override; private: void _doubleClicked(int row, int col); }; /** * The KsCheckBoxTableWidget class provides a widget to hold a table of * checkboxes. */ class KsCheckBoxTableWidget : public KsCheckBoxWidget { Q_OBJECT public: KsCheckBoxTableWidget(const QString &name = "", QWidget *parent = nullptr); protected: void _adjustSize(); void _initTable(QStringList headers, int size); /** The KsCheckBoxTable, shown by this widget. */ KsCheckBoxTable _table; private: void _setCheckState(int i, Qt::CheckState st) override { _table._cb[i]->setCheckState(st); } Qt::CheckState _checkState(int i) const override { return _table._cb[i]->checkState(); } void _update(bool); void _changeState(int row); }; /** The KsCheckBoxTree class provides a tree of checkboxes. */ class KsCheckBoxTree : public QTreeWidget { Q_OBJECT public: explicit KsCheckBoxTree(QWidget *parent = nullptr); signals: /** * Signal emitted when a checkboxes of the tree changes its state * and the state of all toplevel and child checkboxes has to be * reprocesed. */ void verify(); protected: void keyPressEvent(QKeyEvent *event) override; void mousePressEvent(QMouseEvent *event) override; private: void _doubleClicked(QTreeWidgetItem *item, int col); }; /** * The KsCheckBoxTreeWidget class provides a widget to hold a tree of * checkboxes. */ class KsCheckBoxTreeWidget : public KsCheckBoxWidget { Q_OBJECT public: KsCheckBoxTreeWidget() = delete; KsCheckBoxTreeWidget(const QString &name = "", QWidget *parent = nullptr); protected: void _adjustSize(); void _initTree(); /** The KsCheckBoxTree, shown by this widget. */ KsCheckBoxTree _tree; /** A vector of Tree items (checkboxes). */ QVector _cb; private: void _setCheckState(int i, Qt::CheckState st) override { _cb[i]->setCheckState(0, st); } Qt::CheckState _checkState(int i) const override { return _cb[i]->checkState(0); } void _update(QTreeWidgetItem *item, int column); void _verify(); }; /** * The KsCPUCheckBoxWidget class provides a widget for selecting CPU plots to * show. */ struct KsCPUCheckBoxWidget : public KsCheckBoxTreeWidget { KsCPUCheckBoxWidget() = delete; KsCPUCheckBoxWidget(struct tep_handle *pe, QWidget *parent = nullptr); }; /** * The KsTasksCheckBoxWidget class provides a widget for selecting Tasks * to show or hide. */ struct KsTasksCheckBoxWidget : public KsCheckBoxTableWidget { KsTasksCheckBoxWidget() = delete; KsTasksCheckBoxWidget(struct tep_handle *pe, bool cond = true, QWidget *parent = nullptr); private: /** * A positive condition means that you want to show Tasks and * a negative condition means that you want to hide Tasks. */ bool _cond; }; /** * The KsEventsCheckBoxWidget class provides a widget for selecting Trace * events to show or hide. */ struct KsEventsCheckBoxWidget : public KsCheckBoxTreeWidget { KsEventsCheckBoxWidget() = delete; KsEventsCheckBoxWidget(struct tep_handle *pe, QWidget *parent = nullptr); void removeSystem(QString name); }; /** * The KsPluginCheckBoxWidget class provides a widget for selecting plugins. */ struct KsPluginCheckBoxWidget : public KsCheckBoxTableWidget { KsPluginCheckBoxWidget() = delete; KsPluginCheckBoxWidget(QStringList pluginList, QWidget *parent = nullptr); }; #endif trace-cmd-2.8.3/kernel-shark/src/kernelshark.cpp000066400000000000000000000035331351617527000215700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ // C #include #include // Qt #include // KernelShark #include "KsCmakeDef.hpp" #include "KsMainWindow.hpp" #define default_input_file (char*)"trace.dat" static char *input_file; void usage(const char *prog) { printf("Usage: %s\n", prog); printf(" -h Display this help message\n"); printf(" -v Display version and exit\n"); printf(" -i input_file, default is %s\n", default_input_file); printf(" -p register plugin, use plugin name, absolute or relative path\n"); printf(" -u unregister plugin, use plugin name or absolute path\n"); printf(" -s import a session\n"); printf(" -l import the last session\n"); } int main(int argc, char **argv) { QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); KsMainWindow ks; int c; bool fromSession = false; while ((c = getopt(argc, argv, "hvi:p:u:s:l")) != -1) { switch(c) { case 'h': usage(argv[0]); return 0; case 'v': printf("%s - %s\n", basename(argv[0]), KS_VERSION_STRING); return 0; case 'i': input_file = optarg; break; case 'p': ks.registerPlugin(QString(optarg)); break; case 'u': ks.unregisterPlugin(QString(optarg)); break; case 's': ks.loadSession(QString(optarg)); fromSession = true; break; case 'l': ks.loadSession(ks.lastSessionFile()); fromSession = true; break; default: break; } } if (!fromSession) { if ((argc - optind) >= 1) { if (input_file) usage(argv[0]); input_file = argv[optind]; } if (!input_file) { struct stat st; if (stat(default_input_file, &st) == 0) input_file = default_input_file; } if (input_file) ks.loadDataFile(QString(input_file)); } ks.show(); return a.exec(); } trace-cmd-2.8.3/kernel-shark/src/kshark-record.cpp000066400000000000000000000007011351617527000220100ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ // C #include // KernelShark #include "KsCaptureDialog.hpp" int main(int argc, char **argv) { QApplication a(argc, argv); KsCaptureDialog cd; int c; while ((c = getopt(argc, argv, "o:")) != -1) { switch(c) { case 'o': cd.setOutputFileName(QString(optarg)); break; } } cd.show(); return a.exec(); } trace-cmd-2.8.3/kernel-shark/src/libkshark-collection.c000066400000000000000000000562511351617527000230270ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-collection.c * @brief Data Collections. */ // C #include #include #include #include // KernelShark #include "libkshark.h" /* Quiet warnings over documenting simple structures */ //! @cond Doxygen_Suppress enum collection_point_type { COLLECTION_IGNORE = 0, COLLECTION_RESUME, COLLECTION_BREAK, }; #define LAST_BIN -3 struct entry_list { struct entry_list *next; size_t index; uint8_t type; }; enum map_flags { COLLECTION_BEFORE = -1, COLLECTION_INSIDE = 0, COLLECTION_AFTER = 1, }; //! @endcond /* * If the type of the last added entry is COLLECTION_IGNORE, overwrite this * entry (ignore the old entry values). Else add a new entry to the list. */ static bool collection_add_entry(struct entry_list **list, size_t i, uint8_t type) { struct entry_list *entry = *list; if (entry->type != COLLECTION_IGNORE) { entry->next = malloc(sizeof(*entry)); if (!entry->next) return false; entry = entry->next; *list = entry; } entry->index = i; entry->type = type; return true; } static struct kshark_entry_collection * kshark_data_collection_alloc(struct kshark_context *kshark_ctx, struct kshark_entry **data, ssize_t first, size_t n_rows, matching_condition_func cond, int val, size_t margin) { struct kshark_entry_collection *col_ptr = NULL; struct kshark_entry *last_vis_entry = NULL; struct entry_list *col_list, *temp; size_t resume_count = 0, break_count = 0; size_t i, j, last_added = 0; ssize_t end; bool good_data = false; /* Create the collection. */ col_ptr = calloc(1, sizeof(*col_ptr)); if (!col_ptr) goto fail; end = first + n_rows - margin; if (first >= end) return col_ptr; col_list = malloc(sizeof(*col_list)); if (!col_list) goto fail; temp = col_list; if (margin != 0) { /* * If this collection includes margin data, add a margin data * interval at the very beginning of the data-set. */ temp->index = first; temp->type = COLLECTION_RESUME; ++resume_count; collection_add_entry(&temp, first + margin - 1, COLLECTION_BREAK); ++break_count; } else { temp->type = COLLECTION_IGNORE; } for (i = first + margin; i < end; ++i) { if (!cond(kshark_ctx, data[i], val)) { /* * The entry is irrelevant for this collection. * Do nothing. */ continue; } /* The Matching condition is satisfed. */ if (!good_data) { /* * Resume the collection here. Add some margin data * in front of the data of interest. */ good_data = true; if (last_added == 0 || last_added < i - margin) { collection_add_entry(&temp, i - margin, COLLECTION_RESUME); ++resume_count; } else { /* * Ignore the last collection Break point. * Continue extending the previous data * interval. */ temp->type = COLLECTION_IGNORE; --break_count; } } else if (good_data && data[i]->next && !cond(kshark_ctx, data[i]->next, val)) { /* * Break the collection here. Add some margin data * after the data of interest. */ good_data = false; last_vis_entry = data[i]; /* Keep adding entries until the "next" record. */ for (j = i + 1; j != end && last_vis_entry->next != data[j]; j++) ; /* * If the number of added entries is smaller than the * number of margin entries requested, keep adding * until you fill the margin. */ if (i + margin >= j) { for (;j < i + margin; ++j) { if (cond(kshark_ctx, data[j], val)) { /* * Good data has been found. * Continue extending the * previous data interval. */ good_data = true; break; } } } last_added = i = j; if (!good_data) { collection_add_entry(&temp, i, COLLECTION_BREAK); ++break_count; } } } if (good_data) { collection_add_entry(&temp, end - 1, COLLECTION_BREAK); ++break_count; } if (margin != 0) { /* * If this collection includes margin data, add a margin data * interval at the very end of the data-set. */ collection_add_entry(&temp, first + n_rows - margin, COLLECTION_RESUME); ++resume_count; collection_add_entry(&temp, first + n_rows - 1, COLLECTION_BREAK); ++break_count; } /* * If everything is OK, we must have pairs of COLLECTION_RESUME * and COLLECTION_BREAK points. */ assert(break_count == resume_count); col_ptr->next = NULL; col_ptr->resume_points = calloc(resume_count, sizeof(*col_ptr->resume_points)); if (!col_ptr->resume_points) goto fail; col_ptr->break_points = calloc(break_count, sizeof(*col_ptr->break_points)); if (!col_ptr->break_points) { free(col_ptr->resume_points); goto fail; } col_ptr->cond = cond; col_ptr->val = val; col_ptr->size = resume_count; for (i = 0; i < col_ptr->size; ++i) { assert(col_list->type == COLLECTION_RESUME); col_ptr->resume_points[i] = col_list->index; temp = col_list; col_list = col_list->next; free(temp); assert(col_list->type == COLLECTION_BREAK); col_ptr->break_points[i] = col_list->index; temp = col_list; col_list = col_list->next; free(temp); } return col_ptr; fail: fprintf(stderr, "Failed to allocate memory for Data collection.\n"); free(col_ptr); for (i = 0; i < resume_count + break_count; ++i) { temp = col_list; col_list = col_list->next; free(temp); } return NULL; } /* * This function provides mapping between the index inside the data-set and * the index of the collection interval. Additional output flag is used to * resolve the ambiguity of the mapping. If the value of the flag is * COLLECTION_INSIDE, the "source_index" is inside the returned interval. If * the value of the flag is COLLECTION_BEFORE, the "source_index" is inside * the gap before the returned interval. If the value of the flag is * COLLECTION_AFTER, the "source_index" is inside the gap after the returned * interval. */ static ssize_t map_collection_index_from_source(const struct kshark_entry_collection *col, size_t source_index, int *flag) { size_t l, h, mid; if (!col->size) return KS_EMPTY_BIN; l = 0; h = col->size - 1; if (source_index < col->resume_points[l]) { *flag = COLLECTION_BEFORE; return l; } if (source_index >= col->resume_points[h]) { if (source_index < col->break_points[h]) *flag = COLLECTION_INSIDE; else *flag = COLLECTION_AFTER; return h; } BSEARCH(h, l, source_index > col->resume_points[mid]); if (source_index <= col->break_points[l]) *flag = COLLECTION_INSIDE; else *flag = COLLECTION_AFTER; return l; } static ssize_t map_collection_request_init(const struct kshark_entry_collection *col, struct kshark_entry_request **req, bool front, size_t *end) { struct kshark_entry_request *req_tmp = *req; int col_index_flag; ssize_t col_index; size_t req_end; if (req_tmp->next || col->size == 0) { fprintf(stderr, "Unexpected input in "); fprintf(stderr, "map_collection_request_init()\n"); goto do_nothing; } req_end = front ? req_tmp->first + req_tmp->n - 1 : req_tmp->first - req_tmp->n + 1; /* * Find the first Resume Point of the collection which is equal or * greater than the first index of this request. */ col_index = map_collection_index_from_source(col, req_tmp->first, &col_index_flag); /* * The value of "col_index" is ambiguous. Use the "col_index_flag" to * deal with all possible cases. */ if (col_index == KS_EMPTY_BIN) { /* Empty collection. */ goto do_nothing; } if (col_index_flag == COLLECTION_AFTER) { /* * This request starts after the end of interval "col_index". */ if (front && (col_index == col->size - 1 || req_end < col->resume_points[col_index + 1])) { /* * No overlap between the collection and this front * request. Do nothing. */ goto do_nothing; } else if (!front && req_end > col->break_points[col_index]) { /* * No overlap between the collection and this back * request. Do nothing. */ goto do_nothing; } /* Remember that the initial request starts in the gap between * the end of "col_index" interval and the beginning of * "col_index + 1" interval. If we process a Front request, we * have to go forwards, so the proper place for starting our * search will be the Resume point of the "col_index + 1" * interval. However, if we process a Back request, we will be * going backwards, so the proper place to start will be the * Break point of "col_index". */ req_tmp->first = front ? col->resume_points[++col_index] : col->break_points[col_index]; } if (col_index_flag == COLLECTION_BEFORE) { /* * This request starts before the beginning of interval * "col_index". */ if (!front && (col_index == 0 || req_end > col->break_points[col_index - 1])) { /* * No overlap between the collection and this back * request. Do nothing. */ goto do_nothing; } else if (front && req_end < col->resume_points[col_index]) { /* * No overlap between the collection and this front * request. Do nothing. */ goto do_nothing; } /* Remember that the initial request starts in the gap between * the end of "col_index - 1" interval and the beginning of * "col_index" interval. If we process a Front request, we have * to go forwards, so the proper place for starting our search * will be the Resume point of the "col_index" interval. * However, if we process a Back request, we will be going * backwards, so the proper place to start will be the Break * point of "col_index - 1". */ req_tmp->first = front ? col->resume_points[col_index] : col->break_points[--col_index]; } *end = req_end; return col_index; do_nothing: kshark_free_entry_request(*req); *req = NULL; *end = KS_EMPTY_BIN; return KS_EMPTY_BIN; } /* * This function uses the intervals of the Data collection to transform the * inputted single data request into a list of data requests. The new list of * request will ignore the data outside of the intervals of the collection. */ static int map_collection_back_request(const struct kshark_entry_collection *col, struct kshark_entry_request **req) { struct kshark_entry_request *req_tmp; size_t req_first, req_end; ssize_t col_index; int req_count; col_index = map_collection_request_init(col, req, false, &req_end); if (col_index == KS_EMPTY_BIN) return 0; /* * Now loop over the intervals of the collection going backwards till * the end of the inputted request and create a separate request for * each of those interest. */ req_tmp = *req; req_count = 1; while (col_index >= 0 && req_end <= col->break_points[col_index]) { if (req_end >= col->resume_points[col_index]) { /* * The last entry of the original request is inside * the "col_index" collection interval. Close the * collection request here and return. */ req_tmp->n = req_tmp->first - req_end + 1; break; } /* * The last entry of the original request is outside of the * "col_index" interval. Close the collection request at the * end of this interval and move to the next one. Try to make * another request there. */ req_tmp->n = req_tmp->first - col->resume_points[col_index] + 1; --col_index; if (req_end > col->break_points[col_index]) { /* * The last entry of the original request comes before * the end of the next collection interval. Stop here. */ break; } if (col_index > 0) { /* Make a new request. */ req_first = col->break_points[col_index]; req_tmp->next = kshark_entry_request_alloc(req_first, 0, req_tmp->cond, req_tmp->val, req_tmp->vis_only, req_tmp->vis_mask); if (!req_tmp->next) goto fail; req_tmp = req_tmp->next; ++req_count; } } return req_count; fail: fprintf(stderr, "Failed to allocate memory for "); fprintf(stderr, "Collection data request.\n"); kshark_free_entry_request(*req); *req = NULL; return -ENOMEM; } /* * This function uses the intervals of the Data collection to transform the * inputted single data request into a list of data requests. The new list of * requests will ignore the data outside of the intervals of the collection. */ static int map_collection_front_request(const struct kshark_entry_collection *col, struct kshark_entry_request **req) { struct kshark_entry_request *req_tmp; size_t req_first, req_end; ssize_t col_index; int req_count; col_index = map_collection_request_init(col, req, true, &req_end); if (col_index == KS_EMPTY_BIN) return 0; /* * Now loop over the intervals of the collection going forwards till * the end of the inputted request and create a separate request for * each of those interest. */ req_count = 1; req_tmp = *req; while (col_index < col->size && req_end >= col->resume_points[col_index]) { if (req_end <= col->break_points[col_index]) { /* * The last entry of the original request is inside * the "col_index" collection interval. * Close the collection request here and return. */ req_tmp->n = req_end - req_tmp->first + 1; break; } /* * The last entry of the original request is outside this * collection interval (col_index). Close the collection * request at the end of the interval and move to the next * interval. Try to make another request there. */ req_tmp->n = col->break_points[col_index] - req_tmp->first + 1; ++col_index; if (req_end < col->resume_points[col_index]) { /* * The last entry of the original request comes before * the beginning of next collection interval. * Stop here. */ break; } if (col_index < col->size) { /* Make a new request. */ req_first = col->resume_points[col_index]; req_tmp->next = kshark_entry_request_alloc(req_first, 0, req_tmp->cond, req_tmp->val, req_tmp->vis_only, req_tmp->vis_mask); if (!req_tmp->next) goto fail; req_tmp = req_tmp->next; ++req_count; } } return req_count; fail: fprintf(stderr, "Failed to allocate memory for "); fprintf(stderr, "Collection data request.\n"); kshark_free_entry_request(*req); *req = NULL; return -ENOMEM; } /** * @brief Search for an entry satisfying the requirements of a given Data * request. Start from the position provided by the request and go * searching in the direction of the increasing timestamps (front). * The search is performed only inside the intervals, defined by * the data collection. * * @param req: Input location for a single Data request. The imputted request * will be transformed into a list of requests. This new list of * requests will ignore the data outside of the intervals of the * collection. * @param data: Input location for the trace data. * @param col: Input location for the Data collection. * @param index: Optional output location for the index of the returned * entry inside the array. * * @returns Pointer to the first entry satisfying the matching condition on * success, or NULL on failure. * In the special case when some entries, satisfying the Matching * condition function have been found, but all these entries have * been discarded because of the visibility criteria (filtered * entries), the function returns a pointer to a special * "Dummy entry". */ const struct kshark_entry * kshark_get_collection_entry_front(struct kshark_entry_request **req, struct kshark_entry **data, const struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry = NULL; int req_count; /* * Use the intervals of the Data collection to redefine the data * request in a way which will ignore the data outside of the * intervals of the collection. */ req_count = map_collection_front_request(col, req); if (index && !req_count) *index = KS_EMPTY_BIN; /* * Loop over the list of redefined requests and search until you find * the first matching entry. */ while (*req) { entry = kshark_get_entry_front(*req, data, index); if (entry) break; *req = (*req)->next; } return entry; } /** * @brief Search for an entry satisfying the requirements of a given Data * request. Start from the position provided by the request and go * searching in the direction of the decreasing timestamps (back). * The search is performed only inside the intervals, defined by * the data collection. * * @param req: Input location for Data request. The imputed request * will be transformed into a list of requests. This new list of * requests will ignore the data outside of the intervals of the * collection. * @param data: Input location for the trace data. * @param col: Input location for the Data collection. * @param index: Optional output location for the index of the returned * entry inside the array. * * @returns Pointer to the first entry satisfying the matching condition on * success, or NULL on failure. * In the special case when some entries, satisfying the Matching * condition function have been found, but all these entries have * been discarded because of the visibility criteria (filtered * entries), the function returns a pointer to a special * "Dummy entry". */ const struct kshark_entry * kshark_get_collection_entry_back(struct kshark_entry_request **req, struct kshark_entry **data, const struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry = NULL; int req_count; /* * Use the intervals of the Data collection to redefine the data * request in a way which will ignore the data outside of the * intervals of the collection. */ req_count = map_collection_back_request(col, req); if (index && !req_count) *index = KS_EMPTY_BIN; /* * Loop over the list of redefined requests and search until you find * the first matching entry. */ while (*req) { entry = kshark_get_entry_back(*req, data, index); if (entry) break; *req = (*req)->next; } return entry; } /** * @brief Search the list of Data collections and find the collection defined * with a given Matching condition function and value. * * @param col: Input location for the Data collection list. * @param cond: Matching condition function. * @param val: Matching condition value, used by the Matching condition * function. * * @returns Pointer to a Data collections on success, or NULL on failure. */ struct kshark_entry_collection * kshark_find_data_collection(struct kshark_entry_collection *col, matching_condition_func cond, int val) { while (col) { if (col->cond == cond && col->val == val) return col; col = col->next; } return NULL; } /** * @brief Clear all data intervals of the given Data collection. * * @param col: Input location for the Data collection. */ void kshark_reset_data_collection(struct kshark_entry_collection *col) { free(col->resume_points); col->resume_points = NULL; free(col->break_points); col->break_points = NULL; col->size = 0; } static void kshark_free_data_collection(struct kshark_entry_collection *col) { free(col->resume_points); free(col->break_points); free(col); } /** * @brief Allocate and process data collection, defined with a given Matching * condition function and value. Add this collection to the list of * collections used by the session. * * @param kshark_ctx: Input location for the session context pointer. * @param data: Input location for the trace data. * @param n_rows: The size of the inputted data. * @param cond: Matching condition function for the collection to be * registered. * @param val: Matching condition value of for collection to be registered. * @param margin: The size of the additional (margin) data which do not * satisfy the matching condition, but is added at the * beginning and at the end of each interval of the collection * as well as at the beginning and at the end of data-set. If * "0", no margin data is added. * * @returns Pointer to the registered Data collections on success, or NULL * on failure. */ struct kshark_entry_collection * kshark_register_data_collection(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, int val, size_t margin) { struct kshark_entry_collection *col; col = kshark_add_collection_to_list(kshark_ctx, &kshark_ctx->collections, data, n_rows, cond, val, margin); return col; } /** * @brief Allocate and process data collection, defined with a given Matching * condition function and value. Add this collection to a given list of * collections. * * @param kshark_ctx: Input location for the session context pointer. * @param col_list: Input location for the list of collections. * @param data: Input location for the trace data. * @param n_rows: The size of the inputted data. * @param cond: Matching condition function for the collection to be * registered. * @param val: Matching condition value of for collection to be registered. * @param margin: The size of the additional (margin) data which do not * satisfy the matching condition, but is added at the * beginning and at the end of each interval of the collection * as well as at the beginning and at the end of data-set. If * "0", no margin data is added. * * @returns Pointer to the registered Data collections on success, or NULL * on failure. */ struct kshark_entry_collection * kshark_add_collection_to_list(struct kshark_context *kshark_ctx, struct kshark_entry_collection **col_list, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, int val, size_t margin) { struct kshark_entry_collection *col; col = kshark_data_collection_alloc(kshark_ctx, data, 0, n_rows, cond, val, margin); if (col) { col->next = *col_list; *col_list = col; } return col; } /** * @brief Search the list of Data collections for a collection defined * with a given Matching condition function and value. If such a * collection exists, unregister (remove and free) this collection * from the list. * * @param col: Input location for the Data collection list. * @param cond: Matching condition function of the collection to be * unregistered. * * @param val: Matching condition value of the collection to be unregistered. */ void kshark_unregister_data_collection(struct kshark_entry_collection **col, matching_condition_func cond, int val) { struct kshark_entry_collection **last = col; struct kshark_entry_collection *list; for (list = *col; list; list = list->next) { if (list->cond == cond && list->val == val) { *last = list->next; kshark_free_data_collection(list); return; } last = &list->next; } } /** * @brief Free all Data collections in a given list. * * @param col: Input location for the Data collection list. */ void kshark_free_collection_list(struct kshark_entry_collection *col) { struct kshark_entry_collection *last; while (col) { last = col; col = col->next; kshark_free_data_collection(last); } } trace-cmd-2.8.3/kernel-shark/src/libkshark-configio.c000066400000000000000000001255161351617527000224720ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-configio.c * @brief Json Configuration I/O. */ // C #ifndef _GNU_SOURCE /** Use GNU C Library. */ #define _GNU_SOURCE #endif #include #include // KernelShark #include "libkshark.h" #include "libkshark-model.h" static struct json_object *kshark_json_config_alloc(const char *type) { json_object *jobj, *jtype; jobj = json_object_new_object(); jtype = json_object_new_string(type); if (!jobj || !jtype) goto fail; /* Set the type of this Json document. */ json_object_object_add(jobj, "type", jtype); return jobj; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jobj); json_object_put(jtype); return NULL; } /** * @brief Allocate kshark_config_doc and set its format. * * @param format: Input location for the Configuration format identifier. * Currently only Json and String formats are supported. * * @returns kshark_config_doc instance on success, otherwise NULL. Use * free() to free the object. */ struct kshark_config_doc * kshark_config_alloc(enum kshark_config_formats format) { struct kshark_config_doc *doc; switch (format) { case KS_CONFIG_AUTO: case KS_CONFIG_JSON: case KS_CONFIG_STRING: doc = malloc(sizeof(*doc)); if (!doc) goto fail; doc->format = format; doc->conf_doc = NULL; return doc; default: fprintf(stderr, "Document format %d not supported\n", format); } return NULL; fail: fprintf(stderr, "Failed to allocate memory for kshark_config_doc.\n"); return NULL; } /** * @brief Create an empty Configuration document and set its format and type. * * @param type: String describing the type of the document, * e.g. "kshark.config.record" or "kshark.config.filter". * @param format: Input location for the Configuration format identifier. * Currently only Json format is supported. * * @returns kshark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * kshark_config_new(const char *type, enum kshark_config_formats format) { struct kshark_config_doc *doc = NULL; if (format == KS_CONFIG_AUTO) format = KS_CONFIG_JSON; switch (format) { case KS_CONFIG_JSON: doc = kshark_config_alloc(format); if (doc) { doc->conf_doc = kshark_json_config_alloc(type); if (!doc->conf_doc) { free(doc); doc = NULL; } } break; case KS_CONFIG_STRING: doc = kshark_config_alloc(format); break; default: fprintf(stderr, "Document format %d not supported\n", format); return NULL; } return doc; } /** * @brief Free the Configuration document. * * @param conf: Input location for the kshark_config_doc instance. It is safe * to pass a NULL value. */ void kshark_free_config_doc(struct kshark_config_doc *conf) { if (!conf) return; switch (conf->format) { case KS_CONFIG_JSON: json_object_put(conf->conf_doc); break; case KS_CONFIG_STRING: free(conf->conf_doc); break; } free(conf); } /** * @brief Use an existing Json document to create a new KernelShark * Configuration document. * * @param jobj: Input location for the json_object instance. * * @returns shark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj) { struct kshark_config_doc *conf = kshark_config_alloc(KS_CONFIG_JSON); if (conf) conf->conf_doc = jobj; return conf; } /** * @brief Use an existing string to create a new KernelShark Configuration * document. * * @param val: Input location for the string. * * @returns shark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc *kshark_string_to_conf(const char* val) { struct kshark_config_doc *conf; char *str; conf = kshark_config_alloc(KS_CONFIG_STRING); if (conf) { if (asprintf(&str, "%s", val) > 0) { conf->conf_doc = str; } else { fprintf(stderr, "Failed to allocate string conf. doc.\n"); free(conf); conf = NULL; } } return conf; } /** * @brief Add a field to a KernelShark Configuration document. * * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @param key: The name of the field. * @param val: Input location for the kshark_config_doc to be added. Currently * only Json and String formats are supported. Pass KS_CONFIG_AUTO * if you want "val" to have the same fornat as "conf". Upon * calling this function, the ownership of "val" transfers to * "conf". * * @returns True on success, otherwise False. */ bool kshark_config_doc_add(struct kshark_config_doc *conf, const char *key, struct kshark_config_doc *val) { struct json_object *jobj = NULL; if (!conf || !val) return false; if (val->format == KS_CONFIG_AUTO) val->format = conf->format; switch (conf->format) { case KS_CONFIG_JSON: switch (val->format) { case KS_CONFIG_JSON: json_object_object_add(conf->conf_doc, key, val->conf_doc); break; case KS_CONFIG_STRING: jobj = json_object_new_string(val->conf_doc); if (!jobj) goto fail; json_object_object_add(conf->conf_doc, key, jobj); break; default: fprintf(stderr, "Value format %d not supported\n", val->format); return false; } free(val); break; default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jobj); return false; } static bool get_jval(struct kshark_config_doc *conf, const char *key, void **val) { return json_object_object_get_ex(conf->conf_doc, key, (json_object **) val); } /** * @brief Get the KernelShark Configuration document associate with a given * field name. * * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @param key: The name of the field. * @param val: Output location for the kshark_config_doc instance containing * the field. Currently only Json and String formats are supported. * * @returns True, if the key exists, otherwise False. */ bool kshark_config_doc_get(struct kshark_config_doc *conf, const char *key, struct kshark_config_doc *val) { struct kshark_config_doc *tmp; if (!conf || !val) return false; if (val->format == KS_CONFIG_AUTO) val->format = conf->format; switch (conf->format) { case KS_CONFIG_JSON: switch (val->format) { case KS_CONFIG_JSON: json_object_put(val->conf_doc); if (!get_jval(conf, key, &val->conf_doc)) goto fail; return true; case KS_CONFIG_STRING: tmp = kshark_config_alloc(KS_CONFIG_AUTO); if (!tmp) goto fail; if (!get_jval(conf, key, &tmp->conf_doc)) goto fail; free(val->conf_doc); val->conf_doc = (char *) json_object_get_string(tmp->conf_doc); free(tmp); return true; default: fprintf(stderr, "Value format %d not supported\n", val->format); return false; } break; default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } return true; fail: fprintf(stderr, "Failed to get config. document.\n"); return false; } /** * @brief Create an empty Record Configuration document. The type description * is set to "kshark.config.record". * * @returns kshark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * kshark_record_config_new(enum kshark_config_formats format) { return kshark_config_new("kshark.config.record", format); } /** * @brief Create an empty Filter Configuration document. The type description * is set to "kshark.config.filter". * * @returns kshark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * kshark_filter_config_new(enum kshark_config_formats format) { return kshark_config_new("kshark.config.filter", format); } /** * @brief Create an empty Text Configuration document. The Text Configuration * documents do not use type descriptions. * * @returns kshark_config_doc instance on success, otherwise NULL. Use free() * to free the object. */ struct kshark_config_doc *kshark_string_config_alloc(void) { return kshark_config_alloc(KS_CONFIG_STRING); } static void json_del_if_exist(struct json_object *jobj, const char *key) { struct json_object *temp; if (json_object_object_get_ex(jobj, key, &temp)) json_object_object_del(jobj, key); } static bool kshark_json_type_check(struct json_object *jobj, const char *type) { struct json_object *jtype; const char *type_str; if (!json_object_object_get_ex(jobj, "type", &jtype)) return false; type_str = json_object_get_string(jtype); if (strcmp(type_str, type) != 0) return false; return true; } /** * @brief Check the type of a Configuration document and compare with an * expected value. * * @param conf: Input location for the kshark_config_doc instance. * @param type: Input location for the expected value of the Configuration * document type, e.g. "kshark.config.record" or * "kshark.config.filter". * * @returns True, if the document has the expected type, otherwise False. */ bool kshark_type_check(struct kshark_config_doc *conf, const char *type) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_json_type_check(conf->conf_doc, type); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_trace_file_to_json(const char *file, struct json_object *jobj) { struct json_object *jfile_name, *jtime; struct stat st; if (!file || !jobj) return false; if (stat(file, &st) != 0) { fprintf(stderr, "Unable to find file %s\n", file); return false; } jfile_name = json_object_new_string(file); jtime = json_object_new_int64(st.st_mtime); if (!jfile_name || !jtime) goto fail; json_object_object_add(jobj, "file", jfile_name); json_object_object_add(jobj, "time", jtime); return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jobj); json_object_put(jfile_name); json_object_put(jtime); return false; } /** * @brief Record the name of a trace data file and its timestamp into a * Configuration document. * * @param file: The name of the file. * @param format: Input location for the Configuration format identifier. * Currently only Json format is supported. * * @returns True on success, otherwise False. */ struct kshark_config_doc * kshark_export_trace_file(const char *file, enum kshark_config_formats format) { /* Create a new Configuration document. */ struct kshark_config_doc *conf = kshark_config_new("kshark.config.data", format); if (!conf) return NULL; switch (format) { case KS_CONFIG_JSON: kshark_trace_file_to_json(file, conf->conf_doc); return conf; default: fprintf(stderr, "Document format %d not supported\n", conf->format); return NULL; } } static bool kshark_trace_file_from_json(const char **file, struct json_object *jobj) { struct json_object *jfile_name, *jtime; const char *file_str; struct stat st; int64_t time; if (!jobj) return false; if (!kshark_json_type_check(jobj, "kshark.config.data") || !json_object_object_get_ex(jobj, "file", &jfile_name) || !json_object_object_get_ex(jobj, "time", &jtime)) { fprintf(stderr, "Failed to retrieve data file from json_object.\n"); return false; } file_str = json_object_get_string(jfile_name); time = json_object_get_int64(jtime); if (stat(file_str, &st) != 0) { fprintf(stderr, "Unable to find file %s\n", file_str); return false; } if (st.st_mtime != time) { fprintf(stderr,"Timestamp mismatch!\nFile %s", file_str); return false; } *file = file_str; return true; } /** * @brief Read the name of a trace data file and its timestamp from a * Configuration document and check if such a file exists. * If the file exists, open it. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns The name of the file on success, otherwise NULL. "conf" has * the ownership over the returned string. */ const char* kshark_import_trace_file(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { const char *file = NULL; switch (conf->format) { case KS_CONFIG_JSON: if (kshark_trace_file_from_json(&file, conf->conf_doc)) kshark_open(kshark_ctx, file); break; default: fprintf(stderr, "Document format %d not supported\n", conf->format); return NULL; } return file; } static bool kshark_model_to_json(struct kshark_trace_histo *histo, struct json_object *jobj) { struct json_object *jrange, *jmin, *jmax, *jn_bins; if (!histo || !jobj) return false; jrange = json_object_new_array(); jmin = json_object_new_int64(histo->min); jmax = json_object_new_int64(histo->max); jn_bins = json_object_new_int(histo->n_bins); if (!jrange || !jmin || !jmax || !jn_bins) goto fail; json_object_array_put_idx(jrange, 0, jmin); json_object_array_put_idx(jrange, 1, jmax); json_object_object_add(jobj, "range", jrange); json_object_object_add(jobj, "bins", jn_bins); return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jobj); json_object_put(jrange); json_object_put(jmin); json_object_put(jmax); json_object_put(jn_bins); return false; } /** * @brief Record the current configuration of the Vis. model into a * Configuration document. * Load the configuration of the Vis. model from a Configuration * document. * * @param histo: Input location for the Vis. model descriptor. * @param format: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True on success, otherwise False. */ struct kshark_config_doc * kshark_export_model(struct kshark_trace_histo *histo, enum kshark_config_formats format) { /* Create a new Configuration document. */ struct kshark_config_doc *conf = kshark_config_new("kshark.config.model", format); if (!conf) return NULL; switch (format) { case KS_CONFIG_JSON: kshark_model_to_json(histo, conf->conf_doc); return conf; default: fprintf(stderr, "Document format %d not supported\n", format); return NULL; } } static bool kshark_model_from_json(struct kshark_trace_histo *histo, struct json_object *jobj) { struct json_object *jrange, *jmin, *jmax, *jn_bins; uint64_t min, max; int n_bins; if (!histo || !jobj) return false; if (!kshark_json_type_check(jobj, "kshark.config.model") || !json_object_object_get_ex(jobj, "range", &jrange) || !json_object_object_get_ex(jobj, "bins", &jn_bins) || json_object_get_type(jrange) != json_type_array || json_object_array_length(jrange) != 2) goto fail; jmin = json_object_array_get_idx(jrange, 0); jmax = json_object_array_get_idx(jrange, 1); if (!jmin || !jmax) goto fail; min = json_object_get_int64(jmin); max = json_object_get_int64(jmax); n_bins = json_object_get_int(jn_bins); ksmodel_set_bining(histo, n_bins, min, max); if (histo->data && histo->data_size) ksmodel_fill(histo, histo->data, histo->data_size); return true; fail: fprintf(stderr, "Failed to load event filter from json_object.\n"); return false; } /** * @brief Load the configuration of the Vis. model from a Configuration * document. * * @param histo: Input location for the Vis. model descriptor. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if the model has been loaded. If the model configuration * document contains no data or in a case of an error, the function * returns False. */ bool kshark_import_model(struct kshark_trace_histo *histo, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_model_from_json(histo, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_event_filter_to_json(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct json_object *jobj) { json_object *jfilter_data, *jevent, *jsystem, *jname; struct tep_event *event; int i, evt, *ids, nr_events; char *temp; jevent = jsystem = jname = NULL; /* * If this Json document already contains a description of the filter, * delete this description. */ json_del_if_exist(jobj, filter_name); /* Get the array of Ids to be fitered. */ ids = tracecmd_filter_ids(filter); if (!ids) return true; /* Create a Json array and fill the Id values into it. */ jfilter_data = json_object_new_array(); if (!jfilter_data) goto fail; nr_events = tep_get_events_count(pevent); for (i = 0; i < filter->count; ++i) { for (evt = 0; evt < nr_events; ++evt) { event = tep_get_event(pevent, evt); if (event->id == ids[i]) { jevent = json_object_new_object(); temp = event->system; jsystem = json_object_new_string(temp); temp = event->name; jname = json_object_new_string(temp); if (!jevent || !jsystem || !jname) goto fail; json_object_object_add(jevent, "system", jsystem); json_object_object_add(jevent, "name", jname); json_object_array_add(jfilter_data, jevent); break; } } } free(ids); /* Add the array of Ids to the filter config document. */ json_object_object_add(jobj, filter_name, jfilter_data); return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jfilter_data); json_object_put(jevent); json_object_put(jsystem); json_object_put(jname); free(ids); return false; } /** * @brief Record the current configuration of an Event Id filter into a * Configuration document. * * @param pevent: Input location for the Page event. * @param filter: Input location for an Id filter. * @param filter_name: The name of the filter to show up in the Json document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True on success, otherwise False. */ bool kshark_export_event_filter(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_event_filter_to_json(pevent, filter, filter_name, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_event_filter_from_json(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct json_object *jobj) { json_object *jfilter, *jevent, *jsystem, *jname; const char *system_str, *name_str; struct tep_event *event; int i, length; /* * Use the name of the filter to find the array of events associated * with this filter. Notice that the filter config document may * contain no data for this particular filter. */ if (!json_object_object_get_ex(jobj, filter_name, &jfilter)) return false; if (!kshark_json_type_check(jobj, "kshark.config.filter") || json_object_get_type(jfilter) != json_type_array) goto fail; /* Set the filter. */ length = json_object_array_length(jfilter); for (i = 0; i < length; ++i) { jevent = json_object_array_get_idx(jfilter, i); if (!json_object_object_get_ex(jevent, "system", &jsystem) || !json_object_object_get_ex(jevent, "name", &jname)) goto fail; system_str = json_object_get_string(jsystem); name_str = json_object_get_string(jname); event = tep_find_event_by_name(pevent, system_str, name_str); if (!event) goto fail; tracecmd_filter_id_add(filter, event->id); } return true; fail: fprintf(stderr, "Failed to load event filter from json_object.\n"); return false; } /** * @brief Load from Configuration document the configuration of an Event Id filter. * * @param pevent: Input location for the Page event. * @param filter: Input location for an Id filter. * @param filter_name: The name of the filter as showing up in the Config. * document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for this particular filter or in a case * of an error, the function returns False. */ bool kshark_import_event_filter(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_event_filter_from_json(pevent, filter, filter_name, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter, const char *filter_name, struct json_object *jobj) { json_object *jfilter_data, *jpid = NULL; int i, *ids; /* * If this Json document already contains a description of the filter, * delete this description. */ json_del_if_exist(jobj, filter_name); /* Get the array of Ids to be filtered. */ ids = tracecmd_filter_ids(filter); if (!ids) return true; /* Create a Json array and fill the Id values into it. */ jfilter_data = json_object_new_array(); if (!jfilter_data) goto fail; for (i = 0; i < filter->count; ++i) { jpid = json_object_new_int(ids[i]); if (!jpid) goto fail; json_object_array_add(jfilter_data, jpid); } free(ids); /* Add the array of Ids to the filter config document. */ json_object_object_add(jobj, filter_name, jfilter_data); return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jfilter_data); json_object_put(jpid); free(ids); return false; } /** * @brief Record the current configuration of a simple Id filter into a * Configuration document. * * @param filter: Input location for an Id filter. * @param filter_name: The name of the filter to show up in the Json document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True on success, otherwise False. */ bool kshark_export_filter_array(struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_filter_array_to_json(filter, filter_name, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter, const char *filter_name, struct json_object *jobj) { json_object *jfilter, *jpid; int i, length; /* * Use the name of the filter to find the array of events associated * with this filter. Notice that the filter config document may * contain no data for this particular filter. */ if (!json_object_object_get_ex(jobj, filter_name, &jfilter)) return false; if (!kshark_json_type_check(jobj, "kshark.config.filter") || json_object_get_type(jfilter) != json_type_array) goto fail; /* Set the filter. */ length = json_object_array_length(jfilter); for (i = 0; i < length; ++i) { jpid = json_object_array_get_idx(jfilter, i); if (!jpid) goto fail; tracecmd_filter_id_add(filter, json_object_get_int(jpid)); } return true; fail: fprintf(stderr, "Failed to load task filter from json_object.\n"); return false; } /** * @brief Load from Configuration document the configuration of a simple * Id filter. * * @param filter: Input location for an Id filter. * @param filter_name: The name of the filter as showing up in the Config. * document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for this particular filter or in a case * of an error, the function returns False. */ bool kshark_import_filter_array(struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_filter_array_from_json(filter, filter_name, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, struct json_object *jobj) { struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter; json_object *jfilter_data, *jevent, *jsystem, *jname, *jfilter; struct tep_event **events; char *str; int i; jevent = jsystem = jname = jfilter = NULL; /* * If this Json document already contains a description of the model, * delete this description. */ json_del_if_exist(jobj, KS_ADV_EVENT_FILTER_NAME); if (!kshark_ctx->advanced_event_filter || !kshark_ctx->advanced_event_filter->filters) return true; /* Create a Json array and fill the Id values into it. */ jfilter_data = json_object_new_array(); if (!jfilter_data) goto fail; events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); if (!events) return false; for (i = 0; events[i]; i++) { str = tep_filter_make_string(adv_filter, events[i]->id); if (!str) continue; jevent = json_object_new_object(); jsystem = json_object_new_string(events[i]->system); jname = json_object_new_string(events[i]->name); jfilter = json_object_new_string(str); if (!jevent || !jsystem || !jname || !jfilter) goto fail; json_object_object_add(jevent, "system", jsystem); json_object_object_add(jevent, "name", jname); json_object_object_add(jevent, "condition", jfilter); json_object_array_add(jfilter_data, jevent); } /* Add the array of advanced filters to the filter config document. */ json_object_object_add(jobj, KS_ADV_EVENT_FILTER_NAME, jfilter_data); return true; fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jfilter_data); json_object_put(jevent); json_object_put(jsystem); json_object_put(jname); json_object_put(jfilter); return false; } /** * @brief Record the current configuration of the advanced filter into a * Configuration document. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Adv. Filter * Configuration document will be created. * * @returns True on success, otherwise False. */ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf) { if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); if (!*conf) return false; switch ((*conf)->format) { case KS_CONFIG_JSON: return kshark_adv_filters_to_json(kshark_ctx, (*conf)->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", (*conf)->format); return false; } } static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, struct json_object *jobj) { struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter; json_object *jfilter, *jsystem, *jname, *jcond; int i, length, n, ret = 0; char *filter_str = NULL; /* * Use the name of the filter to find the array of events associated * with this filter. Notice that the filter config document may * contain no data for this particular filter. */ if (!json_object_object_get_ex(jobj, KS_ADV_EVENT_FILTER_NAME, &jfilter)) return false; if (!kshark_json_type_check(jobj, "kshark.config.filter") || json_object_get_type(jfilter) != json_type_array) goto fail; /* Set the filter. */ length = json_object_array_length(jfilter); for (i = 0; i < length; ++i) { jfilter = json_object_array_get_idx(jfilter, i); if (!json_object_object_get_ex(jfilter, "system", &jsystem) || !json_object_object_get_ex(jfilter, "name", &jname) || !json_object_object_get_ex(jfilter, "condition", &jcond)) goto fail; n = asprintf(&filter_str, "%s/%s:%s", json_object_get_string(jsystem), json_object_get_string(jname), json_object_get_string(jcond)); if (n <= 0) { filter_str = NULL; goto fail; } ret = tep_filter_add_filter_str(adv_filter, filter_str); if (ret < 0) goto fail; } return true; fail: fprintf(stderr, "Failed to laod Advanced filters.\n"); if (ret < 0) { char error_str[200]; int error_status = tep_strerror(kshark_ctx->pevent, ret, error_str, sizeof(error_str)); if (error_status == 0) fprintf(stderr, "filter failed due to: %s\n", error_str); } free(filter_str); return false; } /** * @brief Load from Configuration document the configuration of the advanced * filter. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for the Adv. filter or in a case of * an error, the function returns False. */ bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_adv_filters_from_json(kshark_ctx, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool kshark_user_mask_to_json(struct kshark_context *kshark_ctx, struct json_object *jobj) { uint8_t mask = kshark_ctx->filter_mask; json_object *jmask; jmask = json_object_new_int((int) mask); if (!jmask) return false; /* Add the mask to the filter config document. */ json_object_object_add(jobj, KS_USER_FILTER_MASK_NAME, jmask); return true; } /** * @brief Record the current value of the the user-specified filter mask into * a Configuration document. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Adv. Filter * Configuration document will be created. * * @returns True on success, otherwise False. */ bool kshark_export_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf) { if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); if (!*conf) return false; switch ((*conf)->format) { case KS_CONFIG_JSON: return kshark_user_mask_to_json(kshark_ctx, (*conf)->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", (*conf)->format); return false; } } static bool kshark_user_mask_from_json(struct kshark_context *kshark_ctx, struct json_object *jobj) { json_object *jmask; uint8_t mask; if (!kshark_json_type_check(jobj, "kshark.config.filter")) return false; /* * Use the name of the filter to find the value of the filter maks. * Notice that the filter config document may contain no data for * the mask. */ if (!json_object_object_get_ex(jobj, KS_USER_FILTER_MASK_NAME, &jmask)) return false; mask = json_object_get_int(jmask); kshark_ctx->filter_mask = mask; return true; } /** * @brief Load from Configuration document the value of the user-specified * filter mask. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a mask has been loaded. If the filter configuration * document contains no data for the mask or in a case of an error, * the function returns False. */ bool kshark_import_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_user_mask_from_json(kshark_ctx, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static bool filter_is_set(struct tracecmd_filter_id *filter) { return filter && filter->count; } /** * @brief Record the current configuration of "show task" and "hide task" * filters into a Json document. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. * * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf) { bool ret = true; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); if (!*conf) return false; /* Save a filter only if it contains Id values. */ if (filter_is_set(kshark_ctx->show_event_filter)) ret &= kshark_export_event_filter(kshark_ctx->pevent, kshark_ctx->show_event_filter, KS_SHOW_EVENT_FILTER_NAME, *conf); if (filter_is_set(kshark_ctx->hide_event_filter)) ret &= kshark_export_event_filter(kshark_ctx->pevent, kshark_ctx->hide_event_filter, KS_HIDE_EVENT_FILTER_NAME, *conf); return ret; } /** * @brief Record the current configuration of "show task" and "hide task" * filters into a Configuration document. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. * * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf) { bool ret = true; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); if (!*conf) return false; /* Save a filter only if it contains Id values. */ if (filter_is_set(kshark_ctx->show_task_filter)) ret &= kshark_export_filter_array(kshark_ctx->show_task_filter, KS_SHOW_TASK_FILTER_NAME, *conf); if (filter_is_set(kshark_ctx->hide_task_filter)) ret &= kshark_export_filter_array(kshark_ctx->hide_task_filter, KS_HIDE_TASK_FILTER_NAME, *conf); return ret; } /** * @brief Record the current configuration of "show cpu" and "hide cpu" * filters into a Configuration document. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. * * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf) { bool ret = true; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); if (!*conf) return false; /* Save a filter only if it contains Id values. */ if (filter_is_set(kshark_ctx->show_task_filter)) ret &= kshark_export_filter_array(kshark_ctx->show_cpu_filter, KS_SHOW_CPU_FILTER_NAME, *conf); if (filter_is_set(kshark_ctx->hide_task_filter)) ret &= kshark_export_filter_array(kshark_ctx->hide_cpu_filter, KS_HIDE_CPU_FILTER_NAME, *conf); return ret; } /** * @brief Load from a Configuration document the configuration of "show event" * and "hide event" filters. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for any event filter or in a case * of an error, the function returns False. */ bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { bool ret = false; ret |= kshark_import_event_filter(kshark_ctx->pevent, kshark_ctx->hide_event_filter, KS_HIDE_EVENT_FILTER_NAME, conf); ret |= kshark_import_event_filter(kshark_ctx->pevent, kshark_ctx->show_event_filter, KS_SHOW_EVENT_FILTER_NAME, conf); return ret; } /** * @brief Load from Configuration document the configuration of "show task" * and "hide task" filters. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for any task filter or in a case of an * error, the function returns False. */ bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { bool ret = false; ret |= kshark_import_filter_array(kshark_ctx->hide_task_filter, KS_HIDE_TASK_FILTER_NAME, conf); ret |= kshark_import_filter_array(kshark_ctx->show_task_filter, KS_SHOW_TASK_FILTER_NAME, conf); return ret; } /** * @brief Load from Configuration document the configuration of "show cpu" * and "hide cpu" filters. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for any cpu filter or in a case of an * error, the function returns False. */ bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { bool ret = false; ret |= kshark_import_filter_array(kshark_ctx->hide_cpu_filter, KS_HIDE_CPU_FILTER_NAME, conf); ret |= kshark_import_filter_array(kshark_ctx->show_cpu_filter, KS_SHOW_CPU_FILTER_NAME, conf); return ret; } /** * @brief Create a Filter Configuration document containing the current * configuration of all filters. * * @param kshark_ctx: Input location for session context pointer. * @param format: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns kshark_config_doc instance on success, otherwise NULL. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * kshark_export_all_filters(struct kshark_context *kshark_ctx, enum kshark_config_formats format) { /* Create a new Configuration document. */ struct kshark_config_doc *conf = kshark_filter_config_new(format); /* Save a filter only if it contains Id values. */ if (!conf || !kshark_export_all_event_filters(kshark_ctx, &conf) || !kshark_export_all_task_filters(kshark_ctx, &conf) || !kshark_export_all_cpu_filters(kshark_ctx, &conf) || !kshark_export_user_mask(kshark_ctx, &conf) || !kshark_export_adv_filters(kshark_ctx, &conf)) { kshark_free_config_doc(conf); return NULL; } return conf; } /** * @brief Load from a Configuration document the configuration of all filters. * * @param kshark_ctx: Input location for session context pointer. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True, if a filter has been loaded. If the filter configuration * document contains no data for any filter or in a case of an error, * the function returns False. */ bool kshark_import_all_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf) { bool ret; ret = kshark_import_all_task_filters(kshark_ctx, conf); ret |= kshark_import_all_cpu_filters(kshark_ctx, conf); ret |= kshark_import_all_event_filters(kshark_ctx, conf); ret |= kshark_import_user_mask(kshark_ctx, conf); ret |= kshark_import_adv_filters(kshark_ctx, conf); return ret; } static bool kshark_save_json_file(const char *file_name, struct json_object *jobj) { int flags; /* Save the file in a human-readable form. */ flags = JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY; if (json_object_to_file_ext(file_name, jobj, flags) == 0) return true; return false; } /** * @brief Save a Configuration document into a file. * * @param file_name: The name of the file. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True on success, otherwise False. */ bool kshark_save_config_file(const char *file_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: return kshark_save_json_file(file_name, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", conf->format); return false; } } static struct json_object *kshark_open_json_file(const char *file_name, const char *type) { struct json_object *jobj, *var; const char *type_var; jobj = json_object_from_file(file_name); if (!jobj) return NULL; /* Get the type of the document. */ if (!json_object_object_get_ex(jobj, "type", &var)) goto fail; type_var = json_object_get_string(var); if (strcmp(type, type_var) != 0) goto fail; return jobj; fail: /* The document has a wrong type. */ fprintf(stderr, "Failed to open Json file %s.\n", file_name); fprintf(stderr, "The document has a wrong type.\n"); json_object_put(jobj); return NULL; } static const char *get_ext(const char *filename) { const char *dot = strrchr(filename, '.'); if(!dot) return "unknown"; return dot + 1; } /** * @brief Open for read a Configuration file and check if it has the * expected type. * * @param file_name: The name of the file. Currently only Json files are * supported. * @param type: String describing the expected type of the document, * e.g. "kshark.config.record" or "kshark.config.filter". * * @returns kshark_config_doc instance on success, or NULL on failure. Use * kshark_free_config_doc() to free the object. */ struct kshark_config_doc *kshark_open_config_file(const char *file_name, const char *type) { struct kshark_config_doc *conf = NULL; if (strcmp(get_ext(file_name), "json") == 0) { struct json_object *jobj = kshark_open_json_file(file_name, type); if (jobj) { conf = malloc(sizeof(*conf)); conf->conf_doc = jobj; conf->format = KS_CONFIG_JSON; } } return conf; } trace-cmd-2.8.3/kernel-shark/src/libkshark-model.c000066400000000000000000001050721351617527000217700ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-model.c * @brief Visualization model for FTRACE (trace-cmd) data. */ // C #include #include // KernelShark #include "libkshark-model.h" /** The index of the Upper Overflow bin. */ #define UOB(histo) (histo->n_bins) /** The index of the Lower Overflow bin. */ #define LOB(histo) (histo->n_bins + 1) /** For all bins. */ # define ALLB(histo) LOB(histo) /** * @brief Initialize the Visualization model. * * @param histo: Input location for the model descriptor. */ void ksmodel_init(struct kshark_trace_histo *histo) { /* * Initialize an empty histo. The histo will have no bins and will * contain no data. */ histo->bin_size = 0; histo->min = 0; histo->max = 0; histo->n_bins = 0; histo->bin_count = NULL; histo->map = NULL; } /** * @brief Clear (reset) the Visualization model. * * @param histo: Input location for the model descriptor. */ void ksmodel_clear(struct kshark_trace_histo *histo) { /* Reset the histo. It will have no bins and will contain no data. */ free(histo->map); free(histo->bin_count); ksmodel_init(histo); } static void ksmodel_reset_bins(struct kshark_trace_histo *histo, size_t first, size_t last) { /* * Reset the content of the bins. * Be careful here! Resetting the entire array of signed integers with * memset() will work only for values of "0" and "-1". Hence * KS_EMPTY_BIN is expected to be "-1". */ memset(&histo->map[first], KS_EMPTY_BIN, (last - first + 1) * sizeof(histo->map[0])); memset(&histo->bin_count[first], 0, (last - first + 1) * sizeof(histo->bin_count[0])); } static bool ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n) { free(histo->bin_count); free(histo->map); /* Create bins. Two overflow bins are added. */ histo->map = calloc(n + 2, sizeof(*histo->map)); histo->bin_count = calloc(n + 2, sizeof(*histo->bin_count)); if (!histo->map || !histo->bin_count) { ksmodel_clear(histo); fprintf(stderr, "Failed to allocate memory for a histo.\n"); return false; } histo->n_bins = n; return true; } static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo, size_t n, uint64_t min, uint64_t max, bool force_in_range) { uint64_t corrected_range, delta_range, range = max - min; struct kshark_entry *last; /* The size of the bin must be >= 1, hence the range must be >= n. */ if (n == 0 || range < n) { range = n; max = min + n; } /* * If the number of bins changes, allocate memory for the descriptor of * the model. */ if (n != histo->n_bins) { if (!ksmodel_histo_alloc(histo, n)) { ksmodel_clear(histo); return; } } /* Reset the content of all bins (including overflow bins) to zero. */ ksmodel_reset_bins(histo, 0, ALLB(histo)); if (range % n == 0) { /* * The range is multiple of the number of bin and needs no * adjustment. This is very unlikely to happen but still ... */ histo->min = min; histo->max = max; histo->bin_size = range / n; } else { /* * The range needs adjustment. The new range will be slightly * bigger, compared to the requested one. */ histo->bin_size = range / n + 1; corrected_range = histo->bin_size * n; delta_range = corrected_range - range; histo->min = min - delta_range / 2; histo->max = histo->min + corrected_range; if (!force_in_range) return; /* * Make sure that the new range doesn't go outside of the time * interval of the dataset. */ last = histo->data[histo->data_size - 1]; if (histo->min < histo->data[0]->ts) { histo->min = histo->data[0]->ts; histo->max = histo->min + corrected_range; } else if (histo->max > last->ts) { histo->max = last->ts; histo->min = histo->max - corrected_range; } } } /** * @brief Prepare the bining of the Visualization model. * * @param histo: Input location for the model descriptor. * @param n: Number of bins. * @param min: Lower edge of the time-window to be visualized. * @param max: Upper edge of the time-window to be visualized. */ void ksmodel_set_bining(struct kshark_trace_histo *histo, size_t n, uint64_t min, uint64_t max) { ksmodel_set_in_range_bining(histo, n, min, max, false); } static size_t ksmodel_set_lower_edge(struct kshark_trace_histo *histo) { /* * Find the index of the first entry inside the range * (timestamp >= min). Note that the value of "min" is considered * inside the range. */ ssize_t row = kshark_find_entry_by_time(histo->min, histo->data, 0, histo->data_size - 1); assert(row != BSEARCH_ALL_SMALLER); if (row == BSEARCH_ALL_GREATER || row == 0) { /* Lower Overflow bin is empty. */ histo->map[LOB(histo)] = KS_EMPTY_BIN; histo->bin_count[LOB(histo)] = 0; row = 0; } else { /* * The first entry inside the range is not the first entry of * the dataset. This means that the Lower Overflow bin contains * data. */ /* Lower Overflow bin starts at "0". */ histo->map[LOB(histo)] = 0; /* * The number of entries inside the Lower Overflow bin is equal * to the index of the first entry inside the range. */ histo->bin_count[LOB(histo)] = row; } /* * Now check if the first entry inside the range falls into the first * bin. */ if (histo->data[row]->ts < histo->min + histo->bin_size) { /* * It is inside the first bin. Set the beginning * of the first bin. */ histo->map[0] = row; } else { /* The first bin is empty. */ histo->map[0] = KS_EMPTY_BIN; } return row; } static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo) { /* * Find the index of the first entry outside the range * (timestamp > max). Note that the value of "max" is considered inside * the range. Remember that kshark_find_entry_by_time returns the first * entry which is equal or greater than the reference time. */ ssize_t row = kshark_find_entry_by_time(histo->max + 1, histo->data, 0, histo->data_size - 1); assert(row != BSEARCH_ALL_GREATER); if (row == BSEARCH_ALL_SMALLER) { /* Upper Overflow bin is empty. */ histo->map[UOB(histo)] = KS_EMPTY_BIN; histo->bin_count[UOB(histo)] = 0; } else { /* * The Upper Overflow bin contains data. Set its beginning and * the number of entries. */ histo->map[UOB(histo)] = row; histo->bin_count[UOB(histo)] = histo->data_size - row; } return row; } static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo, size_t bin, size_t last_row) { size_t time, next_bin = bin + 1; ssize_t row; /* Calculate the beginning of the next bin. */ time = histo->min + next_bin * histo->bin_size; /* * Find the index of the first entry inside * the next bin (timestamp > time). */ row = kshark_find_entry_by_time(time, histo->data, last_row, histo->data_size - 1); if (row < 0 || histo->data[row]->ts >= time + histo->bin_size) { /* The bin is empty. */ histo->map[next_bin] = KS_EMPTY_BIN; return; } /* Set the index of the first entry. */ histo->map[next_bin] = row; } /* * Fill in the bin_count array, which maps the number of entries within each * bin. */ static void ksmodel_set_bin_counts(struct kshark_trace_histo *histo) { int i = 0, prev_not_empty; ssize_t count_tmp = 0; histo->tot_count = 0; memset(&histo->bin_count[0], 0, (histo->n_bins) * sizeof(histo->bin_count[0])); /* * Find the first bin which contains data. Start by checking the Lower * Overflow bin. */ if (histo->map[LOB(histo)] != KS_EMPTY_BIN) { prev_not_empty = LOB(histo); } else { /* Loop till the first non-empty bin. */ while (histo->map[i] < 0 && i < histo->n_bins) { ++i; } prev_not_empty = i++; } /* * Starting from the first not empty bin, loop over all bins and fill * in the bin_count array to hold the number of entries in each bin. */ for (; i < histo->n_bins; ++i) { if (histo->map[i] != KS_EMPTY_BIN) { /* * The current bin is not empty, take its data row and * subtract it from the data row of the previous not * empty bin, which will give us the number of data * rows in the "prev_not_empty" bin. */ count_tmp = histo->map[i] - histo->map[prev_not_empty]; /* * We will do a sanity check. The number of data rows * in the previous not empty bin must be greater than * zero. */ assert(count_tmp > 0); histo->bin_count[prev_not_empty] = count_tmp; if (prev_not_empty != LOB(histo)) histo->tot_count += count_tmp; prev_not_empty = i; } } /* Check if the Upper Overflow bin contains data. */ if (histo->map[UOB(histo)] == KS_EMPTY_BIN) { /* * The Upper Overflow bin is empty. Use the size of the dataset * to calculate the content of the previouse not empty bin. */ count_tmp = histo->data_size - histo->map[prev_not_empty]; } else { /* * Use the index of the first entry inside the Upper Overflow * bin to calculate the content of the previouse not empty * bin. */ count_tmp = histo->map[UOB(histo)] - histo->map[prev_not_empty]; } /* * We will do a sanity check. The number of data rows in the last not * empty bin must be greater than zero. */ assert(count_tmp >= 0); histo->tot_count += histo->bin_count[prev_not_empty] = count_tmp; } /** * @brief Provide the Visualization model with data. Calculate the current * state of the model. * * @param histo: Input location for the model descriptor. * @param data: Input location for the trace data. * @param n: Number of bins. */ void ksmodel_fill(struct kshark_trace_histo *histo, struct kshark_entry **data, size_t n) { size_t last_row = 0; int bin; histo->data_size = n; histo->data = data; if (histo->n_bins == 0 || histo->bin_size == 0 || histo->data_size == 0) { /* * Something is wrong with this histo. * Most likely the binning is not set. */ ksmodel_clear(histo); fprintf(stderr, "Unable to fill the model with data.\n"); fprintf(stderr, "Try to set the bining of the model first.\n"); return; } /* Set the Lower Overflow bin */ ksmodel_set_lower_edge(histo); /* * Loop over the dataset and set the beginning of all individual bins. */ for (bin = 0; bin < histo->n_bins; ++bin) { ksmodel_set_next_bin_edge(histo, bin, last_row); if (histo->map[bin + 1] > 0) last_row = histo->map[bin + 1]; } /* Set the Upper Overflow bin. */ ksmodel_set_upper_edge(histo); /* Calculate the number of entries in each bin. */ ksmodel_set_bin_counts(histo); } /** * @brief Get the total number of entries in a given bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * * @returns The number of entries in this bin. */ size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin) { if (bin >= 0 && bin < histo->n_bins) return histo->bin_count[bin]; if (bin == UPPER_OVERFLOW_BIN) return histo->bin_count[UOB(histo)]; if (bin == LOWER_OVERFLOW_BIN) return histo->bin_count[LOB(histo)]; return 0; } /** * @brief Shift the time-window of the model forward. Recalculate the current * state of the model. * * @param histo: Input location for the model descriptor. * @param n: Number of bins to shift. */ void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n) { size_t last_row = 0; int bin; if (!histo->data_size) return; if (histo->map[UOB(histo)] == KS_EMPTY_BIN) { /* * The Upper Overflow bin is empty. This means that we are at * the upper edge of the dataset already. Do nothing in this * case. */ return; } histo->min += n * histo->bin_size; histo->max += n * histo->bin_size; if (n >= histo->n_bins) { /* * No overlap between the new and the old ranges. Recalculate * all bins from scratch. First calculate the new range. */ ksmodel_set_bining(histo, histo->n_bins, histo->min, histo->max); ksmodel_fill(histo, histo->data, histo->data_size); return; } /* Set the new Lower Overflow bin. */ ksmodel_set_lower_edge(histo); /* * Copy the the mapping indexes of all overlaping bins starting from * bin "0" of the new histo. Note that the number of overlaping bins * is histo->n_bins - n. * We will do a sanity check. ksmodel_set_lower_edge() sets map[0] * index of the new histo. This index should then be equal to map[n] * index of the old histo. */ assert (histo->map[0] == histo->map[n]); memmove(&histo->map[0], &histo->map[n], sizeof(histo->map[0]) * (histo->n_bins - n)); /* * Calculate only the content of the new (non-overlapping) bins. * Start from the last copied bin and set the edge of each consecutive * bin. */ bin = histo->n_bins - n - 1; for (; bin < histo->n_bins - 1; ++bin) { /* * Note that this function will set the bin having index * "bin + 1". */ ksmodel_set_next_bin_edge(histo, bin, last_row); if (histo->map[bin + 1] > 0) last_row = histo->map[bin + 1]; } /* * Set the new Upper Overflow bin and calculate the number of entries * in each bin. */ ksmodel_set_upper_edge(histo); ksmodel_set_bin_counts(histo); } /** * @brief Shift the time-window of the model backward. Recalculate the current * state of the model. * * @param histo: Input location for the model descriptor. * @param n: Number of bins to shift. */ void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n) { size_t last_row = 0; int bin; if (!histo->data_size) return; if (histo->map[LOB(histo)] == KS_EMPTY_BIN) { /* * The Lower Overflow bin is empty. This means that we are at * the Lower edge of the dataset already. Do nothing in this * case. */ return; } histo->min -= n * histo->bin_size; histo->max -= n * histo->bin_size; if (n >= histo->n_bins) { /* * No overlap between the new and the old range. Recalculate * all bins from scratch. First calculate the new range. */ ksmodel_set_bining(histo, histo->n_bins, histo->min, histo->max); ksmodel_fill(histo, histo->data, histo->data_size); return; } /* * Copy the mapping indexes of all overlaping bins starting from * bin "0" of the old histo. Note that the number of overlaping bins * is histo->n_bins - n. */ memmove(&histo->map[n], &histo->map[0], sizeof(histo->map[0]) * (histo->n_bins - n)); /* Set the new Lower Overflow bin. */ ksmodel_set_lower_edge(histo); /* Calculate only the content of the new (non-overlapping) bins. */ for (bin = 0; bin < n - 1; ++bin) { /* * Note that this function will set the bin having index * "bin + 1". */ ksmodel_set_next_bin_edge(histo, bin, last_row); if (histo->map[bin + 1] > 0) last_row = histo->map[bin + 1]; } /* * Set the new Upper Overflow bin and calculate the number of entries * in each bin. */ ksmodel_set_upper_edge(histo); ksmodel_set_bin_counts(histo); } /** * @brief Move the time-window of the model to a given location. Recalculate * the current state of the model. * * @param histo: Input location for the model descriptor. * @param ts: position in time to be visualized. */ void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts) { size_t min, max, range_min; if (ts > histo->min && ts < histo->max) { /* * The new position is already inside the range. * Do nothing in this case. */ return; } /* * Calculate the new range without changing the size and the number * of bins. */ min = ts - histo->n_bins * histo->bin_size / 2; /* Make sure that the range does not go outside of the dataset. */ if (min < histo->data[0]->ts) { min = histo->data[0]->ts; } else { range_min = histo->data[histo->data_size - 1]->ts - histo->n_bins * histo->bin_size; if (min > range_min) min = range_min; } max = min + histo->n_bins * histo->bin_size; /* Use the new range to recalculate all bins from scratch. */ ksmodel_set_bining(histo, histo->n_bins, min, max); ksmodel_fill(histo, histo->data, histo->data_size); } static void ksmodel_zoom(struct kshark_trace_histo *histo, double r, int mark, bool zoom_in) { size_t range, min, max, delta_min; double delta_tot; if (!histo->data_size) return; /* * If the marker is not set, assume that the focal point of the zoom * is the center of the range. */ if (mark < 0) mark = histo->n_bins / 2; range = histo->max - histo->min; /* * Avoid overzooming. If needed, adjust the Scale factor to a the value * which provides bin_size >= 5. */ if (zoom_in && (size_t) (range * (1. - r)) < histo->n_bins * 5) r = 1. - (histo->n_bins * 5.) / range; /* * Now calculate the new range of the histo. Use the bin of the marker * as a focal point for the zoomout. With this the maker will stay * inside the same bin in the new histo. * * First we set delta_tot to increase by the percentage requested (r). * Then we make delta_min equal to a percentage of delta_tot based on * where the position of the mark is. After this we add / subtract the * original min by the delta_min and subtract / add the max by * delta_tot - delta_min. */ delta_tot = range * r; if (mark == (int)histo->n_bins - 1) delta_min = delta_tot; else delta_min = delta_tot * mark / histo->n_bins; min = zoom_in ? histo->min + delta_min : histo->min - delta_min; max = zoom_in ? histo->max - (size_t) delta_tot + delta_min : histo->max + (size_t) delta_tot - delta_min; /* Make sure the new range doesn't go outside of the dataset. */ if (min < histo->data[0]->ts) min = histo->data[0]->ts; if (max > histo->data[histo->data_size - 1]->ts) max = histo->data[histo->data_size - 1]->ts; /* * Use the new range to recalculate all bins from scratch. Enforce * "In Range" adjustment of the range of the model, in order to avoid * slowly drifting outside of the data-set in the case when the very * first or the very last entry is used as a focal point. */ ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true); ksmodel_fill(histo, histo->data, histo->data_size); } /** * @brief Extend the time-window of the model. Recalculate the current state * of the model. * * @param histo: Input location for the model descriptor. * @param r: Scale factor of the zoom-out. * @param mark: Focus point of the zoom-out. */ void ksmodel_zoom_out(struct kshark_trace_histo *histo, double r, int mark) { ksmodel_zoom(histo, r, mark, false); } /** * @brief Shrink the time-window of the model. Recalculate the current state * of the model. * * @param histo: Input location for the model descriptor. * @param r: Scale factor of the zoom-in. * @param mark: Focus point of the zoom-in. */ void ksmodel_zoom_in(struct kshark_trace_histo *histo, double r, int mark) { ksmodel_zoom(histo, r, mark, true); } /** * @brief Get the index of the first entry in a given bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * * @returns Index of the first entry in this bin. If the bin is empty the * function returns negative error identifier (KS_EMPTY_BIN). */ ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin) { if (bin >= 0 && bin < (int) histo->n_bins) return histo->map[bin]; if (bin == UPPER_OVERFLOW_BIN) return histo->map[UOB(histo)]; if (bin == LOWER_OVERFLOW_BIN) return histo->map[LOB(histo)]; return KS_EMPTY_BIN; } /** * @brief Get the index of the last entry in a given bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * * @returns Index of the last entry in this bin. If the bin is empty the * function returns negative error identifier (KS_EMPTY_BIN). */ ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin) { ssize_t index = ksmodel_first_index_at_bin(histo, bin); size_t count = ksmodel_bin_count(histo, bin); if (index >= 0 && count) index += count - 1; return index; } static bool ksmodel_is_visible(struct kshark_entry *e) { if ((e->visible & KS_GRAPH_VIEW_FILTER_MASK) && (e->visible & KS_EVENT_VIEW_FILTER_MASK)) return true; return false; } static struct kshark_entry_request * ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val) { size_t first, n; /* Get the number of entries in this bin. */ n = ksmodel_bin_count(histo, bin); if (!n) return NULL; first = ksmodel_first_index_at_bin(histo, bin); return kshark_entry_request_alloc(first, n, func, val, vis_only, KS_GRAPH_VIEW_FILTER_MASK); } static struct kshark_entry_request * ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val) { size_t first, n; /* Get the number of entries in this bin. */ n = ksmodel_bin_count(histo, bin); if (!n) return NULL; first = ksmodel_last_index_at_bin(histo, bin); return kshark_entry_request_alloc(first, n, func, val, vis_only, KS_GRAPH_VIEW_FILTER_MASK); } /** * @brief Get the index of the first entry from a given Cpu in a given bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param cpu: Cpu Id. * * @returns Index of the first entry from a given Cpu in this bin. */ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, int bin, int cpu) { size_t i, n, first, not_found = KS_EMPTY_BIN; n = ksmodel_bin_count(histo, bin); if (!n) return not_found; first = ksmodel_first_index_at_bin(histo, bin); for (i = first; i < first + n; ++i) { if (histo->data[i]->cpu == cpu) { if (ksmodel_is_visible(histo->data[i])) return i; else not_found = KS_FILTERED_BIN; } } return not_found; } /** * @brief Get the index of the first entry from a given Task in a given bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param pid: Process Id of a task. * * @returns Index of the first entry from a given Task in this bin. */ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, int bin, int pid) { size_t i, n, first, not_found = KS_EMPTY_BIN; n = ksmodel_bin_count(histo, bin); if (!n) return not_found; first = ksmodel_first_index_at_bin(histo, bin); for (i = first; i < first + n; ++i) { if (histo->data[i]->pid == pid) { if (ksmodel_is_visible(histo->data[i])) return i; else not_found = KS_FILTERED_BIN; } } return not_found; } /** * @brief In a given bin, start from the front end of the bin and go towards * the back end, searching for an entry satisfying the Matching * condition defined by a Matching condition function. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param vis_only: If true, a visible entry is requested. * @param func: Matching condition function. * @param val: Matching condition value, used by the Matching condition * function. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. */ const struct kshark_entry * ksmodel_get_entry_front(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val, struct kshark_entry_collection *col, ssize_t *index) { struct kshark_entry_request *req; const struct kshark_entry *entry; if (index) *index = KS_EMPTY_BIN; /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, vis_only, func, val); if (!req) return NULL; if (col && col->size) entry = kshark_get_collection_entry_front(&req, histo->data, col, index); else entry = kshark_get_entry_front(req, histo->data, index); kshark_free_entry_request(req); return entry; } /** * @brief In a given bin, start from the back end of the bin and go towards * the front end, searching for an entry satisfying the Matching * condition defined by a Matching condition function. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param vis_only: If true, a visible entry is requested. * @param func: Matching condition function. * @param val: Matching condition value, used by the Matching condition * function. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. */ const struct kshark_entry * ksmodel_get_entry_back(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val, struct kshark_entry_collection *col, ssize_t *index) { struct kshark_entry_request *req; const struct kshark_entry *entry; if (index) *index = KS_EMPTY_BIN; /* Set the position at the end of the bin and go backwards. */ req = ksmodel_entry_back_request_alloc(histo, bin, vis_only, func, val); if (!req) return NULL; if (col && col->size) entry = kshark_get_collection_entry_back(&req, histo->data, col, index); else entry = kshark_get_entry_back(req, histo->data, index); kshark_free_entry_request(req); return entry; } static int ksmodel_get_entry_pid(const struct kshark_entry *entry) { if (!entry) { /* No data has been found. */ return KS_EMPTY_BIN; } /* * Note that if some data has been found, but this data is * filtered-outa, the Dummy entry is returned. The PID of the Dummy * entry is KS_FILTERED_BIN. */ return entry->pid; } /** * @brief In a given bin, start from the front end of the bin and go towards * the back end, searching for an entry from a given CPU. Return * the Process Id of the task of the entry found. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param cpu: CPU Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Process Id of the task if an entry has been found. Else a negative * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_pid_front(struct kshark_trace_histo *histo, int bin, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry; if (cpu < 0) return KS_EMPTY_BIN; entry = ksmodel_get_entry_front(histo, bin, vis_only, kshark_match_cpu, cpu, col, index); return ksmodel_get_entry_pid(entry); } /** * @brief In a given bin, start from the back end of the bin and go towards * the front end, searching for an entry from a given CPU. Return * the Process Id of the task of the entry found. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param cpu: CPU Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Process Id of the task if an entry has been found. Else a negative * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_pid_back(struct kshark_trace_histo *histo, int bin, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry; if (cpu < 0) return KS_EMPTY_BIN; entry = ksmodel_get_entry_back(histo, bin, vis_only, kshark_match_cpu, cpu, col, index); return ksmodel_get_entry_pid(entry); } static int ksmodel_get_entry_cpu(const struct kshark_entry *entry) { if (!entry) { /* No data has been found. */ return KS_EMPTY_BIN; } /* * Note that if some data has been found, but this data is * filtered-outa, the Dummy entry is returned. The CPU Id of the Dummy * entry is KS_FILTERED_BIN. */ return entry->cpu; } /** * @brief In a given bin, start from the front end of the bin and go towards * the back end, searching for an entry from a given PID. Return * the CPU Id of the entry found. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param pid: Process Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns The CPU Id of the task if an entry has been found. Else a negative * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, int bin, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry; if (pid < 0) return KS_EMPTY_BIN; entry = ksmodel_get_entry_front(histo, bin, vis_only, kshark_match_pid, pid, col, index); return ksmodel_get_entry_cpu(entry); } /** * @brief In a given bin, start from the back end of the bin and go towards * the front end, searching for an entry from a given PID. Return * the CPU Id of the entry found. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param pid: Process Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns The CPU Id of the task if an entry has been found. Else a negative * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, int bin, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { const struct kshark_entry *entry; if (pid < 0) return KS_EMPTY_BIN; entry = ksmodel_get_entry_back(histo, bin, vis_only, kshark_match_pid, pid, col, index); return ksmodel_get_entry_cpu(entry); } /** * @brief Check if a visible trace event from a given Cpu exists in this bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param cpu: Cpu Id. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns True, if a visible entry exists in this bin. Else false. */ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, int bin, int cpu, struct kshark_entry_collection *col, ssize_t *index) { struct kshark_entry_request *req; const struct kshark_entry *entry; if (index) *index = KS_EMPTY_BIN; /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, true, kshark_match_cpu, cpu); if (!req) return false; /* * The default visibility mask of the Model Data request is * KS_GRAPH_VIEW_FILTER_MASK. Change the mask to * KS_EVENT_VIEW_FILTER_MASK because we want to find a visible event. */ req->vis_mask = KS_EVENT_VIEW_FILTER_MASK; if (col && col->size) entry = kshark_get_collection_entry_front(&req, histo->data, col, index); else entry = kshark_get_entry_front(req, histo->data, index); kshark_free_entry_request(req); if (!entry || !entry->visible) { /* No visible entry has been found. */ return false; } return true; } /** * @brief Check if a visible trace event from a given Task exists in this bin. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param pid: Process Id of the task. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns True, if a visible entry exists in this bin. Else false. */ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, int bin, int pid, struct kshark_entry_collection *col, ssize_t *index) { struct kshark_entry_request *req; const struct kshark_entry *entry; if (index) *index = KS_EMPTY_BIN; /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, true, kshark_match_pid, pid); if (!req) return false; /* * The default visibility mask of the Model Data request is * KS_GRAPH_VIEW_FILTER_MASK. Change the mask to * KS_EVENT_VIEW_FILTER_MASK because we want to find a visible event. */ req->vis_mask = KS_EVENT_VIEW_FILTER_MASK; if (col && col->size) entry = kshark_get_collection_entry_front(&req, histo->data, col, index); else entry = kshark_get_entry_front(req, histo->data, index); kshark_free_entry_request(req); if (!entry || !entry->visible) { /* No visible entry has been found. */ return false; } return true; } static bool match_cpu_missed_events(struct kshark_context *kshark_ctx, struct kshark_entry *e, int cpu) { return e->event_id == -EOVERFLOW && e->cpu == cpu; } static bool match_pid_missed_events(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { return e->event_id == -EOVERFLOW && e->pid == pid; } /** * @brief In a given CPU and bin, start from the front end of the bin and go towards * the back end, searching for a Missed Events entry. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param cpu: CPU Id. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. */ const struct kshark_entry * ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, int bin, int cpu, struct kshark_entry_collection *col, ssize_t *index) { return ksmodel_get_entry_front(histo, bin, true, match_cpu_missed_events, cpu, col, index); } /** * @brief In a given task and bin, start from the front end of the bin and go towards * the back end, searching for a Missed Events entry. * * @param histo: Input location for the model descriptor. * @param bin: Bin id. * @param pid: Process Id of the task. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. * * @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL. */ const struct kshark_entry * ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, int bin, int pid, struct kshark_entry_collection *col, ssize_t *index) { return ksmodel_get_entry_front(histo, bin, true, match_pid_missed_events, pid, col, index); } trace-cmd-2.8.3/kernel-shark/src/libkshark-model.h000066400000000000000000000115471351617527000220000ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-model.h * @brief Visualization model for FTRACE (trace-cmd) data. */ #ifndef _LIB_KSHARK_MODEL_H #define _LIB_KSHARK_MODEL_H // KernelShark #include "libkshark.h" #ifdef __cplusplus extern "C" { #endif // __cplusplus /** * Overflow Bin identifiers. The two overflow bins are used to hold the data * outside the visualized range. */ enum OverflowBin { /** * Identifier of the Upper Overflow Bin. This bin is used to hold the * data after (in time) the end of the visualized range. */ UPPER_OVERFLOW_BIN = -1, /** * Identifier of the Lower Overflow Bin. This bin is used to hold the * data before (in time) the beginning of the visualized range. */ LOWER_OVERFLOW_BIN = -2, }; /** Structure describing the current state of the visualization model. */ struct kshark_trace_histo { /** Trace data array. */ struct kshark_entry **data; /** The size of the data array. */ size_t data_size; /** The first entry (index of data array) in each bin. */ ssize_t *map; /** Number of entries in each bin. */ size_t *bin_count; /** Total number of entries in all bin except the overflow bins. */ int tot_count; /** * Lower edge of the time-window to be visualized. Only entries having * timestamp >= min will be visualized. */ uint64_t min; /** * Upper edge of the time-window to be visualized. Only entries having * timestamp <= max will be visualized. */ uint64_t max; /** The size in time for each bin. */ uint64_t bin_size; /** Number of bins. */ int n_bins; }; void ksmodel_init(struct kshark_trace_histo *histo); void ksmodel_clear(struct kshark_trace_histo *histo); void ksmodel_set_bining(struct kshark_trace_histo *histo, size_t n, uint64_t min, uint64_t max); void ksmodel_fill(struct kshark_trace_histo *histo, struct kshark_entry **data, size_t n); size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin); void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n); void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n); void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts); void ksmodel_zoom_out(struct kshark_trace_histo *histo, double r, int mark); void ksmodel_zoom_in(struct kshark_trace_histo *histo, double r, int mark); ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin); ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin); ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, int bin, int cpu); ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, int bin, int pid); const struct kshark_entry * ksmodel_get_entry_front(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_entry_back(struct kshark_trace_histo *histo, int bin, bool vis_only, matching_condition_func func, int val, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_pid_front(struct kshark_trace_histo *histo, int bin, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_pid_back(struct kshark_trace_histo *histo, int bin, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, int bin, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, int bin, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, int bin, int cpu, struct kshark_entry_collection *col, ssize_t *index); bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, int bin, int pid, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, int bin, int cpu, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, int bin, int pid, struct kshark_entry_collection *col, ssize_t *index); static inline double ksmodel_bin_time(struct kshark_trace_histo *histo, int bin) { return (histo->min + bin*histo->bin_size) * 1e-9; } static inline uint64_t ksmodel_bin_ts(struct kshark_trace_histo *histo, int bin) { return (histo->min + bin*histo->bin_size); } #ifdef __cplusplus } #endif // __cplusplus #endif trace-cmd-2.8.3/kernel-shark/src/libkshark-plot.c000066400000000000000000000116371351617527000216510ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-plot.c * @brief Basic tools for OpenGL plotting. */ // OpenGL #include #include // KernelShark #include "libkshark-plot.h" /** * @brief Create an empty scene for drawing. * * @param width: Width of the screen window in pixels. * @param height: Height of the screen window in pixels. */ void ksplot_make_scene(int width, int height) { /* Set Display mode. */ glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); /* Prevent the program from exiting when a window is closed. */ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS); /* Set window size. */ glutInitWindowSize(width, height); /* Set window position on screen. */ glutInitWindowPosition(50, 50); /* Open the screen window. */ glutCreateWindow("KernelShark Plot"); /* * Set the origin of the coordinate system to be the top left corner. * The "Y" coordinate is inverted. */ gluOrtho2D(0, width, height, 0); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); } /** * @brief Initialize OpenGL. * * @param dpr: Device Pixel Ratio. */ void ksplot_init_opengl(int dpr) { glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glDisable(GL_COLOR_MATERIAL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_POLYGON_SMOOTH); glLineWidth(1.5 * dpr); glPointSize(2.5 * dpr); glClearColor(1, 1, 1, 1); } /** * @brief To be called whenever the OpenGL window has been resized. * * @param width: Width of the screen window in pixels. * @param height: Height of the screen window in pixels. */ void ksplot_resize_opengl(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); /* * Set the origin of the coordinate system to be the top left corner. * The "Y" coordinate is inverted. */ gluOrtho2D(0, width, height, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /** * @brief Draw a point. * * @param p: Input location for the point object. * @param col: The color of the point. * @param size: The size of the point. */ void ksplot_draw_point(const struct ksplot_point *p, const struct ksplot_color *col, float size) { if (!p || !col || size < .5f) return; glPointSize(size); glBegin(GL_POINTS); glColor3ub(col->red, col->green, col->blue); glVertex2i(p->x, p->y); glEnd(); } /** * @brief Draw a line. * * @param a: Input location for the first finishing point of the line. * @param b: Input location for the second finishing point of the line. * @param col: The color of the line. * @param size: The size of the line. */ void ksplot_draw_line(const struct ksplot_point *a, const struct ksplot_point *b, const struct ksplot_color *col, float size) { if (!a || !b || !col || size < .5f) return; glLineWidth(size); glBegin(GL_LINES); glColor3ub(col->red, col->green, col->blue); glVertex2i(a->x, a->y); glVertex2i(b->x, b->y); glEnd(); } /** * @brief Draw a polygon. * * @param points: Input location for the array of points defining the polygon. * @param n_points: The size of the array of points. * @param col: The color of the polygon. * @param size: The size of the polygon. */ void ksplot_draw_polygon(const struct ksplot_point *points, size_t n_points, const struct ksplot_color *col, float size) { if (!points || !n_points || !col || size < .5f) return; if (n_points == 1) { ksplot_draw_point(points, col, size); return; } if (n_points == 2) { ksplot_draw_line(points, points + 1, col, size); return; } /* Obtain a point inside the surface of the polygon. */ struct ksplot_point in_point; in_point.x = (points[0].x + points[2].x) / 2; in_point.y = (points[0].y + points[2].y) / 2; /* * Draw a Triangle Fan using the internal point as a central * vertex. */ glBegin(GL_TRIANGLE_FAN); glColor3ub(col->red, col->green, col->blue); glVertex2i(in_point.x, in_point.y); for (size_t i = 0; i < n_points; ++i) glVertex2i(points[i].x, points[i].y); glVertex2i(points[0].x, points[0].y); glEnd(); } /** * @brief Draw the contour of a polygon. * * @param points: Input location for the array of points defining the polygon. * @param n_points: The size of the array of points. * @param col: The color of the polygon. * @param size: The size of the polygon. */ void ksplot_draw_polygon_contour(const struct ksplot_point *points, size_t n_points, const struct ksplot_color *col, float size) { if (!points || !n_points || !col || size < .5f) return; /* Loop over the points of the polygon and draw connecting lines. */ for(size_t i = 1; i < n_points; ++i) ksplot_draw_line(&points[i - 1], &points[i], col, size); /* Close the contour. */ ksplot_draw_line(&points[0], &points[n_points - 1], col, size); } trace-cmd-2.8.3/kernel-shark/src/libkshark-plot.h000066400000000000000000000026611351617527000216530ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-plot.h * @brief Basic tools for OpenGL plotting. */ #ifndef _LIB_KSHARK_PLOT_H #define _LIB_KSHARK_PLOT_H #ifdef __cplusplus extern "C" { #endif /** Structure defining a RGB color. */ struct ksplot_color { /** The Red component of the color. */ uint8_t red; /** The Green component of the color. */ uint8_t green; /** The Blue component of the color. */ uint8_t blue; }; /** Structure defining a 2D point. */ struct ksplot_point { /** The horizontal coordinate of the point in pixels. */ int x; /** The vertical coordinate of the pointin in pixels. */ int y; }; void ksplot_make_scene(int width, int height); void ksplot_init_opengl(int dpr); void ksplot_resize_opengl(int width, int height); void ksplot_draw_point(const struct ksplot_point *p, const struct ksplot_color *col, float size); void ksplot_draw_line(const struct ksplot_point *a, const struct ksplot_point *b, const struct ksplot_color *col, float size); void ksplot_draw_polygon(const struct ksplot_point *points, size_t n_points, const struct ksplot_color *col, float size); void ksplot_draw_polygon_contour(const struct ksplot_point *points, size_t n_points, const struct ksplot_color *col, float size); #ifdef __cplusplus } #endif #endif trace-cmd-2.8.3/kernel-shark/src/libkshark-plugin.c000066400000000000000000000154621351617527000221710ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark-plugin.c * @brief KernelShark plugins. */ // C #ifndef _GNU_SOURCE /** Use GNU C Library. */ #define _GNU_SOURCE #endif #include #include #include #include #include #include // KernelShark #include "libkshark-plugin.h" #include "libkshark.h" static struct kshark_event_handler * gui_event_handler_alloc(int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler *handler = malloc(sizeof(*handler)); if (!handler) { fprintf(stderr, "failed to allocate memory for gui eventhandler"); return NULL; } handler->next = NULL; handler->id = event_id; handler->event_func = evt_func; handler->draw_func = dw_func; return handler; } /** * @brief Search the list of event handlers for a handle associated with a * given event type. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id to search for. */ struct kshark_event_handler * kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id) { for (; handlers; handlers = handlers->next) if (handlers->id == event_id) return handlers; return NULL; } /** * @brief Add new event handler to an existing list of handlers. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id. * @param evt_func: Input location for an Event action provided by the plugin. * @param dw_func: Input location for a Draw action provided by the plugin. * * @returns Zero on success, or a negative error code on failure. */ int kshark_register_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler *handler = gui_event_handler_alloc(event_id, evt_func, dw_func); if(!handler) return -ENOMEM; handler->next = *handlers; *handlers = handler; return 0; } /** * @brief Search the list for a specific plugin handle. If such a plugin handle * exists, unregister (remove and free) this handle from the list. * * @param handlers: Input location for the Event handler list. * @param event_id: Event Id of the plugin handler to be unregistered. * @param evt_func: Event action function of the handler to be unregistered. * @param dw_func: Draw action function of the handler to be unregistered. */ void kshark_unregister_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func) { struct kshark_event_handler **last; for (last = handlers; *last; last = &(*last)->next) { if ((*last)->id == event_id && (*last)->event_func == evt_func && (*last)->draw_func == dw_func) { struct kshark_event_handler *this_handler; this_handler = *last; *last = this_handler->next; free(this_handler); return; } } } /** * @brief Free all Event handlers in a given list. * * @param handlers: Input location for the Event handler list. */ void kshark_free_event_handler_list(struct kshark_event_handler *handlers) { struct kshark_event_handler *last; while (handlers) { last = handlers; handlers = handlers->next; free(last); } } /** * @brief Allocate memory for a new plugin. Add this plugin to the list of * plugins used by the session. * * @param kshark_ctx: Input location for the session context pointer. * @param file: The plugin object file to load. * * @returns Zero on success, or a negative error code on failure. */ int kshark_register_plugin(struct kshark_context *kshark_ctx, const char *file) { struct kshark_plugin_list *plugin = kshark_ctx->plugins; struct stat st; int ret; while (plugin) { if (strcmp(plugin->file, file) == 0) return -EEXIST; plugin = plugin->next; } ret = stat(file, &st); if (ret < 0) { fprintf(stderr, "plugin %s not found\n", file); return -ENODEV; } plugin = calloc(sizeof(struct kshark_plugin_list), 1); if (!plugin) { fprintf(stderr, "failed to allocate memory for plugin\n"); return -ENOMEM; } if (asprintf(&plugin->file, "%s", file) <= 0) { fprintf(stderr, "failed to allocate memory for plugin file name"); return -ENOMEM; } plugin->handle = dlopen(plugin->file, RTLD_NOW | RTLD_GLOBAL); if (!plugin->handle) goto fail; plugin->init = dlsym(plugin->handle, KSHARK_PLUGIN_INITIALIZER_NAME); plugin->close = dlsym(plugin->handle, KSHARK_PLUGIN_DEINITIALIZER_NAME); if (!plugin->init || !plugin->close) goto fail; plugin->next = kshark_ctx->plugins; kshark_ctx->plugins = plugin; return 0; fail: fprintf(stderr, "cannot load plugin '%s'\n%s\n", plugin->file, dlerror()); if (plugin->handle) { dlclose(plugin->handle); plugin->handle = NULL; } free(plugin); return EFAULT; } /** * @brief Unrgister a plugin. * * @param kshark_ctx: Input location for context pointer. * @param file: The plugin object file to unregister. */ void kshark_unregister_plugin(struct kshark_context *kshark_ctx, const char *file) { struct kshark_plugin_list **last; for (last = &kshark_ctx->plugins; *last; last = &(*last)->next) { if (strcmp((*last)->file, file) == 0) { struct kshark_plugin_list *this_plugin; this_plugin = *last; *last = this_plugin->next; dlclose(this_plugin->handle); free(this_plugin); return; } } } /** * @brief Free all plugins in a given list. * * @param plugins: Input location for the plugins list. */ void kshark_free_plugin_list(struct kshark_plugin_list *plugins) { struct kshark_plugin_list *last; while (plugins) { last = plugins; plugins = plugins->next; free(last->file); dlclose(last->handle); free(last); } } /** * @brief Use this function to initialize/update/deinitialize all registered * plugins. * * @param kshark_ctx: Input location for context pointer. * @param task_id: Action identifier specifying the action to be executed. * * @returns The number of successful added/removed plugin handlers on success, * or a negative error code on failure. */ int kshark_handle_plugins(struct kshark_context *kshark_ctx, enum kshark_plugin_actions task_id) { struct kshark_plugin_list *plugin; int handler_count = 0; for (plugin = kshark_ctx->plugins; plugin; plugin = plugin->next) { switch (task_id) { case KSHARK_PLUGIN_INIT: handler_count += plugin->init(kshark_ctx); break; case KSHARK_PLUGIN_UPDATE: plugin->close(kshark_ctx); handler_count += plugin->init(kshark_ctx); break; case KSHARK_PLUGIN_CLOSE: handler_count += plugin->close(kshark_ctx); break; default: return -EINVAL; } } return handler_count; } trace-cmd-2.8.3/kernel-shark/src/libkshark-plugin.h000066400000000000000000000105341351617527000221710ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2016 Red Hat Inc, Steven Rostedt */ /** * @file libkshark-plugin.h * @brief KernelShark plugins. */ #ifndef _KSHARK_PLUGIN_H #define _KSHARK_PLUGIN_H #ifdef __cplusplus extern "C" { #endif // __cplusplus // trace-cmd #include "traceevent/event-parse.h" /* Quiet warnings over documenting simple structures */ //! @cond Doxygen_Suppress #define KSHARK_PLUGIN_INITIALIZER kshark_plugin_initializer #define KSHARK_PLUGIN_DEINITIALIZER kshark_plugin_deinitializer #define _MAKE_STR(x) #x #define MAKE_STR(x) _MAKE_STR(x) #define KSHARK_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_INITIALIZER) #define KSHARK_PLUGIN_DEINITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_DEINITIALIZER) struct kshark_context; struct kshark_entry; //! @endcond /** * A function type to be used when defining load/reload/unload plugin * functions. */ typedef int (*kshark_plugin_load_func)(struct kshark_context *); struct kshark_trace_histo; /** * Structure representing the C arguments of the drawing function of * a plugin. */ struct kshark_cpp_argv { /** Pointer to the model descriptor object. */ struct kshark_trace_histo *histo; }; /** A function type to be used when defining plugin functions for drawing. */ typedef void (*kshark_plugin_draw_handler_func)(struct kshark_cpp_argv *argv, int val, int draw_action); /** * A function type to be used when defining plugin functions for data * manipulation. */ typedef void (*kshark_plugin_event_handler_func)(struct kshark_context *kshark_ctx, struct tep_record *rec, struct kshark_entry *e); /** Plugin action identifier. */ enum kshark_plugin_actions { /** * Load plugins action. This action identifier is used when handling * plugins. */ KSHARK_PLUGIN_INIT, /** * Reload plugins action. This action identifier is used when handling * plugins. */ KSHARK_PLUGIN_UPDATE, /** * Unload plugins action. This action identifier is used when handling * plugins. */ KSHARK_PLUGIN_CLOSE, /** * Task draw action. This action identifier is used by the plugin draw * function. */ KSHARK_PLUGIN_TASK_DRAW, /** * CPU draw action. This action identifier is used by the plugin draw * function. */ KSHARK_PLUGIN_CPU_DRAW, }; /** * Plugin Event handler structure, defining the properties of the required * kshark_entry. */ struct kshark_event_handler { /** Pointer to the next Plugin Event handler. */ struct kshark_event_handler *next; /** Unique Id ot the trace event type. */ int id; /** * Event action function. This action can be used to modify the content * of all kshark_entries having Event Ids equal to "id". */ kshark_plugin_event_handler_func event_func; /** * Draw action function. This action can be used to draw additional * graphical elements (shapes) for all kshark_entries having Event Ids * equal to "id". */ kshark_plugin_draw_handler_func draw_func; }; struct kshark_event_handler * kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id); int kshark_register_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func); void kshark_unregister_event_handler(struct kshark_event_handler **handlers, int event_id, kshark_plugin_event_handler_func evt_func, kshark_plugin_draw_handler_func dw_func); void kshark_free_event_handler_list(struct kshark_event_handler *handlers); /** Linked list of plugins. */ struct kshark_plugin_list { /** Pointer to the next Plugin. */ struct kshark_plugin_list *next; /** The plugin object file to load. */ char *file; /** Plugin Event handler. */ void *handle; /** Callback function for initialization of the plugin. */ kshark_plugin_load_func init; /** Callback function for deinitialization of the plugin. */ kshark_plugin_load_func close; }; int kshark_register_plugin(struct kshark_context *kshark_ctx, const char *file); void kshark_unregister_plugin(struct kshark_context *kshark_ctx, const char *file); void kshark_free_plugin_list(struct kshark_plugin_list *plugins); int kshark_handle_plugins(struct kshark_context *kshark_ctx, enum kshark_plugin_actions task_id); #ifdef __cplusplus } #endif // __cplusplus #endif // _KSHARK_PLUGIN_H trace-cmd-2.8.3/kernel-shark/src/libkshark.c000066400000000000000000001356341351617527000207010ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark.c * @brief API for processing of FTRACE (trace-cmd) data. */ /** Use GNU C Library. */ #define _GNU_SOURCE 1 // C #include #include #include // KernelShark #include "libkshark.h" static __thread struct trace_seq seq; static struct kshark_context *kshark_context_handler = NULL; static bool kshark_default_context(struct kshark_context **context) { struct kshark_context *kshark_ctx; kshark_ctx = calloc(1, sizeof(*kshark_ctx)); if (!kshark_ctx) return false; kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; kshark_ctx->plugins = NULL; kshark_ctx->show_task_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->hide_task_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->show_event_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->hide_event_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->show_cpu_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->hide_cpu_filter = tracecmd_filter_id_hash_alloc(); kshark_ctx->filter_mask = 0x0; /* Will free kshark_context_handler. */ kshark_free(NULL); /* Will do nothing if *context is NULL. */ kshark_free(*context); *context = kshark_context_handler = kshark_ctx; return true; } static bool init_thread_seq(void) { if (!seq.buffer) trace_seq_init(&seq); return seq.buffer != NULL; } /** * @brief Initialize a kshark session. This function must be called before * calling any other kshark function. If the session has been * initialized, this function can be used to obtain the session's * context. * * @param kshark_ctx: Optional input/output location for context pointer. * If it points to a context, that context will become * the new session. If it points to NULL, it will obtain * the current (or new) session. The result is only * valid on return of true. * * @returns True on success, or false on failure. */ bool kshark_instance(struct kshark_context **kshark_ctx) { if (*kshark_ctx != NULL) { /* Will free kshark_context_handler */ kshark_free(NULL); /* Use the context provided by the user. */ kshark_context_handler = *kshark_ctx; } else { if (kshark_context_handler) { /* * No context is provided by the user, but the context * handler is already set. Use the context handler. */ *kshark_ctx = kshark_context_handler; } else { /* No kshark_context exists. Create a default one. */ if (!kshark_default_context(kshark_ctx)) return false; } } if (!init_thread_seq()) return false; return true; } static void kshark_free_task_list(struct kshark_context *kshark_ctx) { struct kshark_task_list *task; int i; if (!kshark_ctx) return; for (i = 0; i < KS_TASK_HASH_SIZE; ++i) { while (kshark_ctx->tasks[i]) { task = kshark_ctx->tasks[i]; kshark_ctx->tasks[i] = task->next; free(task); } } } /** * @brief Open and prepare for reading a trace data file specified by "file". * If the specified file does not exist, or contains no trace data, * the function returns false. * * @param kshark_ctx: Input location for context pointer. * @param file: The file to load. * * @returns True on success, or false on failure. */ bool kshark_open(struct kshark_context *kshark_ctx, const char *file) { struct tracecmd_input *handle; kshark_free_task_list(kshark_ctx); handle = tracecmd_open(file); if (!handle) return false; if (pthread_mutex_init(&kshark_ctx->input_mutex, NULL) != 0) { tracecmd_close(handle); return false; } kshark_ctx->handle = handle; kshark_ctx->pevent = tracecmd_get_pevent(handle); kshark_ctx->advanced_event_filter = tep_filter_alloc(kshark_ctx->pevent); /* * Turn off function trace indent and turn on show parent * if possible. */ trace_util_add_option("ftrace:parent", "1"); trace_util_add_option("ftrace:indent", "0"); return true; } /** * @brief Close the trace data file and free the trace data handle. * * @param kshark_ctx: Input location for the session context pointer. */ void kshark_close(struct kshark_context *kshark_ctx) { if (!kshark_ctx || !kshark_ctx->handle) return; /* * All filters are file specific. Make sure that the Pids and Event Ids * from this file are not going to be used with another file. */ tracecmd_filter_id_clear(kshark_ctx->show_task_filter); tracecmd_filter_id_clear(kshark_ctx->hide_task_filter); tracecmd_filter_id_clear(kshark_ctx->show_event_filter); tracecmd_filter_id_clear(kshark_ctx->hide_event_filter); tracecmd_filter_id_clear(kshark_ctx->show_cpu_filter); tracecmd_filter_id_clear(kshark_ctx->hide_cpu_filter); if (kshark_ctx->advanced_event_filter) { tep_filter_reset(kshark_ctx->advanced_event_filter); tep_filter_free(kshark_ctx->advanced_event_filter); kshark_ctx->advanced_event_filter = NULL; } /* * All data collections are file specific. Make sure that collections * from this file are not going to be used with another file. */ kshark_free_collection_list(kshark_ctx->collections); kshark_ctx->collections = NULL; tracecmd_close(kshark_ctx->handle); kshark_ctx->handle = NULL; kshark_ctx->pevent = NULL; pthread_mutex_destroy(&kshark_ctx->input_mutex); } /** * @brief Deinitialize kshark session. Should be called after closing all * open trace data files and before your application terminates. * * @param kshark_ctx: Optional input location for session context pointer. * If it points to a context of a sessuin, that sessuin * will be deinitialize. If it points to NULL, it will * deinitialize the current session. */ void kshark_free(struct kshark_context *kshark_ctx) { if (kshark_ctx == NULL) { if (kshark_context_handler == NULL) return; kshark_ctx = kshark_context_handler; /* kshark_ctx_handler will be set to NULL below. */ } tracecmd_filter_id_hash_free(kshark_ctx->show_task_filter); tracecmd_filter_id_hash_free(kshark_ctx->hide_task_filter); tracecmd_filter_id_hash_free(kshark_ctx->show_event_filter); tracecmd_filter_id_hash_free(kshark_ctx->hide_event_filter); tracecmd_filter_id_hash_free(kshark_ctx->show_cpu_filter); tracecmd_filter_id_hash_free(kshark_ctx->hide_cpu_filter); if (kshark_ctx->plugins) { kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE); kshark_free_plugin_list(kshark_ctx->plugins); kshark_free_event_handler_list(kshark_ctx->event_handlers); } kshark_free_task_list(kshark_ctx); if (seq.buffer) trace_seq_destroy(&seq); if (kshark_ctx == kshark_context_handler) kshark_context_handler = NULL; free(kshark_ctx); } static inline uint8_t knuth_hash8(uint32_t val) { /* * Hashing functions, based on Donald E. Knuth's Multiplicative * hashing. See The Art of Computer Programming (TAOCP). * Multiplication by the Prime number, closest to the golden * ratio of 2^8. */ return UINT8_C(val) * UINT8_C(157); } static struct kshark_task_list * kshark_find_task(struct kshark_context *kshark_ctx, uint8_t key, int pid) { struct kshark_task_list *list; for (list = kshark_ctx->tasks[key]; list; list = list->next) { if (list->pid == pid) return list; } return NULL; } static struct kshark_task_list * kshark_add_task(struct kshark_context *kshark_ctx, int pid) { struct kshark_task_list *list; uint8_t key; key = knuth_hash8(pid); list = kshark_find_task(kshark_ctx, key, pid); if (list) return list; list = malloc(sizeof(*list)); if (!list) return NULL; list->pid = pid; list->next = kshark_ctx->tasks[key]; kshark_ctx->tasks[key] = list; return list; } /** * @brief Get an array containing the Process Ids of all tasks presented in * the loaded trace data file. * * @param kshark_ctx: Input location for context pointer. * @param pids: Output location for the Pids of the tasks. The user is * responsible for freeing the elements of the outputted array. * * @returns The size of the outputted array of Pids in the case of success, * or a negative error code on failure. */ ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids) { size_t i, pid_count = 0, pid_size = KS_TASK_HASH_SIZE; struct kshark_task_list *list; int *temp_pids; *pids = calloc(pid_size, sizeof(int)); if (!*pids) goto fail; for (i = 0; i < KS_TASK_HASH_SIZE; ++i) { list = kshark_ctx->tasks[i]; while (list) { (*pids)[pid_count] = list->pid; list = list->next; if (++pid_count >= pid_size) { pid_size *= 2; temp_pids = realloc(*pids, pid_size * sizeof(int)); if (!temp_pids) { goto fail; } *pids = temp_pids; } } } if (pid_count) { temp_pids = realloc(*pids, pid_count * sizeof(int)); if (!temp_pids) goto fail; /* Paranoid: In the unlikely case of shrinking *pids, realloc moves it */ *pids = temp_pids; } else { free(*pids); *pids = NULL; } return pid_count; fail: fprintf(stderr, "Failed to allocate memory for Task Pids.\n"); free(*pids); *pids = NULL; return -ENOMEM; } static bool filter_find(struct tracecmd_filter_id *filter, int pid, bool test) { return !filter || !filter->count || !!(unsigned long)tracecmd_filter_id_find(filter, pid) == test; } static bool kshark_show_task(struct kshark_context *kshark_ctx, int pid) { return filter_find(kshark_ctx->show_task_filter, pid, true) && filter_find(kshark_ctx->hide_task_filter, pid, false); } static bool kshark_show_event(struct kshark_context *kshark_ctx, int pid) { return filter_find(kshark_ctx->show_event_filter, pid, true) && filter_find(kshark_ctx->hide_event_filter, pid, false); } static bool kshark_show_cpu(struct kshark_context *kshark_ctx, int cpu) { return filter_find(kshark_ctx->show_cpu_filter, cpu, true) && filter_find(kshark_ctx->hide_cpu_filter, cpu, false); } /** * @brief Add an Id value to the filster specified by "filter_id". * * @param kshark_ctx: Input location for the session context pointer. * @param filter_id: Identifier of the filter. * @param id: Id value to be added to the filter. */ void kshark_filter_add_id(struct kshark_context *kshark_ctx, int filter_id, int id) { struct tracecmd_filter_id *filter; switch (filter_id) { case KS_SHOW_CPU_FILTER: filter = kshark_ctx->show_cpu_filter; break; case KS_HIDE_CPU_FILTER: filter = kshark_ctx->hide_cpu_filter; break; case KS_SHOW_EVENT_FILTER: filter = kshark_ctx->show_event_filter; break; case KS_HIDE_EVENT_FILTER: filter = kshark_ctx->hide_event_filter; break; case KS_SHOW_TASK_FILTER: filter = kshark_ctx->show_task_filter; break; case KS_HIDE_TASK_FILTER: filter = kshark_ctx->hide_task_filter; break; default: return; } tracecmd_filter_id_add(filter, id); } /** * @brief Clear (reset) the filster specified by "filter_id". * * @param kshark_ctx: Input location for the session context pointer. * @param filter_id: Identifier of the filter. */ void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id) { struct tracecmd_filter_id *filter; switch (filter_id) { case KS_SHOW_CPU_FILTER: filter = kshark_ctx->show_cpu_filter; break; case KS_HIDE_CPU_FILTER: filter = kshark_ctx->hide_cpu_filter; break; case KS_SHOW_EVENT_FILTER: filter = kshark_ctx->show_event_filter; break; case KS_HIDE_EVENT_FILTER: filter = kshark_ctx->hide_event_filter; break; case KS_SHOW_TASK_FILTER: filter = kshark_ctx->show_task_filter; break; case KS_HIDE_TASK_FILTER: filter = kshark_ctx->hide_task_filter; break; default: return; } tracecmd_filter_id_clear(filter); } static bool filter_is_set(struct tracecmd_filter_id *filter) { return filter && filter->count; } /** * @brief Check if an Id filter is set. * * @param kshark_ctx: Input location for the session context pointer. * * @returns True if at least one Id filter is set, otherwise False. */ bool kshark_filter_is_set(struct kshark_context *kshark_ctx) { return filter_is_set(kshark_ctx->show_task_filter) || filter_is_set(kshark_ctx->hide_task_filter) || filter_is_set(kshark_ctx->show_cpu_filter) || filter_is_set(kshark_ctx->hide_cpu_filter) || filter_is_set(kshark_ctx->show_event_filter) || filter_is_set(kshark_ctx->hide_event_filter); } static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, struct kshark_entry *e) { /* * All entries, filtered-out by the event filters, will be treated * differently, when visualized. Because of this, ignore the value * of the GRAPH_VIEW flag provided by the user via * kshark_ctx->filter_mask. The value of the EVENT_VIEW flag in * kshark_ctx->filter_mask will be used instead. */ int event_mask = kshark_ctx->filter_mask & ~KS_GRAPH_VIEW_FILTER_MASK; e->visible &= ~event_mask; } /** * @brief This function loops over the array of entries specified by "data" * and "n_entries" and sets the "visible" fields of each entry * according to the criteria provided by the filters of the session's * context. The field "filter_mask" of the session's context is used to * control the level of visibility/invisibility of the entries which * are filtered-out. * WARNING: Do not use this function if the advanced filter is set. * Applying the advanced filter requires access to prevent_record, * hence the data has to be reloaded using kshark_load_data_entries(). * * @param kshark_ctx: Input location for the session context pointer. * @param data: Input location for the trace data to be filtered. * @param n_entries: The size of the inputted data. */ void kshark_filter_entries(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries) { int i; if (kshark_ctx->advanced_event_filter->filters) { /* The advanced filter is set. */ fprintf(stderr, "Failed to filter!\n"); fprintf(stderr, "Reset the Advanced filter or reload the data.\n"); return; } if (!kshark_filter_is_set(kshark_ctx)) return; /* Apply only the Id filters. */ for (i = 0; i < n_entries; ++i) { /* Start with and entry which is visible everywhere. */ data[i]->visible = 0xFF; /* Apply event filtering. */ if (!kshark_show_event(kshark_ctx, data[i]->event_id)) unset_event_filter_flag(kshark_ctx, data[i]); /* Apply CPU filtering. */ if (!kshark_show_cpu(kshark_ctx, data[i]->cpu)) data[i]->visible &= ~kshark_ctx->filter_mask; /* Apply task filtering. */ if (!kshark_show_task(kshark_ctx, data[i]->pid)) data[i]->visible &= ~kshark_ctx->filter_mask; } } /** * @brief This function loops over the array of entries specified by "data" * and "n_entries" and resets the "visible" fields of each entry to * the default value of "0xFF" (visible everywhere). * * @param kshark_ctx: Input location for the session context pointer. * @param data: Input location for the trace data to be unfiltered. * @param n_entries: The size of the inputted data. */ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries) { int i; for (i = 0; i < n_entries; ++i) data[i]->visible = 0xFF; } static void kshark_set_entry_values(struct kshark_context *kshark_ctx, struct tep_record *record, struct kshark_entry *entry) { /* Offset of the record */ entry->offset = record->offset; /* CPU Id of the record */ entry->cpu = record->cpu; /* Time stamp of the record */ entry->ts = record->ts; /* Event Id of the record */ entry->event_id = tep_data_type(kshark_ctx->pevent, record); /* * Is visible mask. This default value means that the entry * is visible everywhere. */ entry->visible = 0xFF; /* Process Id of the record */ entry->pid = tep_data_pid(kshark_ctx->pevent, record); } /** Prior time offset of the "missed_events" entry. */ #define ME_ENTRY_TIME_SHIFT 10 static void missed_events_action(struct kshark_context *kshark_ctx, struct tep_record *record, struct kshark_entry *entry) { /* * Use the offset field of the entry to store the number of missed * events. */ entry->offset = record->missed_events; entry->cpu = record->cpu; /* * Position the "missed_events" entry a bit before (in time) * the original record. */ entry->ts = record->ts - ME_ENTRY_TIME_SHIFT; /* All custom entries must have negative event Identifiers. */ entry->event_id = KS_EVENT_OVERFLOW; entry->visible = 0xFF; entry->pid = tep_data_pid(kshark_ctx->pevent, record); } static const char* missed_events_dump(struct kshark_context *kshark_ctx, const struct kshark_entry *entry, bool get_info) { int size = 0; static char *buffer; if (get_info) size = asprintf(&buffer, "missed_events=%i", (int) entry->offset); else size = asprintf(&buffer, "missed_events"); if (size > 0) return buffer; return NULL; } /** * rec_list is used to pass the data to the load functions. * The rec_list will contain the list of entries from the source, * and will be a link list of per CPU entries. */ struct rec_list { union { /* Used by kshark_load_data_records */ struct { /** next pointer, matches entry->next */ struct rec_list *next; /** pointer to the raw record data */ struct tep_record *rec; }; /** entry - Used for kshark_load_data_entries() */ struct kshark_entry entry; }; }; /** * rec_type defines what type of rec_list is being used. */ enum rec_type { REC_RECORD, REC_ENTRY, }; static void free_rec_list(struct rec_list **rec_list, int n_cpus, enum rec_type type) { struct rec_list *temp_rec; int cpu; for (cpu = 0; cpu < n_cpus; ++cpu) { while (rec_list[cpu]) { temp_rec = rec_list[cpu]; rec_list[cpu] = temp_rec->next; if (type == REC_RECORD) free_record(temp_rec->rec); free(temp_rec); } } free(rec_list); } static ssize_t get_records(struct kshark_context *kshark_ctx, struct rec_list ***rec_list, enum rec_type type) { struct kshark_event_handler *evt_handler; struct tep_event_filter *adv_filter; struct kshark_task_list *task; struct tep_record *rec; struct rec_list **temp_next; struct rec_list **cpu_list; struct rec_list *temp_rec; size_t count, total = 0; int n_cpus; int pid; int cpu; n_cpus = tracecmd_cpus(kshark_ctx->handle); cpu_list = calloc(n_cpus, sizeof(*cpu_list)); if (!cpu_list) return -ENOMEM; /* Just to shorten the name */ if (type == REC_ENTRY) adv_filter = kshark_ctx->advanced_event_filter; for (cpu = 0; cpu < n_cpus; ++cpu) { count = 0; cpu_list[cpu] = NULL; temp_next = &cpu_list[cpu]; rec = tracecmd_read_cpu_first(kshark_ctx->handle, cpu); while (rec) { *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); if (!temp_rec) goto fail; temp_rec->next = NULL; switch (type) { case REC_RECORD: temp_rec->rec = rec; pid = tep_data_pid(kshark_ctx->pevent, rec); break; case REC_ENTRY: { struct kshark_entry *entry; int ret; if (rec->missed_events) { /* * Insert a custom "missed_events" entry just * befor this record. */ entry = &temp_rec->entry; missed_events_action(kshark_ctx, rec, entry); temp_next = &temp_rec->next; ++count; /* Now allocate a new rec_list node and comtinue. */ *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); } entry = &temp_rec->entry; kshark_set_entry_values(kshark_ctx, rec, entry); /* Execute all plugin-provided actions (if any). */ evt_handler = kshark_ctx->event_handlers; while ((evt_handler = kshark_find_event_handler(evt_handler, entry->event_id))) { evt_handler->event_func(kshark_ctx, rec, entry); evt_handler = evt_handler->next; entry->visible &= ~KS_PLUGIN_UNTOUCHED_MASK; } pid = entry->pid; /* Apply event filtering. */ ret = FILTER_MATCH; if (adv_filter->filters) ret = tep_filter_match(adv_filter, rec); if (!kshark_show_event(kshark_ctx, entry->event_id) || ret != FILTER_MATCH) { unset_event_filter_flag(kshark_ctx, entry); } /* Apply CPU filtering. */ if (!kshark_show_cpu(kshark_ctx, entry->pid)) { entry->visible &= ~kshark_ctx->filter_mask; } /* Apply task filtering. */ if (!kshark_show_task(kshark_ctx, entry->pid)) { entry->visible &= ~kshark_ctx->filter_mask; } free_record(rec); break; } /* REC_ENTRY */ } task = kshark_add_task(kshark_ctx, pid); if (!task) { free_record(rec); goto fail; } temp_next = &temp_rec->next; ++count; rec = tracecmd_read_data(kshark_ctx->handle, cpu); } total += count; } *rec_list = cpu_list; return total; fail: free_rec_list(cpu_list, n_cpus, type); return -ENOMEM; } static int pick_next_cpu(struct rec_list **rec_list, int n_cpus, enum rec_type type) { uint64_t ts = 0; uint64_t rec_ts; int next_cpu = -1; int cpu; for (cpu = 0; cpu < n_cpus; ++cpu) { if (!rec_list[cpu]) continue; switch (type) { case REC_RECORD: rec_ts = rec_list[cpu]->rec->ts; break; case REC_ENTRY: rec_ts = rec_list[cpu]->entry.ts; break; } if (!ts || rec_ts < ts) { ts = rec_ts; next_cpu = cpu; } } return next_cpu; } /** * @brief Load the content of the trace data file into an array of * kshark_entries. This function provides an abstraction of the * entries from the raw data that is read, however the "latency" * and the "info" fields can be accessed only via the offset * into the file. This makes the access to these two fields much * slower. * If one or more filters are set, the "visible" fields of each entry * is updated according to the criteria provided by the filters. The * field "filter_mask" of the session's context is used to control the * level of visibility/invisibility of the filtered entries. * * @param kshark_ctx: Input location for context pointer. * @param data_rows: Output location for the trace data. The user is * responsible for freeing the elements of the outputted * array. * * @returns The size of the outputted data in the case of success, or a * negative error code on failure. */ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, struct kshark_entry ***data_rows) { struct kshark_entry **rows; struct rec_list **rec_list; enum rec_type type = REC_ENTRY; ssize_t count, total = 0; int n_cpus; if (*data_rows) free(*data_rows); total = get_records(kshark_ctx, &rec_list, type); if (total < 0) goto fail; n_cpus = tracecmd_cpus(kshark_ctx->handle); rows = calloc(total, sizeof(struct kshark_entry *)); if (!rows) goto fail_free; for (count = 0; count < total; count++) { int next_cpu; next_cpu = pick_next_cpu(rec_list, n_cpus, type); if (next_cpu >= 0) { rows[count] = &rec_list[next_cpu]->entry; rec_list[next_cpu] = rec_list[next_cpu]->next; } } free_rec_list(rec_list, n_cpus, type); *data_rows = rows; return total; fail_free: free_rec_list(rec_list, n_cpus, type); fail: fprintf(stderr, "Failed to allocate memory during data loading.\n"); return -ENOMEM; } /** * @brief Load the content of the trace data file into an array of * tep_records. Use this function only if you need fast access * to all fields of the record. * * @param kshark_ctx: Input location for the session context pointer. * @param data_rows: Output location for the trace data. Use free_record() * to free the elements of the outputted array. * * @returns The size of the outputted data in the case of success, or a * negative error code on failure. */ ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, struct tep_record ***data_rows) { struct tep_record **rows; struct tep_record *rec; struct rec_list **rec_list; struct rec_list *temp_rec; enum rec_type type = REC_RECORD; ssize_t count, total = 0; int n_cpus; total = get_records(kshark_ctx, &rec_list, type); if (total < 0) goto fail; n_cpus = tracecmd_cpus(kshark_ctx->handle); rows = calloc(total, sizeof(struct tep_record *)); if (!rows) goto fail_free; for (count = 0; count < total; count++) { int next_cpu; next_cpu = pick_next_cpu(rec_list, n_cpus, type); if (next_cpu >= 0) { rec = rec_list[next_cpu]->rec; rows[count] = rec; temp_rec = rec_list[next_cpu]; rec_list[next_cpu] = rec_list[next_cpu]->next; free(temp_rec); /* The record is still referenced in rows */ } } /* There should be no records left in rec_list */ free_rec_list(rec_list, n_cpus, type); *data_rows = rows; return total; fail_free: free_rec_list(rec_list, n_cpus, type); fail: fprintf(stderr, "Failed to allocate memory during data loading.\n"); return -ENOMEM; } static inline void free_ptr(void *ptr) { if (ptr) free(*(void **)ptr); } static bool data_matrix_alloc(size_t n_rows, uint64_t **offset_array, uint16_t **cpu_array, uint64_t **ts_array, uint16_t **pid_array, int **event_array) { if (offset_array) { *offset_array = calloc(n_rows, sizeof(**offset_array)); if (!*offset_array) return false; } if (cpu_array) { *cpu_array = calloc(n_rows, sizeof(**cpu_array)); if (!*cpu_array) goto free_offset; } if (ts_array) { *ts_array = calloc(n_rows, sizeof(**ts_array)); if (!*ts_array) goto free_cpu; } if (pid_array) { *pid_array = calloc(n_rows, sizeof(**pid_array)); if (!*pid_array) goto free_ts; } if (event_array) { *event_array = calloc(n_rows, sizeof(**event_array)); if (!*event_array) goto free_pid; } return true; free_pid: free_ptr(pid_array); free_ts: free_ptr(ts_array); free_cpu: free_ptr(cpu_array); free_offset: free_ptr(offset_array); fprintf(stderr, "Failed to allocate memory during data loading.\n"); return false; } /** * @brief Load the content of the trace data file into a table / matrix made * of columns / arrays of data. The user is responsible for freeing the * elements of the outputted array * * @param kshark_ctx: Input location for the session context pointer. * @param offset_array: Output location for the array of record offsets. * @param cpu_array: Output location for the array of CPU Ids. * @param ts_array: Output location for the array of timestamps. * @param pid_array: Output location for the array of Process Ids. * @param event_array: Output location for the array of Event Ids. * * @returns The size of the outputted arrays in the case of success, or a * negative error code on failure. */ size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, uint64_t **offset_array, uint16_t **cpu_array, uint64_t **ts_array, uint16_t **pid_array, int **event_array) { enum rec_type type = REC_ENTRY; struct rec_list **rec_list; ssize_t count, total = 0; bool status; int n_cpus; total = get_records(kshark_ctx, &rec_list, type); if (total < 0) goto fail; n_cpus = tracecmd_cpus(kshark_ctx->handle); status = data_matrix_alloc(total, offset_array, cpu_array, ts_array, pid_array, event_array); if (!status) goto fail_free; for (count = 0; count < total; count++) { int next_cpu; next_cpu = pick_next_cpu(rec_list, n_cpus, type); if (next_cpu >= 0) { struct rec_list *rec = rec_list[next_cpu]; struct kshark_entry *e = &rec->entry; if (offset_array) (*offset_array)[count] = e->offset; if (cpu_array) (*cpu_array)[count] = e->cpu; if (ts_array) (*ts_array)[count] = e->ts; if (pid_array) (*pid_array)[count] = e->pid; if (event_array) (*event_array)[count] = e->event_id; rec_list[next_cpu] = rec_list[next_cpu]->next; free(rec); } } /* There should be no entries left in rec_list. */ free_rec_list(rec_list, n_cpus, type); return total; fail_free: free_rec_list(rec_list, n_cpus, type); fail: fprintf(stderr, "Failed to allocate memory during data loading.\n"); return -ENOMEM; } static const char *kshark_get_latency(struct tep_handle *pe, struct tep_record *record) { if (!record) return NULL; trace_seq_reset(&seq); tep_data_latency_format(pe, &seq, record); return seq.buffer; } static const char *kshark_get_info(struct tep_handle *pe, struct tep_record *record, struct tep_event *event) { char *pos; if (!record || !event) return NULL; trace_seq_reset(&seq); tep_event_info(&seq, event, record); /* * The event info string contains a trailing newline. * Remove this newline. */ if ((pos = strchr(seq.buffer, '\n')) != NULL) *pos = '\0'; return seq.buffer; } /** * @brief This function allows for an easy access to the original value of the * Process Id as recorded in the tep_record object. The record is read * from the file only in the case of an entry being touched by a plugin. * Be aware that using the kshark_get_X_easy functions can be * inefficient if you need an access to more than one of the data fields * of the record. * * @param entry: Input location for the KernelShark entry. * * @returns The original value of the Process Id as recorded in the * tep_record object on success, otherwise negative error code. */ int kshark_get_pid_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; struct tep_record *data; int pid; if (!kshark_instance(&kshark_ctx)) return -ENODEV; if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { pid = entry->pid; } else { /* * The entry has been touched by a plugin callback function. * Because of this we do not trust the value of "entry->pid". * * Currently the data reading operations are not thread-safe. * Use a mutex to protect the access. */ pthread_mutex_lock(&kshark_ctx->input_mutex); data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); pid = tep_data_pid(kshark_ctx->pevent, data); free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); } return pid; } /** * @brief This function allows for an easy access to the original value of the * Task name as recorded in the tep_record object. The record is read * from the file only in the case of an entry being touched by a plugin. * Be aware that using the kshark_get_X_easy functions can be * inefficient if you need an access to more than one of the data fields * of the record. * * @param entry: Input location for the KernelShark entry. * * @returns The original name of the task, retrieved from the Process Id * recorded in the tep_record object on success, otherwise NULL. */ const char *kshark_get_task_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; int pid = kshark_get_pid_easy(entry); if (pid < 0) return NULL; kshark_instance(&kshark_ctx); return tep_data_comm_from_pid(kshark_ctx->pevent, pid); } /** * @brief This function allows for an easy access to the latency information * recorded in the tep_record object. The record is read from the file * using the offset field of kshark_entry. Be aware that using the * kshark_get_X_easy functions can be inefficient if you need an access * to more than one of the data fields of the record. * * @param entry: Input location for the KernelShark entry. * * @returns On success the function returns a string showing the latency * information, coded into 5 fields: * interrupts disabled, need rescheduling, hard/soft interrupt, * preempt count and lock depth. On failure it returns NULL. */ const char *kshark_get_latency_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; struct tep_record *data; const char *lat; if (!kshark_instance(&kshark_ctx)) return NULL; if (entry->event_id < 0) return NULL; /* * Currently the data reading operations are not thread-safe. * Use a mutex to protect the access. */ pthread_mutex_lock(&kshark_ctx->input_mutex); data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); lat = kshark_get_latency(kshark_ctx->pevent, data); free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); return lat; } /** * @brief This function allows for an easy access to the original value of the * Event Id as recorded in the tep_record object. The record is read * from the file only in the case of an entry being touched by a plugin. * Be aware that using the kshark_get_X_easy functions can be * inefficient if you need an access to more than one of the data fields * of the record. * * @param entry: Input location for the KernelShark entry. * * @returns The original value of the Event Id as recorded in the * tep_record object on success, otherwise negative error code. */ int kshark_get_event_id_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; struct tep_record *data; int event_id; if (!kshark_instance(&kshark_ctx)) return -ENODEV; if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { event_id = entry->event_id; } else { /* * The entry has been touched by a plugin callback function. * Because of this we do not trust the value of * "entry->event_id". * * Currently the data reading operations are not thread-safe. * Use a mutex to protect the access. */ pthread_mutex_lock(&kshark_ctx->input_mutex); data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); event_id = tep_data_type(kshark_ctx->pevent, data); free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); } return (event_id == -1)? -EFAULT : event_id; } /** * @brief This function allows for an easy access to the original name of the * trace event as recorded in the tep_record object. The record is read * from the file only in the case of an entry being touched by a plugin. * Be aware that using the kshark_get_X_easy functions can be * inefficient if you need an access to more than one of the data fields * of the record. * * @param entry: Input location for the KernelShark entry. * * @returns The mane of the trace event recorded in the tep_record object on * success, otherwise "[UNKNOWN EVENT]" or NULL. */ const char *kshark_get_event_name_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; struct tep_event *event; int event_id = kshark_get_event_id_easy(entry); if (event_id == -EFAULT) return NULL; kshark_instance(&kshark_ctx); if (event_id < 0) { switch (event_id) { case KS_EVENT_OVERFLOW: return missed_events_dump(kshark_ctx, entry, false); default: return NULL; } } /* * Currently the data reading operations are not thread-safe. * Use a mutex to protect the access. */ pthread_mutex_lock(&kshark_ctx->input_mutex); event = tep_find_event(kshark_ctx->pevent, event_id); pthread_mutex_unlock(&kshark_ctx->input_mutex); if (event) return event->name; return "[UNKNOWN EVENT]"; } /** * @brief This function allows for an easy access to the tep_record's info * streang. The record is read from the file using the offset field of * kshark_entry. Be aware that using the kshark_get_X_easy functions can * be inefficient if you need an access to more than one of the data * fields of the record. * * @param entry: Input location for the KernelShark entry. * * @returns A string showing the data output of the trace event on success, * otherwise NULL. */ const char *kshark_get_info_easy(struct kshark_entry *entry) { struct kshark_context *kshark_ctx = NULL; struct tep_event *event; struct tep_record *data; const char *info = NULL; int event_id; if (!kshark_instance(&kshark_ctx)) return NULL; if (entry->event_id < 0) { switch (entry->event_id) { case KS_EVENT_OVERFLOW: return missed_events_dump(kshark_ctx, entry, true); default: return NULL; } } /* * Currently the data reading operations are not thread-safe. * Use a mutex to protect the access. */ pthread_mutex_lock(&kshark_ctx->input_mutex); data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); event_id = tep_data_type(kshark_ctx->pevent, data); event = tep_find_event(kshark_ctx->pevent, event_id); if (event) info = kshark_get_info(kshark_ctx->pevent, data, event); free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); return info; } /** * @brief Convert the timestamp of the trace record (nanosecond precision) into * seconds and microseconds. * * @param time: Input location for the timestamp. * @param sec: Output location for the value of the seconds. * @param usec: Output location for the value of the microseconds. */ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec) { uint64_t s; *sec = s = time / 1000000000ULL; *usec = (time - s * 1000000000ULL) / 1000; } /** * @brief Dump into a string the content a custom entry. The function allocates * a null terminated string and returns a pointer to this string. * * @param kshark_ctx: Input location for the session context pointer. * @param entry: A Kernel Shark entry to be printed. * @param info_func: * * @returns The returned string contains a semicolon-separated list of data * fields. The user has to free the returned string. */ char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx, const struct kshark_entry *entry, kshark_custom_info_func info_func) { const char *event_name, *task, *info; char *entry_str; int size = 0; task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid); event_name = info_func(kshark_ctx, entry, false); info = info_func(kshark_ctx, entry, true); size = asprintf(&entry_str, "%li; %s-%i; CPU %i; ; %s; %s", entry->ts, task, entry->pid, entry->cpu, event_name, info); if (size > 0) return entry_str; return NULL; } /** * @brief Dump into a string the content of one entry. The function allocates * a null terminated string and returns a pointer to this string. The * user has to free the returned string. * * @param entry: A Kernel Shark entry to be printed. * * @returns The returned string contains a semicolon-separated list of data * fields. */ char* kshark_dump_entry(const struct kshark_entry *entry) { const char *event_name, *task, *lat, *info; struct kshark_context *kshark_ctx; char *temp_str, *entry_str; int size = 0; kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx) || !init_thread_seq()) return NULL; task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid); if (entry->event_id >= 0) { struct tep_event *event; struct tep_record *data; data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); event = tep_find_event(kshark_ctx->pevent, entry->event_id); event_name = event? event->name : "[UNKNOWN EVENT]"; lat = kshark_get_latency(kshark_ctx->pevent, data); size = asprintf(&temp_str, "%li; %s-%i; CPU %i; %s;", entry->ts, task, entry->pid, entry->cpu, lat); info = kshark_get_info(kshark_ctx->pevent, data, event); if (size > 0) { size = asprintf(&entry_str, "%s %s; %s; 0x%x", temp_str, event_name, info, entry->visible); free(temp_str); } free_record(data); if (size < 1) entry_str = NULL; } else { switch (entry->event_id) { case KS_EVENT_OVERFLOW: entry_str = kshark_dump_custom_entry(kshark_ctx, entry, missed_events_dump); default: entry_str = NULL; } } return entry_str; } /** * @brief Binary search inside a time-sorted array of kshark_entries. * * @param time: The value of time to search for. * @param data: Input location for the trace data. * @param l: Array index specifying the lower edge of the range to search in. * @param h: Array index specifying the upper edge of the range to search in. * * @returns On success, the first kshark_entry inside the range, having a timestamp equal or bigger than "time". If all entries inside the range have timestamps greater than "time" the function returns BSEARCH_ALL_GREATER (negative value). If all entries inside the range have timestamps smaller than "time" the function returns BSEARCH_ALL_SMALLER (negative value). */ ssize_t kshark_find_entry_by_time(uint64_t time, struct kshark_entry **data, size_t l, size_t h) { size_t mid; if (data[l]->ts > time) return BSEARCH_ALL_GREATER; if (data[h]->ts < time) return BSEARCH_ALL_SMALLER; /* * After executing the BSEARCH macro, "l" will be the index of the last * entry having timestamp < time and "h" will be the index of the first * entry having timestamp >= time. */ BSEARCH(h, l, data[mid]->ts < time); return h; } /** * @brief Binary search inside a time-sorted array of tep_records. * * @param time: The value of time to search for. * @param data: Input location for the trace data. * @param l: Array index specifying the lower edge of the range to search in. * @param h: Array index specifying the upper edge of the range to search in. * * @returns On success, the first tep_record inside the range, having a timestamp equal or bigger than "time". If all entries inside the range have timestamps greater than "time" the function returns BSEARCH_ALL_GREATER (negative value). If all entries inside the range have timestamps smaller than "time" the function returns BSEARCH_ALL_SMALLER (negative value). */ ssize_t kshark_find_record_by_time(uint64_t time, struct tep_record **data, size_t l, size_t h) { size_t mid; if (data[l]->ts > time) return BSEARCH_ALL_GREATER; if (data[h]->ts < time) return BSEARCH_ALL_SMALLER; /* * After executing the BSEARCH macro, "l" will be the index of the last * record having timestamp < time and "h" will be the index of the * first record having timestamp >= time. */ BSEARCH(h, l, data[mid]->ts < time); return h; } /** * @brief Simple Pid matching function to be user for data requests. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param pid: Matching condition value. * * @returns True if the Pid of the entry matches the value of "pid". * Else false. */ bool kshark_match_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { if (e->pid == pid) return true; return false; } /** * @brief Simple Cpu matching function to be user for data requests. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param cpu: Matching condition value. * * @returns True if the Cpu of the entry matches the value of "cpu". * Else false. */ bool kshark_match_cpu(struct kshark_context *kshark_ctx, struct kshark_entry *e, int cpu) { if (e->cpu == cpu) return true; return false; } /** * @brief Create Data request. The request defines the properties of the * requested kshark_entry. * * @param first: Array index specifying the position inside the array from * where the search starts. * @param n: Number of array elements to search in. * @param cond: Matching condition function. * @param val: Matching condition value, used by the Matching condition * function. * @param vis_only: If true, a visible entry is requested. * @param vis_mask: If "vis_only" is true, use this mask to specify the level * of visibility of the requested entry. * * @returns Pointer to kshark_entry_request on success, or NULL on failure. * The user is responsible for freeing the returned * kshark_entry_request. */ struct kshark_entry_request * kshark_entry_request_alloc(size_t first, size_t n, matching_condition_func cond, int val, bool vis_only, int vis_mask) { struct kshark_entry_request *req = malloc(sizeof(*req)); if (!req) { fprintf(stderr, "Failed to allocate memory for entry request.\n"); return NULL; } req->next = NULL; req->first = first; req->n = n; req->cond = cond; req->val = val; req->vis_only = vis_only; req->vis_mask = vis_mask; return req; } /** * @brief Free all Data requests in a given list. * @param req: Intput location for the Data request list. */ void kshark_free_entry_request(struct kshark_entry_request *req) { struct kshark_entry_request *last; while (req) { last = req; req = req->next; free(last); } } /** Dummy entry, used to indicate the existence of filtered entries. */ const struct kshark_entry dummy_entry = { .next = NULL, .visible = 0x00, .cpu = KS_FILTERED_BIN, .pid = KS_FILTERED_BIN, .event_id = -1, .offset = 0, .ts = 0 }; static const struct kshark_entry * get_entry(const struct kshark_entry_request *req, struct kshark_entry **data, ssize_t *index, ssize_t start, ssize_t end, int inc) { struct kshark_context *kshark_ctx = NULL; const struct kshark_entry *e = NULL; ssize_t i; if (index) *index = KS_EMPTY_BIN; if (!kshark_instance(&kshark_ctx)) return e; /* * We will do a sanity check in order to protect against infinite * loops. */ assert((inc > 0 && start < end) || (inc < 0 && start > end)); for (i = start; i != end; i += inc) { if (req->cond(kshark_ctx, data[i], req->val)) { /* * Data satisfying the condition has been found. */ if (req->vis_only && !(data[i]->visible & req->vis_mask)) { /* This data entry has been filtered. */ e = &dummy_entry; } else { e = data[i]; break; } } } if (index) { if (e) *index = (e->cpu != KS_FILTERED_BIN)? i : KS_FILTERED_BIN; else *index = KS_EMPTY_BIN; } return e; } /** * @brief Search for an entry satisfying the requirements of a given Data * request. Start from the position provided by the request and go * searching in the direction of the increasing timestamps (front). * * @param req: Input location for Data request. * @param data: Input location for the trace data. * @param index: Optional output location for the index of the returned * entry inside the array. * * @returns Pointer to the first entry satisfying the matching conditionon * success, or NULL on failure. * In the special case when some entries, satisfying the Matching * condition function have been found, but all these entries have * been discarded because of the visibility criteria (filtered * entries), the function returns a pointer to a special * "Dummy entry". */ const struct kshark_entry * kshark_get_entry_front(const struct kshark_entry_request *req, struct kshark_entry **data, ssize_t *index) { ssize_t end = req->first + req->n; return get_entry(req, data, index, req->first, end, +1); } /** * @brief Search for an entry satisfying the requirements of a given Data * request. Start from the position provided by the request and go * searching in the direction of the decreasing timestamps (back). * * @param req: Input location for Data request. * @param data: Input location for the trace data. * @param index: Optional output location for the index of the returned * entry inside the array. * * @returns Pointer to the first entry satisfying the matching conditionon * success, or NULL on failure. * In the special case when some entries, satisfying the Matching * condition function have been found, but all these entries have * been discarded because of the visibility criteria (filtered * entries), the function returns a pointer to a special * "Dummy entry". */ const struct kshark_entry * kshark_get_entry_back(const struct kshark_entry_request *req, struct kshark_entry **data, ssize_t *index) { ssize_t end = req->first - req->n; if (end < 0) end = -1; return get_entry(req, data, index, req->first, end, -1); } trace-cmd-2.8.3/kernel-shark/src/libkshark.h000066400000000000000000000434011351617527000206740ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file libkshark.h * @brief API for processing of FTRACE (trace-cmd) data. */ #ifndef _LIB_KSHARK_H #define _LIB_KSHARK_H // C #include #include #include // Json-C #include #ifdef __cplusplus extern "C" { #endif // trace-cmd #include "trace-cmd/trace-cmd.h" #include "trace-cmd/trace-filter-hash.h" #include "traceevent/event-parse.h" // KernelShark #include "libkshark-plugin.h" /** * Kernel Shark entry contains all information from one trace record needed * in order to visualize the time-series of trace records. The part of the * data which is not directly required for the visualization (latency, record * info etc.) is available on-demand via the offset into the trace file. */ struct kshark_entry { /** Pointer to the next (in time) kshark_entry on the same CPU core. */ struct kshark_entry *next; /* MUST BE FIRST ENTRY */ /** * A bit mask controlling the visibility of the entry. A value of OxFF * would mean that the entry is visible everywhere. Use * kshark_filter_masks to check the level of visibility/invisibility * of the entry. */ uint16_t visible; /** The CPU core of the record. */ int16_t cpu; /** The PID of the task the record was generated. */ int32_t pid; /** Unique Id ot the trace event type. */ int32_t event_id; /** The offset into the trace file, used to find the record. */ uint64_t offset; /** * The time of the record in nano seconds. The value is taken from * the timestamps within the trace data file, which are architecture * dependent. The time usually is the timestamp from when the system * started. */ uint64_t ts; }; /** Size of the task's hash table. */ #define KS_TASK_HASH_SIZE 256 /** Linked list of tasks. */ struct kshark_task_list { /** Pointer to the next task's PID. */ struct kshark_task_list *next; /** PID of a task. */ int pid; }; /** Structure representing a kshark session. */ struct kshark_context { /** Input handle for the trace data file. */ struct tracecmd_input *handle; /** Page event used to parse the page. */ struct tep_handle *pevent; /** Hash table of task PIDs. */ struct kshark_task_list *tasks[KS_TASK_HASH_SIZE]; /** A mutex, used to protect the access to the input file. */ pthread_mutex_t input_mutex; /** Hash of tasks to filter on. */ struct tracecmd_filter_id *show_task_filter; /** Hash of tasks to not display. */ struct tracecmd_filter_id *hide_task_filter; /** Hash of events to filter on. */ struct tracecmd_filter_id *show_event_filter; /** Hash of events to not display. */ struct tracecmd_filter_id *hide_event_filter; /** Hash of CPUs to filter on. */ struct tracecmd_filter_id *show_cpu_filter; /** Hash of CPUs to not display. */ struct tracecmd_filter_id *hide_cpu_filter; /** * Bit mask, controlling the visibility of the entries after filtering. * If given bit is set here, all entries which are filtered-out will * have this bit unset in their "visible" fields. */ uint8_t filter_mask; /** * Filter allowing sophisticated filtering based on the content of * the event. */ struct tep_event_filter *advanced_event_filter; /** List of Data collections. */ struct kshark_entry_collection *collections; /** List of Plugins. */ struct kshark_plugin_list *plugins; /** List of Plugin Event handlers. */ struct kshark_event_handler *event_handlers; }; bool kshark_instance(struct kshark_context **kshark_ctx); bool kshark_open(struct kshark_context *kshark_ctx, const char *file); ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, struct kshark_entry ***data_rows); ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, struct tep_record ***data_rows); size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, uint64_t **offset_array, uint16_t **cpu_array, uint64_t **ts_array, uint16_t **pid_array, int **event_array); ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids); void kshark_close(struct kshark_context *kshark_ctx); void kshark_free(struct kshark_context *kshark_ctx); int kshark_get_pid_easy(struct kshark_entry *entry); const char *kshark_get_task_easy(struct kshark_entry *entry); const char *kshark_get_latency_easy(struct kshark_entry *entry); int kshark_get_event_id_easy(struct kshark_entry *entry); const char *kshark_get_event_name_easy(struct kshark_entry *entry); const char *kshark_get_info_easy(struct kshark_entry *entry); void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec); char* kshark_dump_entry(const struct kshark_entry *entry); /** * Custom entry info function type. To be user for dumping info for custom * KernelShark entryes. */ typedef const char *(kshark_custom_info_func)(struct kshark_context *, const struct kshark_entry *, bool); char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx, const struct kshark_entry *entry, kshark_custom_info_func info_func); /** Bit masks used to control the visibility of the entry after filtering. */ enum kshark_filter_masks { /** * Use this mask to check the visibility of the entry in the text * view. */ KS_TEXT_VIEW_FILTER_MASK = 1 << 0, /** * Use this mask to check the visibility of the entry in the graph * view. */ KS_GRAPH_VIEW_FILTER_MASK = 1 << 1, /** Special mask used whene filtering events. */ KS_EVENT_VIEW_FILTER_MASK = 1 << 2, /* The next 4 bits are reserved for more KS_X_VIEW_FILTER_MASKs. */ /** * Use this mask to check if the content of the entry has been accessed * by a plugin-defined function. */ KS_PLUGIN_UNTOUCHED_MASK = 1 << 7 }; /** Filter type identifier. */ enum kshark_filter_type { /** Dummy filter identifier reserved for future use. */ KS_NO_FILTER, /** * Identifier of the filter, used to specified the events to be shown. */ KS_SHOW_EVENT_FILTER, /** * Identifier of the filter, used to specified the events to be * filtered-out. */ KS_HIDE_EVENT_FILTER, /** * Identifier of the filter, used to specified the tasks to be shown. */ KS_SHOW_TASK_FILTER, /** * Identifier of the filter, used to specified the tasks to be * filtered-out. */ KS_HIDE_TASK_FILTER, /** * Identifier of the filter, used to specified the CPUs to be shown. */ KS_SHOW_CPU_FILTER, /** * Identifier of the filter, used to specified the CPUs to be * filtered-out. */ KS_HIDE_CPU_FILTER, }; void kshark_filter_add_id(struct kshark_context *kshark_ctx, int filter_id, int id); void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id); bool kshark_filter_is_set(struct kshark_context *kshark_ctx); void kshark_filter_entries(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries); void kshark_clear_all_filters(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries); /** Search failed identifiers. */ enum kshark_search_failed { /** All entries have timestamps greater timestamps. */ BSEARCH_ALL_GREATER = -1, /** All entries have timestamps smaller timestamps. */ BSEARCH_ALL_SMALLER = -2, }; /** General purpose Binary search macro. */ #define BSEARCH(h, l, cond) \ ({ \ while (h - l > 1) { \ mid = (l + h) / 2; \ if (cond) \ l = mid; \ else \ h = mid; \ } \ }) ssize_t kshark_find_entry_by_time(uint64_t time, struct kshark_entry **data_rows, size_t l, size_t h); ssize_t kshark_find_record_by_time(uint64_t time, struct tep_record **data_rows, size_t l, size_t h); bool kshark_match_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); bool kshark_match_cpu(struct kshark_context *kshark_ctx, struct kshark_entry *e, int cpu); /** * Empty bin identifier. * KS_EMPTY_BIN is used to reset entire arrays to empty with memset(), thus it * must be -1 for that to work. */ #define KS_EMPTY_BIN -1 /** Filtered bin identifier. */ #define KS_FILTERED_BIN -2 /** Overflow Event identifier. */ #define KS_EVENT_OVERFLOW (-EOVERFLOW) /** Matching condition function type. To be user for data requests */ typedef bool (matching_condition_func)(struct kshark_context*, struct kshark_entry*, int); /** * Data request structure, defining the properties of the required * kshark_entry. */ struct kshark_entry_request { /** Pointer to the next Data request. */ struct kshark_entry_request *next; /** * Array index specifying the position inside the array from where * the search starts. */ size_t first; /** Number of array elements to search in. */ size_t n; /** Matching condition function. */ matching_condition_func *cond; /** * Matching condition value, used by the Matching condition function. */ int val; /** If true, a visible entry is requested. */ bool vis_only; /** * If "vis_only" is true, use this mask to specify the level of * visibility of the requested entry. */ uint8_t vis_mask; }; struct kshark_entry_request * kshark_entry_request_alloc(size_t first, size_t n, matching_condition_func cond, int val, bool vis_only, int vis_mask); void kshark_free_entry_request(struct kshark_entry_request *req); const struct kshark_entry * kshark_get_entry_front(const struct kshark_entry_request *req, struct kshark_entry **data, ssize_t *index); const struct kshark_entry * kshark_get_entry_back(const struct kshark_entry_request *req, struct kshark_entry **data, ssize_t *index); /** * Data collections are used to optimize the search for an entry having an * abstract property, defined by a Matching condition function and a value. * When a collection is processed, the data which is relevant for the * collection is enclosed in "Data intervals", defined by pairs of "Resume" and * "Break" points. It is guaranteed that the data outside of the intervals * contains no entries satisfying the abstract matching condition. However, the * intervals may (will) contain data that do not satisfy the matching condition. * Once defined, the Data collection can be used when searching for an entry * having the same (ore related) abstract property. The collection allows to * ignore the irrelevant data, thus it eliminates the linear worst-case time * complexity of the search. */ struct kshark_entry_collection { /** Pointer to the next Data collection. */ struct kshark_entry_collection *next; /** Matching condition function, used to define the collections. */ matching_condition_func *cond; /** * Matching condition value, used by the Matching condition finction * to define the collections. */ int val; /** * Array of indexes defining the beginning of each individual data * interval. */ size_t *resume_points; /** * Array of indexes defining the end of each individual data interval. */ size_t *break_points; /** Number of data intervals in this collection. */ size_t size; }; struct kshark_entry_collection * kshark_add_collection_to_list(struct kshark_context *kshark_ctx, struct kshark_entry_collection **col_list, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, int val, size_t margin); struct kshark_entry_collection * kshark_register_data_collection(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, int val, size_t margin); void kshark_unregister_data_collection(struct kshark_entry_collection **col, matching_condition_func cond, int val); struct kshark_entry_collection * kshark_find_data_collection(struct kshark_entry_collection *col, matching_condition_func cond, int val); void kshark_reset_data_collection(struct kshark_entry_collection *col); void kshark_free_collection_list(struct kshark_entry_collection *col); const struct kshark_entry * kshark_get_collection_entry_front(struct kshark_entry_request **req, struct kshark_entry **data, const struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * kshark_get_collection_entry_back(struct kshark_entry_request **req, struct kshark_entry **data, const struct kshark_entry_collection *col, ssize_t *index); /** Structure representing a KernelShark Configuration document. */ struct kshark_config_doc { /** Document format identifier. */ int format; /** Configuration document instance. */ void *conf_doc; }; /** Configuration format identifiers. */ enum kshark_config_formats { /** Unformatted Configuration document identifier. */ KS_CONFIG_AUTO = 0, /** * String Configuration document identifier. The String format is * meant to be used only by kshark_config_doc_add() and * kshark_config_doc_get(), when adding/getting simple string fields. */ KS_CONFIG_STRING, /** Json Configuration document identifier. */ KS_CONFIG_JSON, }; /** * Field name for the Configuration document describing the Hide Event filter. */ #define KS_HIDE_EVENT_FILTER_NAME "hide event filter" /** * Field name for the Configuration document describing the Show Event filter. */ #define KS_SHOW_EVENT_FILTER_NAME "show event filter" /** * Field name for the Configuration document describing the Hide Task filter. */ #define KS_HIDE_TASK_FILTER_NAME "hide task filter" /** * Field name for the Configuration document describing the Show Task filter. */ #define KS_SHOW_TASK_FILTER_NAME "show task filter" /** * Field name for the Configuration document describing the Hide Task filter. */ #define KS_HIDE_CPU_FILTER_NAME "hide cpu filter" /** * Field name for the Configuration document describing the Show Task filter. */ #define KS_SHOW_CPU_FILTER_NAME "show cpu filter" /** * Field name for the Configuration document describing the Advanced event * filter. */ #define KS_ADV_EVENT_FILTER_NAME "adv event filter" /** * Field name for the Configuration document describing user-specified filter * mask. */ #define KS_USER_FILTER_MASK_NAME "filter mask" /** * Field name for the Configuration document describing the state of the Vis. * model. */ #define KS_HISTO_NAME "vis. model" /** * Field name for the Configuration document describing the currently loaded * trace data file. */ #define KS_DATA_SOURCE_NAME "trace data" struct kshark_config_doc * kshark_config_alloc(enum kshark_config_formats); struct kshark_config_doc * kshark_config_new(const char *type, enum kshark_config_formats); void kshark_free_config_doc(struct kshark_config_doc *conf); struct kshark_config_doc * kshark_record_config_new(enum kshark_config_formats); struct kshark_config_doc * kshark_filter_config_new(enum kshark_config_formats); struct kshark_config_doc *kshark_string_config_alloc(void); bool kshark_type_check(struct kshark_config_doc *conf, const char *type); bool kshark_config_doc_add(struct kshark_config_doc *conf, const char *key, struct kshark_config_doc *val); bool kshark_config_doc_get(struct kshark_config_doc *conf, const char *key, struct kshark_config_doc *val); struct kshark_trace_histo; struct kshark_config_doc * kshark_export_trace_file(const char *file, enum kshark_config_formats format); const char *kshark_import_trace_file(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); struct kshark_config_doc * kshark_export_model(struct kshark_trace_histo *histo, enum kshark_config_formats format); bool kshark_import_model(struct kshark_trace_histo *histo, struct kshark_config_doc *conf); bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_export_event_filter(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf); bool kshark_import_event_filter(struct tep_handle *pevent, struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf); bool kshark_export_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); bool kshark_import_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_export_filter_array(struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf); bool kshark_import_filter_array(struct tracecmd_filter_id *filter, const char *filter_name, struct kshark_config_doc *conf); bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); struct kshark_config_doc * kshark_export_all_filters(struct kshark_context *kshark_ctx, enum kshark_config_formats format); bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_import_all_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); bool kshark_save_config_file(const char *file_name, struct kshark_config_doc *conf); struct kshark_config_doc *kshark_open_config_file(const char *file_name, const char *type); struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj); #ifdef __cplusplus } #endif #endif // _LIB_KSHARK_H trace-cmd-2.8.3/kernel-shark/src/plugins/000077500000000000000000000000001351617527000202305ustar00rootroot00000000000000trace-cmd-2.8.3/kernel-shark/src/plugins/CMakeLists.txt000066400000000000000000000022241351617527000227700ustar00rootroot00000000000000message("\n src/plugins ...") function(BUILD_PLUGIN) set(options ) set(oneValueArgs NAME) set(multiValueArgs SOURCE) cmake_parse_arguments(ADD_PLUGIN "${options}" ${oneValueArgs} ${multiValueArgs} ${ARGN}) message(STATUS ${ADD_PLUGIN_NAME}) add_library(${ADD_PLUGIN_NAME} SHARED ${ADD_PLUGIN_SOURCE}) set_target_properties(${ADD_PLUGIN_NAME} PROPERTIES PREFIX "plugin-") target_link_libraries(${ADD_PLUGIN_NAME} kshark) endfunction() set(PLUGIN_LIST "") BUILD_PLUGIN(NAME sched_events SOURCE sched_events.c SchedEvents.cpp) list(APPEND PLUGIN_LIST "sched_events default") # This plugin will be loaded by default # list(APPEND PLUGIN_LIST "sched_events") # This plugin isn't loaded by default BUILD_PLUGIN(NAME missed_events SOURCE missed_events.c MissedEvents.cpp) list(APPEND PLUGIN_LIST "missed_events default") # This plugin will be loaded by default install(TARGETS sched_events missed_events LIBRARY DESTINATION ${KS_PLUGIN_INSTALL_PREFIX}) set(PLUGINS ${PLUGIN_LIST} PARENT_SCOPE) trace-cmd-2.8.3/kernel-shark/src/plugins/MissedEvents.cpp000066400000000000000000000070251351617527000233510ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file MissedEvents.cpp * @brief Plugin for visualization of events, missed due to overflow of the ring buffer. */ // C++ #include // KernelShark #include "libkshark.h" #include "plugins/missed_events.h" #include "KsPlotTools.hpp" #include "KsPlugins.hpp" using namespace KsPlot; /** * This class represents the graphical element of the KernelShark marker for * Missed events. */ class MissedEventsMark : public PlotObject { public: /** Create a default Missed events marker. */ MissedEventsMark() : _base(0, 0), _height(0) { _color = {0, 0, 255}; _size = 2; } /** * @brief Create and position a Missed events marker. * * @param p: Base point of the marker. * @param h: vertical size (height) of the marker. */ MissedEventsMark(const Point &p, int h) : _base(p.x(), p.y()), _height(h) { _color = {0, 0, 255}; _size = 2; } /** Set the Base point of the marker. */ void setBase(const Point &p) {_base.set(p.x(), p.y());} /** Set the vertical size (height) point of the marker. */ void setHeight(int h) {_height = h;} private: /** Base point of the Mark's line. */ Point _base; /** The vertical size (height) of the Mark. */ int _height; void _draw(const Color &col, float size = 1.) const override; }; void MissedEventsMark::_draw(const Color &col, float size) const { Point p(_base.x(), _base.y() - _height); drawLine(_base, p, col, size); Rectangle rec; rec.setPoint(0, p.x(), p.y()); rec.setPoint(1, p.x() - _height / 4, p.y()); rec.setPoint(2, p.x() - _height / 4, p.y() + _height / 4); rec.setPoint(3, p.x(), p.y() + _height / 4); rec._color = col; rec.draw(); } //! @cond Doxygen_Suppress #define PLUGIN_MAX_ENTRIES 10000 #define KS_TASK_COLLECTION_MARGIN 25 //! @endcond static void pluginDraw(kshark_context *kshark_ctx, KsCppArgV *argvCpp, int val, int draw_action) { int height = argvCpp->_graph->getHeight(); const kshark_entry *entry(nullptr); MissedEventsMark *mark; ssize_t index; int nBins = argvCpp->_graph->size(); for (int bin = 0; bin < nBins; ++bin) { if (draw_action == KSHARK_PLUGIN_TASK_DRAW) entry = ksmodel_get_task_missed_events(argvCpp->_histo, bin, val, nullptr, &index); if (draw_action == KSHARK_PLUGIN_CPU_DRAW) entry = ksmodel_get_cpu_missed_events(argvCpp->_histo, bin, val, nullptr, &index); if (entry) { mark = new MissedEventsMark(argvCpp->_graph->getBin(bin)._base, height); argvCpp->_shapes->push_front(mark); } } } /** * @brief Plugin's draw function. * * @param argv_c: A C pointer to be converted to KsCppArgV (C++ struct). * @param val: Process or CPU Id value. * @param draw_action: Draw action identifier. */ void draw_missed_events(kshark_cpp_argv *argv_c, int val, int draw_action) { kshark_context *kshark_ctx(NULL); if (!kshark_instance(&kshark_ctx)) return; KsCppArgV *argvCpp = KS_ARGV_TO_CPP(argv_c); /* * Plotting the "Missed events" makes sense only in the case of a deep * zoom. Here we set a threshold based on the total number of entries * being visualized by the model. * Don't be afraid to play with different values for this threshold. */ if (argvCpp->_histo->tot_count > PLUGIN_MAX_ENTRIES) return; try { pluginDraw(kshark_ctx, argvCpp, val, draw_action); } catch (const std::exception &exc) { std::cerr << "Exception in MissedEvents\n" << exc.what(); } } trace-cmd-2.8.3/kernel-shark/src/plugins/SchedEvents.cpp000066400000000000000000000175561351617527000231650ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file SchedEvents.cpp * @brief Defines a callback function for Sched events used to plot in green * the wake up latency of the task and in red the time the task was * preempted by another task. */ // C++ #include // C++ 11 #include #include // KernelShark #include "libkshark.h" #include "plugins/sched_events.h" #include "KsPlotTools.hpp" #include "KsPlugins.hpp" //! @cond Doxygen_Suppress #define PLUGIN_MIN_BOX_SIZE 4 #define KS_TASK_COLLECTION_MARGIN 25 //! @endcond extern struct plugin_sched_context *plugin_sched_context_handler; /** Sched Event identifier. */ enum class SchedEvent { /** Sched Switch Event. */ Switch, /** Sched Wakeup Event. */ Wakeup, }; static void pluginDraw(plugin_sched_context *plugin_ctx, kshark_context *kshark_ctx, kshark_trace_histo *histo, kshark_entry_collection *col, SchedEvent e, int pid, KsPlot::Graph *graph, KsPlot::PlotObjList *shapes) { const kshark_entry *entryClose, *entryOpen, *entryME; ssize_t indexClose(0), indexOpen(0), indexME(0); std::function ifSchedBack; KsPlot::Rectangle *rec = nullptr; int height = graph->getHeight() * .3; auto openBox = [&] (const KsPlot::Point &p) { /* * First check if we already have an open box. If we don't * have, open a new one. */ if (!rec) rec = new KsPlot::Rectangle; if (e == SchedEvent::Switch) { /* Red box. */ rec->_color = KsPlot::Color(255, 0, 0); } else { /* Green box. */ rec->_color = KsPlot::Color(0, 255, 0); } rec->setFill(false); rec->setPoint(0, p.x() - 1, p.y() - height); rec->setPoint(1, p.x() - 1, p.y() - 1); }; auto closeBox = [&] (const KsPlot::Point &p) { if (rec == nullptr) return; int boxSize = p.x() - rec->getPoint(0)->x; if (boxSize < PLUGIN_MIN_BOX_SIZE) { /* This box is too small. Don't try to plot it. */ delete rec; rec = nullptr; return; } rec->setPoint(3, p.x() - 1, p.y() - height); rec->setPoint(2, p.x() - 1, p.y() - 1); shapes->push_front(rec); rec = nullptr; }; for (int bin = 0; bin < graph->size(); ++bin) { /* * Starting from the first element in this bin, go forward * in time until you find a trace entry that satisfies the * condition defined by kshark_match_pid. */ entryClose = ksmodel_get_entry_back(histo, bin, false, plugin_switch_match_entry_pid, pid, col, &indexClose); entryME = ksmodel_get_task_missed_events(histo, bin, pid, col, &indexME); if (e == SchedEvent::Switch) { /* * Starting from the last element in this bin, go backward * in time until you find a trace entry that satisfies the * condition defined by plugin_switch_match_rec_pid. */ entryOpen = ksmodel_get_entry_back(histo, bin, false, plugin_switch_match_rec_pid, pid, col, &indexOpen); } else { /* * Starting from the last element in this bin, go backward * in time until you find a trace entry that satisfies the * condition defined by plugin_wakeup_match_rec_pid. */ entryOpen = ksmodel_get_entry_back(histo, bin, false, plugin_wakeup_match_rec_pid, pid, col, &indexOpen); if (entryOpen) { int cpu = ksmodel_get_cpu_back(histo, bin, pid, false, col, nullptr); if (cpu >= 0) { /* * The task is already running. Ignore * this wakeup event. */ entryOpen = nullptr; } } } if (rec) { if (entryME || entryClose) { /* Close the box in this bin. */ closeBox(graph->getBin(bin)._base); if (entryOpen && indexME < indexOpen && indexClose < indexOpen) { /* * We have a Sched switch entry that * comes after (in time) the closure of * the previous box. We have to open a * new box in this bin. */ openBox(graph->getBin(bin)._base); } } } else { if (entryOpen && (!entryClose || indexClose < indexOpen)) { /* Open a new box in this bin. */ openBox(graph->getBin(bin)._base); } } } if (rec) delete rec; return; } /* * Ideally, the sched_switch has to be the last trace event recorded before the * task is preempted. Because of this, when the data is loaded (the first pass), * the "pid" field of the sched_switch entries gets edited by this plugin to be * equal to the "next pid" of the sched_switch event. However, in reality the * sched_switch event may be followed by some trailing events from the same task * (printk events for example). This has the effect of extending the graph of * the task outside of the actual duration of the task. The "second pass" over * the data is used to fix this problem. It takes advantage of the "next" field * of the entry (this field is set during the first pass) to search for trailing * events after the "sched_switch". */ static void secondPass(kshark_entry **data, kshark_entry_collection *col, int pid) { if (!col) return; const kshark_entry *e; kshark_entry *last; int first, n; ssize_t index; /* Loop over the intervals of the data collection. */ for (size_t i = 0; i < col->size; ++i) { first = col->break_points[i]; n = first - col->resume_points[i]; kshark_entry_request *req = kshark_entry_request_alloc(first, n, plugin_switch_match_rec_pid, pid, false, KS_GRAPH_VIEW_FILTER_MASK); e = kshark_get_entry_back(req, data, &index); free(req); if (!e || index < 0) { /* No sched_switch event in this interval. */ continue; } /* Find the very last trailing event. */ for (last = data[index]; last->next; last = last->next) { if (last->next->pid != pid) { /* * This is the last trailing event. Change the * "pid" to be equal to the "next pid" of the * sched_switch event and leave a sign that you * edited this entry. */ last->pid = data[index]->pid; last->visible &= ~KS_PLUGIN_UNTOUCHED_MASK; break; } } } } /** * @brief Plugin's draw function. * * @param argv_c: A C pointer to be converted to KsCppArgV (C++ struct). * @param pid: Process Id. * @param draw_action: Draw action identifier. */ void plugin_draw(kshark_cpp_argv *argv_c, int pid, int draw_action) { plugin_sched_context *plugin_ctx; kshark_context *kshark_ctx(NULL); kshark_entry_collection *col; if (draw_action != KSHARK_PLUGIN_TASK_DRAW || pid == 0) return; plugin_ctx = plugin_sched_context_handler; if (!plugin_ctx || !kshark_instance(&kshark_ctx)) return; KsCppArgV *argvCpp = KS_ARGV_TO_CPP(argv_c); /* * Try to find a collections for this task. It is OK if * coll = NULL. */ col = kshark_find_data_collection(plugin_ctx->collections, plugin_match_pid, pid); if (!col) { /* * If a data collection for this task does not exist, * register a new one. */ kshark_entry **data = argvCpp->_histo->data; int size = argvCpp->_histo->data_size; col = kshark_add_collection_to_list(kshark_ctx, &plugin_ctx->collections, data, size, plugin_match_pid, pid, KS_TASK_COLLECTION_MARGIN); } if (!tracecmd_filter_id_find(plugin_ctx->second_pass_hash, pid)) { /* The second pass for this task is not done yet. */ secondPass(argvCpp->_histo->data, col, pid); tracecmd_filter_id_add(plugin_ctx->second_pass_hash, pid); } try { pluginDraw(plugin_ctx, kshark_ctx, argvCpp->_histo, col, SchedEvent::Wakeup, pid, argvCpp->_graph, argvCpp->_shapes); pluginDraw(plugin_ctx, kshark_ctx, argvCpp->_histo, col, SchedEvent::Switch, pid, argvCpp->_graph, argvCpp->_shapes); } catch (const std::exception &exc) { std::cerr << "Exception in SchedEvents\n" << exc.what(); } } trace-cmd-2.8.3/kernel-shark/src/plugins/missed_events.c000066400000000000000000000016321351617527000232460ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file missed_events.c * @brief Plugin for visualization of missed events due to overflow of the * ring buffer. */ // KernelShark #include "plugins/missed_events.h" static void nop_action(struct kshark_context *kshark_ctx, struct tep_record *record, struct kshark_entry *entry) {} /** Load this plugin. */ int KSHARK_PLUGIN_INITIALIZER(struct kshark_context *kshark_ctx) { kshark_register_event_handler(&kshark_ctx->event_handlers, KS_EVENT_OVERFLOW, nop_action, draw_missed_events); return 1; } /** Unload this plugin. */ int KSHARK_PLUGIN_DEINITIALIZER(struct kshark_context *kshark_ctx) { kshark_unregister_event_handler(&kshark_ctx->event_handlers, KS_EVENT_OVERFLOW, nop_action, draw_missed_events); return 1; } trace-cmd-2.8.3/kernel-shark/src/plugins/missed_events.h000066400000000000000000000010051351617527000232450ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file missed_events.h * @brief Plugin for visualization of missed events due to overflow of the * ring buffer. */ #ifndef _KS_PLUGIN_M_EVTS_H #define _KS_PLUGIN_M_EVTS_H // KernelShark #include "libkshark.h" #ifdef __cplusplus extern "C" { #endif void draw_missed_events(struct kshark_cpp_argv *argv, int pid, int draw_action); #ifdef __cplusplus } #endif #endif trace-cmd-2.8.3/kernel-shark/src/plugins/sched_events.c000066400000000000000000000226751351617527000230620ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2018 VMware Inc, Yordan Karadzhov */ /** * @file sched_events.c * @brief Defines a callback function for Sched events used to registers the * "next" task (if not registered already) and to changes the value * of the "pid" field of the "sched_switch" entries such that, it * will be ploted as part of the "next" task. */ // C #include #include #include // KernelShark #include "plugins/sched_events.h" /** Plugin context instance. */ struct plugin_sched_context *plugin_sched_context_handler = NULL; static bool define_wakeup_event(struct tep_handle *tep, const char *wakeup_name, struct tep_event **wakeup_event, struct tep_format_field **pid_field) { struct tep_event *event; event = tep_find_event_by_name(tep, "sched", wakeup_name); if (!event) return false; *wakeup_event = event; *pid_field = tep_find_any_field(event, "pid"); return true; } static void plugin_free_context(struct plugin_sched_context *plugin_ctx) { if (!plugin_ctx) return; tracecmd_filter_id_hash_free(plugin_ctx->second_pass_hash); kshark_free_collection_list(plugin_ctx->collections); free(plugin_ctx); } static bool plugin_sched_init_context(struct kshark_context *kshark_ctx) { struct plugin_sched_context *plugin_ctx; struct tep_event *event; bool wakeup_found; /* No context should exist when we initialize the plugin. */ assert(plugin_sched_context_handler == NULL); plugin_sched_context_handler = calloc(1, sizeof(*plugin_sched_context_handler)); if (!plugin_sched_context_handler) { fprintf(stderr, "Failed to allocate memory for plugin_sched_context.\n"); return false; } plugin_ctx = plugin_sched_context_handler; plugin_ctx->handle = kshark_ctx->handle; plugin_ctx->pevent = kshark_ctx->pevent; plugin_ctx->collections = NULL; event = tep_find_event_by_name(plugin_ctx->pevent, "sched", "sched_switch"); if (!event) { plugin_free_context(plugin_ctx); plugin_sched_context_handler = NULL; return false; } plugin_ctx->sched_switch_event = event; plugin_ctx->sched_switch_next_field = tep_find_any_field(event, "next_pid"); plugin_ctx->sched_switch_comm_field = tep_find_field(event, "next_comm"); plugin_ctx->sched_switch_prev_state_field = tep_find_field(event, "prev_state"); wakeup_found = define_wakeup_event(kshark_ctx->pevent, "sched_wakeup", &plugin_ctx->sched_wakeup_event, &plugin_ctx->sched_wakeup_pid_field); wakeup_found |= define_wakeup_event(kshark_ctx->pevent, "sched_wakeup_new", &plugin_ctx->sched_wakeup_new_event, &plugin_ctx->sched_wakeup_new_pid_field); wakeup_found |= define_wakeup_event(kshark_ctx->pevent, "sched_waking", &plugin_ctx->sched_waking_event, &plugin_ctx->sched_waking_pid_field); plugin_ctx->second_pass_hash = tracecmd_filter_id_hash_alloc(); return true; } /** * @brief Get the Process Id of the next scheduled task. * * @param record: Input location for a sched_switch record. */ int plugin_get_next_pid(struct tep_record *record) { struct plugin_sched_context *plugin_ctx = plugin_sched_context_handler; unsigned long long val; int ret; ret = tep_read_number_field(plugin_ctx->sched_switch_next_field, record->data, &val); return ret ? : val; } static void plugin_register_command(struct kshark_context *kshark_ctx, struct tep_record *record, int pid) { struct plugin_sched_context *plugin_ctx = plugin_sched_context_handler; const char *comm; if (!plugin_ctx->sched_switch_comm_field) return; comm = record->data + plugin_ctx->sched_switch_comm_field->offset; /* * TODO: The retrieve of the name of the command above needs to be * implemented as a wrapper function in libtracevent. */ if (!tep_is_pid_registered(kshark_ctx->pevent, pid)) tep_register_comm(kshark_ctx->pevent, comm, pid); } static int find_wakeup_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, struct tep_event *wakeup_event, struct tep_format_field *pid_field) { struct tep_record *record; unsigned long long val; int ret; if (!wakeup_event || e->event_id != wakeup_event->id) return -1; record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL); ret = tep_read_number_field(pid_field, record->data, &val); free_record(record); if (ret) return -1; return val; } static bool wakeup_match_rec_pid(struct plugin_sched_context *plugin_ctx, struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { struct tep_event *wakeup_events[] = { plugin_ctx->sched_waking_event, plugin_ctx->sched_wakeup_event, plugin_ctx->sched_wakeup_new_event, }; struct tep_format_field *wakeup_fields[] = { plugin_ctx->sched_waking_pid_field, plugin_ctx->sched_wakeup_pid_field, plugin_ctx->sched_wakeup_new_pid_field, }; int i, wakeup_pid = -1; for (i = 0; i < sizeof(wakeup_events) / sizeof(wakeup_events[0]); i++) { wakeup_pid = find_wakeup_pid(kshark_ctx, e, wakeup_events[i], wakeup_fields[i]); if (wakeup_pid >= 0) break; } if (wakeup_pid >= 0 && wakeup_pid == pid) return true; return false; } /** * @brief Process Id matching function adapted for sched_wakeup and * sched_wakeup_new events. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param pid: Matching condition value. * * @returns True if the Pid of the record matches the value of "pid". * Otherwise false. */ bool plugin_wakeup_match_rec_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { struct plugin_sched_context *plugin_ctx; plugin_ctx = plugin_sched_context_handler; if (!plugin_ctx) return false; return wakeup_match_rec_pid(plugin_ctx, kshark_ctx, e, pid); } /** * @brief Process Id matching function adapted for sched_switch events. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param pid: Matching condition value. * * @returns True if the Pid of the record matches the value of "pid". * Otherwise false. */ bool plugin_switch_match_rec_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { struct plugin_sched_context *plugin_ctx; unsigned long long val; int ret, switch_pid = -1; plugin_ctx = plugin_sched_context_handler; if (plugin_ctx->sched_switch_event && e->event_id == plugin_ctx->sched_switch_event->id) { struct tep_record *record; record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL); ret = tep_read_number_field(plugin_ctx->sched_switch_prev_state_field, record->data, &val); if (ret == 0 && !(val & 0x7f)) switch_pid = tep_data_pid(plugin_ctx->pevent, record); free_record(record); } if (switch_pid >= 0 && switch_pid == pid) return true; return false; } /** * @brief Process Id matching function adapted for sched_switch events. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param pid: Matching condition value. * * @returns True if the Pid of the entry matches the value of "pid". * Otherwise false. */ bool plugin_switch_match_entry_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { struct plugin_sched_context *plugin_ctx; plugin_ctx = plugin_sched_context_handler; if (plugin_ctx->sched_switch_event && e->event_id == plugin_ctx->sched_switch_event->id && e->pid == pid) return true; return false; } /** * @brief A match function to be used to process a data collections for * the Sched events plugin. * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. * @param pid: Matching condition value. * * @returns True if the entry is relevant for the Sched events plugin. * Otherwise false. */ bool plugin_match_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid) { return plugin_switch_match_entry_pid(kshark_ctx, e, pid) || plugin_switch_match_rec_pid(kshark_ctx, e, pid) || plugin_wakeup_match_rec_pid(kshark_ctx, e, pid); } static void plugin_sched_action(struct kshark_context *kshark_ctx, struct tep_record *rec, struct kshark_entry *entry) { int pid = plugin_get_next_pid(rec); if (pid >= 0) { entry->pid = pid; plugin_register_command(kshark_ctx, rec, entry->pid); } } static int plugin_sched_init(struct kshark_context *kshark_ctx) { struct plugin_sched_context *plugin_ctx; if (!plugin_sched_init_context(kshark_ctx)) return 0; plugin_ctx = plugin_sched_context_handler; kshark_register_event_handler(&kshark_ctx->event_handlers, plugin_ctx->sched_switch_event->id, plugin_sched_action, plugin_draw); return 1; } static int plugin_sched_close(struct kshark_context *kshark_ctx) { struct plugin_sched_context *plugin_ctx; if (!plugin_sched_context_handler) return 0; plugin_ctx = plugin_sched_context_handler; kshark_unregister_event_handler(&kshark_ctx->event_handlers, plugin_ctx->sched_switch_event->id, plugin_sched_action, plugin_draw); plugin_free_context(plugin_ctx); plugin_sched_context_handler = NULL; return 1; } /** Load this plugin. */ int KSHARK_PLUGIN_INITIALIZER(struct kshark_context *kshark_ctx) { return plugin_sched_init(kshark_ctx); } /** Unload this plugin. */ int KSHARK_PLUGIN_DEINITIALIZER(struct kshark_context *kshark_ctx) { return plugin_sched_close(kshark_ctx); } trace-cmd-2.8.3/kernel-shark/src/plugins/sched_events.h000066400000000000000000000046621351617527000230630ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1 */ /* * Copyright (C) 2017 VMware Inc, Yordan Karadzhov */ /** * @file sched_events.h * @brief Plugin for Sched events. */ #ifndef _KS_PLUGIN_SHED_H #define _KS_PLUGIN_SHED_H // KernelShark #include "libkshark.h" #ifdef __cplusplus extern "C" { #endif /** Structure representing a plugin-specific context. */ struct plugin_sched_context { /** Input handle for the trace data file. */ struct tracecmd_input *handle; /** Page event used to parse the page. */ struct tep_handle *pevent; /** Pointer to the sched_switch_event object. */ struct tep_event *sched_switch_event; /** Pointer to the sched_switch_next_field format descriptor. */ struct tep_format_field *sched_switch_next_field; /** Pointer to the sched_switch_comm_field format descriptor. */ struct tep_format_field *sched_switch_comm_field; /** Pointer to the sched_switch_prev_state_field format descriptor. */ struct tep_format_field *sched_switch_prev_state_field; /** Pointer to the sched_wakeup_event object. */ struct tep_event *sched_wakeup_event; /** Pointer to the sched_wakeup_pid_field format descriptor. */ struct tep_format_field *sched_wakeup_pid_field; /** Pointer to the sched_wakeup_new_event object. */ struct tep_event *sched_wakeup_new_event; /** Pointer to the sched_wakeup_new_pid_field format descriptor. */ struct tep_format_field *sched_wakeup_new_pid_field; /** Pointer to the sched_waking_event object. */ struct tep_event *sched_waking_event; /** Pointer to the sched_waking_pid_field format descriptor. */ struct tep_format_field *sched_waking_pid_field; /** List of Data collections used by this plugin. */ struct kshark_entry_collection *collections; /** Hash of the tasks for which the second pass is already done. */ struct tracecmd_filter_id *second_pass_hash; }; int plugin_get_next_pid(struct tep_record *record); bool plugin_wakeup_match_rec_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); bool plugin_switch_match_rec_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); bool plugin_switch_match_entry_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); bool plugin_match_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); void plugin_draw(struct kshark_cpp_argv *argv, int pid, int draw_action); #ifdef __cplusplus } #endif #endif trace-cmd-2.8.3/lib/000077500000000000000000000000001351617527000141405ustar00rootroot00000000000000trace-cmd-2.8.3/lib/trace-cmd/000077500000000000000000000000001351617527000157775ustar00rootroot00000000000000trace-cmd-2.8.3/lib/trace-cmd/Makefile000066400000000000000000000017651351617527000174500ustar00rootroot00000000000000 include $(src)/scripts/utils.mk bdir:=$(obj)/lib/trace-cmd DEFAULT_TARGET = $(bdir)/libtracecmd.a OBJS = OBJS += trace-hash.o OBJS += trace-hooks.o OBJS += trace-input.o OBJS += trace-recorder.o OBJS += trace-util.o OBJS += trace-filter-hash.o # Additional util objects OBJS += trace-blk-hack.o OBJS += trace-ftrace.o OBJS := $(OBJS:%.o=$(bdir)/%.o) DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d) all: $(DEFAULT_TARGET) $(bdir): @mkdir -p $(bdir) $(OBJS): | $(bdir) $(DEPS): | $(bdir) $(bdir)/libtracecmd.a: $(OBJS) $(Q)$(call do_build_static_lib) $(bdir)/libtracecmd.so: $(OBJS) $(Q)$(call do_compile_shared_library) $(bdir)/%.o: %.c $(Q)$(call do_fpic_compile) $(bdir)/trace-util.o: $(obj)/plugins/trace_plugin_dir $(DEPS): $(bdir)/.%.d: %.c $(Q)$(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@ $(OBJS): $(bdir)/%.o : $(bdir)/.%.d dep_includes := $(wildcard $(DEPS)) ifneq ($(dep_includes),) include $(dep_includes) endif clean: $(RM) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.o $(bdir)/.*.d .PHONY: clean trace-cmd-2.8.3/lib/trace-cmd/include/000077500000000000000000000000001351617527000174225ustar00rootroot00000000000000trace-cmd-2.8.3/lib/trace-cmd/include/trace-hash-local.h000066400000000000000000000017371351617527000227120ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2009, Steven Rostedt * */ #ifndef _TRACE_HASH_LOCAL_H #define _TRACE_HASH_LOCAL_H static inline unsigned int trace_hash(unsigned int val) { unsigned int hash, tmp; hash = 12546869; /* random prime */ /* * The following hash is based off of Paul Hsieh's super fast hash: * http://www.azillionmonkeys.com/qed/hash.html * Note, he released this code unde the GPL 2.0 license, which * is the same as the license for the programs that use it here. */ hash += (val & 0xffff); tmp = (val >> 16) ^ hash; hash = (hash << 16) ^ tmp; hash += hash >> 11; hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } static inline unsigned int trace_hash_str(char *str) { int val = 0; int i; for (i = 0; str[i]; i++) val += ((int)str[i]) << (i & 0xf); return trace_hash(val); } #endif /* _TRACE_HASH_LOCAL_H */ trace-cmd-2.8.3/lib/trace-cmd/trace-blk-hack.c000066400000000000000000000077621351617527000207270ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * */ #include #include "trace-cmd.h" #include "trace-local.h" static const char blk_event_start[] = "name: blktrace\n" "ID: %d\n" "format:\n" "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\n" "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\n" "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\n" "\tfield:int common_pid;\toffset:4;\tsize:4;\n"; static const char blk_body[] = "\n" "\tfield:u64 sector;\toffset:16;\tsize:8;\n" "\tfield:int bytes;\toffset:24;\tsize:4;\n" "\tfield:int action;\toffset:28;\tsize:4;\n" "\tfield:int pid;\toffset:32;\tsize:4;\n" "\tfield:int device;\toffset:36;\tsize:4;\n" "\tfield:int cpu;\toffset:40;\tsize:4;\n" "\tfield:short error;\toffset:44;\tsize:2;\n" "\tfield:short pdu_len;\toffset:46;\tsize:2;\n" "\tfield:void data;\toffset:48;\tsize:0;\n" "\n" "print fmt: \"%%d\", REC->pid\n"; int tracecmd_blk_hack(struct tracecmd_input *handle) { struct tep_handle *pevent; struct tep_event *event; struct tep_format_field *field; char buf[4096]; /* way more than enough! */ int id; int l; int r; pevent = tracecmd_get_pevent(handle); /* * Unfortunately, the TRACE_BLK has changed a bit. * We need to test if various events exist to try * to guess what event id TRACE_BLK would be. */ /* It was originally behind the "power" event */ event = tep_find_event_by_name(pevent, "ftrace", "power"); if (event) { id = event->id + 1; goto found; } /* * But the power tracer is now in perf. * Then it was after kmem_free */ event = tep_find_event_by_name(pevent, "ftrace", "kmem_free"); if (event) { id = event->id + 1; goto found; } /* * But that then went away. * Currently it should be behind the user stack. */ event = tep_find_event_by_name(pevent, "ftrace", "user_stack"); if (event) { id = event->id + 1; goto found; } /* Give up :( */ return -1; found: /* * Blk events are not exported in the events directory. * This is a hack to attempt to create a block event * that we can read. * * We'll make a format file to look like this: * * name: blktrace * ID: 13 * format: * field:unsigned short common_type; offset:0; size:2; * field:unsigned char common_flags; offset:2; size:1; * field:unsigned char common_preempt_count; offset:3; size:1; * field:int common_pid; offset:4; size:4; * field:int common_lock_depth; offset:8; size:4; * * field:u64 sector; offset:16; size:8; * field:int bytes; offset:32; size:4; * field:int action; offset:36; size:4; * field:int pid; offset:40; size:4; * field:int device; offset:44; size:4; * field:int cpu; offset:48; size:4; * field:short error; offset:52; size:2; * field:short pdu_len; offset:54; size:2; * field:void data; offset:60; size:0; * * print fmt: "%d", REC->pid * * Note: the struct blk_io_trace is used directly and * just the first parts of the struct are not used in order * to not write over the ftrace data. */ /* Make sure the common fields exist */ field = tep_find_common_field(event, "common_type"); if (!field || field->offset != 0 || field->size != 2) goto fail; field = tep_find_common_field(event, "common_flags"); if (!field || field->offset != 2 || field->size != 1) goto fail; field = tep_find_common_field(event, "common_preempt_count"); if (!field || field->offset != 3 || field->size != 1) goto fail; field = tep_find_common_field(event, "common_pid"); if (!field || field->offset != 4 || field->size != 4) goto fail; r = sprintf(buf, blk_event_start, id); l = r; /* lock depth is optional */ field = tep_find_common_field(event, "common_lock_depth"); if (field) { if (field->offset != 8 || field->size != 4) return -1; r = sprintf(buf+l, "\tfield:int common_lock_depth;\toffset:8;\tsize:4;\n"); l += r; } r = sprintf(buf+l, blk_body); /* Parse this event */ l += r; tep_parse_event(pevent, buf, l, "ftrace"); return 0; fail: return -1; } trace-cmd-2.8.3/lib/trace-cmd/trace-filter-hash.c000066400000000000000000000111001351617527000214360ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, Steven Rostedt * Copyright (C) 2018 VMware Inc, Steven Rostedt * */ #include #include #include #include #include #include "trace-filter-hash.h" #define FILTER_HASH_SIZE 256 /* * Hashing functions, based on Donald E. Knuth's Multiplicative hashing. * See The Art of Computer Programming (TAOCP). */ static inline uint8_t knuth_hash8(uint32_t val) { /* * Multiplicative hashing function. * Multiplication by the Prime number, closest to the golden * ratio of 2^8. */ return UINT8_C(val) * UINT8_C(157); } static inline uint16_t knuth_hash16(uint32_t val) { /* * Multiplicative hashing function. * Multiplication by the Prime number, closest to the golden * ratio of 2^16. */ return UINT16_C(val) * UINT16_C(40507); } static inline uint32_t knuth_hash(uint32_t val) { /* * Multiplicative hashing function. * Multiplication by the Prime number, closest to the golden * ratio of 2^32. */ return val * UINT32_C(2654435761); } struct tracecmd_filter_id_item * tracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id) { int key = knuth_hash8(id); struct tracecmd_filter_id_item *item = hash->hash[key]; while (item) { if (item->id == id) break; item = item->next; } return item; } void tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id) { int key = knuth_hash8(id); struct tracecmd_filter_id_item *item; item = calloc(1, sizeof(*item)); assert(item); item->id = id; item->next = hash->hash[key]; hash->hash[key] = item; hash->count++; } void tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id) { int key = knuth_hash8(id); struct tracecmd_filter_id_item **next = &hash->hash[key]; struct tracecmd_filter_id_item *item; while (*next) { if ((*next)->id == id) break; next = &(*next)->next; } if (!*next) return; assert(hash->count); hash->count--; item = *next; *next = item->next; free(item); } void tracecmd_filter_id_clear(struct tracecmd_filter_id *hash) { struct tracecmd_filter_id_item *item, *next; int i; for (i = 0; i < FILTER_HASH_SIZE; i++) { next = hash->hash[i]; if (!next) continue; hash->hash[i] = NULL; while (next) { item = next; next = item->next; free(item); } } hash->count = 0; } struct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void) { struct tracecmd_filter_id *hash; hash = calloc(1, sizeof(*hash)); assert(hash); hash->hash = calloc(FILTER_HASH_SIZE, sizeof(*hash->hash)); hash->count = 0; return hash; } void tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash) { if (!hash) return; tracecmd_filter_id_clear(hash); free(hash->hash); free(hash); } struct tracecmd_filter_id * tracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash) { struct tracecmd_filter_id *new_hash; struct tracecmd_filter_id_item *item, **pitem; int i; if (!hash) return NULL; new_hash = tracecmd_filter_id_hash_alloc(); assert(new_hash); for (i = 0; i < FILTER_HASH_SIZE; i++) { item = hash->hash[i]; if (!item) continue; pitem = &new_hash->hash[i]; while (item) { *pitem = calloc(1, sizeof(*item)); assert(*pitem); **pitem = *item; pitem = &(*pitem)->next; item = item->next; } } new_hash->count = hash->count; return new_hash; } int *tracecmd_filter_ids(struct tracecmd_filter_id *hash) { struct tracecmd_filter_id_item *item; int *ids; int count = 0; int i; if (!hash->count) return NULL; ids = malloc(sizeof(*ids) * (hash->count + 1)); if (!ids) return NULL; for (i = 0; i < FILTER_HASH_SIZE; i++) { item = hash->hash[i]; while (item) { ids[count++] = item->id; item = item->next; } } ids[count] = -1; return ids; } /** * filter_id_compare - compare two id hashes to see if they are equal * @hash1: one hash to compare * @hash2: another hash to compare to @hash1 * * Returns 1 if the two hashes are the same, 0 otherwise. */ int tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1, struct tracecmd_filter_id *hash2) { int *ids; int ret = 0; int i; /* If counts don't match, then they obviously are not the same */ if (hash1->count != hash2->count) return 0; /* If both hashes are empty, they are the same */ if (!hash1->count && !hash2->count) return 1; /* Now compare the pids of one hash with the other */ ids = tracecmd_filter_ids(hash1); for (i = 0; ids[i] >= 0; i++) { if (!tracecmd_filter_id_find(hash2, ids[i])) break; } if (ids[i] == -1) ret = 1; free(ids); return ret; } trace-cmd-2.8.3/lib/trace-cmd/trace-ftrace.c000066400000000000000000000254731351617527000205160ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #include #include #include #include #include "trace-cmd.h" struct tep_plugin_option trace_ftrace_options[] = { { .name = "tailprint", .plugin_alias = "fgraph", .description = "Print function name at function exit in function graph", }, { .name = "depth", .plugin_alias = "fgraph", .description = "Show the depth of each entry", }, { .name = NULL, } }; static struct tep_plugin_option *fgraph_tail = &trace_ftrace_options[0]; static struct tep_plugin_option *fgraph_depth = &trace_ftrace_options[1]; static void find_long_size(struct tracecmd_ftrace *finfo) { finfo->long_size = tracecmd_long_size(finfo->handle); } #define long_size_check(handle) \ do { \ if (!finfo->long_size) \ find_long_size(finfo); \ } while (0) static int find_ret_event(struct tracecmd_ftrace *finfo, struct tep_handle *pevent) { struct tep_event *event; /* Store the func ret id and event for later use */ event = tep_find_event_by_name(pevent, "ftrace", "funcgraph_exit"); if (!event) return -1; finfo->fgraph_ret_id = event->id; finfo->fgraph_ret_event = event; return 0; } #define ret_event_check(finfo, pevent) \ do { \ if (!finfo->fgraph_ret_event && find_ret_event(finfo, pevent) < 0) \ return -1; \ } while (0) static int function_handler(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context) { struct tep_handle *pevent = event->tep; unsigned long long function; const char *func; if (tep_get_field_val(s, event, "ip", record, &function, 1)) return trace_seq_putc(s, '!'); func = tep_find_function(pevent, function); if (func) trace_seq_printf(s, "%s <-- ", func); else trace_seq_printf(s, "0x%llx", function); if (tep_get_field_val(s, event, "parent_ip", record, &function, 1)) return trace_seq_putc(s, '!'); func = tep_find_function(pevent, function); if (func) trace_seq_printf(s, "%s", func); else trace_seq_printf(s, "0x%llx", function); return 0; } #define TRACE_GRAPH_INDENT 2 static struct tep_record * get_return_for_leaf(struct trace_seq *s, int cpu, int cur_pid, unsigned long long cur_func, struct tep_record *next, struct tracecmd_ftrace *finfo) { unsigned long long val; unsigned long long type; unsigned long long pid; /* Searching a common field, can use any event */ if (tep_get_common_field_val(s, finfo->fgraph_ret_event, "common_type", next, &type, 1)) return NULL; if (type != finfo->fgraph_ret_id) return NULL; if (tep_get_common_field_val(s, finfo->fgraph_ret_event, "common_pid", next, &pid, 1)) return NULL; if (cur_pid != pid) return NULL; /* We aleady know this is a funcgraph_ret_event */ if (tep_get_field_val(s, finfo->fgraph_ret_event, "func", next, &val, 1)) return NULL; if (cur_func != val) return NULL; /* this is a leaf, now advance the iterator */ return tracecmd_read_data(tracecmd_curr_thread_handle, cpu); } /* Signal a overhead of time execution to the output */ static void print_graph_overhead(struct trace_seq *s, unsigned long long duration) { /* Non nested entry or return */ if (duration == ~0ULL) return (void)trace_seq_printf(s, " "); /* Duration exceeded 1 sec */ if (duration > 1000000000ULL) return (void)trace_seq_printf(s, "$ "); /* Duration exceeded 1000 usecs */ if (duration > 1000000ULL) return (void)trace_seq_printf(s, "# "); /* Duration exceeded 100 usecs */ if (duration > 100000ULL) return (void)trace_seq_printf(s, "! "); /* Duration exceeded 10 usecs */ if (duration > 10000ULL) return (void)trace_seq_printf(s, "+ "); trace_seq_printf(s, " "); } static void print_graph_duration(struct trace_seq *s, unsigned long long duration) { unsigned long usecs = duration / 1000; unsigned long nsecs_rem = duration % 1000; /* log10(ULONG_MAX) + '\0' */ char msecs_str[21]; char nsecs_str[5]; int len; int i; sprintf(msecs_str, "%lu", usecs); /* Print msecs */ len = s->len; trace_seq_printf(s, "%lu", usecs); /* Print nsecs (we don't want to exceed 7 numbers) */ if ((s->len - len) < 7) { snprintf(nsecs_str, MIN(sizeof(nsecs_str), 8 - len), "%03lu", nsecs_rem); trace_seq_printf(s, ".%s", nsecs_str); } len = s->len - len; trace_seq_puts(s, " us "); /* Print remaining spaces to fit the row's width */ for (i = len; i < 7; i++) trace_seq_putc(s, ' '); trace_seq_puts(s, "| "); } static int print_graph_entry_leaf(struct trace_seq *s, struct tep_event *event, struct tep_record *record, struct tep_record *ret_rec, struct tracecmd_ftrace *finfo) { struct tep_handle *pevent = event->tep; unsigned long long rettime, calltime; unsigned long long duration, depth; unsigned long long val; const char *func; int ret; int i; if (tep_get_field_val(s, finfo->fgraph_ret_event, "rettime", ret_rec, &rettime, 1)) return trace_seq_putc(s, '!'); if (tep_get_field_val(s, finfo->fgraph_ret_event, "calltime", ret_rec, &calltime, 1)) return trace_seq_putc(s, '!'); duration = rettime - calltime; /* Overhead */ print_graph_overhead(s, duration); /* Duration */ print_graph_duration(s, duration); if (tep_get_field_val(s, event, "depth", record, &depth, 1)) return trace_seq_putc(s, '!'); /* Function */ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) trace_seq_putc(s, ' '); if (tep_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); func = tep_find_function(pevent, val); if (func) ret = trace_seq_printf(s, "%s();", func); else ret = trace_seq_printf(s, "%llx();", val); if (ret && fgraph_depth->set) ret = trace_seq_printf(s, " (%lld)", depth); return ret; } static int print_graph_nested(struct trace_seq *s, struct tep_event *event, struct tep_record *record) { struct tep_handle *pevent = event->tep; unsigned long long depth; unsigned long long val; const char *func; int ret; int i; /* No overhead */ print_graph_overhead(s, -1); /* No time */ trace_seq_puts(s, " | "); if (tep_get_field_val(s, event, "depth", record, &depth, 1)) return trace_seq_putc(s, '!'); /* Function */ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) trace_seq_putc(s, ' '); if (tep_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); func = tep_find_function(pevent, val); if (func) ret = trace_seq_printf(s, "%s() {", func); else ret = trace_seq_printf(s, "%llx() {", val); if (ret && fgraph_depth->set) ret = trace_seq_printf(s, " (%lld)", depth); return ret; } static int fgraph_ent_handler(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context) { struct tracecmd_ftrace *finfo = context; struct tep_record *rec; unsigned long long val, pid; int cpu; ret_event_check(finfo, event->tep); if (tep_get_common_field_val(s, event, "common_pid", record, &pid, 1)) return trace_seq_putc(s, '!'); if (tep_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); rec = tracecmd_peek_next_data(tracecmd_curr_thread_handle, &cpu); if (rec) rec = get_return_for_leaf(s, cpu, pid, val, rec, finfo); if (rec) { /* * If this is a leaf function, then get_return_for_leaf * returns the return of the function */ print_graph_entry_leaf(s, event, record, rec, finfo); free_record(rec); } else print_graph_nested(s, event, record); return 0; } static int fgraph_ret_handler(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context) { struct tracecmd_ftrace *finfo = context; unsigned long long rettime, calltime; unsigned long long duration, depth; unsigned long long val; const char *func; int i; ret_event_check(finfo, event->tep); if (tep_get_field_val(s, event, "rettime", record, &rettime, 1)) return trace_seq_putc(s, '!'); if (tep_get_field_val(s, event, "calltime", record, &calltime, 1)) return trace_seq_putc(s, '!'); duration = rettime - calltime; /* Overhead */ print_graph_overhead(s, duration); /* Duration */ print_graph_duration(s, duration); if (tep_get_field_val(s, event, "depth", record, &depth, 1)) return trace_seq_putc(s, '!'); /* Function */ for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) trace_seq_putc(s, ' '); trace_seq_putc(s, '}'); if (fgraph_tail->set) { if (tep_get_field_val(s, event, "func", record, &val, 0)) return 0; func = tep_find_function(event->tep, val); if (!func) return 0; trace_seq_printf(s, " /* %s */", func); } if (fgraph_depth->set) trace_seq_printf(s, " (%lld)", depth); return 0; } static int trace_stack_handler(struct trace_seq *s, struct tep_record *record, struct tep_event *event, void *context) { struct tracecmd_ftrace *finfo = context; struct tep_format_field *field; unsigned long long addr; const char *func; void *data = record->data; field = tep_find_any_field(event, "caller"); if (!field) { trace_seq_printf(s, "", "caller"); return 0; } trace_seq_puts(s, "\n"); long_size_check(finfo); for (data += field->offset; data < record->data + record->size; data += finfo->long_size) { addr = tep_read_number(event->tep, data, finfo->long_size); if ((finfo->long_size == 8 && addr == (unsigned long long)-1) || ((int)addr == -1)) break; func = tep_find_function(event->tep, addr); if (func) trace_seq_printf(s, "=> %s (%llx)\n", func, addr); else trace_seq_printf(s, "=> %llx\n", addr); } return 0; } /** * tracecmd_ftrace_load_options - load the ftrace options * * This routine is used for trace-cmd list, to load the builtin * ftrace options in order to list them. As the list command does * not load a trace.dat file where this would normally be loaded. */ void tracecmd_ftrace_load_options(void) { trace_util_add_options("ftrace", trace_ftrace_options); } int tracecmd_ftrace_overrides(struct tracecmd_input *handle, struct tracecmd_ftrace *finfo) { struct tep_handle *pevent; struct tep_event *event; finfo->handle = handle; pevent = tracecmd_get_pevent(handle); tep_register_event_handler(pevent, -1, "ftrace", "function", function_handler, NULL); tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_entry", fgraph_ent_handler, finfo); tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_exit", fgraph_ret_handler, finfo); tep_register_event_handler(pevent, -1, "ftrace", "kernel_stack", trace_stack_handler, finfo); trace_util_add_options("ftrace", trace_ftrace_options); /* Store the func ret id and event for later use */ event = tep_find_event_by_name(pevent, "ftrace", "funcgraph_exit"); if (!event) return 0; finfo->long_size = tracecmd_long_size(handle); finfo->fgraph_ret_id = event->id; finfo->fgraph_ret_event = event; return 0; } trace-cmd-2.8.3/lib/trace-cmd/trace-hash.c000066400000000000000000000032051351617527000201620ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014, Steven Rostedt * */ #include #include #include #include #include #include "trace-hash.h" int trace_hash_init(struct trace_hash *hash, int buckets) { memset(hash, 0, sizeof(*hash)); hash->buckets = calloc(sizeof(*hash->buckets), buckets); if (!hash->buckets) return -ENOMEM; hash->nr_buckets = buckets; /* If a power of two then we can shortcut */ if (!(buckets & (buckets - 1))) hash->power = buckets - 1; return 0; } void trace_hash_free(struct trace_hash *hash) { free(hash->buckets); } int trace_hash_empty(struct trace_hash *hash) { struct trace_hash_item **bucket; trace_hash_for_each_bucket(bucket, hash) if (*bucket) return 0; return 1; } int trace_hash_add(struct trace_hash *hash, struct trace_hash_item *item) { struct trace_hash_item *next; int bucket = hash->power ? item->key & hash->power : item->key % hash->nr_buckets; if (hash->buckets[bucket]) { next = hash->buckets[bucket]; next->prev = item; } else next = NULL; item->next = next; item->prev = (struct trace_hash_item *)&hash->buckets[bucket]; hash->buckets[bucket] = item; return 1; } struct trace_hash_item * trace_hash_find(struct trace_hash *hash, unsigned long long key, trace_hash_func match, void *data) { struct trace_hash_item *item; int bucket = hash->power ? key & hash->power : key % hash->nr_buckets; for (item = hash->buckets[bucket]; item; item = item->next) { if (item->key == key) { if (!match) return item; if (match(item, data)) return item; } } return NULL; } trace-cmd-2.8.3/lib/trace-cmd/trace-hooks.c000066400000000000000000000061351351617527000203670ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2015 Red Hat Inc, Steven Rostedt * */ #include #include #include #include "trace-cmd.h" #include "event-utils.h" struct hook_list *tracecmd_create_event_hook(const char *arg) { struct hook_list *hook; char *system = NULL; char *event; char *match; char *flags = NULL; char *pid = NULL; char *str; char *tok; int index; int ch; int i; hook = malloc(sizeof(*hook)); if (!hook) return NULL; memset(hook, 0, sizeof(*hook)); str = strdup(arg); if (!str) { free(hook); return NULL; } hook->str = str; hook->hook = arg; /* * Hooks are in the form of: * [:],[,]/ * [:],[,] * * Where start_system, start_pid, end_system, and flags are all * optional. * * Flags are (case insensitive): * P - pinned to cpu (wont migrate) * G - global, not hooked to task - currently ignored. * S - save stacks for this event. */ tok = strtok(str, ":,"); if (!tok) goto invalid_tok; /* See what the token was from the original arg */ index = strlen(tok); if (arg[index] == ':') { /* this is a system, the next token must be ',' */ system = tok; tok = strtok(NULL, ","); if (!tok) goto invalid_tok; } event = tok; tok = strtok(NULL, ",/"); if (!tok) goto invalid_tok; match = tok; index = strlen(tok) + tok - str; if (arg[index] == ',') { tok = strtok(NULL, "/"); if (!tok) goto invalid_tok; pid = tok; } hook->start_system = system; hook->start_event = event; hook->start_match = match; hook->pid = pid; /* Now process the end event */ system = NULL; tok = strtok(NULL, ":,"); if (!tok) goto invalid_tok; /* See what the token was from the original arg */ index = tok - str + strlen(tok); if (arg[index] == ':') { /* this is a system, the next token must be ',' */ system = tok; tok = strtok(NULL, ","); if (!tok) goto invalid_tok; } event = tok; tok = strtok(NULL, ","); if (!tok) goto invalid_tok; match = tok; index = strlen(tok) + tok - str; if (arg[index] == ',') { tok = strtok(NULL, ""); if (!tok) goto invalid_tok; flags = tok; } hook->end_system = system; hook->end_event = event; hook->end_match = match; hook->migrate = 1; if (flags) { for (i = 0; flags[i]; i++) { ch = tolower(flags[i]); switch (ch) { case 'p': hook->migrate = 0; break; case 'g': hook->global = 1; break; case 's': hook->stack = 1; break; default: warning("unknown flag %c\n", flags[i]); } } } printf("start %s:%s:%s (%s) end %s:%s:%s (%s)\n", hook->start_system, hook->start_event, hook->start_match, hook->pid, hook->end_system, hook->end_event, hook->end_match, flags); return hook; invalid_tok: warning("Invalid hook format '%s'", arg); return NULL; } void tracecmd_free_hooks(struct hook_list *hooks) { struct hook_list *hook; while (hooks) { hook = hooks; hooks = hooks->next; free(hook->str); free(hook); } } trace-cmd-2.8.3/lib/trace-cmd/trace-input.c000066400000000000000000002217511351617527000204060ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include "trace-cmd-local.h" #include "trace-local.h" #include "kbuffer.h" #include "list.h" #define MISSING_EVENTS (1 << 31) #define MISSING_STORED (1 << 30) #define COMMIT_MASK ((1 << 27) - 1) /* for debugging read instead of mmap */ static int force_read = 0; struct page_map { struct list_head list; off64_t offset; off64_t size; void *map; int ref_count; }; struct page { struct list_head list; off64_t offset; struct tracecmd_input *handle; struct page_map *page_map; void *map; int ref_count; int cpu; long long lost_events; #if DEBUG_RECORD struct tep_record *records; #endif }; struct cpu_data { /* the first two never change */ unsigned long long file_offset; unsigned long long file_size; unsigned long long offset; unsigned long long size; unsigned long long timestamp; struct list_head page_maps; struct page_map *page_map; struct page **pages; struct tep_record *next; struct page *page; struct kbuffer *kbuf; int nr_pages; int page_cnt; int cpu; int pipe_fd; }; struct input_buffer_instance { char *name; size_t offset; }; struct tracecmd_input { struct tep_handle *pevent; struct tep_plugin_list *plugin_list; struct tracecmd_input *parent; unsigned long flags; int fd; int long_size; int page_size; int page_map_size; int cpus; int ref; int nr_buffers; /* buffer instances */ bool use_trace_clock; bool read_page; bool use_pipe; struct cpu_data *cpu_data; unsigned long long ts_offset; double ts2secs; char * cpustats; char * uname; char * version; struct input_buffer_instance *buffers; int parsing_failures; struct tracecmd_ftrace finfo; struct hook_list *hooks; /* file information */ size_t header_files_start; size_t ftrace_files_start; size_t event_files_start; size_t total_file_size; /* For custom profilers. */ tracecmd_show_data_func show_data_func; }; __thread struct tracecmd_input *tracecmd_curr_thread_handle; void tracecmd_set_flag(struct tracecmd_input *handle, int flag) { handle->flags |= flag; } void tracecmd_clear_flag(struct tracecmd_input *handle, int flag) { handle->flags &= ~flag; } unsigned long tracecmd_get_flags(struct tracecmd_input *handle) { return handle->flags; } #if DEBUG_RECORD static void remove_record(struct page *page, struct tep_record *record) { if (record->prev) record->prev->next = record->next; else page->records = record->next; if (record->next) record->next->prev = record->prev; } static void add_record(struct page *page, struct tep_record *record) { if (page->records) page->records->prev = record; record->next = page->records; record->prev = NULL; page->records = record; } static const char *show_records(struct page **pages, int nr_pages) { static char buf[BUFSIZ + 1]; struct tep_record *record; struct page *page; int len; int i; memset(buf, 0, sizeof(buf)); len = 0; for (i = 0; i < nr_pages; i++) { page = pages[i]; if (!page) continue; for (record = page->records; record; record = record->next) { int n; n = snprintf(buf+len, BUFSIZ - len, " 0x%lx", record->alloc_addr); len += n; if (len >= BUFSIZ) break; } } return buf; } #else static inline void remove_record(struct page *page, struct tep_record *record) {} static inline void add_record(struct page *page, struct tep_record *record) {} static const char *show_records(struct page **pages, int nr_pages) { return ""; } #endif static int init_cpu(struct tracecmd_input *handle, int cpu); static ssize_t do_read(struct tracecmd_input *handle, void *data, size_t size) { ssize_t tot = 0; ssize_t r; do { r = read(handle->fd, data + tot, size - tot); tot += r; if (!r) break; if (r < 0) return r; } while (tot != size); return tot; } static ssize_t do_read_check(struct tracecmd_input *handle, void *data, size_t size) { ssize_t ret; ret = do_read(handle, data, size); if (ret < 0) return ret; if (ret != size) return -1; return 0; } static char *read_string(struct tracecmd_input *handle) { char buf[BUFSIZ]; char *str = NULL; size_t size = 0; ssize_t i; ssize_t r; for (;;) { r = do_read(handle, buf, BUFSIZ); if (r < 0) goto fail; if (!r) goto fail; for (i = 0; i < r; i++) { if (!buf[i]) break; } if (i < r) break; if (str) { size += BUFSIZ; str = realloc(str, size); if (!str) return NULL; memcpy(str + (size - BUFSIZ), buf, BUFSIZ); } else { size = BUFSIZ; str = malloc(size); if (!str) return NULL; memcpy(str, buf, size); } } /* move the file descriptor to the end of the string */ r = lseek(handle->fd, -(r - (i+1)), SEEK_CUR); if (r < 0) goto fail; if (str) { size += i + 1; str = realloc(str, size); if (!str) return NULL; memcpy(str + (size - i), buf, i); str[size] = 0; } else { size = i + 1; str = malloc(size); if (!str) return NULL; memcpy(str, buf, i); str[i] = 0; } return str; fail: if (str) free(str); return NULL; } static int read4(struct tracecmd_input *handle, unsigned int *size) { struct tep_handle *pevent = handle->pevent; unsigned int data; if (do_read_check(handle, &data, 4)) return -1; *size = tep_read_number(pevent, &data, 4); return 0; } static int read8(struct tracecmd_input *handle, unsigned long long *size) { struct tep_handle *pevent = handle->pevent; unsigned long long data; if (do_read_check(handle, &data, 8)) return -1; *size = tep_read_number(pevent, &data, 8); return 0; } static int read_header_files(struct tracecmd_input *handle) { struct tep_handle *pevent = handle->pevent; unsigned long long size; char *header; char buf[BUFSIZ]; if (do_read_check(handle, buf, 12)) return -1; if (memcmp(buf, "header_page", 12) != 0) return -1; if (read8(handle, &size) < 0) return -1; header = malloc(size); if (!header) return -1; if (do_read_check(handle, header, size)) goto failed_read; tep_parse_header_page(pevent, header, size, handle->long_size); free(header); /* * The size field in the page is of type long, * use that instead, since it represents the kernel. */ handle->long_size = tep_get_header_page_size(pevent); if (do_read_check(handle, buf, 13)) return -1; if (memcmp(buf, "header_event", 13) != 0) return -1; if (read8(handle, &size) < 0) return -1; header = malloc(size); if (!header) return -1; if (do_read_check(handle, header, size)) goto failed_read; free(header); handle->ftrace_files_start = lseek64(handle->fd, 0, SEEK_CUR); return 0; failed_read: free(header); return -1; } static int regex_event_buf(const char *file, int size, regex_t *epreg) { char *buf; char *line; int ret; buf = malloc(size + 1); if (!buf) { warning("Insufficient memory"); return 0; } strncpy(buf, file, size); buf[size] = 0; /* get the name from the first line */ line = strtok(buf, "\n"); if (!line) { warning("No newline found in '%s'", buf); return 0; } /* skip name if it is there */ if (strncmp(line, "name: ", 6) == 0) line += 6; ret = regexec(epreg, line, 0, NULL, 0) == 0; free(buf); return ret; } static int read_ftrace_file(struct tracecmd_input *handle, unsigned long long size, int print, regex_t *epreg) { struct tep_handle *pevent = handle->pevent; char *buf; buf = malloc(size); if (!buf) return -1; if (do_read_check(handle, buf, size)) { free(buf); return -1; } if (epreg) { if (print || regex_event_buf(buf, size, epreg)) printf("%.*s\n", (int)size, buf); } else { if (tep_parse_event(pevent, buf, size, "ftrace")) handle->parsing_failures++; } free(buf); return 0; } static int read_event_file(struct tracecmd_input *handle, char *system, unsigned long long size, int print, int *sys_printed, regex_t *epreg) { struct tep_handle *pevent = handle->pevent; char *buf; buf = malloc(size); if (!buf) return -1; if (do_read_check(handle, buf, size)) { free(buf); return -1; } if (epreg) { if (print || regex_event_buf(buf, size, epreg)) { if (!*sys_printed) { printf("\nsystem: %s\n", system); *sys_printed = 1; } printf("%.*s\n", (int)size, buf); } } else { if (tep_parse_event(pevent, buf, size, system)) handle->parsing_failures++; } free(buf); return 0; } static int make_preg_files(const char *regex, regex_t *system, regex_t *event, int *unique) { char *buf; char *sstr; char *estr; int ret; /* unique is set if a colon is found */ *unique = 0; /* split "system:event" into "system" and "event" */ buf = strdup(regex); if (!buf) return -ENOMEM; sstr = strtok(buf, ":"); estr = strtok(NULL, ":"); /* If no colon is found, set event == system */ if (!estr) estr = sstr; else *unique = 1; ret = regcomp(system, sstr, REG_ICASE|REG_NOSUB); if (ret) { warning("Bad regular expression '%s'", sstr); goto out; } ret = regcomp(event, estr, REG_ICASE|REG_NOSUB); if (ret) { warning("Bad regular expression '%s'", estr); goto out; } out: free(buf); return ret; } static int read_ftrace_files(struct tracecmd_input *handle, const char *regex) { unsigned long long size; regex_t spreg; regex_t epreg; regex_t *sreg = NULL; regex_t *ereg = NULL; unsigned int count, i; int print_all = 0; int unique; int ret; if (regex) { sreg = &spreg; ereg = &epreg; ret = make_preg_files(regex, sreg, ereg, &unique); if (ret) return -1; if (regexec(sreg, "ftrace", 0, NULL, 0) == 0) { /* * If the system matches a regex that did * not contain a colon, then print all events. */ if (!unique) print_all = 1; } else if (unique) { /* * The user specified a unique event that did * not match the ftrace system. Don't print any * events here. */ regfree(sreg); regfree(ereg); sreg = NULL; ereg = NULL; } } if (read4(handle, &count) < 0) return -1; for (i = 0; i < count; i++) { if (read8(handle, &size) < 0) return -1; ret = read_ftrace_file(handle, size, print_all, ereg); if (ret < 0) return -1; } handle->event_files_start = lseek64(handle->fd, 0, SEEK_CUR); if (sreg) { regfree(sreg); regfree(ereg); } return 0; } static int read_event_files(struct tracecmd_input *handle, const char *regex) { unsigned long long size; char *system; regex_t spreg; regex_t epreg; regex_t *sreg = NULL; regex_t *ereg = NULL; regex_t *reg; unsigned int systems; unsigned int count; unsigned int i, x; int print_all; int sys_printed; int unique; int ret; if (regex) { sreg = &spreg; ereg = &epreg; ret = make_preg_files(regex, sreg, ereg, &unique); if (ret) return -1; } if (read4(handle, &systems) < 0) return -1; for (i = 0; i < systems; i++) { system = read_string(handle); if (!system) return -1; sys_printed = 0; print_all = 0; reg = ereg; if (sreg) { if (regexec(sreg, system, 0, NULL, 0) == 0) { /* * If the user passed in a regex that * did not contain a colon, then we can * print all the events of this system. */ if (!unique) print_all = 1; } else if (unique) { /* * The user passed in a unique event that * specified a specific system and event. * Since this system doesn't match this * event, then we don't print any events * for this system. */ reg = NULL; } } if (read4(handle, &count) < 0) goto failed; for (x=0; x < count; x++) { if (read8(handle, &size) < 0) goto failed; ret = read_event_file(handle, system, size, print_all, &sys_printed, reg); if (ret < 0) goto failed; } free(system); } if (sreg) { regfree(sreg); regfree(ereg); } return 0; failed: if (sreg) { regfree(sreg); regfree(ereg); } free(system); return -1; } static int read_proc_kallsyms(struct tracecmd_input *handle) { struct tep_handle *pevent = handle->pevent; unsigned int size; char *buf; if (read4(handle, &size) < 0) return -1; if (!size) return 0; /* OK? */ buf = malloc(size+1); if (!buf) return -1; if (do_read_check(handle, buf, size)){ free(buf); return -1; } buf[size] = 0; tracecmd_parse_proc_kallsyms(pevent, buf, size); free(buf); return 0; } static int read_ftrace_printk(struct tracecmd_input *handle) { unsigned int size; char *buf; if (read4(handle, &size) < 0) return -1; if (!size) return 0; /* OK? */ buf = malloc(size + 1); if (!buf) return -1; if (do_read_check(handle, buf, size)) { free(buf); return -1; } buf[size] = 0; tracecmd_parse_ftrace_printk(handle->pevent, buf, size); free(buf); return 0; } static int read_and_parse_cmdlines(struct tracecmd_input *handle); /** * tracecmd_get_parsing_failures - get the count of parsing failures * @handle: input handle for the trace.dat file * * This returns the count of failures while parsing the event files */ int tracecmd_get_parsing_failures(struct tracecmd_input *handle) { if (handle) return handle->parsing_failures; return 0; } /** * tracecmd_read_headers - read the header information from trace.dat * @handle: input handle for the trace.dat file * * This reads the trace.dat file for various information. Like the * format of the ring buffer, event formats, ftrace formats, kallsyms * and printk. */ int tracecmd_read_headers(struct tracecmd_input *handle) { int ret; handle->parsing_failures = 0; ret = read_header_files(handle); if (ret < 0) return -1; ret = read_ftrace_files(handle, NULL); if (ret < 0) return -1; ret = read_event_files(handle, NULL); if (ret < 0) return -1; ret = read_proc_kallsyms(handle); if (ret < 0) return -1; ret = read_ftrace_printk(handle); if (ret < 0) return -1; if (read_and_parse_cmdlines(handle) < 0) return -1; tep_set_long_size(handle->pevent, handle->long_size); return 0; } static unsigned long long calc_page_offset(struct tracecmd_input *handle, unsigned long long offset) { return offset & ~(handle->page_size - 1); } static int read_page(struct tracecmd_input *handle, off64_t offset, int cpu, void *map) { off64_t save_seek; off64_t ret; if (handle->use_pipe) { ret = read(handle->cpu_data[cpu].pipe_fd, map, handle->page_size); /* Set EAGAIN if the pipe is empty */ if (ret < 0) { errno = EAGAIN; return -1; } else if (ret == 0) { /* Set EINVAL when the pipe has closed */ errno = EINVAL; return -1; } return 0; } /* other parts of the code may expect the pointer to not move */ save_seek = lseek64(handle->fd, 0, SEEK_CUR); ret = lseek64(handle->fd, offset, SEEK_SET); if (ret < 0) return -1; ret = read(handle->fd, map, handle->page_size); if (ret < 0) return -1; /* reset the file pointer back */ lseek64(handle->fd, save_seek, SEEK_SET); return 0; } /* page_map_size must be a power of two */ static unsigned long long normalize_size(unsigned long long size) { /* From Hacker's Delight: or bits after first set bit to all 1s */ size |= (size >> 1); size |= (size >> 2); size |= (size >> 4); size |= (size >> 8); size |= (size >> 16); size |= (size >> 32); /* Clear all bits except first one for previous power of two */ return size - (size >> 1); } static void free_page_map(struct page_map *page_map) { page_map->ref_count--; if (page_map->ref_count) return; munmap(page_map->map, page_map->size); list_del(&page_map->list); free(page_map); } static void *allocate_page_map(struct tracecmd_input *handle, struct page *page, int cpu, off64_t offset) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; struct page_map *page_map; off64_t map_size; off64_t map_offset; void *map; int ret; if (handle->read_page) { map = malloc(handle->page_size); if (!map) return NULL; ret = read_page(handle, offset, cpu, map); if (ret < 0) { free(map); return NULL; } return map; } map_size = handle->page_map_size; map_offset = offset & ~(map_size - 1); if (map_offset < cpu_data->file_offset) { map_size -= cpu_data->file_offset - map_offset; map_offset = cpu_data->file_offset; } page_map = cpu_data->page_map; if (page_map && page_map->offset == map_offset) goto out; list_for_each_entry(page_map, &cpu_data->page_maps, list) { if (page_map->offset == map_offset) goto out; } page_map = calloc(1, sizeof(*page_map)); if (!page_map) return NULL; if (map_offset + map_size > cpu_data->file_offset + cpu_data->file_size) map_size -= map_offset + map_size - (cpu_data->file_offset + cpu_data->file_size); again: page_map->size = map_size; page_map->offset = map_offset; page_map->map = mmap(NULL, map_size, PROT_READ, MAP_PRIVATE, handle->fd, map_offset); if (page->map == MAP_FAILED) { /* Try a smaller map */ map_size >>= 1; if (map_size < handle->page_size) { free(page_map); return NULL; } handle->page_map_size = map_size; map_offset = offset & ~(map_size - 1); /* * Note, it is now possible to get duplicate memory * maps. But that's fine, the previous maps with * larger sizes will eventually be unmapped. */ goto again; } list_add(&page_map->list, &cpu_data->page_maps); out: if (cpu_data->page_map != page_map) { struct page_map *old_map = cpu_data->page_map; cpu_data->page_map = page_map; page_map->ref_count++; if (old_map) free_page_map(old_map); } page->page_map = page_map; page_map->ref_count++; return page_map->map + offset - page_map->offset; } static struct page *allocate_page(struct tracecmd_input *handle, int cpu, off64_t offset) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; struct page **pages; struct page *page; int index; index = (offset - cpu_data->file_offset) / handle->page_size; if (index >= cpu_data->nr_pages) { pages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages)); if (!pages) return NULL; memset(pages + cpu_data->nr_pages, 0, (index + 1 - cpu_data->nr_pages) * sizeof(*cpu_data->pages)); cpu_data->pages = pages; cpu_data->nr_pages = index + 1; } if (cpu_data->pages[index]) { cpu_data->pages[index]->ref_count++; return cpu_data->pages[index]; } page = malloc(sizeof(*page)); if (!page) return NULL; memset(page, 0, sizeof(*page)); page->offset = offset; page->handle = handle; page->cpu = cpu; page->map = allocate_page_map(handle, page, cpu, offset); if (!page->map) { free(page); return NULL; } cpu_data->pages[index] = page; cpu_data->page_cnt++; page->ref_count = 1; return page; } static void __free_page(struct tracecmd_input *handle, struct page *page) { struct cpu_data *cpu_data = &handle->cpu_data[page->cpu]; struct page **pages; int index; if (!page->ref_count) die("Page ref count is zero!\n"); page->ref_count--; if (page->ref_count) return; if (handle->read_page) free(page->map); else free_page_map(page->page_map); index = (page->offset - cpu_data->file_offset) / handle->page_size; cpu_data->pages[index] = NULL; cpu_data->page_cnt--; free(page); if (handle->use_pipe) { for (index = cpu_data->nr_pages - 1; index > 0; index--) if (cpu_data->pages[index]) break; if (index < (cpu_data->nr_pages - 1)) { pages = realloc(cpu_data->pages, (index + 1) * sizeof(*cpu_data->pages)); if (!pages) return; cpu_data->pages = pages; cpu_data->nr_pages = index + 1; } } } static void free_page(struct tracecmd_input *handle, int cpu) { if (!handle->cpu_data || cpu >= handle->cpus || !handle->cpu_data[cpu].page) return; __free_page(handle, handle->cpu_data[cpu].page); handle->cpu_data[cpu].page = NULL; } static void __free_record(struct tep_record *record) { if (record->priv) { struct page *page = record->priv; remove_record(page, record); __free_page(page->handle, page); } free(record); } void free_record(struct tep_record *record) { if (!record) return; if (!record->ref_count) die("record ref count is zero!"); record->ref_count--; if (record->ref_count) return; if (record->locked) die("freeing record when it is locked!"); record->data = NULL; __free_record(record); } void tracecmd_record_ref(struct tep_record *record) { record->ref_count++; #if DEBUG_RECORD /* Update locating of last reference */ record->alloc_addr = (unsigned long)__builtin_return_address(0); #endif } static void free_next(struct tracecmd_input *handle, int cpu) { struct tep_record *record; if (!handle->cpu_data || cpu >= handle->cpus) return; record = handle->cpu_data[cpu].next; if (!record) return; handle->cpu_data[cpu].next = NULL; record->locked = 0; free_record(record); } /* * Page is mapped, now read in the page header info. */ static int update_page_info(struct tracecmd_input *handle, int cpu) { struct tep_handle *pevent = handle->pevent; void *ptr = handle->cpu_data[cpu].page->map; struct kbuffer *kbuf = handle->cpu_data[cpu].kbuf; /* FIXME: handle header page */ if (tep_get_header_timestamp_size(pevent) != 8) { warning("expected a long long type for timestamp"); return -1; } kbuffer_load_subbuffer(kbuf, ptr); if (kbuffer_subbuffer_size(kbuf) > handle->page_size) { warning("bad page read, with size of %d", kbuffer_subbuffer_size(kbuf)); return -1; } handle->cpu_data[cpu].timestamp = kbuffer_timestamp(kbuf) + handle->ts_offset; if (handle->ts2secs) handle->cpu_data[cpu].timestamp *= handle->ts2secs; return 0; } /* * get_page maps a page for a given cpu. * * Returns 1 if the page was already mapped, * 0 if it mapped successfully * -1 on error */ static int get_page(struct tracecmd_input *handle, int cpu, off64_t offset) { /* Don't map if the page is already where we want */ if (handle->cpu_data[cpu].offset == offset && handle->cpu_data[cpu].page) return 1; /* Do not map no data for CPU */ if (!handle->cpu_data[cpu].size) return -1; if (offset & (handle->page_size - 1)) { errno = -EINVAL; die("bad page offset %llx", offset); return -1; } if (offset < handle->cpu_data[cpu].file_offset || offset > handle->cpu_data[cpu].file_offset + handle->cpu_data[cpu].file_size) { errno = -EINVAL; die("bad page offset %llx", offset); return -1; } handle->cpu_data[cpu].offset = offset; handle->cpu_data[cpu].size = (handle->cpu_data[cpu].file_offset + handle->cpu_data[cpu].file_size) - offset; free_page(handle, cpu); handle->cpu_data[cpu].page = allocate_page(handle, cpu, offset); if (!handle->cpu_data[cpu].page) return -1; if (update_page_info(handle, cpu)) return -1; return 0; } static int get_next_page(struct tracecmd_input *handle, int cpu) { off64_t offset; if (!handle->cpu_data[cpu].page && !handle->use_pipe) return 0; free_page(handle, cpu); if (handle->cpu_data[cpu].size <= handle->page_size) { handle->cpu_data[cpu].offset = 0; return 0; } offset = handle->cpu_data[cpu].offset + handle->page_size; return get_page(handle, cpu, offset); } static struct tep_record * peek_event(struct tracecmd_input *handle, unsigned long long offset, int cpu) { struct tep_record *record = NULL; /* * Since the timestamp is calculated from the beginning * of the page and through each event, we reset the * page to the beginning. This is just used by * tracecmd_read_at. */ update_page_info(handle, cpu); do { free_next(handle, cpu); record = tracecmd_peek_data(handle, cpu); if (record && (record->offset + record->record_size) > offset) break; } while (record); return record; } static struct tep_record * read_event(struct tracecmd_input *handle, unsigned long long offset, int cpu) { struct tep_record *record; record = peek_event(handle, offset, cpu); if (record) record = tracecmd_read_data(handle, cpu); return record; } static struct tep_record * find_and_peek_event(struct tracecmd_input *handle, unsigned long long offset, int *pcpu) { unsigned long long page_offset; int cpu; /* find the cpu that this offset exists in */ for (cpu = 0; cpu < handle->cpus; cpu++) { if (offset >= handle->cpu_data[cpu].file_offset && offset < handle->cpu_data[cpu].file_offset + handle->cpu_data[cpu].file_size) break; } /* Not found? */ if (cpu == handle->cpus) return NULL; /* Move this cpu index to point to this offest */ page_offset = calc_page_offset(handle, offset); if (get_page(handle, cpu, page_offset) < 0) return NULL; if (pcpu) *pcpu = cpu; return peek_event(handle, offset, cpu); } static struct tep_record * find_and_read_event(struct tracecmd_input *handle, unsigned long long offset, int *pcpu) { struct tep_record *record; int cpu; record = find_and_peek_event(handle, offset, &cpu); if (record) { record = tracecmd_read_data(handle, cpu); if (pcpu) *pcpu = cpu; } return record; } /** * tracecmd_read_at - read a record from a specific offset * @handle: input handle for the trace.dat file * @offset: the offset into the file to find the record * @pcpu: pointer to a variable to store the CPU id the record was found in * * This function is useful when looking for a previous record. * You can store the offset of the record "record->offset" and use that * offset to retreive the record again without needing to store any * other information about the record. * * The record returned must be freed. */ struct tep_record * tracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset, int *pcpu) { unsigned long long page_offset; int cpu; page_offset = calc_page_offset(handle, offset); /* check to see if we have this page already */ for (cpu = 0; cpu < handle->cpus; cpu++) { if (handle->cpu_data[cpu].offset == page_offset && handle->cpu_data[cpu].file_size) break; } if (cpu < handle->cpus) { if (pcpu) *pcpu = cpu; return read_event(handle, offset, cpu); } else return find_and_read_event(handle, offset, pcpu); } /** * tracecmd_refresh_record - remaps the records data * @handle: input handle for the trace.dat file * @record: the record to be refreshed * * A record data points to a mmap section of memory. * by reading new records the mmap section may be unmapped. * This will refresh the record's data mapping. * * ===== OBSOLETED BY PAGE REFERENCES ===== * * Returns 1 if page is still mapped (does not modify CPU iterator) * 0 on successful mapping (was not mapped before, * This will update CPU iterator to point to * the next record) * -1 on error. */ int tracecmd_refresh_record(struct tracecmd_input *handle, struct tep_record *record) { unsigned long long page_offset; int cpu = record->cpu; struct cpu_data *cpu_data = &handle->cpu_data[cpu]; int index; int ret; page_offset = calc_page_offset(handle, record->offset); index = record->offset & (handle->page_size - 1); ret = get_page(handle, record->cpu, page_offset); if (ret < 0) return -1; /* If the page is still mapped, there's nothing to do */ if (ret) return 1; record->data = kbuffer_read_at_offset(cpu_data->kbuf, index, &record->ts); cpu_data->timestamp = record->ts; return 0; } /** * tracecmd_read_cpu_first - get the first record in a CPU * @handle: input handle for the trace.dat file * @cpu: the CPU to search * * This returns the first (by time) record entry in a given CPU. * * The record returned must be freed. */ struct tep_record * tracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu) { int ret; ret = get_page(handle, cpu, handle->cpu_data[cpu].file_offset); if (ret < 0) return NULL; /* If the page was already mapped, we need to reset it */ if (ret) update_page_info(handle, cpu); free_next(handle, cpu); return tracecmd_read_data(handle, cpu); } /** * tracecmd_read_cpu_last - get the last record in a CPU * @handle: input handle for the trace.dat file * @cpu: the CPU to search * * This returns the last (by time) record entry in a given CPU. * * The record returned must be freed. */ struct tep_record * tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu) { struct tep_record *record = NULL; off64_t offset, page_offset; offset = handle->cpu_data[cpu].file_offset + handle->cpu_data[cpu].file_size; if (offset & (handle->page_size - 1)) offset &= ~(handle->page_size - 1); else offset -= handle->page_size; page_offset = offset; again: if (get_page(handle, cpu, page_offset) < 0) return NULL; offset = page_offset; do { free_record(record); record = tracecmd_read_data(handle, cpu); if (record) offset = record->offset; } while (record); record = tracecmd_read_at(handle, offset, NULL); /* * It is possible that a page has just a timestamp * or just padding on it. */ if (!record) { if (page_offset == handle->cpu_data[cpu].file_offset) return NULL; page_offset -= handle->page_size; goto again; } return record; } /** * tracecmd_set_cpu_to_timestamp - set the CPU iterator to a given time * @handle: input handle for the trace.dat file * @cpu: the CPU pointer to set * @ts: the timestamp to set the CPU at. * * This sets the CPU iterator used by tracecmd_read_data and * tracecmd_peek_data to a location in the CPU storage near * a given timestamp. It will try to set the iterator to a time before * the time stamp and not actually at a given time. * * To use this to find a record in a time field, call this function * first, than iterate with tracecmd_read_data to find the records * you need. */ int tracecmd_set_cpu_to_timestamp(struct tracecmd_input *handle, int cpu, unsigned long long ts) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; off64_t start, end, next; if (cpu < 0 || cpu >= handle->cpus) { errno = -EINVAL; return -1; } if (!cpu_data->size) return -1; if (!cpu_data->page) { if (init_cpu(handle, cpu)) return -1; } if (cpu_data->timestamp == ts) { /* * If a record is cached, then that record is most * likely the matching timestamp. Otherwise we need * to start from the beginning of the index; */ if (!cpu_data->next || cpu_data->next->ts != ts) update_page_info(handle, cpu); return 0; } /* Set to the first record on current page */ update_page_info(handle, cpu); if (cpu_data->timestamp < ts) { start = cpu_data->offset; end = cpu_data->file_offset + cpu_data->file_size; if (end & (handle->page_size - 1)) end &= ~(handle->page_size - 1); else end -= handle->page_size; next = end; } else { end = cpu_data->offset; start = cpu_data->file_offset; next = start; } while (start < end) { if (get_page(handle, cpu, next) < 0) return -1; if (cpu_data->timestamp == ts) break; if (cpu_data->timestamp < ts) start = next; else end = next; next = start + (end - start) / 2; next = calc_page_offset(handle, next); /* Prevent an infinite loop if start and end are a page off */ if (next == start) start = next += handle->page_size; } /* * We need to end up on a page before the time stamp. * We go back even if the timestamp is the same. This is because * we want the event with the timestamp, not the page. The page * can start with the timestamp we are looking for, but the event * may be on the previous page. */ if (cpu_data->timestamp >= ts && cpu_data->offset > cpu_data->file_offset) get_page(handle, cpu, cpu_data->offset - handle->page_size); return 0; } /** * tracecmd_set_all_cpus_to_timestamp - set all CPUs iterator to a given time * @handle: input handle for the trace.dat file * @cpu: the CPU pointer to set * @ts: the timestamp to set the CPU at. * * This sets the CPU iterator used by tracecmd_read_data and * tracecmd_peek_data to a location in the CPU storage near * a given timestamp. It will try to set the iterator to a time before * the time stamp and not actually at a given time. * * To use this to find a record in a time field, call this function * first, than iterate with tracecmd_read_next_data to find the records * you need. */ void tracecmd_set_all_cpus_to_timestamp(struct tracecmd_input *handle, unsigned long long time) { int cpu; for (cpu = 0; cpu < handle->cpus; cpu++) tracecmd_set_cpu_to_timestamp(handle, cpu, time); } /** * tracecmd_set_cursor - set the offset for the next tracecmd_read_data * @handle: input handle for the trace.dat file * @cpu: the CPU pointer to set * @offset: the offset to place the cursor * * Set the pointer to the next read or peek. This is useful when * needing to read sequentially and then look at another record * out of sequence without breaking the iteration. This is done with: * * record = tracecmd_peek_data() * offset = record->offset; * record = tracecmd_read_at(); * - do what ever with record - * tracecmd_set_cursor(handle, cpu, offset); * * Now the next tracecmd_peek_data or tracecmd_read_data will return * the original record. */ int tracecmd_set_cursor(struct tracecmd_input *handle, int cpu, unsigned long long offset) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; unsigned long long page_offset; if (cpu < 0 || cpu >= handle->cpus) return -1; if (offset < cpu_data->file_offset || offset > cpu_data->file_offset + cpu_data->file_size) return -1; /* cpu does not have this offset. */ /* Move this cpu index to point to this offest */ page_offset = calc_page_offset(handle, offset); if (get_page(handle, cpu, page_offset) < 0) return -1; peek_event(handle, offset, cpu); return 0; } /** * tracecmd_get_cursor - get the offset for the next tracecmd_read_data * @handle: input handle for the trace.dat file * @cpu: the CPU pointer to get the cursor from * * Returns the offset of the next record that would be read. */ unsigned long long tracecmd_get_cursor(struct tracecmd_input *handle, int cpu) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; struct kbuffer *kbuf = cpu_data->kbuf; if (cpu < 0 || cpu >= handle->cpus) return 0; /* * Use the next pointer if it exists and matches the * current timestamp. */ if (cpu_data->next && cpu_data->next->ts == cpu_data->timestamp) return cpu_data->next->offset; /* * Either the next point does not exist, or it does * not match the timestamp. The next read will use the * current page. * * If the offset is at the end, then return that. */ if (cpu_data->offset >= cpu_data->file_offset + cpu_data->file_size) return cpu_data->offset; return cpu_data->offset + kbuffer_curr_offset(kbuf); } /** * tracecmd_translate_data - create a record from raw data * @handle: input handle for the trace.dat file * @ptr: raw data to read * @size: the size of the data * * This function tries to create a record from some given * raw data. The data does not need to be from the trace.dat file. * It can be stored from another location. * * Note, since the timestamp is calculated from within the trace * buffer, the timestamp for the record will be zero, since it * can't calculate it. * * The record returned must be freed. */ struct tep_record * tracecmd_translate_data(struct tracecmd_input *handle, void *ptr, int size) { struct tep_handle *pevent = handle->pevent; struct tep_record *record; unsigned int length; int swap = 1; /* minimum record read is 8, (warn?) (TODO: make 8 into macro) */ if (size < 8) return NULL; record = malloc(sizeof(*record)); if (!record) return NULL; memset(record, 0, sizeof(*record)); record->ref_count = 1; if (tep_is_local_bigendian(pevent) == tep_is_file_bigendian(pevent)) swap = 0; record->data = kbuffer_translate_data(swap, ptr, &length); record->size = length; if (record->data) record->record_size = record->size + (record->data - ptr); return record; } /** * tracecmd_read_page_record - read a record off of a page * @pevent: pevent used to parse the page * @page: the page to read * @size: the size of the page * @last_record: last record read from this page. * * If a ring buffer page is available, and the need to parse it * without having a handle, then this function can be used. * * The @pevent needs to be initialized to have the page header information * already available. * * The @last_record is used to know where to read the next record from. * If @last_record is NULL, the first record on the page will be read. * * Returns: * A newly allocated record that must be freed with free_record() if * a record is found. Otherwise NULL is returned if the record is bad * or no more records exist. */ struct tep_record * tracecmd_read_page_record(struct tep_handle *pevent, void *page, int size, struct tep_record *last_record) { unsigned long long ts; struct kbuffer *kbuf; struct tep_record *record = NULL; enum kbuffer_long_size long_size; enum kbuffer_endian endian; void *ptr; if (tep_is_file_bigendian(pevent)) endian = KBUFFER_ENDIAN_BIG; else endian = KBUFFER_ENDIAN_LITTLE; if (tep_get_header_page_size(pevent) == 8) long_size = KBUFFER_LSIZE_8; else long_size = KBUFFER_LSIZE_4; kbuf = kbuffer_alloc(long_size, endian); if (!kbuf) return NULL; kbuffer_load_subbuffer(kbuf, page); if (kbuffer_subbuffer_size(kbuf) > size) { warning("tracecmd_read_page_record: page_size > size"); goto out_free; } if (last_record) { if (last_record->data < page || last_record->data >= (page + size)) { warning("tracecmd_read_page_record: bad last record (size=%u)", last_record->size); goto out_free; } do { ptr = kbuffer_next_event(kbuf, NULL); if (!ptr) break; } while (ptr < last_record->data); if (ptr != last_record->data) { warning("tracecmd_read_page_record: could not find last_record"); goto out_free; } } ptr = kbuffer_read_event(kbuf, &ts); if (!ptr) goto out_free; record = malloc(sizeof(*record)); if (!record) return NULL; memset(record, 0, sizeof(*record)); record->ts = ts; record->size = kbuffer_event_size(kbuf); record->record_size = kbuffer_curr_size(kbuf); record->cpu = 0; record->data = ptr; record->ref_count = 1; out_free: kbuffer_free(kbuf); return record; } /** * tracecmd_peek_data - return the record at the current location. * @handle: input handle for the trace.dat file * @cpu: the CPU to pull from * * This returns the record at the current location of the CPU * iterator. It does not increment the CPU iterator. */ struct tep_record * tracecmd_peek_data(struct tracecmd_input *handle, int cpu) { struct tep_record *record; unsigned long long ts; struct kbuffer *kbuf; struct page *page; int index; void *data; if (cpu >= handle->cpus) return NULL; page = handle->cpu_data[cpu].page; kbuf = handle->cpu_data[cpu].kbuf; /* Hack to work around function graph read ahead */ tracecmd_curr_thread_handle = handle; if (handle->cpu_data[cpu].next) { record = handle->cpu_data[cpu].next; if (!record->data) die("Something freed the record"); if (handle->cpu_data[cpu].timestamp == record->ts) return record; /* * The timestamp changed, which means the cached * record is no longer valid. Reread a new record. */ free_next(handle, cpu); } read_again: if (!page) { if (handle->use_pipe) { get_next_page(handle, cpu); page = handle->cpu_data[cpu].page; } if (!page) return NULL; } data = kbuffer_read_event(kbuf, &ts); if (!data) { if (get_next_page(handle, cpu)) return NULL; page = handle->cpu_data[cpu].page; goto read_again; } handle->cpu_data[cpu].timestamp = ts + handle->ts_offset; if (handle->ts2secs) { handle->cpu_data[cpu].timestamp *= handle->ts2secs; ts *= handle->ts2secs; } index = kbuffer_curr_offset(kbuf); record = malloc(sizeof(*record)); if (!record) return NULL; memset(record, 0, sizeof(*record)); record->ts = handle->cpu_data[cpu].timestamp; record->size = kbuffer_event_size(kbuf); record->cpu = cpu; record->data = data; record->offset = handle->cpu_data[cpu].offset + index; record->missed_events = kbuffer_missed_events(kbuf); record->ref_count = 1; record->locked = 1; handle->cpu_data[cpu].next = record; record->record_size = kbuffer_curr_size(kbuf); record->priv = page; add_record(page, record); page->ref_count++; kbuffer_next_event(kbuf, NULL); return record; } /** * tracecmd_read_data - read the next record and increment * @handle: input handle for the trace.dat file * @cpu: the CPU to pull from * * This returns the record at the current location of the CPU * iterator and increments the CPU iterator. * * The record returned must be freed. */ struct tep_record * tracecmd_read_data(struct tracecmd_input *handle, int cpu) { struct tep_record *record; record = tracecmd_peek_data(handle, cpu); handle->cpu_data[cpu].next = NULL; if (record) { record->locked = 0; #if DEBUG_RECORD record->alloc_addr = (unsigned long)__builtin_return_address(0); #endif } return record; } /** * tracecmd_read_next_data - read the next record * @handle: input handle to the trace.dat file * @rec_cpu: return pointer to the CPU that the record belongs to * * This returns the next record by time. This is different than * tracecmd_read_data in that it looks at all CPUs. It does a peek * at each CPU and the record with the earliest time stame is * returned. If @rec_cpu is not NULL it gets the CPU id the record was * on. The CPU cursor of the returned record is moved to the * next record. * * Multiple reads of this function will return a serialized list * of all records for all CPUs in order of time stamp. * * The record returned must be freed. */ struct tep_record * tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu) { struct tep_record *record; int next_cpu; record = tracecmd_peek_next_data(handle, &next_cpu); if (!record) return NULL; if (rec_cpu) *rec_cpu = next_cpu; return tracecmd_read_data(handle, next_cpu); } /** * tracecmd_peek_next_data - return the next record * @handle: input handle to the trace.dat file * @rec_cpu: return pointer to the CPU that the record belongs to * * This returns the next record by time. This is different than * tracecmd_peek_data in that it looks at all CPUs. It does a peek * at each CPU and the record with the earliest time stame is * returned. If @rec_cpu is not NULL it gets the CPU id the record was * on. It does not increment the CPU iterator. */ struct tep_record * tracecmd_peek_next_data(struct tracecmd_input *handle, int *rec_cpu) { unsigned long long ts; struct tep_record *record, *next_record = NULL; int next_cpu; int cpu; if (rec_cpu) *rec_cpu = -1; next_cpu = -1; ts = 0; for (cpu = 0; cpu < handle->cpus; cpu++) { record = tracecmd_peek_data(handle, cpu); if (record && (!next_record || record->ts < ts)) { ts = record->ts; next_cpu = cpu; next_record = record; } } if (next_record) { if (rec_cpu) *rec_cpu = next_cpu; return next_record; } return NULL; } /** * tracecmd_read_prev - read the record before the given record * @handle: input handle to the trace.dat file * @record: the record to use to find the previous record. * * This returns the record before the @record on its CPU. If * @record is the first record, NULL is returned. The cursor is set * as if the previous record was read by tracecmd_read_data(). * * @record can not be NULL, otherwise NULL is returned; the * record ownership goes to this function. * * Note, this is not that fast of an algorithm, since it needs * to build the timestamp for the record. * * The record returned must be freed with free_record(). */ struct tep_record * tracecmd_read_prev(struct tracecmd_input *handle, struct tep_record *record) { unsigned long long offset, page_offset;; struct cpu_data *cpu_data; int index; int cpu; if (!record) return NULL; cpu = record->cpu; offset = record->offset; cpu_data = &handle->cpu_data[cpu]; page_offset = calc_page_offset(handle, offset); index = offset - page_offset; /* Note, the record passed in could have been a peek */ free_next(handle, cpu); /* Reset the cursor */ /* Should not happen */ if (get_page(handle, cpu, page_offset) < 0) return NULL; update_page_info(handle, cpu); /* Find the record before this record */ index = 0; for (;;) { record = tracecmd_read_data(handle, cpu); /* Should not happen! */ if (!record) return NULL; if (record->offset == offset) break; index = record->offset - page_offset; free_record(record); } free_record(record); if (index) /* we found our record */ return tracecmd_read_at(handle, page_offset + index, NULL); /* reset the index to start at the beginning of the page */ update_page_info(handle, cpu); /* The previous record is on the previous page */ for (;;) { /* check if this is the first page */ if (page_offset == cpu_data->file_offset) return NULL; page_offset -= handle->page_size; /* Updating page to a new page will reset index to 0 */ get_page(handle, cpu, page_offset); record = NULL; index = 0; do { if (record) { index = record->offset - page_offset; free_record(record); } record = tracecmd_read_data(handle, cpu); /* Should not happen */ if (!record) return NULL; } while (record->offset != offset); free_record(record); if (index) /* we found our record */ return tracecmd_read_at(handle, page_offset + index, NULL); } /* Not reached */ } static int init_cpu(struct tracecmd_input *handle, int cpu) { struct cpu_data *cpu_data = &handle->cpu_data[cpu]; int i; cpu_data->offset = cpu_data->file_offset; cpu_data->size = cpu_data->file_size; cpu_data->timestamp = 0; list_head_init(&cpu_data->page_maps); if (!cpu_data->size) { printf("CPU %d is empty\n", cpu); return 0; } cpu_data->nr_pages = (cpu_data->size + handle->page_size - 1) / handle->page_size; if (!cpu_data->nr_pages) cpu_data->nr_pages = 1; cpu_data->pages = calloc(cpu_data->nr_pages, sizeof(*cpu_data->pages)); if (!cpu_data->pages) return -1; if (handle->use_pipe) { /* Just make a page, it will be nuked later */ cpu_data->page = malloc(sizeof(*cpu_data->page)); if (!cpu_data->page) goto fail; memset(cpu_data->page, 0, sizeof(*cpu_data->page)); cpu_data->pages[0] = cpu_data->page; cpu_data->page_cnt = 1; cpu_data->page->ref_count = 1; return 0; } cpu_data->page = allocate_page(handle, cpu, cpu_data->offset); if (!cpu_data->page && !handle->read_page) { perror("mmap"); fprintf(stderr, "Can not mmap file, will read instead\n"); if (cpu) { /* * If the other CPUs had size and was able to mmap * then bail. */ for (i = 0; i < cpu; i++) { if (handle->cpu_data[i].size) goto fail; } } /* try again without mmapping, just read it directly */ handle->read_page = true; cpu_data->page = allocate_page(handle, cpu, cpu_data->offset); if (!cpu_data->page) /* Still no luck, bail! */ goto fail; } if (update_page_info(handle, cpu)) goto fail; return 0; fail: free(cpu_data->pages); cpu_data->pages = NULL; free(cpu_data->page); cpu_data->page = NULL; return -1; } void tracecmd_set_ts_offset(struct tracecmd_input *handle, unsigned long long offset) { handle->ts_offset = offset; } void tracecmd_set_ts2secs(struct tracecmd_input *handle, unsigned long long hz) { double ts2secs; ts2secs = (double)NSEC_PER_SEC / (double)hz; handle->ts2secs = ts2secs; handle->use_trace_clock = false; } static int handle_options(struct tracecmd_input *handle) { unsigned long long offset; unsigned short option; unsigned int size; char *cpustats = NULL; unsigned int cpustats_size = 0; struct input_buffer_instance *buffer; struct hook_list *hook; char *buf; int cpus; for (;;) { if (do_read_check(handle, &option, 2)) return -1; if (option == TRACECMD_OPTION_DONE) break; /* next 4 bytes is the size of the option */ if (do_read_check(handle, &size, 4)) return -1; size = tep_read_number(handle->pevent, &size, 4); buf = malloc(size); if (!buf) return -ENOMEM; if (do_read_check(handle, buf, size)) return -1; switch (option) { case TRACECMD_OPTION_DATE: /* * A time has been mapped that is the * difference between the timestamps and * gtod. It is stored as ASCII with '0x' * appended. */ if (handle->flags & TRACECMD_FL_IGNORE_DATE) break; offset = strtoll(buf, NULL, 0); /* Convert from micro to nano */ offset *= 1000; handle->ts_offset += offset; break; case TRACECMD_OPTION_OFFSET: /* * Similar to date option, but just adds an * offset to the timestamp. */ if (handle->flags & TRACECMD_FL_IGNORE_DATE) break; offset = strtoll(buf, NULL, 0); handle->ts_offset += offset; break; case TRACECMD_OPTION_CPUSTAT: buf[size-1] = '\n'; cpustats = realloc(cpustats, cpustats_size + size + 1); if (!cpustats) return -ENOMEM; memcpy(cpustats + cpustats_size, buf, size); cpustats_size += size; cpustats[cpustats_size] = 0; break; case TRACECMD_OPTION_BUFFER: /* A buffer instance is saved at the end of the file */ handle->nr_buffers++; handle->buffers = realloc(handle->buffers, sizeof(*handle->buffers) * handle->nr_buffers); if (!handle->buffers) return -ENOMEM; buffer = &handle->buffers[handle->nr_buffers - 1]; buffer->name = strdup(buf + 8); if (!buffer->name) { free(handle->buffers); handle->buffers = NULL; return -ENOMEM; } offset = *(unsigned long long *)buf; buffer->offset = tep_read_number(handle->pevent, &offset, 8); break; case TRACECMD_OPTION_TRACECLOCK: if (!handle->ts2secs) handle->use_trace_clock = true; break; case TRACECMD_OPTION_UNAME: handle->uname = strdup(buf); break; case TRACECMD_OPTION_VERSION: handle->version = strdup(buf); break; case TRACECMD_OPTION_HOOK: hook = tracecmd_create_event_hook(buf); hook->next = handle->hooks; handle->hooks = hook; break; case TRACECMD_OPTION_CPUCOUNT: cpus = *(int *)buf; handle->cpus = tep_read_number(handle->pevent, &cpus, 4); break; default: warning("unknown option %d", option); break; } free(buf); } handle->cpustats = cpustats; return 0; } static int read_cpu_data(struct tracecmd_input *handle) { struct tep_handle *pevent = handle->pevent; enum kbuffer_long_size long_size; enum kbuffer_endian endian; unsigned long long size; unsigned long long max_size = 0; unsigned long long pages; char buf[10]; int cpus; int cpu; if (do_read_check(handle, buf, 10)) return -1; cpus = handle->cpus; /* check if this handles options */ if (strncmp(buf, "options", 7) == 0) { if (handle_options(handle) < 0) return -1; if (do_read_check(handle, buf, 10)) return -1; } /* * Check if this is a latency report or not. */ if (strncmp(buf, "latency", 7) == 0) { handle->flags |= TRACECMD_FL_LATENCY; return 1; } /* We expect this to be flyrecord */ if (strncmp(buf, "flyrecord", 9) != 0) return -1; handle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus); if (!handle->cpu_data) return -1; memset(handle->cpu_data, 0, sizeof(*handle->cpu_data) * handle->cpus); if (force_read) handle->read_page = true; if (handle->long_size == 8) long_size = KBUFFER_LSIZE_8; else long_size = KBUFFER_LSIZE_4; if (tep_is_file_bigendian(handle->pevent)) endian = KBUFFER_ENDIAN_BIG; else endian = KBUFFER_ENDIAN_LITTLE; for (cpu = 0; cpu < handle->cpus; cpu++) { unsigned long long offset; handle->cpu_data[cpu].cpu = cpu; handle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian); if (!handle->cpu_data[cpu].kbuf) goto out_free; if (tep_is_old_format(pevent)) kbuffer_set_old_format(handle->cpu_data[cpu].kbuf); read8(handle, &offset); read8(handle, &size); handle->cpu_data[cpu].file_offset = offset; handle->cpu_data[cpu].file_size = size; if (size > max_size) max_size = size; if (size && (offset + size > handle->total_file_size)) { /* this happens if the file got truncated */ printf("File possibly truncated. " "Need at least %llu, but file size is %zu.\n", offset + size, handle->total_file_size); errno = EINVAL; goto out_free; } } /* Calculate about a meg of pages for buffering */ pages = handle->page_size ? max_size / handle->page_size : 0; if (!pages) pages = 1; pages = normalize_size(pages); handle->page_map_size = handle->page_size * pages; if (handle->page_map_size < handle->page_size) handle->page_map_size = handle->page_size; for (cpu = 0; cpu < handle->cpus; cpu++) { if (init_cpu(handle, cpu)) goto out_free; } /* * It is possible that an option changed the number of CPUs. * If that happened, then there's "empty" cpu data saved for * backward compatibility. */ if (cpus < handle->cpus) { unsigned long long ignore; int once = 0; read8(handle, &ignore); /* offset */ read8(handle, &ignore); /* size */ if (ignore != 0) { if (!once) { warning("ignored CPU data not zero size"); once++; } } } return 0; out_free: for ( ; cpu >= 0; cpu--) { free_page(handle, cpu); kbuffer_free(handle->cpu_data[cpu].kbuf); handle->cpu_data[cpu].kbuf = NULL; } return -1; } static int read_data_and_size(struct tracecmd_input *handle, char **data, unsigned long long *size) { if (read8(handle, size) < 0) return -1; *data = malloc(*size + 1); if (!*data) return -1; if (do_read_check(handle, *data, *size)) { free(*data); return -1; } return 0; } static int read_and_parse_cmdlines(struct tracecmd_input *handle) { struct tep_handle *pevent = handle->pevent; unsigned long long size; char *cmdlines; if (read_data_and_size(handle, &cmdlines, &size) < 0) return -1; cmdlines[size] = 0; tracecmd_parse_cmdlines(pevent, cmdlines, size); free(cmdlines); return 0; } static int read_and_parse_trace_clock(struct tracecmd_input *handle, struct tep_handle *pevent) { unsigned long long size; char *trace_clock; if (read_data_and_size(handle, &trace_clock, &size) < 0) return -1; trace_clock[size] = 0; tracecmd_parse_trace_clock(pevent, trace_clock, size); free(trace_clock); return 0; } /** * tracecmd_init_data - prepare reading the data from trace.dat * @handle: input handle for the trace.dat file * * This prepares reading the data from trace.dat. This is called * after tracecmd_read_headers() and before tracecmd_read_data(). */ int tracecmd_init_data(struct tracecmd_input *handle) { struct tep_handle *pevent = handle->pevent; unsigned int cpus; int ret; if (read4(handle, &cpus) < 0) return -1; handle->cpus = cpus; tep_set_cpus(pevent, handle->cpus); ret = read_cpu_data(handle); if (ret < 0) return ret; if (handle->use_trace_clock) { /* * There was a bug in the original setting of * the trace_clock file which let it get * corrupted. If it fails to read, force local * clock. */ if (read_and_parse_trace_clock(handle, pevent) < 0) { char clock[] = "[local]"; warning("File has trace_clock bug, using local clock"); tracecmd_parse_trace_clock(pevent, clock, 8); } } tracecmd_blk_hack(handle); return ret; } /** * tracecmd_make_pipe - Have the handle read a pipe instead of a file * @handle: input handle to read from a pipe * @cpu: the cpu that the pipe represents * @fd: the read end of the pipe * @cpus: the total number of cpus for this handle * * In order to stream data from the binary trace files and produce * output or analyze the data, a tracecmd_input descriptor needs to * be created, and then converted into a form that can act on a * pipe. * * Note, there are limitations to what this descriptor can do. * Most notibly, it can not read backwards. Once a page is read * it can not be read at a later time (except if a record is attached * to it and is holding the page ref). * * It is expected that the handle has already been created and * tracecmd_read_headers() has run on it. */ int tracecmd_make_pipe(struct tracecmd_input *handle, int cpu, int fd, int cpus) { enum kbuffer_long_size long_size; enum kbuffer_endian endian; handle->read_page = true; handle->use_pipe = true; if (!handle->cpus) { handle->cpus = cpus; handle->cpu_data = malloc(sizeof(*handle->cpu_data) * handle->cpus); if (!handle->cpu_data) return -1; } if (cpu >= handle->cpus) return -1; if (handle->long_size == 8) long_size = KBUFFER_LSIZE_8; else long_size = KBUFFER_LSIZE_4; if (tep_is_file_bigendian(handle->pevent)) endian = KBUFFER_ENDIAN_BIG; else endian = KBUFFER_ENDIAN_LITTLE; memset(&handle->cpu_data[cpu], 0, sizeof(handle->cpu_data[cpu])); handle->cpu_data[cpu].pipe_fd = fd; handle->cpu_data[cpu].cpu = cpu; handle->cpu_data[cpu].kbuf = kbuffer_alloc(long_size, endian); if (!handle->cpu_data[cpu].kbuf) return -1; if (tep_is_old_format(handle->pevent)) kbuffer_set_old_format(handle->cpu_data[cpu].kbuf); handle->cpu_data[cpu].file_offset = 0; handle->cpu_data[cpu].file_size = -1; init_cpu(handle, cpu); return 0; } /** * tracecmd_print_events - print the events that are stored in trace.dat * @handle: input handle for the trace.dat file * @regex: regex of events to print (NULL is all events) * * This is a debugging routine to print out the events that * are stored in a given trace.dat file. */ void tracecmd_print_events(struct tracecmd_input *handle, const char *regex) { int ret; if (!regex) regex = ".*"; if (!handle->ftrace_files_start) { lseek64(handle->fd, handle->header_files_start, SEEK_SET); read_header_files(handle); } ret = read_ftrace_files(handle, regex); if (ret < 0) return; read_event_files(handle, regex); return; } /* Show the cpu data stats */ static void show_cpu_stats(struct tracecmd_input *handle) { struct cpu_data *cpu_data; int i; for (i = 0; i < handle->cpus; i++) { cpu_data = &handle->cpu_data[i]; printf("CPU%d data recorded at offset=0x%llx\n", i, cpu_data->file_offset); printf(" %lld bytes in size\n", cpu_data->file_size); } } /** * tracecmd_print_stats - prints the stats recorded in the options. * @handle: input handle for the trace.dat file * * Looks for the option TRACECMD_OPTION_CPUSTAT and prints out what's * stored there, if it is found. Otherwise it prints that none were found. */ void tracecmd_print_stats(struct tracecmd_input *handle) { if (handle->cpustats) printf("%s\n", handle->cpustats); else printf(" No stats in this file\n"); show_cpu_stats(handle); } /** * tracecmd_print_uname - prints the recorded uname if it was recorded * @handle: input handle for the trace.dat file * * Looks for the option TRACECMD_OPTION_UNAME and prints out what's * stored there, if it is found. Otherwise it prints that none were found. */ void tracecmd_print_uname(struct tracecmd_input *handle) { if (handle->uname) printf("%s\n", handle->uname); else printf(" uname was not recorded in this file\n"); } /** * tracecmd_print_uname - prints the recorded uname if it was recorded * @handle: input handle for the trace.dat file * * Looks for the option TRACECMD_OPTION_VERSION and prints out what's * stored there, if it is found. Otherwise it prints that none were found. */ void tracecmd_print_version(struct tracecmd_input *handle) { if (handle->version) printf("%s\n", handle->version); else printf(" version was not recorded in this file\n"); } /** * tracecmd_hooks - return the event hooks that were used in record * @handle: input handle for the trace.dat file * * If trace-cmd record used -H to save hooks, they are parsed and * presented as hooks here. * * Returns the hook list (do not free it, they are freed on close) */ struct hook_list *tracecmd_hooks(struct tracecmd_input *handle) { return handle->hooks; } /** * tracecmd_alloc_fd - create a tracecmd_input handle from a file descriptor * @fd: the file descriptor for the trace.dat file * * Allocate a tracecmd_input handle from a file descriptor and open the * file. This tests if the file is of trace-cmd format and allocates * a parse event descriptor. * * The returned pointer is not ready to be read yet. A tracecmd_read_headers() * and tracecmd_init_data() still need to be called on the descriptor. * * Unless you know what you are doing with this, you want to use * tracecmd_open_fd() instead. */ struct tracecmd_input *tracecmd_alloc_fd(int fd) { struct tracecmd_input *handle; char test[] = { 23, 8, 68 }; unsigned int page_size; char *version; char buf[BUFSIZ]; handle = malloc(sizeof(*handle)); if (!handle) return NULL; memset(handle, 0, sizeof(*handle)); handle->fd = fd; handle->ref = 1; if (do_read_check(handle, buf, 3)) goto failed_read; if (memcmp(buf, test, 3) != 0) goto failed_read; if (do_read_check(handle, buf, 7)) goto failed_read; if (memcmp(buf, "tracing", 7) != 0) goto failed_read; version = read_string(handle); if (!version) goto failed_read; pr_stat("version = %s\n", version); free(version); if (do_read_check(handle, buf, 1)) goto failed_read; handle->pevent = tep_alloc(); if (!handle->pevent) goto failed_read; /* register default ftrace functions first */ tracecmd_ftrace_overrides(handle, &handle->finfo); handle->plugin_list = tracecmd_load_plugins(handle->pevent); tep_set_file_bigendian(handle->pevent, buf[0]); tep_set_local_bigendian(handle->pevent, tracecmd_host_bigendian()); do_read_check(handle, buf, 1); handle->long_size = buf[0]; read4(handle, &page_size); handle->page_size = page_size; handle->header_files_start = lseek64(handle->fd, 0, SEEK_CUR); handle->total_file_size = lseek64(handle->fd, 0, SEEK_END); handle->header_files_start = lseek64(handle->fd, handle->header_files_start, SEEK_SET); return handle; failed_read: free(handle); return NULL; } /** * tracecmd_alloc_fd - create a tracecmd_input handle from a file name * @file: the file name of the file that is of tracecmd data type. * * Allocate a tracecmd_input handle from a given file name and open the * file. This tests if the file is of trace-cmd format and allocates * a parse event descriptor. * * The returned pointer is not ready to be read yet. A tracecmd_read_headers() * and tracecmd_init_data() still need to be called on the descriptor. * * Unless you know what you are doing with this, you want to use * tracecmd_open() instead. */ struct tracecmd_input *tracecmd_alloc(const char *file) { int fd; fd = open(file, O_RDONLY); if (fd < 0) return NULL; return tracecmd_alloc_fd(fd); } /** * tracecmd_open_fd - create a tracecmd_handle from the trace.dat file descriptor * @fd: the file descriptor for the trace.dat file */ struct tracecmd_input *tracecmd_open_fd(int fd) { struct tracecmd_input *handle; int ret; handle = tracecmd_alloc_fd(fd); if (!handle) return NULL; if (tracecmd_read_headers(handle) < 0) goto fail; if ((ret = tracecmd_init_data(handle)) < 0) goto fail; return handle; fail: tracecmd_close(handle); return NULL; } /** * tracecmd_open - create a tracecmd_handle from a given file * @file: the file name of the file that is of tracecmd data type. */ struct tracecmd_input *tracecmd_open(const char *file) { int fd; fd = open(file, O_RDONLY); if (fd < 0) return NULL; return tracecmd_open_fd(fd); } /** * tracecmd_ref - add a reference to the handle * @handle: input handle for the trace.dat file * * Some applications may share a handle between parts of * the application. Let those parts add reference counters * to the handle, and the last one to close it will free it. */ void tracecmd_ref(struct tracecmd_input *handle) { if (!handle) return; handle->ref++; } /** * tracecmd_close - close and free the trace.dat handle * @handle: input handle for the trace.dat file * * Close the file descriptor of the handle and frees * the resources allocated by the handle. */ void tracecmd_close(struct tracecmd_input *handle) { int cpu; if (!handle) return; if (handle->ref <= 0) { warning("tracecmd: bad ref count on handle\n"); return; } if (--handle->ref) return; for (cpu = 0; cpu < handle->cpus; cpu++) { /* The tracecmd_peek_data may have cached a record */ free_next(handle, cpu); free_page(handle, cpu); if (handle->cpu_data && handle->cpu_data[cpu].kbuf) { kbuffer_free(handle->cpu_data[cpu].kbuf); if (handle->cpu_data[cpu].page_map) free_page_map(handle->cpu_data[cpu].page_map); if (handle->cpu_data[cpu].page_cnt) warning("%d pages still allocated on cpu %d%s", handle->cpu_data[cpu].page_cnt, cpu, show_records(handle->cpu_data[cpu].pages, handle->cpu_data[cpu].nr_pages)); free(handle->cpu_data[cpu].pages); } } free(handle->cpustats); free(handle->cpu_data); free(handle->uname); close(handle->fd); tracecmd_free_hooks(handle->hooks); handle->hooks = NULL; if (handle->flags & TRACECMD_FL_BUFFER_INSTANCE) tracecmd_close(handle->parent); else { /* Only main handle frees plugins and pevent */ tracecmd_unload_plugins(handle->plugin_list, handle->pevent); tep_free(handle->pevent); } free(handle); } static int read_copy_size8(struct tracecmd_input *handle, int fd, unsigned long long *size) { /* read size */ if (do_read_check(handle, size, 8)) return -1; if (__do_write_check(fd, size, 8)) return -1; *size = tep_read_number(handle->pevent, size, 8); return 0; } static int read_copy_size4(struct tracecmd_input *handle, int fd, unsigned int *size) { /* read size */ if (do_read_check(handle, size, 4)) return -1; if (__do_write_check(fd, size, 4)) return -1; *size = tep_read_number(handle->pevent, size, 4); return 0; } static int read_copy_data(struct tracecmd_input *handle, unsigned long long size, int fd) { char *buf; buf = malloc(size); if (!buf) return -1; if (do_read_check(handle, buf, size)) goto failed_read; if (__do_write_check(fd, buf, size)) goto failed_read; free(buf); return 0; failed_read: free(buf); return -1; } static int copy_header_files(struct tracecmd_input *handle, int fd) { unsigned long long size; lseek64(handle->fd, handle->header_files_start, SEEK_SET); /* "header_page" */ if (read_copy_data(handle, 12, fd) < 0) return -1; if (read_copy_size8(handle, fd, &size) < 0) return -1; if (read_copy_data(handle, size, fd) < 0) return -1; /* "header_event" */ if (read_copy_data(handle, 13, fd) < 0) return -1; if (read_copy_size8(handle, fd, &size) < 0) return -1; if (read_copy_data(handle, size, fd) < 0) return -1; return 0; } static int copy_ftrace_files(struct tracecmd_input *handle, int fd) { unsigned long long size; unsigned int count; unsigned int i; if (read_copy_size4(handle, fd, &count) < 0) return -1; for (i = 0; i < count; i++) { if (read_copy_size8(handle, fd, &size) < 0) return -1; if (read_copy_data(handle, size, fd) < 0) return -1; } return 0; } static int copy_event_files(struct tracecmd_input *handle, int fd) { unsigned long long size; char *system; unsigned int systems; unsigned int count; unsigned int i,x; if (read_copy_size4(handle, fd, &systems) < 0) return -1; for (i = 0; i < systems; i++) { system = read_string(handle); if (!system) return -1; if (__do_write_check(fd, system, strlen(system) + 1)) { free(system); return -1; } free(system); if (read_copy_size4(handle, fd, &count) < 0) return -1; for (x=0; x < count; x++) { if (read_copy_size8(handle, fd, &size) < 0) return -1; if (read_copy_data(handle, size, fd) < 0) return -1; } } return 0; } static int copy_proc_kallsyms(struct tracecmd_input *handle, int fd) { unsigned int size; if (read_copy_size4(handle, fd, &size) < 0) return -1; if (!size) return 0; /* OK? */ if (read_copy_data(handle, size, fd) < 0) return -1; return 0; } static int copy_ftrace_printk(struct tracecmd_input *handle, int fd) { unsigned int size; if (read_copy_size4(handle, fd, &size) < 0) return -1; if (!size) return 0; /* OK? */ if (read_copy_data(handle, size, fd) < 0) return -1; return 0; } static int copy_command_lines(struct tracecmd_input *handle, int fd) { unsigned long long size; if (read_copy_size8(handle, fd, &size) < 0) return -1; if (!size) return 0; /* OK? */ if (read_copy_data(handle, size, fd) < 0) return -1; return 0; } int tracecmd_copy_headers(struct tracecmd_input *handle, int fd) { int ret; ret = copy_header_files(handle, fd); if (ret < 0) return -1; ret = copy_ftrace_files(handle, fd); if (ret < 0) return -1; ret = copy_event_files(handle, fd); if (ret < 0) return -1; ret = copy_proc_kallsyms(handle, fd); if (ret < 0) return -1; ret = copy_ftrace_printk(handle, fd); if (ret < 0) return -1; ret = copy_command_lines(handle, fd); if (ret < 0) return -1; return 0; } /** * tracecmd_record_at_buffer_start - return true if record is first on subbuffer * @handle: input handle for the trace.dat file * @record: The record to test if it is the first record on page * * Returns true if the record is the first record on the page. */ int tracecmd_record_at_buffer_start(struct tracecmd_input *handle, struct tep_record *record) { struct page *page = record->priv; struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf; int offset; if (!page || !kbuf) return 0; offset = record->offset - page->offset; return offset == kbuffer_start_of_data(kbuf); } unsigned long long tracecmd_page_ts(struct tracecmd_input *handle, struct tep_record *record) { struct page *page = record->priv; struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf; if (!page || !kbuf) return 0; return kbuffer_subbuf_timestamp(kbuf, page->map); } unsigned int tracecmd_record_ts_delta(struct tracecmd_input *handle, struct tep_record *record) { struct kbuffer *kbuf = handle->cpu_data[record->cpu].kbuf; struct page *page = record->priv; int offset; if (!page || !kbuf) return 0; offset = record->offset - page->offset; return kbuffer_ptr_delta(kbuf, page->map + offset); } struct kbuffer *tracecmd_record_kbuf(struct tracecmd_input *handle, struct tep_record *record) { return handle->cpu_data[record->cpu].kbuf; } void *tracecmd_record_page(struct tracecmd_input *handle, struct tep_record *record) { struct page *page = record->priv; return page ? page->map : NULL; } void *tracecmd_record_offset(struct tracecmd_input *handle, struct tep_record *record) { struct page *page = record->priv; int offset; if (!page) return NULL; offset = record->offset - page->offset; return page->map + offset; } int tracecmd_buffer_instances(struct tracecmd_input *handle) { return handle->nr_buffers; } const char *tracecmd_buffer_instance_name(struct tracecmd_input *handle, int indx) { if (indx >= handle->nr_buffers) return NULL; return handle->buffers[indx].name; } struct tracecmd_input * tracecmd_buffer_instance_handle(struct tracecmd_input *handle, int indx) { struct tracecmd_input *new_handle; struct input_buffer_instance *buffer = &handle->buffers[indx]; size_t offset; ssize_t ret; if (indx >= handle->nr_buffers) return NULL; /* * We make a copy of the current handle, but we substitute * the cpu data with the cpu data for this buffer. */ new_handle = malloc(sizeof(*handle)); if (!new_handle) return NULL; *new_handle = *handle; new_handle->cpu_data = NULL; new_handle->nr_buffers = 0; new_handle->buffers = NULL; new_handle->ref = 1; new_handle->parent = handle; new_handle->cpustats = NULL; new_handle->hooks = NULL; if (handle->uname) /* Ignore if fails to malloc, no biggy */ new_handle->uname = strdup(handle->uname); tracecmd_ref(handle); new_handle->fd = dup(handle->fd); new_handle->flags |= TRACECMD_FL_BUFFER_INSTANCE; /* Save where we currently are */ offset = lseek64(handle->fd, 0, SEEK_CUR); ret = lseek64(handle->fd, buffer->offset, SEEK_SET); if (ret < 0) { warning("could not seek to buffer %s offset %ld\n", buffer->name, buffer->offset); tracecmd_close(new_handle); return NULL; } ret = read_cpu_data(new_handle); if (ret < 0) { warning("failed to read sub buffer %s\n", buffer->name); tracecmd_close(new_handle); return NULL; } ret = lseek64(handle->fd, offset, SEEK_SET); if (ret < 0) { warning("could not seek to back to offset %ld\n", offset); tracecmd_close(new_handle); return NULL; } return new_handle; } int tracecmd_is_buffer_instance(struct tracecmd_input *handle) { return handle->flags & TRACECMD_FL_BUFFER_INSTANCE; } /** * tracecmd_long_size - return the size of "long" for the arch * @handle: input handle for the trace.dat file */ int tracecmd_long_size(struct tracecmd_input *handle) { return handle->long_size; } /** * tracecmd_page_size - return the PAGE_SIZE for the arch * @handle: input handle for the trace.dat file */ int tracecmd_page_size(struct tracecmd_input *handle) { return handle->page_size; } /** * tracecmd_page_size - return the number of CPUs recorded * @handle: input handle for the trace.dat file */ int tracecmd_cpus(struct tracecmd_input *handle) { return handle->cpus; } /** * tracecmd_get_pevent - return the pevent handle * @handle: input handle for the trace.dat file */ struct tep_handle *tracecmd_get_pevent(struct tracecmd_input *handle) { return handle->pevent; } /** * tracecmd_get_use_trace_clock - return use_trace_clock * @handle: input handle for the trace.dat file */ bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle) { return handle->use_trace_clock; } /** * tracecmd_get_show_data_func - return the show data func * @handle: input handle for the trace.dat file */ tracecmd_show_data_func tracecmd_get_show_data_func(struct tracecmd_input *handle) { return handle->show_data_func; } /** * tracecmd_set_show_data_func - set the show data func * @handle: input handle for the trace.dat file */ void tracecmd_set_show_data_func(struct tracecmd_input *handle, tracecmd_show_data_func func) { handle->show_data_func = func; } trace-cmd-2.8.3/lib/trace-cmd/trace-recorder.c000066400000000000000000000243351351617527000210530ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include "trace-cmd.h" #include "event-utils.h" /* F_GETPIPE_SZ was introduced in 2.6.35, older systems don't have it */ #ifndef F_GETPIPE_SZ # define F_GETPIPE_SZ 1032 /* The Linux number for the option */ #endif #ifndef SPLICE_F_MOVE # define SPLICE_F_MOVE 1 # define SPLICE_F_NONBLOCK 2 # define SPLICE_F_MORE 4 # define SPLICE_F_GIFT 8 #endif struct tracecmd_recorder { int fd; int fd1; int fd2; int trace_fd; int brass[2]; int pipe_size; int page_size; int cpu; int stop; int max; int pages; int count; unsigned fd_flags; unsigned flags; }; static int append_file(int size, int dst, int src) { char buf[size]; int r; lseek64(src, 0, SEEK_SET); /* If there's an error, then we are pretty much screwed :-p */ do { r = read(src, buf, size); if (r < 0) return r; r = write(dst, buf, r); if (r < 0) return r; } while (r); return 0; } void tracecmd_free_recorder(struct tracecmd_recorder *recorder) { if (!recorder) return; if (recorder->max) { /* Need to put everything into fd1 */ if (recorder->fd == recorder->fd1) { int ret; /* * Crap, the older data is in fd2, and we need * to append fd1 onto it, and then copy over to fd1 */ ret = append_file(recorder->page_size, recorder->fd2, recorder->fd1); /* Error on copying, then just keep fd1 */ if (ret) { lseek64(recorder->fd1, 0, SEEK_END); goto close; } lseek64(recorder->fd1, 0, SEEK_SET); ftruncate(recorder->fd1, 0); } append_file(recorder->page_size, recorder->fd1, recorder->fd2); } close: if (recorder->brass[0] >= 0) close(recorder->brass[0]); if (recorder->brass[1] >= 0) close(recorder->brass[1]); if (recorder->trace_fd >= 0) close(recorder->trace_fd); if (recorder->fd1 >= 0) close(recorder->fd1); if (recorder->fd2 >= 0) close(recorder->fd2); free(recorder); } struct tracecmd_recorder * tracecmd_create_buffer_recorder_fd2(int fd, int fd2, int cpu, unsigned flags, const char *buffer, int maxkb) { struct tracecmd_recorder *recorder; char *path = NULL; int pipe_size = 0; int ret; recorder = malloc(sizeof(*recorder)); if (!recorder) return NULL; recorder->cpu = cpu; recorder->flags = flags; recorder->fd_flags = SPLICE_F_MOVE; if (!(recorder->flags & TRACECMD_RECORD_BLOCK)) recorder->fd_flags |= SPLICE_F_NONBLOCK; /* Init to know what to free and release */ recorder->trace_fd = -1; recorder->brass[0] = -1; recorder->brass[1] = -1; recorder->page_size = getpagesize(); if (maxkb) { int kb_per_page = recorder->page_size >> 10; if (!kb_per_page) kb_per_page = 1; recorder->max = maxkb / kb_per_page; /* keep max half */ recorder->max >>= 1; if (!recorder->max) recorder->max = 1; } else recorder->max = 0; recorder->count = 0; recorder->pages = 0; /* fd always points to what to write to */ recorder->fd = fd; recorder->fd1 = fd; recorder->fd2 = fd2; if (flags & TRACECMD_RECORD_SNAPSHOT) ret = asprintf(&path, "%s/per_cpu/cpu%d/snapshot_raw", buffer, cpu); else ret = asprintf(&path, "%s/per_cpu/cpu%d/trace_pipe_raw", buffer, cpu); if (ret < 0) goto out_free; recorder->trace_fd = open(path, O_RDONLY); if (recorder->trace_fd < 0) goto out_free; if ((recorder->flags & TRACECMD_RECORD_NOSPLICE) == 0) { ret = pipe(recorder->brass); if (ret < 0) goto out_free; ret = fcntl(recorder->brass[0], F_GETPIPE_SZ, &pipe_size); /* * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced * in 2.6.31. If we are running on an older kernel, just fall * back to using page_size for splice(). It could also return * success, but not modify pipe_size. */ if (ret < 0 || !pipe_size) pipe_size = recorder->page_size; recorder->pipe_size = pipe_size; } free(path); return recorder; out_free: free(path); tracecmd_free_recorder(recorder); return NULL; } struct tracecmd_recorder * tracecmd_create_buffer_recorder_fd(int fd, int cpu, unsigned flags, const char *buffer) { return tracecmd_create_buffer_recorder_fd2(fd, -1, cpu, flags, buffer, 0); } struct tracecmd_recorder * tracecmd_create_buffer_recorder(const char *file, int cpu, unsigned flags, const char *buffer) { struct tracecmd_recorder *recorder; int fd; fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if (fd < 0) return NULL; recorder = tracecmd_create_buffer_recorder_fd(fd, cpu, flags, buffer); if (!recorder) { close(fd); unlink(file); } return recorder; } struct tracecmd_recorder * tracecmd_create_buffer_recorder_maxkb(const char *file, int cpu, unsigned flags, const char *buffer, int maxkb) { struct tracecmd_recorder *recorder = NULL; char *file2; int len; int fd; int fd2; if (!maxkb) return tracecmd_create_buffer_recorder(file, cpu, flags, buffer); len = strlen(file); file2 = malloc(len + 3); if (!file2) return NULL; sprintf(file2, "%s.1", file); fd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if (fd < 0) goto out; fd2 = open(file2, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if (fd < 0) goto err; recorder = tracecmd_create_buffer_recorder_fd2(fd, fd2, cpu, flags, buffer, maxkb); if (!recorder) goto err2; out: /* Unlink file2, we need to add everything to file at the end */ unlink(file2); free(file2); return recorder; err2: close(fd2); err: close(fd); unlink(file); goto out; } struct tracecmd_recorder *tracecmd_create_recorder_fd(int fd, int cpu, unsigned flags) { const char *tracing; tracing = tracecmd_get_tracing_dir(); if (!tracing) { errno = ENODEV; return NULL; } return tracecmd_create_buffer_recorder_fd(fd, cpu, flags, tracing); } struct tracecmd_recorder *tracecmd_create_recorder(const char *file, int cpu, unsigned flags) { const char *tracing; tracing = tracecmd_get_tracing_dir(); if (!tracing) { errno = ENODEV; return NULL; } return tracecmd_create_buffer_recorder(file, cpu, flags, tracing); } struct tracecmd_recorder * tracecmd_create_recorder_maxkb(const char *file, int cpu, unsigned flags, int maxkb) { const char *tracing; tracing = tracecmd_get_tracing_dir(); if (!tracing) { errno = ENODEV; return NULL; } return tracecmd_create_buffer_recorder_maxkb(file, cpu, flags, tracing, maxkb); } static inline void update_fd(struct tracecmd_recorder *recorder, int size) { int fd; if (!recorder->max) return; recorder->count += size; if (recorder->count >= recorder->page_size) { recorder->count = 0; recorder->pages++; } if (recorder->pages < recorder->max) return; recorder->pages = 0; fd = recorder->fd; /* Swap fd to next file. */ if (fd == recorder->fd1) fd = recorder->fd2; else fd = recorder->fd1; /* Zero out the new file we are writing to */ lseek64(fd, 0, SEEK_SET); ftruncate(fd, 0); recorder->fd = fd; } /* * Returns -1 on error. * or bytes of data read. */ static long splice_data(struct tracecmd_recorder *recorder) { long total_read = 0; long read; long ret; read = splice(recorder->trace_fd, NULL, recorder->brass[1], NULL, recorder->pipe_size, SPLICE_F_MOVE); if (read < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in splice input"); return -1; } return 0; } else if (read == 0) return 0; again: ret = splice(recorder->brass[0], NULL, recorder->fd, NULL, read, recorder->fd_flags); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in splice output"); return -1; } return total_read; } else update_fd(recorder, ret); total_read = ret; read -= ret; if (read) goto again; return total_read; } /* * Returns -1 on error. * or bytes of data read. */ static long read_data(struct tracecmd_recorder *recorder) { char buf[recorder->page_size]; long left; long r, w; r = read(recorder->trace_fd, buf, recorder->page_size); if (r < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in read output"); return -1; } return 0; } left = r; do { w = write(recorder->fd, buf + (r - left), left); if (w > 0) { left -= w; update_fd(recorder, w); } } while (w >= 0 && left); if (w < 0) r = w; return r; } static void set_nonblock(struct tracecmd_recorder *recorder) { long flags; /* Do not block on reads for flushing */ flags = fcntl(recorder->trace_fd, F_GETFL); fcntl(recorder->trace_fd, F_SETFL, flags | O_NONBLOCK); /* Do not block on streams for write */ recorder->fd_flags |= SPLICE_F_NONBLOCK; } long tracecmd_flush_recording(struct tracecmd_recorder *recorder) { char buf[recorder->page_size]; long total = 0; long wrote = 0; long ret; set_nonblock(recorder); do { if (recorder->flags & TRACECMD_RECORD_NOSPLICE) ret = read_data(recorder); else ret = splice_data(recorder); if (ret < 0) return ret; total += ret; } while (ret); /* splice only reads full pages */ do { ret = read(recorder->trace_fd, buf, recorder->page_size); if (ret > 0) { write(recorder->fd, buf, ret); wrote += ret; } } while (ret > 0); /* Make sure we finish off with a page size boundary */ wrote &= recorder->page_size - 1; if (wrote) { memset(buf, 0, recorder->page_size); write(recorder->fd, buf, recorder->page_size - wrote); total += recorder->page_size; } return total; } int tracecmd_start_recording(struct tracecmd_recorder *recorder, unsigned long sleep) { struct timespec req = { .tv_sec = sleep / 1000000, .tv_nsec = (sleep % 1000000) * 1000, }; long read = 1; long ret; recorder->stop = 0; do { /* Only sleep if we did not read anything last time */ if (!read && sleep) nanosleep(&req, NULL); read = 0; do { if (recorder->flags & TRACECMD_RECORD_NOSPLICE) ret = read_data(recorder); else ret = splice_data(recorder); if (ret < 0) return ret; read += ret; } while (ret); } while (!recorder->stop); /* Flush out the rest */ ret = tracecmd_flush_recording(recorder); if (ret < 0) return ret; return 0; } void tracecmd_stop_recording(struct tracecmd_recorder *recorder) { if (!recorder) return; set_nonblock(recorder); recorder->stop = 1; } trace-cmd-2.8.3/lib/trace-cmd/trace-util.c000066400000000000000000001026461351617527000202250ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1 /* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd.h" #include "event-utils.h" #define LOCAL_PLUGIN_DIR ".trace-cmd/plugins" #define TRACEFS_PATH "/sys/kernel/tracing" #define DEBUGFS_PATH "/sys/kernel/debug" int tracecmd_disable_sys_plugins; int tracecmd_disable_plugins; static struct registered_plugin_options { struct registered_plugin_options *next; struct tep_plugin_option *options; } *registered_options; static struct trace_plugin_options { struct trace_plugin_options *next; char *plugin; char *option; char *value; } *trace_plugin_options; #define _STR(x) #x #define STR(x) _STR(x) struct tep_plugin_list { struct tep_plugin_list *next; char *name; void *handle; }; /** * trace_util_list_plugin_options - get list of plugin options * * Returns an array of char strings that list the currently registered * plugin options in the format of :