pax_global_header00006660000000000000000000000064124670120310014506gustar00rootroot0000000000000052 comment=0813335d2e49291e7ab6a4365cec2baa05813eda trace-cmd-2.5.3/000077500000000000000000000000001246701203100133545ustar00rootroot00000000000000trace-cmd-2.5.3/COPYING000066400000000000000000000431031246701203100144100ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 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. 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 Lesser 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 Street, 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 Lesser General Public License instead of this License. trace-cmd-2.5.3/COPYING.LIB000066400000000000000000000636501246701203100150260ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.5.3/Documentation/000077500000000000000000000000001246701203100161655ustar00rootroot00000000000000trace-cmd-2.5.3/Documentation/HTML/000077500000000000000000000000001246701203100167315ustar00rootroot00000000000000trace-cmd-2.5.3/Documentation/HTML/images/000077500000000000000000000000001246701203100201765ustar00rootroot00000000000000trace-cmd-2.5.3/Documentation/HTML/images/kshark-cursor-1.png000066400000000000000000001672661246701203100236620ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-advance-1.png000066400000000000000000000521711246701203100252150ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-del-adv.png000066400000000000000000000523421246701203100247720ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-event-adv-list.png000066400000000000000000000503021246701203100263120ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-events-sched.png000066400000000000000000000461641246701203100260530ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-events.png000066400000000000000000000471721246701203100247670ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-list-adv-irq.png000066400000000000000000000504651246701203100257760ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-sync-graph-1.png000066400000000000000000002013031246701203100256600ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter-task-menu.png000066400000000000000000002434561246701203100253720ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-filter.png000066400000000000000000002001331246701203100234510ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-graph-info-line.png000066400000000000000000000046541246701203100251550ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-graph-plot-area.png000066400000000000000000000121321246701203100251470ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-graph-plot-title.png000066400000000000000000000021701246701203100253610ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-list-adjust.png000066400000000000000000002343611246701203100244410ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-list-enable-filter-1.png000066400000000000000000002432721246701203100260170ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-list-graph-follow-1.png000066400000000000000000002405341246701203100257050ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-list-graph-follow-2.png000066400000000000000000002373471246701203100257160ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-list-info-area.png000066400000000000000000000117331246701203100250040ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-open.png000066400000000000000000001623141246701203100231350ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-cpu-1.png000066400000000000000000000203011246701203100240620ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-cpu-2.png000066400000000000000000000204051246701203100240700ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-cpu-result.png000066400000000000000000001636761246701203100252670ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-task-measure.png000066400000000000000000002002111246701203100255360ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-task-result.png000066400000000000000000002001071246701203100254170ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-plot-task-zoom-1.png000066400000000000000000002016431246701203100252310ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-select-b-1.png000066400000000000000000001640101246701203100240230ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-unsync-events.png000066400000000000000000001770241246701203100250210ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-zoom-in-3.png000066400000000000000000001632141246701203100237240ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-zoom-in-select.png000066400000000000000000001654561246701203100250530ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/images/kshark-zoom-out-select.png000066400000000000000000001725451246701203100252510ustar00rootroot00000000000000PNG  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.5.3/Documentation/HTML/index.html000066400000000000000000000561061246701203100207360ustar00rootroot00000000000000 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.5.3/Documentation/Makefile000066400000000000000000000057751246701203100176430ustar00rootroot00000000000000 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.5.3/Documentation/README.PythonPlugin000066400000000000000000000067431246701203100215160ustar00rootroot00000000000000 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.5.3/Documentation/kernelshark.1.txt000066400000000000000000000015341246701203100214010ustar00rootroot00000000000000KERNELSHARK(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.5.3/Documentation/trace-cmd-check-events.1.txt000066400000000000000000000024011246701203100232760ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-extract.1.txt000066400000000000000000000046761246701203100224110ustar00rootroot00000000000000TRACE-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). 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.5.3/Documentation/trace-cmd-hist.1.txt000066400000000000000000000027321246701203100216750ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-list.1.txt000066400000000000000000000042271246701203100217020ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-listen.1.txt000066400000000000000000000030431246701203100222200ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-mem.1.txt000066400000000000000000000071271246701203100215070ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-options.1.txt000066400000000000000000000014311246701203100224140ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-profile.1.txt000066400000000000000000000760631246701203100223760ustar00rootroot00000000000000TRACE-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) *-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. (Note: 'g' is not yet implemented) *--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.5.3/Documentation/trace-cmd-record.1.txt000066400000000000000000000444461246701203100222140ustar00rootroot00000000000000TRACE-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* 'plugin':: Specify a trace plugin. Plugins are special Ftrace tracers that usually do more than just trace an event. Common plugins are *function*, *function_graph*, *preemptirqsoff*, *irqsoff*, *preemptoff*, and *wakeup*. A plugin must be supported by the running kernel. To see a list of available plugins, 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. *-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* 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. *--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. *--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. *--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.5.3/Documentation/trace-cmd-report.1.txt000066400000000000000000000471321246701203100222440ustar00rootroot00000000000000TRACE-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"' ------------------------------------------ *-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. *-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. 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.5.3/Documentation/trace-cmd-reset.1.txt000066400000000000000000000045561246701203100220560ustar00rootroot00000000000000TRACE-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 ------- *-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 *-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). *-d*:: Delete the previously specified buffer instance that was specified by the *-B* option. It is invalid to use with *-t* as the top level instance can not be deleted. *-t*:: Resets the top level instance buffer. Without the *-B* option this is the same as the default. But if *-B* is used, this is required if the top level instance buffer should also be reset. 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.5.3/Documentation/trace-cmd-restore.1.txt000066400000000000000000000077471246701203100224240ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-show.1.txt000066400000000000000000000053241246701203100217060ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-snapshot.1.txt000066400000000000000000000032151246701203100225620ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-split.1.txt000066400000000000000000000062561246701203100220660ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-stack.1.txt000066400000000000000000000023001246701203100220220ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-start.1.txt000066400000000000000000000023621246701203100220620ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-stat.1.txt000066400000000000000000000033601246701203100216770ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd-stop.1.txt000066400000000000000000000033571246701203100217170ustar00rootroot00000000000000TRACE-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. *-t*:: Stops the top level instance buffer. Without the *-B* option this is the same as the default. But if *-B* 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.5.3/Documentation/trace-cmd-stream.1.txt000066400000000000000000000027031246701203100222170ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd.1.txt000066400000000000000000000047121246701203100207300ustar00rootroot00000000000000TRACE-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.5.3/Documentation/trace-cmd.dat.5.txt000066400000000000000000000167761246701203100215200ustar00rootroot00000000000000TRACE-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.5.3/Makefile000066400000000000000000000405241246701203100150210ustar00rootroot00000000000000# trace-cmd version TC_VERSION = 2 TC_PATCHLEVEL = 5 TC_EXTRAVERSION = 3 # Kernel Shark version KS_VERSION = 0 KS_PATCHLEVEL = 2 KS_EXTRAVERSION = # 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 ?= lib 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 else plugin_dir = $(prefix)/$(libdir)/trace-cmd/plugins python_dir = $(prefix)/$(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))' endif HELP_DIR = -DHELP_DIR=$(html_install) HELP_DIR_SQ = '$(subst ','\'',$(HELP_DIR))' # copy a bit from Linux kbuild ifeq ("$(origin V)", "command line") VERBOSE = $(V) endif ifndef VERBOSE VERBOSE = 0 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) PYTHON_SO_INSTALL := ctracecmd.install PYTHON_PY_PROGS := event-viewer.install PYTHON_PY_LIBS := tracecmd.install tracecmdgui.install endif endif # 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) # have udis86 disassembler library? udis86-flags := $(call test-build,\#include ,-DHAVE_UDIS86 -ludis86) 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") BUILD_OUTPUT := $(O) endif ifeq ($(BUILD_SRC),) ifneq ($(BUILD_OUTPUT),) define build_output $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \ BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 endef saved-output := $(BUILD_OUTPUT) BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd) $(if $(BUILD_OUTPUT),, \ $(error output directory "$(saved-output)" does not exist)) all: sub-make gui: force $(call build_output, all_cmd) $(call build_output, BUILDGUI=1 all_gui) $(filter-out gui,$(MAKECMDGOALS)): sub-make sub-make: force $(call build_output, $(MAKECMDGOALS)) # Leave processing to above invocation of make skip-makefile := 1 endif # BUILD_OUTPUT endif # BUILD_SRC # We process the rest of the Makefile if this is the final invocation of make ifeq ($(skip-makefile),) srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR)) objtree := $(CURDIR) src := $(srctree) obj := $(objtree) export prefix bindir src obj # Shell quotes bindir_SQ = $(subst ','\'',$(bindir)) bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) python_dir_SQ = $(subst ','\'',$(python_dir)) LIBS = -L. -ltracecmd -ldl LIB_FILE = libtracecmd.a PACKAGES= gtk+-2.0 libxml-2.0 gthread-2.0 ifndef BUILDGUI BUILDGUI = 0 endif ifeq ($(BUILDGUI), 1) CONFIG_INCLUDES = $(shell pkg-config --cflags $(PACKAGES)) -I$(obj) CONFIG_FLAGS = -DBUILDGUI \ -DGTK_VERSION=$(shell pkg-config --modversion gtk+-2.0 | \ awk 'BEGIN{FS="."}{ a = ($$1 * (2^16)) + $$2 * (2^8) + $$3; printf ("%d", a);}') CONFIG_LIBS = $(shell pkg-config --libs $(PACKAGES)) VERSION = $(KS_VERSION) PATCHLEVEL = $(KS_PATCHLEVEL) EXTRAVERSION = $(KS_EXTRAVERSION) GUI = 'GUI ' GOBJ = $@ GSPACE = REBUILD_GUI = /bin/true G = N = @/bin/true || CONFIG_FLAGS += $(HELP_DIR_SQ) else CONFIG_INCLUDES = CONFIG_LIBS = CONFIG_FLAGS = VERSION = $(TC_VERSION) PATCHLEVEL = $(TC_PATCHLEVEL) EXTRAVERSION = $(TC_EXTRAVERSION) GUI = GSPACE = " " GOBJ = $(GSPACE)$@ REBUILD_GUI = $(MAKE) -f $(src)/Makefile BUILDGUI=1 $@ G = $(REBUILD_GUI); /bin/true || N = endif export Q VERBOSE TRACECMD_VERSION = $(TC_VERSION).$(TC_PATCHLEVEL).$(TC_EXTRAVERSION) KERNELSHARK_VERSION = $(KS_VERSION).$(KS_PATCHLEVEL).$(KS_EXTRAVERSION) INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES) include $(src)/features.mk # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -Wall CPPFLAGS ?= LDFLAGS ?= # Required CFLAGS override CFLAGS += -D_GNU_SOURCE ifndef NO_PTRACE ifneq ($(call try-cc,$(SOURCE_PTRACE),),y) NO_PTRACE = 1 CFLAGS += -DWARN_NO_PTRACE endif endif ifdef NO_PTRACE CFLAGS += -DNO_PTRACE endif # Append required CFLAGS override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ) override CFLAGS += $(udis86-flags) $(blk-flags) ifeq ($(VERBOSE),1) Q = print_compile = print_app_build = print_fpic_compile = print_shared_lib_compile = print_plugin_obj_compile = print_plugin_build = print_install = else Q = @ print_compile = echo ' $(GUI)COMPILE '$(GOBJ); print_app_build = echo ' $(GUI)BUILD '$(GOBJ); print_fpic_compile = echo ' $(GUI)COMPILE FPIC '$(GOBJ); print_shared_lib_compile = echo ' $(GUI)COMPILE SHARED LIB '$(GOBJ); print_plugin_obj_compile = echo ' $(GUI)COMPILE PLUGIN OBJ '$(GOBJ); print_plugin_build = echo ' $(GUI)BUILD PLUGIN '$(GOBJ); print_static_lib_build = echo ' $(GUI)BUILD STATIC LIB '$(GOBJ); print_install = echo ' $(GUI)INSTALL '$(GSPACE)$1' to $(DESTDIR_SQ)$2'; endif do_fpic_compile = \ ($(print_fpic_compile) \ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) -fPIC $< -o $@) do_app_build = \ ($(print_app_build) \ $(CC) $^ -rdynamic -o $@ $(LDFLAGS) $(CONFIG_LIBS) $(LIBS)) do_compile_shared_library = \ ($(print_shared_lib_compile) \ $(CC) --shared $^ -o $@) do_compile_plugin_obj = \ ($(print_plugin_obj_compile) \ $(CC) -c $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ $<) do_plugin_build = \ ($(print_plugin_build) \ $(CC) $(CFLAGS) $(LDFLAGS) -shared -nostartfiles -o $@ $<) do_build_static_lib = \ ($(print_static_lib_build) \ $(RM) $@; $(AR) rcs $@ $^) define check_gui if [ $(BUILDGUI) -ne 1 -a ! -z "$(filter $(gui_objs),$(@))" ]; then \ $(REBUILD_GUI); \ else \ $(print_compile) \ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(EXT) $< -o $(obj)/$@; \ fi; endef $(obj)/%.o: $(src)/%.c $(Q)$(call check_gui) %.o: $(src)/%.c $(Q)$(call check_gui) TRACE_GUI_OBJS = trace-filter.o trace-compat.o trace-filter-hash.o trace-dialog.o \ trace-xml.o TRACE_CMD_OBJS = trace-cmd.o trace-record.o trace-read.o trace-split.o trace-listen.o \ trace-stack.o trace-hist.o trace-mem.o trace-snapshot.o trace-stat.o \ trace-hash.o trace-profile.o trace-stream.o TRACE_VIEW_OBJS = trace-view.o trace-view-store.o TRACE_GRAPH_OBJS = trace-graph.o trace-plot.o trace-plot-cpu.o trace-plot-task.o TRACE_VIEW_MAIN_OBJS = trace-view-main.o $(TRACE_VIEW_OBJS) $(TRACE_GUI_OBJS) TRACE_GRAPH_MAIN_OBJS = trace-graph-main.o $(TRACE_GRAPH_OBJS) $(TRACE_GUI_OBJS) KERNEL_SHARK_OBJS = $(TRACE_VIEW_OBJS) $(TRACE_GRAPH_OBJS) $(TRACE_GUI_OBJS) \ trace-capture.o kernel-shark.o PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ trace-output.o trace-record.o trace-recorder.o \ trace-restore.o trace-usage.o trace-blk-hack.o \ kbuffer-parse.o event-plugin.o trace-hooks.o PLUGIN_OBJS = PLUGIN_OBJS += plugin_jbd2.o PLUGIN_OBJS += plugin_hrtimer.o PLUGIN_OBJS += plugin_kmem.o PLUGIN_OBJS += plugin_kvm.o PLUGIN_OBJS += plugin_mac80211.o PLUGIN_OBJS += plugin_sched_switch.o PLUGIN_OBJS += plugin_function.o PLUGIN_OBJS += plugin_xen.o PLUGIN_OBJS += plugin_scsi.o PLUGIN_OBJS += plugin_cfg80211.o PLUGIN_OBJS += plugin_blk.o PLUGINS := $(PLUGIN_OBJS:.o=.so) ALL_OBJS = $(TRACE_CMD_OBJS) $(KERNEL_SHARK_OBJS) $(TRACE_VIEW_MAIN_OBJS) \ $(TRACE_GRAPH_MAIN_OBJS) $(TCMD_LIB_OBJS) $(PLUGIN_OBJS) CMD_TARGETS = trace_plugin_dir trace_python_dir tc_version.h libparsevent.a $(LIB_FILE) \ trace-cmd $(PLUGINS) $(BUILD_PYTHON) GUI_TARGETS = ks_version.h trace-graph trace-view kernelshark TARGETS = $(CMD_TARGETS) $(GUI_TARGETS) # cpp $(INCLUDES) ### # Default we just build trace-cmd # # If you want kernelshark, then do: make gui ### all: all_cmd show_gui_make all_cmd: $(CMD_TARGETS) gui: $(CMD_TARGETS) $(Q)$(MAKE) -f $(src)/Makefile BUILDGUI=1 all_gui all_gui: $(GUI_TARGETS) show_gui_done GUI_OBJS = $(KERNEL_SHARK_OBJS) $(TRACE_VIEW_MAIN_OBJS) $(TRACE_GRAPH_MAIN_OBJS) gui_objs := $(sort $(GUI_OBJS)) trace-cmd: $(TRACE_CMD_OBJS) $(Q)$(do_app_build) kernelshark: $(KERNEL_SHARK_OBJS) $(Q)$(G)$(do_app_build) trace-view: $(TRACE_VIEW_MAIN_OBJS) $(Q)$(G)$(do_app_build) trace-graph: $(TRACE_GRAPH_MAIN_OBJS) $(Q)$(G)$(do_app_build) trace-cmd: libtracecmd.a kernelshark: libtracecmd.a trace-view: libtracecmd.a trace-graph: libtracecmd.a libparsevent.so: $(PEVENT_LIB_OBJS) $(Q)$(do_compile_shared_library) libparsevent.a: $(PEVENT_LIB_OBJS) $(Q)$(do_build_static_lib) $(TCMD_LIB_OBJS): %.o: $(src)/%.c $(Q)$(do_fpic_compile) libtracecmd.so: $(TCMD_LIB_OBJS) $(Q)$(do_compile_shared_library) libtracecmd.a: $(TCMD_LIB_OBJS) $(Q)$(do_build_static_lib) trace-util.o: trace_plugin_dir $(PLUGIN_OBJS): %.o : $(src)/%.c $(Q)$(do_compile_plugin_obj) $(PLUGINS): %.so: %.o $(Q)$(do_plugin_build) define make_version.h (echo '/* This file is automatically generated. Do not modify. */'; \ echo \#define VERSION_CODE $(shell \ expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ echo '#define FILE_VERSION '$(FILE_VERSION); \ ) > $1 endef define update_version.h ($(call make_version.h, $@.tmp); \ if [ -r $@ ] && cmp -s $@ $@.tmp; then \ rm -f $@.tmp; \ else \ echo ' UPDATE $@'; \ mv -f $@.tmp $@; \ fi); endef ks_version.h: force $(Q)$(G)$(call update_version.h) tc_version.h: force $(Q)$(N)$(call update_version.h) define update_dir (echo $1 > $@.tmp; \ if [ -r $@ ] && cmp -s $@ $@.tmp; then \ rm -f $@.tmp; \ else \ echo ' UPDATE $@'; \ mv -f $@.tmp $@; \ fi); endef trace_plugin_dir: force $(Q)$(N)$(call update_dir, 'PLUGIN_DIR=$(PLUGIN_DIR)') trace_python_dir: force $(Q)$(N)$(call update_dir, 'PYTHON_DIR=$(PYTHON_DIR)') ## make deps all_objs := $(sort $(ALL_OBJS)) all_deps := $(all_objs:%.o=.%.d) gui_deps := $(gui_objs:%.o=.%.d) non_gui_deps = $(filter-out $(gui_deps),$(all_deps)) define check_gui_deps if [ ! -z "$(filter $(gui_deps),$(@))" ]; then \ if [ $(BUILDGUI) -ne 1 ]; then \ $(REBUILD_GUI); \ else \ $(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@; \ fi \ elif [ $(BUILDGUI) -eq 0 ]; then \ $(CC) -M $(CPPFLAGS) $(CFLAGS) $< > $@; \ else \ echo SKIPPING $@; \ fi; endef $(gui_deps): ks_version.h $(non_gui_deps): tc_version.h $(all_deps): .%.d: $(src)/%.c $(Q)$(call check_gui_deps) $(all_objs) : %.o : .%.d ifeq ($(BUILDGUI), 1) dep_includes := $(wildcard $(gui_deps)) else dep_includes := $(wildcard $(non_gui_deps)) endif ifneq ($(dep_includes),) include $(dep_includes) endif show_gui_make: @echo "Note: to build the gui, type \"make gui\"" @echo " to build man pages, type \"make doc\"" show_gui_done: @echo "gui build complete" PHONY += show_gui_make tags: force $(RM) tags find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px TAGS: force $(RM) TAGS find . -name '*.[ch]' | xargs etags cscope: force $(RM) cscope* find . -name '*.[ch]' | cscope -b -q PLUGINS_INSTALL = $(subst .so,.install,$(PLUGINS)) $(subst .so,.install,$(PYTHON_PLUGINS)) define do_install $(print_install) \ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ fi; \ $(INSTALL) $1 '$(DESTDIR_SQ)$2' endef 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 $(PLUGINS_INSTALL): %.install : %.so force $(Q)$(call do_install_data,$<,$(plugin_dir_SQ)) install_plugins: $(PLUGINS_INSTALL) $(PYTHON_SO_INSTALL): %.install : %.so force $(Q)$(call do_install_data,$<,$(python_dir_SQ)) $(PYTHON_PY_PROGS): %.install : %.py force $(Q)$(call do_install,$<,$(python_dir_SQ)) $(PYTHON_PY_LIBS): %.install : %.py force $(Q)$(call do_install_data,$<,$(python_dir_SQ)) $(PYTHON_PY_PLUGINS): %.install : %.py force $(Q)$(call do_install_data,$<,$(plugin_dir_SQ)) install_python: $(PYTHON_SO_INSTALL) $(PYTHON_PY_PROGS) $(PYTHON_PY_LIBS) $(PYTHON_PY_PLUGINS) install_cmd: all_cmd install_plugins install_python $(Q)$(call do_install,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)$(call do_install,trace-view,$(bindir_SQ)) $(Q)$(call do_install,trace-graph,$(bindir_SQ)) $(Q)$(call do_install,kernelshark,$(bindir_SQ)) doc: $(MAKE) -C $(src)/Documentation all doc_clean: $(MAKE) -C $(src)/Documentation clean install_doc: $(MAKE) -C $(src)/Documentation install clean: $(RM) *.o *~ $(TARGETS) *.a *.so ctracecmd_wrap.c .*.d $(RM) tags TAGS cscope* ##### PYTHON STUFF ##### PYTHON_INCLUDES = `pkg-config --cflags $(PYTHON_VERS)` PYTHON_LDFLAGS = `pkg-config --libs $(PYTHON_VERS)` \ $(shell python2 -c "import distutils.sysconfig; print distutils.sysconfig.get_config_var('LINKFORSHARED')") PYGTK_CFLAGS = `pkg-config --cflags pygtk-2.0` ctracecmd.so: $(TCMD_LIB_OBJS) ctracecmd.i swig -Wall -python -noproxy ctracecmd.i $(CC) -fpic -c $(CPPFLAGS) $(CFLAGS) $(PYTHON_INCLUDES) ctracecmd_wrap.c $(CC) --shared $(TCMD_LIB_OBJS) $(LDFLAGS) ctracecmd_wrap.o -o ctracecmd.so ctracecmdgui.so: $(TRACE_VIEW_OBJS) $(LIB_FILE) swig -Wall -python -noproxy ctracecmdgui.i $(CC) -fpic -c $(CPPFLAGS) $(CFLAGS) $(INCLUDES) $(PYTHON_INCLUDES) $(PYGTK_CFLAGS) ctracecmdgui_wrap.c $(CC) --shared $^ $(LDFLAGS) $(LIBS) $(CONFIG_LIBS) ctracecmdgui_wrap.o -o ctracecmdgui.so PHONY += python python: $(PYTHON) PHONY += python-gui python-gui: $(PYTHON_GUI) PHONY += python-plugin python-plugin: $(PYTHON_PLUGINS) CFLAGS_plugin_python.o += $(PYTHON_DIR_SQ) do_compile_python_plugin_obj = \ ($(print_plugin_obj_compile) \ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(CFLAGS_$@) $(PYTHON_INCLUDES) -fPIC -o $@ $<) do_python_plugin_build = \ ($(print_plugin_build) \ $(CC) $< -shared $(LDFLAGS) $(PYTHON_LDFLAGS) -o $@) plugin_python.o: %.o : $(src)/%.c trace_python_dir $(Q)$(do_compile_python_plugin_obj) plugin_python.so: %.so: %.o $(Q)$(do_python_plugin_build) endif # skip-makefile 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.5.3/README000066400000000000000000000016131246701203100142350ustar00rootroot00000000000000These 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.5.3/bug.h000066400000000000000000000004511246701203100143020ustar00rootroot00000000000000#ifndef __TRACE_CMD_BUG #define __TRACE_CMD_BUG #define unlikely(cond) __builtin_expect(!!(cond), 0) #define WARN_ONCE(cond, fmt, ...) \ ({ \ int __c__ = cond; \ if (unlikely(__c__)) { \ warning(fmt, ##__VA_ARGS__); \ } \ __c__; \ }) #endif /* __TRACE_CMD_BUG */ trace-cmd-2.5.3/cpu.h000066400000000000000000000046701246701203100143230ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _CPU_H #define _CPU_H static inline int cpu_isset(guint64 *cpu_mask, gint cpu) { guint64 mask; mask = *(cpu_mask + (cpu >> 6)); return mask & (1ULL << (cpu & ((1ULL << 6) - 1))); } static inline gboolean cpu_allset(guint64 *cpu_mask, gint max_cpus) { gint cpu; for (cpu = 0; cpu < max_cpus; cpu++) { if (!cpu_isset(cpu_mask, cpu)) return FALSE; } return TRUE; } static inline void cpu_set(guint64 *cpu_mask, gint cpu) { guint64 *mask; mask = cpu_mask + (cpu >> 6); *mask |= (1ULL << (cpu & ((1ULL << 6) - 1))); } static inline void cpu_clear(guint64 *cpu_mask, gint cpu) { guint64 *mask; mask = cpu_mask + (cpu >> 6); *mask &= ~(1ULL << (cpu & ((1ULL << 6) - 1))); } static inline void set_cpus(guint64 *cpu_mask, gint cpus) { gint idx; for (idx = 0; idx < (cpus >> 6); idx++) { *(cpu_mask + idx) = -1ULL; } *(cpu_mask) = (1ULL << (cpus & ((1ULL << 6) - 1))) - 1; } static inline gboolean cpus_equal(guint64 *a_mask, guint64 *b_mask, gint cpus) { gint idx; for (idx = 0; idx < (cpus >> 6) + 1; idx++) { if (*(a_mask + idx) != *(b_mask + idx)) return FALSE; } return TRUE; } /* Hamming weight */ static inline guint hweight(guint mask) { guint64 w = mask; w -= (w >> 1) & 0x5555555555555555ul; w = (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul); w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful; return (w * 0x0101010101010101ul) >> 56; } static inline guint cpu_weight(guint64 *cpu_mask, guint cpus) { guint weight = 0; gint idx; for (idx = 0; idx < (cpus >> 6) + 1; idx++) weight += hweight(*(cpu_mask + idx)); return weight; } #endif /* _CPU_H */ trace-cmd-2.5.3/ctracecmd.i000066400000000000000000000074711246701203100154640ustar00rootroot00000000000000// tracecmd.i %module ctracecmd %include "typemaps.i" %include "constraints.i" %nodefaultctor record; %nodefaultdtor record; %apply Pointer NONNULL { struct tracecmd_input *handle }; %apply Pointer NONNULL { struct pevent *pevent }; %apply Pointer NONNULL { struct format_field * }; %apply unsigned long long *OUTPUT {unsigned long long *} %apply int *OUTPUT {int *} %{ #include "trace-cmd.h" %} %typemap(in) PyObject *pyfunc { if (!PyCallable_Check($input)) { PyErr_SetString(PyExc_TypeError, "Need a callable object!"); return NULL; } $1 = $input; } %ignore python_callback; %inline %{ static int python_callback(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); PyObject *convert_pevent(unsigned long pevent) { void *pev = (void *)pevent; return SWIG_NewPointerObj(SWIG_as_voidptr(pev), SWIGTYPE_p_pevent, 0); } void py_pevent_register_event_handler(struct pevent *pevent, int id, char *subsys, char *evname, PyObject *pyfunc) { Py_INCREF(pyfunc); pevent_register_event_handler(pevent, id, subsys, evname, python_callback, pyfunc); } static PyObject *py_field_get_data(struct format_field *f, struct pevent_record *r) { if (!strncmp(f->type, "__data_loc ", 11)) { unsigned long long val; int len, offset; if (pevent_read_number_field(f, r->data, &val)) { PyErr_SetString(PyExc_TypeError, "Field is not a valid number"); return NULL; } /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset = val & 0xffff; len = val >> 16; return PyBuffer_FromMemory((char *)r->data + offset, len); } return PyBuffer_FromMemory((char *)r->data + f->offset, f->size); } static PyObject *py_field_get_str(struct format_field *f, struct pevent_record *r) { if (!strncmp(f->type, "__data_loc ", 11)) { unsigned long long val; int offset; if (pevent_read_number_field(f, r->data, &val)) { PyErr_SetString(PyExc_TypeError, "Field is not a valid number"); return NULL; } /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset = val & 0xffff; return PyString_FromString((char *)r->data + offset); } return PyString_FromStringAndSize((char *)r->data + f->offset, strnlen((char *)r->data + f->offset, f->size)); } static PyObject *py_format_get_keys(struct event_format *ef) { PyObject *list; struct format_field *f; list = PyList_New(0); for (f = ef->format.fields; f; f = f->next) { if (PyList_Append(list, PyString_FromString(f->name))) { Py_DECREF(list); return NULL; } } return list; } %} %wrapper %{ static int python_callback(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { PyObject *arglist, *result; int r = 0; record->ref_count++; arglist = Py_BuildValue("(OOO)", SWIG_NewPointerObj(SWIG_as_voidptr(s), SWIGTYPE_p_trace_seq, 0), SWIG_NewPointerObj(SWIG_as_voidptr(record), SWIGTYPE_p_pevent_record, 0), SWIG_NewPointerObj(SWIG_as_voidptr(event), SWIGTYPE_p_event_format, 0)); result = PyEval_CallObject(context, arglist); Py_XDECREF(arglist); if (result && result != Py_None) { if (!PyInt_Check(result)) { PyErr_SetString(PyExc_TypeError, "callback must return int"); PyErr_Print(); Py_XDECREF(result); return 0; } r = PyInt_AS_LONG(result); } else if (result == Py_None) r = 0; else PyErr_Print(); Py_XDECREF(result); return r; } %} %ignore trace_seq_vprintf; %ignore vpr_stat; /* SWIG can't grok these, define them to nothing */ #define __trace #define __attribute__(x) #define __thread %include "trace-cmd.h" %include "event-parse.h" trace-cmd-2.5.3/ctracecmdgui.i000066400000000000000000000030351246701203100161610ustar00rootroot00000000000000// ctracecmdgui.i %module ctracecmdgui %include typemaps.i %{ #include "trace-view-store.h" #include #include #include extern GtkTreeModel *trace_view_store_as_gtk_tree_model(struct trace_view_store *store); PyObject * pytype_from_gtype(GType gtype) { PyTypeObject *pt = NULL; switch (gtype) { case G_TYPE_INT: case G_TYPE_UINT: pt = &PyLong_Type; break; case G_TYPE_STRING: pt = &PyUnicode_Type; break; default: return Py_None; } return (PyObject *)pt; } %} /* return python longs from unsigned long long functions */ %typemap(out) unsigned long long { $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); } /* help swig cope with g* types */ %typemap(in) gint { $1 = PyInt_AsLong($input); } %typemap(out) gint { $result = PyInt_FromLong($1); } %typemap(in) guint { $1 = PyLong_AsUnsignedLong($input); } %typemap(out) guint { $result = PyLong_FromUnsignedLong($1); } %typemap(in) guint64 { $1 = PyLong_AsUnsignedLongLong($input); } %typemap(out) guint64 { $result = PyLong_FromUnsignedLongLong($1); } %typemap(out) GType { $result = pytype_from_gtype($1); } %typemap(out) GtkTreeModelFlags { $result = PyLong_FromLong($1); } %inline %{ GtkTreeModel *trace_view_store_as_gtk_tree_model(struct trace_view_store *store) { return GTK_TREE_MODEL(store); } %} /* SWIG can't grok these, define them to nothing */ #define __trace #define __attribute__(x) #define __thread %include "trace-view-store.h" trace-cmd-2.5.3/event-parse.c000066400000000000000000004226021246701203100157570ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * The parts for function graph printing was taken and modified from the * Linux Kernel that were written by * - Copyright (C) 2009 Frederic Weisbecker, * Frederic Weisbecker gave his permission to relicense the code to * the Lesser General Public License. */ #include #include #include #include #include #include #include #include #include #include "event-parse.h" #include "event-utils.h" static const char *input_buf; static unsigned long long input_buf_ptr; static unsigned long long input_buf_siz; static int is_flag_field; static int is_symbolic_field; static int show_warning = 1; #define do_warning(fmt, ...) \ do { \ if (show_warning) \ warning(fmt, ##__VA_ARGS__); \ } while (0) #define do_warning_event(event, fmt, ...) \ do { \ if (!show_warning) \ continue; \ \ if (event) \ warning("[%s:%s] " fmt, event->system, \ event->name, ##__VA_ARGS__); \ else \ warning(fmt, ##__VA_ARGS__); \ } while (0) static void init_input_buf(const char *buf, unsigned long long size) { input_buf = buf; input_buf_siz = size; input_buf_ptr = 0; } const char *pevent_get_input_buf(void) { return input_buf; } unsigned long long pevent_get_input_buf_ptr(void) { return input_buf_ptr; } struct event_handler { struct event_handler *next; int id; const char *sys_name; const char *event_name; pevent_event_handler_func func; void *context; }; struct pevent_func_params { struct pevent_func_params *next; enum pevent_func_arg_type type; }; struct pevent_function_handler { struct pevent_function_handler *next; enum pevent_func_arg_type ret_type; char *name; pevent_func_handler func; struct pevent_func_params *params; int nr_args; }; static unsigned long long process_defined_func(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg); static void free_func_handle(struct pevent_function_handler *func); /** * pevent_buffer_init - init buffer for parsing * @buf: buffer to parse * @size: the size of the buffer * * For use with pevent_read_token(), this initializes the internal * buffer that pevent_read_token() will parse. */ void pevent_buffer_init(const char *buf, unsigned long long size) { init_input_buf(buf, size); } void breakpoint(void) { static int x; x++; } struct print_arg *alloc_arg(void) { return calloc(1, sizeof(struct print_arg)); } struct cmdline { char *comm; int pid; }; static int cmdline_cmp(const void *a, const void *b) { const struct cmdline *ca = a; const struct cmdline *cb = b; if (ca->pid < cb->pid) return -1; if (ca->pid > cb->pid) return 1; return 0; } struct cmdline_list { struct cmdline_list *next; char *comm; int pid; }; static int cmdline_init(struct pevent *pevent) { struct cmdline_list *cmdlist = pevent->cmdlist; struct cmdline_list *item; struct cmdline *cmdlines; int i; cmdlines = malloc(sizeof(*cmdlines) * pevent->cmdline_count); if (!cmdlines) return -1; i = 0; while (cmdlist) { cmdlines[i].pid = cmdlist->pid; cmdlines[i].comm = cmdlist->comm; i++; item = cmdlist; cmdlist = cmdlist->next; free(item); } qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); pevent->cmdlines = cmdlines; pevent->cmdlist = NULL; return 0; } static const char *find_cmdline(struct pevent *pevent, int pid) { const struct cmdline *comm; struct cmdline key; if (!pid) return ""; if (!pevent->cmdlines && cmdline_init(pevent)) return ""; key.pid = pid; comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (comm) return comm->comm; return "<...>"; } /** * pevent_pid_is_registered - return if a pid has a cmdline registered * @pevent: handle for the pevent * @pid: The pid to check if it has a cmdline registered with. * * Returns 1 if the pid has a cmdline mapped to it * 0 otherwise. */ int pevent_pid_is_registered(struct pevent *pevent, int pid) { const struct cmdline *comm; struct cmdline key; if (!pid) return 1; if (!pevent->cmdlines && cmdline_init(pevent)) return 0; key.pid = pid; comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (comm) return 1; return 0; } /* * If the command lines have been converted to an array, then * we must add this pid. This is much slower than when cmdlines * are added before the array is initialized. */ static int add_new_comm(struct pevent *pevent, const char *comm, int pid) { struct cmdline *cmdlines = pevent->cmdlines; const struct cmdline *cmdline; struct cmdline key; if (!pid) return 0; /* avoid duplicates */ key.pid = pid; cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, sizeof(*pevent->cmdlines), cmdline_cmp); if (cmdline) { errno = EEXIST; return -1; } cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1)); if (!cmdlines) { errno = ENOMEM; return -1; } cmdlines[pevent->cmdline_count].comm = strdup(comm); if (!cmdlines[pevent->cmdline_count].comm) { free(cmdlines); errno = ENOMEM; return -1; } cmdlines[pevent->cmdline_count].pid = pid; if (cmdlines[pevent->cmdline_count].comm) pevent->cmdline_count++; qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); pevent->cmdlines = cmdlines; return 0; } /** * pevent_register_comm - register a pid / comm mapping * @pevent: handle for the pevent * @comm: the command line to register * @pid: the pid to map the command line to * * This adds a mapping to search for command line names with * a given pid. The comm is duplicated. */ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) { struct cmdline_list *item; if (pevent->cmdlines) return add_new_comm(pevent, comm, pid); item = malloc(sizeof(*item)); if (!item) return -1; if (comm) item->comm = strdup(comm); else item->comm = strdup("<...>"); if (!item->comm) { free(item); return -1; } item->pid = pid; item->next = pevent->cmdlist; pevent->cmdlist = item; pevent->cmdline_count++; return 0; } int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock) { pevent->trace_clock = strdup(trace_clock); if (!pevent->trace_clock) { errno = ENOMEM; return -1; } return 0; } struct func_map { unsigned long long addr; char *func; char *mod; }; struct func_list { struct func_list *next; unsigned long long addr; char *func; char *mod; }; static int func_cmp(const void *a, const void *b) { const struct func_map *fa = a; const struct func_map *fb = b; if (fa->addr < fb->addr) return -1; if (fa->addr > fb->addr) return 1; return 0; } /* * We are searching for a record in between, not an exact * match. */ static int func_bcmp(const void *a, const void *b) { const struct func_map *fa = a; const struct func_map *fb = b; if ((fa->addr == fb->addr) || (fa->addr > fb->addr && fa->addr < (fb+1)->addr)) return 0; if (fa->addr < fb->addr) return -1; return 1; } static int func_map_init(struct pevent *pevent) { struct func_list *funclist; struct func_list *item; struct func_map *func_map; int i; func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1)); if (!func_map) return -1; funclist = pevent->funclist; i = 0; while (funclist) { func_map[i].func = funclist->func; func_map[i].addr = funclist->addr; func_map[i].mod = funclist->mod; i++; item = funclist; funclist = funclist->next; free(item); } qsort(func_map, pevent->func_count, sizeof(*func_map), func_cmp); /* * Add a special record at the end. */ func_map[pevent->func_count].func = NULL; func_map[pevent->func_count].addr = 0; func_map[pevent->func_count].mod = NULL; pevent->func_map = func_map; pevent->funclist = NULL; return 0; } static struct func_map * find_func(struct pevent *pevent, unsigned long long addr) { struct func_map *func; struct func_map key; if (!pevent->func_map) func_map_init(pevent); key.addr = addr; func = bsearch(&key, pevent->func_map, pevent->func_count, sizeof(*pevent->func_map), func_bcmp); return func; } /** * pevent_find_function - find a function by a given address * @pevent: handle for the pevent * @addr: the address to find the function with * * Returns a pointer to the function stored that has the given * address. Note, the address does not have to be exact, it * will select the function that would contain the address. */ const char *pevent_find_function(struct pevent *pevent, unsigned long long addr) { struct func_map *map; map = find_func(pevent, addr); if (!map) return NULL; return map->func; } /** * pevent_find_function_address - find a function address by a given address * @pevent: handle for the pevent * @addr: the address to find the function with * * Returns the address the function starts at. This can be used in * conjunction with pevent_find_function to print both the function * name and the function offset. */ unsigned long long pevent_find_function_address(struct pevent *pevent, unsigned long long addr) { struct func_map *map; map = find_func(pevent, addr); if (!map) return 0; return map->addr; } /** * pevent_register_function - register a function with a given address * @pevent: handle for the pevent * @function: the function name to register * @addr: the address the function starts at * @mod: the kernel module the function may be in (NULL for none) * * This registers a function name with an address and module. * The @func passed in is duplicated. */ int pevent_register_function(struct pevent *pevent, char *func, unsigned long long addr, char *mod) { struct func_list *item = malloc(sizeof(*item)); if (!item) return -1; item->next = pevent->funclist; item->func = strdup(func); if (!item->func) goto out_free; if (mod) { item->mod = strdup(mod); if (!item->mod) goto out_free_func; } else item->mod = NULL; item->addr = addr; pevent->funclist = item; pevent->func_count++; return 0; out_free_func: free(item->func); item->func = NULL; out_free: free(item); errno = ENOMEM; return -1; } /** * pevent_print_funcs - print out the stored functions * @pevent: handle for the pevent * * This prints out the stored functions. */ void pevent_print_funcs(struct pevent *pevent) { int i; if (!pevent->func_map) func_map_init(pevent); for (i = 0; i < (int)pevent->func_count; i++) { printf("%016llx %s", pevent->func_map[i].addr, pevent->func_map[i].func); if (pevent->func_map[i].mod) printf(" [%s]\n", pevent->func_map[i].mod); else printf("\n"); } } struct printk_map { unsigned long long addr; char *printk; }; struct printk_list { struct printk_list *next; unsigned long long addr; char *printk; }; static int printk_cmp(const void *a, const void *b) { const struct printk_map *pa = a; const struct printk_map *pb = b; if (pa->addr < pb->addr) return -1; if (pa->addr > pb->addr) return 1; return 0; } static int printk_map_init(struct pevent *pevent) { struct printk_list *printklist; struct printk_list *item; struct printk_map *printk_map; int i; printk_map = malloc(sizeof(*printk_map) * (pevent->printk_count + 1)); if (!printk_map) return -1; printklist = pevent->printklist; i = 0; while (printklist) { printk_map[i].printk = printklist->printk; printk_map[i].addr = printklist->addr; i++; item = printklist; printklist = printklist->next; free(item); } qsort(printk_map, pevent->printk_count, sizeof(*printk_map), printk_cmp); pevent->printk_map = printk_map; pevent->printklist = NULL; return 0; } static struct printk_map * find_printk(struct pevent *pevent, unsigned long long addr) { struct printk_map *printk; struct printk_map key; if (!pevent->printk_map && printk_map_init(pevent)) return NULL; key.addr = addr; printk = bsearch(&key, pevent->printk_map, pevent->printk_count, sizeof(*pevent->printk_map), printk_cmp); return printk; } /** * pevent_register_print_string - register a string by its address * @pevent: handle for the pevent * @fmt: the string format to register * @addr: the address the string was located at * * This registers a string by the address it was stored in the kernel. * The @fmt passed in is duplicated. */ int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr) { struct printk_list *item = malloc(sizeof(*item)); char *p; if (!item) return -1; item->next = pevent->printklist; item->addr = addr; /* Strip off quotes and '\n' from the end */ if (fmt[0] == '"') fmt++; item->printk = strdup(fmt); if (!item->printk) goto out_free; p = item->printk + strlen(item->printk) - 1; if (*p == '"') *p = 0; p -= 2; if (strcmp(p, "\\n") == 0) *p = 0; pevent->printklist = item; pevent->printk_count++; return 0; out_free: free(item); errno = ENOMEM; return -1; } /** * pevent_print_printk - print out the stored strings * @pevent: handle for the pevent * * This prints the string formats that were stored. */ void pevent_print_printk(struct pevent *pevent) { int i; if (!pevent->printk_map) printk_map_init(pevent); for (i = 0; i < (int)pevent->printk_count; i++) { printf("%016llx %s\n", pevent->printk_map[i].addr, pevent->printk_map[i].printk); } } static struct event_format *alloc_event(void) { return calloc(1, sizeof(struct event_format)); } static int add_event(struct pevent *pevent, struct event_format *event) { int i; struct event_format **events = realloc(pevent->events, sizeof(event) * (pevent->nr_events + 1)); if (!events) return -1; pevent->events = events; for (i = 0; i < pevent->nr_events; i++) { if (pevent->events[i]->id > event->id) break; } if (i < pevent->nr_events) memmove(&pevent->events[i + 1], &pevent->events[i], sizeof(event) * (pevent->nr_events - i)); pevent->events[i] = event; pevent->nr_events++; event->pevent = pevent; return 0; } static int event_item_type(enum event_type type) { switch (type) { case EVENT_ITEM ... EVENT_SQUOTE: return 1; case EVENT_ERROR ... EVENT_DELIM: default: return 0; } } static void free_flag_sym(struct print_flag_sym *fsym) { struct print_flag_sym *next; while (fsym) { next = fsym->next; free(fsym->value); free(fsym->str); free(fsym); fsym = next; } } static void free_arg(struct print_arg *arg) { struct print_arg *farg; if (!arg) return; switch (arg->type) { case PRINT_ATOM: free(arg->atom.atom); break; case PRINT_FIELD: free(arg->field.name); break; case PRINT_FLAGS: free_arg(arg->flags.field); free(arg->flags.delim); free_flag_sym(arg->flags.flags); break; case PRINT_SYMBOL: free_arg(arg->symbol.field); free_flag_sym(arg->symbol.symbols); break; case PRINT_HEX: free_arg(arg->hex.field); free_arg(arg->hex.size); break; case PRINT_TYPE: free(arg->typecast.type); free_arg(arg->typecast.item); break; case PRINT_STRING: case PRINT_BSTRING: free(arg->string.string); break; case PRINT_BITMASK: free(arg->bitmask.bitmask); break; case PRINT_DYNAMIC_ARRAY: free(arg->dynarray.index); break; case PRINT_OP: free(arg->op.op); free_arg(arg->op.left); free_arg(arg->op.right); break; case PRINT_FUNC: while (arg->func.args) { farg = arg->func.args; arg->func.args = farg->next; free_arg(farg); } break; case PRINT_NULL: default: break; } free(arg); } static enum event_type get_type(int ch) { if (ch == '\n') return EVENT_NEWLINE; if (isspace(ch)) return EVENT_SPACE; if (isalnum(ch) || ch == '_') return EVENT_ITEM; if (ch == '\'') return EVENT_SQUOTE; if (ch == '"') return EVENT_DQUOTE; if (!isprint(ch)) return EVENT_NONE; if (ch == '(' || ch == ')' || ch == ',') return EVENT_DELIM; return EVENT_OP; } static int __read_char(void) { if (input_buf_ptr >= input_buf_siz) return -1; return input_buf[input_buf_ptr++]; } static int __peek_char(void) { if (input_buf_ptr >= input_buf_siz) return -1; return input_buf[input_buf_ptr]; } /** * pevent_peek_char - peek at the next character that will be read * * Returns the next character read, or -1 if end of buffer. */ int pevent_peek_char(void) { return __peek_char(); } static int extend_token(char **tok, char *buf, int size) { char *newtok = realloc(*tok, size); if (!newtok) { free(*tok); *tok = NULL; return -1; } if (!*tok) strcpy(newtok, buf); else strcat(newtok, buf); *tok = newtok; return 0; } static enum event_type force_token(const char *str, char **tok); static enum event_type __read_token(char **tok) { char buf[BUFSIZ]; int ch, last_ch, quote_ch, next_ch; int i = 0; int tok_size = 0; enum event_type type; *tok = NULL; ch = __read_char(); if (ch < 0) return EVENT_NONE; type = get_type(ch); if (type == EVENT_NONE) return type; buf[i++] = ch; switch (type) { case EVENT_NEWLINE: case EVENT_DELIM: if (asprintf(tok, "%c", ch) < 0) return EVENT_ERROR; return type; case EVENT_OP: switch (ch) { case '-': next_ch = __peek_char(); if (next_ch == '>') { buf[i++] = __read_char(); break; } /* fall through */ case '+': case '|': case '&': case '>': case '<': last_ch = ch; ch = __peek_char(); if (ch != last_ch) goto test_equal; buf[i++] = __read_char(); switch (last_ch) { case '>': case '<': goto test_equal; default: break; } break; case '!': case '=': goto test_equal; default: /* what should we do instead? */ break; } buf[i] = 0; *tok = strdup(buf); return type; test_equal: ch = __peek_char(); if (ch == '=') buf[i++] = __read_char(); goto out; case EVENT_DQUOTE: case EVENT_SQUOTE: /* don't keep quotes */ i--; quote_ch = ch; last_ch = 0; concat: do { if (i == (BUFSIZ - 1)) { buf[i] = 0; tok_size += BUFSIZ; if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; i = 0; } last_ch = ch; ch = __read_char(); buf[i++] = ch; /* the '\' '\' will cancel itself */ if (ch == '\\' && last_ch == '\\') last_ch = 0; } while (ch != quote_ch || last_ch == '\\'); /* remove the last quote */ i--; /* * For strings (double quotes) check the next token. * If it is another string, concatinate the two. */ if (type == EVENT_DQUOTE) { unsigned long long save_input_buf_ptr = input_buf_ptr; do { ch = __read_char(); } while (isspace(ch)); if (ch == '"') goto concat; input_buf_ptr = save_input_buf_ptr; } goto out; case EVENT_ERROR ... EVENT_SPACE: case EVENT_ITEM: default: break; } while (get_type(__peek_char()) == type) { if (i == (BUFSIZ - 1)) { buf[i] = 0; tok_size += BUFSIZ; if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; i = 0; } ch = __read_char(); buf[i++] = ch; } out: buf[i] = 0; if (extend_token(tok, buf, tok_size + i + 1) < 0) return EVENT_NONE; if (type == EVENT_ITEM) { /* * Older versions of the kernel has a bug that * creates invalid symbols and will break the mac80211 * parsing. This is a work around to that bug. * * See Linux kernel commit: * 811cb50baf63461ce0bdb234927046131fc7fa8b */ if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\"\%s\" ", tok); } else if (strcmp(*tok, "STA_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\" sta:%pM\" ", tok); } else if (strcmp(*tok, "VIF_PR_FMT") == 0) { free(*tok); *tok = NULL; return force_token("\" vif:%p(%d)\" ", tok); } } return type; } static enum event_type force_token(const char *str, char **tok) { const char *save_input_buf; unsigned long long save_input_buf_ptr; unsigned long long save_input_buf_siz; enum event_type type; /* save off the current input pointers */ save_input_buf = input_buf; save_input_buf_ptr = input_buf_ptr; save_input_buf_siz = input_buf_siz; init_input_buf(str, strlen(str)); type = __read_token(tok); /* reset back to original token */ input_buf = save_input_buf; input_buf_ptr = save_input_buf_ptr; input_buf_siz = save_input_buf_siz; return type; } static void free_token(char *tok) { if (tok) free(tok); } static enum event_type read_token(char **tok) { enum event_type type; for (;;) { type = __read_token(tok); if (type != EVENT_SPACE) return type; free_token(*tok); } /* not reached */ *tok = NULL; return EVENT_NONE; } /** * pevent_read_token - access to utilites to use the pevent parser * @tok: The token to return * * This will parse tokens from the string given by * pevent_init_data(). * * Returns the token type. */ enum event_type pevent_read_token(char **tok) { return read_token(tok); } /** * pevent_free_token - free a token returned by pevent_read_token * @token: the token to free */ void pevent_free_token(char *token) { free_token(token); } /* no newline */ static enum event_type read_token_item(char **tok) { enum event_type type; for (;;) { type = __read_token(tok); if (type != EVENT_SPACE && type != EVENT_NEWLINE) return type; free_token(*tok); *tok = NULL; } /* not reached */ *tok = NULL; return EVENT_NONE; } static int test_type(enum event_type type, enum event_type expect) { if (type != expect) { do_warning("Error: expected type %d but read %d", expect, type); return -1; } return 0; } static int test_type_token(enum event_type type, const char *token, enum event_type expect, const char *expect_tok) { if (type != expect) { do_warning("Error: expected type %d but read %d", expect, type); return -1; } if (strcmp(token, expect_tok) != 0) { do_warning("Error: expected '%s' but read '%s'", expect_tok, token); return -1; } return 0; } static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) { enum event_type type; if (newline_ok) type = read_token(tok); else type = read_token_item(tok); return test_type(type, expect); } static int read_expect_type(enum event_type expect, char **tok) { return __read_expect_type(expect, tok, 1); } static int __read_expected(enum event_type expect, const char *str, int newline_ok) { enum event_type type; char *token; int ret; if (newline_ok) type = read_token(&token); else type = read_token_item(&token); ret = test_type_token(type, token, expect, str); free_token(token); return ret; } static int read_expected(enum event_type expect, const char *str) { return __read_expected(expect, str, 1); } static int read_expected_item(enum event_type expect, const char *str) { return __read_expected(expect, str, 0); } static char *event_read_name(void) { char *token; if (read_expected(EVENT_ITEM, "name") < 0) return NULL; if (read_expected(EVENT_OP, ":") < 0) return NULL; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; return token; fail: free_token(token); return NULL; } static int event_read_id(void) { char *token; int id; if (read_expected_item(EVENT_ITEM, "ID") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; id = strtoul(token, NULL, 0); free_token(token); return id; fail: free_token(token); return -1; } static int field_is_string(struct format_field *field) { if ((field->flags & FIELD_IS_ARRAY) && (strstr(field->type, "char") || strstr(field->type, "u8") || strstr(field->type, "s8"))) return 1; return 0; } static int field_is_dynamic(struct format_field *field) { if (strncmp(field->type, "__data_loc", 10) == 0) return 1; return 0; } static int field_is_long(struct format_field *field) { /* includes long long */ if (strstr(field->type, "long")) return 1; return 0; } static unsigned int type_size(const char *name) { /* This covers all FIELD_IS_STRING types. */ static struct { const char *type; unsigned int size; } table[] = { { "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "char", 1 }, { }, }; int i; for (i = 0; table[i].type; i++) { if (!strcmp(table[i].type, name)) return table[i].size; } return 0; } static int event_read_fields(struct event_format *event, struct format_field **fields) { struct format_field *field = NULL; enum event_type type; char *token; char *last_token; int count = 0; do { unsigned int size_dynamic = 0; type = read_token(&token); if (type == EVENT_NEWLINE) { free_token(token); return count; } count++; if (test_type_token(type, token, EVENT_ITEM, "field")) goto fail; free_token(token); type = read_token(&token); /* * The ftrace fields may still use the "special" name. * Just ignore it. */ if (event->flags & EVENT_FL_ISFTRACE && type == EVENT_ITEM && strcmp(token, "special") == 0) { free_token(token); type = read_token(&token); } if (test_type_token(type, token, EVENT_OP, ":") < 0) goto fail; free_token(token); if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; last_token = token; field = calloc(1, sizeof(*field)); if (!field) goto fail; field->event = event; /* read the rest of the type */ for (;;) { type = read_token(&token); if (type == EVENT_ITEM || (type == EVENT_OP && strcmp(token, "*") == 0) || /* * Some of the ftrace fields are broken and have * an illegal "." in them. */ (event->flags & EVENT_FL_ISFTRACE && type == EVENT_OP && strcmp(token, ".") == 0)) { if (strcmp(token, "*") == 0) field->flags |= FIELD_IS_POINTER; if (field->type) { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(last_token) + 2); if (!new_type) { free(last_token); goto fail; } field->type = new_type; strcat(field->type, " "); strcat(field->type, last_token); free(last_token); } else field->type = last_token; last_token = token; continue; } break; } if (!field->type) { do_warning_event(event, "%s: no type found", __func__); goto fail; } field->name = last_token; if (test_type(type, EVENT_OP)) goto fail; if (strcmp(token, "[") == 0) { enum event_type last_type = type; char *brackets = token; char *new_brackets; int len; field->flags |= FIELD_IS_ARRAY; type = read_token(&token); if (type == EVENT_ITEM) field->arraylen = strtoul(token, NULL, 0); else field->arraylen = 0; while (strcmp(token, "]") != 0) { if (last_type == EVENT_ITEM && type == EVENT_ITEM) len = 2; else len = 1; last_type = type; new_brackets = realloc(brackets, strlen(brackets) + strlen(token) + len); if (!new_brackets) { free(brackets); goto fail; } brackets = new_brackets; if (len == 2) strcat(brackets, " "); strcat(brackets, token); /* We only care about the last token */ field->arraylen = strtoul(token, NULL, 0); free_token(token); type = read_token(&token); if (type == EVENT_NONE) { do_warning_event(event, "failed to find token"); goto fail; } } free_token(token); new_brackets = realloc(brackets, strlen(brackets) + 2); if (!new_brackets) { free(brackets); goto fail; } brackets = new_brackets; strcat(brackets, "]"); /* add brackets to type */ type = read_token(&token); /* * If the next token is not an OP, then it is of * the format: type [] item; */ if (type == EVENT_ITEM) { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(field->name) + strlen(brackets) + 2); if (!new_type) { free(brackets); goto fail; } field->type = new_type; strcat(field->type, " "); strcat(field->type, field->name); size_dynamic = type_size(field->name); free_token(field->name); strcat(field->type, brackets); field->name = token; type = read_token(&token); } else { char *new_type; new_type = realloc(field->type, strlen(field->type) + strlen(brackets) + 1); if (!new_type) { free(brackets); goto fail; } field->type = new_type; strcat(field->type, brackets); } free(brackets); } if (field_is_string(field)) field->flags |= FIELD_IS_STRING; if (field_is_dynamic(field)) field->flags |= FIELD_IS_DYNAMIC; if (field_is_long(field)) field->flags |= FIELD_IS_LONG; if (test_type_token(type, token, EVENT_OP, ";")) goto fail; free_token(token); if (read_expected(EVENT_ITEM, "offset") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->offset = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expected(EVENT_ITEM, "size") < 0) goto fail_expect; if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; field->size = strtoul(token, NULL, 0); free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; type = read_token(&token); if (type != EVENT_NEWLINE) { /* newer versions of the kernel have a "signed" type */ if (test_type_token(type, token, EVENT_ITEM, "signed")) goto fail; free_token(token); if (read_expected(EVENT_OP, ":") < 0) goto fail_expect; if (read_expect_type(EVENT_ITEM, &token)) goto fail; if (strtoul(token, NULL, 0)) field->flags |= FIELD_IS_SIGNED; free_token(token); if (read_expected(EVENT_OP, ";") < 0) goto fail_expect; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; } free_token(token); if (field->flags & FIELD_IS_ARRAY) { if (field->arraylen) field->elementsize = field->size / field->arraylen; else if (field->flags & FIELD_IS_DYNAMIC) field->elementsize = size_dynamic; else if (field->flags & FIELD_IS_STRING) field->elementsize = 1; else if (field->flags & FIELD_IS_LONG) field->elementsize = event->pevent ? event->pevent->long_size : sizeof(long); } else field->elementsize = field->size; *fields = field; fields = &field->next; } while (1); return 0; fail: free_token(token); fail_expect: if (field) { free(field->type); free(field->name); free(field); } return -1; } static int event_read_format(struct event_format *event) { char *token; int ret; if (read_expected_item(EVENT_ITEM, "format") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; free_token(token); ret = event_read_fields(event, &event->format.common_fields); if (ret < 0) return ret; event->format.nr_common = ret; ret = event_read_fields(event, &event->format.fields); if (ret < 0) return ret; event->format.nr_fields = ret; return 0; fail: free_token(token); return -1; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type); static enum event_type process_arg(struct event_format *event, struct print_arg *arg, char **tok) { enum event_type type; char *token; type = read_token(&token); *tok = token; return process_arg_token(event, arg, tok, type); } static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok); /* * For __print_symbolic() and __print_flags, we need to completely * evaluate the first argument, which defines what to print next. */ static enum event_type process_field_arg(struct event_format *event, struct print_arg *arg, char **tok) { enum event_type type; type = process_arg(event, arg, tok); while (type == EVENT_OP) { type = process_op(event, arg, tok); } return type; } static enum event_type process_cond(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg, *left, *right; enum event_type type; char *token = NULL; arg = alloc_arg(); left = alloc_arg(); right = alloc_arg(); if (!arg || !left || !right) { do_warning_event(event, "%s: not enough memory!", __func__); /* arg will be freed at out_free */ free_arg(left); free_arg(right); goto out_free; } arg->type = PRINT_OP; arg->op.left = left; arg->op.right = right; *tok = NULL; type = process_arg(event, left, &token); again: /* Handle other operations in the arguments */ if (type == EVENT_OP && strcmp(token, ":") != 0) { type = process_op(event, left, &token); goto again; } if (test_type_token(type, token, EVENT_OP, ":")) goto out_free; arg->op.op = token; type = process_arg(event, right, &token); top->op.right = arg; *tok = token; return type; out_free: /* Top may point to itself */ top->op.right = NULL; free_token(token); free_arg(arg); return EVENT_ERROR; } static enum event_type process_array(struct event_format *event, struct print_arg *top, char **tok) { struct print_arg *arg; enum event_type type; char *token = NULL; arg = alloc_arg(); if (!arg) { do_warning_event(event, "%s: not enough memory!", __func__); /* '*tok' is set to top->op.op. No need to free. */ *tok = NULL; return EVENT_ERROR; } *tok = NULL; type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "]")) goto out_free; top->op.right = arg; free_token(token); type = read_token_item(&token); *tok = token; return type; out_free: free_token(token); free_arg(arg); return EVENT_ERROR; } static int get_op_prio(char *op) { if (!op[1]) { switch (op[0]) { case '~': case '!': return 4; case '*': case '/': case '%': return 6; case '+': case '-': return 7; /* '>>' and '<<' are 8 */ case '<': case '>': return 9; /* '==' and '!=' are 10 */ case '&': return 11; case '^': return 12; case '|': return 13; case '?': return 16; default: do_warning("unknown op '%c'", op[0]); return -1; } } else { if (strcmp(op, "++") == 0 || strcmp(op, "--") == 0) { return 3; } else if (strcmp(op, ">>") == 0 || strcmp(op, "<<") == 0) { return 8; } else if (strcmp(op, ">=") == 0 || strcmp(op, "<=") == 0) { return 9; } else if (strcmp(op, "==") == 0 || strcmp(op, "!=") == 0) { return 10; } else if (strcmp(op, "&&") == 0) { return 14; } else if (strcmp(op, "||") == 0) { return 15; } else { do_warning("unknown op '%s'", op); return -1; } } } static int set_op_prio(struct print_arg *arg) { /* single ops are the greatest */ if (!arg->op.left || arg->op.left->type == PRINT_NULL) arg->op.prio = 0; else arg->op.prio = get_op_prio(arg->op.op); return arg->op.prio; } /* Note, *tok does not get freed, but will most likely be saved */ static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *left, *right = NULL; enum event_type type; char *token; /* the op is passed in via tok */ token = *tok; if (arg->type == PRINT_OP && !arg->op.left) { /* handle single op */ if (token[1]) { do_warning_event(event, "bad op token %s", token); goto out_free; } switch (token[0]) { case '~': case '!': case '+': case '-': break; default: do_warning_event(event, "bad op token %s", token); goto out_free; } /* make an empty left */ left = alloc_arg(); if (!left) goto out_warn_free; left->type = PRINT_NULL; arg->op.left = left; right = alloc_arg(); if (!right) goto out_warn_free; arg->op.right = right; /* do not free the token, it belongs to an op */ *tok = NULL; type = process_arg(event, right, tok); } else if (strcmp(token, "?") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; /* it will set arg->op.right */ type = process_cond(event, arg, tok); } else if (strcmp(token, ">>") == 0 || strcmp(token, "<<") == 0 || strcmp(token, "&") == 0 || strcmp(token, "|") == 0 || strcmp(token, "&&") == 0 || strcmp(token, "||") == 0 || strcmp(token, "-") == 0 || strcmp(token, "+") == 0 || strcmp(token, "*") == 0 || strcmp(token, "^") == 0 || strcmp(token, "/") == 0 || strcmp(token, "<") == 0 || strcmp(token, ">") == 0 || strcmp(token, "<=") == 0 || strcmp(token, ">=") == 0 || strcmp(token, "==") == 0 || strcmp(token, "!=") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; /* copy the top arg to the left */ *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.right = NULL; if (set_op_prio(arg) == -1) { event->flags |= EVENT_FL_FAILED; /* arg->op.op (= token) will be freed at out_free */ arg->op.op = NULL; goto out_free; } type = read_token_item(&token); *tok = token; /* could just be a type pointer */ if ((strcmp(arg->op.op, "*") == 0) && type == EVENT_DELIM && (strcmp(token, ")") == 0)) { char *new_atom; if (left->type != PRINT_ATOM) { do_warning_event(event, "bad pointer type"); goto out_free; } new_atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); if (!new_atom) goto out_warn_free; left->atom.atom = new_atom; strcat(left->atom.atom, " *"); free(arg->op.op); *arg = *left; free(left); return type; } right = alloc_arg(); if (!right) goto out_warn_free; type = process_arg_token(event, right, tok, type); arg->op.right = right; } else if (strcmp(token, "[") == 0) { left = alloc_arg(); if (!left) goto out_warn_free; *left = *arg; arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; arg->op.prio = 0; /* it will set arg->op.right */ type = process_array(event, arg, tok); } else { do_warning_event(event, "unknown op '%s'", token); event->flags |= EVENT_FL_FAILED; /* the arg is now the left side */ goto out_free; } if (type == EVENT_OP && strcmp(*tok, ":") != 0) { int prio; /* higher prios need to be closer to the root */ prio = get_op_prio(*tok); if (prio > arg->op.prio) return process_op(event, arg, tok); return process_op(event, right, tok); } return type; out_warn_free: do_warning_event(event, "%s: not enough memory!", __func__); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; char *field; char *token; if (read_expected(EVENT_OP, "->") < 0) goto out_err; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; field = token; arg->type = PRINT_FIELD; arg->field.name = field; if (is_flag_field) { arg->field.field = pevent_find_any_field(event, arg->field.name); arg->field.field->flags |= FIELD_IS_FLAG; is_flag_field = 0; } else if (is_symbolic_field) { arg->field.field = pevent_find_any_field(event, arg->field.name); arg->field.field->flags |= FIELD_IS_SYMBOLIC; is_symbolic_field = 0; } type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static char *arg_eval (struct print_arg *arg); static unsigned long long eval_type_str(unsigned long long val, const char *type, int pointer) { int sign = 0; char *ref; int len; len = strlen(type); if (pointer) { if (type[len-1] != '*') { do_warning("pointer expected with non pointer type"); return val; } ref = malloc(len); if (!ref) { do_warning("%s: not enough memory!", __func__); return val; } memcpy(ref, type, len); /* chop off the " *" */ ref[len - 2] = 0; val = eval_type_str(val, ref, 0); free(ref); return val; } /* check if this is a pointer */ if (type[len - 1] == '*') return val; /* Try to figure out the arg size*/ if (strncmp(type, "struct", 6) == 0) /* all bets off */ return val; if (strcmp(type, "u8") == 0) return val & 0xff; if (strcmp(type, "u16") == 0) return val & 0xffff; if (strcmp(type, "u32") == 0) return val & 0xffffffff; if (strcmp(type, "u64") == 0 || strcmp(type, "s64")) return val; if (strcmp(type, "s8") == 0) return (unsigned long long)(char)val & 0xff; if (strcmp(type, "s16") == 0) return (unsigned long long)(short)val & 0xffff; if (strcmp(type, "s32") == 0) return (unsigned long long)(int)val & 0xffffffff; if (strncmp(type, "unsigned ", 9) == 0) { sign = 0; type += 9; } if (strcmp(type, "char") == 0) { if (sign) return (unsigned long long)(char)val & 0xff; else return val & 0xff; } if (strcmp(type, "short") == 0) { if (sign) return (unsigned long long)(short)val & 0xffff; else return val & 0xffff; } if (strcmp(type, "int") == 0) { if (sign) return (unsigned long long)(int)val & 0xffffffff; else return val & 0xffffffff; } return val; } /* * Try to figure out the type. */ static unsigned long long eval_type(unsigned long long val, struct print_arg *arg, int pointer) { if (arg->type != PRINT_TYPE) { do_warning("expected type argument"); return 0; } return eval_type_str(val, arg->typecast.type, pointer); } static int arg_num_eval(struct print_arg *arg, long long *val) { long long left, right; int ret = 1; switch (arg->type) { case PRINT_ATOM: *val = strtoll(arg->atom.atom, NULL, 0); break; case PRINT_TYPE: ret = arg_num_eval(arg->typecast.item, val); if (!ret) break; *val = eval_type(*val, arg, 0); break; case PRINT_OP: switch (arg->op.op[0]) { case '|': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1]) *val = left || right; else *val = left | right; break; case '&': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1]) *val = left && right; else *val = left & right; break; case '<': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case 0: *val = left < right; break; case '<': *val = left << right; break; case '=': *val = left <= right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '>': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case 0: *val = left > right; break; case '>': *val = left >> right; break; case '=': *val = left >= right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '=': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; if (arg->op.op[1] != '=') { do_warning("unknown op '%s'", arg->op.op); ret = 0; } else *val = left == right; break; case '!': ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; switch (arg->op.op[1]) { case '=': *val = left != right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case '-': /* check for negative */ if (arg->op.left->type == PRINT_NULL) left = 0; else ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; *val = left - right; break; case '+': if (arg->op.left->type == PRINT_NULL) left = 0; else ret = arg_num_eval(arg->op.left, &left); if (!ret) break; ret = arg_num_eval(arg->op.right, &right); if (!ret) break; *val = left + right; break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; } break; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: case PRINT_BITMASK: default: do_warning("invalid eval type %d", arg->type); ret = 0; } return ret; } static char *arg_eval (struct print_arg *arg) { long long val; static char buf[20]; switch (arg->type) { case PRINT_ATOM: return arg->atom.atom; case PRINT_TYPE: return arg_eval(arg->typecast.item); case PRINT_OP: if (!arg_num_eval(arg, &val)) break; sprintf(buf, "%lld", val); return buf; case PRINT_NULL: case PRINT_FIELD ... PRINT_SYMBOL: case PRINT_STRING: case PRINT_BSTRING: case PRINT_BITMASK: default: do_warning("invalid eval type %d", arg->type); break; } return NULL; } static enum event_type process_fields(struct event_format *event, struct print_flag_sym **list, char **tok) { enum event_type type; struct print_arg *arg = NULL; struct print_flag_sym *field; char *token = *tok; char *value; do { free_token(token); type = read_token_item(&token); if (test_type_token(type, token, EVENT_OP, "{")) break; arg = alloc_arg(); if (!arg) goto out_free; free_token(token); type = process_arg(event, arg, &token); if (type == EVENT_OP) type = process_op(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; field = calloc(1, sizeof(*field)); if (!field) goto out_free; value = arg_eval(arg); if (value == NULL) goto out_free_field; field->value = strdup(value); if (field->value == NULL) goto out_free_field; free_arg(arg); arg = alloc_arg(); if (!arg) goto out_free; free_token(token); type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "}")) goto out_free_field; value = arg_eval(arg); if (value == NULL) goto out_free_field; field->str = strdup(value); if (field->str == NULL) goto out_free_field; free_arg(arg); arg = NULL; *list = field; list = &field->next; free_token(token); type = read_token_item(&token); } while (type == EVENT_DELIM && strcmp(token, ",") == 0); *tok = token; return type; out_free_field: free_flag_sym(field); out_free: free_arg(arg); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_flags(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token = NULL; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_FLAGS; field = alloc_arg(); if (!field) { do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } type = process_field_arg(event, field, &token); /* Handle operations in the first argument */ while (type == EVENT_OP) type = process_op(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free_field; free_token(token); arg->flags.field = field; type = read_token_item(&token); if (event_item_type(type)) { arg->flags.delim = token; type = read_token_item(&token); } if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; type = process_fields(event, &arg->flags.flags, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free_field: free_arg(field); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_symbols(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token = NULL; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_SYMBOL; field = alloc_arg(); if (!field) { do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } type = process_field_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free_field; arg->symbol.field = field; type = process_fields(event, &arg->symbol.symbols, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(tok); return type; out_free_field: free_arg(field); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_hex(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *field; enum event_type type; char *token = NULL; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_HEX; field = alloc_arg(); if (!field) { do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; arg->hex.field = field; free_token(token); field = alloc_arg(); if (!field) { do_warning_event(event, "%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; arg->hex.size = field; free_token(token); type = read_token_item(tok); return type; out_free: free_arg(field); free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) { struct format_field *field; enum event_type type; char *token; memset(arg, 0, sizeof(*arg)); arg->type = PRINT_DYNAMIC_ARRAY; /* * The item within the parenthesis is another field that holds * the index into where the array starts. */ type = read_token(&token); *tok = token; if (type != EVENT_ITEM) goto out_free; /* Find the field */ field = pevent_find_field(event, token); if (!field) goto out_free; arg->dynarray.field = field; arg->dynarray.index = 0; if (read_expected(EVENT_DELIM, ")") < 0) goto out_free; free_token(token); type = read_token_item(&token); *tok = token; if (type != EVENT_OP || strcmp(token, "[") != 0) return type; free_token(token); arg = alloc_arg(); if (!arg) { do_warning_event(event, "%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free_arg; if (!test_type_token(type, token, EVENT_OP, "]")) goto out_free_arg; free_token(token); type = read_token_item(tok); return type; out_free_arg: free_arg(arg); out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_paren(struct event_format *event, struct print_arg *arg, char **tok) { struct print_arg *item_arg; enum event_type type; char *token; type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (type == EVENT_OP) type = process_op(event, arg, &token); if (type == EVENT_ERROR) goto out_free; if (test_type_token(type, token, EVENT_DELIM, ")")) goto out_free; free_token(token); type = read_token_item(&token); /* * If the next token is an item or another open paren, then * this was a typecast. */ if (event_item_type(type) || (type == EVENT_DELIM && strcmp(token, "(") == 0)) { /* make this a typecast and contine */ /* prevous must be an atom */ if (arg->type != PRINT_ATOM) { do_warning_event(event, "previous needed to be PRINT_ATOM"); goto out_free; } item_arg = alloc_arg(); if (!item_arg) { do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } arg->type = PRINT_TYPE; arg->typecast.type = arg->atom.atom; arg->typecast.item = item_arg; type = process_arg_token(event, item_arg, &token, type); } *tok = token; return type; out_free: free_token(token); *tok = NULL; return EVENT_ERROR; } static enum event_type process_str(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; char *token; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; arg->type = PRINT_STRING; arg->string.string = token; arg->string.offset = -1; if (read_expected(EVENT_DELIM, ")") < 0) goto out_err; type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static enum event_type process_bitmask(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; char *token; if (read_expect_type(EVENT_ITEM, &token) < 0) goto out_free; arg->type = PRINT_BITMASK; arg->bitmask.bitmask = token; arg->bitmask.offset = -1; if (read_expected(EVENT_DELIM, ")") < 0) goto out_err; type = read_token(&token); *tok = token; return type; out_free: free_token(token); out_err: *tok = NULL; return EVENT_ERROR; } static struct pevent_function_handler * find_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; if (!pevent) return NULL; for (func = pevent->func_handlers; func; func = func->next) { if (strcmp(func->name, func_name) == 0) break; } return func; } static void remove_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; struct pevent_function_handler **next; next = &pevent->func_handlers; while ((func = *next)) { if (strcmp(func->name, func_name) == 0) { *next = func->next; free_func_handle(func); break; } next = &func->next; } } static enum event_type process_func_handler(struct event_format *event, struct pevent_function_handler *func, struct print_arg *arg, char **tok) { struct print_arg **next_arg; struct print_arg *farg; enum event_type type; char *token; int i; arg->type = PRINT_FUNC; arg->func.func = func; *tok = NULL; next_arg = &(arg->func.args); for (i = 0; i < func->nr_args; i++) { farg = alloc_arg(); if (!farg) { do_warning_event(event, "%s: not enough memory!", __func__); return EVENT_ERROR; } type = process_arg(event, farg, &token); if (i < (func->nr_args - 1)) { if (type != EVENT_DELIM || strcmp(token, ",") != 0) { do_warning_event(event, "Error: function '%s()' expects %d arguments but event %s only uses %d", func->name, func->nr_args, event->name, i + 1); goto err; } } else { if (type != EVENT_DELIM || strcmp(token, ")") != 0) { do_warning_event(event, "Error: function '%s()' only expects %d arguments but event %s has more", func->name, func->nr_args, event->name); goto err; } } *next_arg = farg; next_arg = &(farg->next); free_token(token); } type = read_token(&token); *tok = token; return type; err: free_arg(farg); free_token(token); return EVENT_ERROR; } static enum event_type process_function(struct event_format *event, struct print_arg *arg, char *token, char **tok) { struct pevent_function_handler *func; if (strcmp(token, "__print_flags") == 0) { free_token(token); is_flag_field = 1; return process_flags(event, arg, tok); } if (strcmp(token, "__print_symbolic") == 0) { free_token(token); is_symbolic_field = 1; return process_symbols(event, arg, tok); } if (strcmp(token, "__print_hex") == 0) { free_token(token); return process_hex(event, arg, tok); } if (strcmp(token, "__get_str") == 0) { free_token(token); return process_str(event, arg, tok); } if (strcmp(token, "__get_bitmask") == 0) { free_token(token); return process_bitmask(event, arg, tok); } if (strcmp(token, "__get_dynamic_array") == 0) { free_token(token); return process_dynamic_array(event, arg, tok); } func = find_func_handler(event->pevent, token); if (func) { free_token(token); return process_func_handler(event, func, arg, tok); } do_warning_event(event, "function %s not defined", token); free_token(token); return EVENT_ERROR; } static enum event_type process_arg_token(struct event_format *event, struct print_arg *arg, char **tok, enum event_type type) { char *token; char *atom; token = *tok; switch (type) { case EVENT_ITEM: if (strcmp(token, "REC") == 0) { free_token(token); type = process_entry(event, arg, &token); break; } atom = token; /* test the next token */ type = read_token_item(&token); /* * If the next token is a parenthesis, then this * is a function. */ if (type == EVENT_DELIM && strcmp(token, "(") == 0) { free_token(token); token = NULL; /* this will free atom. */ type = process_function(event, arg, atom, &token); break; } /* atoms can be more than one token long */ while (type == EVENT_ITEM) { char *new_atom; new_atom = realloc(atom, strlen(atom) + strlen(token) + 2); if (!new_atom) { free(atom); *tok = NULL; free_token(token); return EVENT_ERROR; } atom = new_atom; strcat(atom, " "); strcat(atom, token); free_token(token); type = read_token_item(&token); } arg->type = PRINT_ATOM; arg->atom.atom = atom; break; case EVENT_DQUOTE: case EVENT_SQUOTE: arg->type = PRINT_ATOM; arg->atom.atom = token; type = read_token_item(&token); break; case EVENT_DELIM: if (strcmp(token, "(") == 0) { free_token(token); type = process_paren(event, arg, &token); break; } case EVENT_OP: /* handle single ops */ arg->type = PRINT_OP; arg->op.op = token; arg->op.left = NULL; type = process_op(event, arg, &token); /* On error, the op is freed */ if (type == EVENT_ERROR) arg->op.op = NULL; /* return error type if errored */ break; case EVENT_ERROR ... EVENT_NEWLINE: default: do_warning_event(event, "unexpected type %d", type); return EVENT_ERROR; } *tok = token; return type; } static int event_read_print_args(struct event_format *event, struct print_arg **list) { enum event_type type = EVENT_ERROR; struct print_arg *arg; char *token; int args = 0; do { if (type == EVENT_NEWLINE) { type = read_token_item(&token); continue; } arg = alloc_arg(); if (!arg) { do_warning_event(event, "%s: not enough memory!", __func__); return -1; } type = process_arg(event, arg, &token); if (type == EVENT_ERROR) { free_token(token); free_arg(arg); return -1; } *list = arg; args++; if (type == EVENT_OP) { type = process_op(event, arg, &token); free_token(token); if (type == EVENT_ERROR) { *list = NULL; free_arg(arg); return -1; } list = &arg->next; continue; } if (type == EVENT_DELIM && strcmp(token, ",") == 0) { free_token(token); *list = arg; list = &arg->next; continue; } break; } while (type != EVENT_NONE); if (type != EVENT_NONE && type != EVENT_ERROR) free_token(token); return args; } static int event_read_print(struct event_format *event) { enum event_type type; char *token; int ret; if (read_expected_item(EVENT_ITEM, "print") < 0) return -1; if (read_expected(EVENT_ITEM, "fmt") < 0) return -1; if (read_expected(EVENT_OP, ":") < 0) return -1; if (read_expect_type(EVENT_DQUOTE, &token) < 0) goto fail; concat: event->print_fmt.format = token; event->print_fmt.args = NULL; /* ok to have no arg */ type = read_token_item(&token); if (type == EVENT_NONE) return 0; /* Handle concatenation of print lines */ if (type == EVENT_DQUOTE) { char *cat; if (asprintf(&cat, "%s%s", event->print_fmt.format, token) < 0) goto fail; free_token(token); free_token(event->print_fmt.format); event->print_fmt.format = NULL; token = cat; goto concat; } if (test_type_token(type, token, EVENT_DELIM, ",")) goto fail; free_token(token); ret = event_read_print_args(event, &event->print_fmt.args); if (ret < 0) return -1; return ret; fail: free_token(token); return -1; } /** * pevent_find_common_field - return a common field by event * @event: handle for the event * @name: the name of the common field to return * * Returns a common field from the event by the given @name. * This only searchs the common fields and not all field. */ struct format_field * pevent_find_common_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.common_fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_field - find a non-common field * @event: handle for the event * @name: the name of the non-common field * * Returns a non-common field by the given @name. * This does not search common fields. */ struct format_field * pevent_find_field(struct event_format *event, const char *name) { struct format_field *format; for (format = event->format.fields; format; format = format->next) { if (strcmp(format->name, name) == 0) break; } return format; } /** * pevent_find_any_field - find any field by name * @event: handle for the event * @name: the name of the field * * Returns a field by the given @name. * This searchs the common field names first, then * the non-common ones if a common one was not found. */ struct format_field * pevent_find_any_field(struct event_format *event, const char *name) { struct format_field *format; format = pevent_find_common_field(event, name); if (format) return format; return pevent_find_field(event, name); } /** * pevent_read_number - read a number from data * @pevent: handle for the pevent * @ptr: the raw data * @size: the size of the data that holds the number * * Returns the number (converted to host) from the * raw data. */ unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size) { switch (size) { case 1: return *(unsigned char *)ptr; case 2: return data2host2(pevent, ptr); case 4: return data2host4(pevent, ptr); case 8: return data2host8(pevent, ptr); default: /* BUG! */ return 0; } } /** * pevent_read_number_field - read a number from data * @field: a handle to the field * @data: the raw data to read * @value: the value to place the number in * * Reads raw data according to a field offset and size, * and translates it into @value. * * Returns 0 on success, -1 otherwise. */ int pevent_read_number_field(struct format_field *field, const void *data, unsigned long long *value) { if (!field) return -1; switch (field->size) { case 1: case 2: case 4: case 8: *value = pevent_read_number(field->event->pevent, data + field->offset, field->size); return 0; default: return -1; } } static int get_common_info(struct pevent *pevent, const char *type, int *offset, int *size) { struct event_format *event; struct format_field *field; /* * All events should have the same common elements. * Pick any event to find where the type is; */ if (!pevent->events) { do_warning("no event_list!"); return -1; } event = pevent->events[0]; field = pevent_find_common_field(event, type); if (!field) return -1; *offset = field->offset; *size = field->size; return 0; } static int __parse_common(struct pevent *pevent, void *data, int *size, int *offset, const char *name) { int ret; if (!*size) { ret = get_common_info(pevent, name, offset, size); if (ret < 0) return ret; } return pevent_read_number(pevent, data + *offset, *size); } static int trace_parse_common_type(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->type_size, &pevent->type_offset, "common_type"); } static int parse_common_pid(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pid_size, &pevent->pid_offset, "common_pid"); } static int parse_common_pc(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->pc_size, &pevent->pc_offset, "common_preempt_count"); } static int parse_common_flags(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->flags_size, &pevent->flags_offset, "common_flags"); } static int parse_common_lock_depth(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->ld_size, &pevent->ld_offset, "common_lock_depth"); } static int parse_common_migrate_disable(struct pevent *pevent, void *data) { return __parse_common(pevent, data, &pevent->ld_size, &pevent->ld_offset, "common_migrate_disable"); } static int events_id_cmp(const void *a, const void *b); /** * pevent_find_event - find an event by given id * @pevent: a handle to the pevent * @id: the id of the event * * Returns an event that has a given @id. */ struct event_format *pevent_find_event(struct pevent *pevent, int id) { struct event_format **eventptr; struct event_format key; struct event_format *pkey = &key; /* Check cache first */ if (pevent->last_event && pevent->last_event->id == id) return pevent->last_event; key.id = id; eventptr = bsearch(&pkey, pevent->events, pevent->nr_events, sizeof(*pevent->events), events_id_cmp); if (eventptr) { pevent->last_event = *eventptr; return *eventptr; } return NULL; } /** * pevent_find_event_by_name - find an event by given name * @pevent: a handle to the pevent * @sys: the system name to search for * @name: the name of the event to search for * * This returns an event with a given @name and under the system * @sys. If @sys is NULL the first event with @name is returned. */ struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name) { struct event_format *event; int i; if (pevent->last_event && strcmp(pevent->last_event->name, name) == 0 && (!sys || strcmp(pevent->last_event->system, sys) == 0)) return pevent->last_event; for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (strcmp(event->name, name) == 0) { if (!sys) break; if (strcmp(event->system, sys) == 0) break; } } if (i == pevent->nr_events) event = NULL; pevent->last_event = event; return event; } static unsigned long long eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent *pevent = event->pevent; unsigned long long val = 0; unsigned long long left, right; struct print_arg *typearg = NULL; struct print_arg *larg; unsigned long offset; unsigned int field_size; switch (arg->type) { case PRINT_NULL: /* ?? */ return 0; case PRINT_ATOM: return strtoull(arg->atom.atom, NULL, 0); case PRINT_FIELD: if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) goto out_warning_field; } /* must be a number */ val = pevent_read_number(pevent, data + arg->field.field->offset, arg->field.field->size); break; case PRINT_FLAGS: case PRINT_SYMBOL: case PRINT_HEX: break; case PRINT_TYPE: val = eval_num_arg(data, size, event, arg->typecast.item); return eval_type(val, arg, 0); case PRINT_STRING: case PRINT_BSTRING: case PRINT_BITMASK: return 0; case PRINT_FUNC: { struct trace_seq s; trace_seq_init(&s); val = process_defined_func(&s, data, size, event, arg); trace_seq_destroy(&s); return val; } case PRINT_OP: if (strcmp(arg->op.op, "[") == 0) { /* * Arrays are special, since we don't want * to read the arg as is. */ right = eval_num_arg(data, size, event, arg->op.right); /* handle typecasts */ larg = arg->op.left; while (larg->type == PRINT_TYPE) { if (!typearg) typearg = larg; larg = larg->typecast.item; } /* Default to long size */ field_size = pevent->long_size; switch (larg->type) { case PRINT_DYNAMIC_ARRAY: offset = pevent_read_number(pevent, data + larg->dynarray.field->offset, larg->dynarray.field->size); if (larg->dynarray.field->elementsize) field_size = larg->dynarray.field->elementsize; /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset &= 0xffff; offset += right; break; case PRINT_FIELD: if (!larg->field.field) { larg->field.field = pevent_find_any_field(event, larg->field.name); if (!larg->field.field) { arg = larg; goto out_warning_field; } } field_size = larg->field.field->elementsize; offset = larg->field.field->offset + right * larg->field.field->elementsize; break; default: goto default_op; /* oops, all bets off */ } val = pevent_read_number(pevent, data + offset, field_size); if (typearg) val = eval_type(val, typearg, 1); break; } else if (strcmp(arg->op.op, "?") == 0) { left = eval_num_arg(data, size, event, arg->op.left); arg = arg->op.right; if (left) val = eval_num_arg(data, size, event, arg->op.left); else val = eval_num_arg(data, size, event, arg->op.right); break; } default_op: left = eval_num_arg(data, size, event, arg->op.left); right = eval_num_arg(data, size, event, arg->op.right); switch (arg->op.op[0]) { case '!': switch (arg->op.op[1]) { case 0: val = !right; break; case '=': val = left != right; break; default: goto out_warning_op; } break; case '~': val = ~right; break; case '|': if (arg->op.op[1]) val = left || right; else val = left | right; break; case '&': if (arg->op.op[1]) val = left && right; else val = left & right; break; case '<': switch (arg->op.op[1]) { case 0: val = left < right; break; case '<': val = left << right; break; case '=': val = left <= right; break; default: goto out_warning_op; } break; case '>': switch (arg->op.op[1]) { case 0: val = left > right; break; case '>': val = left >> right; break; case '=': val = left >= right; break; default: goto out_warning_op; } break; case '=': if (arg->op.op[1] != '=') goto out_warning_op; val = left == right; break; case '-': val = left - right; break; case '+': val = left + right; break; case '/': val = left / right; break; case '*': val = left * right; break; default: goto out_warning_op; } break; case PRINT_DYNAMIC_ARRAY: /* Without [], we pass the address to the dynamic data */ offset = pevent_read_number(pevent, data + arg->dynarray.field->offset, arg->dynarray.field->size); /* * The actual length of the dynamic array is stored * in the top half of the field, and the offset * is in the bottom half of the 32 bit field. */ offset &= 0xffff; val = (unsigned long long)((unsigned long)data + offset); break; default: /* not sure what to do there */ return 0; } return val; out_warning_op: do_warning_event(event, "%s: unknown op '%s'", __func__, arg->op.op); return 0; out_warning_field: do_warning_event(event, "%s: field %s not found", __func__, arg->field.name); return 0; } struct flag { const char *name; unsigned long long value; }; static const struct flag flags[] = { { "HI_SOFTIRQ", 0 }, { "TIMER_SOFTIRQ", 1 }, { "NET_TX_SOFTIRQ", 2 }, { "NET_RX_SOFTIRQ", 3 }, { "BLOCK_SOFTIRQ", 4 }, { "BLOCK_IOPOLL_SOFTIRQ", 5 }, { "TASKLET_SOFTIRQ", 6 }, { "SCHED_SOFTIRQ", 7 }, { "HRTIMER_SOFTIRQ", 8 }, { "RCU_SOFTIRQ", 9 }, { "HRTIMER_NORESTART", 0 }, { "HRTIMER_RESTART", 1 }, }; static unsigned long long eval_flag(const char *flag) { int i; /* * Some flags in the format files do not get converted. * If the flag is not numeric, see if it is something that * we already know about. */ if (isdigit(flag[0])) return strtoull(flag, NULL, 0); for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++) if (strcmp(flags[i].name, flag) == 0) return flags[i].value; return 0; } static void print_str_to_seq(struct trace_seq *s, const char *format, int len_arg, const char *str) { if (len_arg >= 0) trace_seq_printf(s, format, len_arg, str); else trace_seq_printf(s, format, str); } static void print_bitmask_to_seq(struct pevent *pevent, struct trace_seq *s, const char *format, int len_arg, const void *data, int size) { int nr_bits = size * 8; int str_size = (nr_bits + 3) / 4; int len = 0; char buf[3]; char *str; int index; int i; /* * The kernel likes to put in commas every 32 bits, we * can do the same. */ str_size += (nr_bits - 1) / 32; str = malloc(str_size + 1); if (!str) { do_warning("%s: not enough memory!", __func__); return; } str[str_size] = 0; /* Start out with -2 for the two chars per byte */ for (i = str_size - 2; i >= 0; i -= 2) { /* * data points to a bit mask of size bytes. * In the kernel, this is an array of long words, thus * endianess is very important. */ if (pevent->file_bigendian) index = size - (len + 1); else index = len; snprintf(buf, 3, "%02x", *((unsigned char *)data + index)); memcpy(str + i, buf, 2); len++; if (!(len & 3) && i > 0) { i--; str[i] = ','; } } if (len_arg >= 0) trace_seq_printf(s, format, len_arg, str); else trace_seq_printf(s, format, str); free(str); } static void print_str_arg(struct trace_seq *s, void *data, int size, struct event_format *event, const char *format, int len_arg, struct print_arg *arg) { struct pevent *pevent = event->pevent; struct print_flag_sym *flag; struct format_field *field; struct printk_map *printk; unsigned long long val, fval; unsigned long addr; char *str; unsigned char *hex; int print; int i, len; switch (arg->type) { case PRINT_NULL: /* ?? */ return; case PRINT_ATOM: print_str_to_seq(s, format, len_arg, arg->atom.atom); return; case PRINT_FIELD: field = arg->field.field; if (!field) { field = pevent_find_any_field(event, arg->field.name); if (!field) { str = arg->field.name; goto out_warning_field; } arg->field.field = field; } /* Zero sized fields, mean the rest of the data */ len = field->size ? : size - field->offset; /* * Some events pass in pointers. If this is not an array * and the size is the same as long_size, assume that it * is a pointer. */ if (!(field->flags & FIELD_IS_ARRAY) && field->size == pevent->long_size) { addr = *(unsigned long *)(data + field->offset); /* Check if it matches a print format */ printk = find_printk(pevent, addr); if (printk) trace_seq_puts(s, printk->printk); else trace_seq_printf(s, "%lx", addr); break; } str = malloc(len + 1); if (!str) { do_warning_event(event, "%s: not enough memory!", __func__); return; } memcpy(str, data + field->offset, len); str[len] = 0; print_str_to_seq(s, format, len_arg, str); free(str); break; case PRINT_FLAGS: val = eval_num_arg(data, size, event, arg->flags.field); print = 0; for (flag = arg->flags.flags; flag; flag = flag->next) { fval = eval_flag(flag->value); if (!val && !fval) { print_str_to_seq(s, format, len_arg, flag->str); break; } if (fval && (val & fval) == fval) { if (print && arg->flags.delim) trace_seq_puts(s, arg->flags.delim); print_str_to_seq(s, format, len_arg, flag->str); print = 1; val &= ~fval; } } break; case PRINT_SYMBOL: val = eval_num_arg(data, size, event, arg->symbol.field); for (flag = arg->symbol.symbols; flag; flag = flag->next) { fval = eval_flag(flag->value); if (val == fval) { print_str_to_seq(s, format, len_arg, flag->str); break; } } break; case PRINT_HEX: if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) { unsigned long offset; offset = pevent_read_number(pevent, data + arg->hex.field->dynarray.field->offset, arg->hex.field->dynarray.field->size); hex = data + (offset & 0xffff); } else { field = arg->hex.field->field.field; if (!field) { str = arg->hex.field->field.name; field = pevent_find_any_field(event, str); if (!field) goto out_warning_field; arg->hex.field->field.field = field; } hex = data + field->offset; } len = eval_num_arg(data, size, event, arg->hex.size); for (i = 0; i < len; i++) { if (i) trace_seq_putc(s, ' '); trace_seq_printf(s, "%02x", hex[i]); } break; case PRINT_TYPE: break; case PRINT_STRING: { int str_offset; if (arg->string.offset == -1) { struct format_field *f; f = pevent_find_any_field(event, arg->string.string); arg->string.offset = f->offset; } str_offset = data2host4(pevent, data + arg->string.offset); str_offset &= 0xffff; print_str_to_seq(s, format, len_arg, ((char *)data) + str_offset); break; } case PRINT_BSTRING: print_str_to_seq(s, format, len_arg, arg->string.string); break; case PRINT_BITMASK: { int bitmask_offset; int bitmask_size; if (arg->bitmask.offset == -1) { struct format_field *f; f = pevent_find_any_field(event, arg->bitmask.bitmask); arg->bitmask.offset = f->offset; } bitmask_offset = data2host4(pevent, data + arg->bitmask.offset); bitmask_size = bitmask_offset >> 16; bitmask_offset &= 0xffff; print_bitmask_to_seq(pevent, s, format, len_arg, data + bitmask_offset, bitmask_size); break; } case PRINT_OP: /* * The only op for string should be ? : */ if (arg->op.op[0] != '?') return; val = eval_num_arg(data, size, event, arg->op.left); if (val) print_str_arg(s, data, size, event, format, len_arg, arg->op.right->op.left); else print_str_arg(s, data, size, event, format, len_arg, arg->op.right->op.right); break; case PRINT_FUNC: process_defined_func(s, data, size, event, arg); break; default: /* well... */ break; } return; out_warning_field: do_warning_event(event, "%s: field %s not found", __func__, arg->field.name); } static unsigned long long process_defined_func(struct trace_seq *s, void *data, int size, struct event_format *event, struct print_arg *arg) { struct pevent_function_handler *func_handle = arg->func.func; struct pevent_func_params *param; unsigned long long *args; unsigned long long ret; struct print_arg *farg; struct trace_seq str; struct save_str { struct save_str *next; char *str; } *strings = NULL, *string; int i; if (!func_handle->nr_args) { ret = (*func_handle->func)(s, NULL); goto out; } farg = arg->func.args; param = func_handle->params; ret = ULLONG_MAX; args = malloc(sizeof(*args) * func_handle->nr_args); if (!args) goto out; for (i = 0; i < func_handle->nr_args; i++) { switch (param->type) { case PEVENT_FUNC_ARG_INT: case PEVENT_FUNC_ARG_LONG: case PEVENT_FUNC_ARG_PTR: args[i] = eval_num_arg(data, size, event, farg); break; case PEVENT_FUNC_ARG_STRING: trace_seq_init(&str); print_str_arg(&str, data, size, event, "%s", -1, farg); trace_seq_terminate(&str); string = malloc(sizeof(*string)); if (!string) { do_warning_event(event, "%s(%d): malloc str", __func__, __LINE__); goto out_free; } string->next = strings; string->str = strdup(str.buffer); if (!string->str) { free(string); do_warning_event(event, "%s(%d): malloc str", __func__, __LINE__); goto out_free; } args[i] = (uintptr_t)string->str; strings = string; trace_seq_destroy(&str); break; default: /* * Something went totally wrong, this is not * an input error, something in this code broke. */ do_warning_event(event, "Unexpected end of arguments\n"); goto out_free; } farg = farg->next; param = param->next; } ret = (*func_handle->func)(s, args); out_free: free(args); while (strings) { string = strings; strings = string->next; free(string->str); free(string); } out: /* TBD : handle return type here */ return ret; } static void free_args(struct print_arg *args) { struct print_arg *next; while (args) { next = args->next; free_arg(args); args = next; } } static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct format_field *field, *ip_field; struct print_arg *args, *arg, **next; unsigned long long ip, val; char *ptr; void *bptr; int vsize; field = pevent->bprint_buf_field; ip_field = pevent->bprint_ip_field; if (!field) { field = pevent_find_field(event, "buf"); if (!field) { do_warning_event(event, "can't find buffer field for binary printk"); return NULL; } ip_field = pevent_find_field(event, "ip"); if (!ip_field) { do_warning_event(event, "can't find ip field for binary printk"); return NULL; } pevent->bprint_buf_field = field; pevent->bprint_ip_field = ip_field; } ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size); /* * The first arg is the IP pointer. */ args = alloc_arg(); if (!args) { do_warning_event(event, "%s(%d): not enough memory!", __func__, __LINE__); return NULL; } arg = args; arg->next = NULL; next = &arg->next; arg->type = PRINT_ATOM; if (asprintf(&arg->atom.atom, "%lld", ip) < 0) goto out_free; /* skip the first "%pf: " */ for (ptr = fmt + 5, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; if (*ptr == '%') { process_again: ptr++; switch (*ptr) { case '%': break; case 'l': ls++; goto process_again; case 'L': ls = 2; goto process_again; case '0' ... '9': goto process_again; case '.': goto process_again; case 'z': case 'Z': ls = 1; goto process_again; case 'p': ls = 1; /* fall through */ case 'd': case 'u': case 'x': case 'i': switch (ls) { case 0: vsize = 4; break; case 1: vsize = pevent->long_size; break; case 2: vsize = 8; break; default: vsize = ls; /* ? */ break; } /* fall through */ case '*': if (*ptr == '*') vsize = 4; /* the pointers are always 4 bytes aligned */ bptr = (void *)(((unsigned long)bptr + 3) & ~3); val = pevent_read_number(pevent, bptr, vsize); bptr += vsize; arg = alloc_arg(); if (!arg) { do_warning_event(event, "%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } arg->next = NULL; arg->type = PRINT_ATOM; if (asprintf(&arg->atom.atom, "%lld", val) < 0) { free(arg); goto out_free; } *next = arg; next = &arg->next; /* * The '*' case means that an arg is used as the length. * We need to continue to figure out for what. */ if (*ptr == '*') goto process_again; break; case 's': arg = alloc_arg(); if (!arg) { do_warning_event(event, "%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } arg->next = NULL; arg->type = PRINT_BSTRING; arg->string.string = strdup(bptr); if (!arg->string.string) goto out_free; bptr += strlen(bptr) + 1; *next = arg; next = &arg->next; default: break; } } } return args; out_free: free_args(args); return NULL; } static char * get_bprint_format(void *data, int size __maybe_unused, struct event_format *event) { struct pevent *pevent = event->pevent; unsigned long long addr; struct format_field *field; struct printk_map *printk; char *format; field = pevent->bprint_fmt_field; if (!field) { field = pevent_find_field(event, "fmt"); if (!field) { do_warning_event(event, "can't find format field for binary printk"); return NULL; } pevent->bprint_fmt_field = field; } addr = pevent_read_number(pevent, data + field->offset, field->size); printk = find_printk(pevent, addr); if (!printk) { if (asprintf(&format, "%%pf: (NO FORMAT FOUND at %llx)\n", addr) < 0) return NULL; return format; } if (asprintf(&format, "%s: %s", "%pf", printk->printk) < 0) return NULL; return format; } static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, struct event_format *event, struct print_arg *arg) { unsigned char *buf; const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return; } if (mac == 'm') fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { do_warning_event(event, "%s: field %s not found", __func__, arg->field.name); return; } } if (arg->field.field->size != 6) { trace_seq_printf(s, "INVALIDMAC"); return; } buf = data + arg->field.field->offset; trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); } static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf) { const char *fmt; if (i == 'i') fmt = "%03d.%03d.%03d.%03d"; else fmt = "%d.%d.%d.%d"; trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]); } static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) { return ((unsigned long)(a->s6_addr32[0] | a->s6_addr32[1]) | (unsigned long)(a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0UL; } static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) { return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); } static void print_ip6c_addr(struct trace_seq *s, unsigned char *addr) { int i, j, range; unsigned char zerolength[8]; int longest = 1; int colonpos = -1; uint16_t word; uint8_t hi, lo; bool needcolon = false; bool useIPv4; struct in6_addr in6; memcpy(&in6, addr, sizeof(struct in6_addr)); useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); memset(zerolength, 0, sizeof(zerolength)); if (useIPv4) range = 6; else range = 8; /* find position of longest 0 run */ for (i = 0; i < range; i++) { for (j = i; j < range; j++) { if (in6.s6_addr16[j] != 0) break; zerolength[i]++; } } for (i = 0; i < range; i++) { if (zerolength[i] > longest) { longest = zerolength[i]; colonpos = i; } } if (longest == 1) /* don't compress a single 0 */ colonpos = -1; /* emit address */ for (i = 0; i < range; i++) { if (i == colonpos) { if (needcolon || i == 0) trace_seq_printf(s, ":"); trace_seq_printf(s, ":"); needcolon = false; i += longest - 1; continue; } if (needcolon) { trace_seq_printf(s, ":"); needcolon = false; } /* hex u16 without leading 0s */ word = ntohs(in6.s6_addr16[i]); hi = word >> 8; lo = word & 0xff; if (hi) trace_seq_printf(s, "%x%02x", hi, lo); else trace_seq_printf(s, "%x", lo); needcolon = true; } if (useIPv4) { if (needcolon) trace_seq_printf(s, ":"); print_ip4_addr(s, 'I', &in6.s6_addr[12]); } return; } static void print_ip6_addr(struct trace_seq *s, char i, unsigned char *buf) { int j; for (j = 0; j < 16; j += 2) { trace_seq_printf(s, "%02x%02x", buf[j], buf[j+1]); if (i == 'I' && j < 14) trace_seq_printf(s, ":"); } } /* * %pi4 print an IPv4 address with leading zeros * %pI4 print an IPv4 address without leading zeros * %pi6 print an IPv6 address without colons * %pI6 print an IPv6 address with colons * %pI6c print an IPv6 address in compressed form with colons * %pISpc print an IP address based on sockaddr; p adds port. */ static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i, void *data, int size, struct event_format *event, struct print_arg *arg) { unsigned char *buf; if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return 0; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return 0; } if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { do_warning("%s: field %s not found", __func__, arg->field.name); return 0; } } buf = data + arg->field.field->offset; if (arg->field.field->size != 4) { trace_seq_printf(s, "INVALIDIPv4"); return 0; } print_ip4_addr(s, i, buf); return 0; } static int print_ipv6_arg(struct trace_seq *s, const char *ptr, char i, void *data, int size, struct event_format *event, struct print_arg *arg) { char have_c = 0; unsigned char *buf; int rc = 0; /* pI6c */ if (i == 'I' && *ptr == 'c') { have_c = 1; ptr++; rc++; } if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return rc; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return rc; } if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { do_warning("%s: field %s not found", __func__, arg->field.name); return rc; } } buf = data + arg->field.field->offset; if (arg->field.field->size != 16) { trace_seq_printf(s, "INVALIDIPv6"); return rc; } if (have_c) print_ip6c_addr(s, buf); else print_ip6_addr(s, i, buf); return rc; } static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i, void *data, int size, struct event_format *event, struct print_arg *arg) { char have_c = 0, have_p = 0; unsigned char *buf; struct sockaddr_storage *sa; int rc = 0; /* pISpc */ if (i == 'I') { if (*ptr == 'p') { have_p = 1; ptr++; rc++; } if (*ptr == 'c') { have_c = 1; ptr++; rc++; } } if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); return rc; } if (arg->type != PRINT_FIELD) { trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); return rc; } if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { do_warning("%s: field %s not found", __func__, arg->field.name); return rc; } } sa = (struct sockaddr_storage *) (data + arg->field.field->offset); if (sa->ss_family == AF_INET) { struct sockaddr_in *sa4 = (struct sockaddr_in *) sa; if (arg->field.field->size < sizeof(struct sockaddr_in)) { trace_seq_printf(s, "INVALIDIPv4"); return rc; } print_ip4_addr(s, i, (unsigned char *) &sa4->sin_addr); if (have_p) trace_seq_printf(s, ":%d", ntohs(sa4->sin_port)); } else if (sa->ss_family == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; if (arg->field.field->size < sizeof(struct sockaddr_in6)) { trace_seq_printf(s, "INVALIDIPv6"); return rc; } if (have_p) trace_seq_printf(s, "["); buf = (unsigned char *) &sa6->sin6_addr; if (have_c) print_ip6c_addr(s, buf); else print_ip6_addr(s, i, buf); if (have_p) trace_seq_printf(s, "]:%d", ntohs(sa6->sin6_port)); } return rc; } static int print_ip_arg(struct trace_seq *s, const char *ptr, void *data, int size, struct event_format *event, struct print_arg *arg) { char i = *ptr; /* 'i' or 'I' */ char ver; int rc = 0; ptr++; rc++; ver = *ptr; ptr++; rc++; switch (ver) { case '4': rc += print_ipv4_arg(s, ptr, i, data, size, event, arg); break; case '6': rc += print_ipv6_arg(s, ptr, i, data, size, event, arg); break; case 'S': rc += print_ipsa_arg(s, ptr, i, data, size, event, arg); break; default: return 0; } return rc; } static int is_printable_array(char *p, unsigned int len) { unsigned int i; for (i = 0; i < len && p[i]; i++) if (!isprint(p[i]) && !isspace(p[i])) return 0; return 1; } static void print_event_fields(struct trace_seq *s, void *data, int size __maybe_unused, struct event_format *event) { struct format_field *field; unsigned long long val; unsigned int offset, len, i; field = event->format.fields; while (field) { trace_seq_printf(s, " %s=", field->name); if (field->flags & FIELD_IS_ARRAY) { offset = field->offset; len = field->size; if (field->flags & FIELD_IS_DYNAMIC) { val = pevent_read_number(event->pevent, data + offset, len); offset = val; len = offset >> 16; offset &= 0xffff; } if (field->flags & FIELD_IS_STRING && is_printable_array(data + offset, len)) { trace_seq_printf(s, "%s", (char *)data + offset); } else { trace_seq_puts(s, "ARRAY["); for (i = 0; i < len; i++) { if (i) trace_seq_puts(s, ", "); trace_seq_printf(s, "%02x", *((unsigned char *)data + offset + i)); } trace_seq_putc(s, ']'); field->flags &= ~FIELD_IS_STRING; } } else { val = pevent_read_number(event->pevent, data + field->offset, field->size); if (field->flags & FIELD_IS_POINTER) { trace_seq_printf(s, "0x%llx", val); } else if (field->flags & FIELD_IS_SIGNED) { switch (field->size) { case 4: /* * If field is long then print it in hex. * A long usually stores pointers. */ if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%x", (int)val); else trace_seq_printf(s, "%d", (int)val); break; case 2: trace_seq_printf(s, "%2d", (short)val); break; case 1: trace_seq_printf(s, "%1d", (char)val); break; default: trace_seq_printf(s, "%lld", val); } } else { if (field->flags & FIELD_IS_LONG) trace_seq_printf(s, "0x%llx", val); else trace_seq_printf(s, "%llu", val); } } field = field->next; } } static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; struct print_fmt *print_fmt = &event->print_fmt; struct print_arg *arg = print_fmt->args; struct print_arg *args = NULL; const char *ptr = print_fmt->format; unsigned long long val; struct func_map *func; const char *saveptr; struct trace_seq p; char *bprint_fmt = NULL; char format[32]; int show_func; int len_as_arg; int len_arg; int len; int ls; if (event->flags & EVENT_FL_FAILED) { trace_seq_printf(s, "[FAILED TO PARSE]"); print_event_fields(s, data, size, event); return; } if (event->flags & EVENT_FL_ISBPRINT) { bprint_fmt = get_bprint_format(data, size, event); args = make_bprint_args(bprint_fmt, data, size, event); arg = args; ptr = bprint_fmt; } for (; *ptr; ptr++) { ls = 0; if (*ptr == '\\') { ptr++; switch (*ptr) { case 'n': trace_seq_putc(s, '\n'); break; case 't': trace_seq_putc(s, '\t'); break; case 'r': trace_seq_putc(s, '\r'); break; case '\\': trace_seq_putc(s, '\\'); break; default: trace_seq_putc(s, *ptr); break; } } else if (*ptr == '%') { saveptr = ptr; show_func = 0; len_as_arg = 0; cont_process: ptr++; switch (*ptr) { case '%': trace_seq_putc(s, '%'); break; case '#': /* FIXME: need to handle properly */ goto cont_process; case 'h': ls--; goto cont_process; case 'l': ls++; goto cont_process; case 'L': ls = 2; goto cont_process; case '*': /* The argument is the length. */ if (!arg) { do_warning_event(event, "no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len_arg = eval_num_arg(data, size, event, arg); len_as_arg = 1; arg = arg->next; goto cont_process; case '.': case 'z': case 'Z': case '0' ... '9': goto cont_process; case 'p': if (pevent->long_size == 4) ls = 1; else ls = 2; if (*(ptr+1) == 'F' || *(ptr+1) == 'f') { ptr++; show_func = *ptr; } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { print_mac_arg(s, *(ptr+1), data, size, event, arg); ptr++; arg = arg->next; break; } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') { int n; n = print_ip_arg(s, ptr+1, data, size, event, arg); if (n > 0) { ptr += n; arg = arg->next; break; } } /* fall through */ case 'd': case 'i': case 'x': case 'X': case 'u': if (!arg) { do_warning_event(event, "no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ if (len > 31) { do_warning_event(event, "bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } memcpy(format, saveptr, len); format[len] = 0; val = eval_num_arg(data, size, event, arg); arg = arg->next; if (show_func) { func = find_func(pevent, val); if (func) { trace_seq_puts(s, func->func); if (show_func == 'F') trace_seq_printf(s, "+0x%llx", val - func->addr); break; } } if (pevent->long_size == 8 && ls && sizeof(long) != 8) { char *p; ls = 2; /* make %l into %ll */ p = strchr(format, 'l'); if (p) memmove(p+1, p, strlen(p)+1); else if (strcmp(format, "%p") == 0) strcpy(format, "0x%llx"); } switch (ls) { case -2: if (len_as_arg) trace_seq_printf(s, format, len_arg, (char)val); else trace_seq_printf(s, format, (char)val); break; case -1: if (len_as_arg) trace_seq_printf(s, format, len_arg, (short)val); else trace_seq_printf(s, format, (short)val); break; case 0: if (len_as_arg) trace_seq_printf(s, format, len_arg, (int)val); else trace_seq_printf(s, format, (int)val); break; case 1: if (len_as_arg) trace_seq_printf(s, format, len_arg, (long)val); else trace_seq_printf(s, format, (long)val); break; case 2: if (len_as_arg) trace_seq_printf(s, format, len_arg, (long long)val); else trace_seq_printf(s, format, (long long)val); break; default: do_warning_event(event, "bad count (%d)", ls); event->flags |= EVENT_FL_FAILED; } break; case 's': if (!arg) { do_warning_event(event, "no matching argument"); event->flags |= EVENT_FL_FAILED; goto out_failed; } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ if (len > 31) { do_warning_event(event, "bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } memcpy(format, saveptr, len); format[len] = 0; if (!len_as_arg) len_arg = -1; /* Use helper trace_seq */ trace_seq_init(&p); print_str_arg(&p, data, size, event, format, len_arg, arg); trace_seq_terminate(&p); trace_seq_puts(s, p.buffer); trace_seq_destroy(&p); arg = arg->next; break; default: trace_seq_printf(s, ">%c<", *ptr); } } else trace_seq_putc(s, *ptr); } if (event->flags & EVENT_FL_FAILED) { out_failed: trace_seq_printf(s, "[FAILED TO PARSE]"); } if (args) { free_args(args); free(bprint_fmt); } } /** * pevent_data_lat_fmt - parse the data for the latency format * @pevent: a handle to the pevent * @s: the trace_seq to write to * @record: the record to read from * * This parses out the Latency format (interrupts disabled, * need rescheduling, in hard/soft interrupt, preempt count * and lock depth) and places it into the trace_seq. */ void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record) { static int check_lock_depth = 1; static int check_migrate_disable = 1; static int lock_depth_exists; static int migrate_disable_exists; unsigned int lat_flags; unsigned int pc; int lock_depth; int migrate_disable; int hardirq; int softirq; void *data = record->data; lat_flags = parse_common_flags(pevent, data); pc = parse_common_pc(pevent, data); /* lock_depth may not always exist */ if (lock_depth_exists) lock_depth = parse_common_lock_depth(pevent, data); else if (check_lock_depth) { lock_depth = parse_common_lock_depth(pevent, data); if (lock_depth < 0) check_lock_depth = 0; else lock_depth_exists = 1; } /* migrate_disable may not always exist */ if (migrate_disable_exists) migrate_disable = parse_common_migrate_disable(pevent, data); else if (check_migrate_disable) { migrate_disable = parse_common_migrate_disable(pevent, data); if (migrate_disable < 0) check_migrate_disable = 0; else migrate_disable_exists = 1; } hardirq = lat_flags & TRACE_FLAG_HARDIRQ; softirq = lat_flags & TRACE_FLAG_SOFTIRQ; trace_seq_printf(s, "%c%c%c", (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? 'X' : '.', (lat_flags & TRACE_FLAG_NEED_RESCHED) ? 'N' : '.', (hardirq && softirq) ? 'H' : hardirq ? 'h' : softirq ? 's' : '.'); if (pc) trace_seq_printf(s, "%x", pc); else trace_seq_putc(s, '.'); if (migrate_disable_exists) { if (migrate_disable < 0) trace_seq_putc(s, '.'); else trace_seq_printf(s, "%d", migrate_disable); } if (lock_depth_exists) { if (lock_depth < 0) trace_seq_putc(s, '.'); else trace_seq_printf(s, "%d", lock_depth); } trace_seq_terminate(s); } /** * pevent_data_type - parse out the given event type * @pevent: a handle to the pevent * @rec: the record to read from * * This returns the event id from the @rec. */ int pevent_data_type(struct pevent *pevent, struct pevent_record *rec) { return trace_parse_common_type(pevent, rec->data); } /** * pevent_data_event_from_type - find the event by a given type * @pevent: a handle to the pevent * @type: the type of the event. * * This returns the event form a given @type; */ struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type) { return pevent_find_event(pevent, type); } /** * pevent_data_pid - parse the PID from raw data * @pevent: a handle to the pevent * @rec: the record to parse * * This returns the PID from a raw data. */ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec) { return parse_common_pid(pevent, rec->data); } /** * pevent_data_comm_from_pid - return the command line from PID * @pevent: a handle to the pevent * @pid: the PID of the task to search for * * This returns a pointer to the command line that has the given * @pid. */ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) { const char *comm; comm = find_cmdline(pevent, pid); return comm; } static struct cmdline * pid_from_cmdlist(struct pevent *pevent, const char *comm, struct cmdline *next) { struct cmdline_list *cmdlist = (struct cmdline_list *)next; if (cmdlist) cmdlist = cmdlist->next; else cmdlist = pevent->cmdlist; while (cmdlist && strcmp(cmdlist->comm, comm) != 0) cmdlist = cmdlist->next; return (struct cmdline *)cmdlist; } /** * pevent_data_pid_from_comm - return the pid from a given comm * @pevent: a handle to the pevent * @comm: the cmdline to find the pid from * @next: the cmdline structure to find the next comm * * This returns the cmdline structure that holds a pid for a given * comm, or NULL if none found. As there may be more than one pid for * a given comm, the result of this call can be passed back into * a recurring call in the @next paramater, and then it will find the * next pid. * Also, it does a linear seach, so it may be slow. */ struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, struct cmdline *next) { struct cmdline *cmdline; /* * If the cmdlines have not been converted yet, then use * the list. */ if (!pevent->cmdlines) return pid_from_cmdlist(pevent, comm, next); if (next) { /* * The next pointer could have been still from * a previous call before cmdlines were created */ if (next < pevent->cmdlines || next >= pevent->cmdlines + pevent->cmdline_count) next = NULL; else cmdline = next++; } if (!next) cmdline = pevent->cmdlines; while (cmdline < pevent->cmdlines + pevent->cmdline_count) { if (strcmp(cmdline->comm, comm) == 0) return cmdline; cmdline++; } return NULL; } /** * pevent_cmdline_pid - return the pid associated to a given cmdline * @cmdline: The cmdline structure to get the pid from * * Returns the pid for a give cmdline. If @cmdline is NULL, then * -1 is returned. */ int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline) { struct cmdline_list *cmdlist = (struct cmdline_list *)cmdline; if (!cmdline) return -1; /* * If cmdlines have not been created yet, or cmdline is * not part of the array, then treat it as a cmdlist instead. */ if (!pevent->cmdlines || cmdline < pevent->cmdlines || cmdline >= pevent->cmdlines + pevent->cmdline_count) return cmdlist->pid; return cmdline->pid; } /** * pevent_data_comm_from_pid - parse the data into the print format * @s: the trace_seq to write to * @event: the handle to the event * @record: the record to read from * * This parses the raw @data using the given @event information and * writes the print format into the trace_seq. */ void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record) { int print_pretty = 1; if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) print_event_fields(s, record->data, record->size, event); else { if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) print_pretty = event->handler(s, record, event, event->context); if (print_pretty) pretty_print(s, record->data, record->size, event); } trace_seq_terminate(s); } static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock) { if (!use_trace_clock) return true; if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global") || !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf")) return true; /* trace_clock is setting in tsc or counter mode */ return false; } void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record, bool use_trace_clock) { static const char *spaces = " "; /* 20 spaces */ struct event_format *event; unsigned long secs; unsigned long usecs; unsigned long nsecs; const char *comm; void *data = record->data; int type; int pid; int len; int p; bool use_usec_format; use_usec_format = is_timestamp_in_us(pevent->trace_clock, use_trace_clock); if (use_usec_format) { secs = record->ts / NSECS_PER_SEC; nsecs = record->ts - secs * NSECS_PER_SEC; } if (record->size < 0) { do_warning("ug! negative record size %d", record->size); return; } type = trace_parse_common_type(pevent, data); event = pevent_find_event(pevent, type); if (!event) { do_warning("ug! no event found for type %d", type); return; } pid = parse_common_pid(pevent, data); comm = find_cmdline(pevent, pid); if (pevent->latency_format) { trace_seq_printf(s, "%8.8s-%-5d %3d", comm, pid, record->cpu); pevent_data_lat_fmt(pevent, s, record); } else trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); if (use_usec_format) { if (pevent->flags & PEVENT_NSEC_OUTPUT) { usecs = nsecs; p = 9; } else { usecs = (nsecs + 500) / NSECS_PER_USEC; p = 6; } trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name); } else trace_seq_printf(s, " %12llu: %s: ", record->ts, event->name); /* Space out the event names evenly. */ len = strlen(event->name); if (len < 20) trace_seq_printf(s, "%.*s", 20 - len, spaces); pevent_event_info(s, event, record); } static int events_id_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; if ((*ea)->id < (*eb)->id) return -1; if ((*ea)->id > (*eb)->id) return 1; return 0; } static int events_name_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; int res; res = strcmp((*ea)->name, (*eb)->name); if (res) return res; res = strcmp((*ea)->system, (*eb)->system); if (res) return res; return events_id_cmp(a, b); } static int events_system_cmp(const void *a, const void *b) { struct event_format * const * ea = a; struct event_format * const * eb = b; int res; res = strcmp((*ea)->system, (*eb)->system); if (res) return res; res = strcmp((*ea)->name, (*eb)->name); if (res) return res; return events_id_cmp(a, b); } struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type sort_type) { struct event_format **events; int (*sort)(const void *a, const void *b); events = pevent->sort_events; if (events && pevent->last_type == sort_type) return events; if (!events) { events = malloc(sizeof(*events) * (pevent->nr_events + 1)); if (!events) return NULL; memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events); events[pevent->nr_events] = NULL; pevent->sort_events = events; /* the internal events are sorted by id */ if (sort_type == EVENT_SORT_ID) { pevent->last_type = sort_type; return events; } } switch (sort_type) { case EVENT_SORT_ID: sort = events_id_cmp; break; case EVENT_SORT_NAME: sort = events_name_cmp; break; case EVENT_SORT_SYSTEM: sort = events_system_cmp; break; default: return events; } qsort(events, pevent->nr_events, sizeof(*events), sort); pevent->last_type = sort_type; return events; } static struct format_field ** get_event_fields(const char *type, const char *name, int count, struct format_field *list) { struct format_field **fields; struct format_field *field; int i = 0; fields = malloc(sizeof(*fields) * (count + 1)); if (!fields) return NULL; for (field = list; field; field = field->next) { fields[i++] = field; if (i == count + 1) { do_warning("event %s has more %s fields than specified", name, type); i--; break; } } if (i != count) do_warning("event %s has less %s fields than specified", name, type); fields[i] = NULL; return fields; } /** * pevent_event_common_fields - return a list of common fields for an event * @event: the event to return the common fields of. * * Returns an allocated array of fields. The last item in the array is NULL. * The array must be freed with free(). */ struct format_field **pevent_event_common_fields(struct event_format *event) { return get_event_fields("common", event->name, event->format.nr_common, event->format.common_fields); } /** * pevent_event_fields - return a list of event specific fields for an event * @event: the event to return the fields of. * * Returns an allocated array of fields. The last item in the array is NULL. * The array must be freed with free(). */ struct format_field **pevent_event_fields(struct event_format *event) { return get_event_fields("event", event->name, event->format.nr_fields, event->format.fields); } static void print_fields(struct trace_seq *s, struct print_flag_sym *field) { trace_seq_printf(s, "{ %s, %s }", field->value, field->str); if (field->next) { trace_seq_puts(s, ", "); print_fields(s, field->next); } } /* for debugging */ static void print_args(struct print_arg *args) { int print_paren = 1; struct trace_seq s; switch (args->type) { case PRINT_NULL: printf("null"); break; case PRINT_ATOM: printf("%s", args->atom.atom); break; case PRINT_FIELD: printf("REC->%s", args->field.name); break; case PRINT_FLAGS: printf("__print_flags("); print_args(args->flags.field); printf(", %s, ", args->flags.delim); trace_seq_init(&s); print_fields(&s, args->flags.flags); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf(")"); break; case PRINT_SYMBOL: printf("__print_symbolic("); print_args(args->symbol.field); printf(", "); trace_seq_init(&s); print_fields(&s, args->symbol.symbols); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf(")"); break; case PRINT_HEX: printf("__print_hex("); print_args(args->hex.field); printf(", "); print_args(args->hex.size); printf(")"); break; case PRINT_STRING: case PRINT_BSTRING: printf("__get_str(%s)", args->string.string); break; case PRINT_BITMASK: printf("__get_bitmask(%s)", args->bitmask.bitmask); break; case PRINT_TYPE: printf("(%s)", args->typecast.type); print_args(args->typecast.item); break; case PRINT_OP: if (strcmp(args->op.op, ":") == 0) print_paren = 0; if (print_paren) printf("("); print_args(args->op.left); printf(" %s ", args->op.op); print_args(args->op.right); if (print_paren) printf(")"); break; default: /* we should warn... */ return; } if (args->next) { printf("\n"); print_args(args->next); } } static void parse_header_field(const char *field, int *offset, int *size, int mandatory) { unsigned long long save_input_buf_ptr; unsigned long long save_input_buf_siz; char *token; int type; save_input_buf_ptr = input_buf_ptr; save_input_buf_siz = input_buf_siz; if (read_expected(EVENT_ITEM, "field") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; /* type */ if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; free_token(token); /* * If this is not a mandatory field, then test it first. */ if (mandatory) { if (read_expected(EVENT_ITEM, field) < 0) return; } else { if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; if (strcmp(token, field) != 0) goto discard; free_token(token); } if (read_expected(EVENT_OP, ";") < 0) return; if (read_expected(EVENT_ITEM, "offset") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; *offset = atoi(token); free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; if (read_expected(EVENT_ITEM, "size") < 0) return; if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token) < 0) goto fail; *size = atoi(token); free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; type = read_token(&token); if (type != EVENT_NEWLINE) { /* newer versions of the kernel have a "signed" type */ if (type != EVENT_ITEM) goto fail; if (strcmp(token, "signed") != 0) goto fail; free_token(token); if (read_expected(EVENT_OP, ":") < 0) return; if (read_expect_type(EVENT_ITEM, &token)) goto fail; free_token(token); if (read_expected(EVENT_OP, ";") < 0) return; if (read_expect_type(EVENT_NEWLINE, &token)) goto fail; } fail: free_token(token); return; discard: input_buf_ptr = save_input_buf_ptr; input_buf_siz = save_input_buf_siz; *offset = 0; *size = 0; free_token(token); } /** * pevent_parse_header_page - parse the data stored in the header page * @pevent: the handle to the pevent * @buf: the buffer storing the header page format string * @size: the size of @buf * @long_size: the long size to use if there is no header * * This parses the header page format for information on the * ring buffer used. The @buf should be copied from * * /sys/kernel/debug/tracing/events/header_page */ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size) { int ignore; if (!size) { /* * Old kernels did not have header page info. * Sorry but we just use what we find here in user space. */ pevent->header_page_ts_size = sizeof(long long); pevent->header_page_size_size = long_size; pevent->header_page_data_offset = sizeof(long long) + long_size; pevent->old_format = 1; return -1; } init_input_buf(buf, size); parse_header_field("timestamp", &pevent->header_page_ts_offset, &pevent->header_page_ts_size, 1); parse_header_field("commit", &pevent->header_page_size_offset, &pevent->header_page_size_size, 1); parse_header_field("overwrite", &pevent->header_page_overwrite, &ignore, 0); parse_header_field("data", &pevent->header_page_data_offset, &pevent->header_page_data_size, 1); return 0; } static int event_matches(struct event_format *event, int id, const char *sys_name, const char *event_name) { if (id >= 0 && id != event->id) return 0; if (event_name && (strcmp(event_name, event->name) != 0)) return 0; if (sys_name && (strcmp(sys_name, event->system) != 0)) return 0; return 1; } static void free_handler(struct event_handler *handle) { free((void *)handle->sys_name); free((void *)handle->event_name); free(handle); } static int find_event_handle(struct pevent *pevent, struct event_format *event) { struct event_handler *handle, **next; for (next = &pevent->handlers; *next; next = &(*next)->next) { handle = *next; if (event_matches(event, handle->id, handle->sys_name, handle->event_name)) break; } if (!(*next)) return 0; pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); event->handler = handle->func; event->context = handle->context; *next = handle->next; free_handler(handle); return 1; } /** * __pevent_parse_format - parse the event format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ enum pevent_errno __pevent_parse_format(struct event_format **eventp, struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event; int ret; init_input_buf(buf, size); *eventp = event = alloc_event(); if (!event) return PEVENT_ERRNO__MEM_ALLOC_FAILED; event->name = event_read_name(); if (!event->name) { /* Bad event? */ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_alloc_failed; } if (strcmp(sys, "ftrace") == 0) { event->flags |= EVENT_FL_ISFTRACE; if (strcmp(event->name, "bprint") == 0) event->flags |= EVENT_FL_ISBPRINT; } event->id = event_read_id(); if (event->id < 0) { ret = PEVENT_ERRNO__READ_ID_FAILED; /* * This isn't an allocation error actually. * But as the ID is critical, just bail out. */ goto event_alloc_failed; } event->system = strdup(sys); if (!event->system) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_alloc_failed; } /* Add pevent to event so that it can be referenced */ event->pevent = pevent; ret = event_read_format(event); if (ret < 0) { ret = PEVENT_ERRNO__READ_FORMAT_FAILED; goto event_parse_failed; } /* * If the event has an override, don't print warnings if the event * print format fails to parse. */ if (pevent && find_event_handle(pevent, event)) show_warning = 0; ret = event_read_print(event); show_warning = 1; if (ret < 0) { ret = PEVENT_ERRNO__READ_PRINT_FAILED; goto event_parse_failed; } if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { struct format_field *field; struct print_arg *arg, **list; /* old ftrace had no args */ list = &event->print_fmt.args; for (field = event->format.fields; field; field = field->next) { arg = alloc_arg(); if (!arg) { event->flags |= EVENT_FL_FAILED; return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; } arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); if (!arg->field.name) { event->flags |= EVENT_FL_FAILED; free_arg(arg); return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; } arg->field.field = field; *list = arg; list = &arg->next; } return 0; } return 0; event_parse_failed: event->flags |= EVENT_FL_FAILED; return ret; event_alloc_failed: free(event->system); free(event->name); free(event); *eventp = NULL; return ret; } static enum pevent_errno __pevent_parse_event(struct pevent *pevent, struct event_format **eventp, const char *buf, unsigned long size, const char *sys) { int ret = __pevent_parse_format(eventp, pevent, buf, size, sys); struct event_format *event = *eventp; if (event == NULL) return ret; if (pevent && add_event(pevent, event)) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_add_failed; } #define PRINT_ARGS 0 if (PRINT_ARGS && event->print_fmt.args) print_args(event->print_fmt.args); return 0; event_add_failed: pevent_free_format(event); return ret; } /** * pevent_parse_format - parse the event format * @pevent: the handle to the pevent * @eventp: returned format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ enum pevent_errno pevent_parse_format(struct pevent *pevent, struct event_format **eventp, const char *buf, unsigned long size, const char *sys) { return __pevent_parse_event(pevent, eventp, buf, size, sys); } /** * pevent_parse_event - parse the event format * @pevent: the handle to the pevent * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to * * This parses the event format and creates an event structure * to quickly parse raw data for a given event. * * These files currently come from: * * /sys/kernel/debug/tracing/events/.../.../format */ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event = NULL; return __pevent_parse_event(pevent, &event, buf, size, sys); } #undef _PE #define _PE(code, str) str static const char * const pevent_error_str[] = { PEVENT_ERRORS }; #undef _PE int pevent_strerror(struct pevent *pevent __maybe_unused, enum pevent_errno errnum, char *buf, size_t buflen) { int idx; const char *msg; if (errnum >= 0) { msg = strerror_r(errnum, buf, buflen); if (msg != buf) { size_t len = strlen(msg); memcpy(buf, msg, min(buflen - 1, len)); *(buf + min(buflen - 1, len)) = '\0'; } return 0; } if (errnum <= __PEVENT_ERRNO__START || errnum >= __PEVENT_ERRNO__END) return -1; idx = errnum - __PEVENT_ERRNO__START - 1; msg = pevent_error_str[idx]; snprintf(buf, buflen, "%s", msg); return 0; } int get_field_val(struct trace_seq *s, struct format_field *field, const char *name, struct pevent_record *record, unsigned long long *val, int err) { if (!field) { if (err) trace_seq_printf(s, "", name); return -1; } if (pevent_read_number_field(field, record->data, val)) { if (err) trace_seq_printf(s, " %s=INVALID", name); return -1; } return 0; } /** * pevent_get_field_raw - return the raw pointer into the data field * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @len: place to store the field length. * @err: print default error if failed. * * Returns a pointer into record->data of the field and places * the length of the field in @len. * * On failure, it returns NULL. */ void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, int *len, int err) { struct format_field *field; void *data = record->data; unsigned offset; int dummy; if (!event) return NULL; field = pevent_find_field(event, name); if (!field) { if (err) trace_seq_printf(s, "", name); return NULL; } /* Allow @len to be NULL */ if (!len) len = &dummy; offset = field->offset; if (field->flags & FIELD_IS_DYNAMIC) { offset = pevent_read_number(event->pevent, data + offset, field->size); *len = offset >> 16; offset &= 0xffff; } else *len = field->size; return data + offset; } /** * pevent_get_field_val - find a field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_get_common_field_val - find a common field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_common_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_get_any_field_val - find a any field and return its value * @s: The seq to print to on error * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @val: place to store the value of the field. * @err: print default error if failed. * * Returns 0 on success -1 on field not found. */ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; if (!event) return -1; field = pevent_find_any_field(event, name); return get_field_val(s, field, name, record, val, err); } /** * pevent_print_num_field - print a field and a format * @s: The seq to print to * @fmt: The printf format to print the field with. * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @err: print default error if failed. * * Returns: 0 on success, -1 field not found, or 1 if buffer is full. */ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err) { struct format_field *field = pevent_find_field(event, name); unsigned long long val; if (!field) goto failed; if (pevent_read_number_field(field, record->data, &val)) goto failed; return trace_seq_printf(s, fmt, val); failed: if (err) trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); return -1; } /** * pevent_print_func_field - print a field and a format for function pointers * @s: The seq to print to * @fmt: The printf format to print the field with. * @event: the event that the field is for * @name: The name of the field * @record: The record with the field name. * @err: print default error if failed. * * Returns: 0 on success, -1 field not found, or 1 if buffer is full. */ int pevent_print_func_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err) { struct format_field *field = pevent_find_field(event, name); struct pevent *pevent = event->pevent; unsigned long long val; struct func_map *func; char tmp[128]; if (!field) goto failed; if (pevent_read_number_field(field, record->data, &val)) goto failed; func = find_func(pevent, val); if (func) snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val); else sprintf(tmp, "0x%08llx", val); return trace_seq_printf(s, fmt, tmp); failed: if (err) trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); return -1; } static void free_func_handle(struct pevent_function_handler *func) { struct pevent_func_params *params; free(func->name); while (func->params) { params = func->params; func->params = params->next; free(params); } free(func); } /** * pevent_register_print_function - register a helper function * @pevent: the handle to the pevent * @func: the function to process the helper function * @ret_type: the return type of the helper function * @name: the name of the helper function * @parameters: A list of enum pevent_func_arg_type * * Some events may have helper functions in the print format arguments. * This allows a plugin to dynamically create a way to process one * of these functions. * * The @parameters is a variable list of pevent_func_arg_type enums that * must end with PEVENT_FUNC_ARG_VOID. */ int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...) { struct pevent_function_handler *func_handle; struct pevent_func_params **next_param; struct pevent_func_params *param; enum pevent_func_arg_type type; va_list ap; int ret; func_handle = find_func_handler(pevent, name); if (func_handle) { /* * This is most like caused by the users own * plugins updating the function. This overrides the * system defaults. */ pr_stat("override of function helper '%s'", name); remove_func_handler(pevent, name); } func_handle = calloc(1, sizeof(*func_handle)); if (!func_handle) { do_warning("Failed to allocate function handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } func_handle->ret_type = ret_type; func_handle->name = strdup(name); func_handle->func = func; if (!func_handle->name) { do_warning("Failed to allocate function name"); free(func_handle); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } next_param = &(func_handle->params); va_start(ap, name); for (;;) { type = va_arg(ap, enum pevent_func_arg_type); if (type == PEVENT_FUNC_ARG_VOID) break; if (type >= PEVENT_FUNC_ARG_MAX_TYPES) { do_warning("Invalid argument type %d", type); ret = PEVENT_ERRNO__INVALID_ARG_TYPE; goto out_free; } param = malloc(sizeof(*param)); if (!param) { do_warning("Failed to allocate function param"); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto out_free; } param->type = type; param->next = NULL; *next_param = param; next_param = &(param->next); func_handle->nr_args++; } va_end(ap); func_handle->next = pevent->func_handlers; pevent->func_handlers = func_handle; return 0; out_free: va_end(ap); free_func_handle(func_handle); return ret; } /** * pevent_unregister_print_function - unregister a helper function * @pevent: the handle to the pevent * @func: the function to process the helper function * @name: the name of the helper function * * This function removes existing print handler for function @name. * * Returns 0 if the handler was removed successully, -1 otherwise. */ int pevent_unregister_print_function(struct pevent *pevent, pevent_func_handler func, char *name) { struct pevent_function_handler *func_handle; func_handle = find_func_handler(pevent, name); if (func_handle && func_handle->func == func) { remove_func_handler(pevent, name); return 0; } return -1; } static struct event_format *pevent_search_event(struct pevent *pevent, int id, const char *sys_name, const char *event_name) { struct event_format *event; if (id >= 0) { /* search by id */ event = pevent_find_event(pevent, id); if (!event) return NULL; if (event_name && (strcmp(event_name, event->name) != 0)) return NULL; if (sys_name && (strcmp(sys_name, event->system) != 0)) return NULL; } else { event = pevent_find_event_by_name(pevent, sys_name, event_name); if (!event) return NULL; } return event; } /** * pevent_register_event_handler - register a way to parse an event * @pevent: the handle to the pevent * @id: the id of the event to register * @sys_name: the system name the event belongs to * @event_name: the name of the event * @func: the function to call to parse the event information * @context: the data to be passed to @func * * This function allows a developer to override the parsing of * a given event. If for some reason the default print format * is not sufficient, this function will register a function * for an event to be used to parse the data instead. * * If @id is >= 0, then it is used to find the event. * else @sys_name and @event_name are used. */ int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context) { struct event_format *event; struct event_handler *handle; event = pevent_search_event(pevent, id, sys_name, event_name); if (event == NULL) goto not_found; pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); event->handler = func; event->context = context; return 0; not_found: /* Save for later use. */ handle = calloc(1, sizeof(*handle)); if (!handle) { do_warning("Failed to allocate event handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } handle->id = id; if (event_name) handle->event_name = strdup(event_name); if (sys_name) handle->sys_name = strdup(sys_name); if ((event_name && !handle->event_name) || (sys_name && !handle->sys_name)) { do_warning("Failed to allocate event/sys name"); free((void *)handle->event_name); free((void *)handle->sys_name); free(handle); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } handle->func = func; handle->next = pevent->handlers; pevent->handlers = handle; handle->context = context; return -1; } static int handle_matches(struct event_handler *handler, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context) { if (id >= 0 && id != handler->id) return 0; if (event_name && (strcmp(event_name, handler->event_name) != 0)) return 0; if (sys_name && (strcmp(sys_name, handler->sys_name) != 0)) return 0; if (func != handler->func || context != handler->context) return 0; return 1; } /** * pevent_unregister_event_handler - unregister an existing event handler * @pevent: the handle to the pevent * @id: the id of the event to unregister * @sys_name: the system name the handler belongs to * @event_name: the name of the event handler * @func: the function to call to parse the event information * @context: the data to be passed to @func * * This function removes existing event handler (parser). * * If @id is >= 0, then it is used to find the event. * else @sys_name and @event_name are used. * * Returns 0 if handler was removed successfully, -1 if event was not found. */ int pevent_unregister_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context) { struct event_format *event; struct event_handler *handle; struct event_handler **next; event = pevent_search_event(pevent, id, sys_name, event_name); if (event == NULL) goto not_found; if (event->handler == func && event->context == context) { pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.", event->id, event->system, event->name); event->handler = NULL; event->context = NULL; return 0; } not_found: for (next = &pevent->handlers; *next; next = &(*next)->next) { handle = *next; if (handle_matches(handle, id, sys_name, event_name, func, context)) break; } if (!(*next)) return -1; *next = handle->next; free_handler(handle); return 0; } /** * pevent_alloc - create a pevent handle */ struct pevent *pevent_alloc(void) { struct pevent *pevent = calloc(1, sizeof(*pevent)); if (pevent) pevent->ref_count = 1; return pevent; } void pevent_ref(struct pevent *pevent) { pevent->ref_count++; } static void free_format_fields(struct format_field *field) { struct format_field *next; while (field) { next = field->next; free(field->type); free(field->name); free(field); field = next; } } static void free_formats(struct format *format) { free_format_fields(format->common_fields); free_format_fields(format->fields); } void pevent_free_format(struct event_format *event) { free(event->name); free(event->system); free_formats(&event->format); free(event->print_fmt.format); free_args(event->print_fmt.args); free(event); } /** * pevent_free - free a pevent handle * @pevent: the pevent handle to free */ void pevent_free(struct pevent *pevent) { struct cmdline_list *cmdlist, *cmdnext; struct func_list *funclist, *funcnext; struct printk_list *printklist, *printknext; struct pevent_function_handler *func_handler; struct event_handler *handle; int i; if (!pevent) return; cmdlist = pevent->cmdlist; funclist = pevent->funclist; printklist = pevent->printklist; pevent->ref_count--; if (pevent->ref_count) return; if (pevent->cmdlines) { for (i = 0; i < pevent->cmdline_count; i++) free(pevent->cmdlines[i].comm); free(pevent->cmdlines); } while (cmdlist) { cmdnext = cmdlist->next; free(cmdlist->comm); free(cmdlist); cmdlist = cmdnext; } if (pevent->func_map) { for (i = 0; i < (int)pevent->func_count; i++) { free(pevent->func_map[i].func); free(pevent->func_map[i].mod); } free(pevent->func_map); } while (funclist) { funcnext = funclist->next; free(funclist->func); free(funclist->mod); free(funclist); funclist = funcnext; } while (pevent->func_handlers) { func_handler = pevent->func_handlers; pevent->func_handlers = func_handler->next; free_func_handle(func_handler); } if (pevent->printk_map) { for (i = 0; i < (int)pevent->printk_count; i++) free(pevent->printk_map[i].printk); free(pevent->printk_map); } while (printklist) { printknext = printklist->next; free(printklist->printk); free(printklist); printklist = printknext; } for (i = 0; i < pevent->nr_events; i++) pevent_free_format(pevent->events[i]); while (pevent->handlers) { handle = pevent->handlers; pevent->handlers = handle->next; free_handler(handle); } free(pevent->trace_clock); free(pevent->events); free(pevent->sort_events); free(pevent); } void pevent_unref(struct pevent *pevent) { pevent_free(pevent); } trace-cmd-2.5.3/event-parse.h000066400000000000000000000573431246701203100157720ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _PARSE_EVENTS_H #define _PARSE_EVENTS_H #include #include #include #include #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) #endif /* ----------------------- trace_seq ----------------------- */ #ifndef TRACE_SEQ_BUF_SIZE #define TRACE_SEQ_BUF_SIZE 4096 #endif #ifndef DEBUG_RECORD #define DEBUG_RECORD 0 #endif struct pevent_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 pevent_record *prev; struct pevent_record *next; long alloc_addr; #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_printf(struct trace_seq *s); /* ----------------------- pevent ----------------------- */ struct pevent; struct event_format; typedef int (*pevent_event_handler_func)(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context); typedef int (*pevent_plugin_load_func)(struct pevent *pevent); typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); struct plugin_option { struct 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: * * PEVENT_PLUGIN_LOADER: (required) * The function name to initialized the plugin. * * int PEVENT_PLUGIN_LOADER(struct pevent *pevent) * * PEVENT_PLUGIN_UNLOADER: (optional) * The function called just before unloading * * int PEVENT_PLUGIN_UNLOADER(void) * * PEVENT_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading * * struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { * { * .name = "option-name", * .plugin_alias = "overide-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. * * PEVENT_PLUGIN_ALIAS: (optional) * The name to use for finding options (uses filename if not defined) */ #define PEVENT_PLUGIN_LOADER pevent_plugin_loader #define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader #define PEVENT_PLUGIN_OPTIONS pevent_plugin_options #define PEVENT_PLUGIN_ALIAS pevent_plugin_alias #define _MAKE_STR(x) #x #define MAKE_STR(x) _MAKE_STR(x) #define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER) #define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER) #define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS) #define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS) #define NSECS_PER_SEC 1000000000ULL #define NSECS_PER_USEC 1000ULL enum format_flags { FIELD_IS_ARRAY = 1, FIELD_IS_POINTER = 2, FIELD_IS_SIGNED = 4, FIELD_IS_STRING = 8, FIELD_IS_DYNAMIC = 16, FIELD_IS_LONG = 32, FIELD_IS_FLAG = 64, FIELD_IS_SYMBOLIC = 128, }; struct format_field { struct format_field *next; struct event_format *event; char *type; char *name; int offset; int size; unsigned int arraylen; unsigned int elementsize; unsigned long flags; }; struct format { int nr_common; int nr_fields; struct format_field *common_fields; struct format_field *fields; }; struct print_arg_atom { char *atom; }; struct print_arg_string { char *string; int offset; }; struct print_arg_bitmask { char *bitmask; int offset; }; struct print_arg_field { char *name; struct format_field *field; }; struct print_flag_sym { struct print_flag_sym *next; char *value; char *str; }; struct print_arg_typecast { char *type; struct print_arg *item; }; struct print_arg_flags { struct print_arg *field; char *delim; struct print_flag_sym *flags; }; struct print_arg_symbol { struct print_arg *field; struct print_flag_sym *symbols; }; struct print_arg_hex { struct print_arg *field; struct print_arg *size; }; struct print_arg_dynarray { struct format_field *field; struct print_arg *index; }; struct print_arg; struct print_arg_op { char *op; int prio; struct print_arg *left; struct print_arg *right; }; struct pevent_function_handler; struct print_arg_func { struct pevent_function_handler *func; struct print_arg *args; }; enum print_arg_type { PRINT_NULL, PRINT_ATOM, PRINT_FIELD, PRINT_FLAGS, PRINT_SYMBOL, PRINT_HEX, PRINT_TYPE, PRINT_STRING, PRINT_BSTRING, PRINT_DYNAMIC_ARRAY, PRINT_OP, PRINT_FUNC, PRINT_BITMASK, }; struct print_arg { struct print_arg *next; enum print_arg_type type; union { struct print_arg_atom atom; struct print_arg_field field; struct print_arg_typecast typecast; struct print_arg_flags flags; struct print_arg_symbol symbol; struct print_arg_hex hex; struct print_arg_func func; struct print_arg_string string; struct print_arg_bitmask bitmask; struct print_arg_op op; struct print_arg_dynarray dynarray; }; }; struct print_fmt { char *format; struct print_arg *args; }; struct event_format { struct pevent *pevent; char *name; int id; int flags; struct format format; struct print_fmt print_fmt; char *system; pevent_event_handler_func handler; void *context; }; enum { EVENT_FL_ISFTRACE = 0x01, EVENT_FL_ISPRINT = 0x02, EVENT_FL_ISBPRINT = 0x04, EVENT_FL_ISFUNCENT = 0x10, EVENT_FL_ISFUNCRET = 0x20, EVENT_FL_NOHANDLE = 0x40, EVENT_FL_PRINTRAW = 0x80, EVENT_FL_FAILED = 0x80000000 }; enum event_sort_type { EVENT_SORT_ID, EVENT_SORT_NAME, EVENT_SORT_SYSTEM, }; enum event_type { EVENT_ERROR, EVENT_NONE, EVENT_SPACE, EVENT_NEWLINE, EVENT_OP, EVENT_DELIM, EVENT_ITEM, EVENT_DQUOTE, EVENT_SQUOTE, }; typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s, unsigned long long *args); enum pevent_func_arg_type { PEVENT_FUNC_ARG_VOID, PEVENT_FUNC_ARG_INT, PEVENT_FUNC_ARG_LONG, PEVENT_FUNC_ARG_STRING, PEVENT_FUNC_ARG_PTR, PEVENT_FUNC_ARG_MAX_TYPES }; enum pevent_flag { PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ }; #define PEVENT_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) PEVENT_ERRNO__ ## __code enum pevent_errno { PEVENT_ERRNO__SUCCESS = 0, PEVENT_ERRNO__FILTER_MATCH = PEVENT_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 */ __PEVENT_ERRNO__START = -100000, PEVENT_ERRORS, __PEVENT_ERRNO__END, }; #undef _PE struct plugin_list; struct plugin_list *traceevent_load_plugins(struct pevent *pevent); void traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent); struct cmdline; struct cmdline_list; struct func_map; struct func_list; struct event_handler; struct pevent { int ref_count; int header_page_ts_offset; int header_page_ts_size; int header_page_size_offset; int header_page_size_size; int header_page_data_offset; int header_page_data_size; int header_page_overwrite; int file_bigendian; int host_bigendian; int latency_format; int old_format; int cpus; int long_size; int page_size; struct cmdline *cmdlines; struct cmdline_list *cmdlist; int cmdline_count; struct func_map *func_map; struct func_list *funclist; unsigned int func_count; struct printk_map *printk_map; struct printk_list *printklist; unsigned int printk_count; struct event_format **events; int nr_events; struct event_format **sort_events; enum event_sort_type last_type; int type_offset; int type_size; int pid_offset; int pid_size; int pc_offset; int pc_size; int flags_offset; int flags_size; int ld_offset; int ld_size; int print_raw; int test_filters; int flags; struct format_field *bprint_ip_field; struct format_field *bprint_fmt_field; struct format_field *bprint_buf_field; struct event_handler *handlers; struct pevent_function_handler *func_handlers; int parsing_failures; /* cache */ struct event_format *last_event; char *trace_clock; }; static inline void pevent_set_flag(struct pevent *pevent, int flag) { pevent->flags |= flag; } static inline unsigned short __data2host2(struct pevent *pevent, unsigned short data) { unsigned short swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 8) | ((data & (0xffULL << 8)) >> 8); return swap; } static inline unsigned int __data2host4(struct pevent *pevent, unsigned int data) { unsigned int swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 24) | ((data & (0xffULL << 8)) << 8) | ((data & (0xffULL << 16)) >> 8) | ((data & (0xffULL << 24)) >> 24); return swap; } static inline unsigned long long __data2host8(struct pevent *pevent, unsigned long long data) { unsigned long long swap; if (pevent->host_bigendian == pevent->file_bigendian) return data; swap = ((data & 0xffULL) << 56) | ((data & (0xffULL << 8)) << 40) | ((data & (0xffULL << 16)) << 24) | ((data & (0xffULL << 24)) << 8) | ((data & (0xffULL << 32)) >> 8) | ((data & (0xffULL << 40)) >> 24) | ((data & (0xffULL << 48)) >> 40) | ((data & (0xffULL << 56)) >> 56); return swap; } #define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr)) #define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr)) #define data2host8(pevent, ptr) \ ({ \ unsigned long long __val; \ \ memcpy(&__val, (ptr), sizeof(unsigned long long)); \ __data2host8(pevent, __val); \ }) static inline int traceevent_host_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 pevent_register_comm(struct pevent *pevent, const char *comm, int pid); int pevent_register_trace_clock(struct pevent *pevent, const char *trace_clock); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr); int pevent_pid_is_registered(struct pevent *pevent, int pid); void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record, bool use_trace_clock); int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); enum pevent_errno pevent_parse_format(struct pevent *pevent, struct event_format **eventp, const char *buf, unsigned long size, const char *sys); void pevent_free_format(struct event_format *event); void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, int *len, int err); int pevent_get_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); int pevent_print_func_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); int pevent_unregister_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...); int pevent_unregister_print_function(struct pevent *pevent, pevent_func_handler func, char *name); struct format_field *pevent_find_common_field(struct event_format *event, const char *name); struct format_field *pevent_find_field(struct event_format *event, const char *name); struct format_field *pevent_find_any_field(struct event_format *event, const char *name); const char *pevent_find_function(struct pevent *pevent, unsigned long long addr); unsigned long long pevent_find_function_address(struct pevent *pevent, unsigned long long addr); unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size); int pevent_read_number_field(struct format_field *field, const void *data, unsigned long long *value); struct event_format *pevent_find_event(struct pevent *pevent, int id); struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name); void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record); int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); struct cmdline; struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *comm, struct cmdline *next); int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline); void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record); int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, char *buf, size_t buflen); struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); struct format_field **pevent_event_common_fields(struct event_format *event); struct format_field **pevent_event_fields(struct event_format *event); static inline int pevent_get_cpus(struct pevent *pevent) { return pevent->cpus; } static inline void pevent_set_cpus(struct pevent *pevent, int cpus) { pevent->cpus = cpus; } static inline int pevent_get_long_size(struct pevent *pevent) { return pevent->long_size; } static inline void pevent_set_long_size(struct pevent *pevent, int long_size) { pevent->long_size = long_size; } static inline int pevent_get_page_size(struct pevent *pevent) { return pevent->page_size; } static inline void pevent_set_page_size(struct pevent *pevent, int _page_size) { pevent->page_size = _page_size; } static inline int pevent_is_file_bigendian(struct pevent *pevent) { return pevent->file_bigendian; } static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian) { pevent->file_bigendian = endian; } static inline int pevent_is_host_bigendian(struct pevent *pevent) { return pevent->host_bigendian; } static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian) { pevent->host_bigendian = endian; } static inline int pevent_is_latency_format(struct pevent *pevent) { return pevent->latency_format; } static inline void pevent_set_latency_format(struct pevent *pevent, int lat) { pevent->latency_format = lat; } struct pevent *pevent_alloc(void); void pevent_free(struct pevent *pevent); void pevent_ref(struct pevent *pevent); void pevent_unref(struct pevent *pevent); /* access to the internal parser */ void pevent_buffer_init(const char *buf, unsigned long long size); enum event_type pevent_read_token(char **tok); void pevent_free_token(char *token); int pevent_peek_char(void); const char *pevent_get_input_buf(void); unsigned long long pevent_get_input_buf_ptr(void); /* for debugging */ void pevent_print_funcs(struct pevent *pevent); void pevent_print_printk(struct pevent *pevent); /* ----------------------- filtering ----------------------- */ enum filter_boolean_type { FILTER_FALSE, FILTER_TRUE, }; enum filter_op_type { FILTER_OP_AND = 1, FILTER_OP_OR, FILTER_OP_NOT, }; enum filter_cmp_type { FILTER_CMP_NONE, FILTER_CMP_EQ, FILTER_CMP_NE, FILTER_CMP_GT, FILTER_CMP_LT, FILTER_CMP_GE, FILTER_CMP_LE, FILTER_CMP_MATCH, FILTER_CMP_NOT_MATCH, FILTER_CMP_REGEX, FILTER_CMP_NOT_REGEX, }; enum filter_exp_type { FILTER_EXP_NONE, FILTER_EXP_ADD, FILTER_EXP_SUB, FILTER_EXP_MUL, FILTER_EXP_DIV, FILTER_EXP_MOD, FILTER_EXP_RSHIFT, FILTER_EXP_LSHIFT, FILTER_EXP_AND, FILTER_EXP_OR, FILTER_EXP_XOR, FILTER_EXP_NOT, }; enum filter_arg_type { FILTER_ARG_NONE, FILTER_ARG_BOOLEAN, FILTER_ARG_VALUE, FILTER_ARG_FIELD, FILTER_ARG_EXP, FILTER_ARG_OP, FILTER_ARG_NUM, FILTER_ARG_STR, }; enum filter_value_type { FILTER_NUMBER, FILTER_STRING, FILTER_CHAR }; struct fliter_arg; struct filter_arg_boolean { enum filter_boolean_type value; }; struct filter_arg_field { struct format_field *field; }; struct filter_arg_value { enum filter_value_type type; union { char *str; unsigned long long val; }; }; struct filter_arg_op { enum filter_op_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_exp { enum filter_exp_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_num { enum filter_cmp_type type; struct filter_arg *left; struct filter_arg *right; }; struct filter_arg_str { enum filter_cmp_type type; struct format_field *field; char *val; char *buffer; regex_t reg; }; struct filter_arg { enum filter_arg_type type; union { struct filter_arg_boolean boolean; struct filter_arg_field field; struct filter_arg_value value; struct filter_arg_op op; struct filter_arg_exp exp; struct filter_arg_num num; struct filter_arg_str str; }; }; struct filter_type { int event_id; struct event_format *event; struct filter_arg *filter; }; #define PEVENT_FILTER_ERROR_BUFSZ 1024 struct event_filter { struct pevent *pevent; int filters; struct filter_type *event_filters; char error_buffer[PEVENT_FILTER_ERROR_BUFSZ]; }; struct event_filter *pevent_filter_alloc(struct pevent *pevent); /* for backward compatibility */ #define FILTER_NONE PEVENT_ERRNO__NO_FILTER #define FILTER_NOEXIST PEVENT_ERRNO__FILTER_NOT_FOUND #define FILTER_MISS PEVENT_ERRNO__FILTER_MISS #define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH enum filter_trivial_type { FILTER_TRIVIAL_FALSE, FILTER_TRIVIAL_TRUE, FILTER_TRIVIAL_BOTH, }; enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str); enum pevent_errno pevent_filter_match(struct event_filter *filter, struct pevent_record *record); int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, char *buf, size_t buflen); int pevent_event_filtered(struct event_filter *filter, int event_id); void pevent_filter_reset(struct event_filter *filter); int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type); void pevent_filter_free(struct event_filter *filter); char *pevent_filter_make_string(struct event_filter *filter, int event_id); int pevent_filter_remove_event(struct event_filter *filter, int event_id); int pevent_filter_event_has_trivial(struct event_filter *filter, int event_id, enum filter_trivial_type type); int pevent_filter_copy(struct event_filter *dest, struct event_filter *source); int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, enum filter_trivial_type type); int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2); #endif /* _PARSE_EVENTS_H */ trace-cmd-2.5.3/event-plugin.c000066400000000000000000000112251246701203100161360ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include "event-parse.h" #include "event-utils.h" #define LOCAL_PLUGIN_DIR ".traceevent/plugins" struct plugin_list { struct plugin_list *next; char *name; void *handle; }; static void load_plugin(struct pevent *pevent, const char *path, const char *file, void *data) { struct plugin_list **plugin_list = data; pevent_plugin_load_func func; struct plugin_list *list; const char *alias; char *plugin; void *handle; plugin = malloc(strlen(path) + strlen(file) + 2); if (!plugin) { warning("could not allocate plugin memory\n"); return; } strcpy(plugin, path); strcat(plugin, "/"); strcat(plugin, file); handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL); if (!handle) { warning("could not load plugin '%s'\n%s\n", plugin, dlerror()); goto out_free; } alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME); if (!alias) alias = file; func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME); if (!func) { warning("could not find func '%s' in plugin '%s'\n%s\n", PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror()); goto out_free; } list = malloc(sizeof(*list)); if (!list) { warning("could not allocate plugin memory\n"); goto out_free; } list->next = *plugin_list; list->handle = handle; list->name = plugin; *plugin_list = list; pr_stat("registering plugin: %s", plugin); func(pevent); return; out_free: free(plugin); } static void load_plugins_dir(struct pevent *pevent, const char *suffix, const char *path, void (*load_plugin)(struct pevent *pevent, const char *path, const char *name, void *data), void *data) { struct dirent *dent; struct stat st; DIR *dir; int ret; ret = stat(path, &st); if (ret < 0) return; if (!S_ISDIR(st.st_mode)) return; dir = opendir(path); if (!dir) return; while ((dent = readdir(dir))) { const char *name = dent->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; /* Only load plugins that end in suffix */ if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0) continue; load_plugin(pevent, path, name, data); } closedir(dir); } static void load_plugins(struct pevent *pevent, const char *suffix, void (*load_plugin)(struct pevent *pevent, const char *path, const char *name, void *data), void *data) { char *home; char *path; char *envdir; /* * If a system plugin directory was defined, * check that first. */ #ifdef PLUGIN_DIR load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data); #endif /* * Next let the environment-set plugin directory * override the system defaults. */ envdir = getenv("TRACEEVENT_PLUGIN_DIR"); if (envdir) load_plugins_dir(pevent, suffix, envdir, load_plugin, data); /* * Now let the home directory override the environment * or system defaults. */ home = getenv("HOME"); if (!home) return; path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); if (!path) { warning("could not allocate plugin memory\n"); return; } strcpy(path, home); strcat(path, "/"); strcat(path, LOCAL_PLUGIN_DIR); load_plugins_dir(pevent, suffix, path, load_plugin, data); free(path); } struct plugin_list* traceevent_load_plugins(struct pevent *pevent) { struct plugin_list *list = NULL; load_plugins(pevent, ".so", load_plugin, &list); return list; } void traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent) { pevent_plugin_unload_func func; struct plugin_list *list; while (plugin_list) { list = plugin_list; plugin_list = list->next; func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME); if (func) func(pevent); dlclose(list->handle); free(list->name); free(list); } } trace-cmd-2.5.3/event-utils.h000066400000000000000000000040001246701203100157760ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef __UTIL_H #define __UTIL_H #include #include #include /* Can be overridden */ void die(const char *fmt, ...); void *malloc_or_die(unsigned int size); void warning(const char *fmt, ...); void pr_stat(const char *fmt, ...); void vpr_stat(const char *fmt, va_list ap); /* Always available */ void __die(const char *fmt, ...); void __warning(const char *fmt, ...); void __pr_stat(const char *fmt, ...); void __vdie(const char *fmt, ...); void __vwarning(const char *fmt, ...); void __vpr_stat(const char *fmt, ...); #define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) static inline char *strim(char *string) { char *ret; if (!string) return NULL; while (*string) { if (!isspace(*string)) break; string++; } ret = string; string = ret + strlen(ret) - 1; while (string > ret) { if (!isspace(*string)) break; string--; } string[1] = 0; return ret; } static inline int has_text(const char *text) { if (!text) return 0; while (*text) { if (!isspace(*text)) return 1; text++; } return 0; } #endif trace-cmd-2.5.3/event-viewer.py000077500000000000000000000173471246701203100163650ustar00rootroot00000000000000#!/usr/bin/env python2 import getopt from gobject import * import gtk from tracecmd import * import time app = None data_func_cnt = 0 # In a "real" app these width should be determined at runtime testing max length # strings in the current font. TS_COL_W = 150 CPU_COL_W = 35 EVENT_COL_W = 150 PID_COL_W = 75 COMM_COL_W = 250 def timing(func): def wrapper(*arg): start = time.time() ret = func(*arg) end = time.time() print '@%s took %0.3f s' % (func.func_name, (end-start)) return ret return wrapper class EventStore(gtk.GenericTreeModel): class EventRef(object): '''Inner class to build the trace event index''' def __init__(self, index, timestamp, offset, cpu): self.index = index self.offset = offset self.ts = timestamp self.cpu = cpu def __cmp__(self, other): if self.ts < other.ts: return -1 if self.ts > other.ts: return 1 if self.offset < other.offset: return -1 if self.offset > other.offset: return 1 return 0 # The store only returns the record offset into the trace # The view is responsible for looking up the Event with the offset column_types = (long,) @timing def __init__(self, trace): gtk.GenericTreeModel.__init__(self) self.trace = trace self.refs = [] self._load_trace() self._sort() self._reindex() @timing def _load_trace(self): print "Building trace index..." index = 0 for cpu in range(0, trace.cpus): rec = tracecmd_read_data(self.trace._handle, cpu) while rec: offset = pevent_record_offset_get(rec) ts = pevent_record_ts_get(rec) self.refs.append(self.EventRef(index, ts, offset, cpu)) index = index + 1 rec = tracecmd_read_data(self.trace._handle, cpu) print "Loaded %d events from trace" % (index) @timing def _sort(self): self.refs.sort() @timing def _reindex(self): for i in range(0, len(self.refs)): self.refs[i].index = i def on_get_flags(self): return gtk.TREE_MODEL_LIST_ONLY | gtk.TREE_MODEL_ITERS_PERSIST def on_get_n_columns(self): return len(self.column_types) def on_get_column_type(self, col): return self.column_types[col] def on_get_iter(self, path): return self.refs[path[0]] def on_get_path(self, ref): return ref.index def on_get_value(self, ref, col): ''' The Event record was getting deleted when passed back via this method, now it just returns the ref itself. Use get_event() instead. ''' if col == 0: #return self.trace.read_event_at(ref.offset) return ref return None def on_iter_next(self, ref): try: return self.refs[ref.index+1] except IndexError: return None def on_iter_children(self, ref): if ref: return None return self.refs[0] def on_iter_has_child(self, ref): return False def on_iter_n_children(self, ref): if ref: return 0 return len(self.refs) def on_iter_nth_child(self, ref, n): if ref: return None try: return self.refs[n] except IndexError: return None def on_iter_parent(self, child): return None def get_event(self, iter): '''This allocates a record which must be freed by the caller''' try: ref = self.refs[self.get_path(iter)[0]] ev = self.trace.read_event_at(ref.offset) return ev except IndexError: return None class EventView(gtk.TreeView): def __init__(self, model): gtk.TreeView.__init__(self, model) self.set_fixed_height_mode(True) ts_col = gtk.TreeViewColumn("Time (s)") ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) ts_col.set_fixed_width(TS_COL_W) ts_cell = gtk.CellRendererText() ts_col.pack_start(ts_cell, False) ts_col.set_cell_data_func(ts_cell, self.data_func, "ts") self.append_column(ts_col) cpu_col = gtk.TreeViewColumn("CPU") cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) cpu_col.set_fixed_width(CPU_COL_W) cpu_cell = gtk.CellRendererText() cpu_col.pack_start(cpu_cell, False) cpu_col.set_cell_data_func(cpu_cell, self.data_func, "cpu") self.append_column(cpu_col) event_col = gtk.TreeViewColumn("Event") event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) event_col.set_fixed_width(EVENT_COL_W) event_cell = gtk.CellRendererText() event_col.pack_start(event_cell, False) event_col.set_cell_data_func(event_cell, self.data_func, "event") self.append_column(event_col) pid_col = gtk.TreeViewColumn("PID") pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) pid_col.set_fixed_width(PID_COL_W) pid_cell = gtk.CellRendererText() pid_col.pack_start(pid_cell, False) pid_col.set_cell_data_func(pid_cell, self.data_func, "pid") self.append_column(pid_col) comm_col = gtk.TreeViewColumn("Comm") comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) comm_col.set_fixed_width(COMM_COL_W) comm_cell = gtk.CellRendererText() comm_col.pack_start(comm_cell, False) comm_col.set_cell_data_func(comm_cell, self.data_func, "comm") self.append_column(comm_col) def data_func(self, col, cell, model, iter, data): global app, data_func_cnt ev = model.get_event(iter) #ev = model.get_value(iter, 0) if not ev: return False if data == "ts": cell.set_property("markup", "%d.%d" % (ev.ts/1000000000, ev.ts%1000000000)) data_func_cnt = data_func_cnt + 1 if app: app.inc_data_func() elif data == "cpu": cell.set_property("markup", ev.cpu) elif data == "event": cell.set_property("markup", ev.name) elif data == "pid": cell.set_property("markup", ev.pid) elif data == "comm": cell.set_property("markup", ev.comm) else: print "Unknown Column:", data return False return True class EventViewerApp(gtk.Window): def __init__(self, trace): gtk.Window.__init__(self) self.set_size_request(650, 400) self.set_position(gtk.WIN_POS_CENTER) self.connect("destroy", gtk.main_quit) self.set_title("Event Viewer") store = EventStore(trace) view = EventView(store) sw = gtk.ScrolledWindow() sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) sw.add(view) # track how often the treeview data_func is called self.data_func_label = gtk.Label("0") hbox = gtk.HBox() hbox.pack_start(gtk.Label("TS Data Func Calls:"), False, False) hbox.pack_start(self.data_func_label, False, False) vbox = gtk.VBox() vbox.pack_start(hbox, False) vbox.pack_end(sw) self.add(vbox) self.show_all() def inc_data_func(self): global data_func_cnt self.data_func_label.set_text(str(data_func_cnt)) if __name__ == "__main__": if len(sys.argv) >=2: filename = sys.argv[1] else: filename = "trace.dat" print "Initializing trace..." trace = Trace(filename) print "Initializing app..." app = EventViewerApp(trace) print "Go!" gtk.main() trace-cmd-2.5.3/features.mk000066400000000000000000000017131246701203100155250ustar00rootroot00000000000000 # 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 trace-cmd-2.5.3/kbuffer-parse.c000066400000000000000000000421171246701203100162610ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "kbuffer.h" #define MISSING_EVENTS (1 << 31) #define MISSING_STORED (1 << 30) #define COMMIT_MASK ((1 << 27) - 1) enum { KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0), KBUFFER_FL_BIG_ENDIAN = (1<<1), KBUFFER_FL_LONG_8 = (1<<2), KBUFFER_FL_OLD_FORMAT = (1<<3), }; #define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN) /** kbuffer * @timestamp - timestamp of current event * @lost_events - # of lost events between this subbuffer and previous * @flags - special flags of the kbuffer * @subbuffer - pointer to the sub-buffer page * @data - pointer to the start of data on the sub-buffer page * @index - index from @data to the @curr event data * @curr - offset from @data to the start of current event * (includes metadata) * @next - offset from @data to the start of next event * @size - The size of data on @data * @start - The offset from @subbuffer where @data lives * * @read_4 - Function to read 4 raw bytes (may swap) * @read_8 - Function to read 8 raw bytes (may swap) * @read_long - Function to read a long word (4 or 8 bytes with needed swap) */ struct kbuffer { unsigned long long timestamp; long long lost_events; unsigned long flags; void *subbuffer; void *data; unsigned int index; unsigned int curr; unsigned int next; unsigned int size; unsigned int start; unsigned int (*read_4)(void *ptr); unsigned long long (*read_8)(void *ptr); unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr); int (*next_event)(struct kbuffer *kbuf); }; static void *zmalloc(size_t size) { return calloc(1, size); } static int host_is_bigendian(void) { unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; unsigned int *ptr; ptr = (unsigned int *)str; return *ptr == 0x01020304; } static int do_swap(struct kbuffer *kbuf) { return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) & ENDIAN_MASK; } static unsigned long long __read_8(void *ptr) { unsigned long long data = *(unsigned long long *)ptr; return data; } static unsigned long long __read_8_sw(void *ptr) { unsigned long long data = *(unsigned long long *)ptr; unsigned long long swap; swap = ((data & 0xffULL) << 56) | ((data & (0xffULL << 8)) << 40) | ((data & (0xffULL << 16)) << 24) | ((data & (0xffULL << 24)) << 8) | ((data & (0xffULL << 32)) >> 8) | ((data & (0xffULL << 40)) >> 24) | ((data & (0xffULL << 48)) >> 40) | ((data & (0xffULL << 56)) >> 56); return swap; } static unsigned int __read_4(void *ptr) { unsigned int data = *(unsigned int *)ptr; return data; } static unsigned int __read_4_sw(void *ptr) { unsigned int data = *(unsigned int *)ptr; unsigned int swap; swap = ((data & 0xffULL) << 24) | ((data & (0xffULL << 8)) << 8) | ((data & (0xffULL << 16)) >> 8) | ((data & (0xffULL << 24)) >> 24); return swap; } static unsigned long long read_8(struct kbuffer *kbuf, void *ptr) { return kbuf->read_8(ptr); } static unsigned int read_4(struct kbuffer *kbuf, void *ptr) { return kbuf->read_4(ptr); } static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr) { return kbuf->read_8(ptr); } static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr) { return kbuf->read_4(ptr); } static unsigned long long read_long(struct kbuffer *kbuf, void *ptr) { return kbuf->read_long(kbuf, ptr); } static int calc_index(struct kbuffer *kbuf, void *ptr) { return (unsigned long)ptr - (unsigned long)kbuf->data; } static int __next_event(struct kbuffer *kbuf); /** * kbuffer_alloc - allocat a new kbuffer * @size; enum to denote size of word * @endian: enum to denote endianness * * Allocates and returns a new kbuffer. */ struct kbuffer * kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian) { struct kbuffer *kbuf; int flags = 0; switch (size) { case KBUFFER_LSIZE_4: break; case KBUFFER_LSIZE_8: flags |= KBUFFER_FL_LONG_8; break; default: return NULL; } switch (endian) { case KBUFFER_ENDIAN_LITTLE: break; case KBUFFER_ENDIAN_BIG: flags |= KBUFFER_FL_BIG_ENDIAN; break; default: return NULL; } kbuf = zmalloc(sizeof(*kbuf)); if (!kbuf) return NULL; kbuf->flags = flags; if (host_is_bigendian()) kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN; if (do_swap(kbuf)) { kbuf->read_8 = __read_8_sw; kbuf->read_4 = __read_4_sw; } else { kbuf->read_8 = __read_8; kbuf->read_4 = __read_4; } if (kbuf->flags & KBUFFER_FL_LONG_8) kbuf->read_long = __read_long_8; else kbuf->read_long = __read_long_4; /* May be changed by kbuffer_set_old_format() */ kbuf->next_event = __next_event; return kbuf; } /** kbuffer_free - free an allocated kbuffer * @kbuf: The kbuffer to free * * Can take NULL as a parameter. */ void kbuffer_free(struct kbuffer *kbuf) { free(kbuf); } static unsigned int type4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 29) & 3; else return type_len_ts & 3; } static unsigned int len4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 27) & 7; else return (type_len_ts >> 2) & 7; } static unsigned int type_len4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return (type_len_ts >> 27) & ((1 << 5) - 1); else return type_len_ts & ((1 << 5) - 1); } static unsigned int ts4host(struct kbuffer *kbuf, unsigned int type_len_ts) { if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) return type_len_ts & ((1 << 27) - 1); else return type_len_ts >> 5; } /* * Linux 2.6.30 and earlier (not much ealier) had a different * ring buffer format. It should be obsolete, but we handle it anyway. */ enum old_ring_buffer_type { OLD_RINGBUF_TYPE_PADDING, OLD_RINGBUF_TYPE_TIME_EXTEND, OLD_RINGBUF_TYPE_TIME_STAMP, OLD_RINGBUF_TYPE_DATA, }; static unsigned int old_update_pointers(struct kbuffer *kbuf) { unsigned long long extend; unsigned int type_len_ts; unsigned int type; unsigned int len; unsigned int delta; unsigned int length; void *ptr = kbuf->data + kbuf->curr; type_len_ts = read_4(kbuf, ptr); ptr += 4; type = type4host(kbuf, type_len_ts); len = len4host(kbuf, type_len_ts); delta = ts4host(kbuf, type_len_ts); switch (type) { case OLD_RINGBUF_TYPE_PADDING: kbuf->next = kbuf->size; return 0; case OLD_RINGBUF_TYPE_TIME_EXTEND: extend = read_4(kbuf, ptr); extend <<= TS_SHIFT; extend += delta; delta = extend; ptr += 4; break; case OLD_RINGBUF_TYPE_TIME_STAMP: /* should never happen! */ kbuf->curr = kbuf->size; kbuf->next = kbuf->size; kbuf->index = kbuf->size; return -1; default: if (len) length = len * 4; else { length = read_4(kbuf, ptr); length -= 4; ptr += 4; } break; } kbuf->timestamp += delta; kbuf->index = calc_index(kbuf, ptr); kbuf->next = kbuf->index + length; return type; } static int __old_next_event(struct kbuffer *kbuf) { int type; do { kbuf->curr = kbuf->next; if (kbuf->next >= kbuf->size) return -1; type = old_update_pointers(kbuf); } while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING); return 0; } static unsigned int translate_data(struct kbuffer *kbuf, void *data, void **rptr, unsigned long long *delta, int *length) { unsigned long long extend; unsigned int type_len_ts; unsigned int type_len; type_len_ts = read_4(kbuf, data); data += 4; type_len = type_len4host(kbuf, type_len_ts); *delta = ts4host(kbuf, type_len_ts); switch (type_len) { case KBUFFER_TYPE_PADDING: *length = read_4(kbuf, data); break; case KBUFFER_TYPE_TIME_EXTEND: extend = read_4(kbuf, data); data += 4; extend <<= TS_SHIFT; extend += *delta; *delta = extend; *length = 0; break; case KBUFFER_TYPE_TIME_STAMP: data += 12; *length = 0; break; case 0: *length = read_4(kbuf, data) - 4; *length = (*length + 3) & ~3; data += 4; break; default: *length = type_len * 4; break; } *rptr = data; return type_len; } static unsigned int update_pointers(struct kbuffer *kbuf) { unsigned long long delta; unsigned int type_len; int length; void *ptr = kbuf->data + kbuf->curr; type_len = translate_data(kbuf, ptr, &ptr, &delta, &length); kbuf->timestamp += delta; kbuf->index = calc_index(kbuf, ptr); kbuf->next = kbuf->index + length; return type_len; } /** * kbuffer_translate_data - read raw data to get a record * @swap: Set to 1 if bytes in words need to be swapped when read * @data: The raw data to read * @size: Address to store the size of the event data. * * Returns a pointer to the event data. To determine the entire * record size (record metadata + data) just add the difference between * @data and the returned value to @size. */ void *kbuffer_translate_data(int swap, void *data, unsigned int *size) { unsigned long long delta; struct kbuffer kbuf; int type_len; int length; void *ptr; if (swap) { kbuf.read_8 = __read_8_sw; kbuf.read_4 = __read_4_sw; kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN; } else { kbuf.read_8 = __read_8; kbuf.read_4 = __read_4; kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0; } type_len = translate_data(&kbuf, data, &ptr, &delta, &length); switch (type_len) { case KBUFFER_TYPE_PADDING: case KBUFFER_TYPE_TIME_EXTEND: case KBUFFER_TYPE_TIME_STAMP: return NULL; }; *size = length; return ptr; } static int __next_event(struct kbuffer *kbuf) { int type; do { kbuf->curr = kbuf->next; if (kbuf->next >= kbuf->size) return -1; type = update_pointers(kbuf); } while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING); return 0; } static int next_event(struct kbuffer *kbuf) { return kbuf->next_event(kbuf); } /** * kbuffer_next_event - increment the current pointer * @kbuf: The kbuffer to read * @ts: Address to store the next record's timestamp (may be NULL to ignore) * * Increments the pointers into the subbuffer of the kbuffer to point to the * next event so that the next kbuffer_read_event() will return a * new event. * * Returns the data of the next event if a new event exists on the subbuffer, * NULL otherwise. */ void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts) { int ret; if (!kbuf || !kbuf->subbuffer) return NULL; ret = next_event(kbuf); if (ret < 0) return NULL; if (ts) *ts = kbuf->timestamp; return kbuf->data + kbuf->index; } /** * kbuffer_load_subbuffer - load a new subbuffer into the kbuffer * @kbuf: The kbuffer to load * @subbuffer: The subbuffer to load into @kbuf. * * Load a new subbuffer (page) into @kbuf. This will reset all * the pointers and update the @kbuf timestamp. The next read will * return the first event on @subbuffer. * * Returns 0 on succes, -1 otherwise. */ int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer) { unsigned long long flags; void *ptr = subbuffer; if (!kbuf || !subbuffer) return -1; kbuf->subbuffer = subbuffer; kbuf->timestamp = read_8(kbuf, ptr); ptr += 8; kbuf->curr = 0; if (kbuf->flags & KBUFFER_FL_LONG_8) kbuf->start = 16; else kbuf->start = 12; kbuf->data = subbuffer + kbuf->start; flags = read_long(kbuf, ptr); kbuf->size = (unsigned int)flags & COMMIT_MASK; if (flags & MISSING_EVENTS) { if (flags & MISSING_STORED) { ptr = kbuf->data + kbuf->size; kbuf->lost_events = read_long(kbuf, ptr); } else kbuf->lost_events = -1; } else kbuf->lost_events = 0; kbuf->index = 0; kbuf->next = 0; next_event(kbuf); return 0; } /** * kbuffer_read_event - read the next event in the kbuffer subbuffer * @kbuf: The kbuffer to read from * @ts: The address to store the timestamp of the event (may be NULL to ignore) * * Returns a pointer to the data part of the current event. * NULL if no event is left on the subbuffer. */ void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts) { if (!kbuf || !kbuf->subbuffer) return NULL; if (kbuf->curr >= kbuf->size) return NULL; if (ts) *ts = kbuf->timestamp; return kbuf->data + kbuf->index; } /** * kbuffer_timestamp - Return the timestamp of the current event * @kbuf: The kbuffer to read from * * Returns the timestamp of the current (next) event. */ unsigned long long kbuffer_timestamp(struct kbuffer *kbuf) { return kbuf->timestamp; } /** * kbuffer_read_at_offset - read the event that is at offset * @kbuf: The kbuffer to read from * @offset: The offset into the subbuffer * @ts: The address to store the timestamp of the event (may be NULL to ignore) * * The @offset must be an index from the @kbuf subbuffer beginning. * If @offset is bigger than the stored subbuffer, NULL will be returned. * * Returns the data of the record that is at @offset. Note, @offset does * not need to be the start of the record, the offset just needs to be * in the record (or beginning of it). * * Note, the kbuf timestamp and pointers are updated to the * returned record. That is, kbuffer_read_event() will return the same * data and timestamp, and kbuffer_next_event() will increment from * this record. */ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts) { void *data; if (offset < kbuf->start) offset = 0; else offset -= kbuf->start; /* Reset the buffer */ kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); while (kbuf->curr < offset) { data = kbuffer_next_event(kbuf, ts); if (!data) break; } return data; } /** * kbuffer_subbuffer_size - the size of the loaded subbuffer * @kbuf: The kbuffer to read from * * Returns the size of the subbuffer. Note, this size is * where the last event resides. The stored subbuffer may actually be * bigger due to padding and such. */ int kbuffer_subbuffer_size(struct kbuffer *kbuf) { return kbuf->size; } /** * kbuffer_curr_index - Return the index of the record * @kbuf: The kbuffer to read from * * Returns the index from the start of the data part of * the subbuffer to the current location. Note this is not * from the start of the subbuffer. An index of zero will * point to the first record. Use kbuffer_curr_offset() for * the actually offset (that can be used by kbuffer_read_at_offset()) */ int kbuffer_curr_index(struct kbuffer *kbuf) { return kbuf->curr; } /** * kbuffer_curr_offset - Return the offset of the record * @kbuf: The kbuffer to read from * * Returns the offset from the start of the subbuffer to the * current location. */ int kbuffer_curr_offset(struct kbuffer *kbuf) { return kbuf->curr + kbuf->start; } /** * kbuffer_event_size - return the size of the event data * @kbuf: The kbuffer to read * * Returns the size of the event data (the payload not counting * the meta data of the record) of the current event. */ int kbuffer_event_size(struct kbuffer *kbuf) { return kbuf->next - kbuf->index; } /** * kbuffer_curr_size - return the size of the entire record * @kbuf: The kbuffer to read * * Returns the size of the entire record (meta data and payload) * of the current event. */ int kbuffer_curr_size(struct kbuffer *kbuf) { return kbuf->next - kbuf->curr; } /** * kbuffer_missed_events - return the # of missed events from last event. * @kbuf: The kbuffer to read from * * Returns the # of missed events (if recorded) before the current * event. Note, only events on the beginning of a subbuffer can * have missed events, all other events within the buffer will be * zero. */ int kbuffer_missed_events(struct kbuffer *kbuf) { /* Only the first event can have missed events */ if (kbuf->curr) return 0; return kbuf->lost_events; } /** * kbuffer_set_old_forma - set the kbuffer to use the old format parsing * @kbuf: The kbuffer to set * * This is obsolete (or should be). The first kernels to use the * new ring buffer had a slightly different ring buffer format * (2.6.30 and earlier). It is still somewhat supported by kbuffer, * but should not be counted on in the future. */ void kbuffer_set_old_format(struct kbuffer *kbuf) { kbuf->flags |= KBUFFER_FL_OLD_FORMAT; kbuf->next_event = __old_next_event; } /** * kbuffer_start_of_data - return offset of where data starts on subbuffer * @kbuf: The kbuffer * * Returns the location on the subbuffer where the data starts. */ int kbuffer_start_of_data(struct kbuffer *kbuf) { return kbuf->start; } trace-cmd-2.5.3/kbuffer.h000066400000000000000000000043271246701203100151570ustar00rootroot00000000000000/* * Copyright (C) 2012 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #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); 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); #endif /* _K_BUFFER_H */ trace-cmd-2.5.3/kernel-shark.c000066400000000000000000001667141246701203100161250ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-compat.h" #include "trace-capture.h" #include "trace-cmd.h" #include "trace-gui.h" #include "kernel-shark.h" #include "event-utils.h" #include "version.h" #define ___stringify(X) #X #define __stringify(X) ___stringify(X) #define DEBUG_LEVEL 0 #if DEBUG_LEVEL > 0 # define dprintf(l, x...) \ do { \ if (l <= DEBUG_LEVEL) \ printf(x); \ } while (0) #else # define dprintf(l, x...) do { if (0) printf(x); } while (0) #endif #define TRACE_WIDTH 800 #define TRACE_HEIGHT 600 #define default_input_file "trace.dat" static char *input_file; void usage(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); } static gboolean display_warnings; /* * trace_sync_select_menu - helper function to the syncing of list and graph filters * * Creates a pop up dialog with the selections given. The selections will be * radio buttons to the user. The keep is a value that will be set to the check * box (default on) if the user wants to keep the selection persistant. */ static int trace_sync_select_menu(const gchar *title, gchar **selections, gboolean *keep) { GtkWidget *dialog; GtkWidget *radio; GtkWidget *check; GSList *group; int result; int i; dialog = gtk_dialog_new_with_buttons(title, NULL, GTK_DIALOG_MODAL, "OK", GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); radio = gtk_radio_button_new_with_label(NULL, selections[0]); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0); gtk_widget_show(radio); group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); for (i = 1; selections[i]; i++) { radio = gtk_radio_button_new_with_label(group, selections[i]); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0); gtk_widget_show(radio); } check = gtk_check_button_new_with_label("Keep the filters in sync?"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), check, TRUE, TRUE, 0); gtk_widget_show(check); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: i = 0; for (i = 0; group; i++, group = g_slist_next(group)) { radio = group->data; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio))) break; } result = i; *keep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check)); break; default: result = -1; } gtk_widget_destroy(dialog); return result; } static void update_tree_view_filters(struct shark_info *info, struct filter_task *task_filter, struct filter_task *hide_tasks) { if (info->list_filter_enabled) trace_view_update_filters(info->treeview, task_filter, hide_tasks); if (filter_task_count(task_filter) || filter_task_count(hide_tasks)) info->list_filter_available = 1; else { info->list_filter_enabled = 0; info->list_filter_available = 0; } } /* graph callbacks */ /* convert_nano() and print_time() are copied from trace-graph.c for debugging purposes, and should be deleted when this is complete (or merged with trace-graph.c */ static void convert_nano(unsigned long long time, unsigned long *sec, unsigned long *usec) { *sec = time / 1000000000ULL; *usec = (time / 1000) % 1000000; } static void print_time(unsigned long long time) { unsigned long sec, usec; if (!DEBUG_LEVEL) return; convert_nano(time, &sec, &usec); printf("%lu.%06lu", sec, usec); } static void ks_graph_select(struct graph_info *ginfo, guint64 cursor) { struct graph_callbacks *cbs; struct shark_info *info; dprintf(1, "Cursor: "); print_time(cursor); dprintf(1, " selected\n"); cbs = trace_graph_get_callbacks(ginfo); info = container_of(cbs, struct shark_info, graph_cbs); trace_view_select(info->treeview, cursor); } static void ks_graph_filter(struct graph_info *ginfo, struct filter_task *task_filter, struct filter_task *hide_tasks) { struct graph_callbacks *cbs; struct shark_info *info; cbs = trace_graph_get_callbacks(ginfo); info = container_of(cbs, struct shark_info, graph_cbs); if (!info->sync_task_filters) return; update_tree_view_filters(info, task_filter, hide_tasks); } static void free_info(struct shark_info *info) { tracecmd_close(info->handle); trace_graph_free_info(info->ginfo); filter_task_hash_free(info->list_task_filter); filter_task_hash_free(info->list_hide_tasks); kernel_shark_clear_capture(info); free(info->current_filter); free(info->ginfo); free(info); } static void update_title(GtkWidget *window, const gchar *file) { GString *gstr; gchar *str; gstr = g_string_new("kernelshark"); g_string_append_printf(gstr, "(%s)", basename(file)); str = g_string_free(gstr, FALSE); gtk_window_set_title(GTK_WINDOW(window), str); g_free(str); } static void unsync_task_filters(struct shark_info *info) { info->sync_task_filters = 0; gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu), "Sync Graph and List Task Filters"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu), "graph tasks"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu), "graph hide tasks"); gtk_widget_show(info->list_task_menu); gtk_widget_show(info->list_hide_task_menu); /* The list now uses its own hash */ info->list_task_filter = filter_task_hash_copy(info->ginfo->task_filter); info->list_hide_tasks = filter_task_hash_copy(info->ginfo->hide_tasks); } static void sync_task_filters(struct shark_info *info) { info->sync_task_filters = 1; gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu), "Unsync Graph and List Task Filters"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu), "tasks"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu), "hide tasks"); gtk_widget_hide(info->list_task_menu); gtk_widget_hide(info->list_hide_task_menu); } static void unsync_event_filters(struct shark_info *info) { info->sync_event_filters = 0; gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu), "Sync Graph and List Event Filters"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu), "graph events"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu), "graph advanced events"); gtk_widget_show(info->list_events_menu); gtk_widget_show(info->list_adv_events_menu); } static void sync_event_filters(struct shark_info *info) { info->sync_event_filters = 1; gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu), "Unsync Graph and List Event Filters"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu), "events"); gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu), "advanced events"); gtk_widget_hide(info->list_events_menu); gtk_widget_hide(info->list_adv_events_menu); } static void alt_warn(const char *fmt, va_list ap) { display_warnings = TRUE; vpr_stat(fmt, ap); } /** * kernelshark_load_file - load a new file into kernelshark * @info: the kernelshark descriptor * @file: the file to load * * Returns: 0 on success, -1 on error */ int kernelshark_load_file(struct shark_info *info, const char *file) { struct tracecmd_input *handle; /* * Have warnings go into the status bar. If we had any * warnings, pop up a message at the end. */ display_warnings = 0; pr_stat("\nLoading file %s", file); trace_dialog_register_alt_warning(alt_warn); handle = tracecmd_open(file); trace_dialog_register_alt_warning(NULL); if (display_warnings) { errno = 0; warning("Warnings occurred on loading.\n" "See display status for details"); } if (!handle) return -1; tracecmd_close(info->handle); info->handle = handle; trace_graph_load_handle(info->ginfo, handle); trace_view_reload(info->treeview, handle, info->spin); update_title(info->window, file); return 0; } static void /* Callback for the clicked signal of the Load button */ load_clicked (gpointer data) { struct shark_info *info = data; gchar *filename; filename = trace_get_file_dialog_filter("Load File", NULL, TRACE_DIALOG_FILTER_DATA, FALSE); if (!filename) return; kernelshark_load_file(info, filename); g_free(filename); } static GString *get_home_filters_new(void) { char *path = getenv("HOME"); GString *str; str = g_string_new(path); g_string_append(str, "/.trace-cmd/filters/"); return str; } static int create_home_filters(void) { char *path = getenv("HOME"); GString *str; struct stat st; int ret; str = g_string_new(path); g_string_append(str, "/.trace-cmd"); ret = stat(str->str, &st); if (ret < 0) { ret = mkdir(str->str, 0755); if (ret < 0) { warning("Can not create %s", str->str); goto out; } } g_string_append(str, "/filters"); ret = stat(str->str, &st); if (ret < 0) { ret = mkdir(str->str, 0755); if (ret < 0) { warning("Can not create %s", str->str); goto out; } } ret = 0; out: g_string_free(str, TRUE); return ret; } static void load_filter(struct shark_info *info, const char *filename) { struct graph_info *ginfo = info->ginfo; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; struct tracecmd_xml_handle *handle; struct filter_task *task_filter; struct filter_task *hide_tasks; struct event_filter *event_filter; int ret; handle = tracecmd_xml_open(filename); if (!handle) { warning("Could not open %s", filename); return; } /* Unsync the list and graph filters */ if (info->sync_task_filters) unsync_task_filters(info); if (info->sync_event_filters) unsync_event_filters(info); ret = tracecmd_xml_system_exists(handle, "GraphTaskFilter"); if (ret) { filter_task_clear(ginfo->task_filter); filter_task_clear(ginfo->hide_tasks); trace_filter_load_filters(handle, "GraphTaskFilter", ginfo->task_filter, ginfo->hide_tasks); trace_graph_refresh_filters(ginfo); } ret = tracecmd_xml_system_exists(handle, "ListTaskFilter"); if (ret) { task_filter = info->list_task_filter; hide_tasks = info->list_hide_tasks; filter_task_clear(task_filter); filter_task_clear(hide_tasks); trace_filter_load_filters(handle, "ListTaskFilter", task_filter, hide_tasks); update_tree_view_filters(info, task_filter, hide_tasks); } trace_graph_load_filters(ginfo, handle); ret = trace_view_load_filters(handle, trace_tree); tracecmd_xml_close(handle); /* * If the events or tasks filters are the same for both * the list and graph, then sync them back. */ if (filter_task_compare(ginfo->task_filter, info->list_task_filter) && filter_task_compare(ginfo->hide_tasks, info->list_hide_tasks)) sync_task_filters(info); model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); event_filter = trace_view_store_get_event_filter(store); if (pevent_filter_compare(event_filter, ginfo->event_filter)) sync_event_filters(info); } static void load_filter_clicked(GtkMenuItem *item, gpointer data) { struct shark_info *info = data; const char *name; GString *path; name = gtk_menu_item_get_label(item); path = get_home_filters_new(); g_string_append_printf(path, "/%s.ksf", name); load_filter(info, path->str); g_string_free(path, TRUE); free(info->current_filter); info->current_filter = strdup(name); } /* Callback for the clicked signal of the Load Filters button */ static void import_filters_clicked (gpointer data) { struct shark_info *info = data; gchar *filename; filename = trace_get_file_dialog_filter("Load Filters", NULL, TRACE_DIALOG_FILTER_FILTER, FALSE); if (!filename) return; load_filter(info, filename); } static void save_filters(struct shark_info *info, const char *filename) { struct graph_info *ginfo = info->ginfo; struct tracecmd_xml_handle *handle; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); struct filter_task *task_filter; struct filter_task *hide_tasks; handle = tracecmd_xml_create(filename, VERSION_STRING); if (!handle) { warning("Could not create %s", filename); return; } trace_view_save_filters(handle, trace_tree); trace_graph_save_filters(ginfo, handle); trace_filter_save_filters(handle, "GraphTaskFilter", ginfo->task_filter, ginfo->hide_tasks); if (info->sync_task_filters) { task_filter = ginfo->task_filter; hide_tasks = ginfo->hide_tasks; } else { task_filter = info->list_task_filter; hide_tasks = info->list_hide_tasks; } trace_filter_save_filters(handle, "ListTaskFilter", task_filter, hide_tasks); tracecmd_xml_close(handle); } /* Callback for the clicked signal of the Save Filters button */ static void export_filters_clicked (gpointer data) { struct shark_info *info = data; gchar *filename; filename = trace_get_file_dialog_filter("Save Filters", "Save", TRACE_DIALOG_FILTER_FILTER, TRUE); if (!filename) return; save_filters(info, filename); g_free(filename); } static void update_load_filter(struct shark_info *info) { struct dirent *dent; struct stat st; DIR *dir; GtkWidget *menu; GtkWidget *sub_item; GString *path; int ret; path = get_home_filters_new(); menu = gtk_menu_new(); ret = stat(path->str, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) goto update_rest; dir = opendir(path->str); if (!dir) goto update_rest; while ((dent = readdir(dir))) { const char *name = dent->d_name; GString *file; gchar *item; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; if (strcmp(name + strlen(name) - 4, ".ksf") != 0) continue; file = g_string_new(path->str); g_string_append_printf(file, "/%s", name); /* Save the file name but remove the .kss extention */ item = g_strdup(name); item[strlen(name) - 4] = 0; sub_item = gtk_menu_item_new_with_label(item); gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); gtk_widget_show(sub_item); g_signal_connect(G_OBJECT (sub_item), "activate", G_CALLBACK (load_filter_clicked), (gpointer) info); g_free(item); g_string_free(file, TRUE); } update_rest: g_string_free(path, TRUE); /* --- File - Load Filter - Filters --- */ sub_item = gtk_menu_item_new_with_label("Import Filter"); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (import_filters_clicked), (gpointer) info); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We do need to show menu items */ gtk_widget_show(sub_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM (info->load_filter_menu), menu); } /* Callback for the clicked signal of the Save Filters button */ static void save_filter_clicked (gpointer data) { struct shark_info *info = data; struct stat st; GtkWidget *dialog; GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; GString *file; const char *name; gint result; int ret; dialog = gtk_dialog_new_with_buttons("Save Filter", NULL, GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Filter Name: "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); gtk_widget_show(entry); if (info->current_filter) gtk_entry_set_text(GTK_ENTRY(entry), info->current_filter); again: result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: name = gtk_entry_get_text(GTK_ENTRY(entry)); if (!name || !has_text(name)) { warning("Must enter a name"); goto again; } /* Make sure home settings exists */ if (create_home_filters() < 0) break; file = get_home_filters_new(); g_string_append_printf(file, "/%s.ksf", name); ret = stat(file->str, &st); if (ret >= 0) { ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK, "The Filter '%s' already exists.\n" "Are you sure you want to replace it", name); if (ret == GTK_RESPONSE_NO) { g_string_free(file, TRUE); goto again; } } save_filters(info, file->str); free(info->current_filter); info->current_filter = strdup(name); update_load_filter(info); g_string_free(file, TRUE); break; case GTK_RESPONSE_REJECT: break; default: break; }; gtk_widget_destroy(dialog); } /* Callback for the clicked signal of the Exit button */ static void exit_clicked (gpointer data) { struct shark_info *info = data; gtk_widget_destroy (info->window); /* the user data points to the main window */ free_info(info); gtk_main_quit (); } /* Callback for the delete_event signal of the main application window */ static gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { struct shark_info *info = data; gtk_widget_destroy (widget); /* destroy the main window */ free_info(info); gtk_main_quit (); return TRUE; } /* Callback for the clicked signal of the tasks sync filter button */ static void sync_task_filter_clicked (GtkWidget *subitem, gpointer data) { struct shark_info *info = data; struct filter_task *task_filter; struct filter_task *hide_tasks; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; gboolean keep; gchar *selections[] = { "Sync List Filter with Graph Filter", "Sync Graph Filter with List Filter", NULL }; int result; if (info->sync_task_filters) { /* Separate the List and Graph filters */ unsync_task_filters(info); return; } model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); /* If they are already equal, then just perminently sync them */ if (filter_task_compare(info->ginfo->task_filter, info->list_task_filter) && filter_task_compare(info->ginfo->hide_tasks, info->list_hide_tasks)) result = 2; else /* Ask user which way to sync */ result = trace_sync_select_menu("Sync Task Filters", selections, &keep); switch (result) { case 0: /* Sync List Filter with Graph Filter */ filter_task_hash_free(info->list_task_filter); filter_task_hash_free(info->list_hide_tasks); info->list_task_filter = NULL; info->list_hide_tasks = NULL; task_filter = info->ginfo->task_filter; hide_tasks = info->ginfo->hide_tasks; if (!keep) { info->list_task_filter = filter_task_hash_copy(task_filter); info->list_hide_tasks = filter_task_hash_copy(hide_tasks); } update_tree_view_filters(info, task_filter, hide_tasks); break; case 1: /* Sync Graph Filter with List Filter */ trace_graph_update_filters(info->ginfo, info->list_task_filter, info->list_hide_tasks); if (keep) { filter_task_hash_free(info->list_task_filter); filter_task_hash_free(info->list_hide_tasks); info->list_task_filter = NULL; info->list_hide_tasks = NULL; } break; case 2: keep = 1; break; default: keep = 0; } if (keep) sync_task_filters(info); } /* Callback for the clicked signal of the events sync filter button */ static void sync_events_filter_clicked (GtkWidget *subitem, gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; struct event_filter *event_filter; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; gboolean keep; gboolean all_events; gchar *selections[] = { "Sync List Filter with Graph Filter", "Sync Graph Filter with List Filter", NULL }; int result; if (info->sync_event_filters) { /* Separate the List and Graph filters */ unsync_event_filters(info); return; } model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); event_filter = trace_view_store_get_event_filter(store); /* If they are already equal, then just perminently sync them */ if (pevent_filter_compare(event_filter, ginfo->event_filter)) result = 2; else /* Ask user which way to sync */ result = trace_sync_select_menu("Sync Event Filters", selections, &keep); switch (result) { case 0: /* Sync List Filter with Graph Filter */ all_events = ginfo->all_events; trace_view_copy_filter(info->treeview, all_events, ginfo->event_filter); break; case 1: /* Sync Graph Filter with List Filter */ all_events = trace_view_store_get_all_events_enabled(store); trace_graph_copy_filter(info->ginfo, all_events, event_filter); break; case 2: keep = 1; break; default: keep = 0; } if (keep) sync_event_filters(info); } static void filter_list_enable_clicked (gpointer data); static void __update_list_task_filter_callback(struct shark_info *info, gboolean accept, gint *selected, struct filter_task *task_filter) { GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; int i; if (!accept) return; model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); filter_task_clear(task_filter); if (selected) { for (i = 0; selected[i] >= 0; i++) filter_task_add_pid(task_filter, selected[i]); } update_tree_view_filters(info, info->list_task_filter, info->list_hide_tasks); /* * The menu filters always enable the filters. */ if (info->list_filter_available && !info->list_filter_enabled) filter_list_enable_clicked(info); } static void update_list_task_filter_callback(gboolean accept, gint *selected, gint *non_select, gpointer data) { struct shark_info *info = data; __update_list_task_filter_callback(info, accept, selected, info->list_task_filter); } static void update_list_hide_task_filter_callback(gboolean accept, gint *selected, gint *non_select, gpointer data) { struct shark_info *info = data; __update_list_task_filter_callback(info, accept, selected, info->list_hide_tasks); } /* Callback for the clicked signal of the List Tasks filter button */ static void __list_tasks_clicked (struct shark_info *info, struct filter_task *task_filter, trace_task_cb_func func) { GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); struct graph_info *ginfo = info->ginfo; GtkTreeModel *model; TraceViewStore *store; gint *selected; gint *tasks; if (!ginfo->handle) return; model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); tasks = trace_graph_task_list(ginfo); selected = filter_task_pids(task_filter); trace_task_dialog(info->handle, tasks, selected, func, info); free(tasks); free(selected); } static void list_tasks_clicked (gpointer data) { struct shark_info *info = data; __list_tasks_clicked(info, info->list_task_filter, update_list_task_filter_callback); } static void list_hide_tasks_clicked (gpointer data) { struct shark_info *info = data; __list_tasks_clicked(info, info->list_hide_tasks, update_list_hide_task_filter_callback); } static void __update_graph_task_filter_callback(struct shark_info *info, gboolean accept, gint *selected, struct filter_task *task_filter) { struct graph_info *ginfo = info->ginfo; int i; if (!accept) return; filter_task_clear(task_filter); if (selected) { for (i = 0; selected[i] >= 0; i++) filter_task_add_pid(task_filter, selected[i]); } trace_graph_refresh_filters(ginfo); /* * The menu filters always enable the filters. */ if (ginfo->filter_available) { if (!ginfo->filter_enabled) trace_graph_filter_toggle(info->ginfo); if (info->sync_task_filters && !info->list_filter_enabled) filter_list_enable_clicked(info); } } static void update_graph_task_filter_callback(gboolean accept, gint *selected, gint *non_select, gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; __update_graph_task_filter_callback(info, accept, selected, ginfo->task_filter); } static void update_graph_hide_task_filter_callback(gboolean accept, gint *selected, gint *non_select, gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; __update_graph_task_filter_callback(info, accept, selected, ginfo->hide_tasks); } /* Callback for the clicked signal of the Tasks filter button */ static void __graph_tasks_clicked (struct shark_info *info, struct filter_task *task_filter, trace_task_cb_func func) { struct graph_info *ginfo = info->ginfo; gint *selected; gint *tasks; if (!ginfo->handle) return; tasks = trace_graph_task_list(ginfo); selected = filter_task_pids(task_filter); trace_task_dialog(ginfo->handle, tasks, selected, func, info); free(tasks); free(selected); } static void graph_tasks_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; __graph_tasks_clicked(info, ginfo->task_filter, update_graph_task_filter_callback); } static void graph_hide_tasks_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; __graph_tasks_clicked(info, ginfo->hide_tasks, update_graph_hide_task_filter_callback); } /* Callback for the clicked signal of the Events filter button */ static void list_events_clicked (gpointer data) { struct shark_info *info = data; struct event_filter *event_filter; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; gboolean all_events; model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); all_events = trace_view_store_get_all_events_enabled(store); event_filter = trace_view_store_get_event_filter(store); /* * This menu is not available when in sync, so we * can call the treeview callback directly. */ trace_filter_event_filter_dialog(store->handle, event_filter, all_events, trace_view_event_filter_callback, info->treeview); } static void graph_event_filter_callback(gboolean accept, gboolean all_events, gchar **systems, gint *events, gpointer data) { struct shark_info *info = data; trace_graph_event_filter_callback(accept, all_events, systems, events, info->ginfo); if (info->sync_event_filters) trace_view_event_filter_callback(accept, all_events, systems, events, info->treeview); } static void graph_events_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; gboolean all_events; all_events = ginfo->all_events; trace_filter_event_filter_dialog(info->handle, ginfo->event_filter, all_events, graph_event_filter_callback, info); } /* Callback for the clicked signal of the List advanced filter button */ static void adv_list_filter_clicked (gpointer data) { struct shark_info *info = data; struct event_filter *event_filter; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); GtkTreeModel *model; TraceViewStore *store; model = gtk_tree_view_get_model(trace_tree); if (!model) return; store = TRACE_VIEW_STORE(model); event_filter = trace_view_store_get_event_filter(store); /* * This menu is not available when in sync, so we * can call the treeview callback directly. */ trace_adv_filter_dialog(store->handle, event_filter, trace_view_adv_filter_callback, trace_tree); } static void graph_adv_filter_callback(gboolean accept, const gchar *text, gint *event_ids, gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; trace_graph_adv_filter_callback(accept, text, event_ids, ginfo); if (info->sync_event_filters) trace_view_adv_filter_callback(accept, text, event_ids, info->treeview); } /* Callback for the clicked signal of the Graph advanced filter button */ static void adv_graph_filter_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; trace_adv_filter_dialog(ginfo->handle, ginfo->event_filter, graph_adv_filter_callback, info); } /* Callback for the clicked signal of the CPUs filter button */ static void cpus_clicked (gpointer data) { struct shark_info *info = data; GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); TraceViewStore *store; gboolean all_cpus; guint64 *cpu_mask; if (!info->handle) return; store = TRACE_VIEW_STORE(gtk_tree_view_get_model(trace_tree)); all_cpus = trace_view_store_get_all_cpus(store); cpu_mask = trace_view_store_get_cpu_mask(store); trace_filter_cpu_dialog(all_cpus, cpu_mask, trace_view_store_get_cpus(store), trace_view_cpu_filter_callback, trace_tree); } /* Callback for the clicked signal of the plot CPUs button */ static void plot_cpu_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; gboolean all_cpus; guint64 *cpu_mask; if (!ginfo->handle) return; graph_plot_cpus_plotted(ginfo, &all_cpus, &cpu_mask); trace_filter_cpu_dialog(all_cpus, cpu_mask, ginfo->cpus, graph_plot_cpus_update_callback, ginfo); g_free(cpu_mask); } /* Callback for the clicked signal of the plot tasks button */ static void plot_tasks_clicked (gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; gint *selected; gint *tasks; if (!ginfo->handle) return; tasks = trace_graph_task_list(ginfo); graph_plot_task_plotted(ginfo, &selected); trace_task_dialog(ginfo->handle, tasks, selected, graph_plot_task_update_callback, ginfo); free(tasks); free(selected); } /* Callback for the clicked signal of the help contents button */ static void help_content_clicked (gpointer data) { struct shark_info *info = data; GError *error = NULL; gchar *link; link = "file://" __stringify(HELP_DIR) "/index.html"; trace_show_help(info->window, link, &error); } /* Callback for the clicked signal of the help about button */ static void help_about_clicked (gpointer data) { struct shark_info *info = data; trace_dialog(GTK_WINDOW(info->window), TRACE_GUI_INFO, "KernelShark\n\n" "version %s\n\n" "Copyright (C) 2009, 2010 Red Hat Inc\n\n" " Author: Steven Rostedt ", VERSION_STRING); } static void graph_follows_tree(struct shark_info *info, GtkTreeView *treeview, GtkTreePath *path) { TraceViewRecord *rec; GtkTreeModel *model; gchar *spath; guint64 time; gint row; model = gtk_tree_view_get_model(treeview); /* This can be called when we NULL out the model */ if (!model) return; spath = gtk_tree_path_to_string(path); row = atoi(spath); g_free(spath); rec = trace_view_store_get_visible_row(TRACE_VIEW_STORE(model), row); time = rec->timestamp; trace_graph_select_by_time(info->ginfo, time); } static void row_double_clicked(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { struct shark_info *info = data; graph_follows_tree(info, treeview, path); } static void cursor_changed(GtkTreeView *treeview, gpointer data) { struct shark_info *info = data; GtkTreePath *path; if (!info->graph_follows) return; gtk_tree_view_get_cursor(treeview, &path, NULL); if (!path) return; graph_follows_tree(info, treeview, path); gtk_tree_path_free(path); } static void filter_graph_enable_clicked (gpointer data) { struct shark_info *info = data; trace_graph_filter_toggle(info->ginfo); } static void filter_list_enable_clicked (gpointer data) { struct shark_info *info = data; struct filter_task *task_filter; struct filter_task *hide_tasks; info->list_filter_enabled ^= 1; if (info->sync_task_filters) { task_filter = info->ginfo->task_filter; hide_tasks = info->ginfo->hide_tasks; } else { task_filter = info->list_task_filter; hide_tasks = info->list_hide_tasks; } if (info->list_filter_enabled) trace_view_update_filters(info->treeview, task_filter, hide_tasks); else trace_view_update_filters(info->treeview, NULL, NULL); } static void filter_update_list_filter(struct shark_info *info, struct filter_task *filter, struct filter_task *other_filter) { struct filter_task_item *task; int pid = info->selected_task; task = filter_task_find_pid(filter, pid); if (task) { filter_task_remove_pid(filter, pid); if (!filter_task_count(filter) && !filter_task_count(other_filter)) { info->list_filter_enabled = 0; info->list_filter_available = 0; } } else { filter_task_add_pid(filter, pid); info->list_filter_available = 1; } } static void filter_add_task_clicked (gpointer data) { struct shark_info *info = data; int pid = info->selected_task; if (info->sync_task_filters) { trace_graph_filter_add_remove_task(info->ginfo, pid); return; } filter_update_list_filter(info, info->list_task_filter, info->list_hide_tasks); trace_view_update_filters(info->treeview, info->list_task_filter, info->list_hide_tasks); } static void filter_graph_add_task_clicked (gpointer data) { struct shark_info *info = data; trace_graph_filter_add_remove_task(info->ginfo, info->selected_task); } static void filter_hide_task_clicked (gpointer data) { struct shark_info *info = data; if (info->sync_task_filters) { trace_graph_filter_hide_show_task(info->ginfo, info->selected_task); return; } filter_update_list_filter(info, info->list_hide_tasks, info->list_task_filter); trace_view_update_filters(info->treeview, info->list_task_filter, info->list_hide_tasks); } static void filter_graph_hide_task_clicked (gpointer data) { struct shark_info *info = data; trace_graph_filter_hide_show_task(info->ginfo, info->selected_task); } static void filter_clear_tasks_clicked (gpointer data) { struct shark_info *info = data; if (info->sync_task_filters) { trace_graph_clear_tasks(info->ginfo); return; } filter_task_clear(info->list_task_filter); filter_task_clear(info->list_hide_tasks); trace_view_update_filters(info->treeview, NULL, NULL); info->list_filter_available = 0; info->list_filter_enabled = 0; } static void filter_graph_clear_tasks_clicked (gpointer data) { struct shark_info *info = data; trace_graph_clear_tasks(info->ginfo); } static void graph_check_toggle(gpointer data, GtkWidget *widget) { struct shark_info *info = data; info->graph_follows = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); } static void set_menu_label(GtkWidget *menu, const char *comm, int pid, const char *fmt) { int len = strlen(comm) + strlen(fmt) + 50; char text[len]; snprintf(text, len, fmt, comm, pid); gtk_menu_item_set_label(GTK_MENU_ITEM(menu), text); } static gboolean do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct shark_info *info = data; struct graph_info *ginfo = info->ginfo; static GtkWidget *menu; static GtkWidget *menu_filter_graph_enable; static GtkWidget *menu_filter_list_enable; static GtkWidget *menu_filter_add_task; static GtkWidget *menu_filter_hide_task; static GtkWidget *menu_filter_clear_tasks; static GtkWidget *menu_filter_graph_add_task; static GtkWidget *menu_filter_graph_hide_task; static GtkWidget *menu_filter_graph_clear_tasks; struct pevent_record *record; TraceViewRecord *vrec; GtkTreeModel *model; const char *comm; gint pid; gint len; guint64 offset; gint row; gint cpu; if (!menu) { menu = gtk_menu_new(); menu_filter_graph_enable = gtk_menu_item_new_with_label("Enable Graph Task Filter"); gtk_widget_show(menu_filter_graph_enable); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_enable); g_signal_connect_swapped (G_OBJECT (menu_filter_graph_enable), "activate", G_CALLBACK (filter_graph_enable_clicked), data); menu_filter_list_enable = gtk_menu_item_new_with_label("Enable List Task Filter"); gtk_widget_show(menu_filter_list_enable); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_list_enable); g_signal_connect_swapped (G_OBJECT (menu_filter_list_enable), "activate", G_CALLBACK (filter_list_enable_clicked), data); menu_filter_add_task = gtk_menu_item_new_with_label("Add Task"); gtk_widget_show(menu_filter_add_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task); g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate", G_CALLBACK (filter_add_task_clicked), data); menu_filter_graph_add_task = gtk_menu_item_new_with_label("Add Task to Graph"); gtk_widget_show(menu_filter_graph_add_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_add_task); g_signal_connect_swapped (G_OBJECT (menu_filter_graph_add_task), "activate", G_CALLBACK (filter_graph_add_task_clicked), data); menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task"); gtk_widget_show(menu_filter_hide_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task); g_signal_connect_swapped (G_OBJECT (menu_filter_hide_task), "activate", G_CALLBACK (filter_hide_task_clicked), data); menu_filter_graph_hide_task = gtk_menu_item_new_with_label("Hide Task from Graph"); gtk_widget_show(menu_filter_graph_hide_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_hide_task); g_signal_connect_swapped (G_OBJECT (menu_filter_graph_hide_task), "activate", G_CALLBACK (filter_graph_hide_task_clicked), data); menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); gtk_widget_show(menu_filter_clear_tasks); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate", G_CALLBACK (filter_clear_tasks_clicked), data); menu_filter_graph_clear_tasks = gtk_menu_item_new_with_label("Clear Graph Task Filter"); gtk_widget_show(menu_filter_graph_clear_tasks); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_clear_tasks); g_signal_connect_swapped (G_OBJECT (menu_filter_graph_clear_tasks), "activate", G_CALLBACK (filter_graph_clear_tasks_clicked), data); } row = trace_view_get_selected_row(GTK_WIDGET(info->treeview)); if (row >= 0) { model = gtk_tree_view_get_model(GTK_TREE_VIEW(info->treeview)); vrec = trace_view_store_get_row(TRACE_VIEW_STORE(model), row); offset = vrec->offset; record = tracecmd_read_at(info->handle, offset, &cpu); if (record) { pid = pevent_data_pid(ginfo->pevent, record); comm = pevent_data_comm_from_pid(ginfo->pevent, pid); len = strlen(comm) + 50; if (info->sync_task_filters) { if (trace_graph_filter_task_find_pid(ginfo, pid)) set_menu_label(menu_filter_add_task, comm, pid, "Remove %s-%d from filters"); else set_menu_label(menu_filter_add_task, comm, pid, "Add %s-%d to filters"); if (trace_graph_hide_task_find_pid(ginfo, pid)) set_menu_label(menu_filter_hide_task, comm, pid, "Show %s-%d"); else set_menu_label(menu_filter_hide_task, comm, pid, "Hide %s-%d"); gtk_widget_hide(menu_filter_graph_add_task); gtk_widget_hide(menu_filter_graph_hide_task); } else { if (filter_task_find_pid(info->list_task_filter, pid)) set_menu_label(menu_filter_add_task, comm, pid, "Remove %s-%d from List filter"); else set_menu_label(menu_filter_add_task, comm, pid, "Add %s-%d to List filter"); if (filter_task_find_pid(info->list_hide_tasks, pid)) set_menu_label(menu_filter_hide_task, comm, pid, "Show %s-%d in List"); else set_menu_label(menu_filter_hide_task, comm, pid, "Hide %s-%d from List"); if (trace_graph_filter_task_find_pid(ginfo, pid)) set_menu_label(menu_filter_graph_add_task, comm, pid, "Remove %s-%d from Graph filter"); else set_menu_label(menu_filter_graph_add_task, comm, pid, "Add %s-%d to Graph filter"); if (trace_graph_hide_task_find_pid(ginfo, pid)) set_menu_label(menu_filter_graph_hide_task, comm, pid, "Show %s-%d in Graph"); else set_menu_label(menu_filter_graph_hide_task, comm, pid, "Hide %s-%d from Graph"); gtk_widget_show(menu_filter_graph_add_task); gtk_widget_show(menu_filter_graph_hide_task); } ginfo->filter_task_selected = pid; info->selected_task = pid; gtk_widget_show(menu_filter_add_task); gtk_widget_show(menu_filter_hide_task); free_record(record); } } else { gtk_widget_hide(menu_filter_add_task); gtk_widget_hide(menu_filter_hide_task); gtk_widget_hide(menu_filter_graph_add_task); gtk_widget_hide(menu_filter_graph_hide_task); } if (ginfo->filter_enabled) gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), "Disable Graph Task Filter"); else gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), "Enable Graph Task Filter"); if (info->list_filter_enabled) gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), "Disable List Task Filter"); else gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), "Enable List Task Filter"); if (ginfo->filter_available) gtk_widget_set_sensitive(menu_filter_graph_enable, TRUE); else gtk_widget_set_sensitive(menu_filter_graph_enable, FALSE); if ((info->sync_task_filters && ginfo->filter_available) || (!info->sync_task_filters && info->list_filter_available)) gtk_widget_set_sensitive(menu_filter_list_enable, TRUE); else gtk_widget_set_sensitive(menu_filter_list_enable, FALSE); if (info->sync_task_filters) { if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); else gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_clear_tasks), "Clear Task Filter"); gtk_widget_hide(menu_filter_graph_clear_tasks); } else { if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, TRUE); else gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, FALSE); if (filter_task_count(info->list_task_filter) || filter_task_count(info->list_hide_tasks)) gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); else gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_clear_tasks), "Clear List Task Filter"); gtk_widget_show(menu_filter_graph_clear_tasks); } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time()); return TRUE; } static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { if (event->button == 3) return do_tree_popup(widget, event, data); return FALSE; } static void sig_end(int sig) { fprintf(stderr, "kernelshark: Received SIGINT\n"); exit(0); } void kernel_shark(int argc, char **argv) { struct tracecmd_input *handle; struct shark_info *info; struct stat st; GtkWidget *window; GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *vpaned; GtkWidget *hbox; GtkWidget *menu_bar; GtkWidget *menu; GtkWidget *menu_item; GtkWidget *sub_item; GtkWidget *scrollwin; GtkWidget *widget; GtkWidget *label; GtkWidget *spin; GtkWidget *check; GtkWidget *statusbar; int ret; int c; g_thread_init(NULL); gdk_threads_init(); gtk_init(&argc, &argv); while ((c = getopt(argc, argv, "hvi:")) != -1) { switch(c) { case 'h': usage(basename(argv[0])); return; case 'v': printf("%s - %s\n", basename(argv[0]), VERSION_STRING); return; case 'i': input_file = optarg; break; default: /* assume the other options are for gtk */ break; } } if ((argc - optind) >= 1) { if (input_file) usage(basename(argv[0])); input_file = argv[optind]; } /* The python plugin overrides ^C */ signal(SIGINT, sig_end); info = g_new0(typeof(*info), 1); if (!info) die("Unable to allocate info"); if (!input_file) { ret = stat(default_input_file, &st); if (ret >= 0) input_file = default_input_file; } if (input_file) handle = tracecmd_open(input_file); else handle = NULL; info->handle = handle; info->sync_task_filters = TRUE; info->sync_event_filters = TRUE; /* --- Main window --- */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); info->window = window; trace_dialog_register_window(window); if (input_file) update_title(window, input_file); /* --- Top Level Vbox --- */ vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER (window), vbox); gtk_widget_show(vbox); /* --- Menu Bar --- */ menu_bar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0); gtk_widget_show(menu_bar); /* --- File Option --- */ menu_item = gtk_menu_item_new_with_label("File"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- File - Load Option --- */ sub_item = gtk_menu_item_new_with_label("Load data"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (load_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- File - Load Filter --- */ sub_item = gtk_menu_item_new_with_label("Load filter"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); info->load_filter_menu = sub_item; /* We do need to show menu items */ gtk_widget_show(sub_item); update_load_filter(info); /* --- File - Save Filter Option --- */ sub_item = gtk_menu_item_new_with_label("Save filter"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We do need to show menu items */ gtk_widget_show(sub_item); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (save_filter_clicked), (gpointer) info); /* --- File - Export Filter Option --- */ sub_item = gtk_menu_item_new_with_label("Export filters"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (export_filters_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- File - Quit Option --- */ sub_item = gtk_menu_item_new_with_label("Quit"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (exit_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- end File options --- */ /* --- Filter Option --- */ menu_item = gtk_menu_item_new_with_label("Filter"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Filter - Sync task Option --- */ sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Task Filters"); info->task_sync_menu = sub_item; /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect (G_OBJECT (sub_item), "activate", G_CALLBACK (sync_task_filter_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Sync events Option --- */ sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Event Filters"); info->events_sync_menu = sub_item; /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect (G_OBJECT (sub_item), "activate", G_CALLBACK (sync_events_filter_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Graph Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (graph_tasks_clicked), (gpointer) info); info->graph_task_menu = sub_item; /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Graph Hide Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("hide tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (graph_hide_tasks_clicked), (gpointer) info); info->graph_hide_task_menu = sub_item; /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Events Option --- */ /* The list and graph events start off insync */ sub_item = gtk_menu_item_new_with_label("events"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (graph_events_clicked), (gpointer) info); info->graph_events_menu = sub_item; /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Graph Advanced Events Option --- */ /* The list and graph events start off in sync */ sub_item = gtk_menu_item_new_with_label("advanced events"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (adv_graph_filter_clicked), (gpointer) info); info->graph_adv_events_menu = sub_item; /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - List Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("list tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (list_tasks_clicked), (gpointer) info); info->list_task_menu = sub_item; /* Only show this item when list and graph tasks are not synced */ /* --- Filter - List Hide Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("list hide tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (list_hide_tasks_clicked), (gpointer) info); info->list_hide_task_menu = sub_item; /* Only show this item when list and graph tasks are not synced */ /* --- Filter - List Events Option --- */ sub_item = gtk_menu_item_new_with_label("list events"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (list_events_clicked), (gpointer) info); info->list_events_menu = sub_item; /* We do not show this menu (yet) */ /* --- Filter - List Advanced Events Option --- */ sub_item = gtk_menu_item_new_with_label("list advanced event"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (adv_list_filter_clicked), (gpointer) info); info->list_adv_events_menu = sub_item; /* We do not show this menu (yet) */ /* --- Filter - CPUs Option --- */ sub_item = gtk_menu_item_new_with_label("list CPUs"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (cpus_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Filter Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Plot Option --- */ menu_item = gtk_menu_item_new_with_label("Plots"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Plot - CPUs Option --- */ sub_item = gtk_menu_item_new_with_label("CPUs"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (plot_cpu_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Plot - Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("Tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (plot_tasks_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Plot Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Capture Option --- */ menu_item = gtk_menu_item_new_with_label("Capture"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Capture - Record Option --- */ sub_item = gtk_menu_item_new_with_label("Record"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (tracecmd_capture_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Capture Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Help Option --- */ menu_item = gtk_menu_item_new_with_label("Help"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Help - Contents Option --- */ sub_item = gtk_menu_item_new_with_label("Contents"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (help_content_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Help - About Option --- */ sub_item = gtk_menu_item_new_with_label("About"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (help_about_clicked), (gpointer) info); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Help Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Top Level Vpaned --- */ vpaned = gtk_vpaned_new(); gtk_box_pack_start(GTK_BOX(vbox), vpaned, TRUE, TRUE, 0); gtk_widget_show(vpaned); gtk_paned_set_position(GTK_PANED(vpaned), TRACE_HEIGHT / 2); /* --- Set up Graph --- */ info->graph_cbs.select = ks_graph_select; info->graph_cbs.filter = ks_graph_filter; info->ginfo = trace_graph_create_with_callbacks(handle, &info->graph_cbs); widget = trace_graph_get_window(info->ginfo); gtk_paned_add1(GTK_PANED(vpaned), widget); gtk_widget_show(widget); /* --- Tree View Vbox --- */ vbox2 = gtk_vbox_new(FALSE, 0); gtk_paned_add2(GTK_PANED(vpaned), vbox2); gtk_widget_show(vbox2); /* --- Paging Hbox --- */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); /* --- Page Spin Button --- */ label = gtk_label_new("Page"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); spin = gtk_spin_button_new(NULL, 1.0, 0); gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), 1, 1); gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0); gtk_widget_show(spin); info->spin = spin; /* --- Search --- */ /* The tree needs its columns loaded now */ info->treeview = gtk_tree_view_new(); trace_view_load(info->treeview, handle, spin); label = gtk_label_new(" Search: "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); trace_view_search_setup(GTK_BOX(hbox), GTK_TREE_VIEW(info->treeview)); check = gtk_check_button_new_with_label("graph follows"); gtk_box_pack_start(GTK_BOX(hbox), check, TRUE, TRUE, 0); gtk_widget_show(check); g_signal_connect_swapped (check, "toggled", G_CALLBACK (graph_check_toggle), (gpointer) info); /* --- Top Level Trace View Paging Hbox --- */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); /* --- Scroll Window --- */ scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0); gtk_widget_show(scrollwin); /* --- Set up Trace Tree --- */ g_signal_connect(info->treeview, "row-activated", (GCallback)row_double_clicked, info); g_signal_connect(info->treeview, "cursor-changed", (GCallback)cursor_changed, info); gtk_container_add(GTK_CONTAINER(scrollwin), info->treeview); gtk_signal_connect(GTK_OBJECT(info->treeview), "button_press_event", (GtkSignalFunc) button_press_event, info); gtk_widget_show(info->treeview); /* --- Set up Status Bar --- */ statusbar = trace_status_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); gtk_widget_show(statusbar); /********************************************** * Main Window **********************************************/ /* Connect to the delete_event signal and Run the application */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", (GtkSignalFunc) delete_event, (gpointer) info); gtk_widget_set_size_request(window, TRACE_WIDTH, TRACE_HEIGHT); gdk_threads_enter(); gtk_widget_show (window); gtk_main (); gdk_threads_leave(); } int main(int argc, char **argv) { kernel_shark(argc, argv); return 0; } trace-cmd-2.5.3/kernel-shark.h000066400000000000000000000044601246701203100161170ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _KERNEL_SHARK_H #define _KERNEL_SHARK_H #include "trace-graph.h" #include "trace-view.h" struct shark_info { GtkWidget *window; struct graph_info *ginfo; struct tracecmd_input *handle; GtkWidget *treeview; GtkWidget *spin; GtkWidget *load_filter_menu; GtkWidget *task_sync_menu; GtkWidget *events_sync_menu; GtkWidget *list_task_menu; GtkWidget *graph_task_menu; GtkWidget *list_hide_task_menu; GtkWidget *graph_hide_task_menu; GtkWidget *list_events_menu; GtkWidget *graph_events_menu; GtkWidget *list_adv_events_menu; GtkWidget *graph_adv_events_menu; gchar *current_filter; struct graph_callbacks graph_cbs; gint selected_task; gboolean list_filter_enabled; gboolean list_filter_available; gboolean graph_follows; gboolean sync_task_filters; gboolean sync_event_filters; struct filter_task *list_task_filter; struct filter_task *list_hide_tasks; /* Save capture state. */ gboolean cap_all_events; gchar **cap_systems; int *cap_events; gchar *cap_plugin; gchar *cap_command; gchar *cap_file; gchar *cap_settings_name; int cap_max_buf_size; gchar *cap_buffer_output; }; #define offset_of(type, field) (long)(&((type *)0)->field) #define container_of(p, type, field) (type *)((long)p - offset_of(type, field)) int kernelshark_load_file(struct shark_info *info, const char *file); void kernel_shark_clear_capture(struct shark_info *info); #endif /* _KERNEL_SHARK_H */ trace-cmd-2.5.3/list.h000066400000000000000000000044611246701203100145050ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef __LIST_H #define __LIST_H #define offset_of(type, field) (long)(&((type *)0)->field) #define container_of(p, type, field) (type *)((long)p - offset_of(type, field)) struct list_head { struct list_head *next; struct list_head *prev; }; static inline void list_head_init(struct list_head *list) { list->next = list; list->prev = list; } static inline void list_add(struct list_head *p, struct list_head *head) { struct list_head *next = head->next; p->prev = head; p->next = next; next->prev = p; head->next = p; } static inline void list_add_tail(struct list_head *p, struct list_head *head) { struct list_head *prev = head->prev; p->prev = prev; p->next = head; prev->next = p; head->prev = p; } static inline void list_del(struct list_head *p) { struct list_head *next = p->next; struct list_head *prev = p->prev; next->prev = prev; prev->next = next; } static inline int list_empty(struct list_head *list) { return list->next == list; } #define list_for_each_entry(p, list, field) \ for (p = container_of((list)->next, typeof(*p), field); \ &(p)->field != list; \ p = container_of((p)->field.next, typeof(*p), field)) #define list_for_each_entry_safe(p, n, list, field) \ for (p = container_of((list)->next, typeof(*p), field), \ n = container_of((p)->field.next, typeof(*p), field); \ &(p)->field != list; \ p = n, n = container_of((p)->field.next, typeof(*p), field)) #endif /* __LIST_H */ trace-cmd-2.5.3/parse-filter.c000066400000000000000000001471171246701203100161300ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include "event-parse.h" #include "event-utils.h" #define COMM "COMM" static struct format_field comm = { .name = "COMM", }; struct event_list { struct event_list *next; struct event_format *event; }; static void show_error(char *error_buf, const char *fmt, ...) { unsigned long long index; const char *input; va_list ap; int len; int i; input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; if (len) { strcpy(error_buf, input); error_buf[len] = '\n'; for (i = 1; i < len && i < index; i++) error_buf[len+i] = ' '; error_buf[len + i] = '^'; error_buf[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); vsnprintf(error_buf + len, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap); va_end(ap); } static void free_token(char *token) { pevent_free_token(token); } static enum event_type read_token(char **tok) { enum event_type type; char *token = NULL; do { free_token(token); type = pevent_read_token(&token); } while (type == EVENT_NEWLINE || type == EVENT_SPACE); /* If token is = or ! check to see if the next char is ~ */ if (token && (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ *tok = malloc(3); if (*tok == NULL) { free_token(token); return EVENT_ERROR; } sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ pevent_read_token(&token); free_token(token); } else *tok = token; return type; } static int filter_cmp(const void *a, const void *b) { const struct filter_type *ea = a; const struct filter_type *eb = b; if (ea->event_id < eb->event_id) return -1; if (ea->event_id > eb->event_id) return 1; return 0; } static struct filter_type * find_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; struct filter_type key; key.event_id = id; filter_type = bsearch(&key, filter->event_filters, filter->filters, sizeof(*filter->event_filters), filter_cmp); return filter_type; } static struct filter_type * add_filter_type(struct event_filter *filter, int id) { struct filter_type *filter_type; int i; filter_type = find_filter_type(filter, id); if (filter_type) return filter_type; filter_type = realloc(filter->event_filters, sizeof(*filter->event_filters) * (filter->filters + 1)); if (!filter_type) return NULL; filter->event_filters = filter_type; for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) break; } if (i < filter->filters) memmove(&filter->event_filters[i+1], &filter->event_filters[i], sizeof(*filter->event_filters) * (filter->filters - i)); filter_type = &filter->event_filters[i]; filter_type->event_id = id; filter_type->event = pevent_find_event(filter->pevent, id); filter_type->filter = NULL; filter->filters++; return filter_type; } /** * pevent_filter_alloc - create a new event filter * @pevent: The pevent that this filter is associated with */ struct event_filter *pevent_filter_alloc(struct pevent *pevent) { struct event_filter *filter; filter = malloc(sizeof(*filter)); if (filter == NULL) return NULL; memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); return filter; } static struct filter_arg *allocate_arg(void) { return calloc(1, sizeof(struct filter_arg)); } static void free_arg(struct filter_arg *arg) { if (!arg) return; switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: break; case FILTER_ARG_NUM: free_arg(arg->num.left); free_arg(arg->num.right); break; case FILTER_ARG_EXP: free_arg(arg->exp.left); free_arg(arg->exp.right); break; case FILTER_ARG_STR: free(arg->str.val); regfree(&arg->str.reg); free(arg->str.buffer); break; case FILTER_ARG_VALUE: if (arg->value.type == FILTER_STRING || arg->value.type == FILTER_CHAR) free(arg->value.str); break; case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); default: break; } free(arg); } static int add_event(struct event_list **events, struct event_format *event) { struct event_list *list; list = malloc(sizeof(*list)); if (list == NULL) return -1; list->next = *events; *events = list; list->event = event; return 0; } static int event_match(struct event_format *event, regex_t *sreg, regex_t *ereg) { if (sreg) { return !regexec(sreg, event->system, 0, NULL, 0) && !regexec(ereg, event->name, 0, NULL, 0); } return !regexec(ereg, event->system, 0, NULL, 0) || !regexec(ereg, event->name, 0, NULL, 0); } static enum pevent_errno find_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name) { struct event_format *event; regex_t ereg; regex_t sreg; int match = 0; int fail = 0; char *reg; int ret; int i; if (!event_name) { /* if no name is given, then swap sys and name */ event_name = sys_name; sys_name = NULL; } reg = malloc(strlen(event_name) + 3); if (reg == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) return PEVENT_ERRNO__INVALID_EVENT_NAME; if (sys_name) { reg = malloc(strlen(sys_name) + 3); if (reg == NULL) { regfree(&ereg); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); return PEVENT_ERRNO__INVALID_EVENT_NAME; } } for (i = 0; i < pevent->nr_events; i++) { event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; if (add_event(events, event) < 0) { fail = 1; break; } } } regfree(&ereg); if (sys_name) regfree(&sreg); if (!match) return PEVENT_ERRNO__EVENT_NOT_FOUND; if (fail) return PEVENT_ERRNO__MEM_ALLOC_FAILED; return 0; } static void free_events(struct event_list *events) { struct event_list *event; while (events) { event = events; events = events->next; free(event); } } static enum pevent_errno create_arg_item(struct event_format *event, const char *token, enum event_type type, struct filter_arg **parg, char *error_str) { struct format_field *field; struct filter_arg *arg; arg = allocate_arg(); if (arg == NULL) { show_error(error_str, "failed to allocate filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: arg->type = FILTER_ARG_VALUE; arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); if (!arg->value.str) { free_arg(arg); show_error(error_str, "failed to allocate string filter arg"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; case EVENT_ITEM: /* if it is a number, then convert it */ if (isdigit(token[0])) { arg->type = FILTER_ARG_VALUE; arg->value.type = FILTER_NUMBER; arg->value.val = strtoull(token, NULL, 0); break; } /* Consider this a field */ field = pevent_find_any_field(event, token); if (!field) { if (strcmp(token, COMM) != 0) { /* not a field, Make it false */ arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_FALSE; break; } /* If token is 'COMM' then it is special */ field = &comm; } arg->type = FILTER_ARG_FIELD; arg->field.field = field; break; default: free_arg(arg); show_error(error_str, "expected a value but found %s", token); return PEVENT_ERRNO__UNEXPECTED_TYPE; } *parg = arg; return 0; } static struct filter_arg * create_arg_op(enum filter_op_type btype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_OP; arg->op.type = btype; return arg; } static struct filter_arg * create_arg_exp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; arg->type = FILTER_ARG_EXP; arg->op.type = etype; return arg; } static struct filter_arg * create_arg_cmp(enum filter_exp_type etype) { struct filter_arg *arg; arg = allocate_arg(); if (!arg) return NULL; /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; return arg; } static enum pevent_errno add_right(struct filter_arg *op, struct filter_arg *arg, char *error_str) { struct filter_arg *left; char *str; int op_type; int ret; switch (op->type) { case FILTER_ARG_EXP: if (op->exp.right) goto out_fail; op->exp.right = arg; break; case FILTER_ARG_OP: if (op->op.right) goto out_fail; op->op.right = arg; break; case FILTER_ARG_NUM: if (op->op.right) goto out_fail; /* * The arg must be num, str, or field */ switch (arg->type) { case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: break; default: show_error(error_str, "Illegal rvalue"); return PEVENT_ERRNO__ILLEGAL_RVALUE; } /* * Depending on the type, we may need to * convert this to a string or regex. */ switch (arg->value.type) { case FILTER_CHAR: /* * A char should be converted to number if * the string is 1 byte, and the compare * is not a REGEX. */ if (strlen(arg->value.str) == 1 && op->num.type != FILTER_CMP_REGEX && op->num.type != FILTER_CMP_NOT_REGEX) { arg->value.type = FILTER_NUMBER; goto do_int; } /* fall through */ case FILTER_STRING: /* convert op to a string arg */ op_type = op->num.type; left = op->num.left; str = arg->value.str; /* reset the op for the new field */ memset(op, 0, sizeof(*op)); /* * If left arg was a field not found then * NULL the entire op. */ if (left->type == FILTER_ARG_BOOLEAN) { free_arg(left); free_arg(arg); op->type = FILTER_ARG_BOOLEAN; op->boolean.value = FILTER_FALSE; break; } /* Left arg must be a field */ if (left->type != FILTER_ARG_FIELD) { show_error(error_str, "Illegal lvalue for string comparison"); return PEVENT_ERRNO__ILLEGAL_LVALUE; } /* Make sure this is a valid string compare */ switch (op_type) { case FILTER_CMP_EQ: op_type = FILTER_CMP_MATCH; break; case FILTER_CMP_NE: op_type = FILTER_CMP_NOT_MATCH; break; case FILTER_CMP_REGEX: case FILTER_CMP_NOT_REGEX: ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB); if (ret) { show_error(error_str, "RegEx '%s' did not compute", str); return PEVENT_ERRNO__INVALID_REGEX; } break; default: show_error(error_str, "Illegal comparison for string"); return PEVENT_ERRNO__ILLEGAL_STRING_CMP; } op->type = FILTER_ARG_STR; op->str.type = op_type; op->str.field = left->field.field; op->str.val = strdup(str); if (!op->str.val) { show_error(error_str, "Failed to allocate string filter"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } /* * Need a buffer to copy data for tests */ op->str.buffer = malloc(op->str.field->size + 1); if (!op->str.buffer) { show_error(error_str, "Failed to allocate string filter"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } /* Null terminate this buffer */ op->str.buffer[op->str.field->size] = 0; /* We no longer have left or right args */ free_arg(arg); free_arg(left); break; case FILTER_NUMBER: do_int: switch (op->num.type) { case FILTER_CMP_REGEX: case FILTER_CMP_NOT_REGEX: show_error(error_str, "Op not allowed with integers"); return PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; default: break; } /* numeric compare */ op->num.right = arg; break; default: goto out_fail; } break; default: goto out_fail; } return 0; out_fail: show_error(error_str, "Syntax error"); return PEVENT_ERRNO__SYNTAX_ERROR; } static struct filter_arg * rotate_op_right(struct filter_arg *a, struct filter_arg *b) { struct filter_arg *arg; arg = a->op.right; a->op.right = b; return arg; } static enum pevent_errno add_left(struct filter_arg *op, struct filter_arg *arg) { switch (op->type) { case FILTER_ARG_EXP: if (arg->type == FILTER_ARG_OP) arg = rotate_op_right(arg, op); op->exp.left = arg; break; case FILTER_ARG_OP: op->op.left = arg; break; case FILTER_ARG_NUM: if (arg->type == FILTER_ARG_OP) arg = rotate_op_right(arg, op); /* left arg of compares must be a field */ if (arg->type != FILTER_ARG_FIELD && arg->type != FILTER_ARG_BOOLEAN) return PEVENT_ERRNO__INVALID_ARG_TYPE; op->num.left = arg; break; default: return PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } enum op_type { OP_NONE, OP_BOOL, OP_NOT, OP_EXP, OP_CMP, }; static enum op_type process_op(const char *token, enum filter_op_type *btype, enum filter_cmp_type *ctype, enum filter_exp_type *etype) { *btype = FILTER_OP_NOT; *etype = FILTER_EXP_NONE; *ctype = FILTER_CMP_NONE; if (strcmp(token, "&&") == 0) *btype = FILTER_OP_AND; else if (strcmp(token, "||") == 0) *btype = FILTER_OP_OR; else if (strcmp(token, "!") == 0) return OP_NOT; if (*btype != FILTER_OP_NOT) return OP_BOOL; /* Check for value expressions */ if (strcmp(token, "+") == 0) { *etype = FILTER_EXP_ADD; } else if (strcmp(token, "-") == 0) { *etype = FILTER_EXP_SUB; } else if (strcmp(token, "*") == 0) { *etype = FILTER_EXP_MUL; } else if (strcmp(token, "/") == 0) { *etype = FILTER_EXP_DIV; } else if (strcmp(token, "%") == 0) { *etype = FILTER_EXP_MOD; } else if (strcmp(token, ">>") == 0) { *etype = FILTER_EXP_RSHIFT; } else if (strcmp(token, "<<") == 0) { *etype = FILTER_EXP_LSHIFT; } else if (strcmp(token, "&") == 0) { *etype = FILTER_EXP_AND; } else if (strcmp(token, "|") == 0) { *etype = FILTER_EXP_OR; } else if (strcmp(token, "^") == 0) { *etype = FILTER_EXP_XOR; } else if (strcmp(token, "~") == 0) *etype = FILTER_EXP_NOT; if (*etype != FILTER_EXP_NONE) return OP_EXP; /* Check for compares */ if (strcmp(token, "==") == 0) *ctype = FILTER_CMP_EQ; else if (strcmp(token, "!=") == 0) *ctype = FILTER_CMP_NE; else if (strcmp(token, "<") == 0) *ctype = FILTER_CMP_LT; else if (strcmp(token, ">") == 0) *ctype = FILTER_CMP_GT; else if (strcmp(token, "<=") == 0) *ctype = FILTER_CMP_LE; else if (strcmp(token, ">=") == 0) *ctype = FILTER_CMP_GE; else if (strcmp(token, "=~") == 0) *ctype = FILTER_CMP_REGEX; else if (strcmp(token, "!~") == 0) *ctype = FILTER_CMP_NOT_REGEX; else return OP_NONE; return OP_CMP; } static int check_op_done(struct filter_arg *arg) { switch (arg->type) { case FILTER_ARG_EXP: return arg->exp.right != NULL; case FILTER_ARG_OP: return arg->op.right != NULL; case FILTER_ARG_NUM: return arg->num.right != NULL; case FILTER_ARG_STR: /* A string conversion is always done */ return 1; case FILTER_ARG_BOOLEAN: /* field not found, is ok */ return 1; default: return 0; } } enum filter_vals { FILTER_VAL_NORM, FILTER_VAL_FALSE, FILTER_VAL_TRUE, }; static enum pevent_errno reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, struct filter_arg *arg, char *error_str) { struct filter_arg *other_child; struct filter_arg **ptr; if (parent->type != FILTER_ARG_OP && arg->type != FILTER_ARG_OP) { show_error(error_str, "can not reparent other than OP"); return PEVENT_ERRNO__REPARENT_NOT_OP; } /* Get the sibling */ if (old_child->op.right == arg) { ptr = &old_child->op.right; other_child = old_child->op.left; } else if (old_child->op.left == arg) { ptr = &old_child->op.left; other_child = old_child->op.right; } else { show_error(error_str, "Error in reparent op, find other child"); return PEVENT_ERRNO__REPARENT_FAILED; } /* Detach arg from old_child */ *ptr = NULL; /* Check for root */ if (parent == old_child) { free_arg(other_child); *parent = *arg; /* Free arg without recussion */ free(arg); return 0; } if (parent->op.right == old_child) ptr = &parent->op.right; else if (parent->op.left == old_child) ptr = &parent->op.left; else { show_error(error_str, "Error in reparent op"); return PEVENT_ERRNO__REPARENT_FAILED; } *ptr = arg; free_arg(old_child); return 0; } /* Returns either filter_vals (success) or pevent_errno (failfure) */ static int test_arg(struct filter_arg *parent, struct filter_arg *arg, char *error_str) { int lval, rval; switch (arg->type) { /* bad case */ case FILTER_ARG_BOOLEAN: return FILTER_VAL_FALSE + arg->boolean.value; /* good cases: */ case FILTER_ARG_STR: case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: return FILTER_VAL_NORM; case FILTER_ARG_EXP: lval = test_arg(arg, arg->exp.left, error_str); if (lval != FILTER_VAL_NORM) return lval; rval = test_arg(arg, arg->exp.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_NUM: lval = test_arg(arg, arg->num.left, error_str); if (lval != FILTER_VAL_NORM) return lval; rval = test_arg(arg, arg->num.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_OP: if (arg->op.type != FILTER_OP_NOT) { lval = test_arg(arg, arg->op.left, error_str); switch (lval) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return reparent_op_arg(parent, arg, arg->op.right, error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return reparent_op_arg(parent, arg, arg->op.right, error_str); default: return lval; } } rval = test_arg(arg, arg->op.right, error_str); switch (rval) { case FILTER_VAL_NORM: default: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_FALSE; return reparent_op_arg(parent, arg, arg->op.left, error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_TRUE; return reparent_op_arg(parent, arg, arg->op.left, error_str); } return rval; default: show_error(error_str, "bad arg in filter tree"); return PEVENT_ERRNO__BAD_FILTER_ARG; } return FILTER_VAL_NORM; } /* Remove any unknown event fields */ static int collapse_tree(struct filter_arg *arg, struct filter_arg **arg_collapsed, char *error_str) { int ret; ret = test_arg(arg, arg, error_str); switch (ret) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: case FILTER_VAL_FALSE: free_arg(arg); arg = allocate_arg(); if (arg) { arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = ret == FILTER_VAL_TRUE; } else { show_error(error_str, "Failed to allocate filter arg"); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; default: /* test_arg() already set the error_str */ free_arg(arg); arg = NULL; break; } *arg_collapsed = arg; return ret; } static enum pevent_errno process_filter(struct event_format *event, struct filter_arg **parg, char *error_str, int not) { enum event_type type; char *token = NULL; struct filter_arg *current_op = NULL; struct filter_arg *current_exp = NULL; struct filter_arg *left_item = NULL; struct filter_arg *arg = NULL; enum op_type op_type; enum filter_op_type btype; enum filter_exp_type etype; enum filter_cmp_type ctype; enum pevent_errno ret; *parg = NULL; do { free(token); type = read_token(&token); switch (type) { case EVENT_SQUOTE: case EVENT_DQUOTE: case EVENT_ITEM: ret = create_arg_item(event, token, type, &arg, error_str); if (ret < 0) goto fail; if (!left_item) left_item = arg; else if (current_exp) { ret = add_right(current_exp, arg, error_str); if (ret < 0) goto fail; left_item = NULL; /* Not's only one one expression */ if (not) { arg = NULL; if (current_op) goto fail_syntax; free(token); *parg = current_exp; return 0; } } else goto fail_syntax; arg = NULL; break; case EVENT_DELIM: if (*token == ',') { show_error(error_str, "Illegal token ','"); ret = PEVENT_ERRNO__ILLEGAL_TOKEN; goto fail; } if (*token == '(') { if (left_item) { show_error(error_str, "Open paren can not come after item"); ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } if (current_exp) { show_error(error_str, "Open paren can not come after expression"); ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } ret = process_filter(event, &arg, error_str, 0); if (ret != PEVENT_ERRNO__UNBALANCED_PAREN) { if (ret == 0) { show_error(error_str, "Unbalanced number of '('"); ret = PEVENT_ERRNO__UNBALANCED_PAREN; } goto fail; } ret = 0; /* A not wants just one expression */ if (not) { if (current_op) goto fail_syntax; *parg = arg; return 0; } if (current_op) ret = add_right(current_op, arg, error_str); else current_exp = arg; if (ret < 0) goto fail; } else { /* ')' */ if (!current_op && !current_exp) goto fail_syntax; /* Make sure everything is finished at this level */ if (current_exp && !check_op_done(current_exp)) goto fail_syntax; if (current_op && !check_op_done(current_op)) goto fail_syntax; if (current_op) *parg = current_op; else *parg = current_exp; free(token); return PEVENT_ERRNO__UNBALANCED_PAREN; } break; case EVENT_OP: op_type = process_op(token, &btype, &ctype, &etype); /* All expect a left arg except for NOT */ switch (op_type) { case OP_BOOL: /* Logic ops need a left expression */ if (!current_exp && !current_op) goto fail_syntax; /* fall through */ case OP_NOT: /* logic only processes ops and exp */ if (left_item) goto fail_syntax; break; case OP_EXP: case OP_CMP: if (!left_item) goto fail_syntax; break; case OP_NONE: show_error(error_str, "Unknown op token %s", token); ret = PEVENT_ERRNO__UNKNOWN_TOKEN; goto fail; } ret = 0; switch (op_type) { case OP_BOOL: arg = create_arg_op(btype); if (arg == NULL) goto fail_alloc; if (current_op) ret = add_left(arg, current_op); else ret = add_left(arg, current_exp); current_op = arg; current_exp = NULL; break; case OP_NOT: arg = create_arg_op(btype); if (arg == NULL) goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) goto fail; current_exp = arg; ret = process_filter(event, &arg, error_str, 1); if (ret < 0) goto fail; ret = add_right(current_exp, arg, error_str); if (ret < 0) goto fail; break; case OP_EXP: case OP_CMP: if (op_type == OP_EXP) arg = create_arg_exp(etype); else arg = create_arg_cmp(ctype); if (arg == NULL) goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) goto fail; ret = add_left(arg, left_item); if (ret < 0) { arg = NULL; goto fail_syntax; } current_exp = arg; break; default: break; } arg = NULL; if (ret < 0) goto fail_syntax; break; case EVENT_NONE: break; case EVENT_ERROR: goto fail_alloc; default: goto fail_syntax; } } while (type != EVENT_NONE); if (!current_op && !current_exp) goto fail_syntax; if (!current_op) current_op = current_exp; ret = collapse_tree(current_op, parg, error_str); if (ret < 0) goto fail; *parg = current_op; free(token); return 0; fail_alloc: show_error(error_str, "failed to allocate filter arg"); ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto fail; fail_syntax: show_error(error_str, "Syntax error"); ret = PEVENT_ERRNO__SYNTAX_ERROR; fail: free_arg(current_op); free_arg(current_exp); free_arg(arg); free(token); return ret; } static enum pevent_errno process_event(struct event_format *event, const char *filter_str, struct filter_arg **parg, char *error_str) { int ret; pevent_buffer_init(filter_str, strlen(filter_str)); ret = process_filter(event, parg, error_str, 0); if (ret < 0) return ret; /* If parg is NULL, then make it into FALSE */ if (!*parg) { *parg = allocate_arg(); if (*parg == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; (*parg)->type = FILTER_ARG_BOOLEAN; (*parg)->boolean.value = FILTER_FALSE; } return 0; } static enum pevent_errno filter_event(struct event_filter *filter, struct event_format *event, const char *filter_str, char *error_str) { struct filter_type *filter_type; struct filter_arg *arg; enum pevent_errno ret; if (filter_str) { ret = process_event(event, filter_str, &arg, error_str); if (ret < 0) return ret; } else { /* just add a TRUE arg */ arg = allocate_arg(); if (arg == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_TRUE; } filter_type = add_filter_type(filter, event->id); if (filter_type == NULL) return PEVENT_ERRNO__MEM_ALLOC_FAILED; if (filter_type->filter) free_arg(filter_type->filter); filter_type->filter = arg; return 0; } static void filter_init_error_buf(struct event_filter *filter) { /* clear buffer to reset show error */ pevent_buffer_init("", 0); filter->error_buffer[0] = '\0'; } /** * pevent_filter_add_filter_str - add a new filter * @filter: the event filter to add to * @filter_str: the filter string that contains the filter * * Returns 0 if the filter was successfully added or a * negative error code. Use pevent_filter_strerror() to see * actual error message in case of error. */ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str) { struct pevent *pevent = filter->pevent; struct event_list *event; struct event_list *events = NULL; const char *filter_start; const char *next_event; char *this_event; char *event_name = NULL; char *sys_name = NULL; char *sp; enum pevent_errno rtn = 0; /* PEVENT_ERRNO__SUCCESS */ int len; int ret; filter_init_error_buf(filter); filter_start = strchr(filter_str, ':'); if (filter_start) len = filter_start - filter_str; else len = strlen(filter_str); do { next_event = strchr(filter_str, ','); if (next_event && (!filter_start || next_event < filter_start)) len = next_event - filter_str; else if (filter_start) len = filter_start - filter_str; else len = strlen(filter_str); this_event = malloc(len + 1); if (this_event == NULL) { /* This can only happen when events is NULL, but still */ free_events(events); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } memcpy(this_event, filter_str, len); this_event[len] = 0; if (next_event) next_event++; filter_str = next_event; sys_name = strtok_r(this_event, "/", &sp); event_name = strtok_r(NULL, "/", &sp); if (!sys_name) { /* This can only happen when events is NULL, but still */ free_events(events); free(this_event); return PEVENT_ERRNO__FILTER_NOT_FOUND; } /* Find this event */ ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); if (ret < 0) { free_events(events); free(this_event); return ret; } free(this_event); } while (filter_str); /* Skip the ':' */ if (filter_start) filter_start++; /* filter starts here */ for (event = events; event; event = event->next) { ret = filter_event(filter, event->event, filter_start, filter->error_buffer); /* Failures are returned if a parse error happened */ if (ret < 0) rtn = ret; if (ret >= 0 && pevent->test_filters) { char *test; test = pevent_filter_make_string(filter, event->event->id); if (test) { printf(" '%s: %s'\n", event->event->name, test); free(test); } } } free_events(events); if (rtn >= 0 && pevent->test_filters) exit(0); return rtn; } static void free_filter_type(struct filter_type *filter_type) { free_arg(filter_type->filter); } /** * pevent_filter_strerror - fill error message in a buffer * @filter: the event filter contains error * @err: the error code * @buf: the buffer to be filled in * @buflen: the size of the buffer * * Returns 0 if message was filled successfully, -1 if error */ int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, char *buf, size_t buflen) { if (err <= __PEVENT_ERRNO__START || err >= __PEVENT_ERRNO__END) return -1; if (strlen(filter->error_buffer) > 0) { size_t len = snprintf(buf, buflen, "%s", filter->error_buffer); if (len > buflen) return -1; return 0; } return pevent_strerror(filter->pevent, err, buf, buflen); } /** * pevent_filter_remove_event - remove a filter for an event * @filter: the event filter to remove from * @event_id: the event to remove a filter for * * Removes the filter saved for an event defined by @event_id * from the @filter. * * Returns 1: if an event was removed * 0: if the event was not found */ int pevent_filter_remove_event(struct event_filter *filter, int event_id) { struct filter_type *filter_type; unsigned long len; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); if (!filter_type) return 0; free_filter_type(filter_type); /* The filter_type points into the event_filters array */ len = (unsigned long)(filter->event_filters + filter->filters) - (unsigned long)(filter_type + 1); memmove(filter_type, filter_type + 1, len); filter->filters--; memset(&filter->event_filters[filter->filters], 0, sizeof(*filter_type)); return 1; } /** * pevent_filter_reset - clear all filters in a filter * @filter: the event filter to reset * * Removes all filters from a filter and resets it. */ void pevent_filter_reset(struct event_filter *filter) { int i; for (i = 0; i < filter->filters; i++) free_filter_type(&filter->event_filters[i]); free(filter->event_filters); filter->filters = 0; filter->event_filters = NULL; } void pevent_filter_free(struct event_filter *filter) { pevent_unref(filter->pevent); pevent_filter_reset(filter); free(filter); } static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg); static int copy_filter_type(struct event_filter *filter, struct event_filter *source, struct filter_type *filter_type) { struct filter_arg *arg; struct event_format *event; const char *sys; const char *name; char *str; /* Can't assume that the pevent's are the same */ sys = filter_type->event->system; name = filter_type->event->name; event = pevent_find_event_by_name(filter->pevent, sys, name); if (!event) return -1; str = arg_to_str(source, filter_type->filter); if (!str) return -1; if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { /* Add trivial event */ arg = allocate_arg(); if (arg == NULL) return -1; arg->type = FILTER_ARG_BOOLEAN; if (strcmp(str, "TRUE") == 0) arg->boolean.value = 1; else arg->boolean.value = 0; filter_type = add_filter_type(filter, event->id); if (filter_type == NULL) return -1; filter_type->filter = arg; free(str); return 0; } filter_event(filter, event, str, NULL); free(str); return 0; } /** * pevent_filter_copy - copy a filter using another filter * @dest - the filter to copy to * @source - the filter to copy from * * Returns 0 on success and -1 if not all filters were copied */ int pevent_filter_copy(struct event_filter *dest, struct event_filter *source) { int ret = 0; int i; pevent_filter_reset(dest); for (i = 0; i < source->filters; i++) { if (copy_filter_type(dest, source, &source->event_filters[i])) ret = -1; } return ret; } /** * pevent_update_trivial - update the trivial filters with the given filter * @dest - the filter to update * @source - the filter as the source of the update * @type - the type of trivial filter to update. * * Scan dest for trivial events matching @type to replace with the source. * * Returns 0 on success and -1 if there was a problem updating, but * events may have still been updated on error. */ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, enum filter_trivial_type type) { struct pevent *src_pevent; struct pevent *dest_pevent; struct event_format *event; struct filter_type *filter_type; struct filter_arg *arg; char *str; int i; src_pevent = source->pevent; dest_pevent = dest->pevent; /* Do nothing if either of the filters has nothing to filter */ if (!dest->filters || !source->filters) return 0; for (i = 0; i < dest->filters; i++) { filter_type = &dest->event_filters[i]; arg = filter_type->filter; if (arg->type != FILTER_ARG_BOOLEAN) continue; if ((arg->boolean.value && type == FILTER_TRIVIAL_FALSE) || (!arg->boolean.value && type == FILTER_TRIVIAL_TRUE)) continue; event = filter_type->event; if (src_pevent != dest_pevent) { /* do a look up */ event = pevent_find_event_by_name(src_pevent, event->system, event->name); if (!event) return -1; } str = pevent_filter_make_string(source, event->id); if (!str) continue; /* Don't bother if the filter is trivial too */ if (strcmp(str, "TRUE") != 0 && strcmp(str, "FALSE") != 0) filter_event(dest, event, str, NULL); free(str); } return 0; } /** * pevent_filter_clear_trivial - clear TRUE and FALSE filters * @filter: the filter to remove trivial filters from * @type: remove only true, false, or both * * Removes filters that only contain a TRUE or FALES boolean arg. * * Returns 0 on success and -1 if there was a problem. */ int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type) { struct filter_type *filter_type; int count = 0; int *ids = NULL; int i; if (!filter->filters) return 0; /* * Two steps, first get all ids with trivial filters. * then remove those ids. */ for (i = 0; i < filter->filters; i++) { int *new_ids; filter_type = &filter->event_filters[i]; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) continue; switch (type) { case FILTER_TRIVIAL_FALSE: if (filter_type->filter->boolean.value) continue; case FILTER_TRIVIAL_TRUE: if (!filter_type->filter->boolean.value) continue; default: break; } new_ids = realloc(ids, sizeof(*ids) * (count + 1)); if (!new_ids) { free(ids); return -1; } ids = new_ids; ids[count++] = filter_type->event_id; } if (!count) return 0; for (i = 0; i < count; i++) pevent_filter_remove_event(filter, ids[i]); free(ids); return 0; } /** * pevent_filter_event_has_trivial - return true event contains trivial filter * @filter: the filter with the information * @event_id: the id of the event to test * @type: trivial type to test for (TRUE, FALSE, EITHER) * * Returns 1 if the event contains a matching trivial type * otherwise 0. */ int pevent_filter_event_has_trivial(struct event_filter *filter, int event_id, enum filter_trivial_type type) { struct filter_type *filter_type; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); if (!filter_type) return 0; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) return 0; switch (type) { case FILTER_TRIVIAL_FALSE: return !filter_type->filter->boolean.value; case FILTER_TRIVIAL_TRUE: return filter_type->filter->boolean.value; default: return 1; } } static int test_filter(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err); static const char * get_comm(struct event_format *event, struct pevent_record *record) { const char *comm; int pid; pid = pevent_data_pid(event->pevent, record); comm = pevent_data_comm_from_pid(event->pevent, pid); return comm; } static unsigned long long get_value(struct event_format *event, struct format_field *field, struct pevent_record *record) { unsigned long long val; /* Handle our dummy "comm" field */ if (field == &comm) { const char *name; name = get_comm(event, record); return (unsigned long)name; } pevent_read_number_field(field, record->data, &val); if (!(field->flags & FIELD_IS_SIGNED)) return val; switch (field->size) { case 1: return (char)val; case 2: return (short)val; case 4: return (int)val; case 8: return (long long)val; } return val; } static unsigned long long get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err); static unsigned long long get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; lval = get_arg_value(event, arg->exp.left, record, err); rval = get_arg_value(event, arg->exp.right, record, err); if (*err) { /* * There was an error, no need to process anymore. */ return 0; } switch (arg->exp.type) { case FILTER_EXP_ADD: return lval + rval; case FILTER_EXP_SUB: return lval - rval; case FILTER_EXP_MUL: return lval * rval; case FILTER_EXP_DIV: return lval / rval; case FILTER_EXP_MOD: return lval % rval; case FILTER_EXP_RSHIFT: return lval >> rval; case FILTER_EXP_LSHIFT: return lval << rval; case FILTER_EXP_AND: return lval & rval; case FILTER_EXP_OR: return lval | rval; case FILTER_EXP_XOR: return lval ^ rval; case FILTER_EXP_NOT: default: if (!*err) *err = PEVENT_ERRNO__INVALID_EXP_TYPE; } return 0; } static unsigned long long get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { switch (arg->type) { case FILTER_ARG_FIELD: return get_value(event, arg->field.field, record); case FILTER_ARG_VALUE: if (arg->value.type != FILTER_NUMBER) { if (!*err) *err = PEVENT_ERRNO__NOT_A_NUMBER; } return arg->value.val; case FILTER_ARG_EXP: return get_exp_value(event, arg, record, err); default: if (!*err) *err = PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } static int test_num(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; lval = get_arg_value(event, arg->num.left, record, err); rval = get_arg_value(event, arg->num.right, record, err); if (*err) { /* * There was an error, no need to process anymore. */ return 0; } switch (arg->num.type) { case FILTER_CMP_EQ: return lval == rval; case FILTER_CMP_NE: return lval != rval; case FILTER_CMP_GT: return lval > rval; case FILTER_CMP_LT: return lval < rval; case FILTER_CMP_GE: return lval >= rval; case FILTER_CMP_LE: return lval <= rval; default: if (!*err) *err = PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; return 0; } } static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) { struct event_format *event; struct pevent *pevent; unsigned long long addr; const char *val = NULL; char hex[64]; /* If the field is not a string convert it */ if (arg->str.field->flags & FIELD_IS_STRING) { val = record->data + arg->str.field->offset; /* * We need to copy the data since we can't be sure the field * is null terminated. */ if (*(val + arg->str.field->size - 1)) { /* copy it */ memcpy(arg->str.buffer, val, arg->str.field->size); /* the buffer is already NULL terminated */ val = arg->str.buffer; } } else { event = arg->str.field->event; pevent = event->pevent; addr = get_value(event, arg->str.field, record); if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG)) /* convert to a kernel symbol */ val = pevent_find_function(pevent, addr); if (val == NULL) { /* just use the hex of the string name */ snprintf(hex, 64, "0x%llx", addr); val = hex; } } return val; } static int test_str(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { const char *val; if (arg->str.field == &comm) val = get_comm(event, record); else val = get_field_str(arg, record); switch (arg->str.type) { case FILTER_CMP_MATCH: return strcmp(val, arg->str.val) == 0; case FILTER_CMP_NOT_MATCH: return strcmp(val, arg->str.val) != 0; case FILTER_CMP_REGEX: /* Returns zero on match */ return !regexec(&arg->str.reg, val, 0, NULL, 0); case FILTER_CMP_NOT_REGEX: return regexec(&arg->str.reg, val, 0, NULL, 0); default: if (!*err) *err = PEVENT_ERRNO__ILLEGAL_STRING_CMP; return 0; } } static int test_op(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { switch (arg->op.type) { case FILTER_OP_AND: return test_filter(event, arg->op.left, record, err) && test_filter(event, arg->op.right, record, err); case FILTER_OP_OR: return test_filter(event, arg->op.left, record, err) || test_filter(event, arg->op.right, record, err); case FILTER_OP_NOT: return !test_filter(event, arg->op.right, record, err); default: if (!*err) *err = PEVENT_ERRNO__INVALID_OP_TYPE; return 0; } } static int test_filter(struct event_format *event, struct filter_arg *arg, struct pevent_record *record, enum pevent_errno *err) { if (*err) { /* * There was an error, no need to process anymore. */ return 0; } switch (arg->type) { case FILTER_ARG_BOOLEAN: /* easy case */ return arg->boolean.value; case FILTER_ARG_OP: return test_op(event, arg, record, err); case FILTER_ARG_NUM: return test_num(event, arg, record, err); case FILTER_ARG_STR: return test_str(event, arg, record, err); case FILTER_ARG_EXP: case FILTER_ARG_VALUE: case FILTER_ARG_FIELD: /* * Expressions, fields and values evaluate * to true if they return non zero */ return !!get_arg_value(event, arg, record, err); default: if (!*err) *err = PEVENT_ERRNO__INVALID_ARG_TYPE; return 0; } } /** * pevent_event_filtered - return true if event has filter * @filter: filter struct with filter information * @event_id: event id to test if filter exists * * Returns 1 if filter found for @event_id * otherwise 0; */ int pevent_event_filtered(struct event_filter *filter, int event_id) { struct filter_type *filter_type; if (!filter->filters) return 0; filter_type = find_filter_type(filter, event_id); return filter_type ? 1 : 0; } /** * pevent_filter_match - test if a record matches a filter * @filter: filter struct with filter information * @record: the record to test against the filter * * Returns: match result or error code (prefixed with PEVENT_ERRNO__) * FILTER_MATCH - filter found for event and @record matches * FILTER_MISS - filter found for event and @record does not match * FILTER_NOT_FOUND - no filter found for @record's event * NO_FILTER - if no filters exist * otherwise - error occurred during test */ enum pevent_errno pevent_filter_match(struct event_filter *filter, struct pevent_record *record) { struct pevent *pevent = filter->pevent; struct filter_type *filter_type; int event_id; int ret; enum pevent_errno err = 0; filter_init_error_buf(filter); if (!filter->filters) return PEVENT_ERRNO__NO_FILTER; event_id = pevent_data_type(pevent, record); filter_type = find_filter_type(filter, event_id); if (!filter_type) return PEVENT_ERRNO__FILTER_NOT_FOUND; ret = test_filter(filter_type->event, filter_type->filter, record, &err); if (err) return err; return ret ? PEVENT_ERRNO__FILTER_MATCH : PEVENT_ERRNO__FILTER_MISS; } static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *left = NULL; char *right = NULL; char *op = NULL; int left_val = -1; int right_val = -1; int val; switch (arg->op.type) { case FILTER_OP_AND: op = "&&"; /* fall through */ case FILTER_OP_OR: if (!op) op = "||"; left = arg_to_str(filter, arg->op.left); right = arg_to_str(filter, arg->op.right); if (!left || !right) break; /* Try to consolidate boolean values */ if (strcmp(left, "TRUE") == 0) left_val = 1; else if (strcmp(left, "FALSE") == 0) left_val = 0; if (strcmp(right, "TRUE") == 0) right_val = 1; else if (strcmp(right, "FALSE") == 0) right_val = 0; if (left_val >= 0) { if ((arg->op.type == FILTER_OP_AND && !left_val) || (arg->op.type == FILTER_OP_OR && left_val)) { /* Just return left value */ str = left; left = NULL; break; } if (right_val >= 0) { /* just evaluate this. */ val = 0; switch (arg->op.type) { case FILTER_OP_AND: val = left_val && right_val; break; case FILTER_OP_OR: val = left_val || right_val; break; default: break; } asprintf(&str, val ? "TRUE" : "FALSE"); break; } } if (right_val >= 0) { if ((arg->op.type == FILTER_OP_AND && !right_val) || (arg->op.type == FILTER_OP_OR && right_val)) { /* Just return right value */ str = right; right = NULL; break; } /* The right value is meaningless */ str = left; left = NULL; break; } asprintf(&str, "(%s) %s (%s)", left, op, right); break; case FILTER_OP_NOT: op = "!"; right = arg_to_str(filter, arg->op.right); if (!right) break; /* See if we can consolidate */ if (strcmp(right, "TRUE") == 0) right_val = 1; else if (strcmp(right, "FALSE") == 0) right_val = 0; if (right_val >= 0) { /* just return the opposite */ asprintf(&str, right_val ? "FALSE" : "TRUE"); break; } asprintf(&str, "%s(%s)", op, right); break; default: /* ?? */ break; } free(left); free(right); return str; } static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; asprintf(&str, "%lld", arg->value.val); return str; } static char *field_to_str(struct event_filter *filter, struct filter_arg *arg) { return strdup(arg->field.field->name); } static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) { char *lstr; char *rstr; char *op; char *str = NULL; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); if (!lstr || !rstr) goto out; switch (arg->exp.type) { case FILTER_EXP_ADD: op = "+"; break; case FILTER_EXP_SUB: op = "-"; break; case FILTER_EXP_MUL: op = "*"; break; case FILTER_EXP_DIV: op = "/"; break; case FILTER_EXP_MOD: op = "%"; break; case FILTER_EXP_RSHIFT: op = ">>"; break; case FILTER_EXP_LSHIFT: op = "<<"; break; case FILTER_EXP_AND: op = "&"; break; case FILTER_EXP_OR: op = "|"; break; case FILTER_EXP_XOR: op = "^"; break; default: op = "[ERROR IN EXPRESSION TYPE]"; break; } asprintf(&str, "%s %s %s", lstr, op, rstr); out: free(lstr); free(rstr); return str; } static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) { char *lstr; char *rstr; char *str = NULL; char *op = NULL; lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); if (!lstr || !rstr) goto out; switch (arg->num.type) { case FILTER_CMP_EQ: op = "=="; /* fall through */ case FILTER_CMP_NE: if (!op) op = "!="; /* fall through */ case FILTER_CMP_GT: if (!op) op = ">"; /* fall through */ case FILTER_CMP_LT: if (!op) op = "<"; /* fall through */ case FILTER_CMP_GE: if (!op) op = ">="; /* fall through */ case FILTER_CMP_LE: if (!op) op = "<="; asprintf(&str, "%s %s %s", lstr, op, rstr); break; default: /* ?? */ break; } out: free(lstr); free(rstr); return str; } static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *op = NULL; switch (arg->str.type) { case FILTER_CMP_MATCH: op = "=="; /* fall through */ case FILTER_CMP_NOT_MATCH: if (!op) op = "!="; /* fall through */ case FILTER_CMP_REGEX: if (!op) op = "=~"; /* fall through */ case FILTER_CMP_NOT_REGEX: if (!op) op = "!~"; asprintf(&str, "%s %s \"%s\"", arg->str.field->name, op, arg->str.val); break; default: /* ?? */ break; } return str; } static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; switch (arg->type) { case FILTER_ARG_BOOLEAN: asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE"); return str; case FILTER_ARG_OP: return op_to_str(filter, arg); case FILTER_ARG_NUM: return num_to_str(filter, arg); case FILTER_ARG_STR: return str_to_str(filter, arg); case FILTER_ARG_VALUE: return val_to_str(filter, arg); case FILTER_ARG_FIELD: return field_to_str(filter, arg); case FILTER_ARG_EXP: return exp_to_str(filter, arg); default: /* ?? */ return NULL; } } /** * pevent_filter_make_string - return a string showing the filter * @filter: filter struct with filter information * @event_id: the event id to return the filter string with * * Returns a string that displays the filter contents. * This string must be freed with free(str). * NULL is returned if no filter is found or allocation failed. */ char * pevent_filter_make_string(struct event_filter *filter, int event_id) { struct filter_type *filter_type; if (!filter->filters) return NULL; filter_type = find_filter_type(filter, event_id); if (!filter_type) return NULL; return arg_to_str(filter, filter_type->filter); } /** * pevent_filter_compare - compare two filters and return if they are the same * @filter1: Filter to compare with @filter2 * @filter2: Filter to compare with @filter1 * * Returns: * 1 if the two filters hold the same content. * 0 if they do not. */ int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2) { struct filter_type *filter_type1; struct filter_type *filter_type2; char *str1, *str2; int result; int i; /* Do the easy checks first */ if (filter1->filters != filter2->filters) return 0; if (!filter1->filters && !filter2->filters) return 1; /* * Now take a look at each of the events to see if they have the same * filters to them. */ for (i = 0; i < filter1->filters; i++) { filter_type1 = &filter1->event_filters[i]; filter_type2 = find_filter_type(filter2, filter_type1->event_id); if (!filter_type2) break; if (filter_type1->filter->type != filter_type2->filter->type) break; switch (filter_type1->filter->type) { case FILTER_TRIVIAL_FALSE: case FILTER_TRIVIAL_TRUE: /* trivial types just need the type compared */ continue; default: break; } /* The best way to compare complex filters is with strings */ str1 = arg_to_str(filter1, filter_type1->filter); str2 = arg_to_str(filter2, filter_type2->filter); if (str1 && str2) result = strcmp(str1, str2) != 0; else /* bail out if allocation fails */ result = 1; free(str1); free(str2); if (result) break; } if (i < filter1->filters) return 0; return 1; } trace-cmd-2.5.3/parse-utils.c000066400000000000000000000027031246701203100157720ustar00rootroot00000000000000#include #include #include #include #include #define __weak __attribute__((weak)) void __vdie(const char *fmt, va_list ap) { int ret = errno; if (errno) perror("trace-cmd"); else ret = -1; fprintf(stderr, " "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); exit(ret); } void __die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vdie(fmt, ap); va_end(ap); } void __weak die(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vdie(fmt, ap); va_end(ap); } void __vwarning(const char *fmt, va_list ap) { if (errno) perror("trace-cmd"); errno = 0; fprintf(stderr, " "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } void __warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vwarning(fmt, ap); va_end(ap); } void __weak warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vwarning(fmt, ap); va_end(ap); } void __vpr_stat(const char *fmt, va_list ap) { vprintf(fmt, ap); printf("\n"); } void __pr_stat(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vpr_stat(fmt, ap); va_end(ap); } void __weak vpr_stat(const char *fmt, va_list ap) { __vpr_stat(fmt, ap); } void __weak pr_stat(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __vpr_stat(fmt, ap); va_end(ap); } void __weak *malloc_or_die(unsigned int size) { void *data; data = malloc(size); if (!data) die("malloc"); return data; } trace-cmd-2.5.3/plugin_blk.c000066400000000000000000000232261246701203100156530ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "trace-cmd.h" #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) struct blk_data { unsigned long long sector; struct event_format *event; unsigned int action; unsigned int pid; unsigned int device; unsigned int bytes; unsigned int error; void *pdu_data; unsigned short pdu_len; }; static void fill_rwbs(char *rwbs, int action, unsigned int bytes) { int i = 0; int tc = action >> BLK_TC_SHIFT; if (action == BLK_TN_MESSAGE) { rwbs[i++] = 'N'; goto out; } #if defined(HAVE_BLK_TC_FLUSH) if (tc & BLK_TC_FLUSH) rwbs[i++] = 'F'; #endif if (tc & BLK_TC_DISCARD) rwbs[i++] = 'D'; else if (tc & BLK_TC_WRITE) rwbs[i++] = 'W'; else if (bytes) rwbs[i++] = 'R'; else rwbs[i++] = 'N'; #if defined(HAVE_BLK_TC_FLUSH) if (tc & BLK_TC_FUA) rwbs[i++] = 'F'; #endif if (tc & BLK_TC_AHEAD) rwbs[i++] = 'A'; #if !defined(HAVE_BLK_TC_FLUSH) if (tc & BLK_TC_BARRIER) rwbs[i++] = 'B'; #endif if (tc & BLK_TC_SYNC) rwbs[i++] = 'S'; if (tc & BLK_TC_META) rwbs[i++] = 'M'; out: rwbs[i] = '\0'; } static int log_action(struct trace_seq *s, struct blk_data *data, const char *act) { char rwbs[6]; fill_rwbs(rwbs, data->action, data->bytes); return trace_seq_printf(s, "%3d,%-3d %2s %3s ", MAJOR(data->device), MINOR(data->device), act, rwbs); } static void blk_log_msg(struct trace_seq *s, void *data, int len) { trace_seq_printf(s, "%.*s", len, (char *)data); } static int blk_log_dump_pdu(struct trace_seq *s, const unsigned char *pdu_buf, int pdu_len) { int i, end, ret; if (!pdu_len) return 1; /* find the last zero that needs to be printed */ for (end = pdu_len - 1; end >= 0; end--) if (pdu_buf[end]) break; end++; if (!trace_seq_putc(s, '(')) return 0; for (i = 0; i < pdu_len; i++) { ret = trace_seq_printf(s, "%s%02x", i == 0 ? "" : " ", pdu_buf[i]); if (!ret) return ret; /* * stop when the rest is just zeroes and indicate so * with a ".." appended */ if (i == end && end != pdu_len - 1) return trace_seq_puts(s, " ..) "); } return trace_seq_puts(s, ") "); } static unsigned int t_sec(int bytes) { return bytes >> 9; } static unsigned int be32_to_cpu(unsigned int val) { unsigned int swap; if (tracecmd_host_bigendian()) return val; swap = ((val & 0xffULL) << 24) | ((val & (0xffULL << 8)) << 8) | ((val & (0xffULL << 16)) >> 8) | ((val & (0xffULL << 24)) >> 24); return swap; } static unsigned long long be64_to_cpu(unsigned long long val) { unsigned long long swap; if (tracecmd_host_bigendian()) return val; swap = ((val & 0xffULL) << 56) | ((val & (0xffULL << 8)) << 40) | ((val & (0xffULL << 16)) << 24) | ((val & (0xffULL << 24)) << 8) | ((val & (0xffULL << 32)) >> 8) | ((val & (0xffULL << 40)) >> 24) | ((val & (0xffULL << 48)) >> 40) | ((val & (0xffULL << 56)) >> 56); return swap; } static unsigned long long get_pdu_int(void *data) { const unsigned long long *val = data; return be64_to_cpu(*val); } static void get_pdu_remap(void *pdu_data, struct blk_io_trace_remap *r) { const struct blk_io_trace_remap *__r = pdu_data; unsigned long long sector_from = __r->sector_from; r->device_from = be32_to_cpu(__r->device_from); r->device_to = be32_to_cpu(__r->device_to); r->sector_from = be64_to_cpu(sector_from); } static int blk_log_remap(struct trace_seq *s, struct blk_data *data) { struct blk_io_trace_remap r = { .device_from = 0, }; get_pdu_remap(data->pdu_data, &r); return trace_seq_printf(s, "%llu + %u <- (%d,%d) %llu\n", data->sector, t_sec(data->bytes), MAJOR(r.device_from), MINOR(r.device_from), (unsigned long long)r.sector_from); } static int blk_log_split(struct trace_seq *s, struct blk_data *data) { const char *cmd; cmd = pevent_data_comm_from_pid(data->event->pevent, data->pid); return trace_seq_printf(s, "%llu / %llu [%s]\n", data->sector, get_pdu_int(data->pdu_data), cmd); } static int blk_log_plug(struct trace_seq *s, struct blk_data *data) { const char *cmd; cmd = pevent_data_comm_from_pid(data->event->pevent, data->pid); return trace_seq_printf(s, "[%s]\n", cmd); } static int blk_log_unplug(struct trace_seq *s, struct blk_data *data) { const char *cmd; cmd = pevent_data_comm_from_pid(data->event->pevent, data->pid); return trace_seq_printf(s, "[%s] %llu\n", cmd, get_pdu_int(data->pdu_data)); } static int blk_log_with_error(struct trace_seq *s, struct blk_data *data) { if (data->action & BLK_TC_ACT(BLK_TC_PC)) { blk_log_dump_pdu(s, data->pdu_data, data->pdu_len); trace_seq_printf(s, "[%d]\n", data->error); return 0; } else { if (t_sec(data->bytes)) return trace_seq_printf(s, "%llu + %u [%d]\n", data->sector, t_sec(data->bytes), data->error); return trace_seq_printf(s, "%llu [%d]\n", data->sector, data->error); } } static int blk_log_generic(struct trace_seq *s, struct blk_data *data) { const char *cmd; cmd = pevent_data_comm_from_pid(data->event->pevent, data->pid); if (data->action & BLK_TC_ACT(BLK_TC_PC)) { int ret; ret = trace_seq_printf(s, "%u ", data->bytes); if (!ret) return 0; ret = blk_log_dump_pdu(s, data->pdu_data, data->pdu_len); if (!ret) return 0; return trace_seq_printf(s, "[%s]\n", cmd); } else { if (t_sec(data->bytes)) return trace_seq_printf(s, "%llu + %u [%s]\n", data->sector, t_sec(data->bytes), cmd); return trace_seq_printf(s, "[%s]\n", cmd); } } static const struct { const char *act[2]; int (*print)(struct trace_seq *s, struct blk_data *data); } what2act[] = { [__BLK_TA_QUEUE] = {{ "Q", "queue" }, blk_log_generic }, [__BLK_TA_BACKMERGE] = {{ "M", "backmerge" }, blk_log_generic }, [__BLK_TA_FRONTMERGE] = {{ "F", "frontmerge" }, blk_log_generic }, [__BLK_TA_GETRQ] = {{ "G", "getrq" }, blk_log_generic }, [__BLK_TA_SLEEPRQ] = {{ "S", "sleeprq" }, blk_log_generic }, [__BLK_TA_REQUEUE] = {{ "R", "requeue" }, blk_log_with_error }, [__BLK_TA_ISSUE] = {{ "D", "issue" }, blk_log_generic }, [__BLK_TA_COMPLETE] = {{ "C", "complete" }, blk_log_with_error }, [__BLK_TA_PLUG] = {{ "P", "plug" }, blk_log_plug }, [__BLK_TA_UNPLUG_IO] = {{ "U", "unplug_io" }, blk_log_unplug }, [__BLK_TA_UNPLUG_TIMER] = {{ "UT", "unplug_timer" }, blk_log_unplug }, [__BLK_TA_INSERT] = {{ "I", "insert" }, blk_log_generic }, [__BLK_TA_SPLIT] = {{ "X", "split" }, blk_log_split }, [__BLK_TA_BOUNCE] = {{ "B", "bounce" }, blk_log_generic }, [__BLK_TA_REMAP] = {{ "A", "remap" }, blk_log_remap }, }; static int blktrace_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { struct format_field *field; unsigned long long val; void *data = record->data; struct blk_data blk_data; unsigned short what; int long_act = 0; field = pevent_find_field(event, "action"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.action = val; field = pevent_find_field(event, "bytes"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.bytes = val; field = pevent_find_field(event, "device"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.device = val; field = pevent_find_field(event, "pdu_len"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.pdu_len = val; field = pevent_find_field(event, "data"); if (!field) return 1; blk_data.pdu_data = data + field->offset; field = pevent_find_field(event, "sector"); if (!field) return 1; if (pevent_read_number_field(field, data, &blk_data.sector)) return 1; field = pevent_find_field(event, "pid"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.pid = val; field = pevent_find_field(event, "error"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; blk_data.error = val; blk_data.event = event; what = blk_data.action & ((1 << BLK_TC_SHIFT) - 1); if (blk_data.action == BLK_TN_MESSAGE) { log_action(s, &blk_data, "m"); blk_log_msg(s, blk_data.pdu_data, blk_data.pdu_len); goto out; } if (what == 0 || what >= ARRAY_SIZE(what2act)) trace_seq_printf(s, "Unknown action %x\n", what); else { log_action(s, &blk_data, what2act[what].act[long_act]); what2act[what].print(s, &blk_data); } out: return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "ftrace", "blktrace", blktrace_handler, NULL); return 0; } trace-cmd-2.5.3/plugin_cfg80211.c000066400000000000000000000013311246701203100162270ustar00rootroot00000000000000#include #include #include #include #include "event-parse.h" static unsigned long long process___le16_to_cpup(struct trace_seq *s, unsigned long long *args) { uint16_t *val = (uint16_t *) (unsigned long) args[0]; return val ? (long long) le16toh(*val) : 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_print_function(pevent, process___le16_to_cpup, PEVENT_FUNC_ARG_INT, "__le16_to_cpup", PEVENT_FUNC_ARG_PTR, PEVENT_FUNC_ARG_VOID); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_print_function(pevent, process___le16_to_cpup, "__le16_to_cpup"); } trace-cmd-2.5.3/plugin_function.c000066400000000000000000000105031246701203100167220ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "trace-cmd.h" static struct func_stack { int size; char **stack; } *fstack; static int cpus = -1; #define STK_BLK 10 struct plugin_option plugin_options[] = { { .name = "parent", .plugin_alias = "ftrace", .description = "Print parent of functions for function events", }, { .name = "indent", .plugin_alias = "ftrace", .description = "Try to show function call indents, based on parents", .set = 1, }, { .name = NULL, } }; static struct plugin_option *ftrace_parent = &plugin_options[0]; static struct plugin_option *ftrace_indent = &plugin_options[1]; static void add_child(struct func_stack *stack, const char *child, int pos) { int i; if (!child) return; if (pos < stack->size) free(stack->stack[pos]); else { char **ptr; ptr = realloc(stack->stack, sizeof(char *) * (stack->size + STK_BLK)); if (!ptr) { warning("could not allocate plugin memory\n"); return; } stack->stack = ptr; for (i = stack->size; i < stack->size + STK_BLK; i++) stack->stack[i] = NULL; stack->size += STK_BLK; } stack->stack[pos] = strdup(child); } static int add_and_get_index(const char *parent, const char *child, int cpu) { int i; if (cpu < 0) return 0; if (cpu > cpus) { struct func_stack *ptr; ptr = realloc(fstack, sizeof(*fstack) * (cpu + 1)); if (!ptr) { warning("could not allocate plugin memory\n"); return 0; } fstack = ptr; /* Account for holes in the cpu count */ for (i = cpus + 1; i <= cpu; i++) memset(&fstack[i], 0, sizeof(fstack[i])); cpus = cpu; } for (i = 0; i < fstack[cpu].size && fstack[cpu].stack[i]; i++) { if (strcmp(parent, fstack[cpu].stack[i]) == 0) { add_child(&fstack[cpu], child, i+1); return i; } } /* Not found */ add_child(&fstack[cpu], parent, 0); add_child(&fstack[cpu], child, 1); return 0; } static int function_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { struct pevent *pevent = event->pevent; unsigned long long function; unsigned long long pfunction; const char *func; const char *parent; int index; if (pevent_get_field_val(s, event, "ip", record, &function, 1)) return trace_seq_putc(s, '!'); func = pevent_find_function(pevent, function); if (pevent_get_field_val(s, event, "parent_ip", record, &pfunction, 1)) return trace_seq_putc(s, '!'); parent = pevent_find_function(pevent, pfunction); if (parent && ftrace_indent->set) index = add_and_get_index(parent, func, record->cpu); trace_seq_printf(s, "%*s", index*3, ""); if (func) trace_seq_printf(s, "%s", func); else trace_seq_printf(s, "0x%llx", function); if (ftrace_parent->set) { trace_seq_printf(s, " <-- "); if (parent) trace_seq_printf(s, "%s", parent); else trace_seq_printf(s, "0x%llx", pfunction); } return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "ftrace", "function", function_handler, NULL); trace_util_add_options("ftrace", plugin_options); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { int i, x; pevent_unregister_event_handler(pevent, -1, "ftrace", "function", function_handler, NULL); for (i = 0; i <= cpus; i++) { for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++) free(fstack[i].stack[x]); free(fstack[i].stack); } trace_util_remove_options(plugin_options); free(fstack); fstack = NULL; cpus = -1; } trace-cmd-2.5.3/plugin_hrtimer.c000066400000000000000000000052201246701203100165470ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * Copyright (C) 2009 Johannes Berg * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "event-parse.h" static int timer_expire_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { trace_seq_printf(s, "hrtimer="); if (pevent_print_num_field(s, "0x%llx", event, "timer", record, 0) == -1) pevent_print_num_field(s, "0x%llx", event, "hrtimer", record, 1); trace_seq_printf(s, " now="); pevent_print_num_field(s, "%llu", event, "now", record, 1); pevent_print_func_field(s, " function=%s", event, "function", record, 0); return 0; } static int timer_start_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { trace_seq_printf(s, "hrtimer="); if (pevent_print_num_field(s, "0x%llx", event, "timer", record, 0) == -1) pevent_print_num_field(s, "0x%llx", event, "hrtimer", record, 1); pevent_print_func_field(s, " function=%s", event, "function", record, 0); trace_seq_printf(s, " expires="); pevent_print_num_field(s, "%llu", event, "expires", record, 1); trace_seq_printf(s, " softexpires="); pevent_print_num_field(s, "%llu", event, "softexpires", record, 1); return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "timer", "hrtimer_expire_entry", timer_expire_handler, NULL); pevent_register_event_handler(pevent, -1, "timer", "hrtimer_start", timer_start_handler, NULL); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_expire_entry", timer_expire_handler, NULL); pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_start", timer_start_handler, NULL); } trace-cmd-2.5.3/plugin_jbd2.c000066400000000000000000000043401246701203100157200ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "event-parse.h" #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) static unsigned long long process_jbd2_dev_to_name(struct trace_seq *s, unsigned long long *args) { unsigned int dev = args[0]; trace_seq_printf(s, "%d:%d", MAJOR(dev), MINOR(dev)); return 0; } static unsigned long long process_jiffies_to_msecs(struct trace_seq *s, unsigned long long *args) { unsigned long long jiffies = args[0]; trace_seq_printf(s, "%lld", jiffies); return jiffies; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_print_function(pevent, process_jbd2_dev_to_name, PEVENT_FUNC_ARG_STRING, "jbd2_dev_to_name", PEVENT_FUNC_ARG_INT, PEVENT_FUNC_ARG_VOID); pevent_register_print_function(pevent, process_jiffies_to_msecs, PEVENT_FUNC_ARG_LONG, "jiffies_to_msecs", PEVENT_FUNC_ARG_LONG, PEVENT_FUNC_ARG_VOID); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_print_function(pevent, process_jbd2_dev_to_name, "jbd2_dev_to_name"); pevent_unregister_print_function(pevent, process_jiffies_to_msecs, "jiffies_to_msecs"); } trace-cmd-2.5.3/plugin_kmem.c000066400000000000000000000055321246701203100160340ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "trace-cmd.h" static int call_site_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { struct format_field *field; unsigned long long val, addr; void *data = record->data; const char *func; field = pevent_find_field(event, "call_site"); if (!field) return 1; if (pevent_read_number_field(field, data, &val)) return 1; func = pevent_find_function(event->pevent, val); if (!func) return 1; addr = pevent_find_function_address(event->pevent, val); trace_seq_printf(s, "(%s+0x%x) ", func, (int)(val - addr)); return 1; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "kmem", "kfree", call_site_handler, NULL); pevent_register_event_handler(pevent, -1, "kmem", "kmalloc", call_site_handler, NULL); pevent_register_event_handler(pevent, -1, "kmem", "kmalloc_node", call_site_handler, NULL); pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", call_site_handler, NULL); pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_alloc_node", call_site_handler, NULL); pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_free", call_site_handler, NULL); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_event_handler(pevent, -1, "kmem", "kfree", call_site_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc", call_site_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc_node", call_site_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", call_site_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc_node", call_site_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_free", call_site_handler, NULL); } trace-cmd-2.5.3/plugin_kvm.c000066400000000000000000000325371246701203100157050ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "event-parse.h" #ifdef HAVE_UDIS86 #include static ud_t ud; static void init_disassembler(void) { ud_init(&ud); ud_set_syntax(&ud, UD_SYN_ATT); } static const char *disassemble(unsigned char *insn, int len, uint64_t rip, int cr0_pe, int eflags_vm, int cs_d, int cs_l) { int mode; if (!cr0_pe) mode = 16; else if (eflags_vm) mode = 16; else if (cs_l) mode = 64; else if (cs_d) mode = 32; else mode = 16; ud_set_pc(&ud, rip); ud_set_mode(&ud, mode); ud_set_input_buffer(&ud, insn, len); ud_disassemble(&ud); return ud_insn_asm(&ud); } #else static void init_disassembler(void) { } static const char *disassemble(unsigned char *insn, int len, uint64_t rip, int cr0_pe, int eflags_vm, int cs_d, int cs_l) { static char out[15*3+1]; int i; for (i = 0; i < len; ++i) sprintf(out + i * 3, "%02x ", insn[i]); out[len*3-1] = '\0'; return out; } #endif #define VMX_EXIT_REASONS \ _ER(EXCEPTION_NMI, 0) \ _ER(EXTERNAL_INTERRUPT, 1) \ _ER(TRIPLE_FAULT, 2) \ _ER(PENDING_INTERRUPT, 7) \ _ER(NMI_WINDOW, 8) \ _ER(TASK_SWITCH, 9) \ _ER(CPUID, 10) \ _ER(HLT, 12) \ _ER(INVD, 13) \ _ER(INVLPG, 14) \ _ER(RDPMC, 15) \ _ER(RDTSC, 16) \ _ER(VMCALL, 18) \ _ER(VMCLEAR, 19) \ _ER(VMLAUNCH, 20) \ _ER(VMPTRLD, 21) \ _ER(VMPTRST, 22) \ _ER(VMREAD, 23) \ _ER(VMRESUME, 24) \ _ER(VMWRITE, 25) \ _ER(VMOFF, 26) \ _ER(VMON, 27) \ _ER(CR_ACCESS, 28) \ _ER(DR_ACCESS, 29) \ _ER(IO_INSTRUCTION, 30) \ _ER(MSR_READ, 31) \ _ER(MSR_WRITE, 32) \ _ER(MWAIT_INSTRUCTION, 36) \ _ER(MONITOR_INSTRUCTION,39) \ _ER(PAUSE_INSTRUCTION, 40) \ _ER(MCE_DURING_VMENTRY, 41) \ _ER(TPR_BELOW_THRESHOLD,43) \ _ER(APIC_ACCESS, 44) \ _ER(EOI_INDUCED, 45) \ _ER(EPT_VIOLATION, 48) \ _ER(EPT_MISCONFIG, 49) \ _ER(INVEPT, 50) \ _ER(PREEMPTION_TIMER, 52) \ _ER(WBINVD, 54) \ _ER(XSETBV, 55) \ _ER(APIC_WRITE, 56) \ _ER(INVPCID, 58) #define SVM_EXIT_REASONS \ _ER(EXIT_READ_CR0, 0x000) \ _ER(EXIT_READ_CR3, 0x003) \ _ER(EXIT_READ_CR4, 0x004) \ _ER(EXIT_READ_CR8, 0x008) \ _ER(EXIT_WRITE_CR0, 0x010) \ _ER(EXIT_WRITE_CR3, 0x013) \ _ER(EXIT_WRITE_CR4, 0x014) \ _ER(EXIT_WRITE_CR8, 0x018) \ _ER(EXIT_READ_DR0, 0x020) \ _ER(EXIT_READ_DR1, 0x021) \ _ER(EXIT_READ_DR2, 0x022) \ _ER(EXIT_READ_DR3, 0x023) \ _ER(EXIT_READ_DR4, 0x024) \ _ER(EXIT_READ_DR5, 0x025) \ _ER(EXIT_READ_DR6, 0x026) \ _ER(EXIT_READ_DR7, 0x027) \ _ER(EXIT_WRITE_DR0, 0x030) \ _ER(EXIT_WRITE_DR1, 0x031) \ _ER(EXIT_WRITE_DR2, 0x032) \ _ER(EXIT_WRITE_DR3, 0x033) \ _ER(EXIT_WRITE_DR4, 0x034) \ _ER(EXIT_WRITE_DR5, 0x035) \ _ER(EXIT_WRITE_DR6, 0x036) \ _ER(EXIT_WRITE_DR7, 0x037) \ _ER(EXIT_EXCP_BASE, 0x040) \ _ER(EXIT_INTR, 0x060) \ _ER(EXIT_NMI, 0x061) \ _ER(EXIT_SMI, 0x062) \ _ER(EXIT_INIT, 0x063) \ _ER(EXIT_VINTR, 0x064) \ _ER(EXIT_CR0_SEL_WRITE, 0x065) \ _ER(EXIT_IDTR_READ, 0x066) \ _ER(EXIT_GDTR_READ, 0x067) \ _ER(EXIT_LDTR_READ, 0x068) \ _ER(EXIT_TR_READ, 0x069) \ _ER(EXIT_IDTR_WRITE, 0x06a) \ _ER(EXIT_GDTR_WRITE, 0x06b) \ _ER(EXIT_LDTR_WRITE, 0x06c) \ _ER(EXIT_TR_WRITE, 0x06d) \ _ER(EXIT_RDTSC, 0x06e) \ _ER(EXIT_RDPMC, 0x06f) \ _ER(EXIT_PUSHF, 0x070) \ _ER(EXIT_POPF, 0x071) \ _ER(EXIT_CPUID, 0x072) \ _ER(EXIT_RSM, 0x073) \ _ER(EXIT_IRET, 0x074) \ _ER(EXIT_SWINT, 0x075) \ _ER(EXIT_INVD, 0x076) \ _ER(EXIT_PAUSE, 0x077) \ _ER(EXIT_HLT, 0x078) \ _ER(EXIT_INVLPG, 0x079) \ _ER(EXIT_INVLPGA, 0x07a) \ _ER(EXIT_IOIO, 0x07b) \ _ER(EXIT_MSR, 0x07c) \ _ER(EXIT_TASK_SWITCH, 0x07d) \ _ER(EXIT_FERR_FREEZE, 0x07e) \ _ER(EXIT_SHUTDOWN, 0x07f) \ _ER(EXIT_VMRUN, 0x080) \ _ER(EXIT_VMMCALL, 0x081) \ _ER(EXIT_VMLOAD, 0x082) \ _ER(EXIT_VMSAVE, 0x083) \ _ER(EXIT_STGI, 0x084) \ _ER(EXIT_CLGI, 0x085) \ _ER(EXIT_SKINIT, 0x086) \ _ER(EXIT_RDTSCP, 0x087) \ _ER(EXIT_ICEBP, 0x088) \ _ER(EXIT_WBINVD, 0x089) \ _ER(EXIT_MONITOR, 0x08a) \ _ER(EXIT_MWAIT, 0x08b) \ _ER(EXIT_MWAIT_COND, 0x08c) \ _ER(EXIT_NPF, 0x400) \ _ER(EXIT_ERR, -1) #define _ER(reason, val) { #reason, val }, struct str_values { const char *str; int val; }; static struct str_values vmx_exit_reasons[] = { VMX_EXIT_REASONS { NULL, -1} }; static struct str_values svm_exit_reasons[] = { SVM_EXIT_REASONS { NULL, -1} }; static struct isa_exit_reasons { unsigned isa; struct str_values *strings; } isa_exit_reasons[] = { { .isa = 1, .strings = vmx_exit_reasons }, { .isa = 2, .strings = svm_exit_reasons }, { } }; static const char *find_exit_reason(unsigned isa, int val) { struct str_values *strings = NULL; int i; for (i = 0; isa_exit_reasons[i].strings; ++i) if (isa_exit_reasons[i].isa == isa) { strings = isa_exit_reasons[i].strings; break; } if (!strings) return "UNKNOWN-ISA"; for (i = 0; strings[i].val >= 0; i++) if (strings[i].val == val) break; return strings[i].str; } static int print_exit_reason(struct trace_seq *s, struct pevent_record *record, struct event_format *event, const char *field) { unsigned long long isa; unsigned long long val; const char *reason; if (pevent_get_field_val(s, event, field, record, &val, 1) < 0) return -1; if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0) isa = 1; reason = find_exit_reason(isa, val); if (reason) trace_seq_printf(s, "reason %s", reason); else trace_seq_printf(s, "reason UNKNOWN (%llu)", val); return 0; } static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long info1 = 0, info2 = 0; if (print_exit_reason(s, record, event, "exit_reason") < 0) return -1; pevent_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1); if (pevent_get_field_val(s, event, "info1", record, &info1, 0) >= 0 && pevent_get_field_val(s, event, "info2", record, &info2, 0) >= 0) trace_seq_printf(s, " info %llx %llx\n", info1, info2); return 0; } #define KVM_EMUL_INSN_F_CR0_PE (1 << 0) #define KVM_EMUL_INSN_F_EFL_VM (1 << 1) #define KVM_EMUL_INSN_F_CS_D (1 << 2) #define KVM_EMUL_INSN_F_CS_L (1 << 3) static int kvm_emulate_insn_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long rip, csbase, len, flags, failed; int llen; uint8_t *insn; const char *disasm; if (pevent_get_field_val(s, event, "rip", record, &rip, 1) < 0) return -1; if (pevent_get_field_val(s, event, "csbase", record, &csbase, 1) < 0) return -1; if (pevent_get_field_val(s, event, "len", record, &len, 1) < 0) return -1; if (pevent_get_field_val(s, event, "flags", record, &flags, 1) < 0) return -1; if (pevent_get_field_val(s, event, "failed", record, &failed, 1) < 0) return -1; insn = pevent_get_field_raw(s, event, "insn", record, &llen, 1); if (!insn) return -1; disasm = disassemble(insn, len, rip, flags & KVM_EMUL_INSN_F_CR0_PE, flags & KVM_EMUL_INSN_F_EFL_VM, flags & KVM_EMUL_INSN_F_CS_D, flags & KVM_EMUL_INSN_F_CS_L); trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm, failed ? " FAIL" : ""); return 0; } static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { if (print_exit_reason(s, record, event, "exit_code") < 0) return -1; pevent_print_num_field(s, " info1 %llx", event, "exit_info1", record, 1); pevent_print_num_field(s, " info2 %llx", event, "exit_info2", record, 1); pevent_print_num_field(s, " int_info %llx", event, "exit_int_info", record, 1); pevent_print_num_field(s, " int_info_err %llx", event, "exit_int_info_err", record, 1); return 0; } static int kvm_nested_vmexit_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { pevent_print_num_field(s, "rip %lx ", event, "rip", record, 1); return kvm_nested_vmexit_inject_handler(s, record, event, context); } union kvm_mmu_page_role { unsigned word; struct { unsigned glevels:4; unsigned level:4; unsigned quadrant:2; unsigned pad_for_nice_hex_output:6; unsigned direct:1; unsigned access:3; unsigned invalid:1; unsigned cr4_pge:1; unsigned nxe:1; }; }; static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; static const char *access_str[] = { "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" }; union kvm_mmu_page_role role; if (pevent_get_field_val(s, event, "role", record, &val, 1) < 0) return -1; role.word = (int)val; /* * We can only use the structure if file is of the same * endianess. */ if (pevent_is_file_bigendian(event->pevent) == pevent_is_host_bigendian(event->pevent)) { trace_seq_printf(s, "%u/%u q%u%s %s%s %spge %snxe", role.level, role.glevels, role.quadrant, role.direct ? " direct" : "", access_str[role.access], role.invalid ? " invalid" : "", role.cr4_pge ? "" : "!", role.nxe ? "" : "!"); } else trace_seq_printf(s, "WORD: %08x", role.word); pevent_print_num_field(s, " root %u ", event, "root_count", record, 1); if (pevent_get_field_val(s, event, "unsync", record, &val, 1) < 0) return -1; trace_seq_printf(s, "%s%c", val ? "unsync" : "sync", 0); return 0; } static int kvm_mmu_get_page_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { unsigned long long val; if (pevent_get_field_val(s, event, "created", record, &val, 1) < 0) return -1; trace_seq_printf(s, "%s ", val ? "new" : "existing"); if (pevent_get_field_val(s, event, "gfn", record, &val, 1) < 0) return -1; trace_seq_printf(s, "sp gfn %llx ", val); return kvm_mmu_print_role(s, record, event, context); } #define PT_WRITABLE_SHIFT 1 #define PT_WRITABLE_MASK (1ULL << PT_WRITABLE_SHIFT) static unsigned long long process_is_writable_pte(struct trace_seq *s, unsigned long long *args) { unsigned long pte = args[0]; return pte & PT_WRITABLE_MASK; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { init_disassembler(); pevent_register_event_handler(pevent, -1, "kvm", "kvm_exit", kvm_exit_handler, NULL); pevent_register_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", kvm_emulate_insn_handler, NULL); pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit", kvm_nested_vmexit_handler, NULL); pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject", kvm_nested_vmexit_inject_handler, NULL); pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", kvm_mmu_get_page_handler, NULL); pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", kvm_mmu_print_role, NULL); pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_unsync_page", kvm_mmu_print_role, NULL); pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", kvm_mmu_print_role, NULL); pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, NULL); pevent_register_print_function(pevent, process_is_writable_pte, PEVENT_FUNC_ARG_INT, "is_writable_pte", PEVENT_FUNC_ARG_LONG, PEVENT_FUNC_ARG_VOID); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_exit", kvm_exit_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", kvm_emulate_insn_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit", kvm_nested_vmexit_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject", kvm_nested_vmexit_inject_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", kvm_mmu_get_page_handler, NULL); pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", kvm_mmu_print_role, NULL); pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_unsync_page", kvm_mmu_print_role, NULL); pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", kvm_mmu_print_role, NULL); pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, NULL); pevent_unregister_print_function(pevent, process_is_writable_pte, "is_writable_pte"); } trace-cmd-2.5.3/plugin_mac80211.c000066400000000000000000000120221246701203100162270ustar00rootroot00000000000000/* * Copyright (C) 2009 Johannes Berg * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "event-parse.h" #define INDENT 65 static void print_string(struct trace_seq *s, struct event_format *event, const char *name, const void *data) { struct format_field *f = pevent_find_field(event, name); int offset; int length; if (!f) { trace_seq_printf(s, "NOTFOUND:%s", name); return; } offset = f->offset; length = f->size; if (!strncmp(f->type, "__data_loc", 10)) { unsigned long long v; if (pevent_read_number_field(f, data, &v)) { trace_seq_printf(s, "invalid_data_loc"); return; } offset = v & 0xffff; length = v >> 16; } trace_seq_printf(s, "%.*s", length, (char *)data + offset); } struct value_name { unsigned long long value; const char *name; }; static void _print_enum(struct trace_seq *s, struct event_format *event, const char *name, const void *data, const struct value_name *names, int n_names) { struct format_field *f = pevent_find_field(event, name); unsigned long long val; int i; if (!f) { trace_seq_puts(s, "field-not-found"); return; } if (pevent_read_number_field(f, data, &val)) { trace_seq_puts(s, "field-invalid"); return; } for (i = 0; i < n_names; i++) { if (names[i].value == val) { trace_seq_puts(s, names[i].name); return; } } trace_seq_printf(s, "%llu", val); } #define print_enum(s, ev, name, data, enums...) \ ({ static const struct value_name __n[] = { enums }; \ _print_enum(s, ev, name, data, __n, sizeof(__n)/sizeof(__n[0])); \ }) static void _print_flag(struct trace_seq *s, struct event_format *event, const char *name, const void *data, const struct value_name *names, int n_names) { struct format_field *f = pevent_find_field(event, name); unsigned long long val; int i, j, found, first = 1; if (!f) { trace_seq_puts(s, "field-not-found"); return; } if (pevent_read_number_field(f, data, &val)) { trace_seq_puts(s, "field-invalid"); return; } for (i = 0; i < 64; i++) { if (!(val & (1ULL<data; print_string(s, event, "wiphy_name", data); trace_seq_printf(s, " vif:"); print_string(s, event, "vif_name", data); pevent_print_num_field(s, "(%d)", event, "vif_type", record, 1); trace_seq_printf(s, "\n%*s", INDENT, ""); SF("assoc"); SP(); SF("aid"); SP(); SF("cts"); SP(); SF("shortpre"); SP(); SF("shortslot"); SP(); SF("dtimper"); SP(); trace_seq_printf(s, "\n%*s", INDENT, ""); SF("bcnint"); SP(); SFX("assoc_cap"); SP(); SFX("basic_rates"); SP(); SF("enable_beacon"); trace_seq_printf(s, "\n%*s", INDENT, ""); SF("ht_operation_mode"); return 0; } static int drv_config(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { void *data = record->data; print_string(s, event, "wiphy_name", data); trace_seq_putc(s, ' '); print_flag(s, event, "flags", data, { 0, "MONITOR" }, { 1, "PS" }, { 2, "IDLE" }, { 3, "QOS"}, ); pevent_print_num_field(s, " chan:%d/", event, "center_freq", record, 1); print_enum(s, event, "channel_type", data, { 0, "noht" }, { 1, "ht20" }, { 2, "ht40-" }, { 3, "ht40+" }); trace_seq_putc(s, ' '); SF("power_level"); return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "mac80211", "drv_bss_info_changed", drv_bss_info_changed, NULL); pevent_register_event_handler(pevent, -1, "mac80211", "drv_config", drv_config, NULL); return 0; } trace-cmd-2.5.3/plugin_python.c000066400000000000000000000040541246701203100164220ustar00rootroot00000000000000#include #include #include "trace-cmd.h" #ifndef PYTHON_DIR #define PYTHON_DIR "." #endif static const char pypath[] = "import sys\n" "sys.path.append(\"" PYTHON_DIR "\")\n"; static const char pyload[] = "import imp, tracecmd, ctracecmd\n" "fn = r'%s'\n" "file = open(fn, 'r')\n" "try:\n" " module = imp.load_source('%s', fn, file)\n" " module.register(tracecmd.PEvent(ctracecmd.convert_pevent(pevent)))\n" "finally:\n" " file.close()\n"; static void load_plugin(struct pevent *pevent, const char *path, const char *name, void *data) { PyObject *globals = data; int len = strlen(path) + strlen(name) + 2; int nlen = strlen(name) + 1; char *full = malloc(len); char *n = malloc(nlen); char *load; PyObject *res; if (!full || !n) return; strcpy(full, path); strcat(full, "/"); strcat(full, name); strcpy(n, name); n[nlen - 4] = '\0'; asprintf(&load, pyload, full, n); if (!load) return; res = PyRun_String(load, Py_file_input, globals, globals); if (!res) { fprintf(stderr, "failed loading %s\n", full); PyErr_Print(); } else Py_DECREF(res); free(load); } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { PyObject *globals, *m, *py_pevent, *str, *res; char **plugin_list; /* Only load plugins if they exist */ plugin_list = trace_util_find_plugin_files(".py"); if (!plugin_list) return 0; trace_util_free_plugin_files(plugin_list); Py_Initialize(); m = PyImport_AddModule("__main__"); globals = PyModule_GetDict(m); res = PyRun_String(pypath, Py_file_input, globals, globals); if (!res) { PyErr_Print(); return -1; } else Py_DECREF(res); str = PyString_FromString("pevent"); if (!str) return -ENOMEM; py_pevent = PyLong_FromUnsignedLong((unsigned long)pevent); if (!py_pevent) return -ENOMEM; if (PyDict_SetItem(globals, str, py_pevent)) fprintf(stderr, "failed to insert pevent\n"); Py_DECREF(py_pevent); Py_DECREF(str); trace_util_load_plugins(pevent, ".py", load_plugin, globals); return 0; } int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { Py_Finalize(); return 0; } trace-cmd-2.5.3/plugin_sched_switch.c000066400000000000000000000105611246701203100175500ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include "trace-cmd.h" static void write_state(struct trace_seq *s, int val) { const char states[] = "SDTtZXxW"; int found = 0; int i; for (i=0; i < (sizeof(states) - 1); i++) { if (!(val & (1 << i))) continue; if (found) trace_seq_putc(s, '|'); found = 1; trace_seq_putc(s, states[i]); } if (!found) trace_seq_putc(s, 'R'); } static void write_and_save_comm(struct format_field *field, struct pevent_record *record, struct trace_seq *s, int pid) { const char *comm; int len; comm = (char *)(record->data + field->offset); len = s->len; trace_seq_printf(s, "%.*s", field->size, comm); /* make sure the comm has a \0 at the end. */ trace_seq_terminate(s); comm = &s->buffer[len]; /* Help out the comm to ids. This will handle dups */ pevent_register_comm(field->event->pevent, comm, pid); } static int sched_wakeup_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { struct format_field *field; unsigned long long val; if (pevent_get_field_val(s, event, "pid", record, &val, 1)) return trace_seq_putc(s, '!'); field = pevent_find_any_field(event, "comm"); if (field) { write_and_save_comm(field, record, s, val); trace_seq_putc(s, ':'); } trace_seq_printf(s, "%lld", val); if (pevent_get_field_val(s, event, "prio", record, &val, 0) == 0) trace_seq_printf(s, " [%lld]", val); if (pevent_get_field_val(s, event, "success", record, &val, 1) == 0) trace_seq_printf(s, " success=%lld", val); if (pevent_get_field_val(s, event, "target_cpu", record, &val, 0) == 0) trace_seq_printf(s, " CPU:%03llu", val); return 0; } static int sched_switch_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { struct format_field *field; unsigned long long val; if (pevent_get_field_val(s, event, "prev_pid", record, &val, 1)) return trace_seq_putc(s, '!'); field = pevent_find_any_field(event, "prev_comm"); if (field) { write_and_save_comm(field, record, s, val); trace_seq_putc(s, ':'); } trace_seq_printf(s, "%lld ", val); if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0) trace_seq_printf(s, "[%lld] ", val); if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0) write_state(s, val); trace_seq_puts(s, " ==> "); if (pevent_get_field_val(s, event, "next_pid", record, &val, 1)) return trace_seq_putc(s, '!'); field = pevent_find_any_field(event, "next_comm"); if (field) { write_and_save_comm(field, record, s, val); trace_seq_putc(s, ':'); } trace_seq_printf(s, "%lld", val); if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0) trace_seq_printf(s, " [%lld]", val); return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_event_handler(pevent, -1, "sched", "sched_switch", sched_switch_handler, NULL); pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup", sched_wakeup_handler, NULL); pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup_new", sched_wakeup_handler, NULL); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_event_handler(pevent, -1, "sched", "sched_switch", sched_switch_handler, NULL); pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup", sched_wakeup_handler, NULL); pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup_new", sched_wakeup_handler, NULL); } trace-cmd-2.5.3/plugin_scsi.c000066400000000000000000000247661246701203100160560ustar00rootroot00000000000000#include #include #include #include "event-parse.h" typedef unsigned long sector_t; typedef uint64_t u64; typedef unsigned int u32; /* * SCSI opcodes */ #define TEST_UNIT_READY 0x00 #define REZERO_UNIT 0x01 #define REQUEST_SENSE 0x03 #define FORMAT_UNIT 0x04 #define READ_BLOCK_LIMITS 0x05 #define REASSIGN_BLOCKS 0x07 #define INITIALIZE_ELEMENT_STATUS 0x07 #define READ_6 0x08 #define WRITE_6 0x0a #define SEEK_6 0x0b #define READ_REVERSE 0x0f #define WRITE_FILEMARKS 0x10 #define SPACE 0x11 #define INQUIRY 0x12 #define RECOVER_BUFFERED_DATA 0x14 #define MODE_SELECT 0x15 #define RESERVE 0x16 #define RELEASE 0x17 #define COPY 0x18 #define ERASE 0x19 #define MODE_SENSE 0x1a #define START_STOP 0x1b #define RECEIVE_DIAGNOSTIC 0x1c #define SEND_DIAGNOSTIC 0x1d #define ALLOW_MEDIUM_REMOVAL 0x1e #define READ_FORMAT_CAPACITIES 0x23 #define SET_WINDOW 0x24 #define READ_CAPACITY 0x25 #define READ_10 0x28 #define WRITE_10 0x2a #define SEEK_10 0x2b #define POSITION_TO_ELEMENT 0x2b #define WRITE_VERIFY 0x2e #define VERIFY 0x2f #define SEARCH_HIGH 0x30 #define SEARCH_EQUAL 0x31 #define SEARCH_LOW 0x32 #define SET_LIMITS 0x33 #define PRE_FETCH 0x34 #define READ_POSITION 0x34 #define SYNCHRONIZE_CACHE 0x35 #define LOCK_UNLOCK_CACHE 0x36 #define READ_DEFECT_DATA 0x37 #define MEDIUM_SCAN 0x38 #define COMPARE 0x39 #define COPY_VERIFY 0x3a #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define UPDATE_BLOCK 0x3d #define READ_LONG 0x3e #define WRITE_LONG 0x3f #define CHANGE_DEFINITION 0x40 #define WRITE_SAME 0x41 #define UNMAP 0x42 #define READ_TOC 0x43 #define READ_HEADER 0x44 #define GET_EVENT_STATUS_NOTIFICATION 0x4a #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define XDWRITEREAD_10 0x53 #define MODE_SELECT_10 0x55 #define RESERVE_10 0x56 #define RELEASE_10 0x57 #define MODE_SENSE_10 0x5a #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f #define VARIABLE_LENGTH_CMD 0x7f #define REPORT_LUNS 0xa0 #define SECURITY_PROTOCOL_IN 0xa2 #define MAINTENANCE_IN 0xa3 #define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 #define EXCHANGE_MEDIUM 0xa6 #define READ_12 0xa8 #define WRITE_12 0xaa #define READ_MEDIA_SERIAL_NUMBER 0xab #define WRITE_VERIFY_12 0xae #define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 #define SECURITY_PROTOCOL_OUT 0xb5 #define READ_ELEMENT_STATUS 0xb8 #define SEND_VOLUME_TAG 0xb6 #define WRITE_LONG_2 0xea #define EXTENDED_COPY 0x83 #define RECEIVE_COPY_RESULTS 0x84 #define ACCESS_CONTROL_IN 0x86 #define ACCESS_CONTROL_OUT 0x87 #define READ_16 0x88 #define WRITE_16 0x8a #define READ_ATTRIBUTE 0x8c #define WRITE_ATTRIBUTE 0x8d #define VERIFY_16 0x8f #define SYNCHRONIZE_CACHE_16 0x91 #define WRITE_SAME_16 0x93 #define SERVICE_ACTION_IN 0x9e /* values for service action in */ #define SAI_READ_CAPACITY_16 0x10 #define SAI_GET_LBA_STATUS 0x12 /* values for VARIABLE_LENGTH_CMD service action codes * see spc4r17 Section D.3.5, table D.7 and D.8 */ #define VLC_SA_RECEIVE_CREDENTIAL 0x1800 /* values for maintenance in */ #define MI_REPORT_IDENTIFYING_INFORMATION 0x05 #define MI_REPORT_TARGET_PGS 0x0a #define MI_REPORT_ALIASES 0x0b #define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c #define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d #define MI_REPORT_PRIORITY 0x0e #define MI_REPORT_TIMESTAMP 0x0f #define MI_MANAGEMENT_PROTOCOL_IN 0x10 /* value for MI_REPORT_TARGET_PGS ext header */ #define MI_EXT_HDR_PARAM_FMT 0x20 /* values for maintenance out */ #define MO_SET_IDENTIFYING_INFORMATION 0x06 #define MO_SET_TARGET_PGS 0x0a #define MO_CHANGE_ALIASES 0x0b #define MO_SET_PRIORITY 0x0e #define MO_SET_TIMESTAMP 0x0f #define MO_MANAGEMENT_PROTOCOL_OUT 0x10 /* values for variable length command */ #define XDREAD_32 0x03 #define XDWRITE_32 0x04 #define XPWRITE_32 0x06 #define XDWRITEREAD_32 0x07 #define READ_32 0x09 #define VERIFY_32 0x0a #define WRITE_32 0x0b #define WRITE_SAME_32 0x0d #define SERVICE_ACTION16(cdb) (cdb[1] & 0x1f) #define SERVICE_ACTION32(cdb) ((cdb[8] << 8) | cdb[9]) static const char * scsi_trace_misc(struct trace_seq *, unsigned char *, int); static const char * scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; sector_t lba = 0, txlen = 0; lba |= ((cdb[1] & 0x1F) << 16); lba |= (cdb[2] << 8); lba |= cdb[3]; txlen = cdb[4]; trace_seq_printf(p, "lba=%llu txlen=%llu", (unsigned long long)lba, (unsigned long long)txlen); trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; sector_t lba = 0, txlen = 0; lba |= (cdb[2] << 24); lba |= (cdb[3] << 16); lba |= (cdb[4] << 8); lba |= cdb[5]; txlen |= (cdb[7] << 8); txlen |= cdb[8]; trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", (unsigned long long)lba, (unsigned long long)txlen, cdb[1] >> 5); if (cdb[0] == WRITE_SAME) trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; sector_t lba = 0, txlen = 0; lba |= (cdb[2] << 24); lba |= (cdb[3] << 16); lba |= (cdb[4] << 8); lba |= cdb[5]; txlen |= (cdb[6] << 24); txlen |= (cdb[7] << 16); txlen |= (cdb[8] << 8); txlen |= cdb[9]; trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", (unsigned long long)lba, (unsigned long long)txlen, cdb[1] >> 5); trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; sector_t lba = 0, txlen = 0; lba |= ((u64)cdb[2] << 56); lba |= ((u64)cdb[3] << 48); lba |= ((u64)cdb[4] << 40); lba |= ((u64)cdb[5] << 32); lba |= (cdb[6] << 24); lba |= (cdb[7] << 16); lba |= (cdb[8] << 8); lba |= cdb[9]; txlen |= (cdb[10] << 24); txlen |= (cdb[11] << 16); txlen |= (cdb[12] << 8); txlen |= cdb[13]; trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", (unsigned long long)lba, (unsigned long long)txlen, cdb[1] >> 5); if (cdb[0] == WRITE_SAME_16) trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len, *cmd; sector_t lba = 0, txlen = 0; u32 ei_lbrt = 0; switch (SERVICE_ACTION32(cdb)) { case READ_32: cmd = "READ"; break; case VERIFY_32: cmd = "VERIFY"; break; case WRITE_32: cmd = "WRITE"; break; case WRITE_SAME_32: cmd = "WRITE_SAME"; break; default: trace_seq_printf(p, "UNKNOWN"); goto out; } lba |= ((u64)cdb[12] << 56); lba |= ((u64)cdb[13] << 48); lba |= ((u64)cdb[14] << 40); lba |= ((u64)cdb[15] << 32); lba |= (cdb[16] << 24); lba |= (cdb[17] << 16); lba |= (cdb[18] << 8); lba |= cdb[19]; ei_lbrt |= (cdb[20] << 24); ei_lbrt |= (cdb[21] << 16); ei_lbrt |= (cdb[22] << 8); ei_lbrt |= cdb[23]; txlen |= (cdb[28] << 24); txlen |= (cdb[29] << 16); txlen |= (cdb[30] << 8); txlen |= cdb[31]; trace_seq_printf(p, "%s_32 lba=%llu txlen=%llu protect=%u ei_lbrt=%u", cmd, (unsigned long long)lba, (unsigned long long)txlen, cdb[10] >> 5, ei_lbrt); if (SERVICE_ACTION32(cdb) == WRITE_SAME_32) trace_seq_printf(p, " unmap=%u", cdb[10] >> 3 & 1); out: trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; unsigned int regions = cdb[7] << 8 | cdb[8]; trace_seq_printf(p, "regions=%u", (regions - 8) / 16); trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len, *cmd; sector_t lba = 0; u32 alloc_len = 0; switch (SERVICE_ACTION16(cdb)) { case SAI_READ_CAPACITY_16: cmd = "READ_CAPACITY_16"; break; case SAI_GET_LBA_STATUS: cmd = "GET_LBA_STATUS"; break; default: trace_seq_printf(p, "UNKNOWN"); goto out; } lba |= ((u64)cdb[2] << 56); lba |= ((u64)cdb[3] << 48); lba |= ((u64)cdb[4] << 40); lba |= ((u64)cdb[5] << 32); lba |= (cdb[6] << 24); lba |= (cdb[7] << 16); lba |= (cdb[8] << 8); lba |= cdb[9]; alloc_len |= (cdb[10] << 24); alloc_len |= (cdb[11] << 16); alloc_len |= (cdb[12] << 8); alloc_len |= cdb[13]; trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, (unsigned long long)lba, alloc_len); out: trace_seq_putc(p, 0); return ret; } static const char * scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len) { switch (SERVICE_ACTION32(cdb)) { case READ_32: case VERIFY_32: case WRITE_32: case WRITE_SAME_32: return scsi_trace_rw32(p, cdb, len); default: return scsi_trace_misc(p, cdb, len); } } static const char * scsi_trace_misc(struct trace_seq *p, unsigned char *cdb, int len) { const char *ret = p->buffer + p->len; trace_seq_printf(p, "-"); trace_seq_putc(p, 0); return ret; } const char * scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) { switch (cdb[0]) { case READ_6: case WRITE_6: return scsi_trace_rw6(p, cdb, len); case READ_10: case VERIFY: case WRITE_10: case WRITE_SAME: return scsi_trace_rw10(p, cdb, len); case READ_12: case VERIFY_12: case WRITE_12: return scsi_trace_rw12(p, cdb, len); case READ_16: case VERIFY_16: case WRITE_16: case WRITE_SAME_16: return scsi_trace_rw16(p, cdb, len); case UNMAP: return scsi_trace_unmap(p, cdb, len); case SERVICE_ACTION_IN: return scsi_trace_service_action_in(p, cdb, len); case VARIABLE_LENGTH_CMD: return scsi_trace_varlen(p, cdb, len); default: return scsi_trace_misc(p, cdb, len); } } unsigned long long process_scsi_trace_parse_cdb(struct trace_seq *s, unsigned long long *args) { scsi_trace_parse_cdb(s, (unsigned char *) (unsigned long) args[1], args[2]); return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_print_function(pevent, process_scsi_trace_parse_cdb, PEVENT_FUNC_ARG_STRING, "scsi_trace_parse_cdb", PEVENT_FUNC_ARG_PTR, PEVENT_FUNC_ARG_PTR, PEVENT_FUNC_ARG_INT, PEVENT_FUNC_ARG_VOID); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_print_function(pevent, process_scsi_trace_parse_cdb, "scsi_trace_parse_cdb"); } trace-cmd-2.5.3/plugin_xen.c000066400000000000000000000067151246701203100157010ustar00rootroot00000000000000#include #include #include #include "event-parse.h" #define __HYPERVISOR_set_trap_table 0 #define __HYPERVISOR_mmu_update 1 #define __HYPERVISOR_set_gdt 2 #define __HYPERVISOR_stack_switch 3 #define __HYPERVISOR_set_callbacks 4 #define __HYPERVISOR_fpu_taskswitch 5 #define __HYPERVISOR_sched_op_compat 6 #define __HYPERVISOR_dom0_op 7 #define __HYPERVISOR_set_debugreg 8 #define __HYPERVISOR_get_debugreg 9 #define __HYPERVISOR_update_descriptor 10 #define __HYPERVISOR_memory_op 12 #define __HYPERVISOR_multicall 13 #define __HYPERVISOR_update_va_mapping 14 #define __HYPERVISOR_set_timer_op 15 #define __HYPERVISOR_event_channel_op_compat 16 #define __HYPERVISOR_xen_version 17 #define __HYPERVISOR_console_io 18 #define __HYPERVISOR_physdev_op_compat 19 #define __HYPERVISOR_grant_table_op 20 #define __HYPERVISOR_vm_assist 21 #define __HYPERVISOR_update_va_mapping_otherdomain 22 #define __HYPERVISOR_iret 23 /* x86 only */ #define __HYPERVISOR_vcpu_op 24 #define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ #define __HYPERVISOR_mmuext_op 26 #define __HYPERVISOR_acm_op 27 #define __HYPERVISOR_nmi_op 28 #define __HYPERVISOR_sched_op 29 #define __HYPERVISOR_callback_op 30 #define __HYPERVISOR_xenoprof_op 31 #define __HYPERVISOR_event_channel_op 32 #define __HYPERVISOR_physdev_op 33 #define __HYPERVISOR_hvm_op 34 #define __HYPERVISOR_tmem_op 38 /* Architecture-specific hypercall definitions. */ #define __HYPERVISOR_arch_0 48 #define __HYPERVISOR_arch_1 49 #define __HYPERVISOR_arch_2 50 #define __HYPERVISOR_arch_3 51 #define __HYPERVISOR_arch_4 52 #define __HYPERVISOR_arch_5 53 #define __HYPERVISOR_arch_6 54 #define __HYPERVISOR_arch_7 55 #define N(x) [__HYPERVISOR_##x] = "("#x")" static const char *xen_hypercall_names[] = { N(set_trap_table), N(mmu_update), N(set_gdt), N(stack_switch), N(set_callbacks), N(fpu_taskswitch), N(sched_op_compat), N(dom0_op), N(set_debugreg), N(get_debugreg), N(update_descriptor), N(memory_op), N(multicall), N(update_va_mapping), N(set_timer_op), N(event_channel_op_compat), N(xen_version), N(console_io), N(physdev_op_compat), N(grant_table_op), N(vm_assist), N(update_va_mapping_otherdomain), N(iret), N(vcpu_op), N(set_segment_base), N(mmuext_op), N(acm_op), N(nmi_op), N(sched_op), N(callback_op), N(xenoprof_op), N(event_channel_op), N(physdev_op), N(hvm_op), /* Architecture-specific hypercall definitions. */ N(arch_0), N(arch_1), N(arch_2), N(arch_3), N(arch_4), N(arch_5), N(arch_6), N(arch_7), }; #undef N #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static const char *xen_hypercall_name(unsigned op) { if (op < ARRAY_SIZE(xen_hypercall_names) && xen_hypercall_names[op] != NULL) return xen_hypercall_names[op]; return ""; } unsigned long long process_xen_hypercall_name(struct trace_seq *s, unsigned long long *args) { unsigned int op = args[0]; trace_seq_printf(s, "%s", xen_hypercall_name(op)); return 0; } int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { pevent_register_print_function(pevent, process_xen_hypercall_name, PEVENT_FUNC_ARG_STRING, "xen_hypercall_name", PEVENT_FUNC_ARG_INT, PEVENT_FUNC_ARG_VOID); return 0; } void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { pevent_unregister_print_function(pevent, process_xen_hypercall_name, "xen_hypercall_name"); } trace-cmd-2.5.3/trace-blk-hack.c000066400000000000000000000115061246701203100162730ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #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 pevent *pevent; struct event_format *event; struct 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 = pevent_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 = pevent_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 = pevent_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 = pevent_find_common_field(event, "common_type"); if (!field || field->offset != 0 || field->size != 2) goto fail; field = pevent_find_common_field(event, "common_flags"); if (!field || field->offset != 2 || field->size != 1) goto fail; field = pevent_find_common_field(event, "common_preempt_count"); if (!field || field->offset != 3 || field->size != 1) goto fail; field = pevent_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 = pevent_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; pevent_parse_event(pevent, buf, l, "ftrace"); return 0; fail: return -1; } trace-cmd-2.5.3/trace-capture.c000066400000000000000000001074471246701203100162740ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd.h" #include "trace-gui.h" #include "kernel-shark.h" #include "version.h" #define default_output_file "trace.dat" #define PLUGIN_NONE "NONE" #define DIALOG_WIDTH 820 #define DIALOG_HEIGHT 600 #define CAP_STOP "Stop" #define DEFAULT_MAX_BUF_SIZE 1000000 struct trace_capture { struct pevent *pevent; struct shark_info *info; GtkWidget *main_dialog; GtkWidget *command_entry; GtkWidget *file_entry; GtkWidget *output_text; GtkTextBuffer *output_buffer; GtkWidget *event_view; GtkWidget *plugin_combo; GtkWidget *settings_combo; GtkWidget *run_button; GtkWidget *max_num_entry; pthread_t thread; gboolean kill_thread; gboolean capture_done; gboolean load_file; int command_input_fd; int command_output_fd; int command_pid; }; static int is_just_ws(const char *str) { int i; for (i = 0; str[i]; i++) if (!isspace(str[i])) break; return !str[i]; } static gboolean settings_saved; static GString *get_home_settings_new(void) { char *path = getenv("HOME"); GString *str; str = g_string_new(path); g_string_append(str, "/.trace-cmd/settings/"); return str; } static int create_home_settings(void) { char *path = getenv("HOME"); GString *str; struct stat st; int ret; str = g_string_new(path); g_string_append(str, "/.trace-cmd"); ret = stat(str->str, &st); if (ret < 0) { ret = mkdir(str->str, 0755); if (ret < 0) { warning("Can not create %s", str->str); goto out; } } g_string_append(str, "/settings"); ret = stat(str->str, &st); if (ret < 0) { ret = mkdir(str->str, 0755); if (ret < 0) { warning("Can not create %s", str->str); goto out; } } ret = 0; out: g_string_free(str, TRUE); return ret; } static GtkTreeModel *create_settings_model(gpointer data) { struct dirent *dent; GtkListStore *list; GtkTreeIter iter; struct stat st; GString *str; DIR *dir; int ret; list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, "Current", 1, "", -1); /* Search for user settings first */ str = get_home_settings_new(); ret = stat(str->str, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) goto read_system; dir = opendir(str->str); if (!dir) goto read_system; while ((dent = readdir(dir))) { const char *name = dent->d_name; GString *file; gchar *item; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; if (strcmp(name + strlen(name) - 4, ".kss") != 0) continue; file = g_string_new(str->str); g_string_append_printf(file, "/%s", name); /* Save the file name but remove the .kss extention */ item = g_strdup(name); item[strlen(name) - 4] = 0; gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, item, 1, file->str, -1); g_free(item); g_string_free(file, TRUE); } read_system: g_string_free(str, TRUE); return GTK_TREE_MODEL(list); } static void refresh_settings(struct trace_capture *cap) { GtkTreeModel *model; model = create_settings_model(NULL); gtk_combo_box_set_model(GTK_COMBO_BOX(cap->settings_combo), model); g_object_unref(model); } static void ks_clear_capture_events(struct shark_info *info) { info->cap_all_events = FALSE; tracecmd_free_list(info->cap_systems); info->cap_systems = NULL; free(info->cap_events); info->cap_events = NULL; } static void clear_capture_events(struct trace_capture *cap) { ks_clear_capture_events(cap->info); } void kernel_shark_clear_capture(struct shark_info *info) { ks_clear_capture_events(info); g_free(info->cap_plugin); info->cap_plugin = NULL; g_free(info->cap_settings_name); info->cap_settings_name = NULL; free(info->cap_file); info->cap_file = NULL; g_free(info->cap_buffer_output); info->cap_buffer_output = NULL; } static gboolean end_capture(struct trace_capture *cap) { const char *filename; const char *val; int pid; val = gtk_button_get_label(GTK_BUTTON(cap->run_button)); if (strcmp(val, CAP_STOP) != 0) return FALSE; gtk_button_set_label(GTK_BUTTON(cap->run_button), "Run"); cap->capture_done = TRUE; pid = cap->command_pid; cap->command_pid = 0; if (pid) { kill(pid, SIGINT); gdk_threads_leave(); waitpid(pid, NULL, 0); gdk_threads_enter(); } if (cap->kill_thread) { gdk_threads_leave(); pthread_join(cap->thread, NULL); gdk_threads_enter(); cap->kill_thread = FALSE; } if (cap->command_input_fd) { close(cap->command_input_fd); cap->command_input_fd = 0; } if (cap->command_output_fd) { close(cap->command_output_fd); cap->command_output_fd = 0; } if (cap->load_file) { filename = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); kernelshark_load_file(cap->info, filename); cap->load_file = FALSE; } return TRUE; } static int is_latency(char *plugin) { return strcmp(plugin, "wakeup") == 0 || strcmp(plugin, "wakeup_rt") == 0 || strcmp(plugin, "irqsoff") == 0 || strcmp(plugin, "preemptoff") == 0 || strcmp(plugin, "preemptirqsoff") == 0; } static int calculate_trace_cmd_words(struct trace_capture *cap) { int words = 4; /* trace-cmd record -o file */ int i; if (cap->info->cap_all_events) words += 2; else { if (cap->info->cap_systems) { for (i = 0; cap->info->cap_systems[i]; i++) words += 2; } if (cap->info->cap_events) for (i = 0; cap->info->cap_events[i] >= 0; i++) words += 2; } if (cap->info->cap_plugin) words += 2; return words; } static char *find_tracecmd(void) { struct stat st; char *path = getenv("PATH"); char *saveptr; char *str; char *loc; char *tracecmd = NULL; int len; int ret; if (!path) return NULL; path = strdup(path); for (str = path; ; str = NULL) { loc = strtok_r(str, ":", &saveptr); if (!loc) break; len = strlen(loc) + 11; tracecmd = malloc_or_die(len); snprintf(tracecmd, len, "%s/trace-cmd", loc); ret = stat(tracecmd, &st); if (ret >= 0 && S_ISREG(st.st_mode)) { /* Do we have execute permissions */ if (st.st_uid == geteuid() && st.st_mode & S_IXUSR) break; if (st.st_gid == getegid() && st.st_mode & S_IXGRP) break; if (st.st_mode & S_IXOTH) break; } free(tracecmd); tracecmd = NULL; } free(path); return tracecmd; } static int add_trace_cmd_words(struct trace_capture *cap, char **args) { struct event_format *event; char **systems = cap->info->cap_systems; const gchar *output; int *events = cap->info->cap_events; int words = 0; int len; int i; output = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); args[words++] = find_tracecmd(); if (!args[0]) return -1; args[words++] = strdup("record"); args[words++] = strdup("-o"); args[words++] = strdup(output); if (cap->info->cap_plugin) { args[words++] = strdup("-p"); args[words++] = strdup(cap->info->cap_plugin); } if (cap->info->cap_all_events) { args[words++] = strdup("-e"); args[words++] = strdup("all"); } else { if (systems) { for (i = 0; systems[i]; i++) { args[words++] = strdup("-e"); args[words++] = strdup(systems[i]); } } if (events) { for (i = 0; events[i] >= 0; i++) { event = pevent_find_event(cap->pevent, events[i]); if (!event) continue; args[words++] = strdup("-e"); len = strlen(event->name) + strlen(event->system) + 2; args[words] = malloc_or_die(len); snprintf(args[words++], len, "%s:%s", event->system, event->name); } } } return words; } static gchar *get_combo_text(GtkComboBox *combo) { GtkTreeModel *model; GtkTreeIter iter; gchar *text; model = gtk_combo_box_get_model(combo); if (!model) return NULL; if (!gtk_combo_box_get_active_iter(combo, &iter)) return NULL; gtk_tree_model_get(model, &iter, 0, &text, -1); return text; } static void update_plugin(struct trace_capture *cap) { cap->info->cap_plugin = get_combo_text(GTK_COMBO_BOX(cap->plugin_combo)); if (strcmp(cap->info->cap_plugin, PLUGIN_NONE) == 0) { g_free(cap->info->cap_plugin); cap->info->cap_plugin = NULL; } } /* * The plugin and settings combo's are set by the first item * in the model. The can share the same code to set the model. * * Return TRUE if set, FALSE if name was not found. */ static int set_combo(GtkComboBox *combo, const char *name) { GtkTreeModel *model; GtkTreeIter iter; gchar *text; gboolean ret; model = gtk_combo_box_get_model(combo); if (!model) return FALSE; if (!gtk_tree_model_get_iter_first(model, &iter)) return FALSE; do { gtk_tree_model_get(model, &iter, 0, &text, -1); if (strcmp(text, name) == 0) { g_free(text); break; } g_free(text); ret = gtk_tree_model_iter_next(model, &iter); } while (ret); if (ret) { /* Found */ gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); return TRUE; } /* set to first item (default) */ gtk_tree_model_get_iter_first(model, &iter); gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); return FALSE; } static void set_plugin(struct trace_capture *cap) { GtkComboBox *combo = GTK_COMBO_BOX(cap->plugin_combo); const gchar *plugin = cap->info->cap_plugin; if (!plugin) plugin = PLUGIN_NONE; if (set_combo(combo, plugin)) return; /* Not found? */ g_free(cap->info->cap_plugin); cap->info->cap_plugin = NULL; } static void set_settings(struct trace_capture *cap) { GtkComboBox *combo = GTK_COMBO_BOX(cap->settings_combo); const gchar *name = cap->info->cap_settings_name; if (!name) name = ""; if (set_combo(combo, name)) return; /* Not found? */ g_free(cap->info->cap_settings_name); cap->info->cap_settings_name = NULL; } static void update_events(struct trace_capture *cap) { struct shark_info *info = cap->info; if (!cap->event_view) return; clear_capture_events(cap); trace_extract_event_list_view(cap->event_view, &info->cap_all_events, &info->cap_systems, &info->cap_events); } static void execute_command(struct trace_capture *cap) { const gchar *ccommand; gchar *command; gchar **args; gboolean space; int words; int tc_words; int i; update_plugin(cap); update_events(cap); ccommand = gtk_entry_get_text(GTK_ENTRY(cap->command_entry)); if (!ccommand || !strlen(ccommand) || is_just_ws(ccommand)) { words = 0; command = NULL; } else { command = strdup(ccommand); space = TRUE; words = 0; for (i = 0; command[i]; i++) { if (isspace(command[i])) space = TRUE; else { if (space) words++; space = FALSE; } } } tc_words = calculate_trace_cmd_words(cap); args = malloc_or_die(sizeof(*args) * (tc_words + words + 1)); add_trace_cmd_words(cap, args); words = tc_words; space = TRUE; for (i = 0; command && command[i]; i++) { if (isspace(command[i])) { space = TRUE; command[i] = 0; } else { if (space) { args[words] = &command[i]; words++; } space = FALSE; } } args[words] = NULL; write(1, "# ", 2); for (i = 0; args[i]; i++) { write(1, args[i], strlen(args[i])); write(1, " ", 1); } write(1, "\n", 1); execvp(args[0], args); perror("execvp"); for (i = 0; args[i]; i++) free(args[i]); free(args); g_free(cap->info->cap_plugin); } static void *monitor_pipes(void *data) { struct trace_capture *cap = data; GtkTextIter start_iter; GtkTextIter cut_iter; GtkTextIter iter; gchar buf[BUFSIZ+1]; struct timeval tv; const char *val; fd_set fds; gboolean eof; int max_size; int total; int del; int ret; int r; gdk_threads_enter(); /* get the max size */ val = gtk_entry_get_text(GTK_ENTRY(cap->max_num_entry)); max_size = atoi(val); /* Clear the buffer */ gtk_text_buffer_get_start_iter(cap->output_buffer, &start_iter); gtk_text_buffer_get_end_iter(cap->output_buffer, &cut_iter); gtk_text_buffer_delete(cap->output_buffer, &start_iter, &cut_iter); total = 0; gdk_threads_leave(); do { FD_ZERO(&fds); FD_SET(cap->command_input_fd, &fds); tv.tv_sec = 6; tv.tv_usec = 0; ret = select(cap->command_input_fd+1, &fds, NULL, NULL, &tv); if (ret < 0) break; eof = TRUE; while ((r = read(cap->command_input_fd, buf, BUFSIZ)) > 0) { eof = FALSE; buf[r] = 0; total += r; if (total > max_size) del = total - max_size; else del = 0; gdk_threads_enter(); if (del) { gtk_text_buffer_get_start_iter(cap->output_buffer, &start_iter); gtk_text_buffer_get_start_iter(cap->output_buffer, &cut_iter); gtk_text_iter_forward_chars(&cut_iter, del); gtk_text_buffer_delete(cap->output_buffer, &start_iter, &cut_iter); total -= del; } gtk_text_buffer_get_end_iter(cap->output_buffer, &iter); gtk_text_buffer_insert(cap->output_buffer, &iter, buf, -1); gdk_threads_leave(); } } while (!cap->capture_done && !eof); if (eof) { gdk_threads_enter(); end_capture(cap); gdk_threads_leave(); } pthread_exit(NULL); } static void run_command(struct trace_capture *cap) { int brass[2]; int copper[2]; int pid; if (pipe(brass) < 0) { warning("Could not create pipe"); return; } if (pipe(copper) < 0) { warning("Could not create pipe"); goto fail_pipe; } if ((pid = fork()) < 0) { warning("Could not fork process"); goto fail_fork; } cap->command_pid = pid; if (!pid) { close(brass[0]); close(copper[1]); close(0); close(1); close(2); dup(copper[0]); dup(brass[1]); dup(brass[1]); execute_command(cap); close(1); exit(0); } close(brass[1]); close(copper[0]); /* these should never be 0 */ if (!brass[1] || !copper[0]) warning("Pipes have zero as file descriptor"); cap->command_input_fd = brass[0]; cap->command_output_fd = copper[1]; /* Do not create a thread under the gdk lock */ gdk_threads_leave(); if (pthread_create(&cap->thread, NULL, monitor_pipes, cap) < 0) warning("Failed to create thread"); else { cap->kill_thread = TRUE; cap->load_file = TRUE; } gdk_threads_enter(); return; fail_fork: close(copper[0]); close(copper[1]); fail_pipe: close(brass[0]); close(brass[1]); } static int trim_plugins(char **plugins) { int len = 0; int i; if (!plugins) return 0; for (i = 0; plugins[i]; i++) { if (is_latency(plugins[i])) continue; plugins[len++] = plugins[i]; } plugins[len] = NULL; return len; } static void file_clicked (GtkWidget *widget, gpointer data) { struct trace_capture *cap = data; gchar *filename; filename = trace_get_file_dialog_filter("Trace File", "Save", TRACE_DIALOG_FILTER_DATA, FALSE); if (!filename) return; gtk_entry_set_text(GTK_ENTRY(cap->file_entry), filename); } static void execute_button_clicked(struct trace_capture *cap) { struct stat st; GtkResponseType ret; const char *filename; char *tracecmd; if (end_capture(cap)) return; tracecmd = find_tracecmd(); if (!tracecmd) { warning("trace-cmd not found in path"); return; } free(tracecmd); filename = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); if (stat(filename, &st) >= 0) { ret = trace_dialog(GTK_WINDOW(cap->main_dialog), TRACE_GUI_ASK, "The file '%s' already exists.\n" "Are you sure you want to replace it", filename); if (ret == GTK_RESPONSE_NO) return; } gtk_button_set_label(GTK_BUTTON(cap->run_button), CAP_STOP); run_command(cap); } static int load_events(struct trace_capture *cap, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node) { struct shark_info *info = cap->info; struct tracecmd_xml_system_node *event_node; struct event_format *event; struct pevent *pevent = cap->pevent; const char *name; int *events = NULL; int event_len = 0; const char *system; const char *event_name; for (node = tracecmd_xml_node_child(node); node; node = tracecmd_xml_node_next(node)) { name = tracecmd_xml_node_type(node); if (strcmp(name, "Event") != 0) continue; event_node = tracecmd_xml_node_child(node); if (!event_node) continue; name = tracecmd_xml_node_type(event_node); if (strcmp(name, "System") != 0) continue; system = tracecmd_xml_node_value(handle, event_node); event_node = tracecmd_xml_node_next(event_node); if (!event_node) continue; name = tracecmd_xml_node_type(event_node); if (strcmp(name, "Name") != 0) continue; event_name = tracecmd_xml_node_value(handle, event_node); event = pevent_find_event_by_name(pevent, system, event_name); if (!event) continue; events = tracecmd_add_id(events, event->id, event_len++); } info->cap_events = events; return 0; } static int load_cap_events(struct trace_capture *cap, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node) { struct shark_info *info = cap->info; const char *name; char **systems = NULL; int sys_len = 0; ks_clear_capture_events(info); for (node = tracecmd_xml_node_child(node); node; node = tracecmd_xml_node_next(node)) { name = tracecmd_xml_node_type(node); if (strcmp(name, "CaptureType") == 0) { name = tracecmd_xml_node_value(handle, node); if (strcmp(name, "all events") == 0) { info->cap_all_events = TRUE; break; } continue; } else if (strcmp(name, "System") == 0) { name = tracecmd_xml_node_value(handle, node); systems = tracecmd_add_list(systems, name, sys_len++); } else if (strcmp(name, "Events") == 0) load_events(cap, handle, node); } info->cap_systems = systems; return 0; } static void load_settings(struct trace_capture *cap, gchar *filename) { struct shark_info *info = cap->info; struct tracecmd_xml_system_node *syschild; struct tracecmd_xml_handle *handle; struct tracecmd_xml_system *system; const char *plugin; const char *name; handle = tracecmd_xml_open(filename); if (!handle) { warning("Could not open %s", filename); return; } system = tracecmd_xml_find_system(handle, "CaptureSettings"); if (!system) goto out; syschild = tracecmd_xml_system_node(system); if (!syschild) goto out_free_sys; g_free(info->cap_plugin); info->cap_plugin = NULL; do { name = tracecmd_xml_node_type(syschild); if (strcmp(name, "Events") == 0) { load_cap_events(cap, handle, syschild); trace_update_event_view(cap->event_view, cap->pevent, NULL, info->cap_all_events, info->cap_systems, info->cap_events); } else if (strcmp(name, "Plugin") == 0) { plugin = tracecmd_xml_node_value(handle, syschild); info->cap_plugin = g_strdup(plugin); } else if (strcmp(name, "Command") == 0) { name = tracecmd_xml_node_value(handle, syschild); gtk_entry_set_text(GTK_ENTRY(cap->command_entry), name); } else if (strcmp(name, "File") == 0) { name = tracecmd_xml_node_value(handle, syschild); gtk_entry_set_text(GTK_ENTRY(cap->file_entry), name); } syschild = tracecmd_xml_node_next(syschild); } while (syschild); set_plugin(cap); out_free_sys: tracecmd_xml_free_system(system); out: tracecmd_xml_close(handle); } static void settings_changed(GtkComboBox *combo, gpointer data) { struct trace_capture *cap = data; GtkTreeModel *model; GtkTreeIter iter; gchar *text; model = gtk_combo_box_get_model(combo); if (!model) return; if (!gtk_combo_box_get_active_iter(combo, &iter)) return; gtk_tree_model_get(model, &iter, 1, &text, -1); if (text && strlen(text)) load_settings(cap, text); g_free(text); } static void import_settings_clicked(GtkWidget *widget, gpointer data) { struct trace_capture *cap = data; gchar *filename; filename = trace_get_file_dialog_filter("Import Settings", NULL, TRACE_DIALOG_FILTER_SETTING, FALSE); if (!filename) return; load_settings(cap, filename); g_free(filename); } static void save_events(struct trace_capture *cap, struct tracecmd_xml_handle *handle) { struct pevent *pevent = cap->pevent; struct event_format *event; char **systems = cap->info->cap_systems; int *events = cap->info->cap_events; int i; tracecmd_xml_write_element(handle, "CaptureType", "Events"); for (i = 0; systems && systems[i]; i++) tracecmd_xml_write_element(handle, "System", systems[i]); if (!events || events[0] < 0) return; tracecmd_xml_start_sub_system(handle, "Events"); for (i = 0; events[i] > 0; i++) { event = pevent_find_event(pevent, events[i]); if (event) { tracecmd_xml_start_sub_system(handle, "Event"); tracecmd_xml_write_element(handle, "System", event->system); tracecmd_xml_write_element(handle, "Name", event->name); tracecmd_xml_end_sub_system(handle); } } tracecmd_xml_end_sub_system(handle); } static void save_settings(struct trace_capture *cap, const char *filename) { struct shark_info *info = cap->info; struct tracecmd_xml_handle *handle; const char *file; const char *command; handle = tracecmd_xml_create(filename, VERSION_STRING); if (!handle) { warning("Could not create %s", filename); return; } update_events(cap); tracecmd_xml_start_system(handle, "CaptureSettings"); tracecmd_xml_start_sub_system(handle, "Events"); if (info->cap_all_events) tracecmd_xml_write_element(handle, "CaptureType", "all events"); else if ((info->cap_systems && info->cap_systems[0]) || (info->cap_events && info->cap_events[0] >= 0)) { save_events(cap, handle); } tracecmd_xml_end_sub_system(handle); update_plugin(cap); if (info->cap_plugin) tracecmd_xml_write_element(handle, "Plugin", info->cap_plugin); command = gtk_entry_get_text(GTK_ENTRY(cap->command_entry)); if (command && strlen(command) && !is_just_ws(command)) tracecmd_xml_write_element(handle, "Command", command); file = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); if (file && strlen(file) && !is_just_ws(file)) tracecmd_xml_write_element(handle, "File", file); tracecmd_xml_end_system(handle); tracecmd_xml_close(handle); } static void save_settings_clicked(GtkWidget *widget, gpointer data) { struct trace_capture *cap = data; struct stat st; GtkWidget *dialog; GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; GString *file; const char *name; gint result; int ret; dialog = gtk_dialog_new_with_buttons("Save Settings", NULL, GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Settings Name: "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); gtk_widget_show(entry); again: result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: name = gtk_entry_get_text(GTK_ENTRY(entry)); if (!name || is_just_ws(name)) { warning("Must enter a name"); goto again; } /* Make sure home settings exists */ if (create_home_settings() < 0) break; file = get_home_settings_new(); g_string_append_printf(file, "/%s.kss", name); ret = stat(file->str, &st); if (ret >= 0) { ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK, "The setting '%s' already exists.\n" "Are you sure you want to replace it", name); if (ret == GTK_RESPONSE_NO) { g_string_free(file, TRUE); goto again; } } save_settings(cap, file->str); refresh_settings(cap); g_free(cap->info->cap_settings_name); cap->info->cap_settings_name = g_strdup(name); set_settings(cap); g_string_free(file, TRUE); break; case GTK_RESPONSE_REJECT: break; default: break; }; gtk_widget_destroy(dialog); } static void export_settings_clicked(GtkWidget *widget, gpointer data) { struct trace_capture *cap = data; gchar *filename; filename = trace_get_file_dialog_filter("Save Settings", "Save", TRACE_DIALOG_FILTER_SETTING, TRUE); if (!filename) return; save_settings(cap, filename); g_free(filename); } static GtkTreeModel *create_plugin_combo_model(gpointer data) { char **plugins = data; GtkListStore *list; GtkTreeIter iter; int i; list = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, PLUGIN_NONE, -1); for (i = 0; plugins && plugins[i]; i++) { gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, plugins[i], -1); } return GTK_TREE_MODEL(list); } static void insert_text(GtkEditable *buffer, gchar *new_text, gint new_text_length, gint *position, gpointer data) { int i; guint sigid; /* Only allow 0-9 to be written to the entry */ for (i = 0; i < new_text_length; i++) { if (new_text[i] < '0' || new_text[i] > '9') { sigid = g_signal_lookup("insert-text", G_OBJECT_TYPE(buffer)); g_signal_stop_emission(buffer, sigid, 0); return; } } } /* * Trace Capture Dialog Window * * +--------------------------------------------------------------------+ * | Dialog Window | * | +-------------------------------+-------------------------------+ | * | | Paned Window | +---------------------------+ | | * | | +---------------------------+ | | Scroll window | | | * | | | Hbox | | | +-----------------------+ | | | * | | | Label Plugin Combo | | | | Event Tree | | | | * | | +---------------------------+ | | | | | | | * | | | | | | | | | * | | | | +-----------------------+ | | | * | | | +---------------------------+ | | * | +-------------------------------+-------------------------------+ | * +--------------------------------------------------------------------+ */ static void tracing_dialog(struct shark_info *info, const char *tracing) { struct pevent *pevent; GtkWidget *dialog; GtkWidget *button; GtkWidget *combo; GtkWidget *label; GtkWidget *entry; GtkWidget *frame; GtkWidget *vbox; GtkWidget *scrollwin; GtkWidget *table; GtkWidget *table2; GtkWidget *event_tree; GtkWidget *viewport; GtkWidget *textview; GtkWidget *hbox; GtkTextBuffer *buffer; GtkTextIter start_iter; GtkTextIter end_iter; char **plugins; int nr_plugins; struct trace_capture cap; const gchar *file; const char *command; const char *val; GString *str; gint result; memset(&cap, 0, sizeof(cap)); cap.info = info; plugins = tracecmd_local_plugins(tracing); /* Skip latency plugins */ nr_plugins = trim_plugins(plugins); if (!nr_plugins && plugins) { tracecmd_free_list(plugins); plugins = NULL; } /* Send parse warnings to status display */ trace_dialog_register_alt_warning(vpr_stat); pevent = tracecmd_local_events(tracing); trace_dialog_register_alt_warning(NULL); cap.pevent = pevent; if (!pevent && !nr_plugins) { warning("No events or plugins found"); return; } dialog = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(dialog), "Capture"); button = gtk_button_new_with_label("Run"); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_ACCEPT); gtk_widget_show(button); cap.run_button = button; gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_REJECT); cap.main_dialog = dialog; /* --- Top Level Hpaned --- */ table = gtk_table_new(4, 2, FALSE); /* It is possible that no pevents exist. */ if (pevent) { scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_table_attach(GTK_TABLE(table), scrollwin, 0, 1, 1, 2, GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0); gtk_widget_show(scrollwin); event_tree = trace_create_event_list_view(pevent, NULL, cap.info->cap_all_events, cap.info->cap_systems, cap.info->cap_events); gtk_container_add(GTK_CONTAINER(scrollwin), event_tree); gtk_widget_show(event_tree); cap.event_view = event_tree; } else { /* No events */ label = gtk_label_new("No events enabled on system"); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 10); gtk_widget_show(label); cap.event_view = NULL; } gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0); gtk_widget_show(table); /*------------------ Frame Settings --------------------------- */ frame = gtk_frame_new("Settings"); gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1, GTK_FILL, 0, 0, 10); gtk_widget_show(frame); table2 = gtk_table_new(2, 3, FALSE); gtk_container_add(GTK_CONTAINER(frame), table2); gtk_widget_show(table2); gtk_table_set_col_spacings(GTK_TABLE(table2), 5); button = gtk_button_new_with_label("Save Settings"); gtk_table_attach_defaults(GTK_TABLE(table2), button, 0, 1, 0, 1); gtk_widget_show(button); g_signal_connect (button, "clicked", G_CALLBACK (save_settings_clicked), (gpointer)&cap); button = gtk_button_new_with_label("Import Settings"); gtk_table_attach_defaults(GTK_TABLE(table2), button, 1, 2, 0, 1); gtk_widget_show(button); g_signal_connect (button, "clicked", G_CALLBACK (import_settings_clicked), (gpointer)&cap); button = gtk_button_new_with_label("Export Settings"); gtk_table_attach_defaults(GTK_TABLE(table2), button, 2, 3, 0, 1); gtk_widget_show(button); g_signal_connect (button, "clicked", G_CALLBACK (export_settings_clicked), (gpointer)&cap); if (cap.info->cap_settings_name) set_settings(&cap); label = gtk_label_new("Available Settings: "); gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 1, 2); gtk_widget_show(label); combo = trace_create_combo_box(NULL, NULL, create_settings_model, NULL); gtk_table_attach_defaults(GTK_TABLE(table2), combo, 1, 3, 1, 2); cap.settings_combo = combo; g_signal_connect (combo, "changed", G_CALLBACK (settings_changed), (gpointer)&cap); /*------------------ Frame Settings --------------------------- */ frame = gtk_frame_new("Execute"); gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 3, 4, GTK_FILL, 0, 0, 10); gtk_widget_show(frame); table2 = gtk_table_new(3, 3, FALSE); gtk_container_add(GTK_CONTAINER(frame), table2); gtk_widget_show(table2); label = gtk_label_new("Plugin: "); gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 0, 1); gtk_widget_show(label); combo = trace_create_combo_box(NULL, NULL, create_plugin_combo_model, plugins); cap.plugin_combo = combo; gtk_table_attach_defaults(GTK_TABLE(table2), combo, 1, 3, 0, 1); if (cap.info->cap_plugin) set_plugin(&cap); label = gtk_label_new("Command:"); gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 1, 2); gtk_widget_show(label); entry = gtk_entry_new(); gtk_table_attach_defaults(GTK_TABLE(table2), entry, 1, 3, 1, 2); gtk_widget_show(entry); cap.command_entry = entry; if (cap.info->cap_command) gtk_entry_set_text(GTK_ENTRY(entry), cap.info->cap_command); label = gtk_label_new("Output file: "); gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 2, 3); gtk_widget_show(label); entry = gtk_entry_new(); gtk_table_attach_defaults(GTK_TABLE(table2), entry, 1, 2, 2, 3); gtk_widget_show(entry); if (cap.info->cap_file) file = cap.info->cap_file; else file = default_output_file; gtk_entry_set_text(GTK_ENTRY(entry), file); cap.file_entry = entry; button = gtk_button_new_with_label("Browse"); gtk_table_attach_defaults(GTK_TABLE(table2), button, 2, 3, 2, 3); gtk_widget_show(button); g_signal_connect (button, "clicked", G_CALLBACK (file_clicked), (gpointer)&cap); /*------------------ Command Output ------------------ */ vbox = gtk_vbox_new(FALSE, 0); gtk_table_attach_defaults(GTK_TABLE(table), vbox, 1, 2, 0, 4); gtk_widget_show(vbox); gtk_widget_set_size_request(GTK_WIDGET(vbox), 500, 0); label = gtk_label_new("Output Display:"); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); gtk_widget_show(label); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0); gtk_widget_show(scrollwin); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrollwin), viewport); textview = gtk_text_view_new(); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); gtk_container_add(GTK_CONTAINER(viewport), textview); gtk_widget_show(textview); cap.output_text = textview; cap.output_buffer = buffer; /* set the buffer from its previous setting */ if (info->cap_buffer_output) gtk_text_buffer_set_text(buffer, info->cap_buffer_output, strlen(info->cap_buffer_output)); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Max # of characters in output display: "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0); gtk_widget_show(entry); cap.max_num_entry = entry; if (!info->cap_max_buf_size) info->cap_max_buf_size = DEFAULT_MAX_BUF_SIZE; str = g_string_new(""); g_string_append_printf(str, "%d", info->cap_max_buf_size); gtk_entry_set_text(GTK_ENTRY(entry), str->str); g_string_free(str, TRUE); g_signal_connect (entry, "insert-text", G_CALLBACK (insert_text), (gpointer)&cap); gtk_widget_set_size_request(GTK_WIDGET(dialog), DIALOG_WIDTH, DIALOG_HEIGHT); gtk_widget_show(dialog); cont: result = gtk_dialog_run(GTK_DIALOG(dialog)); if (result == GTK_RESPONSE_ACCEPT) { execute_button_clicked(&cap); goto cont; } /* Make sure no capture is running */ end_capture(&cap); /* Get the max buffer size */ val = gtk_entry_get_text(GTK_ENTRY(entry)); info->cap_max_buf_size = atoi(val); gtk_text_buffer_get_start_iter(cap.output_buffer, &start_iter); gtk_text_buffer_get_end_iter(cap.output_buffer, &end_iter); g_free(info->cap_buffer_output); info->cap_buffer_output = gtk_text_buffer_get_text(cap.output_buffer, &start_iter, &end_iter, FALSE); /* save the plugin and file to reuse if we come back */ update_plugin(&cap); free(info->cap_file); cap.info->cap_file = strdup(gtk_entry_get_text(GTK_ENTRY(cap.file_entry))); free(info->cap_command); command = gtk_entry_get_text(GTK_ENTRY(cap.command_entry)); if (command && strlen(command) && !is_just_ws(command)) cap.info->cap_command = strdup(command); else cap.info->cap_command = NULL; update_events(&cap); gtk_widget_destroy(dialog); if (pevent) pevent_free(pevent); if (plugins) tracecmd_free_list(plugins); } void tracecmd_capture_clicked(gpointer data) { struct shark_info *info = data; char *tracing; tracing = tracecmd_get_tracing_dir(); if (!tracing) { warning("Can not find or mount tracing directory!\n" "Either tracing is not configured for this kernel\n" "or you do not have the proper permissions to mount the directory"); return; } tracing_dialog(info, tracing); } trace-cmd-2.5.3/trace-capture.h000066400000000000000000000017641246701203100162740ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_CAPTURE_H #define _TRACE_CAPTURE_H #include "kernel-shark.h" void tracecmd_capture_clicked(gpointer data); #endif /* _TRACE_CAPTURE_H */ trace-cmd-2.5.3/trace-cmd-local.h000066400000000000000000000026771246701203100164700ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_CMD_LOCAL_H #define _TRACE_CMD_LOCAL_H /* Local for trace-input.c and trace-output.c */ #include "trace-cmd.h" static ssize_t __do_write(int fd, const void *data, size_t size) { ssize_t tot = 0; ssize_t w; do { w = write(fd, data, size - tot); tot += w; if (!w) break; if (w < 0) return w; } while (tot != size); return tot; } static ssize_t __do_write_check(int fd, const void *data, size_t size) { ssize_t ret; ret = __do_write(fd, data, size); if (ret < 0) return ret; if (ret != size) return -1; return 0; } #endif /* _TRACE_CMD_LOCAL_H */ trace-cmd-2.5.3/trace-cmd.c000066400000000000000000000360501246701203100153630ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include "trace-local.h" int silence_warnings; int show_status; void warning(const char *fmt, ...) { va_list ap; if (silence_warnings) return; if (errno) perror("trace-cmd"); errno = 0; va_start(ap, fmt); fprintf(stderr, " "); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } void pr_stat(const char *fmt, ...) { va_list ap; if (!show_status) return; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); } void *malloc_or_die(unsigned int size) { void *data; data = malloc(size); if (!data) die("malloc"); return data; } static void dump_file_content(const char *path) { char buf[BUFSIZ]; ssize_t n; FILE *fp; fp = fopen(path, "r"); if (!fp) die("reading %s", path); do { n = fread(buf, 1, BUFSIZ, fp); if (n > 0) fwrite(buf, 1, n, stdout); } while (n > 0); fclose(fp); } void show_file(const char *name) { char *path; path = tracecmd_get_tracing_file(name); dump_file_content(path); tracecmd_put_tracing_file(path); } typedef int (*process_file_func)(char *buf, int len); static void process_file_re(process_file_func func, const char *name, const char *re) { regex_t reg; char *path; char *buf = NULL; char *str; FILE *fp; ssize_t n; size_t l = strlen(re); /* Just in case :-p */ if (!re || l == 0) { show_file(name); return; } /* Handle the newline at end of names for the user */ str = malloc_or_die(l + 3); strcpy(str, re); if (re[l-1] == '$') strcpy(&str[l-1], "\n*$"); if (regcomp(®, str, REG_ICASE|REG_NOSUB)) die("invalid function regex '%s'", re); free(str); path = tracecmd_get_tracing_file(name); fp = fopen(path, "r"); if (!fp) die("reading %s", path); tracecmd_put_tracing_file(path); do { n = getline(&buf, &l, fp); if (n > 0 && regexec(®, buf, 0, NULL, 0) == 0) func(buf, n); } while (n > 0); free(buf); fclose(fp); regfree(®); } static int show_file_write(char *buf, int len) { return fwrite(buf, 1, len, stdout); } static void show_file_re(const char *name, const char *re) { process_file_re(show_file_write, name, re); } static char *get_event_file(const char *type, char *buf, int len) { char *system; char *event; char *path; char *file; if (buf[len-1] == '\n') buf[len-1] = '\0'; system = strtok(buf, ":"); if (!system) die("no system found in %s", buf); event = strtok(NULL, ":"); if (!event) die("no event found in %s\n", buf); path = tracecmd_get_tracing_file("events"); file = malloc_or_die(strlen(path) + strlen(system) + strlen(event) + strlen(type) + strlen("///") + 1); sprintf(file, "%s/%s/%s/%s", path, system, event, type); tracecmd_put_tracing_file(path); return file; } static int event_filter_write(char *buf, int len) { char *file; if (buf[len-1] == '\n') buf[len-1] = '\0'; printf("%s\n", buf); file = get_event_file("filter", buf, len); dump_file_content(file); free(file); printf("\n"); return 0; } static int event_trigger_write(char *buf, int len) { char *file; if (buf[len-1] == '\n') buf[len-1] = '\0'; printf("%s\n", buf); file = get_event_file("trigger", buf, len); dump_file_content(file); free(file); printf("\n"); return 0; } static int event_format_write(char *fbuf, int len) { char *file = get_event_file("format", fbuf, len); char *buf = NULL; size_t l; FILE *fp; int n; /* The get_event_file() crops system in fbuf */ printf("system: %s\n", fbuf); /* Don't print the print fmt, it's ugly */ fp = fopen(file, "r"); if (!fp) die("reading %s", file); do { n = getline(&buf, &l, fp); if (n > 0) { if (strncmp(buf, "print fmt", 9) == 0) break; fwrite(buf, 1, n, stdout); } } while (n > 0); fclose(fp); free(buf); free(file); return 0; } static void show_event_filter_re(const char *re) { process_file_re(event_filter_write, "available_events", re); } static void show_event_trigger_re(const char *re) { process_file_re(event_trigger_write, "available_events", re); } static void show_event_format_re(const char *re) { process_file_re(event_format_write, "available_events", re); } void show_instance_file(struct buffer_instance *instance, const char *name) { char *path; path = get_instance_file(instance, name); dump_file_content(path); tracecmd_put_tracing_file(path); } enum { SHOW_EVENT_FORMAT = 1 << 0, SHOW_EVENT_FILTER = 1 << 1, SHOW_EVENT_TRIGGER = 1 << 2, }; static void show_events(const char *eventre, int flags) { if (flags && !eventre) die("When specifying event files, an event must be named"); if (eventre) { if (flags & SHOW_EVENT_FORMAT) show_event_format_re(eventre); else if (flags & SHOW_EVENT_FILTER) show_event_filter_re(eventre); else if (flags & SHOW_EVENT_TRIGGER) show_event_trigger_re(eventre); else show_file_re("available_events", eventre); } else show_file("available_events"); } static void show_tracers(void) { show_file("available_tracers"); } static void show_options(void) { show_file("trace_options"); } static void show_clocks(void) { show_file("trace_clock"); } static void show_functions(const char *funcre) { if (funcre) show_file_re("available_filter_functions", funcre); else show_file("available_filter_functions"); } static void show_buffers(void) { struct dirent *dent; DIR *dir; char *path; int printed = 0; path = tracecmd_get_tracing_file("instances"); dir = opendir(path); tracecmd_put_tracing_file(path); if (!dir) die("Can not read instance directory"); while ((dent = readdir(dir))) { const char *name = dent->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; printf("%s\n", name); printed = 1; } closedir(dir); if (!printed) printf("No buffer instances defined\n"); } static void show_plugins(void) { struct pevent *pevent; struct plugin_list *list; struct trace_seq s; pevent = pevent_alloc(); if (!pevent) die("Can not allocate pevent\n"); trace_seq_init(&s); list = tracecmd_load_plugins(pevent); trace_util_print_plugins(&s, " ", "\n", list); trace_seq_do_printf(&s); tracecmd_unload_plugins(list, pevent); pevent_free(pevent); } static void show_plugin_options(void) { struct pevent *pevent; struct plugin_list *list; struct trace_seq s; tracecmd_ftrace_load_options(); pevent = pevent_alloc(); if (!pevent) die("Can not allocate pevent\n"); trace_seq_init(&s); list = tracecmd_load_plugins(pevent); trace_util_print_plugin_options(&s); trace_seq_do_printf(&s); tracecmd_unload_plugins(list, pevent); pevent_free(pevent); } enum { OPT_tracing_on = 255, OPT_current_tracer = 254, OPT_buffer_size_kb = 253, OPT_buffer_total_size_kb = 252, OPT_ftrace_filter = 251, OPT_ftrace_notrace = 250, OPT_ftrace_pid = 249, OPT_graph_function = 248, OPT_graph_notrace = 247, OPT_cpumask = 246, }; int main (int argc, char **argv) { int c; errno = 0; if (argc < 2) usage(argv); if (strcmp(argv[1], "report") == 0) { trace_report(argc, argv); exit(0); } else if (strcmp(argv[1], "snapshot") == 0) { trace_snapshot(argc, argv); exit(0); } else if (strcmp(argv[1], "hist") == 0) { trace_hist(argc, argv); exit(0); } else if (strcmp(argv[1], "mem") == 0) { trace_mem(argc, argv); exit(0); } else if (strcmp(argv[1], "listen") == 0) { trace_listen(argc, argv); exit(0); } else if (strcmp(argv[1], "split") == 0) { trace_split(argc, argv); exit(0); } else if (strcmp(argv[1], "restore") == 0) { trace_restore(argc, argv); exit(0); } else if (strcmp(argv[1], "stack") == 0) { trace_stack(argc, argv); exit(0); } else if (strcmp(argv[1], "check-events") == 0) { const char *tracing; int ret; struct pevent *pevent = NULL; struct plugin_list *list = NULL; while ((c = getopt(argc-1, argv+1, "+hN")) >= 0) { switch (c) { case 'h': default: usage(argv); break; case 'N': tracecmd_disable_plugins = 1; break; } } tracing = tracecmd_get_tracing_dir(); if (!tracing) { printf("Can not find or mount tracing directory!\n" "Either tracing is not configured for this " "kernel\n" "or you do not have the proper permissions to " "mount the directory"); exit(EINVAL); } pevent = pevent_alloc(); if (!pevent) exit(EINVAL); list = tracecmd_load_plugins(pevent); ret = tracecmd_fill_local_events(tracing, pevent); if (ret || pevent->parsing_failures) ret = EINVAL; tracecmd_unload_plugins(list, pevent); pevent_free(pevent); exit(ret); } else if (strcmp(argv[1], "record") == 0 || strcmp(argv[1], "start") == 0 || strcmp(argv[1], "extract") == 0 || strcmp(argv[1], "stop") == 0 || strcmp(argv[1], "stream") == 0 || strcmp(argv[1], "profile") == 0 || strcmp(argv[1], "restart") == 0 || strcmp(argv[1], "reset") == 0) { trace_record(argc, argv); exit(0); } else if (strcmp(argv[1], "stat") == 0) { trace_stat(argc, argv); exit(0); } else if (strcmp(argv[1], "options") == 0) { show_plugin_options(); exit(0); } else if (strcmp(argv[1], "show") == 0) { const char *buffer = NULL; const char *file = "trace"; const char *cpu = NULL; struct buffer_instance *instance = &top_instance; char cpu_path[128]; char *path; int snap = 0; int pipe = 0; int show_name = 0; int option_index = 0; int stop = 0; static struct option long_options[] = { {"tracing_on", no_argument, NULL, OPT_tracing_on}, {"current_tracer", no_argument, NULL, OPT_current_tracer}, {"buffer_size", no_argument, NULL, OPT_buffer_size_kb}, {"buffer_total_size", no_argument, NULL, OPT_buffer_total_size_kb}, {"ftrace_filter", no_argument, NULL, OPT_ftrace_filter}, {"ftrace_notrace", no_argument, NULL, OPT_ftrace_notrace}, {"ftrace_pid", no_argument, NULL, OPT_ftrace_pid}, {"graph_function", no_argument, NULL, OPT_graph_function}, {"graph_notrace", no_argument, NULL, OPT_graph_notrace}, {"cpumask", no_argument, NULL, OPT_cpumask}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; while ((c = getopt_long(argc-1, argv+1, "B:c:fsp", long_options, &option_index)) >= 0) { switch (c) { case 'h': usage(argv); break; case 'B': if (buffer) die("Can only show one buffer at a time"); buffer = optarg; instance = create_instance(optarg); break; case 'c': if (cpu) die("Can only show one CPU at a time"); cpu = optarg; break; case 'f': show_name = 1; break; case 's': snap = 1; if (pipe) die("Can not have -s and -p together"); break; case 'p': pipe = 1; if (snap) die("Can not have -s and -p together"); break; case OPT_tracing_on: show_instance_file(instance, "tracing_on"); stop = 1; break; case OPT_current_tracer: show_instance_file(instance, "current_tracer"); stop = 1; break; case OPT_buffer_size_kb: show_instance_file(instance, "buffer_size_kb"); stop = 1; break; case OPT_buffer_total_size_kb: show_instance_file(instance, "buffer_total_size_kb"); stop = 1; break; case OPT_ftrace_filter: show_instance_file(instance, "set_ftrace_filter"); stop = 1; break; case OPT_ftrace_notrace: show_instance_file(instance, "set_ftrace_notrace"); stop = 1; break; case OPT_ftrace_pid: show_instance_file(instance, "set_ftrace_pid"); stop = 1; break; case OPT_graph_function: show_instance_file(instance, "set_graph_function"); stop = 1; break; case OPT_graph_notrace: show_instance_file(instance, "set_graph_notrace"); stop = 1; break; case OPT_cpumask: show_instance_file(instance, "tracing_cpumask"); stop = 1; break; default: usage(argv); } } if (stop) exit(0); if (pipe) file = "trace_pipe"; else if (snap) file = "snapshot"; if (cpu) { snprintf(cpu_path, 128, "per_cpu/cpu%d/%s", atoi(cpu), file); file = cpu_path; } if (buffer) { path = malloc_or_die(strlen(buffer) + strlen("instances//") + strlen(file) + 1); sprintf(path, "instances/%s/%s", buffer, file); file = path; } if (show_name) { char *name; name = tracecmd_get_tracing_file(file); printf("%s\n", name); tracecmd_put_tracing_file(name); } show_file(file); if (buffer) free(path); exit(0); } else if (strcmp(argv[1], "list") == 0) { int events = 0; int tracer = 0; int options = 0; int funcs = 0; int buffers = 0; int clocks = 0; int plug = 0; int plug_op = 0; int flags = 0; int show_all = 1; int i; const char *arg; const char *funcre = NULL; const char *eventre = NULL; for (i = 2; i < argc; i++) { arg = NULL; if (argv[i][0] == '-') { if (i < argc - 1) { if (argv[i+1][0] != '-') arg = argv[i+1]; } switch (argv[i][1]) { case 'h': usage(argv); break; case 'e': events = 1; eventre = arg; show_all = 0; break; case 'B': buffers = 1; show_all = 0; break; case 'C': clocks = 1; show_all = 0; break; case 'F': flags |= SHOW_EVENT_FORMAT; break; case 'R': flags |= SHOW_EVENT_TRIGGER; break; case 'l': flags |= SHOW_EVENT_FILTER; break; case 'p': case 't': tracer = 1; show_all = 0; break; case 'P': plug = 1; show_all = 0; break; case 'O': plug_op = 1; show_all = 0; break; case 'o': options = 1; show_all = 0; break; case 'f': funcs = 1; funcre = arg; show_all = 0; break; default: fprintf(stderr, "list: invalid option -- '%c'\n", argv[optind][1]); usage(argv); } } } if (events) show_events(eventre, flags); if (tracer) show_tracers(); if (options) show_options(); if (plug) show_plugins(); if (plug_op) show_plugin_options(); if (funcs) show_functions(funcre); if (buffers) show_buffers(); if (clocks) show_clocks(); if (show_all) { printf("events:\n"); show_events(NULL, 0); printf("\ntracers:\n"); show_tracers(); printf("\noptions:\n"); show_options(); } exit(0); } else if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "help") == 0) { usage(argv); } else { fprintf(stderr, "unknown command: %s\n", argv[1]); usage(argv); } return 0; } trace-cmd-2.5.3/trace-cmd.h000066400000000000000000000276471246701203100154040ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_CMD_H #define _TRACE_CMD_H #include #include "event-utils.h" #include "event-parse.h" #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 parse_cmdlines(struct pevent *pevent, char *file, int size); void parse_trace_clock(struct pevent *pevent, char *file, int size); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); extern int tracecmd_disable_sys_plugins; extern int tracecmd_disable_plugins; struct plugin_list; struct plugin_list *tracecmd_load_plugins(struct pevent *pevent); void tracecmd_unload_plugins(struct plugin_list *list, struct pevent *pevent); char **tracecmd_event_systems(const char *tracing_dir); char **tracecmd_system_events(const char *tracing_dir, const char *system); struct pevent *tracecmd_local_events(const char *tracing_dir); int tracecmd_fill_local_events(const char *tracing_dir, struct pevent *pevent); 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 pevent_record *record); void free_record(struct pevent_record *record); struct tracecmd_input; struct tracecmd_output; struct tracecmd_recorder; 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, }; enum { TRACECMD_FL_IGNORE_DATE = (1 << 0), TRACECMD_FL_BUFFER_INSTANCE = (1 << 1), }; struct tracecmd_ftrace { struct tracecmd_input *handle; struct event_format *fgraph_ret_event; int fgraph_ret_id; int long_size; }; 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_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); 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_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); struct pevent_record * tracecmd_read_page_record(struct pevent *pevent, void *page, int size, struct pevent_record *last_record); struct pevent_record * tracecmd_peek_data(struct tracecmd_input *handle, int cpu); static inline struct pevent_record * tracecmd_peek_data_ref(struct tracecmd_input *handle, int cpu) { struct pevent_record *rec = tracecmd_peek_data(handle, cpu); if (rec) rec->ref_count++; return rec; } struct pevent_record * tracecmd_read_data(struct tracecmd_input *handle, int cpu); struct pevent_record * tracecmd_read_prev(struct tracecmd_input *handle, struct pevent_record *record); struct pevent_record * tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu); struct pevent_record * tracecmd_read_at(struct tracecmd_input *handle, unsigned long long offset, int *cpu); struct pevent_record * tracecmd_translate_data(struct tracecmd_input *handle, void *ptr, int size); struct pevent_record * tracecmd_read_cpu_first(struct tracecmd_input *handle, int cpu); struct pevent_record * tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu); int tracecmd_refresh_record(struct tracecmd_input *handle, struct pevent_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 pevent *tracecmd_get_pevent(struct tracecmd_input *handle); bool tracecmd_get_use_trace_clock(struct tracecmd_input *handle); 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 pevent_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_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_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 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_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); int tracecmd_attach_cpu_data(char *file, int cpus, char * const *cpu_data_files); int tracecmd_attach_cpu_data_fd(int fd, int cpus, char * const *cpu_data_files); /* --- 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); struct tracecmd_recorder *tracecmd_create_buffer_recorder_pipe(const char *file, int cpu, unsigned flags, const char *buffer, int *brass); 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); /* --- Plugin handling --- */ extern struct plugin_option trace_ftrace_options[]; void trace_util_add_options(const char *name, struct plugin_option *options); void trace_util_remove_options(struct plugin_option *options); void trace_util_add_option(const char *name, const char *val); void trace_util_load_plugins(struct pevent *pevent, const char *suffix, void (*load_plugin)(struct pevent *pevent, const char *path, const char *name, void *data), void *data); struct plugin_option *trace_util_read_plugin_options(void); void trace_util_free_options(struct 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 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); #endif /* _TRACE_CMD_H */ trace-cmd-2.5.3/trace-compat.c000066400000000000000000000062101246701203100160760ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include "trace-compat.h" #include "trace-gui.h" #include "trace-cmd.h" #include #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) #warning Using compat functions for older GTK library. This should work fine #warning but when you get a chance, please upgrade your GTK to at least 2.18 void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, gint *xpad, gint *ypad) { if (xpad) *xpad = cell->xpad; if (ypad) *ypad = cell->ypad; } #endif /* version < 2.18.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,16,0) const gchar *gtk_menu_item_get_label(GtkMenuItem *menu_item) { g_return_val_if_fail(GTK_IS_MENU_ITEM(menu_item), NULL); if (GTK_IS_LABEL(GTK_BIN(menu_item)->child)) return gtk_label_get_label(GTK_LABEL(GTK_BIN(menu_item)->child)); return NULL; } void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label) { g_return_if_fail(GTK_IS_MENU_ITEM(menu_item)); if (GTK_IS_LABEL(GTK_BIN(menu_item)->child)) { gtk_label_set_label(GTK_LABEL(GTK_BIN(menu_item)->child), label ? label : ""); } } #endif /* version < 2.18.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj) { return adj->page_size; } gdouble gtk_adjustment_get_upper(GtkAdjustment *adj) { return adj->upper; } gdouble gtk_adjustment_get_lower(GtkAdjustment *adj) { return adj->lower; } gboolean gtk_show_uri(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error) { return FALSE; } void g_string_vprintf(GString *string, const gchar *format, va_list args) { char buf[1024]; gint len; len = vsnprintf(buf, 1024, format, args); if (len >= 1024) die("compat g_string_vprintf can not process length of %d\n", len); g_string_printf(string, "%s", buf); } #endif /* version < 2.14.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) GtkWidget *gtk_tree_view_column_get_tree_view(GtkTreeViewColumn *col) { return col->tree_view; } void gtk_widget_set_tooltip_text(GtkWidget *widget, const gchar *text) { static GtkTooltips *tooltips; /* Only works for widgets with windows, sorry */ if (GTK_WIDGET_NO_WINDOW(widget)) return; if (!tooltips) { tooltips = gtk_tooltips_new(); gtk_tooltips_enable(tooltips); } gtk_tooltips_set_tip(GTK_TOOLTIPS(tooltips), widget, text, text); } #endif /* version < 2.12.0 */ trace-cmd-2.5.3/trace-compat.h000066400000000000000000000040541246701203100161070ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_COMPAT_H #define _TRACE_COMPAT_H #include #include #define CALC_GTK_VERSION(maj, min, ext) ((maj << 16) + (min << 8) + ext) #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, gint *xpad, gint *ypad); #endif /* version < 2.18.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,16,0) const gchar *gtk_menu_item_get_label(GtkMenuItem *menu_item); void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label); #endif /* version < 2.16.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj); gdouble gtk_adjustment_get_upper(GtkAdjustment *adj); gdouble gtk_adjustment_get_lower(GtkAdjustment *adj); gboolean gtk_show_uri(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error); void g_string_vprintf(GString *string, const gchar *format, va_list args); #endif /* version < 2.14.0 */ #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) GtkWidget *gtk_tree_view_column_get_tree_view(GtkTreeViewColumn *col); void gtk_widget_set_tooltip_text(GtkWidget *widget, const gchar *text); #endif /* version < 2.12.0 */ #endif /* _TRACE_COMPAT_H */ trace-cmd-2.5.3/trace-dialog.c000066400000000000000000000313101246701203100160510ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include "trace-compat.h" #include "trace-cmd.h" #include "trace-gui.h" #define DIALOG_WIDTH 500 #define DIALOG_HEIGHT 550 static GtkWidget *statusbar; static GtkWidget *statuspix; static GString *statusstr; static GtkWidget *parent_window; static void (*alt_warning)(const char *fmt, va_list ap); void vpr_stat(const char *fmt, va_list ap) { GString *str; if (!statusstr) { statusstr = g_string_new(""); if (!statusstr) die("Allocating status string"); } str = g_string_new(""); g_string_vprintf(str, fmt, ap); g_string_append_printf(statusstr, "%s\n", str->str); if (statusbar) { gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, str->str); gtk_widget_show(statuspix); } g_string_free(str, TRUE); } void pr_stat(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vpr_stat(fmt, ap); va_end(ap); } /** * trace_dialog_register_window - register window for warning dialogs * @window: parent window to use for other dialogs * * The warning messages do not have a way to pass the window to * the function, since these functions are also used by the command * line interface. This allows an application to give the warning * messages a window to use. */ void trace_dialog_register_window(GtkWidget *window) { parent_window = window; } static struct cursor_stack { struct cursor_stack *next; GdkCursor *cursor; } *cursor_stack; static void push_cursor(GdkCursor *cursor) { struct cursor_stack *item; item = malloc_or_die(sizeof(item)); item->next = cursor_stack; cursor_stack = item; item->cursor = cursor; } static GdkCursor *pop_cursor(void) { struct cursor_stack *item; GdkCursor *cursor; item = cursor_stack; if (!item) return NULL; cursor_stack = item->next; cursor = item->cursor; free(item); return cursor; } void trace_set_cursor(GdkCursorType type) { GdkWindow *window; GdkCursor *cursor; if (!parent_window) return; window = GTK_WIDGET(parent_window)->window; /* save the previous cursor */ cursor = gdk_window_get_cursor(window); push_cursor(cursor); cursor = gdk_cursor_new(type); if (!cursor) die("Can't create cursor"); gdk_window_set_cursor(window, cursor); } void trace_put_cursor(void) { GdkWindow *window; GdkCursor *cursor; if (!parent_window) return; window = GTK_WIDGET(parent_window)->window; cursor = gdk_window_get_cursor(window); if (cursor) gdk_cursor_unref(cursor); cursor = pop_cursor(); gdk_window_set_cursor(window, cursor); } void trace_freeze_all(void) { if (parent_window) gtk_widget_set_sensitive(GTK_WIDGET(parent_window), FALSE); } void trace_unfreeze_all(void) { if (parent_window) gtk_widget_set_sensitive(GTK_WIDGET(parent_window), TRUE); } /** * trace_dialog_register_alt_warning - register an alternate function for warning() * @alt: the function to be called instead of warning. * * Add an alternate warning function to be called instead of a popup. * To go back to the popup, simply call this again with NULL. */ void trace_dialog_register_alt_warning(void (*alt)(const char *fmt, va_list ap)) { alt_warning = alt; } void warning(const char *fmt, ...) { GString *str; va_list ap; int err; if (alt_warning) { va_start(ap, fmt); alt_warning(fmt, ap); va_end(ap); return; } if (!parent_window) { va_start(ap, fmt); __vwarning(fmt, ap); va_end(ap); return; } err = errno; errno = 0; str = g_string_new(""); va_start(ap, fmt); g_string_vprintf(str, fmt, ap); va_end(ap); g_string_append(str, "\n"); if (errno) { g_string_prepend(str, "\n"); g_string_prepend(str, strerror(errno)); } errno = 0; trace_dialog(GTK_WINDOW(parent_window), TRACE_GUI_WARNING, str->str); g_string_free(str, TRUE); } static void status_display_clicked (gpointer data) { GtkWidget *dialog; GtkWidget *scrollwin; GtkWidget *viewport; GtkWidget *textview; GtkTextBuffer *buffer; dialog = gtk_dialog_new_with_buttons("Status", NULL, GTK_DIALOG_MODAL, "OK", GTK_RESPONSE_ACCEPT, NULL); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); gtk_widget_show(scrollwin); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrollwin), viewport); textview = gtk_text_view_new(); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); gtk_text_buffer_set_text(buffer, statusstr->str, -1); gtk_container_add(GTK_CONTAINER(viewport), textview); gtk_widget_show(textview); gtk_widget_set_size_request(GTK_WIDGET(dialog), DIALOG_WIDTH, DIALOG_HEIGHT); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } static gboolean do_status_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) { static GtkWidget *menu; static GtkWidget *menu_status_display; if (!menu) { menu = gtk_menu_new(); menu_status_display = gtk_menu_item_new_with_label("Display Status"); gtk_widget_show(menu_status_display); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_status_display); g_signal_connect_swapped (G_OBJECT (menu_status_display), "activate", G_CALLBACK (status_display_clicked), data); } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time()); return TRUE; } static gboolean button_press_status(GtkWidget *widget, GdkEventButton *event, gpointer data) { if (event->button == 1) return do_status_popup(widget, event, data); return FALSE; } GtkWidget *trace_status_bar_new(void) { GtkWidget *eventbox; statusbar = gtk_statusbar_new(); statuspix = gtk_image_new_from_stock(GTK_STOCK_INFO, GTK_ICON_SIZE_SMALL_TOOLBAR); eventbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(eventbox), statuspix); gtk_widget_show(eventbox); gtk_box_pack_end(GTK_BOX(statusbar), eventbox, FALSE, FALSE, 0); if (statusstr) gtk_widget_show(statuspix); gtk_signal_connect(GTK_OBJECT(eventbox), "button_press_event", (GtkSignalFunc) button_press_status, NULL); return statusbar; } void trace_show_help(GtkWidget *window, const gchar *link, GError **error) { #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) trace_dialog(GTK_WINDOW(window), TRACE_GUI_WARNING, "This version of GTK+ does not implement gtk_show_uri.\n" "Please upgrade your GTK and recompile"); #else gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(window)), link, GDK_CURRENT_TIME, error); #endif } GtkResponseType trace_dialog(GtkWindow *parent, enum trace_dialog_type type, gchar *message, ...) { GtkWidget *dialog; GtkMessageType mtype; GtkButtonsType btype = GTK_BUTTONS_CLOSE; gchar *str; va_list ap; int result; if (!parent) parent = GTK_WINDOW(parent_window); switch (type) { case TRACE_GUI_INFO: mtype = GTK_MESSAGE_INFO; break; case TRACE_GUI_WARNING: mtype = GTK_MESSAGE_WARNING; break; case TRACE_GUI_ERROR: mtype = GTK_MESSAGE_ERROR; break; case TRACE_GUI_ASK: mtype = GTK_MESSAGE_WARNING; btype = GTK_BUTTONS_YES_NO; break; } va_start(ap, message); str = g_strdup_vprintf(message, ap); va_end(ap); dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, mtype, btype, "%s", str); g_free(str); result = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return result; } /** * trace_get_file_dialog - pop up a file dialog to get a file * @title: the title of the dialog * @open: the text for the "open" button (NULL for default) * @ftype: What extension the dialog should default filter on. * @warn: if the file exists, warn and let them choose again. * * Returns: the filename if it should be used. NULL otherwise. * The filename needs to be freed with g_free(). */ gchar *trace_get_file_dialog_filter(const gchar *title, const char *open, enum trace_dialog_filter ftype, gboolean warn) { struct stat st; GtkWidget *dialog; GtkResponseType ret; GtkFileFilter *filter; GtkFileFilter *setfilter; gchar *filename = NULL; gchar *ext = NULL; if (!open) open = GTK_STOCK_OPEN; dialog = gtk_file_chooser_dialog_new(title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, open, GTK_RESPONSE_ACCEPT, NULL); setfilter = filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "All Files"); gtk_file_filter_add_pattern(filter, "*"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "trace-cmd .dat files"); gtk_file_filter_add_pattern(filter, "*.dat"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (ftype == TRACE_DIALOG_FILTER_DATA) { setfilter = filter; ext = ".dat"; } filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "KernelShark filter files"); gtk_file_filter_add_pattern(filter, "*.ksf"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (ftype == TRACE_DIALOG_FILTER_FILTER) { setfilter = filter; ext = ".ksf"; } filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "KernelShark setting files"); gtk_file_filter_add_pattern(filter, "*.kss"); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); if (ftype == TRACE_DIALOG_FILTER_SETTING) { setfilter = filter; ext = ".kss"; } gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), setfilter); again: ret = gtk_dialog_run(GTK_DIALOG(dialog)); if (ret == GTK_RESPONSE_ACCEPT) { filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); if (filename && warn) { if (ext) { int len = strlen(filename); gchar *tmp; /* Add extension if not already there */ if (strcmp(filename + (len - 4), ext) != 0) { tmp = filename; filename = g_strdup_printf("%s%s", tmp, ext); g_free(tmp); } } if (stat(filename, &st) >= 0) { ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK, "The file '%s' already exists.\n" "Are you sure you want to replace it", filename); if (ret == GTK_RESPONSE_NO) { g_free(filename); filename = NULL; goto again; } } } } gtk_widget_destroy(dialog); return filename; } gchar *trace_get_file_dialog(const gchar *title, const char *open, gboolean warn) { return trace_get_file_dialog_filter(title, open, TRACE_DIALOG_FILTER_NONE, warn); } /** * trace_create_combo_box - helper function to create a label and combo box * @hbox: The hbox to add the label and combo box to * @text: The text of the label * @combo_model_create: The function used to create the combo model * @data: data to pass to the combo_model_create. * * If no @hbox is given, the @text is ignored, and only the combo box * is created. * * Returns the combo box in the hbox. */ GtkWidget * trace_create_combo_box(GtkWidget *hbox, const gchar *text, GtkTreeModel *(*combo_model_create)(gpointer data), gpointer data) { GtkCellRenderer *renderer; GtkTreeModel *model; GtkWidget *label; GtkWidget *combo; if (hbox) { label = gtk_label_new(text); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); } /* --- Set up the selection combo box --- */ model = combo_model_create(data); renderer = gtk_cell_renderer_text_new(); combo = gtk_combo_box_new_with_model(model); if (hbox) gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0); gtk_widget_show(combo); /* Free model with combobox */ g_object_unref(model); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "text", 0, NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); return combo; } trace-cmd-2.5.3/trace-filter-hash.c000066400000000000000000000105241246701203100170240ustar00rootroot00000000000000/* * Copyright (C) 2009, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "trace-filter-hash.h" #define FILTER_TASK_HASH_SIZE 256 struct filter_task_item * filter_task_find_pid(struct filter_task *hash, gint pid) { gint key = trace_hash(pid) % FILTER_TASK_HASH_SIZE; struct filter_task_item *task = hash->hash[key]; while (task) { if (task->pid == pid) break; task = task->next; } return task; } void filter_task_add_pid(struct filter_task *hash, gint pid) { gint key = trace_hash(pid) % FILTER_TASK_HASH_SIZE; struct filter_task_item *task; task = g_new0(typeof(*task), 1); g_assert(task); task->pid = pid; task->next = hash->hash[key]; hash->hash[key] = task; hash->count++; } void filter_task_remove_pid(struct filter_task *hash, gint pid) { gint key = trace_hash(pid) % FILTER_TASK_HASH_SIZE; struct filter_task_item **next = &hash->hash[key]; struct filter_task_item *task; while (*next) { if ((*next)->pid == pid) break; next = &(*next)->next; } if (!*next) return; g_assert(hash->count); hash->count--; task = *next; *next = task->next; g_free(task); } void filter_task_clear(struct filter_task *hash) { struct filter_task_item *task, *next;; gint i; for (i = 0; i < FILTER_TASK_HASH_SIZE; i++) { next = hash->hash[i]; if (!next) continue; hash->hash[i] = NULL; while (next) { task = next; next = task->next; g_free(task); } } hash->count = 0; } struct filter_task *filter_task_hash_alloc(void) { struct filter_task *hash; hash = g_new0(typeof(*hash), 1); g_assert(hash); hash->hash = g_new0(typeof(*hash->hash), FILTER_TASK_HASH_SIZE); return hash; } void filter_task_hash_free(struct filter_task *hash) { if (!hash) return; filter_task_clear(hash); g_free(hash->hash); g_free(hash); } struct filter_task *filter_task_hash_copy(struct filter_task *hash) { struct filter_task *new_hash; struct filter_task_item *task, **ptask; gint i; if (!hash) return NULL; new_hash = filter_task_hash_alloc(); g_assert(new_hash); for (i = 0; i < FILTER_TASK_HASH_SIZE; i++) { task = hash->hash[i]; if (!task) continue; ptask = &new_hash->hash[i]; while (task) { *ptask = g_new0(typeof(*task), 1); g_assert(*ptask); **ptask = *task; ptask = &(*ptask)->next; task = task->next; } } new_hash->count = hash->count; return new_hash; } int *filter_task_pids(struct filter_task *hash) { struct filter_task_item *task; int *pids; int count = 0; int i; if (!hash->count) return NULL; pids = malloc(sizeof(*pids) * (hash->count + 1)); if (!pids) return NULL; for (i = 0; i < FILTER_TASK_HASH_SIZE; i++) { task = hash->hash[i]; while (task) { pids[count++] = task->pid; task = task->next; } } pids[count] = -1; return pids; } /** * filter_task_compare - compare two task hashs 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 filter_task_compare(struct filter_task *hash1, struct filter_task *hash2) { int *pids; 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 */ pids = filter_task_pids(hash1); for (i = 0; pids[i] >= 0; i++) { if (!filter_task_find_pid(hash2, pids[i])) break; } if (pids[i] == -1) ret = 1; free(pids); return ret; } trace-cmd-2.5.3/trace-filter-hash.h000066400000000000000000000034071246701203100170330ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_FILTER_HASH_H #define _TRACE_FILTER_HASH_H #include #include "trace-hash-local.h" struct filter_task_item { struct filter_task_item *next; gint pid; }; struct filter_task { struct filter_task_item **hash; gint count; }; struct filter_task_item * filter_task_find_pid(struct filter_task *hash, gint pid); void filter_task_add_pid(struct filter_task *hash, gint pid); void filter_task_remove_pid(struct filter_task *hash, gint pid); void filter_task_clear(struct filter_task *hash); struct filter_task *filter_task_hash_alloc(void); void filter_task_hash_free(struct filter_task *hash); struct filter_task *filter_task_hash_copy(struct filter_task *hash); int *filter_task_pids(struct filter_task *hash); int filter_task_compare(struct filter_task *hash1, struct filter_task *hash2); static inline gint filter_task_count(struct filter_task *hash) { return hash->count; } #endif /* _TRACE_FILTER_HASH_H */ trace-cmd-2.5.3/trace-filter.c000066400000000000000000001517651246701203100161200ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "trace-cmd.h" #include "trace-local.h" #include "trace-view-store.h" #include "trace-view.h" #include "trace-gui.h" #include "cpu.h" #include "event-utils.h" #include "list.h" #define DIALOG_WIDTH 400 #define DIALOG_HEIGHT 600 #define TEXT_DIALOG_WIDTH 600 #define TEXT_DIALOG_HEIGHT 400 int str_cmp(const void *a, const void *b) { char * const * sa = a; char * const * sb = b; return strcmp(*sa, *sb); } int id_cmp(const void *a, const void *b) { const gint *ia = a; const gint *ib = b; if (*ia > *ib) return 1; if (*ia < *ib) return -1; return 0; } /** * trace_array_add - allocate and add an int to an array. * @array: address of array to allocate * @count: address of the current count of array data * @val: the value to append to the array. * * The first call to this, @array should be uninitialized * and @count should be zero. @array will be malloced and * val will be added to it. If count is greater than zero, * then array will be realloced, and val will be appened * to it. * * This also always ends the array with -1, so the values are * assumed to always be positive. * * @array must be freed with free(). */ void trace_array_add(gint **array, gint *count, gint val) { if (*count) *array = realloc(*array, sizeof(val) * (*count + 2)); else *array = malloc(sizeof(val) * 2); (*array)[(*count)++] = val; (*array)[*count] = -1; } /* --- event info box --- */ struct event_combo_info { struct pevent *pevent; GtkWidget *event_combo; GtkWidget *op_combo; GtkWidget *field_combo; GtkWidget *entry; }; static GtkTreeModel *create_event_combo_model(gpointer data) { struct pevent *pevent = data; GtkTreeStore *tree; GtkTreeIter sys_iter; GtkTreeIter iter; struct event_format **events; struct event_format *event; const char *last_sys = NULL; int i; events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); if (!events) return NULL; tree = gtk_tree_store_new(1, G_TYPE_STRING); for (i = 0; events[i]; i++) { event = events[i]; if (!last_sys || strcmp(last_sys, event->system) != 0) { last_sys = event->system; gtk_tree_store_append(tree, &sys_iter, NULL); gtk_tree_store_set(tree, &sys_iter, 0, last_sys, -1); } gtk_tree_store_append(tree, &iter, &sys_iter); gtk_tree_store_set(tree, &iter, 0, event->name, -1); } return GTK_TREE_MODEL(tree); } static GtkTreeModel *create_op_combo_model(gpointer data) { GtkListStore *list; GtkTreeIter iter; int i; const gchar *ops[] = {":", ",", "==", "!=", "<", ">", "<=", ">=", "=~", "!~", "!", "(", ")", "+", "-", "*", "/", "<<", ">>", "&&", "||", "&", "|", NULL}; list = gtk_list_store_new(1, G_TYPE_STRING); for (i = 0; ops[i]; i++) { gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, ops[i], -1); } return GTK_TREE_MODEL(list); } static GtkTreeModel *create_field_combo_model(gpointer data) { struct pevent *pevent = data; GtkListStore *list; GtkTreeIter iter; struct event_format **events; struct format_field **fields; struct format_field *field; int i; events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); if (!events) return NULL; list = gtk_list_store_new(1, G_TYPE_STRING); /* use any event for common fields */ fields = pevent_event_common_fields(events[0]); for (i = 0; fields[i]; i++) { field = fields[i]; gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, field->name, -1); } free(fields); return GTK_TREE_MODEL(list); } static void update_field_combo(struct pevent *pevent, GtkWidget *combo, const char *system, const char *event_name) { struct event_format **events; struct event_format *event; struct format_field **fields; struct format_field *field; GtkTreeModel *model; GtkListStore *list; GtkTreeIter iter; int i; model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); if (!model) return; if (!gtk_tree_model_get_iter_first(model, &iter)) return; if (event_name) { event = pevent_find_event_by_name(pevent, system, event_name); if (!event) return; } else { /* use any event */ events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); if (!events) return; event = events[0]; } /* Remove all the objects and reset it */ g_object_ref(model); gtk_combo_box_set_model(GTK_COMBO_BOX(combo), NULL); list = GTK_LIST_STORE(model); while (gtk_list_store_remove(list, &iter)) ; /* always load the common fields first */ fields = pevent_event_common_fields(event); for (i = 0; fields[i]; i++) { field = fields[i]; gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, field->name, -1); } free(fields); /* Now add event specific events */ if (event_name) { fields = pevent_event_fields(event); for (i = 0; fields[i]; i++) { field = fields[i]; gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, 0, field->name, -1); } free(fields); } gtk_combo_box_set_model(GTK_COMBO_BOX(combo), model); g_object_unref(model); if (!gtk_tree_model_get_iter_first(model, &iter)) return; gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } static void event_combo_changed(GtkComboBox *combo, gpointer data) { struct event_combo_info *info = data; GtkWidget *field_combo = info->field_combo; GtkTreeIter parent_iter; GtkTreeIter iter; GtkTreeModel *model; GtkTreePath *path; gchar *system_name = NULL; gchar *event_name = NULL; gint depth; model = gtk_combo_box_get_model(combo); if (!model) return; if (!gtk_combo_box_get_active_iter(combo, &iter)) return; path = gtk_tree_model_get_path(model, &iter); if (!path) return; depth = gtk_tree_path_get_depth(path); if (depth > 1) { gtk_tree_model_get(model, &iter, 0, &event_name, -1); gtk_tree_model_iter_parent(model, &parent_iter, &iter); gtk_tree_model_get(model, &parent_iter, 0, &system_name, -1); } else gtk_tree_model_get(model, &iter, 0, &system_name, -1); update_field_combo(info->pevent, field_combo, system_name, event_name); g_free(system_name); g_free(event_name); gtk_tree_path_free(path); } static void insert_combo_text(struct event_combo_info *info, GtkComboBox *combo) { GtkTreeModel *model; GtkTreeIter iter; GtkWidget *entry; gchar *text; gint pos; model = gtk_combo_box_get_model(combo); if (!model) return; if (!gtk_combo_box_get_active_iter(combo, &iter)) return; gtk_tree_model_get(model, &iter, 0, &text, -1); entry = info->entry; pos = gtk_editable_get_position(GTK_EDITABLE(entry)); gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text), &pos); gtk_editable_set_position(GTK_EDITABLE(entry), pos); gtk_editable_insert_text(GTK_EDITABLE(entry), " ", 1, &pos); gtk_editable_set_position(GTK_EDITABLE(entry), pos); g_free(text); } static void event_insert_pressed(GtkButton *button, gpointer data) { struct event_combo_info *info = data; insert_combo_text(info, GTK_COMBO_BOX(info->event_combo)); } static void op_insert_pressed(GtkButton *button, gpointer data) { struct event_combo_info *info = data; insert_combo_text(info, GTK_COMBO_BOX(info->op_combo)); } static void field_insert_pressed(GtkButton *button, gpointer data) { struct event_combo_info *info = data; insert_combo_text(info, GTK_COMBO_BOX(info->field_combo)); } static GtkWidget * create_combo_box(struct event_combo_info *info, GtkWidget *hbox, const gchar *text, GtkTreeModel *(*combo_model_create)(gpointer data), void (*insert_pressed)(GtkButton *button, gpointer data)) { GtkWidget *hbox2; GtkWidget *combo; GtkWidget *button; hbox2 = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), hbox2, TRUE, TRUE, 0); gtk_widget_show(hbox2); combo = trace_create_combo_box(hbox, text, combo_model_create, info->pevent); /* --- add insert button --- */ button = gtk_button_new_with_label("Insert"); gtk_box_pack_start(GTK_BOX(hbox2), button, FALSE, FALSE, 0); gtk_widget_show(button); g_signal_connect (button, "pressed", G_CALLBACK (insert_pressed), (gpointer) info); return combo; } static GtkWidget *event_info_box(struct event_combo_info *info) { GtkWidget *hbox; GtkWidget *event_combo; GtkWidget *op_combo; GtkWidget *field_combo; hbox = gtk_hbox_new(FALSE, 0); event_combo = create_combo_box(info, hbox, "Event:", create_event_combo_model, event_insert_pressed); op_combo = create_combo_box(info, hbox, "Op:", create_op_combo_model, op_insert_pressed); field_combo = create_combo_box(info, hbox, "Field:", create_field_combo_model, field_insert_pressed); g_signal_connect (event_combo, "changed", G_CALLBACK (event_combo_changed), (gpointer) info); info->event_combo = event_combo; info->op_combo = op_combo; info->field_combo = field_combo; return hbox; } /* --- advance filter --- */ enum { ADV_COL_DELETE, ADV_COL_EVENT, ADV_COL_FILTER, ADV_COL_ID, NUM_ADV_FILTER_COLS, }; static gint *get_event_ids(GtkTreeView *treeview) { GtkTreeModel *model; GtkTreeIter iter; gboolean active; gint *ids = NULL; gint id; int count = 0; model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); if (!model) return NULL; if (!gtk_tree_model_iter_children(model, &iter, NULL)) return NULL; for (;;) { gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, ADV_COL_DELETE, &active, ADV_COL_ID, &id, -1); if (active) trace_array_add(&ids, &count, id); if (!gtk_tree_model_iter_next(model, &iter)) break; } return ids; } static GtkTreeModel * create_tree_filter_model(struct pevent *pevent, struct event_filter *event_filter) { GtkTreeStore *treestore; GtkTreeIter iter_events; struct event_format **events; char *str; gint i; treestore = gtk_tree_store_new(NUM_ADV_FILTER_COLS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); if (!events) return GTK_TREE_MODEL(treestore); for (i = 0; events[i]; i++) { str = pevent_filter_make_string(event_filter, events[i]->id); if (!str) continue; /* We only want to show advanced filters */ if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { free(str); continue; } gtk_tree_store_append(treestore, &iter_events, NULL); gtk_tree_store_set(treestore, &iter_events, ADV_COL_DELETE, FALSE, ADV_COL_EVENT, events[i]->name, ADV_COL_FILTER, str, ADV_COL_ID, events[i]->id, -1); free(str); } return GTK_TREE_MODEL(treestore); } #define DELETE_FILTER "Delete Filter" static void adv_filter_cursor_changed(GtkTreeView *treeview, gpointer data) { GtkTreeViewColumn *col; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gboolean active; const gchar *title; model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); if (!model) return; gtk_tree_view_get_cursor(treeview, &path, &col); if (!path) return; if (!col) goto free; title = gtk_tree_view_column_get_title(col); if (strcmp(title, DELETE_FILTER) != 0) goto free; if (!gtk_tree_model_get_iter(model, &iter, path)) goto free; gtk_tree_model_get(model, &iter, ADV_COL_DELETE, &active, -1); active = active ? FALSE : TRUE; gtk_tree_store_set(GTK_TREE_STORE(model), &iter, ADV_COL_DELETE, active, -1); free: gtk_tree_path_free(path); } static GtkWidget * create_adv_filter_view(struct pevent *pevent, struct event_filter *event_filter) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; GtkCellRenderer *togrend; GtkWidget *view; GtkTreeModel *model; view = gtk_tree_view_new(); renderer = gtk_cell_renderer_text_new(); togrend = gtk_cell_renderer_toggle_new(); /* --- delete column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, DELETE_FILTER); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, togrend, FALSE); gtk_tree_view_column_add_attribute(col, togrend, "active", ADV_COL_DELETE); /* --- events column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Event"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, renderer, FALSE); gtk_tree_view_column_add_attribute(col, renderer, "text", ADV_COL_EVENT); /* --- filter column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Filter"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, renderer, FALSE); gtk_tree_view_column_add_attribute(col, renderer, "text", ADV_COL_FILTER); model = create_tree_filter_model(pevent, event_filter); gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); g_object_unref(model); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_NONE); g_signal_connect_swapped (view, "cursor-changed", G_CALLBACK (adv_filter_cursor_changed), (gpointer) view); return view; } /** * trace_adv_filter_dialog - make dialog for text * @handle: the handle to the tracecmd data file * @event_filter: advanced filters * @func: The function to call when accept or cancel is pressed * @data: data to pass to the function @func */ void trace_adv_filter_dialog(struct tracecmd_input *handle, struct event_filter *event_filter, trace_adv_filter_cb_func func, gpointer data) { struct event_combo_info combo_info; struct pevent *pevent; GtkWidget *dialog; GtkWidget *hbox; GtkWidget *label; GtkWidget *entry; GtkWidget *scrollwin; GtkWidget *view; GtkWidget *event_box; const gchar *text; gint *event_ids; int result; if (!handle) return; pevent = tracecmd_get_pevent(handle); if (!pevent) return; /* --- Make dialog window --- */ dialog = gtk_dialog_new_with_buttons("Advanced Filters", NULL, GTK_DIALOG_MODAL, "Apply", GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); view = create_adv_filter_view(pevent, event_filter); gtk_container_add(GTK_CONTAINER(scrollwin), view); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); label = gtk_label_new(" [,] : [!][(][)]" "[&&/|| [(][)]]\n\n" "Examples:\n" " sched_switch : next_prio < 100 && (prev_prio > 100" "&& prev_pid != 0)\n" " irq.* : irq != 38\n" " .* : common_pid == 1234"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, FALSE, 0); gtk_widget_show(label); combo_info.pevent = pevent; event_box = event_info_box(&combo_info); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), event_box, FALSE, FALSE, 0); gtk_widget_show(event_box); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Filter:"); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); entry = gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); gtk_widget_show(entry); combo_info.entry = entry; gtk_widget_set_size_request(GTK_WIDGET(dialog), TEXT_DIALOG_WIDTH, TEXT_DIALOG_HEIGHT); gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: text = gtk_entry_get_text(GTK_ENTRY(entry)); event_ids = get_event_ids(GTK_TREE_VIEW(view)); func(TRUE, text, event_ids, data); free(event_ids); break; case GTK_RESPONSE_REJECT: func(FALSE, NULL, NULL, data); break; default: break; }; gtk_widget_destroy(dialog); } /* --- task list dialog --- */ enum { TASK_COL_SELECT, TASK_COL_PID, TASK_COL_COMM, NUM_TASK_COLS, }; static void get_tasks(GtkTreeView *treeview, gint **selected, gint **non_selected) { GtkTreeModel *model; GtkTreeIter iter; gboolean active; gint *pids = NULL; gint *non_pids = NULL; gint pid; int pid_count = 0; int non_count = 0; model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); if (!model) return; if (!gtk_tree_model_iter_children(model, &iter, NULL)) return; for (;;) { gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, TASK_COL_SELECT, &active, TASK_COL_PID, &pid, -1); if (active) trace_array_add(&pids, &pid_count, pid); else trace_array_add(&non_pids, &non_count, pid); if (!gtk_tree_model_iter_next(model, &iter)) break; } *selected = pids; *non_selected = non_pids; } static GtkTreeModel * create_task_model(struct pevent *pevent, gint *tasks, gint *selected) { GtkTreeStore *treestore; GtkTreeIter iter; const char *comm; gboolean select; gint *ret; gint *list; gint count; gint i; if (!tasks) return NULL; treestore = gtk_tree_store_new(NUM_TASK_COLS, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_STRING); /* sort our tasks */ for (i = 0; tasks[i] >= 0; i++) ; count = i; /* Don't assume we can modify the array passed in. */ list = malloc_or_die(sizeof(gint) * (count + 1)); memcpy(list, tasks, sizeof(gint) * (count + 1)); qsort(list, count, sizeof(gint), id_cmp); /* Now that we have our own copy, use it instead */ tasks = list; if (selected) { /* do the same for selected */ for (i = 0; selected[i] >= 0; i++) ; count = i; /* count is now the size of selected */ list = malloc_or_die(sizeof(gint) * (count + 1)); memcpy(list, selected, sizeof(gint) * (count + 1)); qsort(list, count, sizeof(gint), id_cmp); selected = list; } for (i = 0; tasks[i] >= 0; i++) { comm = pevent_data_comm_from_pid(pevent, tasks[i]); gtk_tree_store_append(treestore, &iter, NULL); select = FALSE; if (selected) { ret = bsearch(&tasks[i], selected, count, sizeof(gint), id_cmp); if (ret) select = TRUE; } gtk_tree_store_set(treestore, &iter, TASK_COL_SELECT, select, TASK_COL_PID, tasks[i], TASK_COL_COMM, comm, -1); } free(tasks); free(selected); return GTK_TREE_MODEL(treestore); } static void task_cursor_changed(gpointer data, GtkTreeView *treeview) { gboolean *start = data; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gboolean active; if (!*start) { *start = TRUE; return; } model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); if (!model) return; gtk_tree_view_get_cursor(treeview, &path, NULL); if (!path) return; if (!gtk_tree_model_get_iter(model, &iter, path)) goto free; gtk_tree_model_get(model, &iter, TASK_COL_SELECT, &active, -1); active = active ? FALSE : TRUE; gtk_tree_store_set(GTK_TREE_STORE(model), &iter, TASK_COL_SELECT, active, -1); free: gtk_tree_path_free(path); } static GtkWidget * create_task_view(struct pevent *pevent, gint *tasks, gint *selected, gboolean *start) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; GtkCellRenderer *togrend; GtkWidget *view; GtkTreeModel *model; view = gtk_tree_view_new(); renderer = gtk_cell_renderer_text_new(); togrend = gtk_cell_renderer_toggle_new(); /* --- select column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Plot"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, togrend, FALSE); gtk_tree_view_column_add_attribute(col, togrend, "active", TASK_COL_SELECT); /* --- pid column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "PID"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, renderer, FALSE); gtk_tree_view_column_add_attribute(col, renderer, "text", TASK_COL_PID); /* --- comm column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Task"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); gtk_tree_view_column_pack_start(col, renderer, FALSE); gtk_tree_view_column_add_attribute(col, renderer, "text", TASK_COL_COMM); model = create_task_model(pevent, tasks, selected); gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); g_object_unref(model); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_NONE); g_signal_connect_swapped (view, "cursor-changed", G_CALLBACK (task_cursor_changed), (gpointer)start); return view; } /** * trace_task_dialog - make dialog to select tasks * @handle: the handle to the tracecmd data file * @tasks: array of task pids ending with -1 * @selected: tasks from @tasks that should be selected (can be NULL). * Also an array of pids ending with -1 * @func: callback function when accept or cancel is selected * @data: data to pass to the function @func */ void trace_task_dialog(struct tracecmd_input *handle, gint *tasks, gint *selected, trace_task_cb_func func, gpointer data) { struct pevent *pevent; GtkWidget *dialog; GtkWidget *scrollwin; GtkWidget *view; gboolean start = FALSE; gint *non_select; int result; if (!handle) return; pevent = tracecmd_get_pevent(handle); if (!pevent) return; /* --- Make dialog window --- */ dialog = gtk_dialog_new_with_buttons("Select Tasks", NULL, GTK_DIALOG_MODAL, "Apply", GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); view = create_task_view(pevent, tasks, selected, &start); gtk_container_add(GTK_CONTAINER(scrollwin), view); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); gtk_widget_set_size_request(GTK_WIDGET(dialog), DIALOG_WIDTH, DIALOG_HEIGHT); gtk_widget_show_all(dialog); selected = NULL; result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: get_tasks(GTK_TREE_VIEW(view), &selected, &non_select); func(TRUE, selected, non_select, data); free(selected); free(non_select); break; case GTK_RESPONSE_REJECT: func(FALSE, NULL, NULL, data); break; default: break; }; gtk_widget_destroy(dialog); } enum { COL_EVENT, COL_ACTIVE, COL_NORMAL, COL_ACTIVE_START, COL_EVENT_ID, NUM_EVENT_COLS, }; struct event_filter_helper { trace_filter_event_cb_func func; GtkTreeView *view; gpointer data; }; gboolean system_is_enabled(gchar **systems, gint systems_size, const gchar *system) { const gchar **sys = &system; if (!systems) return FALSE; sys = bsearch(sys, systems, systems_size, sizeof(system), str_cmp); return sys != NULL; } gboolean event_is_enabled(gint *events, gint events_size, gint event) { gint *ret; if (!events) return FALSE; ret = bsearch(&event, events, events_size, sizeof(gint), id_cmp); return ret != NULL; } static GtkTreeModel * create_tree_event_model(struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems_set, gint *event_ids_set) { GtkTreeStore *treestore; GtkTreeIter iter_all, iter_sys, iter_events; struct event_format **events; struct event_format *event; char *last_system = NULL; gboolean sysactive; gboolean active, normal; gchar **systems = NULL; gint *event_ids = NULL; gint systems_size; gint event_ids_size; gint i; treestore = gtk_tree_store_new(NUM_EVENT_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_INT); gtk_tree_store_append(treestore, &iter_all, NULL); gtk_tree_store_set(treestore, &iter_all, COL_EVENT, "All", COL_ACTIVE, all_events, COL_NORMAL, TRUE, COL_ACTIVE_START, FALSE, -1); events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); if (!events) return GTK_TREE_MODEL(treestore); if (systems_set) { for (systems_size = 0; systems_set[systems_size]; systems_size++) ; systems = g_new(typeof(*systems), systems_size + 1); memcpy(systems, systems_set, sizeof(*systems) * (systems_size + 1)); qsort(systems, systems_size, sizeof(gchar *), str_cmp); } if (event_ids_set) { for (event_ids_size = 0; event_ids_set[event_ids_size] != -1; event_ids_size++) ; event_ids = g_new(typeof(*event_ids), event_ids_size + 1); memcpy(event_ids, event_ids_set, sizeof(*event_ids) * (event_ids_size + 1)); qsort(event_ids, event_ids_size, sizeof(gint), id_cmp); } for (i = 0; events[i]; i++) { event = events[i]; if (!last_system || strcmp(last_system, event->system) != 0) { gtk_tree_store_append(treestore, &iter_sys, &iter_all); sysactive = all_events || system_is_enabled(systems, systems_size, event->system); gtk_tree_store_set(treestore, &iter_sys, COL_EVENT, event->system, COL_ACTIVE, sysactive, COL_NORMAL, TRUE, -1); last_system = event->system; } active = all_events || sysactive || event_is_enabled(event_ids, event_ids_size, event->id); normal = TRUE; if (active && filter) { if (pevent_event_filtered(filter, event->id) && !pevent_filter_event_has_trivial(filter, event->id, FILTER_TRIVIAL_BOTH)) normal = FALSE; /* Make trivial false not selected */ else if (pevent_filter_event_has_trivial(filter, event->id, FILTER_TRIVIAL_FALSE)) active = FALSE; } gtk_tree_store_append(treestore, &iter_events, &iter_sys); gtk_tree_store_set(treestore, &iter_events, COL_EVENT, event->name, COL_ACTIVE, active, COL_NORMAL, normal, COL_EVENT_ID, event->id, -1); } /* These are copies of the sets, using glib allocation */ g_free(systems); g_free(event_ids); return GTK_TREE_MODEL(treestore); } static void update_active_events(GtkTreeModel *model, GtkTreeIter *parent, gboolean active) { GtkTreeIter event; if (!gtk_tree_model_iter_children(model, &event, parent)) return; for (;;) { gtk_tree_store_set(GTK_TREE_STORE(model), &event, COL_ACTIVE, active, -1); if (!gtk_tree_model_iter_next(model, &event)) break; } } static void update_active_systems(GtkTreeModel *model, GtkTreeIter *parent, gboolean active) { GtkTreeIter sys; if (!gtk_tree_model_iter_children(model, &sys, parent)) return; for (;;) { gtk_tree_store_set(GTK_TREE_STORE(model), &sys, COL_ACTIVE, active, -1); update_active_events(model, &sys, active); if (!gtk_tree_model_iter_next(model, &sys)) break; } } static void event_cursor_changed(GtkTreeView *treeview, gpointer data) { GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter, parent, grandparent; gboolean active, start; gint depth; model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)); if (!model) return; gtk_tree_view_get_cursor(treeview, &path, NULL); if (!path) return; if (!gtk_tree_model_get_iter(model, &iter, path)) goto free; depth = gtk_tree_path_get_depth(path); if (depth == 1) { /* * The first time we start up, the cursor will * select the "All Events" row, and call * this routine. But we don't want to do anything. * Check and activate. */ gtk_tree_model_get(model, &iter, COL_ACTIVE_START, &start, -1); if (!start) { gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COL_ACTIVE_START, TRUE, -1); goto free; } } gtk_tree_model_get(model, &iter, COL_ACTIVE, &active, -1); active = active ? FALSE : TRUE; gtk_tree_store_set(GTK_TREE_STORE(model), &iter, COL_ACTIVE, active, -1); if (depth == 1) { /* Set all rows */ update_active_systems(model, &iter, active); } else if (depth == 2) { /* set this system */ update_active_events(model, &iter, active); if (!active) { /* disable the all events toggle */ gtk_tree_model_iter_parent(model, &parent, &iter); gtk_tree_store_set(GTK_TREE_STORE(model), &parent, COL_ACTIVE, FALSE, -1); } } else { if (!active) { /* disable system and all events toggles */ gtk_tree_model_iter_parent(model, &parent, &iter); gtk_tree_store_set(GTK_TREE_STORE(model), &parent, COL_ACTIVE, FALSE, -1); gtk_tree_model_iter_parent(model, &grandparent, &parent); gtk_tree_store_set(GTK_TREE_STORE(model), &grandparent, COL_ACTIVE, FALSE, -1); } } free: gtk_tree_path_free(path); } static gboolean child_set(GtkTreeModel *model, GtkTreeIter *parent) { GtkTreeIter iter; gboolean active; if (!gtk_tree_model_iter_children(model, &iter, parent)) return FALSE; for (;;) { gtk_tree_model_get(model, &iter, COL_ACTIVE, &active, -1); if (active) return TRUE; if (!gtk_tree_model_iter_next(model, &iter)) break; } return FALSE; } static void expand_rows(GtkTreeView *tree, GtkTreeModel *model, gboolean all_events, gchar **systems, gint *events) { GtkTreePath *path; GtkTreeIter all; GtkTreeIter sys; gboolean active; /* Expand the "All Events" row */ path = gtk_tree_path_new_from_string("0"); gtk_tree_view_expand_row(tree, path, FALSE); gtk_tree_path_free(path); if (all_events) return; /* Expand the system rows that are not full or empty */ if (!gtk_tree_model_get_iter_first(model, &all)) return; if (!gtk_tree_model_iter_children(model, &sys, &all)) return; for (;;) { gtk_tree_model_get(model, &sys, COL_ACTIVE, &active, -1); if (!active && child_set(model, &sys)) { path = gtk_tree_model_get_path(model, &sys); gtk_tree_view_expand_row(tree, path, FALSE); gtk_tree_path_free(path); } if (!gtk_tree_model_iter_next(model, &sys)) break; } } /** * trace_update_event_view - update the events of an existing event view * @event_view: event view to update * @pevent: The parse event descriptor * @filter: Event filter to determine what events have advanced filters * May be NULL. * @all_events: True if all should be selected, * @systems: Array of system names of systems that should be selected. * @events: Array of event ids of events that should be selecetd. */ int trace_update_event_view(GtkWidget *event_view, struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems, gint *events) { GtkTreeView *view = GTK_TREE_VIEW(event_view); GtkTreeModel *model; model = create_tree_event_model(pevent, filter, all_events, systems, events); if (!model) return -1; gtk_tree_view_set_model(view, model); g_object_unref(model); expand_rows(view, model, all_events, systems, events); return 0; } /** * trace_create_event_list_view - create a list view of events in pevent * @pevent: The parse event descriptor * @filter: Event filter to determine what events have advanced filters * May be NULL. * @all_events: True if all should be selected, * @systems: Array of system names of systems that should be selected. * @events: Array of event ids of events that should be selected. * * Returns a tree view widget of the events. */ GtkWidget * trace_create_event_list_view(struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems, gint *events) { GtkTreeViewColumn *col; GtkCellRenderer *renderer; GtkCellRenderer *togrend; GtkWidget *view; GtkTreeModel *model; view = gtk_tree_view_new(); /* --- events column --- */ col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Events"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); renderer = gtk_cell_renderer_text_new(); togrend = gtk_cell_renderer_toggle_new(); gtk_tree_view_column_pack_start(col, togrend, FALSE); gtk_tree_view_column_pack_start(col, renderer, FALSE); gtk_tree_view_column_add_attribute(col, togrend, "active", COL_ACTIVE); gtk_tree_view_column_add_attribute(col, togrend, "sensitive", COL_NORMAL); gtk_tree_view_column_add_attribute(col, renderer, "text", COL_EVENT); gtk_tree_view_column_add_attribute(col, renderer, "sensitive", COL_NORMAL); model = create_tree_event_model(pevent, filter, all_events, systems, events); gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); g_object_unref(model); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_NONE); expand_rows(GTK_TREE_VIEW(view), model, all_events, systems, events); g_signal_connect_swapped (view, "cursor-changed", G_CALLBACK (event_cursor_changed), (gpointer) view); return view; } static gint update_events(GtkTreeModel *model, GtkTreeIter *parent, gint **events, gint size) { GtkTreeIter event; gboolean active; gint id; if (!gtk_tree_model_iter_children(model, &event, parent)) return size; for (;;) { gtk_tree_model_get(model, &event, COL_ACTIVE, &active, COL_EVENT_ID, &id, -1); if (active) *events = tracecmd_add_id(*events, id, size++); if (!gtk_tree_model_iter_next(model, &event)) break; } return size; } static void update_system_events(GtkTreeModel *model, GtkTreeIter *parent, gchar ***systems, gint **events) { GtkTreeIter sys; gboolean active; gchar *system; gint sys_size = 0; gint event_size = 0; if (!gtk_tree_model_iter_children(model, &sys, parent)) return; for (;;) { gtk_tree_model_get(model, &sys, COL_ACTIVE, &active, COL_EVENT, &system, -1); if (active) *systems = tracecmd_add_list(*systems, system, sys_size++); else event_size = update_events(model, &sys, events, event_size); g_free(system); if (!gtk_tree_model_iter_next(model, &sys)) break; } } /** * trace_extract_event_list_view - extract selected events from event view * @event_view: the event list view widget to extract from * @all_events: Set if all events are set (@systems and @events are left as is) * @systems: Set to the selected systems (if @all_events is FALSE) * @events: Set to the selected events (not in selected systems and if @all_events is FALSE) * * Returns 0 on success, -1 otherwise. */ gint trace_extract_event_list_view(GtkWidget *event_view, gboolean *all_events, gchar ***systems, gint **events) { GtkTreeView *view = GTK_TREE_VIEW(event_view); GtkTreeModel *model; GtkTreeIter iter; model = gtk_tree_view_get_model(view); if (!model) return -1; if (!gtk_tree_model_get_iter_first(model, &iter)) return -1; gtk_tree_model_get(model, &iter, COL_ACTIVE, all_events, -1); if (!*all_events) update_system_events(model, &iter, systems, events); return 0; } static void accept_events(GtkWidget *view, trace_filter_event_cb_func func, gpointer data) { gboolean all_events; gchar **systems = NULL; gint *events = NULL; if (trace_extract_event_list_view(view, &all_events, &systems, &events) < 0) { /* Failed to extract ? */ func(FALSE, FALSE, NULL, NULL, data); return; } func(TRUE, all_events, systems, events, data); tracecmd_free_list(systems); free(events); } static void filter_event_dialog(struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems, gint *events, trace_filter_event_cb_func func, gpointer data) { GtkWidget *dialog; GtkWidget *scrollwin; GtkWidget *view; int result; /* --- Make dialog window --- */ dialog = gtk_dialog_new_with_buttons("Filter Events", NULL, GTK_DIALOG_MODAL, "Apply", GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); view = trace_create_event_list_view(pevent, filter, all_events, systems, events); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(scrollwin), view); gtk_widget_set_size_request(GTK_WIDGET(dialog), DIALOG_WIDTH, DIALOG_HEIGHT); gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: accept_events(view, func, data); break; case GTK_RESPONSE_REJECT: func(FALSE, FALSE, NULL, NULL, data); break; default: break; }; gtk_widget_destroy(dialog); } /** * trace_filter_event_dialog - make dialog with event listing * @handle: the handle to the tracecmd data file * @all_events: if TRUE then select all events. * @systems: NULL or a string array of systems terminated with NULL * @events: NULL or a int array of event ids terminated with -1 * @func: The function to call when accept or cancel is pressed * @data: data to pass to the function @func * * If @all_events is set, then @systems and @events are ignored. */ void trace_filter_event_dialog(struct tracecmd_input *handle, gboolean all_events, gchar **systems, gint *events, trace_filter_event_cb_func func, gpointer data) { struct pevent *pevent; if (!handle) return; pevent = tracecmd_get_pevent(handle); if (!pevent) return; filter_event_dialog(pevent, NULL, all_events, systems, events, func, data); } void trace_filter_pevent_dialog(struct pevent *pevent, gboolean all_events, gchar **systems, gint *events, trace_filter_event_cb_func func, gpointer data) { if (!pevent) return; filter_event_dialog(pevent, NULL, all_events, systems, events, func, data); } /** * trace_filter_event_filter_dialog - make dialog with event listing * @handle: the handle to the tracecmd data file * @filter: the event filter to determine what to display. * @all_events: if TRUE then select all events. * @func: The function to call when accept or cancel is pressed * @data: data to pass to the function @func * * If @all_events is set, then @systems and @events are ignored. */ void trace_filter_event_filter_dialog(struct tracecmd_input *handle, struct event_filter *filter, gboolean all_events, trace_filter_event_cb_func func, gpointer data) { struct pevent *pevent; gchar **systems; gint *event_ids; if (!handle) return; pevent = tracecmd_get_pevent(handle); if (!pevent) return; trace_filter_convert_filter_to_names(filter, &systems, &event_ids); filter_event_dialog(pevent, filter, all_events, systems, event_ids, func, data); } struct cpu_filter_helper { gboolean allcpus; guint64 *cpu_mask; GtkWidget **buttons; int cpus; }; static void destroy_cpu_helper(struct cpu_filter_helper *cpu_helper) { g_free(cpu_helper->cpu_mask); g_free(cpu_helper->buttons); g_free(cpu_helper); } #define CPU_ALL_CPUS_STR "All CPUs" void cpu_toggle(gpointer data, GtkWidget *widget) { struct cpu_filter_helper *cpu_helper = data; const gchar *label; gboolean active; gint cpu; label = gtk_button_get_label(GTK_BUTTON(widget)); active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if (strcmp(label, CPU_ALL_CPUS_STR) == 0) { cpu_helper->allcpus = active; /* enable/disable all toggles */ for (cpu = 0; cpu < cpu_helper->cpus; cpu++) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cpu_helper->buttons[cpu]), active ? TRUE : FALSE); return; } /* Get the CPU # from the label. Pass "CPU " */ cpu = atoi(label + 4); if (active) cpu_set(cpu_helper->cpu_mask, cpu); else cpu_clear(cpu_helper->cpu_mask, cpu); cpu_helper->allcpus = cpu_allset(cpu_helper->cpu_mask, cpu_helper->cpus); /* * Deactivate sending 'toggled' signal for "All CPUs" * while we adjust its state */ g_signal_handlers_block_by_func(GTK_TOGGLE_BUTTON(cpu_helper->buttons[cpu_helper->cpus]), G_CALLBACK (cpu_toggle), (gpointer) cpu_helper); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cpu_helper->buttons[cpu_helper->cpus]), cpu_helper->allcpus); g_signal_handlers_unblock_by_func(GTK_TOGGLE_BUTTON(cpu_helper->buttons[cpu_helper->cpus]), G_CALLBACK (cpu_toggle), (gpointer) cpu_helper); } /** * trace_filter_cpu_dialog - make dialog with cpu listing * @all_cpus: if TRUE then select all cpus. * @cpus_selected: NULL or a CPU mask with the CPUs to be select set. * @func: The function to call when accept or cancel is pressed * @data: data to pass to the function @func * * If @all_cpus is set, then @cpus_selected is ignored. */ void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpus, trace_filter_cpu_cb_func func, gpointer data) { struct cpu_filter_helper *cpu_helper; guint64 *cpu_mask = NULL; GtkWidget *dialog; GtkWidget *scrollwin; GtkWidget *viewport; GtkWidget *hbox; GtkWidget *vbox; GtkWidget *check; GtkRequisition req; gchar counter[100]; gint width, height; gint allset; gint cpu; int result; cpu_helper = g_new0(typeof(*cpu_helper), 1); g_assert(cpu_helper != NULL); /* --- Make dialog window --- */ dialog = gtk_dialog_new_with_buttons("Filter CPUS", NULL, GTK_DIALOG_MODAL, "Apply", GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); cpu_helper->cpus = cpus; cpu_helper->buttons = g_new0(GtkWidget *, cpus + 1); g_assert(cpu_helper->buttons); scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); viewport = gtk_viewport_new(NULL, NULL); gtk_widget_show(viewport); gtk_container_add(GTK_CONTAINER(scrollwin), viewport); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); /* Add hbox to center buttons. Is there a better way? */ hbox = gtk_hbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(viewport), hbox); gtk_widget_show(hbox); vbox = gtk_vbox_new(TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, FALSE, 0); gtk_widget_show(vbox); check = gtk_check_button_new_with_label(CPU_ALL_CPUS_STR); gtk_box_pack_start(GTK_BOX(vbox), check, TRUE, TRUE, 0); /* The last button will be the all CPUs button */ cpu_helper->buttons[cpus] = check; allset = cpus_selected ? 0 : 1; if (!allset) { /* check if the list is all set */ for (cpu = 0; cpu < cpus; cpu++) if (!cpu_isset(cpus_selected, cpu)) break; if (cpu == cpus) allset = 1; } if (allset) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); g_signal_connect_swapped (check, "toggled", G_CALLBACK (cpu_toggle), (gpointer) cpu_helper); cpu_helper->allcpus = allset; cpu_helper->cpu_mask = g_new0(guint64, (cpus >> 6) + 1); g_assert(cpu_helper->cpu_mask != NULL); gtk_widget_show(check); for (cpu = 0; cpu < cpus; cpu++) { g_snprintf(counter, 100, "CPU %d", cpu); check = gtk_check_button_new_with_label(counter); cpu_helper->buttons[cpu] = check; gtk_box_pack_start(GTK_BOX(vbox), check, TRUE, FALSE, 0); if (cpus_selected && cpu_isset(cpus_selected, cpu)) { cpu_set(cpu_helper->cpu_mask, cpu); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); } else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE); g_signal_connect_swapped (check, "toggled", G_CALLBACK (cpu_toggle), (gpointer) cpu_helper); gtk_widget_show(check); } /* Figure out a good size to show */ gtk_widget_size_request(hbox, &req); height = req.height; gtk_widget_size_request(scrollwin, &req); height += req.height; gtk_widget_size_request(dialog, &req); width = req.width; height += req.height; if (width > DIALOG_WIDTH) width = DIALOG_WIDTH; if (height > DIALOG_HEIGHT) height = DIALOG_HEIGHT; gtk_widget_set_size_request(GTK_WIDGET(dialog), width, height); gtk_widget_show_all(dialog); result = gtk_dialog_run(GTK_DIALOG(dialog)); switch (result) { case GTK_RESPONSE_ACCEPT: if (!cpu_helper->allcpus) { cpu_mask = cpu_helper->cpu_mask; cpu_helper->cpu_mask = NULL; } func(TRUE, cpu_helper->allcpus, cpu_mask, data); break; case GTK_RESPONSE_REJECT: func(FALSE, FALSE, NULL, data); break; default: break; }; g_free(cpu_mask); gtk_widget_destroy(dialog); destroy_cpu_helper(cpu_helper); } /* -- Helper functions -- */ /** * trace_filter_convert_filter_to_names - convert a filter to names. * @filter: the filter to convert * @systems: array of systems that the filter selects (may be NULL) * @event_ids: array of event ids that the filter selects (may be NULL) * * @systems will be filled when the filter selects all events within * its system. (may return NULL) * * @event_ids will be all events selected (not including those selected * by @systems) */ void trace_filter_convert_filter_to_names(struct event_filter *filter, gchar ***systems, gint **event_ids) { struct pevent *pevent = filter->pevent; struct event_format **events; struct event_format *event; char *last_system = NULL; int all_selected = 1; int start_sys = 0; int sys_count = 0; int event_count = 0; int i, x; if (systems) *systems = NULL; if (event_ids) *event_ids = NULL; events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); for (i = 0; events[i]; i++) { event = events[i]; if (systems && last_system && strcmp(last_system, events[i]->system) != 0) { if (all_selected) *systems = tracecmd_add_list(*systems, last_system, sys_count++); start_sys = i; all_selected = 1; } if (pevent_filter_event_has_trivial(filter, event->id, FILTER_TRIVIAL_TRUE)) { if (!all_selected || !systems) *event_ids = tracecmd_add_id(*event_ids, event->id, event_count++); } else { if (all_selected && event_ids) { for (x = start_sys; x < i; x++) { *event_ids = tracecmd_add_id(*event_ids, events[x]->id, event_count++); } } all_selected = 0; /* If this event is filtered, still add it */ if (pevent_event_filtered(filter, event->id)) *event_ids = tracecmd_add_id(*event_ids, event->id, event_count++); } last_system = event->system; } if (systems && last_system && all_selected) *systems = tracecmd_add_list(*systems, last_system, sys_count++); } /** * trace_filter_convert_char_to_filter - convert the strings to the filter * @filter: the filter to convert * @systems: array of systems that will have its events selected in @filter * @events: array of event ids that will be selected in @filter */ void trace_filter_convert_char_to_filter(struct event_filter *filter, gchar **systems, gint *events) { struct pevent *pevent; struct event_filter *copy; struct event_format *event; int i; pevent = filter->pevent; /* Make a copy to use later */ copy = pevent_filter_alloc(pevent); pevent_filter_copy(copy, filter); pevent_filter_reset(filter); if (systems) { for (i = 0; systems[i]; i++) pevent_filter_add_filter_str(filter, systems[i]); } if (events) { for (i = 0; events[i] >= 0; i++) { event = pevent_find_event(filter->pevent, events[i]); if (event) pevent_filter_add_filter_str(filter, event->name); } } pevent_update_trivial(filter, copy, FILTER_TRIVIAL_BOTH); pevent_filter_free(copy); } int trace_filter_save_events(struct tracecmd_xml_handle *handle, struct event_filter *filter) { struct event_format *event; char **systems; gint *event_ids; char *str; int i; trace_filter_convert_filter_to_names(filter, &systems, &event_ids); for (i = 0; systems && systems[i]; i++) tracecmd_xml_write_element(handle, "System", systems[i]); for (i = 0; event_ids && event_ids[i] > 0; i++) { str = pevent_filter_make_string(filter, event_ids[i]); if (!str) continue; event = pevent_find_event(filter->pevent, event_ids[i]); if (event) { /* skip not filtered items */ if (strcmp(str, "FALSE") == 0) { free(str); continue; } tracecmd_xml_start_sub_system(handle, "Event"); tracecmd_xml_write_element(handle, "System", event->system); tracecmd_xml_write_element(handle, "Name", event->name); /* If this is has an advanced filter, include that too */ if (strcmp(str, "TRUE") != 0) { tracecmd_xml_write_element(handle, "Advanced", str); } tracecmd_xml_end_sub_system(handle); } free(str); } return 0; } int trace_filter_save_tasks(struct tracecmd_xml_handle *handle, struct filter_task *filter) { char buffer[100]; int *pids; int i; pids = filter_task_pids(filter); if (!pids) return -1; for (i = 0; pids[i] >= 0; i++) { snprintf(buffer, 100, "%d", pids[i]); tracecmd_xml_write_element(handle, "Task", buffer); } free(pids); return 0; } int trace_filter_load_events(struct event_filter *event_filter, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node) { struct tracecmd_xml_system_node *child; const char *name; const char *system; const char *event; const char *value; char *buffer; while (node) { name = tracecmd_xml_node_type(node); if (strcmp(name, "System") == 0) { system = tracecmd_xml_node_value(handle, node); pevent_filter_add_filter_str(event_filter, system); } else if (strcmp(name, "Event") == 0) { system = NULL; event = NULL; value = NULL; child = tracecmd_xml_node_child(node); if (!child) return -1; do { name = tracecmd_xml_node_type(child); if (strcmp(name, "System") == 0) system = tracecmd_xml_node_value(handle, child); else if (strcmp(name, "Name") == 0) event = tracecmd_xml_node_value(handle, child); else if (strcmp(name, "Advanced") == 0) value = tracecmd_xml_node_value(handle, child); child = tracecmd_xml_node_next(child); } while (child); if (event || system) { if (event && system) { if (value) { buffer = malloc_or_die(strlen(event) + strlen(system) + strlen(value) + 3); sprintf(buffer, "%s/%s:%s", system, event, value); } else { buffer = malloc_or_die(strlen(event) + strlen(system) + 2); sprintf(buffer, "%s/%s", system, event); } } else { if (!event) event = system; if (value) { buffer = malloc_or_die(strlen(event) + strlen(value) + 2); sprintf(buffer, "%s:%s", event, value); } else { buffer = malloc_or_die(strlen(event) + 1); sprintf(buffer, "%s", event); } } pevent_filter_add_filter_str(event_filter, buffer); free(buffer); } } node = tracecmd_xml_node_next(node); } return 0; } int trace_filter_load_task_filter(struct filter_task *filter, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node) { const char *name; const char *task; int pid; if (!filter) return 0; node = tracecmd_xml_node_child(node); while (node) { name = tracecmd_xml_node_type(node); if (strcmp(name, "Task") == 0) { task = tracecmd_xml_node_value(handle, node); pid = atoi(task); if (!filter_task_find_pid(filter, pid)) filter_task_add_pid(filter, pid); } node = tracecmd_xml_node_next(node); } return 0; } int trace_filter_load_filters(struct tracecmd_xml_handle *handle, const char *system_name, struct filter_task *task_filter, struct filter_task *hide_tasks) { struct tracecmd_xml_system *system; struct tracecmd_xml_system_node *syschild; const char *name; system = tracecmd_xml_find_system(handle, system_name); if (!system) return -1; syschild = tracecmd_xml_system_node(system); if (!syschild) goto out_free_sys; do { name = tracecmd_xml_node_type(syschild); if (strcmp(name, "TaskFilter") == 0) trace_filter_load_task_filter(task_filter, handle, syschild); else if (strcmp(name, "HideTasks") == 0) trace_filter_load_task_filter(hide_tasks, handle, syschild); syschild = tracecmd_xml_node_next(syschild); } while (syschild); tracecmd_xml_free_system(system); return 0; out_free_sys: tracecmd_xml_free_system(system); return -1; } int trace_filter_save_filters(struct tracecmd_xml_handle *handle, const char *system_name, struct filter_task *task_filter, struct filter_task *hide_tasks) { tracecmd_xml_start_system(handle, system_name); if (task_filter && filter_task_count(task_filter)) { tracecmd_xml_start_sub_system(handle, "TaskFilter"); trace_filter_save_tasks(handle, task_filter); tracecmd_xml_end_sub_system(handle); } if (hide_tasks && filter_task_count(hide_tasks)) { tracecmd_xml_start_sub_system(handle, "HideTasks"); trace_filter_save_tasks(handle, hide_tasks); tracecmd_xml_end_sub_system(handle); } tracecmd_xml_end_system(handle); return 0; } trace-cmd-2.5.3/trace-filter.h000066400000000000000000000147101246701203100161110ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_FILTER_H #define _TRACE_FILTER_H #include #include "trace-xml.h" struct event_filter_list { struct event_filter_list *next; struct event *event; }; /** * trace_adv_filter_cb_func - callback type for advanced filter dialog * @accept: TRUE if the accept button was pressed, otherwise FALSE * @text: The text that was entered * @delete_event_filters: The list of event ids for filters to remove * @data: The data given passed in to the event dialog function * * If @accept is FALSE then @text and @delete_event_filters * should be ignored. @data is still valid. * * @text may be NULL or empty, and @delete_event_ids may also be NULL. * @delete_event_ids if not NULL, then ends with -1 */ typedef void (*trace_adv_filter_cb_func)(gboolean accept, const gchar *text, gint *delete_event_filters, gpointer data); /** * trace_task_cb_func - callback type for task dialog * @accept: TRUE if the accept button was pressed, otherwise FALSE * @selected: list of pids of tasks selected * @non_select: list of pids of tasks not selected * @data: The data given passed in to the event dialog function * * If @accept is FALSE then @selected and @non_select * should be ignored. @data is still valid. * * Both @selected and @non_select may be NULL, if either is not * NULL they will be sorted and end with -1. */ typedef void (*trace_task_cb_func)(gboolean accept, gint *selected, gint *non_selected, gpointer data); /** * trace_filter_event_cb_func - callback type for event dialog * @accept: TRUE if the accept button was pressed, otherwise FALSE * @all_events: TRUE if "All Events" was checked * @systems: NULL or a string array of systems terminated with NULL * @events: NULL or a int array of event ids terminated with -1 * @data: The data given passed in to the event dialog function * * If @accept is FALSE then @all_events, @systems, and @events * should be ignored. @data is still valid. * * If @all_events is TRUE then @systems and @events should be ignored. */ typedef void (*trace_filter_event_cb_func)(gboolean accept, gboolean all_events, char **systems, gint *events, gpointer data); void trace_adv_filter_dialog(struct tracecmd_input *handle, struct event_filter *event_filter, trace_adv_filter_cb_func func, gpointer data); void trace_task_dialog(struct tracecmd_input *handle, gint *tasks, gint *selected, trace_task_cb_func func, gpointer data); void trace_filter_event_dialog(struct tracecmd_input *handle, gboolean all_events, gchar **systems, gint *events, trace_filter_event_cb_func func, gpointer data); void trace_filter_pevent_dialog(struct pevent *pevent, gboolean all_events, gchar **systems, gint *events, trace_filter_event_cb_func func, gpointer data); void trace_filter_event_filter_dialog(struct tracecmd_input *handle, struct event_filter *filter, gboolean all_events, trace_filter_event_cb_func func, gpointer data); void trace_filter_convert_filter_to_names(struct event_filter *filter, gchar ***systems, gint **events); void trace_filter_convert_char_to_filter(struct event_filter *filter, gchar **systems, gint *events); /** * trace_filter_cpu_cb_func - callback type for CPU dialog * @accept: TRUE if the accept button was pressed, otherwise FALSE * @all_cpus: TRUE if "All CPUS" was checked * @selected_cpus: NULL or a cpu_mask with the cpus that were checked set. * @data: The data given passed in to the CPU dialog function * * If @accept is FALSE then @all_cpus and @selected_cpus should be ignored. * @data is still valid. * * If @all_cpus is TRUE then @selected_cpus should be ignored. */ typedef void (*trace_filter_cpu_cb_func)(gboolean accept, gboolean all_cpus, guint64 *selected_cpus, gpointer data); void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint cpus, trace_filter_cpu_cb_func func, gpointer data); void trace_array_add(gint **array, gint *count, gint val); /* save and load filters */ int trace_filter_save_events(struct tracecmd_xml_handle *handle, struct event_filter *filter); int trace_filter_save_tasks(struct tracecmd_xml_handle *handle, struct filter_task *filter); int trace_filter_load_events(struct event_filter *event_filter, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node); int trace_filter_load_task_filter(struct filter_task *filter, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node); int trace_filter_load_filters(struct tracecmd_xml_handle *handle, const char *system_name, struct filter_task *task_filter, struct filter_task *hide_tasks); int trace_filter_save_filters(struct tracecmd_xml_handle *handle, const char *system_name, struct filter_task *task_filter, struct filter_task *hide_tasks); GtkWidget *trace_create_event_list_view(struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems, gint *events); gint trace_extract_event_list_view(GtkWidget *event_view, gboolean *all_events, gchar ***systems, gint **events); int trace_update_event_view(GtkWidget *event_view, struct pevent *pevent, struct event_filter *filter, gboolean all_events, gchar **systems, gint *events); /* put here because there's no other place */ int str_cmp(const void *a, const void *b); int id_cmp(const void *a, const void *b); #endif /* _TRACE_FILTER_H */ trace-cmd-2.5.3/trace-ftrace.c000066400000000000000000000267511246701203100160730ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "trace-cmd.h" struct 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 plugin_option *fgraph_tail = &trace_ftrace_options[0]; static struct 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 pevent *pevent) { struct event_format *event; /* Store the func ret id and event for later use */ event = pevent_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 pevent_record *record, struct event_format *event, void *context) { struct pevent *pevent = event->pevent; unsigned long long function; const char *func; if (pevent_get_field_val(s, event, "ip", record, &function, 1)) return trace_seq_putc(s, '!'); func = pevent_find_function(pevent, function); if (func) trace_seq_printf(s, "%s <-- ", func); else trace_seq_printf(s, "0x%llx", function); if (pevent_get_field_val(s, event, "parent_ip", record, &function, 1)) return trace_seq_putc(s, '!'); func = pevent_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 pevent_record * get_return_for_leaf(struct trace_seq *s, int cpu, int cur_pid, unsigned long long cur_func, struct pevent_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 (pevent_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 (pevent_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 (pevent_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 100 msecs */ if (duration > 100000ULL) return (void)trace_seq_printf(s, "! "); /* Duration exceeded 10 msecs */ 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 event_format *event, struct pevent_record *record, struct pevent_record *ret_rec, struct tracecmd_ftrace *finfo) { struct pevent *pevent = event->pevent; unsigned long long rettime, calltime; unsigned long long duration, depth; unsigned long long val; const char *func; int ret; int i; if (pevent_get_field_val(s, finfo->fgraph_ret_event, "rettime", ret_rec, &rettime, 1)) return trace_seq_putc(s, '!'); if (pevent_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 (pevent_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 (pevent_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); func = pevent_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 event_format *event, struct pevent_record *record) { struct pevent *pevent = event->pevent; 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 (pevent_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 (pevent_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); func = pevent_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 pevent_record *record, struct event_format *event, void *context) { struct tracecmd_ftrace *finfo = context; struct pevent_record *rec; unsigned long long val, pid; int cpu = record->cpu; ret_event_check(finfo, event->pevent); if (pevent_get_common_field_val(s, event, "common_pid", record, &pid, 1)) return trace_seq_putc(s, '!'); if (pevent_get_field_val(s, event, "func", record, &val, 1)) return trace_seq_putc(s, '!'); rec = tracecmd_peek_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 pevent_record *record, struct event_format *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->pevent); if (pevent_get_field_val(s, event, "rettime", record, &rettime, 1)) return trace_seq_putc(s, '!'); if (pevent_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 (pevent_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 (pevent_get_field_val(s, event, "func", record, &val, 0)) return 0; func = pevent_find_function(event->pevent, 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 pevent_record *record, struct event_format *event, void *context) { struct tracecmd_ftrace *finfo = context; struct format_field *field; unsigned long long addr; const char *func; void *data = record->data; field = pevent_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 = pevent_read_number(event->pevent, data, finfo->long_size); if ((finfo->long_size == 8 && addr == (unsigned long long)-1) || ((int)addr == -1)) break; func = pevent_find_function(event->pevent, 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 pevent *pevent; struct event_format *event; finfo->handle = handle; pevent = tracecmd_get_pevent(handle); pevent_register_event_handler(pevent, -1, "ftrace", "function", function_handler, NULL); pevent_register_event_handler(pevent, -1, "ftrace", "funcgraph_entry", fgraph_ent_handler, finfo); pevent_register_event_handler(pevent, -1, "ftrace", "funcgraph_exit", fgraph_ret_handler, finfo); pevent_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 = pevent_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.5.3/trace-graph-main.c000066400000000000000000000270501246701203100166430ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include "trace-cmd.h" #include "trace-graph.h" #include "trace-filter.h" #include "trace-gui.h" #include "version.h" #define version "0.1.1" #define TRACE_WIDTH 800 #define TRACE_HEIGHT 600 #define default_input_file "trace.dat" static char *input_file; static struct graph_info *ginfo; void usage(char *prog) { printf("Usage: %s\n", prog); printf(" -h Display this help message\n"); printf(" -i input_file, default is %s\n", default_input_file); } /* Callback for the clicked signal of the Load button */ static void load_clicked (gpointer data) { struct graph_info *ginfo = data; struct tracecmd_input *handle; gchar *filename; filename = trace_get_file_dialog("Load File", NULL, FALSE); if (!filename) return; handle = tracecmd_open(filename); if (handle) { trace_graph_load_handle(ginfo, handle); /* Free handle when freeing graph */ tracecmd_close(handle); } g_free(filename); } /* Callback for the clicked signal of the Exit button */ static void exit_clicked (GtkWidget *widget, gpointer data) { gtk_widget_destroy (GTK_WIDGET (data)); /* the user data points to the main window */ tracecmd_close(ginfo->handle); gtk_main_quit (); } /* Callback for the delete_event signal of the main application window */ static gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { gtk_widget_destroy (widget); /* destroy the main window */ tracecmd_close(ginfo->handle); gtk_main_quit (); return TRUE; } /* Callback for the clicked signal of the Events filter button */ static void events_clicked (gpointer data) { struct graph_info *ginfo = data; gboolean all_events = TRUE; if (!ginfo->handle) return; all_events = ginfo->all_events; trace_filter_event_filter_dialog(ginfo->handle, ginfo->event_filter, all_events, trace_graph_event_filter_callback, ginfo); } /* Callback for the clicked signal of the Advanced filter button */ static void adv_filter_clicked (gpointer data) { struct graph_info *ginfo = data; trace_adv_filter_dialog(ginfo->handle, ginfo->event_filter, trace_graph_adv_filter_callback, ginfo); } /* Callback for the clicked signal of the plot CPUs button */ static void plot_cpu_clicked (gpointer data) { struct graph_info *ginfo = data; gboolean all_cpus; guint64 *cpu_mask; if (!ginfo->handle) return; graph_plot_cpus_plotted(ginfo, &all_cpus, &cpu_mask); trace_filter_cpu_dialog(all_cpus, cpu_mask, ginfo->cpus, graph_plot_cpus_update_callback, ginfo); g_free(cpu_mask); } /* Callback for the clicked signal of the plot tasks button */ static void plot_tasks_clicked (gpointer data) { struct graph_info *ginfo = data; gint *selected; gint *tasks; if (!ginfo->handle) return; tasks = trace_graph_task_list(ginfo); graph_plot_task_plotted(ginfo, &selected); trace_task_dialog(ginfo->handle, tasks, selected, graph_plot_task_update_callback, ginfo); free(tasks); free(selected); } /* Callback for the clicked signal of the Load Filters button */ static void load_filters_clicked (gpointer data) { struct graph_info *ginfo = data; struct tracecmd_xml_handle *handle; gchar *filename; filename = trace_get_file_dialog("Load Filters", NULL, FALSE); if (!filename) return; handle = tracecmd_xml_open(filename); if (!handle) warning("Could not open %s", filename); g_free(filename); trace_filter_load_filters(handle, "GraphTaskFilter", ginfo->task_filter, ginfo->hide_tasks); trace_graph_load_filters(ginfo, handle); tracecmd_xml_close(handle); } /* Callback for the clicked signal of the Save Filters button */ static void save_filters_clicked (gpointer data) { struct graph_info *ginfo = data; struct tracecmd_xml_handle *handle; gchar *filename; filename = trace_get_file_dialog("Save Filters", "Save", TRUE); if (!filename) return; handle = tracecmd_xml_create(filename, VERSION_STRING); if (!handle) warning("Could not create %s", filename); g_free(filename); trace_filter_save_filters(handle, "GraphTaskFilter", ginfo->task_filter, ginfo->hide_tasks); trace_graph_save_filters(ginfo, handle); tracecmd_xml_close(handle); } void trace_graph(int argc, char **argv) { struct tracecmd_input *handle = NULL; struct stat st; GtkWidget *window; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *menu_bar; GtkWidget *menu; GtkWidget *menu_item; GtkWidget *sub_item; GtkWidget *widget; GtkWidget *statusbar; int c; int ret; gtk_init(&argc, &argv); while ((c = getopt(argc, argv, "hi:")) != -1) { switch(c) { case 'h': usage(basename(argv[0])); return; case 'i': input_file = optarg; break; default: /* assume the other options are for gtk */ break; } } if ((argc - optind) >= 1) { if (input_file) usage(basename(argv[0])); input_file = argv[optind]; } if (!input_file) { ret = stat(default_input_file, &st); if (ret >= 0) input_file = default_input_file; } if (input_file) handle = tracecmd_open(input_file); /* graph struct is used by handlers */ ginfo = trace_graph_create(handle); /* Free handle when freeing graph */ if (handle) tracecmd_close(handle); /* --- Main window --- */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); trace_dialog_register_window(window); /* --- Top Level Vbox --- */ vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER (window), vbox); gtk_widget_show(vbox); /* --- Menu Bar --- */ menu_bar = gtk_menu_bar_new(); gtk_box_pack_start(GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0); gtk_widget_show(menu_bar); /* --- File Option --- */ menu_item = gtk_menu_item_new_with_label("File"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- File - Load Option --- */ sub_item = gtk_menu_item_new_with_label("Load info"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (load_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- File - Load Filter Option --- */ sub_item = gtk_menu_item_new_with_label("Load filters"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (load_filters_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- File - Save Filter Option --- */ sub_item = gtk_menu_item_new_with_label("Save filters"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (save_filters_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- File - Quit Option --- */ sub_item = gtk_menu_item_new_with_label("Quit"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (exit_clicked), (gpointer) window); /* We do need to show menu items */ gtk_widget_show(sub_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- end File options --- */ /* --- Filter Option --- */ menu_item = gtk_menu_item_new_with_label("Filter"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Filter - Events Option --- */ sub_item = gtk_menu_item_new_with_label("events"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (events_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Filter - Advanced Events Option --- */ sub_item = gtk_menu_item_new_with_label("advanced event filter"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (adv_filter_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Filter Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Plot Option --- */ menu_item = gtk_menu_item_new_with_label("Plots"); gtk_widget_show(menu_item); gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); menu = gtk_menu_new(); /* Don't need to show menus */ /* --- Plot - CPUs Option --- */ sub_item = gtk_menu_item_new_with_label("CPUs"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (plot_cpu_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- Plot - Tasks Option --- */ sub_item = gtk_menu_item_new_with_label("Tasks"); /* Add them to the menu */ gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); /* We can attach the Quit menu item to our exit function */ g_signal_connect_swapped (G_OBJECT (sub_item), "activate", G_CALLBACK (plot_tasks_clicked), (gpointer) ginfo); /* We do need to show menu items */ gtk_widget_show(sub_item); /* --- End Plot Options --- */ gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); /* --- Top Level Hbox --- */ hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); /* --- Set up the Graph --- */ widget = trace_graph_get_window(ginfo); gtk_box_pack_start(GTK_BOX (hbox), widget, TRUE, TRUE, 0); gtk_widget_show(widget); /* --- Set up Status Bar --- */ statusbar = trace_status_bar_new(); gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); gtk_widget_show(statusbar); /********************************************** * Main Window **********************************************/ /* Connect to the delete_event signal and Run the application */ gtk_signal_connect (GTK_OBJECT (window), "delete_event", (GtkSignalFunc) delete_event, NULL); gtk_widget_set_size_request(window, TRACE_WIDTH, TRACE_HEIGHT); gtk_widget_show (window); gtk_main (); } int main(int argc, char **argv) { trace_graph(argc, argv); return 0; } trace-cmd-2.5.3/trace-graph.c000066400000000000000000002105501246701203100157200ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include "trace-compat.h" #include "trace-cmd.h" #include "trace-local.h" #include "trace-graph.h" #include "trace-filter-hash.h" #include "trace-filter.h" #include "trace-gui.h" #include "event-utils.h" #define DEBUG_LEVEL 0 #if DEBUG_LEVEL > 0 # define dprintf(l, x...) \ do { \ if (l <= DEBUG_LEVEL) \ printf(x); \ } while (0) #else # define dprintf(l, x...) do { if (0) printf(x); } while (0) #endif #define MAX_WIDTH 10000 #define PLOT_SIZE 10 #define PLOT_BOX_SIZE PLOT_SIZE #define PLOT_GIVE 2 #define PLOT_BEGIN 80 #define PLOT_SEP 50 #define PLOT_LINE(plot) (PLOT_SEP * (plot) + PLOT_BEGIN + PLOT_SIZE) #define PLOT_TOP(plot) (PLOT_LINE(plot) - PLOT_SIZE * 2) #define PLOT_BOX_TOP(plot) (PLOT_LINE(plot) - PLOT_SIZE) #define PLOT_BOTTOM(plot) (PLOT_LINE(plot)-1) #define PLOT_BOX_BOTTOM(plot) (PLOT_LINE(plot)) #define PLOT_SPACE(plots) (PLOT_SEP * (plots) + PLOT_BEGIN) #define PLOT_LABEL(plot) (PLOT_TOP(plot)) #define PLOT_X 5 static gint largest_plot_label; static GdkGC *green; static GdkGC *red; static void redraw_pixmap_backend(struct graph_info *ginfo); static void update_label_window(struct graph_info *ginfo); struct task_list { struct task_list *next; gint pid; }; static guint get_task_hash_key(gint pid) { return trace_hash(pid) % TASK_HASH_SIZE; } static struct task_list *find_task_hash(struct graph_info *ginfo, gint key, gint pid) { struct task_list *list; for (list = ginfo->tasks[key]; list; list = list->next) { if (list->pid == pid) return list; } return NULL; } static struct task_list *add_task_hash(struct graph_info *ginfo, int pid) { struct task_list *list; guint key = get_task_hash_key(pid); list = find_task_hash(ginfo, key, pid); if (list) return list; list = malloc_or_die(sizeof(*list)); list->pid = pid; list->next = ginfo->tasks[key]; ginfo->tasks[key] = list; return list; } static void free_task_hash(struct graph_info *ginfo) { struct task_list *list; int i; for (i = 0; i < TASK_HASH_SIZE; i++) { while (ginfo->tasks[i]) { list = ginfo->tasks[i]; ginfo->tasks[i] = list->next; free(list); } } } /** * trace_graph_task_list - return an allocated list of all found tasks * @ginfo: The graph info structure * * Returns an allocated list of pids found in the graph, ending * with a -1. This array must be freed with free(). */ gint *trace_graph_task_list(struct graph_info *ginfo) { struct task_list *list; gint *pids; gint count = 0; gint i; for (i = 0; i < TASK_HASH_SIZE; i++) { list = ginfo->tasks[i]; while (list) { if (count) pids = realloc(pids, sizeof(*pids) * (count + 2)); else pids = malloc(sizeof(*pids) * 2); pids[count++] = list->pid; pids[count] = -1; list = list->next; } } return pids; } static void convert_nano(unsigned long long time, unsigned long *sec, unsigned long *usec) { *sec = time / 1000000000ULL; *usec = (time / 1000) % 1000000; } static int convert_time_to_x(struct graph_info *ginfo, guint64 time) { if (time < ginfo->view_start_time) return 0; return (time - ginfo->view_start_time) * ginfo->resolution; } static guint64 convert_x_to_time(struct graph_info *ginfo, gint x) { double d = x; return (guint64)(d / ginfo->resolution) + ginfo->view_start_time; } static void print_time(unsigned long long time) { unsigned long sec, usec; if (!DEBUG_LEVEL) return; convert_nano(time, &sec, &usec); printf("%lu.%06lu", sec, usec); } static void init_event_cache(struct graph_info *ginfo) { ginfo->ftrace_sched_switch_id = -1; ginfo->event_sched_switch_id = -1; ginfo->event_wakeup_id = -1; ginfo->event_wakeup_new_id = -1; ginfo->event_pid_field = NULL; ginfo->event_comm_field = NULL; ginfo->ftrace_pid_field = NULL; ginfo->ftrace_comm_field = NULL; ginfo->wakeup_pid_field = NULL; ginfo->wakeup_success_field = NULL; ginfo->wakeup_new_pid_field = NULL; ginfo->wakeup_new_success_field = NULL; /* * The first time reading the through the list * test the sched_switch for comms that did not make * it into the pevent command line list. */ ginfo->read_comms = TRUE; } struct filter_task_item * trace_graph_filter_task_find_pid(struct graph_info *ginfo, gint pid) { return filter_task_find_pid(ginfo->task_filter, pid); } struct filter_task_item * trace_graph_hide_task_find_pid(struct graph_info *ginfo, gint pid) { return filter_task_find_pid(ginfo->hide_tasks, pid); } static void graph_filter_task_add_pid(struct graph_info *ginfo, gint pid) { filter_task_add_pid(ginfo->task_filter, pid); ginfo->filter_available = 1; } static void graph_filter_task_remove_pid(struct graph_info *ginfo, gint pid) { filter_task_remove_pid(ginfo->task_filter, pid); if (!filter_task_count(ginfo->task_filter) && !filter_task_count(ginfo->hide_tasks)) { ginfo->filter_available = 0; ginfo->filter_enabled = 0; } } static void graph_hide_task_add_pid(struct graph_info *ginfo, gint pid) { filter_task_add_pid(ginfo->hide_tasks, pid); ginfo->filter_available = 1; } static void graph_hide_task_remove_pid(struct graph_info *ginfo, gint pid) { filter_task_remove_pid(ginfo->hide_tasks, pid); if (!filter_task_count(ginfo->task_filter) && !filter_task_count(ginfo->hide_tasks)) { ginfo->filter_available = 0; ginfo->filter_enabled = 0; } } static void graph_filter_task_clear(struct graph_info *ginfo) { filter_task_clear(ginfo->task_filter); filter_task_clear(ginfo->hide_tasks); ginfo->filter_available = 0; ginfo->filter_enabled = 0; } gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct pevent_record *record) { int ret; if (!record) return TRUE; if (ginfo->all_events) return FALSE; ret = pevent_filter_match(ginfo->event_filter, record); return ret == FILTER_MATCH ? FALSE : TRUE; } gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid) { gboolean filter; filter = FALSE; if (ginfo->filter_enabled && ((filter_task_count(ginfo->task_filter) && !trace_graph_filter_task_find_pid(ginfo, pid)) || (filter_task_count(ginfo->hide_tasks) && trace_graph_hide_task_find_pid(ginfo, pid)))) filter = TRUE; return filter; } static void __update_with_backend(struct graph_info *ginfo, gint x, gint y, gint width, gint height) { gdk_draw_drawable(ginfo->draw->window, ginfo->draw->style->fg_gc[GTK_WIDGET_STATE(ginfo->draw)], ginfo->curr_pixmap, x, y, x, y, width, height); } static void update_label_time(GtkWidget *label, gint64 time) { unsigned long sec, usec; struct trace_seq s; char *min = ""; if (time < 0) { time *= -1; min = "-"; } convert_nano(time, &sec, &usec); trace_seq_init(&s); trace_seq_printf(&s, "%s%lu.%06lu", min, sec, usec); gtk_label_set_text(GTK_LABEL(label), s.buffer); trace_seq_destroy(&s); } static void update_cursor(struct graph_info *ginfo) { update_label_time(ginfo->cursor_label, ginfo->cursor); } static void update_pointer(struct graph_info *ginfo, gint x) { guint64 time; time = convert_x_to_time(ginfo, x); update_label_time(ginfo->pointer_time, time); } static void update_marka(struct graph_info *ginfo, gint x) { guint64 timeA; timeA = convert_x_to_time(ginfo, x); ginfo->marka_time = timeA; update_label_time(ginfo->marka_label, timeA); } static void update_markb(struct graph_info *ginfo, guint x) { gint64 timeA, timeB; timeA = ginfo->marka_time; timeB = convert_x_to_time(ginfo, x); ginfo->markb_time = timeB; update_label_time(ginfo->markb_label, timeB); update_label_time(ginfo->delta_label, timeB - timeA); } static void draw_cursor(struct graph_info *ginfo) { gint x; if (!ginfo->cursor) return; update_cursor(ginfo); if (ginfo->cursor < ginfo->view_start_time || ginfo->cursor > ginfo->view_end_time) return; x = convert_time_to_x(ginfo, ginfo->cursor); gdk_draw_line(ginfo->draw->window, ginfo->draw->style->mid_gc[3], x, 0, x, ginfo->draw->allocation.height); } static void draw_marka(struct graph_info *ginfo) { gint x; if (!ginfo->show_marka) return; x = convert_time_to_x(ginfo, ginfo->marka_time); gdk_draw_line(ginfo->draw->window, green, x, 0, x, ginfo->draw->allocation.height); } static void draw_markb(struct graph_info *ginfo) { gint x; if (!ginfo->show_markb) return; x = convert_time_to_x(ginfo, ginfo->markb_time); gdk_draw_line(ginfo->draw->window, red, x, 0, x, ginfo->draw->allocation.height); } static void update_with_backend(struct graph_info *ginfo, gint x, gint y, gint width, gint height) { __update_with_backend(ginfo, x, y, width, height); draw_cursor(ginfo); draw_markb(ginfo); draw_marka(ginfo); } static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct graph_info *ginfo = data; update_with_backend(ginfo, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } static void draw_line(GtkWidget *widget, gdouble x, struct graph_info *ginfo) { gdk_draw_line(widget->window, widget->style->black_gc, x, 0, x, widget->allocation.height); } static void clear_line(struct graph_info *ginfo, gint x) { if (x) x--; update_with_backend(ginfo, x, 0, x+2, ginfo->draw->allocation.height); } static void clear_info_box(struct graph_info *ginfo) { update_with_backend(ginfo, ginfo->plot_data_x, ginfo->plot_data_y, ginfo->plot_data_w, ginfo->plot_data_h); } static void redraw_graph(struct graph_info *ginfo) { gdouble height; gdouble width; redraw_pixmap_backend(ginfo); width = ginfo->draw->allocation.width; height = ginfo->draw->allocation.height; update_with_backend(ginfo, 0, 0, width, height); } void trace_graph_filter_toggle(struct graph_info *ginfo) { ginfo->filter_enabled ^= 1; redraw_graph(ginfo); } static void filter_enable_clicked (gpointer data) { struct graph_info *ginfo = data; trace_graph_filter_toggle(ginfo); } void trace_graph_filter_add_remove_task(struct graph_info *ginfo, gint pid) { gint filter_enabled = ginfo->filter_enabled; struct filter_task_item *task; task = trace_graph_filter_task_find_pid(ginfo, pid); if (task) graph_filter_task_remove_pid(ginfo, task->pid); else graph_filter_task_add_pid(ginfo, pid); if (ginfo->callbacks && ginfo->callbacks->filter) ginfo->callbacks->filter(ginfo, ginfo->task_filter, ginfo->hide_tasks); if (filter_enabled) redraw_graph(ginfo); } void trace_graph_filter_hide_show_task(struct graph_info *ginfo, gint pid) { gint filter_enabled = ginfo->filter_enabled; struct filter_task_item *task; task = trace_graph_hide_task_find_pid(ginfo, pid); if (task) graph_hide_task_remove_pid(ginfo, task->pid); else graph_hide_task_add_pid(ginfo, pid); if (ginfo->callbacks && ginfo->callbacks->filter) ginfo->callbacks->filter(ginfo, ginfo->task_filter, ginfo->hide_tasks); if (filter_enabled) redraw_graph(ginfo); } static void filter_add_task_clicked (gpointer data) { struct graph_info *ginfo = data; trace_graph_filter_add_remove_task(ginfo, ginfo->filter_task_selected); } static void filter_hide_task_clicked (gpointer data) { struct graph_info *ginfo = data; trace_graph_filter_hide_show_task(ginfo, ginfo->filter_task_selected); } void trace_graph_clear_tasks(struct graph_info *ginfo) { gint filter_enabled = ginfo->filter_enabled; graph_filter_task_clear(ginfo); if (ginfo->callbacks && ginfo->callbacks->filter) ginfo->callbacks->filter(ginfo, ginfo->task_filter, ginfo->hide_tasks); if (filter_enabled) redraw_graph(ginfo); } void trace_graph_update_filters(struct graph_info *ginfo, struct filter_task *task_filter, struct filter_task *hide_tasks) { /* Make sure the filter passed in is not the filter we use */ if (task_filter != ginfo->task_filter) { filter_task_hash_free(ginfo->task_filter); ginfo->task_filter = filter_task_hash_copy(task_filter); } if (hide_tasks != ginfo->hide_tasks) { filter_task_hash_free(ginfo->hide_tasks); ginfo->hide_tasks = filter_task_hash_copy(hide_tasks); } if (ginfo->callbacks && ginfo->callbacks->filter) ginfo->callbacks->filter(ginfo, ginfo->task_filter, ginfo->hide_tasks); if (ginfo->filter_enabled) redraw_graph(ginfo); if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) ginfo->filter_available = 1; else { ginfo->filter_enabled = 0; ginfo->filter_available = 0; } } void trace_graph_refresh_filters(struct graph_info *ginfo) { trace_graph_update_filters(ginfo, ginfo->task_filter, ginfo->hide_tasks); } static void filter_clear_tasks_clicked (gpointer data) { struct graph_info *ginfo = data; trace_graph_clear_tasks(ginfo); } static void plot_task_clicked (gpointer data) { struct graph_info *ginfo = data; struct graph_plot *plot = ginfo->plot_clicked; int pos; if (plot) pos = plot->pos + 1; else pos = ginfo->plots + 1; graph_plot_task(ginfo, ginfo->filter_task_selected, pos); ginfo->draw_height = PLOT_SPACE(ginfo->plots); gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height); update_label_window(ginfo); } static void remove_plot_clicked (gpointer data) { struct graph_info *ginfo = data; struct graph_plot *plot = ginfo->plot_clicked; if (!plot) return; trace_graph_plot_remove(ginfo, plot); ginfo->draw_height = PLOT_SPACE(ginfo->plots); gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height); update_label_window(ginfo); } static struct graph_plot *find_plot_by_y(struct graph_info *ginfo, gint y) { gint i; for (i = 0; i < ginfo->plots; i++) { if (y >= (PLOT_TOP(i) - PLOT_GIVE) && y <= (PLOT_BOTTOM(i) + PLOT_GIVE)) { return ginfo->plot_array[i]; } } return NULL; } static gboolean do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct graph_info *ginfo = data; static GtkWidget *menu; static GtkWidget *menu_filter_enable; static GtkWidget *menu_filter_add_task; static GtkWidget *menu_filter_hide_task; static GtkWidget *menu_filter_clear_tasks; static GtkWidget *menu_plot_task; static GtkWidget *menu_remove_plot; struct pevent_record *record = NULL; struct graph_plot *plot; const char *comm; guint64 time; gchar *text; gint pid; gint len; gint x, y; x = event->x; y = event->y; if (!menu) { menu = gtk_menu_new(); menu_filter_enable = gtk_menu_item_new_with_label("Enable Filter"); gtk_widget_show(menu_filter_enable); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_enable); g_signal_connect_swapped (G_OBJECT (menu_filter_enable), "activate", G_CALLBACK (filter_enable_clicked), data); menu_filter_add_task = gtk_menu_item_new_with_label("Add Task"); gtk_widget_show(menu_filter_add_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task); g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate", G_CALLBACK (filter_add_task_clicked), data); menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task"); gtk_widget_show(menu_filter_hide_task); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task); g_signal_connect_swapped (G_OBJECT (menu_filter_hide_task), "activate", G_CALLBACK (filter_hide_task_clicked), data); menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); gtk_widget_show(menu_filter_clear_tasks); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate", G_CALLBACK (filter_clear_tasks_clicked), data); menu_plot_task = gtk_menu_item_new_with_label("Plot task"); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_plot_task); g_signal_connect_swapped (G_OBJECT (menu_plot_task), "activate", G_CALLBACK (plot_task_clicked), data); menu_remove_plot = gtk_menu_item_new_with_label("Remove Plot"); gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_remove_plot); gtk_widget_show(menu_remove_plot); g_signal_connect_swapped (G_OBJECT (menu_remove_plot), "activate", G_CALLBACK (remove_plot_clicked), data); } if (ginfo->filter_enabled) gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), "Disable Filter"); else gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), "Enable Filter"); if (ginfo->filter_available) gtk_widget_set_sensitive(menu_filter_enable, TRUE); else gtk_widget_set_sensitive(menu_filter_enable, FALSE); if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); else gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); time = convert_x_to_time(ginfo, x); plot = find_plot_by_y(ginfo, y); ginfo->plot_clicked = plot; if (plot) { record = trace_graph_plot_find_record(ginfo, plot, time); gtk_widget_set_sensitive(menu_remove_plot, TRUE); } else gtk_widget_set_sensitive(menu_remove_plot, FALSE); if (record) { if (!trace_graph_check_sched_switch(ginfo, record, &pid, &comm)) { pid = pevent_data_pid(ginfo->pevent, record); comm = pevent_data_comm_from_pid(ginfo->pevent, pid); } len = strlen(comm) + 50; text = g_malloc(len); g_assert(text); if (trace_graph_filter_task_find_pid(ginfo, pid)) snprintf(text, len, "Remove %s-%d from filter", comm, pid); else snprintf(text, len, "Add %s-%d to filter", comm, pid); ginfo->filter_task_selected = pid; gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), text); if (trace_graph_hide_task_find_pid(ginfo, pid)) snprintf(text, len, "Show %s-%d", comm, pid); else snprintf(text, len, "Hide %s-%d", comm, pid); gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), text); snprintf(text, len, "Plot %s-%d", comm, pid); gtk_menu_item_set_label(GTK_MENU_ITEM(menu_plot_task), text); g_free(text); gtk_widget_set_sensitive(menu_filter_add_task, TRUE); gtk_widget_set_sensitive(menu_filter_hide_task, TRUE); gtk_widget_show(menu_plot_task); free_record(record); } else { gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), "Add task to filter"); gtk_widget_set_sensitive(menu_filter_add_task, FALSE); gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), "Hide task"); gtk_widget_set_sensitive(menu_filter_hide_task, FALSE); gtk_widget_hide(menu_plot_task); } gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time()); return TRUE; } static void draw_info_box(struct graph_info *ginfo, const gchar *buffer, gint x, gint y); static void stop_zoom_tip(struct graph_info *ginfo) { clear_info_box(ginfo); } static void show_zoom_tip(struct graph_info *ginfo, gint x, gint y) { clear_info_box(ginfo); draw_info_box(ginfo, "Click and hold left mouse and drag right to zoom in\n" "Click and hold left mouse and drag left to zoom out", x, y); } static void button_press(struct graph_info *ginfo, gint x, gint y, guint state) { ginfo->press_x = x; ginfo->last_x = 0; draw_line(ginfo->draw, x, ginfo); ginfo->line_active = TRUE; ginfo->line_time = convert_x_to_time(ginfo, x); if (state & GDK_SHIFT_MASK) { ginfo->show_markb = FALSE; clear_line(ginfo, convert_time_to_x(ginfo, ginfo->markb_time)); /* We only update A if it hasn't been made yet */ if (!ginfo->marka_time) { ginfo->show_marka = FALSE; clear_line(ginfo, convert_time_to_x(ginfo, ginfo->marka_time)); update_marka(ginfo, x); } } else { ginfo->zoom = TRUE; show_zoom_tip(ginfo, x, y); } return; } static gboolean button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct graph_info *ginfo = data; if (!ginfo->handle) return FALSE; if (event->button == 3) return do_pop_up(widget, event, data); if (event->button != 1) return TRUE; /* check for double click */ if (event->type == GDK_2BUTTON_PRESS) { stop_zoom_tip(ginfo); if (ginfo->line_active) { ginfo->line_active = FALSE; clear_line(ginfo, ginfo->last_x); clear_line(ginfo, ginfo->press_x); } if (ginfo->cursor >= ginfo->view_start_time && ginfo->cursor <= ginfo->view_end_time) { ginfo->last_x = convert_time_to_x(ginfo, ginfo->cursor); ginfo->cursor = 0; clear_line(ginfo, ginfo->last_x); } ginfo->cursor = convert_x_to_time(ginfo, event->x); draw_cursor(ginfo); if (ginfo->callbacks && ginfo->callbacks->select) ginfo->callbacks->select(ginfo, ginfo->cursor); return TRUE; } button_press(ginfo, event->x, event->y, event->state); return TRUE; } static void draw_latency(struct graph_info *ginfo, gint x, gint y); static void draw_plot_info(struct graph_info *ginfo, struct graph_plot *plot, gint x, gint y); static void motion_plot(struct graph_info *ginfo, gint x, gint y) { struct graph_plot *plot; if (ginfo->zoom) stop_zoom_tip(ginfo); if (!ginfo->curr_pixmap) return; if (ginfo->pointer_time) update_pointer(ginfo, x); if (ginfo->line_active) { if (ginfo->last_x) clear_line(ginfo, ginfo->last_x); ginfo->last_x = x; draw_line(ginfo->draw, ginfo->press_x, ginfo); draw_line(ginfo->draw, x, ginfo); if (!ginfo->zoom) draw_latency(ginfo, x, y); return; } plot = find_plot_by_y(ginfo, y); if (plot) draw_plot_info(ginfo, plot, x, y); } static gboolean info_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct graph_info *ginfo = data; if (!ginfo->handle) return FALSE; if (event->button != 1) return FALSE; /* check for double click */ if (event->type == GDK_2BUTTON_PRESS) return FALSE; button_press(ginfo, gtk_adjustment_get_value(ginfo->hadj), event->y, event->state); return FALSE; } static gboolean info_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; GdkModifierType state; gint x, y; if (!ginfo->handle) return FALSE; if (!ginfo->line_active) return FALSE; if (!ginfo->curr_pixmap) return FALSE; clear_info_box(ginfo); if (event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); else { x = event->x; y = event->y; } /* Position x relative to the location in the drawing area */ x -= ginfo->scrollwin->allocation.x - ginfo->info_scrollwin->allocation.x; if (x < 0) return FALSE; x += gtk_adjustment_get_value(ginfo->hadj); motion_plot(ginfo, x, y); return FALSE; } static void button_release(struct graph_info *ginfo, gint x); static gboolean info_button_release_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; gint x; if (!ginfo->handle) return FALSE; x = event->x - ginfo->scrollwin->allocation.x - ginfo->info_scrollwin->allocation.x; button_release(ginfo, x); return FALSE; } #define PLOT_BOARDER 5 int trace_graph_check_sched_wakeup(struct graph_info *ginfo, struct pevent_record *record, gint *pid) { struct event_format *event; unsigned long long val; gboolean found; gint id; if (ginfo->event_wakeup_id < 0) { found = FALSE; event = pevent_find_event_by_name(ginfo->pevent, NULL, "sched_wakeup"); if (event) { found = TRUE; ginfo->event_wakeup_id = event->id; ginfo->wakeup_pid_field = pevent_find_field(event, "pid"); ginfo->wakeup_success_field = pevent_find_field(event, "success"); } event = pevent_find_event_by_name(ginfo->pevent, NULL, "sched_wakeup_new"); if (event) { found = TRUE; ginfo->event_wakeup_new_id = event->id; ginfo->wakeup_new_pid_field = pevent_find_field(event, "pid"); ginfo->wakeup_new_success_field = pevent_find_field(event, "success"); } if (!found) return 0; } id = pevent_data_type(ginfo->pevent, record); if (id == ginfo->event_wakeup_id) { /* We only want those that actually woke up the task */ if (ginfo->wakeup_success_field) { pevent_read_number_field(ginfo->wakeup_success_field, record->data, &val); if (!val) return 0; } pevent_read_number_field(ginfo->wakeup_pid_field, record->data, &val); if (pid) *pid = val; return 1; } if (id == ginfo->event_wakeup_new_id) { /* We only want those that actually woke up the task */ if (ginfo->wakeup_new_success_field) { pevent_read_number_field(ginfo->wakeup_new_success_field, record->data, &val); if (!val) return 0; } pevent_read_number_field(ginfo->wakeup_new_pid_field, record->data, &val); if (pid) *pid = val; return 1; } return 0; } int trace_graph_check_sched_switch(struct graph_info *ginfo, struct pevent_record *record, gint *pid, const char **comm) { unsigned long long val; struct event_format *event; gint this_pid; gint id; int ret = 1; if (ginfo->read_comms) { /* record all pids, for task plots */ this_pid = pevent_data_pid(ginfo->pevent, record); add_task_hash(ginfo, this_pid); } if (ginfo->event_sched_switch_id < 0) { event = pevent_find_event_by_name(ginfo->pevent, NULL, "sched_switch"); if (!event) return 0; ginfo->event_sched_switch_id = event->id; ginfo->event_prev_state = pevent_find_field(event, "prev_state"); ginfo->event_pid_field = pevent_find_field(event, "next_pid"); ginfo->event_comm_field = pevent_find_field(event, "next_comm"); event = pevent_find_event_by_name(ginfo->pevent, "ftrace", "context_switch"); if (event) { ginfo->ftrace_sched_switch_id = event->id; ginfo->ftrace_pid_field = pevent_find_field(event, "next_pid"); ginfo->ftrace_comm_field = pevent_find_field(event, "next_comm"); } } id = pevent_data_type(ginfo->pevent, record); if (id == ginfo->event_sched_switch_id) { pevent_read_number_field(ginfo->event_pid_field, record->data, &val); if (comm) *comm = record->data + ginfo->event_comm_field->offset; if (pid) *pid = val; goto out; } if (id == ginfo->ftrace_sched_switch_id) { pevent_read_number_field(ginfo->ftrace_pid_field, record->data, &val); if (comm && ginfo->ftrace_comm_field) *comm = record->data + ginfo->ftrace_comm_field->offset; else comm = NULL; if (pid) *pid = val; goto out; } ret = 0; out: if (ret && comm && ginfo->read_comms) { /* * First time through, register any missing * comm / pid mappings. */ if (!pevent_pid_is_registered(ginfo->pevent, *pid)) pevent_register_comm(ginfo->pevent, *comm, *pid); } return ret; } static void draw_info_box(struct graph_info *ginfo, const gchar *buffer, gint x, gint y) { PangoLayout *layout; GtkAdjustment *vadj; gint width, height; GdkPixmap *pix; static GdkGC *pix_bg; gint view_width; gint view_start; if (!pix_bg) { GdkColor color; pix_bg = gdk_gc_new(ginfo->draw->window); color.red = (0xff) *(65535/255); color.green = (0xfa) *(65535/255); color.blue = (0xcd) *(65535/255); gdk_color_alloc(gtk_widget_get_colormap(ginfo->draw), &color); gdk_gc_set_foreground(pix_bg, &color); } layout = gtk_widget_create_pango_layout(ginfo->draw, buffer); pango_layout_get_pixel_size(layout, &width, &height); width += PLOT_BOARDER * 2; height += PLOT_BOARDER * 2; view_start = gtk_adjustment_get_value(ginfo->hadj); view_width = gtk_adjustment_get_page_size(ginfo->hadj); if (x > view_start + width) x -= width; vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(ginfo->scrollwin)); view_start = gtk_adjustment_get_value(vadj); if (y > view_start + height) y -= height; ginfo->plot_data_x = x; ginfo->plot_data_y = y; ginfo->plot_data_w = width; ginfo->plot_data_h = height; pix = gdk_pixmap_new(ginfo->draw->window, width, height, -1); gdk_draw_rectangle(pix, pix_bg, TRUE, 0, 0, width, height); gdk_draw_rectangle(pix, ginfo->draw->style->black_gc, FALSE, 0, 0, width-1, height-1); gdk_draw_layout(pix, ginfo->draw->style->black_gc, PLOT_BOARDER, PLOT_BOARDER, layout); gdk_draw_drawable(ginfo->draw->window, ginfo->draw->style->fg_gc[GTK_WIDGET_STATE(ginfo->draw)], pix, 0, 0, x, y, width, height); g_object_unref(layout); g_object_unref(pix); } static void draw_plot_info(struct graph_info *ginfo, struct graph_plot *plot, gint x, gint y) { struct pevent *pevent; guint64 time; unsigned long sec, usec; struct trace_seq s; time = convert_x_to_time(ginfo, x); convert_nano(time, &sec, &usec); pevent = ginfo->pevent; trace_seq_init(&s); dprintf(3, "start=%llu end=%llu time=%llu\n", (u64)ginfo->start_time, (u64)ginfo->end_time, (u64)time); if (!trace_graph_plot_display_info(ginfo, plot, &s, time)) { /* Just display the current time */ trace_seq_destroy(&s); trace_seq_init(&s); trace_seq_printf(&s, "%lu.%06lu", sec, usec); } trace_seq_putc(&s, 0); draw_info_box(ginfo, s.buffer, x, y); trace_seq_destroy(&s); } static void draw_latency(struct graph_info *ginfo, gint x, gint y) { struct pevent *pevent; unsigned long sec, usec; struct trace_seq s; gboolean neg; gint64 time; update_markb(ginfo, x); time = convert_x_to_time(ginfo, x); time -= ginfo->line_time; if (time < 0) { neg = TRUE; time *= -1; } else neg = FALSE; convert_nano(time, &sec, &usec); pevent = ginfo->pevent; trace_seq_init(&s); trace_seq_printf(&s, "Diff: %s%ld.%06lu secs", neg ? "-":"", sec, usec); draw_info_box(ginfo, s.buffer, x, y); trace_seq_destroy(&s); } static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; GdkModifierType state; gint x, y; if (!ginfo->handle) return FALSE; clear_info_box(ginfo); if (event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); else { x = event->x; y = event->y; state = event->state; } motion_plot(ginfo, x, y); return TRUE; } static int update_graph(struct graph_info *ginfo, gdouble percent) { gint full_width = ginfo->full_width * percent; gdouble resolution = (gdouble)full_width / (gdouble)(ginfo->end_time - ginfo->start_time); /* Check if we are too big */ if (!resolution || full_width <= 0) return -1; ginfo->full_width = full_width; ginfo->resolution = resolution; ginfo->start_x = (ginfo->view_start_time - ginfo->start_time) * ginfo->resolution; dprintf(1, "new resolution = %f\n", resolution); return 0; } static void update_graph_to_start_x(struct graph_info *ginfo) { gint width = ginfo->draw_width;; if (!width) { ginfo->view_start_time = ginfo->start_time; ginfo->view_end_time = ginfo->end_time; return; } ginfo->view_start_time = (gdouble)ginfo->start_x / ginfo->resolution + ginfo->start_time; ginfo->view_end_time = (gdouble)width / ginfo->resolution + ginfo->view_start_time; g_assert (ginfo->view_start_time < ginfo->end_time); } static void reset_graph(struct graph_info *ginfo, gdouble view_width) { ginfo->full_width = view_width; ginfo->draw_width = 0; ginfo->view_start_time = ginfo->start_time; ginfo->view_end_time = ginfo->end_time; ginfo->start_x = 0; } static void zoom_in_window(struct graph_info *ginfo, gint start, gint end) { guint64 start_time; gdouble view_width; gdouble new_width; gdouble select_width; gdouble curr_width; gdouble mid; gdouble percent; gint old_width = ginfo->draw_width; g_assert(start < end); g_assert(ginfo->hadj); start_time = ginfo->start_time + (ginfo->start_x + start) / ginfo->resolution; view_width = gtk_adjustment_get_page_size(ginfo->hadj); select_width = end - start; percent = view_width / select_width; dprintf(1, "view width=%f select width=%f percent=%f\n", view_width, select_width, percent); if (update_graph(ginfo, percent) < 0) return; curr_width = ginfo->draw->allocation.width; new_width = curr_width * percent; ginfo->draw_width = new_width; dprintf(1, "zoom in draw_width=%d full_width=%d\n", ginfo->draw_width, ginfo->full_width); if (ginfo->draw_width > MAX_WIDTH) { gint new_start; gint new_end; /* * The drawing is now greater than our max. We must * limit the maximum size of the drawing area or * we risk running out of X resources. * * We will now shorten the trace to that of what will * fit in this zoomed area. */ ginfo->draw_width = MAX_WIDTH; mid = start + (end - start) / 2; mid *= percent; mid += ginfo->start_x; /* * mid now points to the center of the viewable area * if the draw area was of new_width. * * new_start new_end * +------------------------------------------------+ * | | | | * | | | | * +------------------------------------------------+ * ^ ^ * | mid * old view start * */ new_start = mid - MAX_WIDTH / 2; new_end = new_start + MAX_WIDTH; if (new_start < 0) { mid += new_start; new_start = 0; } else if (new_end > ginfo->full_width) { new_start -= new_end - ginfo->full_width; mid += new_end - ginfo->full_width; new_end = ginfo->full_width; g_assert(new_start >= 0); } ginfo->start_x = new_start; dprintf(1, "new start/end =%d/%d full:%d start_time:", new_start, new_end, ginfo->full_width); print_time(ginfo->view_start_time); dprintf(1, "\n"); /* Adjust start to be the location for the hadj */ start = (mid - new_start) - view_width / 2; } else start *= percent; update_graph_to_start_x(ginfo); ginfo->hadj_value = start; ginfo->hadj_value = convert_time_to_x(ginfo, start_time); if (ginfo->hadj_value > (ginfo->draw_width - view_width)) ginfo->hadj_value = ginfo->draw_width - view_width; dprintf(1, "new width=%d\n", ginfo->draw_width); /* make sure the width is sent */ if (ginfo->draw_width == old_width) redraw_graph(ginfo); else gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height); dprintf(1, "set val %f\n", ginfo->hadj_value); dprintf(1, "*** ended with with "); print_time(convert_x_to_time(ginfo, ginfo->hadj_value)); dprintf(1, "\n"); } static gboolean value_changed(GtkWidget *widget, gpointer data) { GtkAdjustment *adj = GTK_ADJUSTMENT(widget); dprintf(2, "value = %f\n", gtk_adjustment_get_value(adj)); return TRUE; } static void zoom_out_window(struct graph_info *ginfo, gint start, gint end) { gdouble view_width; gdouble divider; gdouble curr_width; gdouble new_width; gdouble mid; gdouble start_x; unsigned long long time; gint old_width = ginfo->draw_width; g_assert(start > end); g_assert(ginfo->hadj); view_width = gtk_adjustment_get_page_size(ginfo->hadj); start_x = gtk_adjustment_get_value(ginfo->hadj); mid = start_x + view_width / 2; time = convert_x_to_time(ginfo, mid); divider = start - end; curr_width = ginfo->draw->allocation.width; new_width = curr_width / divider; if (update_graph(ginfo, 1 / divider) < 0) return; dprintf(1, "width=%d\n", ginfo->draw->allocation.width); ginfo->draw_width = new_width; dprintf(1, "draw_width=%d full_width=%d\n", ginfo->draw_width, ginfo->full_width); if (ginfo->full_width < view_width) { reset_graph(ginfo, view_width); time = ginfo->view_start_time; } else if (ginfo->draw_width < ginfo->full_width) { if (ginfo->full_width < MAX_WIDTH) { ginfo->draw_width = ginfo->full_width; ginfo->view_start_time = ginfo->start_time; ginfo->view_end_time = ginfo->end_time; ginfo->start_x = 0; } else { ginfo->draw_width = MAX_WIDTH; mid /= divider; mid += ginfo->start_x; /* mid now is the current mid with full_width */ ginfo->start_x = mid - MAX_WIDTH / 2; if (ginfo->start_x < 0) ginfo->start_x = 0; update_graph_to_start_x(ginfo); } } dprintf(1, "new width=%d\n", ginfo->draw_width); /* make sure the width is sent */ if (ginfo->draw_width == old_width) redraw_graph(ginfo); else gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height); mid = convert_time_to_x(ginfo, time); start_x = mid - view_width / 2; if (start_x < 0) start_x = 0; ginfo->hadj_value = start_x; } static void activate_zoom(struct graph_info *ginfo, gint x) { if (!ginfo->zoom) return; ginfo->zoom = FALSE; if (x > ginfo->press_x) { /* make a decent zoom */ if (x - ginfo->press_x < 10) return; zoom_in_window(ginfo, ginfo->press_x, x); } else if (x < ginfo->press_x) zoom_out_window(ginfo, ginfo->press_x, x); } static void button_release(struct graph_info *ginfo, gint x) { gint old_x; if (!ginfo->line_active) return; if (!ginfo->zoom) { ginfo->show_marka = TRUE; ginfo->show_markb = TRUE; update_markb(ginfo, x); } else stop_zoom_tip(ginfo); clear_line(ginfo, ginfo->last_x); clear_line(ginfo, ginfo->press_x); ginfo->line_active = FALSE; clear_info_box(ginfo); /* If button is released at same location, set A (without shift) */ if (ginfo->zoom && x >= ginfo->press_x-1 && x <= ginfo->press_x+1) { old_x = convert_time_to_x(ginfo, ginfo->marka_time); ginfo->show_marka = TRUE; update_marka(ginfo, x); clear_line(ginfo, old_x); if (ginfo->markb_time) update_label_time(ginfo->delta_label, ginfo->markb_time - ginfo->marka_time); } activate_zoom(ginfo, x); } static gboolean button_release_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; if (!ginfo->handle) return FALSE; button_release(ginfo, event->x); return TRUE; } static gboolean leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer data) { struct graph_info *ginfo = data; if (!ginfo->handle) return FALSE; clear_info_box(ginfo); return FALSE; } static void set_color(GtkWidget *widget, GdkGC *gc, gint c) { GdkColor color; color.red = (c & 0xff)*(65535/255); color.blue = ((c >> 8) & 0xff)*(65535/255); color.green = ((c >> 16) & 0xff)*(65535/255); gdk_color_alloc(gtk_widget_get_colormap(widget), &color); gdk_gc_set_foreground(gc, &color); } #define LABEL_SPACE 3 static gint draw_event_label(struct graph_info *ginfo, gint i, gint p1, gint p2, gint p3, gint width_16, PangoFontDescription *font) { struct graph_plot *plot = ginfo->plot_array[i]; PangoLayout *layout; struct trace_seq s; gint text_width; gint text_height; gint start, end; gint x, y; gint ret; /* * We are testing if we can print the label at p2. * p1 has the start of the area that we can print. * p3 is the location of the next label. * We will not print any label unless we have enough * room to print a minimum of 16 characters. */ if (p3 - p1 < width_16 || p3 - p2 < width_16 / 2) return p2; /* Now get p2's drawing size */ trace_seq_init(&s); /* * Display the event after p2 - 1. We use "-1" because we need to * find the event at this pixel, and due to rounding, p2 time can * be after the time of the event. Since tracecmd finds the next event * after the time, we use this to find our next event. */ ret = trace_graph_plot_display_last_event(ginfo, plot, &s, convert_x_to_time(ginfo, p2-1)); if (!ret) { trace_seq_destroy(&s); return p2; } layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer); pango_layout_set_font_description(layout, font); pango_layout_get_pixel_size(layout, &text_width, &text_height); trace_seq_destroy(&s); /* Lets see if we can print this info */ if (p2 < text_width) start = 1; else start = p2 - text_width / 2; end = start + text_width; if (start < p1 || end > p3) { g_object_unref(layout); return p2; } /* Display the info */ x = start; y = (PLOT_TOP(i) - text_height + 5); gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc, x, y, layout); gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, p2, PLOT_TOP(i) - 5, p2, PLOT_TOP(i) - 1); g_object_unref(layout); /* * Set the next p1 to start after the end of what was displayed * plus a little padding. */ return end + LABEL_SPACE; } static gint draw_plot_line(struct graph_info *ginfo, int i, unsigned long long time, GdkGC *gc) { gint x; x = convert_time_to_x(ginfo, time); gdk_draw_line(ginfo->curr_pixmap, gc, x, PLOT_TOP(i), x, PLOT_BOTTOM(i)); return x; } static void draw_plot_box(struct graph_info *ginfo, int i, unsigned long long start, unsigned long long end, gboolean fill, GdkGC *gc) { gint x1; gint x2; x1 = convert_time_to_x(ginfo, start); x2 = convert_time_to_x(ginfo, end); gdk_draw_rectangle(ginfo->curr_pixmap, gc, fill, x1, PLOT_BOX_TOP(i), x2 - x1, PLOT_BOX_SIZE); } static void draw_plot(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record) { static PangoFontDescription *font; PangoLayout *layout; static gint width_16; struct plot_info info; gint x; /* Calculate the size of 16 characters */ if (!width_16) { gchar buf[17]; gint text_height; memset(buf, 'a', 16); buf[16] = 0; font = pango_font_description_from_string("Sans 8"); layout = gtk_widget_create_pango_layout(ginfo->draw, buf); pango_layout_set_font_description(layout, font); pango_layout_get_pixel_size(layout, &width_16, &text_height); g_object_unref(layout); } trace_graph_plot_event(ginfo, plot, record, &info); if (info.box) { if (info.bcolor != plot->last_color) { plot->last_color = info.bcolor; set_color(ginfo->draw, plot->gc, plot->last_color); } draw_plot_box(ginfo, plot->pos, info.bstart, info.bend, info.bfill, plot->gc); } if (info.line) { if (info.lcolor != plot->last_color) { plot->last_color = info.lcolor; set_color(ginfo->draw, plot->gc, plot->last_color); } x = draw_plot_line(ginfo, plot->pos, info.ltime, plot->gc); /* Figure out if we can show the text for the previous record */ plot->p3 = x; /* Make sure p2 will be non-zero the next iteration */ if (!plot->p3) plot->p3 = 1; /* first record, continue */ if (plot->p2) plot->p2 = draw_event_label(ginfo, plot->pos, plot->p1, plot->p2, plot->p3, width_16, font); plot->p1 = plot->p2; plot->p2 = plot->p3; } if (!record && plot->p2) draw_event_label(ginfo, plot->pos, plot->p1, plot->p2, ginfo->draw_width, width_16, font); } static void draw_plots(struct graph_info *ginfo, gint new_width) { struct plot_list *list; struct graph_plot *plot; struct pevent_record *record; struct plot_hash *hash; gint pid; gint cpu; gint i; /* Initialize plots */ for (i = 0; i < ginfo->plots; i++) { plot = ginfo->plot_array[i]; if (!plot->gc) plot->gc = gdk_gc_new(ginfo->draw->window); plot->p1 = 0; plot->p2 = 0; plot->p3 = 0; plot->last_color = -1; gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, 0, PLOT_LINE(i), new_width, PLOT_LINE(i)); trace_graph_plot_start(ginfo, plot, ginfo->view_start_time); set_color(ginfo->draw, plot->gc, plot->last_color); } tracecmd_set_all_cpus_to_timestamp(ginfo->handle, ginfo->view_start_time); trace_set_cursor(GDK_WATCH); /* Shortcut if we don't have any task plots */ if (!ginfo->nr_task_hash && !ginfo->all_recs) { for (cpu = 0; cpu < ginfo->cpus; cpu++) { hash = trace_graph_plot_find_cpu(ginfo, cpu); if (!hash) continue; while ((record = tracecmd_read_data(ginfo->handle, cpu))) { if (record->ts < ginfo->view_start_time) { free_record(record); continue; } if (record->ts > ginfo->view_end_time) { free_record(record); break; } for (list = hash->plots; list; list = list->next) draw_plot(ginfo, list->plot, record); free_record(record); } } goto out; } while ((record = tracecmd_read_next_data(ginfo->handle, &cpu))) { if (record->ts < ginfo->view_start_time) { free_record(record); continue; } if (record->ts > ginfo->view_end_time) { free_record(record); break; } hash = trace_graph_plot_find_cpu(ginfo, cpu); if (hash) { for (list = hash->plots; list; list = list->next) draw_plot(ginfo, list->plot, record); } pid = pevent_data_pid(ginfo->pevent, record); hash = trace_graph_plot_find_task(ginfo, pid); if (hash) { for (list = hash->plots; list; list = list->next) draw_plot(ginfo, list->plot, record); } for (list = ginfo->all_recs; list; list = list->next) draw_plot(ginfo, list->plot, record); free_record(record); } out: for (i = 0; i < ginfo->plots; i++) { plot = ginfo->plot_array[i]; draw_plot(ginfo, plot, NULL); trace_graph_plot_end(ginfo, plot); if (plot->gc) gdk_gc_unref(plot->gc); plot->gc = NULL; } trace_put_cursor(); } static void draw_timeline(struct graph_info *ginfo, gint width) { PangoLayout *layout; struct trace_seq s; unsigned long sec, usec; unsigned long long time; gint mid; gint w, h, height; gint view_width; /* --- draw timeline text --- */ layout = gtk_widget_create_pango_layout(ginfo->draw, "Time Line"); pango_layout_get_pixel_size(layout, &w, &h); height = 10 + h; mid = width / 2; gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc, mid - w / 2, 5, layout); g_object_unref(layout); /* --- draw time line lines --- */ gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, 0, height, width, height); gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, 0, height, 0, height + 5); gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, width-1, height, width-1, height); gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, width-1, height, width-1, height + 5); /* --- draw starting time --- */ convert_nano(ginfo->view_start_time, &sec, &usec); trace_seq_init(&s); trace_seq_printf(&s, "%lu.%06lu", sec, usec); layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer); pango_layout_get_pixel_size(layout, &w, &h); gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc, 1, height+10, layout); g_object_unref(layout); trace_seq_destroy(&s); /* --- draw ending time --- */ convert_nano(ginfo->view_end_time, &sec, &usec); trace_seq_init(&s); trace_seq_printf(&s, "%lu.%06lu", sec, usec); layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer); pango_layout_get_pixel_size(layout, &w, &h); gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc, width - (w + 2), height+10, layout); g_object_unref(layout); trace_seq_destroy(&s); /* --- draw time at intervals --- */ view_width = gtk_adjustment_get_page_size(ginfo->hadj); for (mid = view_width / 2; mid < (width - view_width / 2 + 10); mid += view_width / 2) { time = convert_x_to_time(ginfo, mid); convert_nano(time, &sec, &usec); trace_seq_init(&s); trace_seq_printf(&s, "%lu.%06lu", sec, usec); gdk_draw_line(ginfo->curr_pixmap, ginfo->draw->style->black_gc, mid, height, mid, height + 5); layout = gtk_widget_create_pango_layout(ginfo->draw, s.buffer); pango_layout_get_pixel_size(layout, &w, &h); gdk_draw_layout(ginfo->curr_pixmap, ginfo->draw->style->black_gc, mid - (w / 2), height+10, layout); g_object_unref(layout); trace_seq_destroy(&s); } } static void draw_info(struct graph_info *ginfo, gint new_width) { if (!ginfo->handle) return; ginfo->resolution = (gdouble)new_width / (gdouble)(ginfo->view_end_time - ginfo->view_start_time); ginfo->full_width = (ginfo->end_time - ginfo->start_time) * ginfo->resolution; draw_timeline(ginfo, new_width); draw_plots(ginfo, new_width); ginfo->read_comms = FALSE; } void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time) { GtkAdjustment *vadj; gint view_start; gint view_width; gint width; gint mid; gint start; gint end; int ret; gint i; guint64 old_start_time = ginfo->view_start_time; view_width = gtk_adjustment_get_page_size(ginfo->hadj); width = ginfo->draw_width ? : ginfo->full_width; mid = (time - ginfo->start_time) * ginfo->resolution; start = mid - width / 2; if (start < 0) start = 0; end = start + width; /* * Readjust the drawing to be centered on the selection. */ if (end > ginfo->full_width) { start -= end - ginfo->full_width; g_assert(start >= 0); end = ginfo->full_width; } ginfo->start_x = start; update_graph_to_start_x(ginfo); /* force redraw if we changed the time*/ if (old_start_time != ginfo->view_start_time) redraw_pixmap_backend(ginfo); /* Adjust start to be the location for the hadj */ mid = convert_time_to_x(ginfo, time); start = mid - view_width / 2; if (start < 0) start = 0; if (start > (width - view_width)) start = width - view_width; gtk_adjustment_set_value(ginfo->hadj, start); ginfo->last_x = convert_time_to_x(ginfo, ginfo->cursor); ginfo->cursor = 0; clear_line(ginfo, ginfo->last_x); ginfo->cursor = time; update_with_backend(ginfo, 0, 0, width, ginfo->draw_height); /* * If a record exists at this exact time value, we should * make sure that it is in view. */ for (i = 0; i < ginfo->plots; i++) { ret = trace_graph_plot_match_time(ginfo, ginfo->plot_array[i], time); if (ret) break; } if (i == ginfo->plots) return; /* Make sure PLOT is visible */ vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(ginfo->scrollwin)); view_start = gtk_adjustment_get_value(vadj); view_width = gtk_adjustment_get_page_size(vadj); if (PLOT_TOP(i) > view_start && PLOT_BOTTOM(i) < view_start + view_width) return; if (PLOT_TOP(i) < view_start) gtk_adjustment_set_value(vadj, PLOT_TOP(i) - 5); if (PLOT_BOTTOM(i) > view_start + view_width) gtk_adjustment_set_value(vadj, (PLOT_BOTTOM(i) - view_width) + 10); } void trace_graph_event_filter_callback(gboolean accept, gboolean all_events, gchar **systems, gint *events, gpointer data) { struct graph_info *ginfo = data; if (!accept) return; if (all_events) { ginfo->all_events = TRUE; /* filter is no longer used */ pevent_filter_reset(ginfo->event_filter); redraw_graph(ginfo); return; } ginfo->all_events = FALSE; pevent_filter_clear_trivial(ginfo->event_filter, FILTER_TRIVIAL_BOTH); trace_filter_convert_char_to_filter(ginfo->event_filter, systems, events); redraw_graph(ginfo); } void trace_graph_adv_filter_callback(gboolean accept, const gchar *text, gint *event_ids, gpointer data) { struct graph_info *ginfo = data; struct event_filter *event_filter; char error_str[200]; int ret; int i; if (!accept) return; if (!has_text(text) && !event_ids) return; event_filter = ginfo->event_filter; if (event_ids) { for (i = 0; event_ids[i] >= 0; i++) pevent_filter_remove_event(event_filter, event_ids[i]); } if (has_text(text)) { ginfo->all_events = FALSE; pevent_filter_clear_trivial(event_filter, FILTER_TRIVIAL_BOTH); ret = pevent_filter_add_filter_str(event_filter, text); if (ret < 0) { pevent_strerror(event_filter->pevent, ret, error_str, sizeof(error_str)); warning("filter failed due to: %s", error_str); return; } } redraw_graph(ginfo); } void trace_graph_copy_filter(struct graph_info *ginfo, gboolean all_events, struct event_filter *event_filter) { if (all_events) { ginfo->all_events = TRUE; /* filter is no longer used */ pevent_filter_reset(ginfo->event_filter); redraw_graph(ginfo); return; } ginfo->all_events = FALSE; pevent_filter_copy(ginfo->event_filter, event_filter); redraw_graph(ginfo); } static void redraw_pixmap_backend(struct graph_info *ginfo) { GdkPixmap *old_pix; static gboolean init; old_pix = ginfo->curr_pixmap; /* initialize full width if needed */ if (!ginfo->full_width) ginfo->full_width = ginfo->draw->allocation.width; ginfo->curr_pixmap = gdk_pixmap_new(ginfo->draw->window, ginfo->draw->allocation.width, ginfo->draw->allocation.height, -1); gdk_draw_rectangle(ginfo->curr_pixmap, ginfo->draw->style->white_gc, TRUE, 0, 0, ginfo->draw->allocation.width, ginfo->draw->allocation.height); draw_info(ginfo, ginfo->draw->allocation.width); if (!init) { init = TRUE; green = gdk_gc_new(ginfo->draw->window); red = gdk_gc_new(ginfo->draw->window); set_color(ginfo->draw, green, (0xff<<16)); set_color(ginfo->draw, red, 0xff); } if (old_pix) g_object_unref(old_pix); if (ginfo->hadj_value) { // gtk_adjustment_set_lower(ginfo->hadj, -100.0); gtk_adjustment_set_value(ginfo->hadj, ginfo->hadj_value); } } static gboolean configure_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; gtk_widget_set_size_request(widget, ginfo->draw_width, ginfo->draw_height); redraw_pixmap_backend(ginfo); /* debug */ ginfo->hadj_value = gtk_adjustment_get_value(ginfo->hadj); dprintf(2, "get val %f\n", ginfo->hadj_value); ginfo->hadj_value = 0.0; return TRUE; } static gboolean destroy_event(GtkWidget *widget, gpointer data) { struct graph_info *ginfo = data; trace_graph_free_info(ginfo); filter_task_hash_free(ginfo->task_filter); filter_task_hash_free(ginfo->hide_tasks); return TRUE; } static void redraw_label_window(struct graph_info *ginfo, int x, int y, int w, int h) { gdk_draw_drawable(ginfo->info->window, ginfo->info->style->fg_gc[GTK_WIDGET_STATE(ginfo->info)], ginfo->info_pixmap, x, y, x, y, w, h); } static gboolean info_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct graph_info *ginfo = data; redraw_label_window(ginfo, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } static void info_draw_plot_label(struct graph_info *ginfo, gint i) { PangoLayout *layout; gint width, height; char *label; label = ginfo->plot_array[i]->label; layout = gtk_widget_create_pango_layout(ginfo->info, label); pango_layout_get_pixel_size(layout, &width, &height); width += 4; if (width > largest_plot_label) largest_plot_label = width; gdk_draw_rectangle(ginfo->info_pixmap, ginfo->info->style->white_gc, TRUE, PLOT_X, PLOT_LABEL(i)+4, width, height); gdk_draw_layout(ginfo->info_pixmap, ginfo->info->style->black_gc, PLOT_X+ 2, PLOT_LABEL(i) + 4, layout); g_object_unref(layout); } static void info_draw_plot_labels(struct graph_info *ginfo) { gint i; if (!ginfo->handle) return; largest_plot_label = 0; for (i = 0; i < ginfo->plots; i++) info_draw_plot_label(ginfo, i); } static void update_label_window(struct graph_info *ginfo) { if (ginfo->info_pixmap) g_object_unref(ginfo->info_pixmap); ginfo->info_pixmap = gdk_pixmap_new(ginfo->info->window, ginfo->info->allocation.width, ginfo->info->allocation.height, -1); gdk_draw_rectangle(ginfo->info_pixmap, ginfo->info->style->white_gc, TRUE, 0, 0, ginfo->info->allocation.width, ginfo->info->allocation.height); info_draw_plot_labels(ginfo); gtk_widget_set_size_request(ginfo->info, largest_plot_label + 10, ginfo->draw_height); redraw_label_window(ginfo, 0, 0, ginfo->info->allocation.width, ginfo->info->allocation.height); } static gboolean info_configure_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct graph_info *ginfo = data; update_label_window(ginfo); return TRUE; } static GtkWidget * create_graph_info(struct graph_info *ginfo) { GtkWidget *info; info = gtk_drawing_area_new(); gtk_signal_connect(GTK_OBJECT(info), "expose_event", (GtkSignalFunc) info_expose_event, ginfo); gtk_signal_connect(GTK_OBJECT(info), "configure_event", (GtkSignalFunc) info_configure_event, ginfo); gtk_signal_connect(GTK_OBJECT(info), "button_press_event", (GtkSignalFunc) info_button_press_event, ginfo); gtk_signal_connect(GTK_OBJECT(info), "motion_notify_event", (GtkSignalFunc) info_motion_notify_event, ginfo); gtk_signal_connect(GTK_OBJECT(info), "button_release_event", (GtkSignalFunc) info_button_release_event, ginfo); gtk_widget_set_events(info, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); return info; } void trace_graph_free_info(struct graph_info *ginfo) { if (ginfo->handle) { pevent_filter_free(ginfo->event_filter); trace_graph_plot_free(ginfo); tracecmd_close(ginfo->handle); free_task_hash(ginfo); ginfo->cursor = 0; } ginfo->handle = NULL; } static int load_handle(struct graph_info *ginfo, struct tracecmd_input *handle) { struct pevent_record *record; unsigned long sec, usec; gint cpu; if (!handle) return -1; trace_graph_free_info(ginfo); trace_graph_plot_init(ginfo); ginfo->handle = handle; tracecmd_ref(handle); init_event_cache(ginfo); ginfo->pevent = tracecmd_get_pevent(handle); ginfo->cpus = tracecmd_cpus(handle); ginfo->all_events = TRUE; ginfo->event_filter = pevent_filter_alloc(ginfo->pevent); ginfo->start_time = -1ULL; ginfo->end_time = 0; graph_plot_init_cpus(ginfo, ginfo->cpus); ginfo->draw_height = PLOT_SPACE(ginfo->plots); for (cpu = 0; cpu < ginfo->cpus; cpu++) { record = tracecmd_read_cpu_first(handle, cpu); if (!record) continue; if (record->ts < ginfo->start_time) ginfo->start_time = record->ts; free_record(record); record = tracecmd_read_cpu_last(handle, cpu); if (!record) continue; if (record->ts > ginfo->end_time) ginfo->end_time = record->ts; free_record(record); } convert_nano(ginfo->start_time, &sec, &usec); dprintf(1, "start=%lu.%06lu ", sec, usec); convert_nano(ginfo->end_time, &sec, &usec); dprintf(1, "end=%lu.%06lu\n", sec, usec); ginfo->view_start_time = ginfo->start_time; ginfo->view_end_time = ginfo->end_time; if (!ginfo->draw) return 0; update_cursor(ginfo); update_pointer(ginfo, 0); update_marka(ginfo, 0); update_markb(ginfo, 0); return 0; } void trace_graph_refresh(struct graph_info *ginfo) { ginfo->draw_height = PLOT_SPACE(ginfo->plots); gtk_widget_set_size_request(ginfo->draw, ginfo->draw_width, ginfo->draw_height); update_label_window(ginfo); redraw_graph(ginfo); } int trace_graph_load_handle(struct graph_info *ginfo, struct tracecmd_input *handle) { if (load_handle(ginfo, handle) < 0) return -1; update_label_window(ginfo); redraw_graph(ginfo); return 0; } static int load_event_filter(struct graph_info *ginfo, struct tracecmd_xml_handle *handle, struct tracecmd_xml_system_node *node) { struct tracecmd_xml_system_node *child; struct event_filter *event_filter; const char *name; const char *value; event_filter = ginfo->event_filter; child = tracecmd_xml_node_child(node); name = tracecmd_xml_node_type(child); if (strcmp(name, "FilterType") != 0) return -1; value = tracecmd_xml_node_value(handle, child); /* Do nothing with all events enabled */ if (strcmp(value, "all events") == 0) return 0; node = tracecmd_xml_node_next(child); if (!node) return -1; pevent_filter_clear_trivial(event_filter, FILTER_TRIVIAL_BOTH); ginfo->all_events = FALSE; trace_filter_load_events(event_filter, handle, node); return 0; } int trace_graph_load_filters(struct graph_info *ginfo, struct tracecmd_xml_handle *handle) { struct tracecmd_xml_system *system; struct tracecmd_xml_system_node *syschild; const char *name; if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) ginfo->filter_available = 1; else ginfo->filter_available = 0; system = tracecmd_xml_find_system(handle, "TraceGraph"); if (!system) return -1; syschild = tracecmd_xml_system_node(system); if (!syschild) goto out_free_sys; do { name = tracecmd_xml_node_type(syschild); if (strcmp(name, "EventFilter") == 0) load_event_filter(ginfo, handle, syschild); syschild = tracecmd_xml_node_next(syschild); } while (syschild); if (filter_task_count(ginfo->task_filter) || filter_task_count(ginfo->hide_tasks)) ginfo->filter_available = 1; else ginfo->filter_available = 0; tracecmd_xml_free_system(system); trace_graph_refresh(ginfo); return 0; out_free_sys: tracecmd_xml_free_system(system); if (ginfo->filter_enabled) trace_graph_refresh(ginfo); return -1; } int trace_graph_save_filters(struct graph_info *ginfo, struct tracecmd_xml_handle *handle) { struct event_filter *event_filter; tracecmd_xml_start_system(handle, "TraceGraph"); event_filter = ginfo->event_filter; tracecmd_xml_start_sub_system(handle, "EventFilter"); if (ginfo->all_events || !event_filter) tracecmd_xml_write_element(handle, "FilterType", "all events"); else { tracecmd_xml_write_element(handle, "FilterType", "filter"); trace_filter_save_events(handle, event_filter); } tracecmd_xml_end_sub_system(handle); tracecmd_xml_end_system(handle); return 0; } static void set_label_a(GtkWidget *widget) { gtk_widget_set_tooltip_text(widget, "Click left mouse on graph\n" "to set Marker A"); } static void set_label_b(GtkWidget *widget) { gtk_widget_set_tooltip_text(widget, "Shift and click left mouse on graph\n" "to set Marker B"); } static void set_label_cursor(GtkWidget *widget) { gtk_widget_set_tooltip_text(widget, "Double click Left mouse on graph\n" "to set Cursor"); } struct graph_info * trace_graph_create_with_callbacks(struct tracecmd_input *handle, struct graph_callbacks *cbs) { struct graph_info *ginfo; GtkWidget *table; GtkWidget *hbox; GtkWidget *label; GtkWidget *eventbox; GdkColor color; GdkColor colorAB; ginfo = g_new0(typeof(*ginfo), 1); g_assert(ginfo != NULL); if (handle) load_handle(ginfo, handle); ginfo->handle = handle; ginfo->callbacks = cbs; ginfo->task_filter = filter_task_hash_alloc(); ginfo->hide_tasks = filter_task_hash_alloc(); ginfo->widget = gtk_vbox_new(FALSE, 0); gtk_widget_show(ginfo->widget); ginfo->status_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(ginfo->widget), ginfo->status_hbox, FALSE, FALSE, 0); gtk_widget_show(ginfo->status_hbox); table = gtk_table_new(1, 23, FALSE); gtk_box_pack_start(GTK_BOX(ginfo->status_hbox), table, FALSE, FALSE, 0); gtk_widget_show(table); color.red = (0xff) *(65535/255); color.green = (0xff) *(65535/255); color.blue = (0xff) *(65535/255); /* --- Pointer --- */ label = gtk_label_new("Pointer:"); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(label); ginfo->pointer_time = gtk_label_new("0.0"); eventbox = gtk_event_box_new(); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); gtk_container_add(GTK_CONTAINER(eventbox), ginfo->pointer_time); gtk_table_attach(GTK_TABLE(table), eventbox, 1, 3, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(ginfo->pointer_time); /* --- Cursor --- */ label = gtk_label_new("Cursor:"); set_label_cursor(label); gtk_table_attach(GTK_TABLE(table), label, 4, 5, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(label); ginfo->cursor_label = gtk_label_new("0.0"); eventbox = gtk_event_box_new(); set_label_cursor(eventbox); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); gtk_container_add(GTK_CONTAINER(eventbox), ginfo->cursor_label); gtk_table_attach(GTK_TABLE(table), eventbox, 6, 8, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(ginfo->cursor_label); /* --- Marker A --- */ hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Marker"); set_label_a(label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); label = gtk_label_new("A:"); colorAB.red = 0; colorAB.green = (0xff) *(65535/255); colorAB.blue = 0; eventbox = gtk_event_box_new(); set_label_a(eventbox); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); gtk_container_add(GTK_CONTAINER(eventbox), label); gtk_box_pack_start(GTK_BOX(hbox), eventbox, FALSE, FALSE, 0); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), hbox, 9, 10, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); ginfo->marka_label = gtk_label_new("0.0"); eventbox = gtk_event_box_new(); set_label_a(eventbox); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); gtk_container_add(GTK_CONTAINER(eventbox), ginfo->marka_label); gtk_table_attach(GTK_TABLE(table), eventbox, 11, 13, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(ginfo->marka_label); /* --- Marker B --- */ hbox = gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); label = gtk_label_new("Marker"); set_label_b(label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); label = gtk_label_new("B:"); colorAB.red = (0xff) *(65535/255); colorAB.green = 0; colorAB.blue = 0; eventbox = gtk_event_box_new(); set_label_b(eventbox); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); gtk_container_add(GTK_CONTAINER(eventbox), label); gtk_box_pack_start(GTK_BOX(hbox), eventbox, FALSE, FALSE, 0); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), hbox, 14, 15, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(label); ginfo->markb_label = gtk_label_new("0.0"); eventbox = gtk_event_box_new(); set_label_b(eventbox); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); gtk_container_add(GTK_CONTAINER(eventbox), ginfo->markb_label); gtk_table_attach(GTK_TABLE(table), eventbox, 16, 18, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(ginfo->markb_label); /* --- Delta --- */ label = gtk_label_new("A,B Delta:"); gtk_table_attach(GTK_TABLE(table), label, 19, 20, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(label); ginfo->delta_label = gtk_label_new("0.0"); eventbox = gtk_event_box_new(); gtk_widget_show(eventbox); gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); gtk_container_add(GTK_CONTAINER(eventbox), ginfo->delta_label); gtk_table_attach(GTK_TABLE(table), eventbox, 21, 23, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); gtk_widget_show(ginfo->delta_label); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(ginfo->widget), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); ginfo->scrollwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ginfo->scrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show(ginfo->scrollwin); ginfo->hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(ginfo->scrollwin)); ginfo->info_scrollwin = gtk_scrolled_window_new(NULL, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(ginfo->scrollwin))); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ginfo->info_scrollwin), GTK_POLICY_NEVER, GTK_POLICY_NEVER); gtk_widget_show(ginfo->info_scrollwin); gtk_box_pack_start(GTK_BOX(hbox), ginfo->info_scrollwin, FALSE, FALSE, 0); ginfo->info = create_graph_info(ginfo); gtk_widget_show(ginfo->info); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ginfo->info_scrollwin), ginfo->info); gtk_box_pack_start(GTK_BOX (hbox), ginfo->scrollwin, TRUE, TRUE, 0); gtk_signal_connect(GTK_OBJECT(ginfo->hadj), "value_changed", (GtkSignalFunc) value_changed, ginfo); ginfo->draw = gtk_drawing_area_new(); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "expose_event", (GtkSignalFunc) expose_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "button_press_event", (GtkSignalFunc) button_press_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "configure_event", (GtkSignalFunc) configure_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "motion_notify_event", (GtkSignalFunc) motion_notify_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "button_release_event", (GtkSignalFunc) button_release_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "leave-notify-event", (GtkSignalFunc) leave_notify_event, ginfo); gtk_signal_connect(GTK_OBJECT(ginfo->draw), "destroy", (GtkSignalFunc) destroy_event, ginfo); gtk_widget_set_events(ginfo->draw, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ginfo->scrollwin), ginfo->draw); gtk_widget_show(ginfo->draw); return ginfo; } struct graph_info * trace_graph_create(struct tracecmd_input *handle) { return trace_graph_create_with_callbacks(handle, NULL); } trace-cmd-2.5.3/trace-graph.h000066400000000000000000000317201246701203100157250ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_GRAPH_H #define _TRACE_GRAPH_H #include #include "trace-cmd.h" #include "trace-filter-hash.h" #include "trace-xml.h" struct graph_info; typedef void (graph_select_cb)(struct graph_info *ginfo, guint64 time); typedef void (graph_filter_cb)(struct graph_info *ginfo, struct filter_task *task_filter, struct filter_task *hide_tasks); /* Used for quereing what plots are defined */ enum graph_plot_type { PLOT_TYPE_OTHER, PLOT_TYPE_CPU, PLOT_TYPE_TASK, }; struct graph_plot; struct plot_info { gboolean line; int lcolor; unsigned long long ltime; gboolean box; int bcolor; unsigned long long bstart; unsigned long long bend; gboolean bfill; }; /* * match_time: * Return true if a selected time should expose plot. * Should only return true if an event has the exact time that * is passed in. * * start: * Initialize for plotting. This is called with the start time * to start plotting. * * plot_event: * This is called by the plotter. * color returns the color that should be printed. * line returns 1 or 0 if a line should be drawn. * ltime returns the time that the line should be drawn at * (ignored if line is 0) * box returns 1 or 0 if a box should be drawn * bstart is the time the box starts at * bend is the time the box ends at * (bstart and bend are ignored if box is 0) * bfill whether or not to fill the box (default TRUE) * time is the time of the current event * * end: * called at the end of the plotting in case the plotter needs to * release any resourses. * display_last_event: * If enough space between the event before and the event after * a event, the plot may ask to display that event. * The time will be given to find the event, the time may be before * the given event. * * find_record: * return a tracecmd record for a given time. * * display_info: * display information about a given time. A resolution is * passed in to show how much time is in 1 pixel. * * destroy: * destructor routine. Cleans up all resourses that the plot allocated. */ struct plot_callbacks { int (*match_time)(struct graph_info *, struct graph_plot *, unsigned long long time); void (*start)(struct graph_info *, struct graph_plot *, unsigned long long time); int (*plot_event)(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record, struct plot_info *info); void (*end)(struct graph_info *, struct graph_plot *); int (*display_last_event)(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time); struct pevent_record *(*find_record)(struct graph_info *, struct graph_plot *, unsigned long long time); int (*display_info)(struct graph_info *, struct graph_plot *, struct trace_seq *s, unsigned long long time); void (*destroy)(struct graph_info *, struct graph_plot *); }; struct graph_plot { enum graph_plot_type type; int pos; char *label; const struct plot_callbacks *cb; void *private; /* Used for drawing */ gint last_color; gint p1, p2, p3; GdkGC *gc; }; struct graph_callbacks { graph_select_cb *select; graph_filter_cb *filter; }; struct plot_list { struct plot_list *next; struct graph_plot *plot; }; struct plot_hash { struct plot_hash *next; struct plot_list *plots; gint val; }; #define PLOT_HASH_SIZE 1024 #define TASK_HASH_SIZE 1024 struct task_list; struct graph_info { struct tracecmd_input *handle; struct pevent *pevent; gint cpus; gint plots; struct graph_plot **plot_array; /* all plots */ struct graph_plot *plot_clicked; /* plot that was clicked on */ gint nr_task_hash; struct plot_hash *task_hash[PLOT_HASH_SIZE]; struct plot_hash *cpu_hash[PLOT_HASH_SIZE]; struct plot_list *all_recs; struct task_list *tasks[TASK_HASH_SIZE]; GtkWidget *widget; /* Box to hold graph */ GtkWidget *status_hbox; /* hbox holding status info */ GtkWidget *pointer_time; /* time that pointer is at */ GtkWidget *cursor_label; /* label showing cursor time */ GtkWidget *marka_label; /* label showing Marker A time */ GtkWidget *markb_label; /* label showing Marker B time */ GtkWidget *delta_label; /* label showing delta of B - A */ GtkWidget *scrollwin; /* graph scroll window */ GtkWidget *info_scrollwin; /* graph scroll window (for info widget) */ GtkWidget *info; /* info window */ GtkWidget *draw; GdkPixmap *curr_pixmap; /* pixmap backstore */ GdkPixmap *info_pixmap; /* pixmap backstore */ GtkAdjustment *hadj; /* scrollwindow horizontal adjust */ guint64 start_time; /* True start time of trace */ guint64 end_time; /* True end time of trace */ guint64 view_start_time; /* visible start time */ guint64 view_end_time; /* visible end time */ gint start_x; /* virutal start of visible area */ guint64 cursor; /* time of cursor (double clicked) */ gdouble resolution; /* pixels / time */ gint press_x; /* x where button is pressed */ gint last_x; /* last x seen while moving mouse */ gboolean line_active; /* set when button is pressed */ guint64 line_time; /* time line of where line_active is set */ guint64 marka_time; /* time that marker A is at */ guint64 markb_time; /* time that marker B is at */ gboolean show_marka; /* draw marker A line */ gboolean show_markb; /* draw marker B line */ gboolean zoom; /* set when shift button is pressed */ gdouble hadj_value; /* value to set hadj width */ gdouble hadj_page_size; /* visible size to set hadj */ gint draw_width; /* width of pixmap */ gint draw_height; /* height of pixmap */ gint full_width; /* width of full trace in pixels */ /* This includes non visible part of trace */ struct graph_callbacks *callbacks; /* call back hooks for changes to graph */ gboolean filter_enabled; gboolean filter_available; gboolean all_events; /* all events enabled */ struct event_filter *event_filter; /* filtered events */ /* cache of event fields */ gint ftrace_sched_switch_id; gint event_sched_switch_id; gint event_wakeup_id; gint event_wakeup_new_id; struct format_field *event_prev_state; struct format_field *event_pid_field; struct format_field *event_comm_field; struct format_field *ftrace_pid_field; struct format_field *ftrace_comm_field; struct format_field *wakeup_pid_field; struct format_field *wakeup_success_field; struct format_field *wakeup_new_pid_field; struct format_field *wakeup_new_success_field; gboolean read_comms; /* Read all comms on first load */ struct filter_task *task_filter; gint filter_task_selected; struct filter_task *hide_tasks; /* Box info for plot data info window */ gint plot_data_x; gint plot_data_y; gint plot_data_w; gint plot_data_h; }; struct graph_info * trace_graph_create(struct tracecmd_input *handle); struct graph_info * trace_graph_create_with_callbacks(struct tracecmd_input *handle, struct graph_callbacks *cbs); void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time); void trace_graph_event_filter_callback(gboolean accept, gboolean all_events, gchar **systems, gint *events, gpointer data); void trace_graph_adv_filter_callback(gboolean accept, const gchar *text, gint *event_ids, gpointer data); static inline GtkWidget *trace_graph_get_draw(struct graph_info *ginfo) { return ginfo->draw; } static inline struct graph_callbacks *trace_graph_get_callbacks(struct graph_info *ginfo) { return ginfo->callbacks; } static inline GtkWidget *trace_graph_get_window(struct graph_info *ginfo) { return ginfo->widget; } void trace_graph_refresh(struct graph_info *ginfo); struct filter_task_item * trace_graph_filter_task_find_pid(struct graph_info *ginfo, gint pid); struct filter_task_item * trace_graph_hide_task_find_pid(struct graph_info *ginfo, gint pid); void trace_graph_filter_toggle(struct graph_info *ginfo); void trace_graph_filter_add_remove_task(struct graph_info *info, gint pid); void trace_graph_filter_hide_show_task(struct graph_info *ginfo, gint pid); void trace_graph_clear_tasks(struct graph_info *ginfo); void trace_graph_free_info(struct graph_info *ginfo); int trace_graph_load_handle(struct graph_info *ginfo, struct tracecmd_input *handle); int trace_graph_check_sched_switch(struct graph_info *ginfo, struct pevent_record *record, gint *pid, const char **comm); int trace_graph_check_sched_wakeup(struct graph_info *ginfo, struct pevent_record *record, gint *pid); gboolean trace_graph_filter_on_task(struct graph_info *ginfo, gint pid); gboolean trace_graph_filter_on_event(struct graph_info *ginfo, struct pevent_record *record); void trace_graph_copy_filter(struct graph_info *ginfo, gboolean all_events, struct event_filter *event_filter); gint *trace_graph_task_list(struct graph_info *ginfo); int trace_graph_load_filters(struct graph_info *ginfo, struct tracecmd_xml_handle *handle); int trace_graph_save_filters(struct graph_info *ginfo, struct tracecmd_xml_handle *handle); void trace_graph_update_filters(struct graph_info *ginfo, struct filter_task *task_filter, struct filter_task *hide_tasks); void trace_graph_refresh_filters(struct graph_info *ginfo); /* plots */ void trace_graph_plot_free(struct graph_info *ginfo); void trace_graph_plot_init(struct graph_info *ginfo); struct graph_plot *trace_graph_plot_append(struct graph_info *ginfo, const char *label, enum graph_plot_type type, const struct plot_callbacks *cb, void *data); struct graph_plot *trace_graph_plot_insert(struct graph_info *ginfo, int pos, const char *label, enum graph_plot_type type, const struct plot_callbacks *cb, void *data); void trace_graph_plot_remove(struct graph_info *ginfo, struct graph_plot *plot); struct plot_hash *trace_graph_plot_find_task(struct graph_info *ginfo, gint task); void trace_graph_plot_add_task(struct graph_info *ginfo, struct graph_plot *plot, gint task); void trace_graph_plot_remove_task(struct graph_info *ginfo, struct graph_plot *plot, gint task); struct plot_hash *trace_graph_plot_find_cpu(struct graph_info *ginfo, gint cpu); void trace_graph_plot_add_cpu(struct graph_info *ginfo, struct graph_plot *plot, gint cpu); void trace_graph_plot_remove_cpu(struct graph_info *ginfo, struct graph_plot *plot, gint cpu); void trace_graph_plot_add_all_recs(struct graph_info *ginfo, struct graph_plot *plot); void trace_graph_plot_remove_all_recs(struct graph_info *ginfo, struct graph_plot *plot); /* plot callbacks */ int trace_graph_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time); int trace_graph_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time); void trace_graph_plot_start(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time); int trace_graph_plot_event(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record, struct plot_info *info); void trace_graph_plot_end(struct graph_info *ginfo, struct graph_plot *plot); struct pevent_record * trace_graph_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time); int trace_graph_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time); /* cpu plot */ void graph_plot_init_cpus(struct graph_info *ginfo, int cpus); void graph_plot_cpus_plotted(struct graph_info *ginfo, gboolean *all_cpus, guint64 **cpu_mask); void graph_plot_cpus_update_callback(gboolean accept, gboolean all_cpus, guint64 *selected_cpu_mask, gpointer data); /* task plot */ void graph_plot_task(struct graph_info *ginfo, int pid, int pos); void graph_plot_task_update_callback(gboolean accept, gint *selected, gint *non_select, gpointer data); void graph_plot_task_plotted(struct graph_info *ginfo, gint **plotted); #endif /* _TRACE_GRAPH_H */ trace-cmd-2.5.3/trace-gui.h000066400000000000000000000040071246701203100154060ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_GUI #define _TRACE_GUI #include enum trace_dialog_type { TRACE_GUI_INFO, TRACE_GUI_WARNING, TRACE_GUI_ERROR, TRACE_GUI_ASK, }; GtkWidget *trace_status_bar_new(void); enum trace_dialog_filter { TRACE_DIALOG_FILTER_NONE, TRACE_DIALOG_FILTER_DATA, TRACE_DIALOG_FILTER_FILTER, TRACE_DIALOG_FILTER_SETTING, }; void trace_dialog_register_window(GtkWidget *window); void trace_dialog_register_alt_warning(void (*alt)(const char *fmt, va_list ap)); void trace_show_help(GtkWidget *window, const gchar *link, GError **error); GtkResponseType trace_dialog(GtkWindow *parent, enum trace_dialog_type type, gchar *message, ...); gchar *trace_get_file_dialog_filter(const gchar *title, const char *open, enum trace_dialog_filter, gboolean warn); gchar *trace_get_file_dialog(const gchar *title, const char *open, gboolean warn); void trace_set_cursor(GdkCursorType type); void trace_put_cursor(void); void trace_freeze_all(void); void trace_unfreeze_all(void); GtkWidget * trace_create_combo_box(GtkWidget *hbox, const gchar *text, GtkTreeModel *(*combo_model_create)(gpointer data), gpointer data); #endif /* _TRACE_GUI */ trace-cmd-2.5.3/trace-hash-local.h000066400000000000000000000027771246701203100166510ustar00rootroot00000000000000/* * Copyright (C) 2009, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_HASH_LOCAL_H #define _TRACE_HASH_LOCAL_H static inline unsigned int trace_hash(int val) { 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; } #endif /* _TRACE_HASH_LOCAL_H */ trace-cmd-2.5.3/trace-hash.c000066400000000000000000000042731246701203100155450ustar00rootroot00000000000000/* * Copyright (C) 2014, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #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_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.5.3/trace-hash.h000066400000000000000000000043251246701203100155500ustar00rootroot00000000000000/* * Copyright (C) 2014 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef _TRACE_HASH_H #define _TRACE_HASH_H #include "trace-hash-local.h" #include "list.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); 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.5.3/trace-hist.c000066400000000000000000000574131246701203100155750ustar00rootroot00000000000000/* * Copyright (C) 2013 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Several of the ideas in this file came from Arnaldo Carvalho de Melo's * work on the perf ui. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include "trace-local.h" #include "list.h" static int sched_wakeup_type; static int sched_wakeup_new_type; static int sched_switch_type; static int function_type; static int function_graph_entry_type; static int function_graph_exit_type; static int kernel_stack_type; static int long_size; struct format_field *common_type_field; struct format_field *common_pid_field; struct format_field *sched_wakeup_comm_field; struct format_field *sched_wakeup_new_comm_field; struct format_field *sched_wakeup_pid_field; struct format_field *sched_wakeup_new_pid_field; struct format_field *sched_switch_prev_field; struct format_field *sched_switch_next_field; struct format_field *sched_switch_prev_pid_field; struct format_field *sched_switch_next_pid_field; struct format_field *function_ip_field; struct format_field *function_parent_ip_field; struct format_field *function_graph_entry_func_field; struct format_field *function_graph_entry_depth_field; struct format_field *function_graph_exit_func_field; struct format_field *function_graph_exit_depth_field; struct format_field *function_graph_exit_calltime_field; struct format_field *function_graph_exit_rettime_field; struct format_field *function_graph_exit_overrun_field; struct format_field *kernel_stack_caller_field; static int compact; static void *zalloc(size_t size) { return calloc(1, size); } static const char **ips; static int ips_idx; static int func_depth; static int current_pid = -1; struct stack_save { struct stack_save *next; const char **ips; int ips_idx; int func_depth; int pid; }; struct stack_save *saved_stacks; static void reset_stack(void) { current_pid = -1; ips_idx = 0; func_depth = 0; /* Don't free here, it may be saved */ ips = NULL; } static void save_stack(void) { struct stack_save *stack; stack = zalloc(sizeof(*stack)); if (!stack) die("malloc"); stack->pid = current_pid; stack->ips_idx = ips_idx; stack->func_depth = func_depth; stack->ips = ips; stack->next = saved_stacks; saved_stacks = stack; reset_stack(); } static void restore_stack(int pid) { struct stack_save *last = NULL, *stack; for (stack = saved_stacks; stack; last = stack, stack = stack->next) { if (stack->pid == pid) break; } if (!stack) return; if (last) last->next = stack->next; else saved_stacks = stack->next; current_pid = stack->pid; ips_idx = stack->ips_idx; func_depth = stack->func_depth; free(ips); ips = stack->ips; free(stack); } struct pid_list; struct chain { struct chain *next; struct chain *sibling; const char *func; struct chain *parents; struct pid_list *pid_list; int nr_parents; int count; int total; int event; }; static struct chain *chains; static int nr_chains; static int total_counts; struct pid_list { struct pid_list *next; struct chain chain; int pid; }; static struct pid_list *list_pids; static struct pid_list all_pid_list; static void add_chain(struct chain *chain) { if (chain->next) die("chain not null?"); chain->next = chains; chains = chain; nr_chains++; } static void insert_chain(struct pid_list *pid_list, struct chain *chain_list, const char **chain_str, int size, int event) { struct chain *chain; /* Record all counts */ if (!chain_list->func) total_counts++; chain_list->count++; if (!size--) return; for (chain = chain_list->parents; chain; chain = chain->sibling) { if (chain->func == chain_str[size]) { insert_chain(pid_list, chain, chain_str, size, 0); return; } } chain_list->nr_parents++; chain = zalloc(sizeof(struct chain)); if (!chain) die("malloc"); chain->sibling = chain_list->parents; chain_list->parents = chain; chain->func = chain_str[size]; chain->pid_list = pid_list; chain->event = event; /* NULL func means this is the top level of the chain. Store it */ if (!chain_list->func) add_chain(chain); insert_chain(pid_list, chain, chain_str, size, 0); } static void save_call_chain(int pid, const char **chain, int size, int event) { static struct pid_list *pid_list; if (compact) pid_list = &all_pid_list; else if (!pid_list || pid_list->pid != pid) { for (pid_list = list_pids; pid_list; pid_list = pid_list->next) { if (pid_list->pid == pid) break; } if (!pid_list) { pid_list = zalloc(sizeof(*pid_list)); if (!pid_list) die("malloc"); pid_list->pid = pid; pid_list->next = list_pids; list_pids = pid_list; } } insert_chain(pid_list, &pid_list->chain, chain, size, event); } static void save_stored_stacks(void) { while (saved_stacks) { restore_stack(saved_stacks->pid); save_call_chain(current_pid, ips, ips_idx, 0); } } static void flush_stack(void) { if (current_pid < 0) return; save_call_chain(current_pid, ips, ips_idx, 0); free(ips); reset_stack(); } static void push_stack_func(const char *func) { ips_idx++; ips = realloc(ips, ips_idx * sizeof(char *)); ips[ips_idx - 1] = func; } static void pop_stack_func(void) { ips_idx--; ips[ips_idx] = NULL; } static void process_function(struct pevent *pevent, struct pevent_record *record) { unsigned long long parent_ip; unsigned long long ip; unsigned long long val; const char *parent; const char *func; int pid; int ret; ret = pevent_read_number_field(common_pid_field, record->data, &val); if (ret < 0) die("no pid field for function?"); ret = pevent_read_number_field(function_ip_field, record->data, &ip); if (ret < 0) die("no ip field for function?"); ret = pevent_read_number_field(function_parent_ip_field, record->data, &parent_ip); if (ret < 0) die("no parent ip field for function?"); pid = val; func = pevent_find_function(pevent, ip); parent = pevent_find_function(pevent, parent_ip); if (current_pid >= 0 && pid != current_pid) { save_stack(); restore_stack(pid); } current_pid = pid; if (ips_idx) { if (ips[ips_idx - 1] == parent) push_stack_func(func); else { save_call_chain(pid, ips, ips_idx, 0); while (ips_idx) { pop_stack_func(); if (ips[ips_idx - 1] == parent) { push_stack_func(func); break; } } } } /* The above check can set ips_idx to zero again */ if (!ips_idx) { push_stack_func(parent); push_stack_func(func); } } static void process_function_graph_entry(struct pevent *pevent, struct pevent_record *record) { unsigned long long depth; unsigned long long ip; unsigned long long val; const char *func; int pid; int ret; ret = pevent_read_number_field(common_pid_field, record->data, &val); if (ret < 0) die("no pid field for function graph entry?"); ret = pevent_read_number_field(function_graph_entry_func_field, record->data, &ip); if (ret < 0) die("no ip field for function graph entry?"); ret = pevent_read_number_field(function_graph_entry_depth_field, record->data, &depth); if (ret < 0) die("no parent ip field for function entry?"); pid = val; func = pevent_find_function(pevent, ip); if (current_pid >= 0 && pid != current_pid) { save_stack(); restore_stack(pid); } current_pid = pid; if (depth != ips_idx) { save_call_chain(pid, ips, ips_idx, 0); while (ips_idx > depth) pop_stack_func(); } func_depth = depth; push_stack_func(func); } static void process_function_graph_exit(struct pevent *pevent, struct pevent_record *record) { unsigned long long depth; unsigned long long val; int pid; int ret; ret = pevent_read_number_field(common_pid_field, record->data, &val); if (ret < 0) die("no pid field for function graph exit?"); ret = pevent_read_number_field(function_graph_exit_depth_field, record->data, &depth); if (ret < 0) die("no parent ip field for function?"); pid = val; if (current_pid >= 0 && pid != current_pid) { save_stack(); restore_stack(pid); } current_pid = pid; if (ips_idx != depth) { save_call_chain(pid, ips, ips_idx, 0); while (ips_idx > depth) pop_stack_func(); } func_depth = depth - 1; } static int pending_pid = -1; static const char **pending_ips; static int pending_ips_idx; static void reset_pending_stack(void) { pending_pid = -1; pending_ips_idx = 0; free(pending_ips); pending_ips = NULL; } static void copy_stack_to_pending(int pid) { pending_pid = pid; pending_ips = zalloc(sizeof(char *) * ips_idx); memcpy(pending_ips, ips, sizeof(char *) * ips_idx); pending_ips_idx = ips_idx; } static void process_kernel_stack(struct pevent *pevent, struct pevent_record *record) { struct format_field *field = kernel_stack_caller_field; unsigned long long val; void *data = record->data; int do_restore = 0; int pid; int ret; ret = pevent_read_number_field(common_pid_field, record->data, &val); if (ret < 0) die("no pid field for function?"); pid = val; if (pending_pid >= 0 && pid != pending_pid) { reset_pending_stack(); return; } if (!field) die("no caller field for kernel stack?"); if (pending_pid >= 0) { if (current_pid >= 0) { save_stack(); do_restore = 1; } } else { /* function stack trace? */ if (current_pid >= 0) { copy_stack_to_pending(current_pid); free(ips); reset_stack(); } } current_pid = pid; /* Need to start at the end of the callers and work up */ for (data += field->offset; data < record->data + record->size; data += long_size) { unsigned long long addr; addr = pevent_read_number(pevent, data, long_size); if ((long_size == 8 && addr == (unsigned long long)-1) || ((int)addr == -1)) break; } for (data -= long_size; data >= record->data + field->offset; data -= long_size) { unsigned long long addr; const char *func; addr = pevent_read_number(pevent, data, long_size); func = pevent_find_function(pevent, addr); if (func) push_stack_func(func); } if (pending_pid >= 0) { push_stack_func(pending_ips[pending_ips_idx - 1]); reset_pending_stack(); } save_call_chain(current_pid, ips, ips_idx, 1); if (do_restore) restore_stack(current_pid); } static void process_sched_wakeup(struct pevent *pevent, struct pevent_record *record, int type) { unsigned long long val; const char *comm; int pid; int ret; if (type == sched_wakeup_type) { comm = (char *)(record->data + sched_wakeup_comm_field->offset); ret = pevent_read_number_field(sched_wakeup_pid_field, record->data, &val); if (ret < 0) die("no pid field in sched_wakeup?"); } else { comm = (char *)(record->data + sched_wakeup_new_comm_field->offset); ret = pevent_read_number_field(sched_wakeup_new_pid_field, record->data, &val); if (ret < 0) die("no pid field in sched_wakeup_new?"); } pid = val; pevent_register_comm(pevent, comm, pid); } static void process_sched_switch(struct pevent *pevent, struct pevent_record *record) { unsigned long long val; const char *comm; int pid; int ret; comm = (char *)(record->data + sched_switch_prev_field->offset); ret = pevent_read_number_field(sched_switch_prev_pid_field, record->data, &val); if (ret < 0) die("no prev_pid field in sched_switch?"); pid = val; pevent_register_comm(pevent, comm, pid); comm = (char *)(record->data + sched_switch_next_field->offset); ret = pevent_read_number_field(sched_switch_next_pid_field, record->data, &val); if (ret < 0) die("no next_pid field in sched_switch?"); pid = val; pevent_register_comm(pevent, comm, pid); } static void process_event(struct pevent *pevent, struct pevent_record *record, int type) { struct event_format *event; const char *event_name; unsigned long long val; int pid; int ret; if (pending_pid >= 0) { save_call_chain(pending_pid, pending_ips, pending_ips_idx, 1); reset_pending_stack(); } event = pevent_data_event_from_type(pevent, type); event_name = event->name; ret = pevent_read_number_field(common_pid_field, record->data, &val); if (ret < 0) die("no pid field for function?"); pid = val; /* * Even if function or function graph tracer is running, * if the user ran with stack traces on events, we want to use * that instead. But unfortunately, that stack doesn't come * until after the event. Thus, we only add the event into * the pending stack. */ push_stack_func(event_name); copy_stack_to_pending(pid); pop_stack_func(); } static void process_record(struct pevent *pevent, struct pevent_record *record) { unsigned long long val; int type; pevent_read_number_field(common_type_field, record->data, &val); type = val; if (type == function_type) return process_function(pevent, record); if (type == function_graph_entry_type) return process_function_graph_entry(pevent, record); if (type == function_graph_exit_type) return process_function_graph_exit(pevent, record); if (type == kernel_stack_type) return process_kernel_stack(pevent, record); if (type == sched_wakeup_type || type == sched_wakeup_new_type) process_sched_wakeup(pevent, record, type); else if (type == sched_switch_type) process_sched_switch(pevent, record); process_event(pevent, record, type); } static struct event_format * update_event(struct pevent *pevent, const char *sys, const char *name, int *id) { struct event_format *event; event = pevent_find_event_by_name(pevent, sys, name); if (!event) return NULL; *id = event->id; return event; } static void update_sched_wakeup(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "sched", "sched_wakeup", &sched_wakeup_type); if (!event) return; sched_wakeup_comm_field = pevent_find_field(event, "comm"); sched_wakeup_pid_field = pevent_find_field(event, "pid"); } static void update_sched_wakeup_new(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "sched", "sched_wakeup_new", &sched_wakeup_new_type); if (!event) return; sched_wakeup_new_comm_field = pevent_find_field(event, "comm"); sched_wakeup_new_pid_field = pevent_find_field(event, "pid"); } static void update_sched_switch(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "sched", "sched_switch", &sched_switch_type); if (!event) return; sched_switch_prev_field = pevent_find_field(event, "prev_comm"); sched_switch_next_field = pevent_find_field(event, "next_comm"); sched_switch_prev_pid_field = pevent_find_field(event, "prev_pid"); sched_switch_next_pid_field = pevent_find_field(event, "next_pid"); } static void update_function(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "ftrace", "function", &function_type); if (!event) return; function_ip_field = pevent_find_field(event, "ip"); function_parent_ip_field = pevent_find_field(event, "parent_ip"); } static void update_function_graph_entry(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "ftrace", "funcgraph_entry", &function_graph_entry_type); if (!event) return; function_graph_entry_func_field = pevent_find_field(event, "func"); function_graph_entry_depth_field = pevent_find_field(event, "depth"); } static void update_function_graph_exit(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "ftrace", "funcgraph_exit", &function_graph_exit_type); if (!event) return; function_graph_exit_func_field = pevent_find_field(event, "func"); function_graph_exit_depth_field = pevent_find_field(event, "depth"); function_graph_exit_calltime_field = pevent_find_field(event, "calltime"); function_graph_exit_rettime_field = pevent_find_field(event, "rettime"); function_graph_exit_overrun_field = pevent_find_field(event, "overrun"); } static void update_kernel_stack(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "ftrace", "kernel_stack", &kernel_stack_type); if (!event) return; kernel_stack_caller_field = pevent_find_field(event, "caller"); } enum field { NEXT_PTR, SIB_PTR }; static struct chain *next_ptr(struct chain *chain, enum field field) { if (field == NEXT_PTR) return chain->next; return chain->sibling; } static struct chain *split_chain(struct chain *orig, int size, enum field field) { struct chain *chain; int i; if (size < 2) return NULL; for (i = 1; i < (size + 1) / 2; i++, orig = next_ptr(orig, field)) ; if (field == NEXT_PTR) { chain = orig->next; orig->next = NULL; } else { chain = orig->sibling; orig->sibling = NULL; } return chain; } static struct chain * merge_chains(struct chain *a, int nr_a, struct chain *b, int nr_b, enum field field) { struct chain *chain; struct chain *final; struct chain **next = &final; int i; if (!a) return b; if (!b) return a; for (i = 0, chain = a; chain; i++, chain = next_ptr(chain, field)) ; if (i != nr_a) die("WTF %d %d", i, nr_a); chain = split_chain(a, nr_a, field); a = merge_chains(chain, nr_a / 2, a, (nr_a + 1) / 2, field); chain = split_chain(b, nr_b, field); b = merge_chains(chain, nr_b / 2, b, (nr_b + 1) / 2, field); while (a && b) { if (a->count > b->count) { *next = a; if (field == NEXT_PTR) next = &a->next; else next = &a->sibling; a = *next; *next = NULL; } else { *next = b; if (field == NEXT_PTR) next = &b->next; else next = &b->sibling; b = *next; *next = NULL; } } if (a) *next = a; else *next = b; return final; } static void sort_chain_parents(struct chain *chain) { struct chain *parent; parent = split_chain(chain->parents, chain->nr_parents, SIB_PTR); chain->parents = merge_chains(parent, chain->nr_parents / 2, chain->parents, (chain->nr_parents + 1) / 2, SIB_PTR); for (chain = chain->parents; chain; chain = chain->sibling) sort_chain_parents(chain); } static void sort_chains(void) { struct chain *chain; chain = split_chain(chains, nr_chains, NEXT_PTR); /* The original always has more or equal to the split */ chains = merge_chains(chain, nr_chains / 2, chains, (nr_chains + 1) / 2, NEXT_PTR); for (chain = chains; chain; chain = chain->next) sort_chain_parents(chain); } static double get_percent(int total, int partial) { return ((double)partial / (double)total) * 100.0; } static int single_chain(struct chain *chain) { if (chain->nr_parents > 1) return 0; if (!chain->parents) return 1; return single_chain(chain->parents); } #define START " |\n" #define TICK " --- " #define BLANK " " #define LINE " |" #define INDENT " " unsigned long long line_mask; void make_indent(int indent) { int i; for (i = 0; i < indent; i++) { if (line_mask & (1 << i)) printf(LINE); else printf(INDENT); } } static void print_single_parent(struct chain *chain, int indent) { make_indent(indent); printf(BLANK); printf("%s\n", chain->parents->func); } static void dump_chain(struct pevent *pevent, struct chain *chain, int indent) { if (!chain->parents) return; print_single_parent(chain, indent); dump_chain(pevent, chain->parents, indent); } static void print_parents(struct pevent *pevent, struct chain *chain, int indent) { struct chain *parent = chain->parents; int x; if (single_chain(chain)) { dump_chain(pevent, chain, indent); return; } line_mask |= 1ULL << (indent); for (x = 0; parent; x++, parent = parent->sibling) { struct chain *save_parent; make_indent(indent + 1); printf("\n"); make_indent(indent + 1); printf("--%%%.2f-- %s # %d\n", get_percent(chain->count, parent->count), parent->func, parent->count); if (x == chain->nr_parents - 1) line_mask &= (1ULL << indent) - 1; if (single_chain(parent)) dump_chain(pevent, parent, indent + 1); else { save_parent = parent; while (parent && parent->parents && parent->nr_parents < 2 && parent->parents->count == parent->count) { print_single_parent(parent, indent + 1); parent = parent->parents; } if (parent) print_parents(pevent, parent, indent + 1); parent = save_parent; } } } static void print_chains(struct pevent *pevent) { struct chain *chain = chains; int pid; for (; chain; chain = chain->next) { pid = chain->pid_list->pid; if (chain != chains) printf("\n"); if (compact) printf(" %%%3.2f %30s #%d\n", get_percent(total_counts, chain->count), chain->func, chain->count); else printf(" %%%3.2f (%d) %s %30s #%d\n", get_percent(total_counts, chain->count), pid, pevent_data_comm_from_pid(pevent, pid), chain->func, chain->count); printf(START); if (chain->event) printf(TICK "*%s*\n", chain->func); else printf(TICK "%s\n", chain->func); print_parents(pevent, chain, 0); } } static void do_trace_hist(struct tracecmd_input *handle) { struct pevent *pevent = tracecmd_get_pevent(handle); struct event_format *event; struct pevent_record *record; int cpus; int cpu; int ret; ret = tracecmd_init_data(handle); if (ret < 0) die("failed to init data"); if (ret > 0) die("trace-cmd hist does not work with latency traces\n"); cpus = tracecmd_cpus(handle); /* Need to get any event */ for (cpu = 0; cpu < cpus; cpu++) { record = tracecmd_peek_data(handle, cpu); if (record) break; } if (!record) die("No records found in file"); ret = pevent_data_type(pevent, record); event = pevent_data_event_from_type(pevent, ret); long_size = tracecmd_long_size(handle); common_type_field = pevent_find_common_field(event, "common_type"); if (!common_type_field) die("Can't find a 'type' field?"); common_pid_field = pevent_find_common_field(event, "common_pid"); if (!common_pid_field) die("Can't find a 'pid' field?"); update_sched_wakeup(pevent); update_sched_wakeup_new(pevent); update_sched_switch(pevent); update_function(pevent); update_function_graph_entry(pevent); update_function_graph_exit(pevent); update_kernel_stack(pevent); for (cpu = 0; cpu < cpus; cpu++) { for (;;) { struct pevent_record *record; record = tracecmd_read_data(handle, cpu); if (!record) break; /* If we missed events, just flush out the current stack */ if (record->missed_events) flush_stack(); process_record(pevent, record); free_record(record); } } if (current_pid >= 0) save_call_chain(current_pid, ips, ips_idx, 0); if (pending_pid >= 0) save_call_chain(pending_pid, pending_ips, pending_ips_idx, 1); save_stored_stacks(); sort_chains(); print_chains(pevent); } void trace_hist(int argc, char **argv) { struct tracecmd_input *handle; const char *input_file = NULL; int ret; for (;;) { int c; c = getopt(argc-1, argv+1, "+hi:P"); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'i': if (input_file) die("Only one input for historgram"); input_file = optarg; break; case 'P': compact = 1; break; default: usage(argv); } } if ((argc - optind) >= 2) { if (input_file) usage(argv); input_file = argv[optind + 1]; } if (!input_file) input_file = "trace.dat"; handle = tracecmd_alloc(input_file); if (!handle) die("can't open %s\n", input_file); ret = tracecmd_read_headers(handle); if (ret) return; do_trace_hist(handle); tracecmd_close(handle); } trace-cmd-2.5.3/trace-hooks.c000066400000000000000000000073721246701203100157500ustar00rootroot00000000000000/* * Copyright (C) 2015 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include "trace-cmd.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_or_die(sizeof(*hook)); memset(hook, 0, sizeof(*hook)); str = strdup(arg); if (!str) die("malloc"); 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: die("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.5.3/trace-input.c000066400000000000000000002000041246701203100157470ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd-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 { struct list_head list; off64_t offset; struct tracecmd_input *handle; void *map; int ref_count; long long lost_events; #if DEBUG_RECORD struct pevent_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 pages; struct pevent_record *next; struct page *page; struct kbuffer *kbuf; int cpu; int pipe_fd; }; struct input_buffer_instance { char *name; size_t offset; }; struct tracecmd_input { struct pevent *pevent; struct plugin_list *plugin_list; struct tracecmd_input *parent; unsigned long flags; int fd; int long_size; int page_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; char * cpustats; char * uname; struct input_buffer_instance *buffers; 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; }; __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; } #if DEBUG_RECORD static void remove_record(struct page *page, struct pevent_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 pevent_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 list_head *pages) { static char buf[BUFSIZ + 1]; struct pevent_record *record; struct page *page; int len; memset(buf, 0, sizeof(buf)); len = 0; list_for_each_entry(page, pages, list) { 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 pevent_record *record) {} static inline void add_record(struct page *page, struct pevent_record *record) {} static const char *show_records(struct list_head *pages) { return ""; } #endif static int init_cpu(struct tracecmd_input *handle, int cpu); static int do_read(struct tracecmd_input *handle, void *data, int size) { int tot = 0; int r; do { r = read(handle->fd, data, size - tot); tot += r; if (!r) break; if (r < 0) return r; } while (tot != size); return tot; } static int do_read_check(struct tracecmd_input *handle, void *data, int size) { int 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; int size = 0; int i; int 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 unsigned int read4(struct tracecmd_input *handle) { struct pevent *pevent = handle->pevent; unsigned int data; if (do_read_check(handle, &data, 4)) return -1; return __data2host4(pevent, data); } static unsigned long long read8(struct tracecmd_input *handle) { struct pevent *pevent = handle->pevent; unsigned long long data; if (do_read_check(handle, &data, 8)) return -1; return __data2host8(pevent, data); } static int read_header_files(struct tracecmd_input *handle) { struct pevent *pevent = handle->pevent; 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; size = read8(handle); if (size < 0) return -1; header = malloc(size); if (!header) return -1; if (do_read_check(handle, header, size)) goto failed_read; pevent_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 = pevent->header_page_size_size; if (do_read_check(handle, buf, 13)) return -1; if (memcmp(buf, "header_event", 13) != 0) return -1; size = read8(handle); if (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) die("malloc"); 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 pevent *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 (pevent_parse_event(pevent, buf, size, "ftrace")) pevent->parsing_failures = 1; } 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 pevent *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 (pevent_parse_event(pevent, buf, size, system)) pevent->parsing_failures = 1; } 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) die("malloc"); 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; int print_all = 0; int unique; int count; int ret; int i; 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; } } count = read4(handle); if (count < 0) return -1; for (i = 0; i < count; i++) { size = read8(handle); if (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; int systems; int print_all; int sys_printed; int count; int unique; int ret; int i,x; if (regex) { sreg = &spreg; ereg = &epreg; ret = make_preg_files(regex, sreg, ereg, &unique); if (ret) return -1; } systems = read4(handle); if (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; } } count = read4(handle); if (count < 0) goto failed; for (x=0; x < count; x++) { size = read8(handle); if (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 pevent *pevent = handle->pevent; int size; char *buf; size = read4(handle); if (!size) return 0; /* OK? */ if (size < 0) return -1; buf = malloc(size+1); if (!buf) return -1; if (do_read_check(handle, buf, size)){ free(buf); return -1; } buf[size] = 0; parse_proc_kallsyms(pevent, buf, size); free(buf); return 0; } static int read_ftrace_printk(struct tracecmd_input *handle) { int size; char *buf; size = read4(handle); if (!size) return 0; /* OK? */ if (size < 0) return -1; buf = malloc(size + 1); if (!buf) return -1; if (do_read_check(handle, buf, size)) { free(buf); return -1; } buf[size] = 0; parse_ftrace_printk(handle->pevent, buf, size); free(buf); return 0; } static int read_and_parse_cmdlines(struct tracecmd_input *handle); /** * 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; 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; pevent_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; } 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 *page; int ret; list_for_each_entry(page, &cpu_data->pages, list) { if (page->offset == offset) { page->ref_count++; return page; } } page = malloc(sizeof(*page)); if (!page) return NULL; memset(page, 0, sizeof(*page)); page->offset = offset; page->handle = handle; if (handle->read_page) { page->map = malloc(handle->page_size); if (page->map) { ret = read_page(handle, offset, cpu, page->map); if (ret < 0) { free(page->map); page->map = NULL; } } } else { page->map = mmap(NULL, handle->page_size, PROT_READ, MAP_PRIVATE, handle->fd, offset); if (page->map == MAP_FAILED) page->map = NULL; } if (!page->map) { free(page); return NULL; } list_add(&page->list, &cpu_data->pages); page->ref_count = 1; return page; } static void __free_page(struct tracecmd_input *handle, struct page *page) { 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 munmap(page->map, handle->page_size); list_del(&page->list); free(page); } 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 pevent_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 pevent_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 pevent_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 pevent_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 pevent *pevent = handle->pevent; void *ptr = handle->cpu_data[cpu].page->map; struct kbuffer *kbuf = handle->cpu_data[cpu].kbuf; /* FIXME: handle header page */ if (pevent->header_page_ts_size != 8) { warning("expected a long long type for timestamp"); return -1; } kbuffer_load_subbuffer(kbuf, ptr); if (kbuffer_subbuffer_size(kbuf) > handle->page_size) die("bad page read, with size of %d", kbuffer_subbuffer_size(kbuf)); handle->cpu_data[cpu].timestamp = kbuffer_timestamp(kbuf) + handle->ts_offset; 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 pevent_record * peek_event(struct tracecmd_input *handle, unsigned long long offset, int cpu) { struct pevent_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 pevent_record * read_event(struct tracecmd_input *handle, unsigned long long offset, int cpu) { struct pevent_record *record; record = peek_event(handle, offset, cpu); if (record) record = tracecmd_read_data(handle, cpu); return record; } static struct pevent_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 pevent_record * find_and_read_event(struct tracecmd_input *handle, unsigned long long offset, int *pcpu) { struct pevent_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 pevent_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 pevent_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 pevent_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 pevent_record * tracecmd_read_cpu_last(struct tracecmd_input *handle, int cpu) { struct pevent_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 pevent_record * tracecmd_translate_data(struct tracecmd_input *handle, void *ptr, int size) { struct pevent *pevent = handle->pevent; struct pevent_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 (pevent->host_bigendian == pevent->file_bigendian) 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 pevent_record * tracecmd_read_page_record(struct pevent *pevent, void *page, int size, struct pevent_record *last_record) { unsigned long long ts; struct kbuffer *kbuf; struct pevent_record *record = NULL; enum kbuffer_long_size long_size; enum kbuffer_endian endian; void *ptr; if (pevent->file_bigendian) endian = KBUFFER_ENDIAN_BIG; else endian = KBUFFER_ENDIAN_LITTLE; if (pevent->header_page_size_size == 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 pevent_record * tracecmd_peek_data(struct tracecmd_input *handle, int cpu) { struct pevent_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; 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 pevent_record * tracecmd_read_data(struct tracecmd_input *handle, int cpu) { struct pevent_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 pevent_record * tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu) { unsigned long long ts; struct pevent_record *record; int first_record = 1; int next; int cpu; if (rec_cpu) *rec_cpu = -1; next = -1; ts = 0; for (cpu = 0; cpu < handle->cpus; cpu++) { record = tracecmd_peek_data(handle, cpu); if (record && (first_record || record->ts < ts)) { ts = record->ts; next = cpu; first_record = 0; } } if (next >= 0) { if (rec_cpu) *rec_cpu = next; return tracecmd_read_data(handle, next); } 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 pevent_record * tracecmd_read_prev(struct tracecmd_input *handle, struct pevent_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->pages); if (!cpu_data->size) { printf("CPU %d is empty\n", cpu); return 0; } 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) return -1; memset(cpu_data->page, 0, sizeof(*cpu_data->page)); list_add(&cpu_data->page->list, &cpu_data->pages); 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) return -1; } } /* 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! */ return -1; } if (update_page_info(handle, cpu)) return -1; return 0; } 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; 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 = __data2host4(handle->pevent, size); buf = malloc_or_die(size); 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_CPUSTAT: buf[size-1] = '\n'; cpustats = realloc(cpustats, cpustats_size + size + 1); if (!cpustats) die("realloc"); 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) die("realloc"); buffer = &handle->buffers[handle->nr_buffers - 1]; buffer->name = strdup(buf + 8); if (!buffer->name) die("strdup"); offset = *(unsigned long long *)buf; buffer->offset = __data2host8(handle->pevent, offset); break; case TRACECMD_OPTION_TRACECLOCK: handle->use_trace_clock = true; break; case TRACECMD_OPTION_UNAME: handle->uname = strdup(buf); break; case TRACECMD_OPTION_HOOK: hook = tracecmd_create_event_hook(buf); hook = handle->hooks; handle->hooks = hook; 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 pevent *pevent = handle->pevent; enum kbuffer_long_size long_size; enum kbuffer_endian endian; unsigned long long size; char buf[10]; int cpu; if (do_read_check(handle, buf, 10)) return -1; /* 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) 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 (handle->pevent->file_bigendian) 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 (pevent->old_format) kbuffer_set_old_format(handle->cpu_data[cpu].kbuf); offset = read8(handle); size = read8(handle); handle->cpu_data[cpu].file_offset = offset; handle->cpu_data[cpu].file_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; } if (init_cpu(handle, cpu)) goto out_free; } 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) { *size = read8(handle); if (*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 pevent *pevent = handle->pevent; unsigned long long size; char *cmdlines; if (read_data_and_size(handle, &cmdlines, &size) < 0) return -1; cmdlines[size] = 0; parse_cmdlines(pevent, cmdlines, size); free(cmdlines); return 0; } static int read_and_parse_trace_clock(struct tracecmd_input *handle, struct pevent *pevent) { unsigned long long size; char *trace_clock; if (read_data_and_size(handle, &trace_clock, &size) < 0) return -1; trace_clock[size] = 0; 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 pevent *pevent = handle->pevent; int ret; handle->cpus = read4(handle); if (handle->cpus < 0) return -1; pevent_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"); 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 (handle->pevent->file_bigendian) 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 (handle->pevent->old_format) 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_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 }; 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; printf("version = %s\n", version); free(version); if (do_read_check(handle, buf, 1)) goto failed_read; handle->pevent = pevent_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); handle->pevent->file_bigendian = buf[0]; handle->pevent->host_bigendian = tracecmd_host_bigendian(); do_read_check(handle, buf, 1); handle->long_size = buf[0]; handle->page_size = read4(handle); 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; handle = tracecmd_alloc_fd(fd); if (!handle) return NULL; if (tracecmd_read_headers(handle) < 0) goto fail; if (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 (!list_empty(&handle->cpu_data[cpu].pages)) warning("pages still allocated on cpu %d%s", cpu, show_records(&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); pevent_free(handle->pevent); } free(handle); } static long long read_copy_size8(struct tracecmd_input *handle, int fd) { long long size; /* read size */ if (do_read_check(handle, &size, 8)) return -1; if (__do_write_check(fd, &size, 8)) return -1; size = __data2host8(handle->pevent, size); return size; } static int read_copy_size4(struct tracecmd_input *handle, int fd) { int size; /* read size */ if (do_read_check(handle, &size, 4)) return -1; if (__do_write_check(fd, &size, 4)) return -1; size = __data2host4(handle->pevent, size); return size; } 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) { long long size; lseek64(handle->fd, handle->header_files_start, SEEK_SET); /* "header_page" */ if (read_copy_data(handle, 12, fd) < 0) return -1; size = read_copy_size8(handle, fd); if (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; size = read_copy_size8(handle, fd); if (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; int count; int i; count = read_copy_size4(handle, fd); if (count < 0) return -1; for (i = 0; i < count; i++) { size = read_copy_size8(handle, fd); if (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; int systems; int count; int ret; int i,x; systems = read_copy_size4(handle, fd); if (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); count = read_copy_size4(handle, fd); if (count < 0) return -1; for (x=0; x < count; x++) { size = read_copy_size8(handle, fd); if (size < 0) return -1; ret = read_copy_data(handle, size, fd); if (ret < 0) return -1; } } return 0; } static int copy_proc_kallsyms(struct tracecmd_input *handle, int fd) { int size; size = read_copy_size4(handle, fd); if (!size) return 0; /* OK? */ if (size < 0) return -1; if (read_copy_data(handle, size, fd) < 0) return -1; return 0; } static int copy_ftrace_printk(struct tracecmd_input *handle, int fd) { int size; size = read_copy_size4(handle, fd); if (!size) return 0; /* OK? */ if (size < 0) return -1; 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 size; size = read_copy_size8(handle, fd); if (!size) return 0; /* OK? */ if (size < 0) return -1; 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 pevent_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); } 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 pevent *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; } trace-cmd-2.5.3/trace-listen.c000066400000000000000000000371541246701203100161240ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-local.h" #define MAX_OPTION_SIZE 4096 static char *default_output_dir = "."; static char *output_dir; static char *default_output_file = "trace"; static char *output_file; static FILE *logfp; static int debug; static int use_tcp; static int backlog = 5; #define TEMP_FILE_STR "%s.%s:%s.cpu%d", output_file, host, port, cpu static char *get_temp_file(const char *host, const char *port, int cpu) { char *file = NULL; int size; size = snprintf(file, 0, TEMP_FILE_STR); file = malloc_or_die(size + 1); sprintf(file, TEMP_FILE_STR); return file; } static void put_temp_file(char *file) { free(file); } #define MAX_PATH 1024 static void signal_setup(int sig, sighandler_t handle) { struct sigaction action; sigaction(sig, NULL, &action); /* Make accept return EINTR */ action.sa_flags &= ~SA_RESTART; action.sa_handler = handle; sigaction(sig, &action, NULL); } static void delete_temp_file(const char *host, const char *port, int cpu) { char file[MAX_PATH]; snprintf(file, MAX_PATH, TEMP_FILE_STR); unlink(file); } static int read_string(int fd, char *buf, size_t size) { size_t i; int n; for (i = 0; i < size; i++) { n = read(fd, buf+i, 1); if (!buf[i] || n <= 0) break; } return i; } static int process_option(char *option) { /* currently the only option we have is to us TCP */ if (strcmp(option, "TCP") == 0) { use_tcp = 1; return 1; } return 0; } static int done; static void finish(int sig) { done = 1; } #define LOG_BUF_SIZE 1024 static void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp) { static int newline = 1; char buf[LOG_BUF_SIZE]; int r; r = vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); if (r > LOG_BUF_SIZE) r = LOG_BUF_SIZE; if (logfp) { if (newline) fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf); else fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf); newline = buf[r - 1] == '\n'; fflush(logfp); return; } fprintf(fp, "%.*s", r, buf); } static void plog(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __plog("", fmt, ap, stdout); va_end(ap); } static void pdie(const char *fmt, ...) { va_list ap; char *str = ""; va_start(ap, fmt); __plog("Error: ", fmt, ap, stderr); va_end(ap); if (errno) str = strerror(errno); if (logfp) fprintf(logfp, "\n%s\n", str); else fprintf(stderr, "\n%s\n", str); exit(-1); } static void process_udp_child(int sfd, const char *host, const char *port, int cpu, int page_size) { struct sockaddr_storage peer_addr; socklen_t peer_addr_len; char buf[page_size]; char *tempfile; int cfd; int fd; int n; int once = 0; signal_setup(SIGUSR1, finish); tempfile = get_temp_file(host, port, cpu); fd = open(tempfile, O_WRONLY | O_TRUNC | O_CREAT, 0644); if (fd < 0) pdie("creating %s", tempfile); if (use_tcp) { if (listen(sfd, backlog) < 0) pdie("listen"); peer_addr_len = sizeof(peer_addr); cfd = accept(sfd, (struct sockaddr *)&peer_addr, &peer_addr_len); if (cfd < 0 && errno == EINTR) goto done; if (cfd < 0) pdie("accept"); close(sfd); sfd = cfd; } do { /* TODO, make this copyless! */ n = read(sfd, buf, page_size); if (n < 0) { if (errno == EINTR) continue; pdie("reading client"); } if (!n) break; /* UDP requires that we get the full size in one go */ if (!use_tcp && n < page_size && !once) { once = 1; warning("read %d bytes, expected %d", n, page_size); } write(fd, buf, n); } while (!done); done: put_temp_file(tempfile); exit(0); } #define START_PORT_SEARCH 1500 #define MAX_PORT_SEARCH 6000 static int udp_bind_a_port(int start_port, int *sfd) { struct addrinfo hints; struct addrinfo *result, *rp; char buf[BUFSIZ]; int s; int num_port = start_port; again: snprintf(buf, BUFSIZ, "%d", num_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = use_tcp ? SOCK_STREAM : SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; s = getaddrinfo(NULL, buf, &hints, &result); if (s != 0) pdie("getaddrinfo: error opening udp socket"); for (rp = result; rp != NULL; rp = rp->ai_next) { *sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (*sfd < 0) continue; if (bind(*sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; close(*sfd); } if (rp == NULL) { freeaddrinfo(result); if (++num_port > MAX_PORT_SEARCH) pdie("No available ports to bind"); goto again; } freeaddrinfo(result); return num_port; } static void fork_udp_reader(int sfd, const char *node, const char *port, int *pid, int cpu, int pagesize) { *pid = fork(); if (*pid < 0) pdie("creating udp reader"); if (!*pid) process_udp_child(sfd, node, port, cpu, pagesize); close(sfd); } static int open_udp(const char *node, const char *port, int *pid, int cpu, int pagesize, int start_port) { int sfd; int num_port; /* * udp_bind_a_port() currently does not return an error, but if that * changes in the future, we have a check for it now. */ num_port = udp_bind_a_port(start_port, &sfd); if (num_port < 0) return num_port; fork_udp_reader(sfd, node, port, pid, cpu, pagesize); return num_port; } static int communicate_with_client(int fd, int *cpus, int *pagesize) { char buf[BUFSIZ]; char *option; int options; int size; int n, s, t, i; /* Let the client know what we are */ write(fd, "tracecmd", 8); /* read back the CPU count */ n = read_string(fd, buf, BUFSIZ); if (n == BUFSIZ) /** ERROR **/ return -1; *cpus = atoi(buf); plog("cpus=%d\n", *cpus); if (*cpus < 0) return -1; /* next read the page size */ n = read_string(fd, buf, BUFSIZ); if (n == BUFSIZ) /** ERROR **/ return -1; *pagesize = atoi(buf); plog("pagesize=%d\n", *pagesize); if (*pagesize <= 0) return -1; /* Now the number of options */ n = read_string(fd, buf, BUFSIZ); if (n == BUFSIZ) /** ERROR **/ return -1; options = atoi(buf); for (i = 0; i < options; i++) { /* next is the size of the options */ n = read_string(fd, buf, BUFSIZ); if (n == BUFSIZ) /** ERROR **/ return -1; size = atoi(buf); /* prevent a client from killing us */ if (size > MAX_OPTION_SIZE) return -1; option = malloc_or_die(size); do { t = size; s = 0; s = read(fd, option+s, t); if (s <= 0) return -1; t -= s; s = size - t; } while (t); s = process_option(option); free(option); /* do we understand this option? */ if (!s) return -1; } if (use_tcp) plog("Using TCP for live connection\n"); return 0; } static int create_client_file(const char *node, const char *port) { char buf[BUFSIZ]; int ofd; snprintf(buf, BUFSIZ, "%s.%s:%s.dat", output_file, node, port); ofd = open(buf, O_RDWR | O_CREAT | O_TRUNC, 0644); if (ofd < 0) pdie("Can not create file %s", buf); return ofd; } static void destroy_all_readers(int cpus, int *pid_array, const char *node, const char *port) { int cpu; for (cpu = 0; cpu < cpus; cpu++) { if (pid_array[cpu] > 0) { kill(pid_array[cpu], SIGKILL); waitpid(pid_array[cpu], NULL, 0); delete_temp_file(node, port, cpu); pid_array[cpu] = 0; } } } static int *create_all_readers(int cpus, const char *node, const char *port, int pagesize, int fd) { char buf[BUFSIZ]; int *port_array; int *pid_array; int start_port; int udp_port; int cpu; int pid; port_array = malloc_or_die(sizeof(int) * cpus); pid_array = malloc_or_die(sizeof(int) * cpus); memset(pid_array, 0, sizeof(int) * cpus); start_port = START_PORT_SEARCH; /* Now create a UDP port for each CPU */ for (cpu = 0; cpu < cpus; cpu++) { udp_port = open_udp(node, port, &pid, cpu, pagesize, start_port); if (udp_port < 0) goto out_free; port_array[cpu] = udp_port; pid_array[cpu] = pid; /* * Due to some bugging finding ports, * force search after last port */ start_port = udp_port + 1; } /* send the client a comma deliminated set of port numbers */ for (cpu = 0; cpu < cpus; cpu++) { snprintf(buf, BUFSIZ, "%s%d", cpu ? "," : "", port_array[cpu]); write(fd, buf, strlen(buf)); } /* end with null terminator */ write(fd, "\0", 1); return pid_array; out_free: destroy_all_readers(cpus, pid_array, node, port); return NULL; } static void collect_metadata_from_client(int ifd, int ofd) { char buf[BUFSIZ]; int n, s, t; do { n = read(ifd, buf, BUFSIZ); if (n < 0) { if (errno == EINTR) continue; pdie("reading client"); } t = n; s = 0; do { s = write(ofd, buf+s, t); if (s < 0) { if (errno == EINTR) break; pdie("writing to file"); } t -= s; s = n - t; } while (t); } while (n > 0 && !done); } static void stop_all_readers(int cpus, int *pid_array) { int cpu; for (cpu = 0; cpu < cpus; cpu++) { if (pid_array[cpu] > 0) kill(pid_array[cpu], SIGUSR1); } } static void put_together_file(int cpus, int ofd, const char *node, const char *port) { char **temp_files; int cpu; /* Now put together the file */ temp_files = malloc_or_die(sizeof(*temp_files) * cpus); for (cpu = 0; cpu < cpus; cpu++) temp_files[cpu] = get_temp_file(node, port, cpu); tracecmd_attach_cpu_data_fd(ofd, cpus, temp_files); free(temp_files); } static void process_client(const char *node, const char *port, int fd) { int *pid_array; int pagesize; int cpus; int ofd; if (communicate_with_client(fd, &cpus, &pagesize) < 0) return; ofd = create_client_file(node, port); pid_array = create_all_readers(cpus, node, port, pagesize, fd); if (!pid_array) return; /* Now we are ready to start reading data from the client */ collect_metadata_from_client(fd, ofd); /* wait a little to let our readers finish reading */ sleep(1); /* stop our readers */ stop_all_readers(cpus, pid_array); /* wait a little to have the readers clean up */ sleep(1); put_together_file(cpus, ofd, node, port); destroy_all_readers(cpus, pid_array, node, port); } static int do_fork(int cfd) { pid_t pid; /* in debug mode, we do not fork off children */ if (debug) return 0; pid = fork(); if (pid < 0) { warning("failed to create child"); return -1; } if (pid > 0) { close(cfd); return pid; } signal_setup(SIGINT, finish); return 0; } static int do_connection(int cfd, struct sockaddr_storage *peer_addr, socklen_t peer_addr_len) { char host[NI_MAXHOST], service[NI_MAXSERV]; int s; int ret; ret = do_fork(cfd); if (ret) return ret; s = getnameinfo((struct sockaddr *)peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV); if (s == 0) plog("Connected with %s:%s\n", host, service); else { plog("Error with getnameinfo: %s\n", gai_strerror(s)); close(cfd); return -1; } process_client(host, service, cfd); close(cfd); if (!debug) exit(0); return 0; } static int *client_pids; static int saved_pids; static int size_pids; #define PIDS_BLOCK 32 static void add_process(int pid) { if (!client_pids) { size_pids = PIDS_BLOCK; client_pids = malloc_or_die(sizeof(*client_pids) * size_pids); } else if (!(saved_pids % PIDS_BLOCK)) { size_pids += PIDS_BLOCK; client_pids = realloc(client_pids, sizeof(*client_pids) * size_pids); if (!client_pids) pdie("realloc of pids"); } client_pids[saved_pids++] = pid; } static void remove_process(int pid) { int i; for (i = 0; i < saved_pids; i++) { if (client_pids[i] == pid) break; } if (i == saved_pids) return; saved_pids--; if (saved_pids == i) return; memmove(&client_pids[i], &client_pids[i+1], sizeof(*client_pids) * (saved_pids - i)); } static void kill_clients(void) { int status; int i; for (i = 0; i < saved_pids; i++) { kill(client_pids[i], SIGINT); waitpid(client_pids[i], &status, 0); } saved_pids = 0; } static void clean_up(int sig) { int status; int ret; /* Clean up any children that has started before */ do { ret = waitpid(0, &status, WNOHANG); if (ret > 0) remove_process(ret); } while (ret > 0); } static void do_accept_loop(int sfd) { struct sockaddr_storage peer_addr; socklen_t peer_addr_len; int cfd, pid; peer_addr_len = sizeof(peer_addr); do { cfd = accept(sfd, (struct sockaddr *)&peer_addr, &peer_addr_len); printf("connected!\n"); if (cfd < 0 && errno == EINTR) continue; if (cfd < 0) pdie("connecting"); pid = do_connection(cfd, &peer_addr, peer_addr_len); if (pid > 0) add_process(pid); } while (!done); } static void do_listen(char *port) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s; if (!debug) signal_setup(SIGCHLD, clean_up); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; s = getaddrinfo(NULL, port, &hints, &result); if (s != 0) pdie("getaddrinfo: error opening %s", port); for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd < 0) continue; if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; close(sfd); } if (rp == NULL) pdie("Could not bind"); freeaddrinfo(result); if (listen(sfd, backlog) < 0) pdie("listen"); do_accept_loop(sfd); kill_clients(); } static void start_daemon(void) { if (daemon(1, 0) < 0) die("starting daemon"); } enum { OPT_debug = 255, }; void trace_listen(int argc, char **argv) { char *logfile = NULL; char *port = NULL; int daemon = 0; int c; if (argc < 2) usage(argv); if (strcmp(argv[1], "listen") != 0) usage(argv); for (;;) { int option_index = 0; static struct option long_options[] = { {"port", required_argument, NULL, 'p'}, {"help", no_argument, NULL, '?'}, {"debug", no_argument, NULL, OPT_debug}, {NULL, 0, NULL, 0} }; c = getopt_long (argc-1, argv+1, "+hp:o:d:l:D", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'p': port = optarg; break; case 'd': output_dir = optarg; break; case 'o': output_file = optarg; break; case 'l': logfile = optarg; break; case 'D': daemon = 1; break; case OPT_debug: debug = 1; break; default: usage(argv); } } if (!port) usage(argv); if ((argc - optind) >= 2) usage(argv); if (!output_file) output_file = default_output_file; if (!output_dir) output_dir = default_output_dir; if (logfile) { /* set the writes to a logfile instead */ logfp = fopen(logfile, "w"); if (!logfp) die("creating log file %s", logfile); } if (chdir(output_dir) < 0) die("Can't access directory %s", output_dir); if (daemon) start_daemon(); signal_setup(SIGINT, finish); signal_setup(SIGTERM, finish); do_listen(port); return; } trace-cmd-2.5.3/trace-local.h000066400000000000000000000111311246701203100157100ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef __TRACE_LOCAL_H #define __TRACE_LOCAL_H #include #include /* for DIR */ #include "trace-cmd.h" /* fix stupid glib guint64 typecasts and printf formats */ typedef unsigned long long u64; struct buffer_instance; /* for local shared information with trace-cmd executable */ void usage(char **argv); extern int silence_warnings; extern int show_status; struct pid_record_data { int pid; int brass[2]; int cpu; int closed; struct tracecmd_input *stream; struct buffer_instance *instance; struct pevent_record *record; }; void show_file(const char *name); struct tracecmd_input *read_trace_header(const char *file); int read_trace_files(void); void trace_record(int argc, char **argv); void trace_report(int argc, char **argv); void trace_split(int argc, char **argv); void trace_listen(int argc, char **argv); void trace_restore(int argc, char **argv); void trace_stack(int argc, char **argv); void trace_option(int argc, char **argv); void trace_hist(int argc, char **argv); void trace_snapshot(int argc, char **argv); void trace_mem(int argc, char **argv); void trace_stat(int argc, char **argv); struct hook_list; int trace_profile_record(struct tracecmd_input *handle, struct pevent_record *record, int cpu); void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hooks); int trace_profile(void); struct tracecmd_input * trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, int profile, struct hook_list *hooks); int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv, int profile); void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record, int profile); /* --- event interation --- */ /* * Use this to iterate through the event directories */ enum event_process { PROCESSED_NONE, PROCESSED_EVENT, PROCESSED_SYSTEM }; enum process_type { PROCESS_EVENT, PROCESS_SYSTEM }; struct event_iter { DIR *system_dir; DIR *event_dir; struct dirent *system_dent; struct dirent *event_dent; }; enum event_iter_type { EVENT_ITER_NONE, EVENT_ITER_SYSTEM, EVENT_ITER_EVENT }; struct event_iter *trace_event_iter_alloc(const char *path); enum event_iter_type trace_event_iter_next(struct event_iter *iter, const char *path, const char *system); void trace_event_iter_free(struct event_iter *iter); char *append_file(const char *dir, const char *name); char *get_file_content(const char *file); char *strstrip(char *str); /* --- instance manipulation --- */ struct func_list { struct func_list *next; const char *func; }; struct buffer_instance { struct buffer_instance *next; const char *name; const char *cpumask; struct event_list *events; struct event_list **event_next; struct event_list *sched_switch_event; struct event_list *sched_wakeup_event; struct event_list *sched_wakeup_new_event; const char *plugin; struct func_list *filter_funcs; struct func_list *notrace_funcs; const char *clock; struct trace_seq *s; struct tracecmd_input *handle; int tracing_on_init_val; int tracing_on_fd; int keep; int buffer_size; int profile; }; extern struct buffer_instance top_instance; extern struct buffer_instance *buffer_instances; extern struct buffer_instance *first_instance; #define for_each_instance(i) for (i = buffer_instances; i; i = (i)->next) #define for_all_instances(i) for (i = first_instance; i; \ i = i == &top_instance ? buffer_instances : (i)->next) struct buffer_instance *create_instance(char *name); void add_instance(struct buffer_instance *instance); char *get_instance_file(struct buffer_instance *instance, const char *file); void show_instance_file(struct buffer_instance *instance, const char *name); int count_cpus(void); #endif /* __TRACE_LOCAL_H */ trace-cmd-2.5.3/trace-mem.c000066400000000000000000000337711246701203100154050ustar00rootroot00000000000000/* * Copyright (C) 2013 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This code was inspired by Ezequiel Garcia's trace_analyze program: * git://github.com/ezequielgarcia/trace_analyze.git * * Unfortuntately, I hate working with Python, and I also had trouble * getting it to work, as I had an old python on my Fedora 13, and it * was written for the newer version. I decided to do some of it here * in C. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include "trace-local.h" #include "trace-hash-local.h" #include "list.h" static int kmalloc_type; static int kmalloc_node_type; static int kfree_type; static int kmem_cache_alloc_type; static int kmem_cache_alloc_node_type; static int kmem_cache_free_type; struct format_field *common_type_field; struct format_field *kmalloc_callsite_field; struct format_field *kmalloc_bytes_req_field; struct format_field *kmalloc_bytes_alloc_field; struct format_field *kmalloc_ptr_field; struct format_field *kmalloc_node_callsite_field; struct format_field *kmalloc_node_bytes_req_field; struct format_field *kmalloc_node_bytes_alloc_field; struct format_field *kmalloc_node_ptr_field; struct format_field *kfree_ptr_field; struct format_field *kmem_cache_callsite_field; struct format_field *kmem_cache_bytes_req_field; struct format_field *kmem_cache_bytes_alloc_field; struct format_field *kmem_cache_ptr_field; struct format_field *kmem_cache_node_callsite_field; struct format_field *kmem_cache_node_bytes_req_field; struct format_field *kmem_cache_node_bytes_alloc_field; struct format_field *kmem_cache_node_ptr_field; struct format_field *kmem_cache_free_ptr_field; static void *zalloc(size_t size) { return calloc(1, size); } static struct event_format * update_event(struct pevent *pevent, const char *sys, const char *name, int *id) { struct event_format *event; event = pevent_find_event_by_name(pevent, sys, name); if (!event) return NULL; *id = event->id; return event; } static void update_kmalloc(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kmalloc", &kmalloc_type); if (!event) return; kmalloc_callsite_field = pevent_find_field(event, "call_site"); kmalloc_bytes_req_field = pevent_find_field(event, "bytes_req"); kmalloc_bytes_alloc_field = pevent_find_field(event, "bytes_alloc"); kmalloc_ptr_field = pevent_find_field(event, "ptr"); } static void update_kmalloc_node(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kmalloc_node", &kmalloc_node_type); if (!event) return; kmalloc_node_callsite_field = pevent_find_field(event, "call_site"); kmalloc_node_bytes_req_field = pevent_find_field(event, "bytes_req"); kmalloc_node_bytes_alloc_field = pevent_find_field(event, "bytes_alloc"); kmalloc_node_ptr_field = pevent_find_field(event, "ptr"); } static void update_kfree(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kfree", &kfree_type); if (!event) return; kfree_ptr_field = pevent_find_field(event, "ptr"); } static void update_kmem_cache_alloc(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kmem_cache_alloc", &kmem_cache_alloc_type); if (!event) return; kmem_cache_callsite_field = pevent_find_field(event, "call_site"); kmem_cache_bytes_req_field = pevent_find_field(event, "bytes_req"); kmem_cache_bytes_alloc_field = pevent_find_field(event, "bytes_alloc"); kmem_cache_ptr_field = pevent_find_field(event, "ptr"); } static void update_kmem_cache_alloc_node(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kmem_cache_alloc_node", &kmem_cache_alloc_node_type); if (!event) return; kmem_cache_node_callsite_field = pevent_find_field(event, "call_site"); kmem_cache_node_bytes_req_field = pevent_find_field(event, "bytes_req"); kmem_cache_node_bytes_alloc_field = pevent_find_field(event, "bytes_alloc"); kmem_cache_node_ptr_field = pevent_find_field(event, "ptr"); } static void update_kmem_cache_free(struct pevent *pevent) { struct event_format *event; event = update_event(pevent, "kmem", "kmem_cache_free", &kmem_cache_free_type); if (!event) return; kmem_cache_free_ptr_field = pevent_find_field(event, "ptr"); } struct func_descr { struct func_descr *next; const char *func; unsigned long total_alloc; unsigned long total_req; unsigned long current_alloc; unsigned long current_req; unsigned long max_alloc; unsigned long max_req; unsigned long waste; unsigned long max_waste; }; struct ptr_descr { struct ptr_descr *next; struct func_descr *func; unsigned long long ptr; unsigned long alloc; unsigned long req; }; #define HASH_BITS 12 #define HASH_SIZE (1 << HASH_BITS) #define HASH_MASK (HASH_SIZE - 1); static struct func_descr *func_hash[HASH_SIZE]; static struct ptr_descr *ptr_hash[HASH_SIZE]; static struct func_descr **func_list; static unsigned func_count; static int make_key(const void *ptr, int size) { int key = 0; int i; char *kp = (char *)&key; const char *indx = ptr; for (i = 0; i < size; i++) kp[i & 3] ^= indx[i]; return trace_hash(key); } static struct func_descr *find_func(const char *func) { struct func_descr *funcd; int key = make_key(func, strlen(func)) & HASH_MASK; for (funcd = func_hash[key]; funcd; funcd = funcd->next) { /* * As func is always a constant to one pointer, * we can use a direct compare instead of strcmp. */ if (funcd->func == func) return funcd; } return NULL; } static struct func_descr *create_func(const char *func) { struct func_descr *funcd; int key = make_key(func, strlen(func)) & HASH_MASK; funcd = zalloc(sizeof(*funcd)); if (!funcd) die("malloc"); funcd->func = func; funcd->next = func_hash[key]; func_hash[key] = funcd; func_count++; return funcd; } static struct ptr_descr *find_ptr(unsigned long long ptr) { struct ptr_descr *ptrd; int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK; for (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) { if (ptrd->ptr == ptr) return ptrd; } return NULL; } static struct ptr_descr *create_ptr(unsigned long long ptr) { struct ptr_descr *ptrd; int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK; ptrd = zalloc(sizeof(*ptrd)); if (!ptrd) die("malloc"); ptrd->ptr = ptr; ptrd->next = ptr_hash[key]; ptr_hash[key] = ptrd; return ptrd; } static void remove_ptr(unsigned long long ptr) { struct ptr_descr *ptrd, **last; int key = make_key(&ptr, sizeof(ptr)) & HASH_MASK; last = &ptr_hash[key]; for (ptrd = ptr_hash[key]; ptrd; ptrd = ptrd->next) { if (ptrd->ptr == ptr) break; last = &ptrd->next; } if (!ptrd) return; *last = ptrd->next; free(ptrd); } static void add_kmalloc(const char *func, unsigned long long ptr, unsigned int req, int alloc) { struct func_descr *funcd; struct ptr_descr *ptrd; funcd = find_func(func); if (!funcd) funcd = create_func(func); funcd->total_alloc += alloc; funcd->total_req += req; funcd->current_alloc += alloc; funcd->current_req += req; if (funcd->current_alloc > funcd->max_alloc) funcd->max_alloc = funcd->current_alloc; if (funcd->current_req > funcd->max_req) funcd->max_req = funcd->current_req; ptrd = find_ptr(ptr); if (!ptrd) ptrd = create_ptr(ptr); ptrd->alloc = alloc; ptrd->req = req; ptrd->func = funcd; } static void remove_kmalloc(unsigned long long ptr) { struct func_descr *funcd; struct ptr_descr *ptrd; ptrd = find_ptr(ptr); if (!ptrd) return; funcd = ptrd->func; funcd->current_alloc -= ptrd->alloc; funcd->current_req -= ptrd->req; remove_ptr(ptr); } static void process_kmalloc(struct pevent *pevent, struct pevent_record *record, struct format_field *callsite_field, struct format_field *bytes_req_field, struct format_field *bytes_alloc_field, struct format_field *ptr_field) { unsigned long long callsite; unsigned long long val; unsigned long long ptr; unsigned int req; int alloc; const char *func; pevent_read_number_field(callsite_field, record->data, &callsite); pevent_read_number_field(bytes_req_field, record->data, &val); req = val; pevent_read_number_field(bytes_alloc_field, record->data, &val); alloc = val; pevent_read_number_field(ptr_field, record->data, &ptr); func = pevent_find_function(pevent, callsite); add_kmalloc(func, ptr, req, alloc); } static void process_kfree(struct pevent *pevent, struct pevent_record *record, struct format_field *ptr_field) { unsigned long long ptr; pevent_read_number_field(ptr_field, record->data, &ptr); remove_kmalloc(ptr); } static void process_record(struct pevent *pevent, struct pevent_record *record) { unsigned long long val; int type; pevent_read_number_field(common_type_field, record->data, &val); type = val; if (type == kmalloc_type) return process_kmalloc(pevent, record, kmalloc_callsite_field, kmalloc_bytes_req_field, kmalloc_bytes_alloc_field, kmalloc_ptr_field); if (type == kmalloc_node_type) return process_kmalloc(pevent, record, kmalloc_node_callsite_field, kmalloc_node_bytes_req_field, kmalloc_node_bytes_alloc_field, kmalloc_node_ptr_field); if (type == kfree_type) return process_kfree(pevent, record, kfree_ptr_field); if (type == kmem_cache_alloc_type) return process_kmalloc(pevent, record, kmem_cache_callsite_field, kmem_cache_bytes_req_field, kmem_cache_bytes_alloc_field, kmem_cache_ptr_field); if (type == kmem_cache_alloc_node_type) return process_kmalloc(pevent, record, kmem_cache_node_callsite_field, kmem_cache_node_bytes_req_field, kmem_cache_node_bytes_alloc_field, kmem_cache_node_ptr_field); if (type == kmem_cache_free_type) return process_kfree(pevent, record, kmem_cache_free_ptr_field); } static int func_cmp(const void *a, const void *b) { const struct func_descr *fa = *(const struct func_descr **)a; const struct func_descr *fb = *(const struct func_descr **)b; if (fa->waste > fb->waste) return -1; if (fa->waste < fb->waste) return 1; return 0; } static void sort_list(void) { struct func_descr *funcd; int h; int i = 0; func_list = zalloc(sizeof(*func_list) * func_count); for (h = 0; h < HASH_SIZE; h++) { for (funcd = func_hash[h]; funcd; funcd = funcd->next) { funcd->waste = funcd->current_alloc - funcd->current_req; funcd->max_waste = funcd->max_alloc - funcd->max_req; if (i == func_count) die("more funcs than expected\n"); func_list[i++] = funcd; } } qsort(func_list, func_count, sizeof(*func_list), func_cmp); } static void print_list(void) { struct func_descr *funcd; int i; printf(" Function \t"); printf("Waste\tAlloc\treq\t\tTotAlloc TotReq\t\tMaxAlloc MaxReq\t"); printf("MaxWaste\n"); printf(" -------- \t"); printf("-----\t-----\t---\t\t-------- ------\t\t-------- ------\t"); printf("--------\n"); for (i = 0; i < func_count; i++) { funcd = func_list[i]; printf("%32s\t%ld\t%ld\t%ld\t\t%8ld %8ld\t\t%8ld %8ld\t%ld\n", funcd->func, funcd->waste, funcd->current_alloc, funcd->current_req, funcd->total_alloc, funcd->total_req, funcd->max_alloc, funcd->max_req, funcd->max_waste); } } static void do_trace_mem(struct tracecmd_input *handle) { struct pevent *pevent = tracecmd_get_pevent(handle); struct event_format *event; struct pevent_record *record; int missed_events = 0; int cpus; int cpu; int ret; ret = tracecmd_init_data(handle); if (ret < 0) die("failed to init data"); if (ret > 0) die("trace-cmd mem does not work with latency traces\n"); cpus = tracecmd_cpus(handle); /* Need to get any event */ for (cpu = 0; cpu < cpus; cpu++) { record = tracecmd_peek_data(handle, cpu); if (record) break; } if (!record) die("No records found in file"); ret = pevent_data_type(pevent, record); event = pevent_data_event_from_type(pevent, ret); common_type_field = pevent_find_common_field(event, "common_type"); if (!common_type_field) die("Can't find a 'type' field?"); update_kmalloc(pevent); update_kmalloc_node(pevent); update_kfree(pevent); update_kmem_cache_alloc(pevent); update_kmem_cache_alloc_node(pevent); update_kmem_cache_free(pevent); while ((record = tracecmd_read_next_data(handle, &cpu))) { /* record missed event */ if (!missed_events && record->missed_events) missed_events = 1; process_record(pevent, record); free_record(record); } sort_list(); print_list(); } void trace_mem(int argc, char **argv) { struct tracecmd_input *handle; const char *input_file = NULL; int ret; for (;;) { int c; c = getopt(argc-1, argv+1, "+hi:"); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'i': if (input_file) die("Only one input for mem"); input_file = optarg; break; default: usage(argv); } } if ((argc - optind) >= 2) { if (input_file) usage(argv); input_file = argv[optind + 1]; } if (!input_file) input_file = "trace.dat"; handle = tracecmd_alloc(input_file); if (!handle) die("can't open %s\n", input_file); ret = tracecmd_read_headers(handle); if (ret) return; do_trace_mem(handle); tracecmd_close(handle); } trace-cmd-2.5.3/trace-output.c000066400000000000000000000701451246701203100161630ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd-local.h" #include "list.h" #include "version.h" /* We can't depend on the host size for size_t, all must be 64 bit */ typedef unsigned long long tsize_t; typedef long long stsize_t; static struct tracecmd_event_list all_event_list = { .next = NULL, .glob = "all" }; struct tracecmd_option { unsigned short id; int size; void *data; tsize_t offset; struct list_head list; }; struct tracecmd_output { int fd; int page_size; int cpus; struct pevent *pevent; char *tracing_dir; int options_written; int nr_options; struct list_head options; }; struct list_event { struct list_event *next; char *name; char *file; }; struct list_event_system { struct list_event_system *next; struct list_event *events; char *name; }; static stsize_t do_write_check(struct tracecmd_output *handle, const void *data, tsize_t size) { return __do_write_check(handle->fd, data, size); } static short convert_endian_2(struct tracecmd_output *handle, short val) { if (!handle->pevent) return val; return __data2host2(handle->pevent, val); } static int convert_endian_4(struct tracecmd_output *handle, int val) { if (!handle->pevent) return val; return __data2host4(handle->pevent, val); } static unsigned long long convert_endian_8(struct tracecmd_output *handle, unsigned long long val) { if (!handle->pevent) return val; return __data2host8(handle->pevent, val); } void tracecmd_output_free(struct tracecmd_output *handle) { struct tracecmd_option *option; if (!handle) return; if (handle->tracing_dir) free(handle->tracing_dir); if (handle->pevent) pevent_unref(handle->pevent); while (!list_empty(&handle->options)) { option = container_of(handle->options.next, struct tracecmd_option, list); list_del(&option->list); free(option->data); free(option); } free(handle); } void tracecmd_output_close(struct tracecmd_output *handle) { if (!handle) return; if (handle->fd >= 0) { close(handle->fd); handle->fd = -1; } tracecmd_output_free(handle); } static unsigned long get_size_fd(int fd) { unsigned long long size = 0; char buf[BUFSIZ]; int r; do { r = read(fd, buf, BUFSIZ); if (r > 0) size += r; } while (r > 0); lseek(fd, 0, SEEK_SET); return size; } static unsigned long get_size(const char *file) { unsigned long long size = 0; int fd; fd = open(file, O_RDONLY); if (fd < 0) die("Can't read '%s'", file); size = get_size_fd(fd); close(fd); return size; } static tsize_t copy_file_fd(struct tracecmd_output *handle, int fd) { tsize_t size = 0; char buf[BUFSIZ]; stsize_t r; do { r = read(fd, buf, BUFSIZ); if (r > 0) { size += r; if (do_write_check(handle, buf, r)) return 0; } } while (r > 0); return size; } static tsize_t copy_file(struct tracecmd_output *handle, const char *file) { tsize_t size = 0; int fd; fd = open(file, O_RDONLY); if (fd < 0) die("Can't read '%s'", file); size = copy_file_fd(handle, fd); close(fd); return size; } /* * Finds the path to the debugfs/tracing * Allocates the string and stores it. */ static const char *find_tracing_dir(struct tracecmd_output *handle) { if (!handle->tracing_dir) handle->tracing_dir = tracecmd_find_tracing_dir(); return handle->tracing_dir; } static char *get_tracing_file(struct tracecmd_output *handle, const char *name) { const char *tracing; char *file; tracing = find_tracing_dir(handle); if (!tracing) return NULL; file = malloc_or_die(strlen(tracing) + strlen(name) + 2); if (!file) return NULL; sprintf(file, "%s/%s", tracing, name); return file; } static void put_tracing_file(char *file) { free(file); } int tracecmd_ftrace_enable(int set) { struct stat buf; char *path = "/proc/sys/kernel/ftrace_enabled"; int fd; char *val = set ? "1" : "0"; int ret = 0; /* if ftace_enable does not exist, simply ignore it */ fd = stat(path, &buf); if (fd < 0) return ENODEV; fd = open(path, O_WRONLY); if (fd < 0) die ("Can't %s ftrace", set ? "enable" : "disable"); if (write(fd, val, 1) < 0) ret = -1; close(fd); return ret; } static int read_header_files(struct tracecmd_output *handle) { tsize_t size, check_size, endian8; struct stat st; char *path; int fd; int ret; path = get_tracing_file(handle, "events/header_page"); if (!path) return -1; ret = stat(path, &st); if (ret < 0) { /* old style did not show this info, just add zero */ put_tracing_file(path); if (do_write_check(handle, "header_page", 12)) return -1; size = 0; if (do_write_check(handle, &size, 8)) return -1; if (do_write_check(handle, "header_event", 13)) return -1; if (do_write_check(handle, &size, 8)) return -1; return 0; } fd = open(path, O_RDONLY); if (fd < 0) { warning("can't read '%s'", path); return -1; } /* unfortunately, you can not stat debugfs files for size */ size = get_size_fd(fd); if (do_write_check(handle, "header_page", 12)) goto out_close; endian8 = convert_endian_8(handle, size); if (do_write_check(handle, &endian8, 8)) goto out_close; check_size = copy_file_fd(handle, fd); close(fd); if (size != check_size) { warning("wrong size for '%s' size=%lld read=%lld", path, size, check_size); errno = EINVAL; return -1; } put_tracing_file(path); path = get_tracing_file(handle, "events/header_event"); if (!path) return -1; fd = open(path, O_RDONLY); if (fd < 0) die("can't read '%s'", path); size = get_size_fd(fd); if (do_write_check(handle, "header_event", 13)) goto out_close; endian8 = convert_endian_8(handle, size); if (do_write_check(handle, &endian8, 8)) goto out_close; check_size = copy_file_fd(handle, fd); close(fd); if (size != check_size) { warning("wrong size for '%s'", path); return -1; } put_tracing_file(path); return 0; out_close: close(fd); return -1; } static int copy_event_system(struct tracecmd_output *handle, struct list_event_system *slist) { struct list_event *elist; unsigned long long size, check_size, endian8; struct stat st; char *format; int endian4; int count = 0; int ret; for (elist = slist->events; elist; elist = elist->next) count++; endian4 = convert_endian_4(handle, count); if (do_write_check(handle, &endian4, 4)) return -1; for (elist = slist->events; elist; elist = elist->next) { format = elist->file; ret = stat(format, &st); if (ret >= 0) { /* unfortunately, you can not stat debugfs files for size */ size = get_size(format); endian8 = convert_endian_8(handle, size); if (do_write_check(handle, &endian8, 8)) return -1; check_size = copy_file(handle, format); if (size != check_size) { warning("error in size of file '%s'", format); return -1; } } } return 0; } static void add_list_event_system(struct list_event_system **systems, const char *system, const char *event, const char *path) { struct list_event_system *slist; struct list_event *elist; for (slist = *systems; slist; slist = slist->next) if (strcmp(slist->name, system) == 0) break; if (!slist) { slist = malloc_or_die(sizeof(*slist)); slist->name = strdup(system); slist->next = *systems; slist->events = NULL; *systems = slist; } for (elist = slist->events; elist; elist = elist->next) if (strcmp(elist->name, event) == 0) break; if (!elist) { elist = malloc_or_die(sizeof(*elist)); elist->name = strdup(event); elist->file = strdup(path); elist->next = slist->events; slist->events = elist; } } static void free_list_events(struct list_event_system *list) { struct list_event_system *slist; struct list_event *elist; while (list) { slist = list; list = list->next; while (slist->events) { elist = slist->events; slist->events = elist->next; free(elist->name); free(elist->file); free(elist); } free(slist->name); free(slist); } } static void glob_events(struct tracecmd_output *handle, struct list_event_system **systems, const char *str) { glob_t globbuf; char *events_path; char *system; char *event; char *path; char *file; char *ptr; int do_ftrace = 0; int events_len; int ret; int i; if (strncmp(str, "ftrace/", 7) == 0) do_ftrace = 1; events_path = get_tracing_file(handle, "events"); events_len = strlen(events_path); path = malloc_or_die(events_len + strlen(str) + strlen("/format") + 2); path[0] = '\0'; strcat(path, events_path); strcat(path, "/"); strcat(path, str); strcat(path, "/format"); put_tracing_file(events_path); globbuf.gl_offs = 0; ret = glob(path, 0, NULL, &globbuf); free(path); if (ret < 0) return; for (i = 0; i < globbuf.gl_pathc; i++) { file = globbuf.gl_pathv[i]; system = strdup(file + events_len + 1); system = strtok_r(system, "/", &ptr); if (!ptr) { /* ?? should we warn? */ free(system); continue; } if (!do_ftrace && strcmp(system, "ftrace") == 0) { free(system); continue; } event = strtok_r(NULL, "/", &ptr); if (!ptr) { /* ?? should we warn? */ free(system); continue; } add_list_event_system(systems, system, event, file); free(system); } globfree(&globbuf); } static void create_event_list_item(struct tracecmd_output *handle, struct list_event_system **systems, struct tracecmd_event_list *list) { char *ptr; char *str; str = strdup(list->glob); if (!str) die("strdup - no memory"); /* system and event names are separated by a ':' */ ptr = strchr(str, ':'); if (ptr) *ptr = '/'; else /* system and event may also be separated by a '/' */ ptr = strchr(str, '/'); if (ptr) { glob_events(handle, systems, str); free(str); return; } ptr = str; str = malloc_or_die(strlen(ptr) + 3); str[0] = '\0'; strcat(str, ptr); strcat(str, "/*"); glob_events(handle, systems, str); str[0] = '\0'; strcat(str, "*/"); strcat(str, ptr); glob_events(handle, systems, str); free(ptr); free(str); } static int read_ftrace_files(struct tracecmd_output *handle) { struct list_event_system *systems = NULL; struct tracecmd_event_list list = { .glob = "ftrace/*" }; int ret; create_event_list_item(handle, &systems, &list); ret = copy_event_system(handle, systems); free_list_events(systems); return ret; } static struct list_event_system * create_event_list(struct tracecmd_output *handle, struct tracecmd_event_list *event_list) { struct list_event_system *systems = NULL; struct tracecmd_event_list *list; for (list = event_list; list; list = list->next) create_event_list_item(handle, &systems, list); return systems; } static int read_event_files(struct tracecmd_output *handle, struct tracecmd_event_list *event_list) { struct list_event_system *systems; struct list_event_system *slist; struct tracecmd_event_list *list; struct tracecmd_event_list all_events = { .glob = "*/*" }; int count = 0; int endian4; int ret; /* * If any of the list is the special keyword "all" then * just do all files. */ for (list = event_list; list; list = list->next) { if (strcmp(list->glob, "all") == 0) break; } /* all events are listed, use a global glob */ if (list) event_list = &all_events; systems = create_event_list(handle, event_list); for (slist = systems; slist; slist = slist->next) count++; ret = -1; endian4 = convert_endian_4(handle, count); if (do_write_check(handle, &endian4, 4)) goto out_free; ret = 0; for (slist = systems; !ret && slist; slist = slist->next) { if (do_write_check(handle, slist->name, strlen(slist->name) + 1)) { ret = -1; continue; } ret = copy_event_system(handle, slist); } out_free: free_list_events(systems); return ret; } static int read_proc_kallsyms(struct tracecmd_output *handle, const char *kallsyms) { unsigned int size, check_size, endian4; const char *path = "/proc/kallsyms"; struct stat st; int ret; if (kallsyms) path = kallsyms; ret = stat(path, &st); if (ret < 0) { /* not found */ size = 0; endian4 = convert_endian_4(handle, size); if (do_write_check(handle, &endian4, 4)) return -1; return 0; } size = get_size(path); endian4 = convert_endian_4(handle, size); if (do_write_check(handle, &endian4, 4)) return -1; check_size = copy_file(handle, path); if (size != check_size) { errno = EINVAL; warning("error in size of file '%s'", path); return -1; } return 0; } static int read_ftrace_printk(struct tracecmd_output *handle) { unsigned int size, check_size, endian4; struct stat st; char *path; int ret; path = get_tracing_file(handle, "printk_formats"); if (!path) return -1; ret = stat(path, &st); if (ret < 0) { /* not found */ size = 0; endian4 = convert_endian_4(handle, size); if (do_write_check(handle, &endian4, 4)) goto fail; goto out; } size = get_size(path); endian4 = convert_endian_4(handle, size); if (do_write_check(handle, &endian4, 4)) goto fail; check_size = copy_file(handle, path); if (size != check_size) { errno = EINVAL; warning("error in size of file '%s'", path); goto fail; } out: put_tracing_file(path); return 0; fail: put_tracing_file(path); return -1; } static int save_tracing_file_data(struct tracecmd_output *handle, const char *filename) { unsigned long long endian8; char *file = NULL; struct stat st; off64_t check_size; off64_t size; int ret = -1; file = get_tracing_file(handle, filename); if (!file) return -1; ret = stat(file, &st); if (ret >= 0) { size = get_size(file); endian8 = convert_endian_8(handle, size); if (do_write_check(handle, &endian8, 8)) goto out_free; check_size = copy_file(handle, file); if (size != check_size) { errno = EINVAL; warning("error in size of file '%s'", file); goto out_free; } } else { size = 0; endian8 = convert_endian_8(handle, size); if (do_write_check(handle, &endian8, 8)) goto out_free; } ret = 0; out_free: put_tracing_file(file); return ret; } static struct tracecmd_output * create_file_fd(int fd, struct tracecmd_input *ihandle, const char *tracing_dir, const char *kallsyms, struct tracecmd_event_list *list) { struct tracecmd_output *handle; struct pevent *pevent; char buf[BUFSIZ]; int endian4; handle = malloc(sizeof(*handle)); if (!handle) return NULL; memset(handle, 0, sizeof(*handle)); handle->fd = fd; if (tracing_dir) { handle->tracing_dir = strdup(tracing_dir); if (!handle->tracing_dir) goto out_free; } list_head_init(&handle->options); buf[0] = 23; buf[1] = 8; buf[2] = 68; memcpy(buf + 3, "tracing", 7); if (do_write_check(handle, buf, 10)) goto out_free; if (do_write_check(handle, FILE_VERSION_STRING, strlen(FILE_VERSION_STRING) + 1)) goto out_free; /* get endian and page size */ if (ihandle) { pevent = tracecmd_get_pevent(ihandle); /* Use the pevent of the ihandle for later writes */ handle->pevent = tracecmd_get_pevent(ihandle); pevent_ref(pevent); if (pevent->file_bigendian) buf[0] = 1; else buf[0] = 0; handle->page_size = tracecmd_page_size(ihandle); } else { if (tracecmd_host_bigendian()) buf[0] = 1; else buf[0] = 0; handle->page_size = getpagesize(); } if (do_write_check(handle, buf, 1)) goto out_free; /* save size of long (this may not be what the kernel is) */ buf[0] = sizeof(long); if (do_write_check(handle, buf, 1)) goto out_free; endian4 = convert_endian_4(handle, handle->page_size); if (do_write_check(handle, &endian4, 4)) goto out_free; if (ihandle) return handle; if (read_header_files(handle)) goto out_free; if (read_ftrace_files(handle)) goto out_free; if (read_event_files(handle, list)) goto out_free; if (read_proc_kallsyms(handle, kallsyms)) goto out_free; if (read_ftrace_printk(handle)) goto out_free; /* * Save the command lines; */ if (save_tracing_file_data(handle, "saved_cmdlines") < 0) goto out_free; return handle; out_free: tracecmd_output_close(handle); return NULL; } static struct tracecmd_output *create_file(const char *output_file, struct tracecmd_input *ihandle, const char *tracing_dir, const char *kallsyms, struct tracecmd_event_list *list) { struct tracecmd_output *handle; int fd; fd = open(output_file, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); if (fd < 0) return NULL; handle = create_file_fd(fd, ihandle, tracing_dir, kallsyms, list); if (!handle) { close(fd); unlink(output_file); } return handle; } /** * tracecmd_add_option - add options to the file * @handle: the output file handle name * @id: the id of the option * @size: the size of the option data * @data: the data to write to the file. * * Returns handle to update option if needed. * Just the content can be updated, with smaller or equal to * content than the specified size. */ struct tracecmd_option * tracecmd_add_option(struct tracecmd_output *handle, unsigned short id, int size, const void *data) { struct tracecmd_option *option; /* * We can only add options before they were written. * This may change in the future. */ if (handle->options_written) return NULL; handle->nr_options++; option = malloc(sizeof(*option)); if (!option) die("Could not allocate space for option"); option->id = id; option->size = size; option->data = malloc_or_die(size); memcpy(option->data, data, size); list_add_tail(&option->list, &handle->options); return option; } static int add_options(struct tracecmd_output *handle) { struct tracecmd_option *options; unsigned short option; unsigned short endian2; unsigned int endian4; /* If already written, ignore */ if (handle->options_written) return 0; if (do_write_check(handle, "options ", 10)) return -1; list_for_each_entry(options, &handle->options, list) { endian2 = convert_endian_2(handle, options->id); if (do_write_check(handle, &endian2, 2)) return -1; endian4 = convert_endian_4(handle, options->size); if (do_write_check(handle, &endian4, 4)) return -1; /* Save the data location in case it needs to be updated */ options->offset = lseek64(handle->fd, 0, SEEK_CUR); if (do_write_check(handle, options->data, options->size)) return -1; } option = TRACECMD_OPTION_DONE; if (do_write_check(handle, &option, 2)) return -1; handle->options_written = 1; return 0; } int tracecmd_update_option(struct tracecmd_output *handle, struct tracecmd_option *option, int size, const void *data) { tsize_t offset; stsize_t ret; if (size > option->size) { warning("Can't update option with more data than allocated"); return -1; } if (!handle->options_written) { /* Hasn't been written yet. Just update current pointer */ option->size = size; memcpy(option->data, data, size); return 0; } /* Save current offset */ offset = lseek64(handle->fd, 0, SEEK_CUR); ret = lseek64(handle->fd, option->offset, SEEK_SET); if (ret == (off64_t)-1) { warning("could not seek to %lld\n", option->offset); return -1; } if (do_write_check(handle, data, size)) return -1; ret = lseek64(handle->fd, offset, SEEK_SET); if (ret == (off64_t)-1) { warning("could not seek to %lld\n", offset); return -1; } return 0; } struct tracecmd_option * tracecmd_add_buffer_option(struct tracecmd_output *handle, const char *name) { struct tracecmd_option *option; char *buf; int size = 8 + strlen(name) + 1; buf = malloc(size); if (!buf) { warning("Failed to malloc buffer"); return NULL; } *(tsize_t *)buf = 0; strcpy(buf + 8, name); option = tracecmd_add_option(handle, TRACECMD_OPTION_BUFFER, size, buf); free(buf); return option; } struct tracecmd_output *tracecmd_create_file_latency(const char *output_file, int cpus) { struct tracecmd_output *handle; char *path; handle = create_file(output_file, NULL, NULL, NULL, &all_event_list); if (!handle) return NULL; cpus = convert_endian_4(handle, cpus); if (do_write_check(handle, &cpus, 4)) goto out_free; if (add_options(handle) < 0) goto out_free; if (do_write_check(handle, "latency ", 10)) goto out_free; path = get_tracing_file(handle, "trace"); if (!path) goto out_free; copy_file(handle, path); put_tracing_file(path); return handle; out_free: tracecmd_output_close(handle); return NULL; } static int __tracecmd_append_cpu_data(struct tracecmd_output *handle, int cpus, char * const *cpu_data_files) { off64_t *offsets = NULL; unsigned long long *sizes = NULL; off64_t offset; unsigned long long endian8; off64_t check_size; char *file; struct stat st; int ret; int i; if (do_write_check(handle, "flyrecord", 10)) goto out_free; offsets = malloc_or_die(sizeof(*offsets) * cpus); if (!offsets) goto out_free; sizes = malloc_or_die(sizeof(*sizes) * cpus); if (!sizes) goto out_free; offset = lseek64(handle->fd, 0, SEEK_CUR); /* hold any extra data for data */ offset += cpus * (16); /* * Unfortunately, the trace_clock data was placed after the * cpu data, and wasn't accounted for with the offsets. * We need to save room for the trace_clock file. This means * we need to find the size of it before we define the final * offsets. */ file = get_tracing_file(handle, "trace_clock"); if (!file) goto out_free; /* Save room for storing the size */ offset += 8; ret = stat(file, &st); if (ret >= 0) offset += get_size(file); put_tracing_file(file); /* Page align offset */ offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1); for (i = 0; i < cpus; i++) { file = cpu_data_files[i]; ret = stat(file, &st); if (ret < 0) { warning("can not stat '%s'", file); goto out_free; } offsets[i] = offset; sizes[i] = st.st_size; offset += st.st_size; offset = (offset + (handle->page_size - 1)) & ~(handle->page_size - 1); endian8 = convert_endian_8(handle, offsets[i]); if (do_write_check(handle, &endian8, 8)) goto out_free; endian8 = convert_endian_8(handle, sizes[i]); if (do_write_check(handle, &endian8, 8)) goto out_free; } if (save_tracing_file_data(handle, "trace_clock") < 0) goto out_free; for (i = 0; i < cpus; i++) { fprintf(stderr, "CPU%d data recorded at offset=0x%llx\n", i, (unsigned long long) offsets[i]); offset = lseek64(handle->fd, offsets[i], SEEK_SET); if (offset == (off64_t)-1) { warning("could not seek to %lld\n", offsets[i]); goto out_free; } check_size = copy_file(handle, cpu_data_files[i]); if (check_size != sizes[i]) { errno = EINVAL; warning("did not match size of %lld to %lld", check_size, sizes[i]); goto out_free; } fprintf(stderr, " %llu bytes in size\n", (unsigned long long)check_size); } free(offsets); free(sizes); return 0; out_free: free(offsets); free(sizes); return -1; } int tracecmd_append_cpu_data(struct tracecmd_output *handle, int cpus, char * const *cpu_data_files) { int endian4; endian4 = convert_endian_4(handle, cpus); if (do_write_check(handle, &endian4, 4)) return -1; if (add_options(handle) < 0) return -1; return __tracecmd_append_cpu_data(handle, cpus, cpu_data_files); } int tracecmd_append_buffer_cpu_data(struct tracecmd_output *handle, struct tracecmd_option *option, int cpus, char * const *cpu_data_files) { tsize_t offset; stsize_t ret; offset = lseek64(handle->fd, 0, SEEK_CUR); /* Go to the option data, where will write the offest */ ret = lseek64(handle->fd, option->offset, SEEK_SET); if (ret == (off64_t)-1) { warning("could not seek to %lld\n", option->offset); return -1; } if (do_write_check(handle, &offset, 8)) return -1; /* Go back to end of file */ ret = lseek64(handle->fd, offset, SEEK_SET); if (ret == (off64_t)-1) { warning("could not seek to %lld\n", offset); return -1; } return __tracecmd_append_cpu_data(handle, cpus, cpu_data_files); } int tracecmd_attach_cpu_data_fd(int fd, int cpus, char * const *cpu_data_files) { struct tracecmd_input *ihandle; struct tracecmd_output *handle; struct pevent *pevent; int ret = -1; /* Move the file descriptor to the beginning */ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) return -1; /* get a input handle from this */ ihandle = tracecmd_alloc_fd(fd); if (!ihandle) return -1; /* move the file descriptor to the end */ if (lseek(fd, 0, SEEK_END) == (off_t)-1) goto out_free; /* create a partial output handle */ handle = malloc(sizeof(*handle)); if (!handle) goto out_free; memset(handle, 0, sizeof(*handle)); handle->fd = fd; /* get endian and page size */ pevent = tracecmd_get_pevent(ihandle); /* Use the pevent of the ihandle for later writes */ handle->pevent = tracecmd_get_pevent(ihandle); pevent_ref(pevent); handle->page_size = tracecmd_page_size(ihandle); list_head_init(&handle->options); if (tracecmd_append_cpu_data(handle, cpus, cpu_data_files) >= 0) ret = 0; tracecmd_output_close(handle); out_free: tracecmd_close(ihandle); return ret; } int tracecmd_attach_cpu_data(char *file, int cpus, char * const *cpu_data_files) { int fd; fd = open(file, O_RDWR); if (fd < 0) return -1; return tracecmd_attach_cpu_data_fd(fd, cpus, 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 *list) { struct tracecmd_output *handle; handle = create_file(output_file, NULL, NULL, NULL, list); if (!handle) return NULL; if (tracecmd_append_cpu_data(handle, cpus, cpu_data_files) < 0) { tracecmd_output_close(handle); return NULL; } return handle; } struct tracecmd_output *tracecmd_create_file(const char *output_file, int cpus, char * const *cpu_data_files) { return tracecmd_create_file_glob(output_file, cpus, cpu_data_files, &all_event_list); } struct tracecmd_output *tracecmd_create_init_fd(int fd) { return create_file_fd(fd, NULL, NULL, NULL, &all_event_list); } struct tracecmd_output * tracecmd_create_init_fd_glob(int fd, struct tracecmd_event_list *list) { return create_file_fd(fd, NULL, NULL, NULL, list); } struct tracecmd_output * tracecmd_create_init_file_glob(const char *output_file, struct tracecmd_event_list *list) { return create_file(output_file, NULL, NULL, NULL, list); } struct tracecmd_output *tracecmd_create_init_file(const char *output_file) { return create_file(output_file, NULL, NULL, NULL, &all_event_list); } struct tracecmd_output *tracecmd_create_init_file_override(const char *output_file, const char *tracing_dir, const char *kallsyms) { return create_file(output_file, NULL, tracing_dir, kallsyms, &all_event_list); } /** * tracecmd_copy - copy the headers of one trace.dat file for another * @ihandle: input handle of the trace.dat file to copy * @file: the trace.dat file to create * * Reads the header information and creates a new trace data file * with the same characteristics (events and all) and returns * tracecmd_output handle to this new file. */ struct tracecmd_output *tracecmd_copy(struct tracecmd_input *ihandle, const char *file) { struct tracecmd_output *handle; handle = create_file(file, ihandle, NULL, NULL, &all_event_list); if (!handle) return NULL; if (tracecmd_copy_headers(ihandle, handle->fd) < 0) goto out_free; /* The file is all ready to have cpu data attached */ return handle; out_free: tracecmd_output_close(handle); return NULL; } trace-cmd-2.5.3/trace-plot-cpu.c000066400000000000000000000340511246701203100163620ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include "trace-graph.h" #include "cpu.h" struct cpu_plot_info { int cpu; unsigned long long last_time; int last_pid; struct pevent_record *last_record; }; static gint hash_pid(gint val) { /* idle always gets black */ if (!val) return 0; return trace_hash(val); } static void convert_nano(unsigned long long time, unsigned long *sec, unsigned long *usec) { *sec = time / 1000000000ULL; *usec = (time / 1000) % 1000000; } static struct pevent_record *get_record_from_time(struct graph_info *ginfo, int cpu, unsigned long long time) { struct pevent_record *record; tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); record = tracecmd_read_data(ginfo->handle, cpu); while (record && record->ts < time) { free_record(record); record = tracecmd_read_data(ginfo->handle, cpu); } return record; } static int cpu_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct cpu_plot_info *cpu_info = plot->private; struct pevent_record *record; int ret = 0; record = get_record_from_time(ginfo, cpu_info->cpu, time); if (record && record->ts == time) ret = 1; free_record(record); return ret; } /* * Return 1 if we should skip record, otherwise 0 * * @orig_pid gets the pid of the record * @sched_pid gets the pid of the record or if the record is * a sched_switch, it gets the next task * If it is a wakeup, then sched_pid gets the task being woken * @is_sched_switch returns 1 on context switch, otherwise 0 */ static int filter_record(struct graph_info *ginfo, struct pevent_record *record, int *orig_pid, int *sched_pid, gboolean *sched_switch) { gboolean is_sched_switch = FALSE; gboolean is_wakeup = FALSE; const char *comm; int wake_pid; int filter; *orig_pid = pevent_data_pid(ginfo->pevent, record); filter = trace_graph_filter_on_task(ginfo, *orig_pid); if (trace_graph_check_sched_switch(ginfo, record, sched_pid, &comm)) { is_sched_switch = TRUE; /* Also show the task switching out */ if (filter) filter = trace_graph_filter_on_task(ginfo, *sched_pid); } else *sched_pid = *orig_pid; if (filter) { /* Lets see if a filtered task is waking up */ is_wakeup = trace_graph_check_sched_wakeup(ginfo, record, &wake_pid); if (is_wakeup) { filter = trace_graph_filter_on_task(ginfo, wake_pid); if (!filter) *sched_pid = wake_pid; } } *sched_switch = is_sched_switch; return filter; } static int cpu_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { struct cpu_plot_info *cpu_info = plot->private; struct event_format *event; struct pevent_record *record; int cpu = cpu_info->cpu; unsigned long long offset = 0; gboolean is_sched_switch; int sched_pid; int pid; int type; /* * Get the next record so we know can save its offset and * reset the cursor, not to mess up the plotting */ record = tracecmd_peek_data(ginfo->handle, cpu); if (record) offset = record->offset; /* Don't need to free a peek */ tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); again: /* find the non filtered event */ while ((record = tracecmd_read_data(ginfo->handle, cpu))) { if (!filter_record(ginfo, record, &pid, &sched_pid, &is_sched_switch) && !trace_graph_filter_on_event(ginfo, record) && record->ts >= time) break; free_record(record); } if (offset) tracecmd_set_cursor(ginfo->handle, cpu, offset); if (!record) return 0; /* Must have the record we want */ type = pevent_data_type(ginfo->pevent, record); event = pevent_data_event_from_type(ginfo->pevent, type); /* Unlikely that the event was not saved */ if (!event) goto again; if (is_sched_switch) pid = sched_pid; trace_seq_printf(s, "%s-%d\n%s\n", pevent_data_comm_from_pid(ginfo->pevent, pid), pid, event->name); free_record(record); if (offset) return 1; /* * We need to stop the iterator, read last record. */ record = tracecmd_read_cpu_last(ginfo->handle, cpu); free_record(record); return 1; } static void cpu_plot_start(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct cpu_plot_info *cpu_info = plot->private; int cpu; cpu = cpu_info->cpu; cpu_info->last_time = 0ULL; cpu_info->last_pid = -1; free_record(cpu_info->last_record); cpu_info->last_record = NULL; } static void update_last_record(struct graph_info *ginfo, struct cpu_plot_info *cpu_info, struct pevent_record *record) { struct tracecmd_input *handle = ginfo->handle; struct pevent_record *trecord; int filter; int sched_pid; int orig_pid; int is_sched_switch; if (record) tracecmd_record_ref(record); else record = get_record_from_time(ginfo, cpu_info->cpu, ginfo->view_end_time); trecord = tracecmd_read_prev(handle, record); free_record(record); if (!trecord) return; filter = filter_record(ginfo, trecord, &orig_pid, &sched_pid, &is_sched_switch); cpu_info->last_pid = is_sched_switch ? sched_pid : orig_pid; cpu_info->last_record = trecord; cpu_info->last_time = trecord->ts; /* We moved the cursor, put it back */ trecord = tracecmd_read_data(handle, cpu_info->cpu); free_record(trecord); } static int cpu_plot_event(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record, struct plot_info *info) { struct cpu_plot_info *cpu_info = plot->private; int sched_pid; int orig_pid; int is_sched_switch; int filter; int box_filter; int pid; int cpu; int ret = 1; cpu = cpu_info->cpu; if (!record) { if (!cpu_info->last_record) update_last_record(ginfo, cpu_info, record); /* Finish a box if the last record was not idle */ if (cpu_info->last_pid > 0) { info->box = TRUE; info->bstart = cpu_info->last_time; info->bend = ginfo->view_end_time; info->bcolor = hash_pid(cpu_info->last_pid); } if (cpu_info->last_record) { free_record(cpu_info->last_record); cpu_info->last_record = NULL; } return 0; } /* * If last record is NULL, then it may exist off the * viewable range. Search to see if one exists. */ if (!cpu_info->last_record) update_last_record(ginfo, cpu_info, record); free_record(cpu_info->last_record); cpu_info->last_record = record; tracecmd_record_ref(record); cpu = cpu_info->cpu; filter = filter_record(ginfo, record, &orig_pid, &sched_pid, &is_sched_switch); /* set pid to record, or next task on sched_switch */ pid = is_sched_switch ? sched_pid : orig_pid; if (cpu_info->last_pid != pid) { if (cpu_info->last_pid < 0) { /* if we hit a sched switch, use the original pid for box*/ if (is_sched_switch) cpu_info->last_pid = orig_pid; else cpu_info->last_pid = pid; /* Box should always use the original pid (prev in sched_switch) */ box_filter = trace_graph_filter_on_task(ginfo, orig_pid); } else box_filter = trace_graph_filter_on_task(ginfo, cpu_info->last_pid); if (!box_filter && cpu_info->last_pid) { info->bcolor = hash_pid(cpu_info->last_pid); info->box = TRUE; info->bstart = cpu_info->last_time; info->bend = record->ts; } cpu_info->last_time = record->ts; } if (!filter && !trace_graph_filter_on_event(ginfo, record)) { info->line = TRUE; info->ltime = record->ts; info->lcolor = hash_pid(pid); } cpu_info->last_pid = pid; if (record->ts > ginfo->view_end_time) ret = 0; return ret; } static struct pevent_record * find_record_on_cpu(struct graph_info *ginfo, gint cpu, guint64 time) { struct pevent_record *record = NULL; guint64 offset = 0; tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); do { if (record) { offset = record->offset; free_record(record); } record = tracecmd_read_data(ginfo->handle, cpu); } while (record && record->ts <= (time - 1 / ginfo->resolution)); if (record) { if (record->ts > (time + 1 / ginfo->resolution) && offset) { free_record(record); record = tracecmd_read_at(ginfo->handle, offset, NULL); } } return record; } static struct pevent_record * cpu_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct cpu_plot_info *cpu_info = plot->private; int cpu; cpu = cpu_info->cpu; return find_record_on_cpu(ginfo, cpu, time); } int cpu_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { struct cpu_plot_info *cpu_info = plot->private; struct event_format *event; struct pevent_record *record; struct pevent *pevent; unsigned long sec, usec; const char *comm; int type; int pid; int cpu; int ret = 0; cpu = cpu_info->cpu; record = find_record_on_cpu(ginfo, cpu, time); if (!record) { /* try last record */ record = tracecmd_read_cpu_last(ginfo->handle, cpu); if (record && record->ts < time) { if (!trace_graph_check_sched_switch(ginfo, record, &pid, &comm)) { pid = pevent_data_pid(ginfo->pevent, record); comm = pevent_data_comm_from_pid(ginfo->pevent, pid); } convert_nano(record->ts, &sec, &usec); trace_seq_printf(s, "%lu.%06lu", sec, usec); if (pid) trace_seq_printf(s, " %s-%d", comm, pid); else trace_seq_puts(s, " "); ret = 1; } free_record(record); return ret; } convert_nano(record->ts, &sec, &usec); pevent = ginfo->pevent; pid = pevent_data_pid(ginfo->pevent, record); comm = pevent_data_comm_from_pid(ginfo->pevent, pid); if (record->ts > time - 2/ginfo->resolution && record->ts < time + 2/ginfo->resolution) { type = pevent_data_type(pevent, record); event = pevent_data_event_from_type(pevent, type); if (event) { trace_seq_puts(s, event->name); trace_seq_putc(s, '\n'); pevent_data_lat_fmt(pevent, s, record); trace_seq_putc(s, '\n'); pevent_event_info(s, event, record); trace_seq_putc(s, '\n'); } else trace_seq_printf(s, "UNKNOW EVENT %d\n", type); } else { if (record->ts < time) trace_graph_check_sched_switch(ginfo, record, &pid, &comm); } trace_seq_printf(s, "%lu.%06lu", sec, usec); if (pid) trace_seq_printf(s, " %s-%d", comm, pid); else trace_seq_puts(s, " "); free_record(record); return 1; } static void cpu_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) { struct cpu_plot_info *cpu_info = plot->private; trace_graph_plot_remove_cpu(ginfo, plot, cpu_info->cpu); free_record(cpu_info->last_record); free(cpu_info); } static const struct plot_callbacks cpu_plot_cb = { .match_time = cpu_plot_match_time, .plot_event = cpu_plot_event, .start = cpu_plot_start, .display_last_event = cpu_plot_display_last_event, .find_record = cpu_plot_find_record, .display_info = cpu_plot_display_info, .destroy = cpu_plot_destroy }; static void add_cpu_plot(struct graph_info *ginfo, gint cpu) { struct cpu_plot_info *cpu_info; struct graph_plot *plot; char label[100]; cpu_info = malloc_or_die(sizeof(*cpu_info)); memset(cpu_info, 0, sizeof(*cpu_info)); cpu_info->cpu = cpu; snprintf(label, 100, "CPU %d", cpu); plot = trace_graph_plot_append(ginfo, label, PLOT_TYPE_CPU, &cpu_plot_cb, cpu_info); trace_graph_plot_add_cpu(ginfo, plot, cpu); } void graph_plot_cpus_update_callback(gboolean accept, gboolean all_cpus, guint64 *selected_cpu_mask, gpointer data) { struct graph_info *ginfo = data; struct cpu_plot_info *cpu_info; struct graph_plot *plot; gboolean old_all_cpus; guint64 *old_cpu_mask; int i; if (!accept) return; /* Get the current status */ graph_plot_cpus_plotted(ginfo, &old_all_cpus, &old_cpu_mask); if (selected_cpu_mask && cpus_equal(old_cpu_mask, selected_cpu_mask, ginfo->cpus)) { /* Nothing to do */ g_free(old_cpu_mask); return; } if (!all_cpus) { /* * Remove any plots not selected. * Go backwards, since removing a plot shifts the * array from current position back. */ for (i = ginfo->plots - 1; i >= 0; i--) { plot = ginfo->plot_array[i]; if (plot->type != PLOT_TYPE_CPU) continue; cpu_info = plot->private; if (!cpu_isset(selected_cpu_mask, cpu_info->cpu)) trace_graph_plot_remove(ginfo, plot); } } /* Now add any plots not set */ for (i = 0; i < ginfo->cpus; i++) { if (!all_cpus && !cpu_isset(selected_cpu_mask, i)) continue; if (cpu_isset(old_cpu_mask, i)) continue; add_cpu_plot(ginfo, i); } g_free(old_cpu_mask); trace_graph_refresh(ginfo); } /** * graph_plot_cpus_plotted - return what CPUs are plotted * @ginfo: the graph info structure * @all_cpus: returns true if all CPUS are currently plotted * @cpu_mask: returns an allocated mask of what cpus are set * * @cpu_mask must be freed with g_free() after this is called. */ void graph_plot_cpus_plotted(struct graph_info *ginfo, gboolean *all_cpus, guint64 **cpu_mask) { struct cpu_plot_info *cpu_info; struct graph_plot *plot; int i; *cpu_mask = g_new0(guint64, (ginfo->cpus >> 6) + 1); g_assert(*cpu_mask); for (i = 0; i < ginfo->plots; i++) { plot = ginfo->plot_array[i]; if (plot->type != PLOT_TYPE_CPU) continue; cpu_info = plot->private; cpu_set(*cpu_mask, cpu_info->cpu); } *all_cpus = cpu_weight(*cpu_mask, ginfo->cpus) == ginfo->cpus ? TRUE : FALSE; } void graph_plot_init_cpus(struct graph_info *ginfo, int cpus) { long cpu; for (cpu = 0; cpu < cpus; cpu++) add_cpu_plot(ginfo, cpu); } trace-cmd-2.5.3/trace-plot-task.c000066400000000000000000000512121246701203100165330ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include "trace-graph.h" #include "trace-filter.h" #define RED 0xff #define GREEN (0xff<<16) struct task_plot_info { int pid; struct cpu_data *cpu_data; struct pevent_record **last_records; unsigned long long last_time; unsigned long long wake_time; unsigned long long display_wake_time; int wake_color; int last_cpu; }; static void convert_nano(unsigned long long time, unsigned long *sec, unsigned long *usec) { *sec = time / 1000000000ULL; *usec = (time / 1000) % 1000000; } static gint hash_pid(gint val) { /* idle always gets black */ if (!val) return 0; return trace_hash(val); } static int hash_cpu(int cpu) { cpu = (cpu << 3) + cpu * 21; return trace_hash(cpu); } static gboolean is_running(struct graph_info *ginfo, struct pevent_record *record) { unsigned long long val; int id; id = pevent_data_type(ginfo->pevent, record); if (id != ginfo->event_sched_switch_id) return FALSE; pevent_read_number_field(ginfo->event_prev_state, record->data, &val); return val ? FALSE : TRUE; } static gboolean record_matches_pid(struct graph_info *ginfo, struct pevent_record *record, int match_pid, int *pid, int *sched_pid, gboolean *is_sched, gboolean *wakeup) { const char *comm; *is_sched = FALSE; *wakeup = FALSE; *pid = pevent_data_pid(ginfo->pevent, record); *sched_pid = *pid; if (trace_graph_check_sched_switch(ginfo, record, sched_pid, &comm)) { if (*pid == match_pid || *sched_pid == match_pid) { *is_sched = TRUE; return TRUE; } } if (trace_graph_check_sched_wakeup(ginfo, record, sched_pid)) { if (*sched_pid == match_pid) { *wakeup = TRUE; return TRUE; } } if (*pid == match_pid) return TRUE; return FALSE; } static void set_cpu_to_time(int cpu, struct graph_info *ginfo, unsigned long long time) { struct pevent_record *record; tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); while ((record = tracecmd_read_data(ginfo->handle, cpu))) { if (record->ts >= time) break; free_record(record); } if (record) { tracecmd_set_cursor(ginfo->handle, cpu, record->offset); free_record(record); } else tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); } static void set_cpus_to_time(struct graph_info *ginfo, unsigned long long time) { int cpu; for (cpu = 0; cpu < ginfo->cpus; cpu++) set_cpu_to_time(cpu, ginfo, time); } static int task_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct task_plot_info *task_info = plot->private; struct pevent_record *record = NULL; gboolean is_wakeup; gboolean is_sched; gboolean match; int rec_pid; int sched_pid; int next_cpu; int pid; int ret = 0; pid = task_info->pid; set_cpus_to_time(ginfo, time); do { free_record(record); record = tracecmd_read_next_data(ginfo->handle, &next_cpu); if (!record) return 0; match = record_matches_pid(ginfo, record, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup); /* Use +1 to make sure we have a match first */ } while ((!match && record->ts < time + 1) || (match && record->ts < time)); if (record && record->ts == time) ret = 1; free_record(record); return ret; } struct offset_cache { guint64 *offsets; }; static struct offset_cache *save_offsets(struct graph_info *ginfo) { struct offset_cache *offsets; struct pevent_record *record; int cpu; offsets = malloc_or_die(sizeof(*offsets)); offsets->offsets = malloc_or_die(sizeof(*offsets->offsets) * ginfo->cpus); memset(offsets->offsets, 0, sizeof(*offsets->offsets) * ginfo->cpus); for (cpu = 0; cpu < ginfo->cpus; cpu++) { record = tracecmd_peek_data(ginfo->handle, cpu); if (record) offsets->offsets[cpu] = record->offset; } return offsets; } static void restore_offsets(struct graph_info *ginfo, struct offset_cache *offsets) { struct pevent_record *record; int cpu; for (cpu = 0; cpu < ginfo->cpus; cpu++) { if (offsets->offsets[cpu]) tracecmd_set_cursor(ginfo->handle, cpu, offsets->offsets[cpu]); else { /* end of cpu, make sure it stays the end */ record = tracecmd_read_cpu_last(ginfo->handle, cpu); free_record(record); } } free(offsets->offsets); free(offsets); } static struct pevent_record * find_record(struct graph_info *ginfo, gint pid, guint64 time) { struct pevent_record *record = NULL; gboolean is_wakeup; gboolean is_sched; gboolean match; int sched_pid; int rec_pid; int next_cpu; set_cpus_to_time(ginfo, time); do { free_record(record); record = tracecmd_read_next_data(ginfo->handle, &next_cpu); if (!record) return NULL; match = record_matches_pid(ginfo, record, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup); /* Use +1 to make sure we have a match first */ } while (!(record->ts > time && match)); return record; } static int task_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { struct task_plot_info *task_info = plot->private; struct event_format *event; struct pevent_record *record; struct offset_cache *offsets; gboolean is_sched; gboolean is_wakeup; int sched_pid; int rec_pid; int pid; int type; pid = task_info->pid; /* * Get the next record so we know can save its offset and * reset the cursor, not to mess up the plotting */ offsets = save_offsets(ginfo); record = find_record(ginfo, pid, time); restore_offsets(ginfo, offsets); if (!record) return 0; record_matches_pid(ginfo, record, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup); if (is_sched) { if (sched_pid == pid) { if (task_info->display_wake_time) { trace_seq_printf(s, "sched_switch\n" "CPU %d: lat: %.3fus\n", record->cpu, (double)(record->ts - task_info->display_wake_time) / 1000.0); task_info->display_wake_time = 0; } else { trace_seq_printf(s, "sched_switch\n" "CPU %d\n", record->cpu); } } else { trace_seq_printf(s, "sched_switch\n" "CPU %d %s-%d\n", record->cpu, pevent_data_comm_from_pid(ginfo->pevent, pid), pid); } } else { /* Must have the record we want */ type = pevent_data_type(ginfo->pevent, record); event = pevent_data_event_from_type(ginfo->pevent, type); if (pid == rec_pid) trace_seq_printf(s, "CPU %d\n%s\n", record->cpu, event->name); else trace_seq_printf(s, "%s-%d\n%s\n", pevent_data_comm_from_pid(ginfo->pevent, rec_pid), rec_pid, event->name); } free_record(record); return 1; } static void task_plot_start(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct task_plot_info *task_info = plot->private; memset(task_info->last_records, 0, sizeof(struct pevent_record *) * ginfo->cpus); task_info->last_time = 0ULL; task_info->last_cpu = -1; task_info->wake_time = 0ULL; task_info->display_wake_time = 0ULL; } static void update_last_record(struct graph_info *ginfo, struct task_plot_info *task_info, struct pevent_record *record) { struct tracecmd_input *handle = ginfo->handle; struct pevent_record *trecord, *t2record; struct pevent_record *saved; unsigned long long ts; int sched_pid; int pid; int rec_pid; int is_wakeup; int is_sched; int this_cpu; int cpu; pid = task_info->pid; if (record) { ts = record->ts; this_cpu = record->cpu; } else { ts = ginfo->view_end_time; this_cpu = -1; } for (cpu = 0; cpu < ginfo->cpus; cpu++) { if (task_info->last_records[cpu]) continue; if (cpu == this_cpu) { static int once; trecord = tracecmd_read_prev(handle, record); /* Set cpu cursor back to what it was */ saved = tracecmd_read_data(handle, cpu); if (!once && saved->offset != record->offset) { once++; warning("failed to reset cursor!"); } free_record(saved); } else { static int once; saved = tracecmd_read_data(handle, cpu); set_cpu_to_time(cpu, ginfo, ts); t2record = tracecmd_read_data(handle, cpu); trecord = tracecmd_read_prev(handle, t2record); free_record(t2record); /* reset cursor back to what it was */ if (saved) { tracecmd_set_cursor(handle, cpu, saved->offset); free_record(saved); } else { saved = tracecmd_read_data(handle, cpu); if (!once && saved) { once++; warning("failed to reset cursor to end!"); } /* saved should always be NULL */ free_record(saved); } } if (!trecord) continue; if (record_matches_pid(ginfo, trecord, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup) && !is_wakeup && (!is_sched || (is_sched && sched_pid == pid))) { task_info->last_records[cpu] = trecord; task_info->last_cpu = trecord->cpu; task_info->last_time = trecord->ts; break; } free_record(trecord); } } static int task_plot_event(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record, struct plot_info *info) { struct task_plot_info *task_info = plot->private; gboolean match; int sched_pid; int rec_pid; int is_wakeup; int is_sched; int pid; int cpu; pid = task_info->pid; if (!record) { update_last_record(ginfo, task_info, record); /* no more records, finish a box if one was started */ if (task_info->last_cpu >= 0) { info->box = TRUE; info->bstart = task_info->last_time; info->bend = ginfo->view_end_time; info->bcolor = hash_cpu(task_info->last_cpu); } for (cpu = 0; cpu < ginfo->cpus; cpu++) { free_record(task_info->last_records[cpu]); task_info->last_records[cpu] = NULL; } return 0; } match = record_matches_pid(ginfo, record, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup); if (!match && record->cpu != task_info->last_cpu) { if (!task_info->last_records[record->cpu]) { task_info->last_records[record->cpu] = record; tracecmd_record_ref(record); } return 0; } if (match) { info->line = TRUE; info->lcolor = hash_pid(rec_pid); info->ltime = record->ts; /* * Is this our first match? * * If last record is NULL, then it may exist off the * viewable range. Search to see if one exists, and if * it is the record we want to match. */ update_last_record(ginfo, task_info, record); if (is_wakeup) { /* Wake up but not task */ info->ltime = hash_pid(rec_pid); /* Another task ? */ if (task_info->last_cpu == record->cpu) { info->box = TRUE; info->bcolor = hash_cpu(task_info->last_cpu); info->bstart = task_info->last_time; info->bend = record->ts; task_info->last_cpu = -1; } task_info->wake_time = record->ts; task_info->wake_color = GREEN; task_info->display_wake_time = record->ts; return 1; } if (task_info->last_cpu != record->cpu) { if (task_info->last_cpu >= 0) { /* Switched CPUs */ info->box = TRUE; info->bcolor = hash_cpu(task_info->last_cpu); info->bstart = task_info->last_time; info->bend = record->ts; } task_info->last_time = record->ts; } task_info->last_cpu = record->cpu; if (is_sched) { if (rec_pid != pid) { /* Just got scheduled in */ task_info->last_cpu = record->cpu; task_info->last_time = record->ts; if (task_info->wake_time) { info->box = TRUE; info->bfill = FALSE; info->bstart = task_info->wake_time; info->bend = record->ts; info->bcolor = task_info->wake_color; } else task_info->wake_time = 0; } else if (!info->box) { /* just got scheduled out */ info->box = TRUE; info->bcolor = hash_cpu(task_info->last_cpu); info->bstart = task_info->last_time; info->bend = record->ts; task_info->last_cpu = -1; if (is_running(ginfo, record)) { task_info->wake_time = record->ts; task_info->wake_color = RED; } else task_info->wake_time = 0; } else task_info->wake_time = 0; } else task_info->wake_time = 0; return 1; } cpu = record->cpu; if (!task_info->last_records[cpu]) { task_info->last_records[cpu] = record; tracecmd_record_ref(record); } /* not a match, and on the last CPU, scheduled out? */ if (task_info->last_cpu >= 0) { info->box = TRUE; info->bcolor = hash_cpu(task_info->last_cpu); info->bstart = task_info->last_time; info->bend = record->ts; task_info->last_cpu = -1; } return 1; } static struct pevent_record * task_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { struct task_plot_info *task_info = plot->private; int pid; pid = task_info->pid; return find_record(ginfo, pid, time); } #define MAX_SEARCH 20 static struct pevent_record * find_previous_record(struct graph_info *ginfo, struct pevent_record *start_record, int pid, int cpu) { struct pevent_record *last_record = start_record; struct pevent_record *record; gboolean match; gboolean is_sched; gboolean is_wakeup; gint rec_pid; gint sched_pid; int count = 0; if (last_record) last_record->ref_count++; else last_record = tracecmd_read_cpu_last(ginfo->handle, cpu); while ((record = tracecmd_read_prev(ginfo->handle, last_record))) { count++; match = record_matches_pid(ginfo, record, pid, &rec_pid, &sched_pid, &is_sched, &is_wakeup); if (match) break; free_record(last_record); if (count > MAX_SEARCH) { free_record(record); return NULL; } last_record = record; } free_record(last_record); return record; } static struct pevent_record * get_display_record(struct graph_info *ginfo, int pid, unsigned long long time) { struct pevent_record *record; struct pevent_record **records; unsigned long long ts; int next_cpu; int cpu; record = find_record(ginfo, pid, time); /* If the time is right at this record, use it */ if (record && record->ts < time + (1 / ginfo->resolution)) return record; if (record) { tracecmd_set_cursor(ginfo->handle, record->cpu, record->offset); free_record(record); } /* find a previous record */ records = malloc_or_die(sizeof(*records) * ginfo->cpus); for (cpu = 0; cpu < ginfo->cpus; cpu++) { record = tracecmd_read_data(ginfo->handle, cpu); records[cpu] = find_previous_record(ginfo, record, pid, cpu); free_record(record); } record = NULL; for (;;) { ts = 0; next_cpu = -1; for (cpu = 0; cpu < ginfo->cpus; cpu++) { if (!records[cpu]) continue; if (records[cpu]->ts > ts) { ts = records[cpu]->ts; next_cpu = cpu; } } if (next_cpu < 0) break; if (records[next_cpu]->ts < time + (2 / ginfo->resolution)) { record = records[next_cpu]; break; } record = find_previous_record(ginfo, records[next_cpu], pid, next_cpu); free_record(records[next_cpu]); records[next_cpu] = record; record = NULL; } for (cpu = 0; cpu < ginfo->cpus; cpu++) { if (records[cpu] == record) continue; free_record(records[cpu]); } free(records); return record; } int task_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { struct task_plot_info *task_info = plot->private; struct event_format *event; struct pevent_record *record; struct pevent *pevent; unsigned long sec, usec; const char *comm; int cpu; int type; int sched_pid = -1; int pid; pid = task_info->pid; record = get_display_record(ginfo, pid, time); if (!record) return 0; pevent = ginfo->pevent; pid = pevent_data_pid(ginfo->pevent, record); cpu = record->cpu; convert_nano(record->ts, &sec, &usec); if (record->ts > time - 2/ginfo->resolution && record->ts < time + 2/ginfo->resolution) { type = pevent_data_type(pevent, record); event = pevent_data_event_from_type(pevent, type); if (event) { trace_seq_puts(s, event->name); trace_seq_putc(s, '\n'); pevent_data_lat_fmt(pevent, s, record); trace_seq_putc(s, '\n'); pevent_event_info(s, event, record); trace_seq_putc(s, '\n'); } else trace_seq_printf(s, "UNKNOW EVENT %d\n", type); } trace_graph_check_sched_switch(ginfo, record, &sched_pid, &comm); trace_seq_printf(s, "%lu.%06lu", sec, usec); if (pid == task_info->pid || sched_pid == task_info->pid) trace_seq_printf(s, " CPU: %03d", cpu); free_record(record); return 1; } void task_plot_destroy(struct graph_info *ginfo, struct graph_plot *plot) { struct task_plot_info *task_info = plot->private; trace_graph_plot_remove_all_recs(ginfo, plot); free(task_info->last_records); free(task_info); } static const struct plot_callbacks task_plot_cb = { .match_time = task_plot_match_time, .plot_event = task_plot_event, .start = task_plot_start, .display_last_event = task_plot_display_last_event, .find_record = task_plot_find_record, .display_info = task_plot_display_info, .destroy = task_plot_destroy }; /** * graph_plot_task_plotted - return what tasks are plotted * @ginfo: the graph info structure * @plotted: returns an allocated array of gints holding the pids. * the last pid is -1, NULL, if none are. * * @plotted must be freed with free() after this is called. */ void graph_plot_task_plotted(struct graph_info *ginfo, gint **plotted) { struct task_plot_info *task_info; struct graph_plot *plot; int count = 0; int i; *plotted = NULL; for (i = 0; i < ginfo->plots; i++) { plot = ginfo->plot_array[i]; if (plot->type != PLOT_TYPE_TASK) continue; task_info = plot->private; trace_array_add(plotted, &count, task_info->pid); } } void graph_plot_task_update_callback(gboolean accept, gint *selected, gint *non_select, gpointer data) { struct graph_info *ginfo = data; struct task_plot_info *task_info; struct graph_plot *plot; gint select_size = 0; gint *ptr; int i; if (!accept) return; /* The selected and non_select are sorted */ if (selected) { for (i = 0; selected[i] >= 0; i++) ; select_size = i; } /* * Remove and add task plots. * Go backwards, since removing a plot shifts the * array from current position back. */ for (i = ginfo->plots - 1; i >= 0; i--) { plot = ginfo->plot_array[i]; if (plot->type != PLOT_TYPE_TASK) continue; /* If non are selected, then remove all */ if (!select_size) { trace_graph_plot_remove(ginfo, plot); continue; } task_info = plot->private; ptr = bsearch(&task_info->pid, selected, select_size, sizeof(gint), id_cmp); if (ptr) { /* * This plot plot already exists, remove it * from the selected array. */ memmove(ptr, ptr + 1, (unsigned long)(selected + select_size) - (unsigned long)(ptr + 1)); select_size--; continue; } /* Remove the plot */ trace_graph_plot_remove(ginfo, plot); } /* Now add any plots that need to be added */ for (i = 0; i < select_size; i++) graph_plot_task(ginfo, selected[i], ginfo->plots); trace_graph_refresh(ginfo); } void graph_plot_init_tasks(struct graph_info *ginfo) { struct task_plot_info *task_info; char label[100]; struct pevent_record *record; int pid; /* Just for testing */ record = tracecmd_read_cpu_first(ginfo->handle, 0); while (record) { pid = pevent_data_pid(ginfo->pevent, record); free_record(record); if (pid) break; record = tracecmd_read_data(ginfo->handle, 0); } task_info = malloc_or_die(sizeof(*task_info)); task_info->last_records = malloc_or_die(sizeof(struct pevent_record *) * ginfo->cpus); task_info->pid = pid; snprintf(label, 100, "TASK %d", pid); trace_graph_plot_insert(ginfo, 1, label, PLOT_TYPE_TASK, &task_plot_cb, task_info); } void graph_plot_task(struct graph_info *ginfo, int pid, int pos) { struct task_plot_info *task_info; struct graph_plot *plot; const char *comm; char *label; int len; task_info = malloc_or_die(sizeof(*task_info)); task_info->last_records = malloc_or_die(sizeof(struct pevent_record *) * ginfo->cpus); task_info->pid = pid; comm = pevent_data_comm_from_pid(ginfo->pevent, pid); len = strlen(comm) + 100; label = malloc_or_die(len); snprintf(label, len, "%s-%d", comm, pid); plot = trace_graph_plot_insert(ginfo, pos, label, PLOT_TYPE_TASK, &task_plot_cb, task_info); free(label); trace_graph_plot_add_all_recs(ginfo, plot); } trace-cmd-2.5.3/trace-plot.c000066400000000000000000000205511246701203100155750ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include "trace-graph.h" void trace_graph_plot_free(struct graph_info *ginfo) { struct graph_plot **array; int plots; int i; /* copy the plot_array since the removing plots will modify it */ array = malloc_or_die(sizeof(*array) * ginfo->plots); memcpy(array, ginfo->plot_array, sizeof(*array) * ginfo->plots); plots = ginfo->plots; for (i = 0; i < plots; i++) trace_graph_plot_remove(ginfo, array[i]); free(array); if (ginfo->plot_array) { free(ginfo->plot_array); ginfo->plot_array = NULL; }; ginfo->plots = 0; } void trace_graph_plot_init(struct graph_info *ginfo) { ginfo->plots = 0; ginfo->plot_array = NULL; } static struct graph_plot * allocate_plot(struct graph_info *ginfo, const char *label, const struct plot_callbacks *cb, void *data) { struct graph_plot *plot; char *name; name = strdup(label); if (!name) die("Unable to allocate label"); plot = malloc_or_die(sizeof(*plot)); memset(plot, 0, sizeof(*plot)); plot->label = name; plot->cb = cb; plot->private = data; return plot; } struct graph_plot * trace_graph_plot_append(struct graph_info *ginfo, const char *label, enum graph_plot_type type, const struct plot_callbacks *cb, void *data) { struct graph_plot *plot; plot = allocate_plot(ginfo, label, cb, data); plot->type = type; plot->pos = ginfo->plots; if (!ginfo->plots) { ginfo->plot_array = malloc_or_die(sizeof(ginfo->plot_array[0])); ginfo->plot_array[0] = plot; } else { ginfo->plot_array = realloc(ginfo->plot_array, sizeof(ginfo->plot_array[0]) * (ginfo->plots + 1)); if (!ginfo->plot_array) die("unable to resize plot array"); ginfo->plot_array[ginfo->plots] = plot; } ginfo->plots++; return plot; } struct graph_plot * trace_graph_plot_insert(struct graph_info *ginfo, int pos, const char *label, enum graph_plot_type type, const struct plot_callbacks *cb, void *data) { struct graph_plot *plot; int i; if (pos >= ginfo->plots) return trace_graph_plot_append(ginfo, label, type, cb, data); if (pos < 0) pos = 0; plot = allocate_plot(ginfo, label, cb, data); plot->pos = pos; plot->type = type; ginfo->plot_array = realloc(ginfo->plot_array, sizeof(ginfo->plot_array[0]) * (ginfo->plots + 1)); if (!ginfo->plot_array) die("unable to resize plot array"); memmove(&ginfo->plot_array[pos+1], &ginfo->plot_array[pos], sizeof(ginfo->plot_array[0]) * (ginfo->plots - pos)); ginfo->plot_array[pos] = plot; ginfo->plots++; /* Update the new positions */ for (i = pos + 1; i < ginfo->plots; i++) ginfo->plot_array[i]->pos = i; return plot; } void trace_graph_plot_remove(struct graph_info *ginfo, struct graph_plot *plot) { int pos = plot->pos; int i; if (plot->cb->destroy) plot->cb->destroy(ginfo, plot); free(plot->label); free(plot); ginfo->plots--; if (ginfo->plots) { memmove(&ginfo->plot_array[pos], &ginfo->plot_array[pos+1], sizeof(ginfo->plot_array[0]) * (ginfo->plots - pos)); /* Update the new positions */ for (i = pos; i < ginfo->plots; i++) ginfo->plot_array[i]->pos = i; } else { free(ginfo->plot_array); ginfo->plot_array = NULL; } } static struct plot_hash *find_hash(struct plot_hash **array, gint val) { struct plot_hash *hash; gint key; key = trace_hash(val) % PLOT_HASH_SIZE; for (hash = array[key]; hash; hash = hash->next) { if (hash->val == val) return hash; } return NULL; } static void add_hash(struct plot_hash **array, struct graph_plot *plot, gint val) { struct plot_hash *hash; struct plot_list *list; gint key; list = malloc_or_die(sizeof(*list)); hash = find_hash(array, val); if (!hash) { hash = g_new0(typeof(*hash), 1); g_assert(hash); key = trace_hash(val) % PLOT_HASH_SIZE; hash->next = array[key]; hash->val = val; array[key] = hash; } list->next = hash->plots; list->plot = plot; hash->plots = list; } static void remove_hash(struct plot_hash **array, struct graph_plot *plot, gint val) { struct plot_hash *hash, **phash; struct plot_list **pplot; struct plot_list *list; gint key; hash = find_hash(array, val); pplot = &hash->plots; while ((list = *pplot)) { if (list->plot == plot) { *pplot = list->next; free(list); break; } pplot = &list->next; } if (hash->plots) return; /* remove this hash item */ key = trace_hash(val) % PLOT_HASH_SIZE; phash = &array[key]; while (*phash) { if (*phash == hash) { *phash = hash->next; break; } phash = &(*phash)->next; } g_free(hash); } struct plot_hash * trace_graph_plot_find_task(struct graph_info *ginfo, gint task) { return find_hash(ginfo->task_hash, task); } void trace_graph_plot_add_task(struct graph_info *ginfo, struct graph_plot *plot, gint task) { add_hash(ginfo->task_hash, plot, task); ginfo->nr_task_hash++; } void trace_graph_plot_remove_task(struct graph_info *ginfo, struct graph_plot *plot, gint task) { remove_hash(ginfo->task_hash, plot, task); ginfo->nr_task_hash--; } struct plot_hash * trace_graph_plot_find_cpu(struct graph_info *ginfo, gint cpu) { return find_hash(ginfo->cpu_hash, cpu); } void trace_graph_plot_add_cpu(struct graph_info *ginfo, struct graph_plot *plot, gint cpu) { add_hash(ginfo->cpu_hash, plot, cpu); } void trace_graph_plot_remove_cpu(struct graph_info *ginfo, struct graph_plot *plot, gint cpu) { remove_hash(ginfo->cpu_hash, plot, cpu); } void trace_graph_plot_add_all_recs(struct graph_info *ginfo, struct graph_plot *plot) { struct plot_list *list; list = malloc_or_die(sizeof(*list)); list->next = ginfo->all_recs; list->plot = plot; ginfo->all_recs = list; } void trace_graph_plot_remove_all_recs(struct graph_info *ginfo, struct graph_plot *plot) { struct plot_list **pplot; struct plot_list *list; pplot = &ginfo->all_recs; while ((list = *pplot)) { if (list->plot == plot) { *pplot = list->next; free(list); break; } pplot = &list->next; } } /* Plot callback helpers */ int trace_graph_plot_match_time(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { if (!plot->cb->match_time) return 0; return plot->cb->match_time(ginfo, plot, time); } void trace_graph_plot_start(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { if (!plot->cb->start) return; return plot->cb->start(ginfo, plot, time); } int trace_graph_plot_event(struct graph_info *ginfo, struct graph_plot *plot, struct pevent_record *record, struct plot_info *info) { info->line = FALSE; info->box = FALSE; info->bfill = TRUE; if (!plot->cb->plot_event) return 0; return plot->cb->plot_event(ginfo, plot, record, info); } void trace_graph_plot_end(struct graph_info *ginfo, struct graph_plot *plot) { if (!plot->cb->end) return; return plot->cb->end(ginfo, plot); } int trace_graph_plot_display_last_event(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { if (!plot->cb->display_last_event) return 0; return plot->cb->display_last_event(ginfo, plot, s, time); } struct pevent_record * trace_graph_plot_find_record(struct graph_info *ginfo, struct graph_plot *plot, unsigned long long time) { if (!plot->cb->find_record) return 0; return plot->cb->find_record(ginfo, plot, time); } int trace_graph_plot_display_info(struct graph_info *ginfo, struct graph_plot *plot, struct trace_seq *s, unsigned long long time) { if (!plot->cb->display_info) return 0; return plot->cb->display_info(ginfo, plot, s, time); } trace-cmd-2.5.3/trace-profile.c000066400000000000000000001405351246701203100162640ustar00rootroot00000000000000/* * Copyright (C) 2014 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** FIXME: Convert numbers based on machine and file */ #define _LARGEFILE64_SOURCE #include #include #include #include "trace-local.h" #include "trace-hash.h" #define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWP" #define TASK_STATE_MAX 1024 #define task_from_item(item) container_of(item, struct task_data, hash) #define start_from_item(item) container_of(item, struct start_data, hash) #define event_from_item(item) container_of(item, struct event_hash, hash) #define stack_from_item(item) container_of(item, struct stack_data, hash) #define event_data_from_item(item) container_of(item, struct event_data, hash) static unsigned long long nsecs_per_sec(unsigned long long ts) { return ts / NSECS_PER_SEC; } static unsigned long long mod_to_usec(unsigned long long ts) { return ((ts % NSECS_PER_SEC) + NSECS_PER_USEC / 2) / NSECS_PER_USEC; } struct handle_data; struct event_hash; struct event_data; typedef void (*event_data_print)(struct trace_seq *s, struct event_hash *hash); typedef int (*handle_event_func)(struct handle_data *h, unsigned long long pid, struct event_data *data, struct pevent_record *record, int cpu); enum event_data_type { EVENT_TYPE_UNDEFINED, EVENT_TYPE_STACK, EVENT_TYPE_SCHED_SWITCH, EVENT_TYPE_WAKEUP, EVENT_TYPE_FUNC, EVENT_TYPE_SYSCALL, EVENT_TYPE_IRQ, EVENT_TYPE_SOFTIRQ, EVENT_TYPE_SOFTIRQ_RAISE, EVENT_TYPE_PROCESS_EXEC, EVENT_TYPE_USER_MATE, }; struct event_data { struct trace_hash_item hash; int id; int trace; struct event_format *event; struct event_data *end; struct event_data *start; struct format_field *pid_field; struct format_field *start_match_field; /* match with start */ struct format_field *end_match_field; /* match with end */ struct format_field *data_field; /* optional */ event_data_print print_func; handle_event_func handle_event; void *private; int migrate; /* start/end pairs can migrate cpus */ enum event_data_type type; }; struct stack_data { struct trace_hash_item hash; unsigned long long count; unsigned long long time; unsigned long long time_min; unsigned long long ts_min; unsigned long long time_max; unsigned long long ts_max; unsigned long long time_avg; unsigned long size; char caller[]; }; struct stack_holder { unsigned long size; void *caller; struct pevent_record *record; }; struct start_data { struct trace_hash_item hash; struct event_data *event_data; struct list_head list; struct task_data *task; unsigned long long timestamp; unsigned long long search_val; unsigned long long val; int cpu; struct stack_holder stack; }; struct event_hash { struct trace_hash_item hash; struct event_data *event_data; unsigned long long search_val; unsigned long long val; unsigned long long count; unsigned long long time_total; unsigned long long time_avg; unsigned long long time_max; unsigned long long ts_max; unsigned long long time_min; unsigned long long ts_min; unsigned long long time_std; unsigned long long last_time; struct trace_hash stacks; }; struct task_data { struct trace_hash_item hash; int pid; int sleeping; char *comm; struct trace_hash start_hash; struct trace_hash event_hash; struct task_data *proxy; struct start_data *last_start; struct event_hash *last_event; struct pevent_record *last_stack; struct handle_data *handle; }; struct cpu_info { int current; }; struct sched_switch_data { struct format_field *prev_state; int match_state; }; struct handle_data { struct handle_data *next; struct tracecmd_input *handle; struct pevent *pevent; struct trace_hash events; struct cpu_info **cpu_data; struct format_field *common_pid; struct format_field *wakeup_comm; struct format_field *switch_prev_comm; struct format_field *switch_next_comm; struct sched_switch_data sched_switch_blocked; struct sched_switch_data sched_switch_preempt; struct trace_hash task_hash; struct list_head *cpu_starts; struct list_head migrate_starts; int cpus; }; static struct handle_data *handles; static struct event_data *stacktrace_event; static struct start_data * add_start(struct task_data *task, struct event_data *event_data, struct pevent_record *record, unsigned long long search_val, unsigned long long val) { struct start_data *start; start = malloc_or_die(sizeof(*start)); memset(start, 0, sizeof(*start)); start->hash.key = trace_hash(search_val); start->search_val = search_val; start->val = val; start->timestamp = record->ts; start->event_data = event_data; start->cpu = record->cpu; start->task = task; trace_hash_add(&task->start_hash, &start->hash); if (event_data->migrate) list_add(&start->list, &task->handle->migrate_starts); else list_add(&start->list, &task->handle->cpu_starts[record->cpu]); return start; } struct event_data_match { struct event_data *event_data; unsigned long long search_val; unsigned long long val; }; static int match_start(struct trace_hash_item *item, void *data) { struct start_data *start = start_from_item(item); struct event_data_match *edata = data; return start->event_data == edata->event_data && start->search_val == edata->search_val; } static int match_event(struct trace_hash_item *item, void *data) { struct event_data_match *edata = data; struct event_hash *event = event_from_item(item); return event->event_data == edata->event_data && event->search_val == edata->search_val && event->val == edata->val; } static struct event_hash * find_event_hash(struct task_data *task, struct event_data_match *edata) { struct event_hash *event_hash; struct trace_hash_item *item; unsigned long long key; key = (unsigned long)edata->event_data + (unsigned long)edata->search_val + (unsigned long)edata->val; key = trace_hash(key); item = trace_hash_find(&task->event_hash, key, match_event, edata); if (item) return event_from_item(item); event_hash = malloc_or_die(sizeof(*event_hash)); memset(event_hash, 0, sizeof(*event_hash)); event_hash->event_data = edata->event_data; event_hash->search_val = edata->search_val; event_hash->val = edata->val; event_hash->hash.key = key; trace_hash_init(&event_hash->stacks, 32); trace_hash_add(&task->event_hash, &event_hash->hash); return event_hash; } static struct event_hash * find_start_event_hash(struct task_data *task, struct event_data *event_data, struct start_data *start) { struct event_data_match edata; edata.event_data = event_data; edata.search_val = start->search_val; edata.val = start->val; return find_event_hash(task, &edata); } static struct start_data * find_start(struct task_data *task, struct event_data *event_data, unsigned long long search_val) { unsigned long long key = trace_hash(search_val); struct event_data_match edata; void *data = &edata; struct trace_hash_item *item; struct start_data *start; edata.event_data = event_data; edata.search_val = search_val; item = trace_hash_find(&task->start_hash, key, match_start, data); if (!item) return NULL; start = start_from_item(item); return start; } struct stack_match { void *caller; unsigned long size; }; static int match_stack(struct trace_hash_item *item, void *data) { struct stack_data *stack = stack_from_item(item); struct stack_match *match = data; if (match->size != stack->size) return 0; return memcmp(stack->caller, match->caller, stack->size) == 0; } static void add_event_stack(struct event_hash *event_hash, void *caller, unsigned long size, unsigned long long time, unsigned long long ts) { unsigned long long key; struct stack_data *stack; struct stack_match match; struct trace_hash_item *item; int i; match.caller = caller; match.size = size; if (size < sizeof(int)) die("Stack size of less than sizeof(int)??"); for (key = 0, i = 0; i <= size - sizeof(int); i += sizeof(int)) key += trace_hash(*(int *)(caller + i)); item = trace_hash_find(&event_hash->stacks, key, match_stack, &match); if (!item) { stack = malloc_or_die(sizeof(*stack) + size); memset(stack, 0, sizeof(*stack)); memcpy(&stack->caller, caller, size); stack->size = size; stack->hash.key = key; trace_hash_add(&event_hash->stacks, &stack->hash); } else stack = stack_from_item(item); stack->count++; stack->time += time; if (stack->count == 1 || time < stack->time_min) { stack->time_min = time; stack->ts_min = ts; } if (time > stack->time_max) { stack->time_max = time; stack->ts_max = ts; } } static void free_start(struct start_data *start) { if (start->task->last_start == start) start->task->last_start = NULL; if (start->stack.record) free_record(start->stack.record); trace_hash_del(&start->hash); list_del(&start->list); free(start); } static struct event_hash * add_and_free_start(struct task_data *task, struct start_data *start, struct event_data *event_data, unsigned long long ts) { struct event_hash *event_hash; long long delta; delta = ts - start->timestamp; /* * It's possible on a live trace, because of timestamps being * different on different CPUs, we can go back in time. When * that happens, just zero out the delta. */ if (delta < 0) delta = 0; event_hash = find_start_event_hash(task, event_data, start); event_hash->count++; event_hash->time_total += delta; event_hash->last_time = delta; if (delta > event_hash->time_max) { event_hash->time_max = delta; event_hash->ts_max = ts; } if (event_hash->count == 1 || delta < event_hash->time_min) { event_hash->time_min = delta; event_hash->ts_min = ts; } if (start->stack.record) { unsigned long size; void *caller; size = start->stack.size; caller = start->stack.caller; add_event_stack(event_hash, caller, size, delta, start->stack.record->ts); free_record(start->stack.record); start->stack.record = NULL; } free_start(start); return event_hash; } static struct event_hash * find_and_update_start(struct task_data *task, struct event_data *event_data, unsigned long long ts, unsigned long long search_val) { struct start_data *start; start = find_start(task, event_data, search_val); if (!start) return NULL; return add_and_free_start(task, start, event_data, ts); } static int match_task(struct trace_hash_item *item, void *data) { struct task_data *task = task_from_item(item); int pid = *(unsigned long *)data; return task->pid == pid; } static struct task_data * add_task(struct handle_data *h, int pid) { unsigned long long key = trace_hash(pid); struct task_data *task; task = malloc_or_die(sizeof(*task)); memset(task, 0, sizeof(*task)); task->pid = pid; task->hash.key = key; trace_hash_add(&h->task_hash, &task->hash); task->handle = h; trace_hash_init(&task->start_hash, 16); trace_hash_init(&task->event_hash, 32); return task; } static struct task_data * find_task(struct handle_data *h, int pid) { unsigned long long key = trace_hash(pid); struct trace_hash_item *item; static struct task_data *last_task; void *data = (unsigned long *)&pid; if (last_task && last_task->pid == pid) return last_task; item = trace_hash_find(&h->task_hash, key, match_task, data); if (item) last_task = task_from_item(item); else last_task = add_task(h, pid); return last_task; } static void add_task_comm(struct task_data *task, struct format_field *field, struct pevent_record *record) { const char *comm; task->comm = malloc_or_die(field->size + 1); comm = record->data + field->offset; memcpy(task->comm, comm, field->size); task->comm[field->size] = 0; } /* Account for tasks that don't have starts */ static void account_task(struct task_data *task, struct event_data *event_data, struct pevent_record *record) { struct event_data_match edata; struct event_hash *event_hash; struct task_data *proxy = NULL; unsigned long long search_val = 0; unsigned long long val = 0; unsigned long long pid; /* * If an event has the pid_field set, then find that task for * this event instead. Let this task proxy for it to handle * stack traces on this event. */ if (event_data->pid_field) { pevent_read_number_field(event_data->pid_field, record->data, &pid); proxy = task; task = find_task(task->handle, pid); proxy->proxy = task; } /* * If data_field is defined, use that for val, * if the start_field is defined, use that for search_val. */ if (event_data->data_field) { pevent_read_number_field(event_data->data_field, record->data, &val); } if (event_data->start_match_field) { pevent_read_number_field(event_data->start_match_field, record->data, &search_val); } edata.event_data = event_data; edata.search_val = val; edata.val = val; event_hash = find_event_hash(task, &edata); event_hash->count++; task->last_event = event_hash; } static struct task_data * find_event_task(struct handle_data *h, struct event_data *event_data, struct pevent_record *record, unsigned long long pid) { /* If pid_field is defined, use that to find the task */ if (event_data->pid_field) pevent_read_number_field(event_data->pid_field, record->data, &pid); return find_task(h, pid); } static struct task_data * handle_end_event(struct handle_data *h, struct event_data *event_data, struct pevent_record *record, int pid) { struct event_hash *event_hash; struct task_data *task; unsigned long long val; task = find_event_task(h, event_data, record, pid); pevent_read_number_field(event_data->start_match_field, record->data, &val); event_hash = find_and_update_start(task, event_data->start, record->ts, val); task->last_start = NULL; task->last_event = event_hash; return task; } static struct task_data * handle_start_event(struct handle_data *h, struct event_data *event_data, struct pevent_record *record, unsigned long long pid) { struct start_data *start; struct task_data *task; unsigned long long val; task = find_event_task(h, event_data, record, pid); pevent_read_number_field(event_data->end_match_field, record->data, &val); start = add_start(task, event_data, record, val, val); task->last_start = start; task->last_event = NULL; return task; } static int handle_event_data(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *task = NULL; /* If this is the end of a event pair (start is set) */ if (event_data->start) task = handle_end_event(h, event_data, record, pid); /* If this is the start of a event pair (end is set) */ if (event_data->end) task = handle_start_event(h, event_data, record, pid); if (!task) { task = find_task(h, pid); task->proxy = NULL; task->last_start = NULL; task->last_event = NULL; account_task(task, event_data, record); } return 0; } static void handle_missed_events(struct handle_data *h, int cpu) { struct start_data *start; struct start_data *n; /* Clear all starts on this CPU */ list_for_each_entry_safe(start, n, &h->cpu_starts[cpu], list) { free_start(start); } /* Now clear all starts whose events can migrate */ list_for_each_entry_safe(start, n, &h->migrate_starts, list) { free_start(start); } } static int match_event_data(struct trace_hash_item *item, void *data) { struct event_data *event_data = event_data_from_item(item); int id = (int)(unsigned long)data; return event_data->id == id; } static struct event_data * find_event_data(struct handle_data *h, int id) { struct trace_hash_item *item; unsigned long long key = trace_hash(id); void *data = (void *)(unsigned long)id; item = trace_hash_find(&h->events, key, match_event_data, data); if (item) return event_data_from_item(item); return NULL; } int trace_profile_record(struct tracecmd_input *handle, struct pevent_record *record, int cpu) { static struct handle_data *last_handle; struct pevent_record *stack_record; struct event_data *event_data; struct task_data *task; struct handle_data *h; struct pevent *pevent; unsigned long long pid; int id; if (last_handle && last_handle->handle == handle) h = last_handle; else { for (h = handles; h; h = h->next) { if (h->handle == handle) break; } if (!h) die("Handle not found?"); last_handle = h; } if (record->missed_events) handle_missed_events(h, cpu); pevent = h->pevent; id = pevent_data_type(pevent, record); event_data = find_event_data(h, id); if (!event_data) return -1; /* Get this current PID */ pevent_read_number_field(h->common_pid, record->data, &pid); task = find_task(h, pid); stack_record = task->last_stack; if (event_data->handle_event) event_data->handle_event(h, pid, event_data, record, cpu); else handle_event_data(h, pid, event_data, record, cpu); /* If the last stack hasn't changed, free it */ if (stack_record && task->last_stack == stack_record) { free_record(stack_record); task->last_stack = NULL; } return 0; } static struct event_data * add_event(struct handle_data *h, const char *system, const char *event_name, enum event_data_type type) { struct event_format *event; struct event_data *event_data; event = pevent_find_event_by_name(h->pevent, system, event_name); if (!event) return NULL; if (!h->common_pid) { h->common_pid = pevent_find_common_field(event, "common_pid"); if (!h->common_pid) die("No 'common_pid' found in event"); } event_data = malloc_or_die(sizeof(*event_data)); memset(event_data, 0, sizeof(*event_data)); event_data->id = event->id; event_data->event = event; event_data->type = type; event_data->hash.key = trace_hash(event_data->event->id); trace_hash_add(&h->events, &event_data->hash); return event_data; } static void mate_events(struct handle_data *h, struct event_data *start, const char *pid_field, const char *end_match_field, struct event_data *end, const char *start_match_field, int migrate) { start->end = end; end->start = start; if (pid_field) { start->pid_field = pevent_find_field(start->event, pid_field); if (!start->pid_field) die("Event: %s does not have field %s", start->event->name, pid_field); } /* Field to match with end */ start->end_match_field = pevent_find_field(start->event, end_match_field); if (!start->end_match_field) die("Event: %s does not have field %s", start->event->name, end_match_field); /* Field to match with start */ end->start_match_field = pevent_find_field(end->event, start_match_field); if (!end->start_match_field) die("Event: %s does not have field %s", end->event->name, start_match_field); start->migrate = migrate; } /** * tracecmd_mate_events - match events to profile against * @handle: The input handle where the events exist. * @start_event: The event that starts the transaction * @pid_field: Use this over common_pid (may be NULL to use common_pid) * @end_match_field: The field that matches the end events @start_match_field * @end_event: The event that ends the transaction * @start_match_field: The end event field that matches start's @end_match_field * @migrate: Can the transaction switch CPUs? 1 for yes, 0 for no */ void tracecmd_mate_events(struct tracecmd_input *handle, struct event_format *start_event, const char *pid_field, const char *end_match_field, struct event_format *end_event, const char *start_match_field, int migrate) { struct handle_data *h; struct event_data *start; struct event_data *end; for (h = handles; h; h = h->next) { if (h->handle == handle) break; } if (!h) die("Handle not found for trace profile"); start = add_event(h, start_event->system, start_event->name, EVENT_TYPE_USER_MATE); end = add_event(h, end_event->system, end_event->name, EVENT_TYPE_USER_MATE); mate_events(h, start, pid_field, end_match_field, end, start_match_field, migrate); } static void func_print(struct trace_seq *s, struct event_hash *event_hash) { const char *func; func = pevent_find_function(event_hash->event_data->event->pevent, event_hash->val); if (func) trace_seq_printf(s, "func: %s()", func); else trace_seq_printf(s, "func: 0x%llx", event_hash->val); } static void print_int(struct trace_seq *s, struct event_hash *event_hash) { trace_seq_printf(s, "%s:%d", event_hash->event_data->event->name, (int)event_hash->val); } /* From Linux include/linux/interrupt.h */ #define SOFTIRQS \ C(HI), \ C(TIMER), \ C(NET_TX), \ C(NET_RX), \ C(BLOCK), \ C(BLOCK_IOPOLL), \ C(TASKLET), \ C(SCHED), \ C(HRTIMER), \ C(RCU), \ C(NR), #undef C #define C(a) a##_SOFTIRQ enum { SOFTIRQS }; #undef C #define C(a) #a static const char *softirq_map[] = { SOFTIRQS }; static void softirq_print(struct trace_seq *s, struct event_hash *event_hash) { int softirq = (int)event_hash->val; if (softirq < NR_SOFTIRQ) trace_seq_printf(s, "%s:%s", event_hash->event_data->event->name, softirq_map[softirq]); else trace_seq_printf(s, "%s:%d", event_hash->event_data->event->name, softirq); } static void sched_switch_print(struct trace_seq *s, struct event_hash *event_hash) { const char states[] = TASK_STATE_TO_CHAR_STR; int i; trace_seq_printf(s, "%s:", event_hash->event_data->event->name); if (event_hash->val) { int val = event_hash->val; for (i = 0; val && i < sizeof(states) - 1; i++, val >>= 1) { if (val & 1) trace_seq_putc(s, states[i+1]); } } else trace_seq_putc(s, 'R'); } static int handle_sched_switch_event(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *task; unsigned long long prev_pid; unsigned long long prev_state; unsigned long long next_pid; struct start_data *start; /* pid_field holds prev_pid, data_field holds prev_state */ pevent_read_number_field(event_data->pid_field, record->data, &prev_pid); pevent_read_number_field(event_data->data_field, record->data, &prev_state); /* only care about real states */ prev_state &= TASK_STATE_MAX - 1; /* end_match_field holds next_pid */ pevent_read_number_field(event_data->end_match_field, record->data, &next_pid); task = find_task(h, prev_pid); if (!task->comm) add_task_comm(task, h->switch_prev_comm, record); if (prev_state) task->sleeping = 1; else task->sleeping = 0; /* task is being scheduled out. prev_state tells why */ start = add_start(task, event_data, record, prev_pid, prev_state); task->last_start = start; task->last_event = NULL; task = find_task(h, next_pid); if (!task->comm) add_task_comm(task, h->switch_next_comm, record); /* * If the next task was blocked, it required a wakeup to * restart, and there should be one. * But if it was preempted, we look for the previous sched switch. * Unfortunately, we have to look for both types of events as * we do not know why next_pid scheduled out. * * event_data->start holds the sched_wakeup event data. */ find_and_update_start(task, event_data->start, record->ts, next_pid); /* Look for this task if it was preempted (no wakeup found). */ find_and_update_start(task, event_data, record->ts, next_pid); return 0; } static int handle_stacktrace_event(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *orig_task; struct task_data *proxy; struct task_data *task; unsigned long long size; struct event_hash *event_hash; struct start_data *start; void *caller; task = find_task(h, pid); if (task->last_stack) { free_record(task->last_stack); task->last_stack = NULL; } if ((proxy = task->proxy)) { task->proxy = NULL; orig_task = task; task = proxy; } if (!task->last_start && !task->last_event) { /* * Save this stack in case function graph needs it. * Need the original task, not a proxy. */ if (proxy) task = orig_task; tracecmd_record_ref(record); task->last_stack = record; return 0; } /* * start_match_field holds the size. * data_field holds the caller location. */ size = record->size - event_data->data_field->offset; caller = record->data + event_data->data_field->offset; /* * If there's a "start" then don't add the stack until * it finds a matching "end". */ if ((start = task->last_start)) { tracecmd_record_ref(record); start->stack.record = record; start->stack.size = size; start->stack.caller = caller; task->last_start = NULL; task->last_event = NULL; return 0; } event_hash = task->last_event; task->last_event = NULL; add_event_stack(event_hash, caller, size, event_hash->last_time, record->ts); return 0; } static int handle_fgraph_entry_event(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { unsigned long long size; struct start_data *start; struct task_data *task; void *caller; task = handle_start_event(h, event_data, record, pid); /* * If a stack trace hasn't been used for a previous task, * then it could be a function trace that we can use for * the function graph. But stack traces come before the function * graph events (unfortunately). So we need to attach the previous * stack trace (if there is one) to this start event. */ if (task->last_stack) { start = task->last_start; record = task->last_stack; size = record->size - stacktrace_event->data_field->offset; caller = record->data + stacktrace_event->data_field->offset; start->stack.record = record; start->stack.size = size; start->stack.caller = caller; task->last_stack = NULL; task->last_event = NULL; } /* Do not map stacks after this event to this event */ task->last_start = NULL; return 0; } static int handle_fgraph_exit_event(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *task; task = handle_end_event(h, event_data, record, pid); /* Do not match stacks with function graph exit events */ task->last_event = NULL; return 0; } static int handle_process_exec(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *task; unsigned long long val; /* Task has execed, remove the comm for it */ if (event_data->data_field) { pevent_read_number_field(event_data->data_field, record->data, &val); pid = val; } task = find_task(h, pid); free(task->comm); task->comm = NULL; return 0; } static int handle_sched_wakeup_event(struct handle_data *h, unsigned long long pid, struct event_data *event_data, struct pevent_record *record, int cpu) { struct task_data *proxy; struct task_data *task = NULL; struct start_data *start; unsigned long long success; proxy = find_task(h, pid); /* If present, data_field holds "success" */ if (event_data->data_field) { pevent_read_number_field(event_data->data_field, record->data, &success); /* If not a successful wakeup, ignore this */ if (!success) return 0; } pevent_read_number_field(event_data->pid_field, record->data, &pid); task = find_task(h, pid); if (!task->comm) add_task_comm(task, h->wakeup_comm, record); /* if the task isn't sleeping, then ignore the wake up */ if (!task->sleeping) return 0; /* It's being woken up */ task->sleeping = 0; /* * We need the stack trace to be hooked to the woken up * task, not the waker. */ proxy->proxy = task; /* There should be a blocked schedule out of this task */ find_and_update_start(task, event_data->start, record->ts, pid); /* Set this up for timing how long the wakeup takes */ start = add_start(task, event_data, record, pid, pid); task->last_event = NULL; task->last_start = start; return 0; } void trace_init_profile(struct tracecmd_input *handle, struct hook_list *hook) { struct pevent *pevent = tracecmd_get_pevent(handle); struct event_format **events; struct format_field **fields; struct handle_data *h; struct event_data *event_data; struct event_data *sched_switch; struct event_data *sched_wakeup; struct event_data *irq_entry; struct event_data *irq_exit; struct event_data *softirq_entry; struct event_data *softirq_exit; struct event_data *softirq_raise; struct event_data *fgraph_entry; struct event_data *fgraph_exit; struct event_data *syscall_enter; struct event_data *syscall_exit; struct event_data *process_exec; struct event_data *start_event; struct event_data *end_event; int i; h = malloc_or_die(sizeof(*h)); memset(h, 0, sizeof(*h)); h->next = handles; handles = h; trace_hash_init(&h->task_hash, 1024); trace_hash_init(&h->events, 1024); h->handle = handle; h->pevent = pevent; h->cpus = tracecmd_cpus(handle); /* * For streaming profiling, cpus will not be set up yet. * In this case, we simply use the number of cpus on the * system. */ if (!h->cpus) h->cpus = count_cpus(); list_head_init(&h->migrate_starts); h->cpu_starts = malloc_or_die(sizeof(*h->cpu_starts) * h->cpus); for (i = 0; i < h->cpus; i++) list_head_init(&h->cpu_starts[i]); h->cpu_data = malloc_or_die(h->cpus * sizeof(*h->cpu_data)); memset(h->cpu_data, 0, h->cpus * sizeof(h->cpu_data)); irq_entry = add_event(h, "irq", "irq_handler_entry", EVENT_TYPE_IRQ); irq_exit = add_event(h, "irq", "irq_handler_exit", EVENT_TYPE_IRQ); softirq_entry = add_event(h, "irq", "softirq_entry", EVENT_TYPE_SOFTIRQ); softirq_exit = add_event(h, "irq", "softirq_exit", EVENT_TYPE_SOFTIRQ); softirq_raise = add_event(h, "irq", "softirq_raise", EVENT_TYPE_SOFTIRQ_RAISE); sched_wakeup = add_event(h, "sched", "sched_wakeup", EVENT_TYPE_WAKEUP); sched_switch = add_event(h, "sched", "sched_switch", EVENT_TYPE_SCHED_SWITCH); fgraph_entry = add_event(h, "ftrace", "funcgraph_entry", EVENT_TYPE_FUNC); fgraph_exit = add_event(h, "ftrace", "funcgraph_exit", EVENT_TYPE_FUNC); syscall_enter = add_event(h, "raw_syscalls", "sys_enter", EVENT_TYPE_SYSCALL); syscall_exit = add_event(h, "raw_syscalls", "sys_exit", EVENT_TYPE_SYSCALL); process_exec = add_event(h, "sched", "sched_process_exec", EVENT_TYPE_PROCESS_EXEC); stacktrace_event = add_event(h, "ftrace", "kernel_stack", EVENT_TYPE_STACK); if (stacktrace_event) { stacktrace_event->handle_event = handle_stacktrace_event; stacktrace_event->data_field = pevent_find_field(stacktrace_event->event, "caller"); if (!stacktrace_event->data_field) die("Event: %s does not have field caller", stacktrace_event->event->name); } if (process_exec) { process_exec->handle_event = handle_process_exec; process_exec->data_field = pevent_find_field(process_exec->event, "old_pid"); } if (sched_switch) { sched_switch->handle_event = handle_sched_switch_event; sched_switch->data_field = pevent_find_field(sched_switch->event, "prev_state"); if (!sched_switch->data_field) die("Event: %s does not have field prev_state", sched_switch->event->name); h->switch_prev_comm = pevent_find_field(sched_switch->event, "prev_comm"); if (!h->switch_prev_comm) die("Event: %s does not have field prev_comm", sched_switch->event->name); h->switch_next_comm = pevent_find_field(sched_switch->event, "next_comm"); if (!h->switch_next_comm) die("Event: %s does not have field next_comm", sched_switch->event->name); sched_switch->print_func = sched_switch_print; } if (sched_switch && sched_wakeup) { mate_events(h, sched_switch, "prev_pid", "next_pid", sched_wakeup, "pid", 1); mate_events(h, sched_wakeup, "pid", "pid", sched_switch, "prev_pid", 1); sched_wakeup->handle_event = handle_sched_wakeup_event; /* The 'success' field may or may not be present */ sched_wakeup->data_field = pevent_find_field(sched_wakeup->event, "success"); h->wakeup_comm = pevent_find_field(sched_wakeup->event, "comm"); if (!h->wakeup_comm) die("Event: %s does not have field comm", sched_wakeup->event->name); } if (irq_entry && irq_exit) mate_events(h, irq_entry, NULL, "irq", irq_exit, "irq", 0); if (softirq_entry) softirq_entry->print_func = softirq_print; if (softirq_exit) softirq_exit->print_func = softirq_print; if (softirq_raise) softirq_raise->print_func = softirq_print; if (softirq_entry && softirq_exit) mate_events(h, softirq_entry, NULL, "vec", softirq_exit, "vec", 0); if (softirq_entry && softirq_raise) mate_events(h, softirq_raise, NULL, "vec", softirq_entry, "vec", 0); if (fgraph_entry && fgraph_exit) { mate_events(h, fgraph_entry, NULL, "func", fgraph_exit, "func", 1); fgraph_entry->handle_event = handle_fgraph_entry_event; fgraph_exit->handle_event = handle_fgraph_exit_event; fgraph_entry->print_func = func_print; } if (syscall_enter && syscall_exit) { mate_events(h, syscall_enter, NULL, "id", syscall_exit, "id", 1); syscall_enter->print_func = print_int; syscall_exit->print_func = print_int; } events = pevent_list_events(pevent, EVENT_SORT_ID); if (!events) die("malloc"); /* Add some other events */ event_data = add_event(h, "ftrace", "function", EVENT_TYPE_FUNC); if (event_data) { event_data->data_field = pevent_find_field(event_data->event, "ip"); } /* Add any user defined hooks */ for (; hook; hook = hook->next) { start_event = add_event(h, hook->start_system, hook->start_event, EVENT_TYPE_USER_MATE); end_event = add_event(h, hook->end_system, hook->end_event, EVENT_TYPE_USER_MATE); if (!start_event) { warning("Event %s not found", hook->start_event); continue; } if (!end_event) { warning("Event %s not found", hook->end_event); continue; } mate_events(h, start_event, hook->pid, hook->start_match, end_event, hook->end_match, hook->migrate); } /* Now add any defined event that we haven't processed */ for (i = 0; events[i]; i++) { event_data = find_event_data(h, events[i]->id); if (event_data) continue; event_data = add_event(h, events[i]->system, events[i]->name, EVENT_TYPE_UNDEFINED); fields = pevent_event_fields(events[i]); if (!fields) die("malloc"); if (fields[0]) event_data->data_field = fields[0]; free(fields); } } static void output_event_stack(struct pevent *pevent, struct stack_data *stack) { int longsize = pevent_get_long_size(pevent); unsigned long long val; const char *func; unsigned long long stop = -1ULL; void *ptr; int i; if (longsize < 8) stop &= (1ULL << (longsize * 8)) - 1; if (stack->count) stack->time_avg = stack->time / stack->count; printf(" %lld total:%lld min:%lld(ts:%lld.%06lld) max:%lld(ts:%lld.%06lld) avg=%lld\n", stack->count, stack->time, stack->time_min, nsecs_per_sec(stack->ts_min), mod_to_usec(stack->ts_min), stack->time_max, nsecs_per_sec(stack->ts_max), mod_to_usec(stack->ts_max), stack->time_avg); for (i = 0; i < stack->size; i += longsize) { ptr = stack->caller + i; switch (longsize) { case 4: /* todo, read value from pevent */ val = *(unsigned int *)ptr; break; case 8: val = *(unsigned long long *)ptr; break; default: die("Strange long size %d", longsize); } if (val == stop) break; func = pevent_find_function(pevent, val); if (func) printf(" => %s (0x%llx)\n", func, val); else printf(" => 0x%llx\n", val); } } struct stack_chain { struct stack_chain *children; unsigned long long val; unsigned long long time; unsigned long long time_min; unsigned long long ts_min; unsigned long long time_max; unsigned long long ts_max; unsigned long long time_avg; unsigned long long count; int percent; int nr_children; }; static int compare_chains(const void *a, const void *b) { const struct stack_chain * A = a; const struct stack_chain * B = b; if (A->time > B->time) return -1; if (A->time < B->time) return 1; /* If stacks don't use time, then use count */ if (A->count > B->count) return -1; if (A->count < B->count) return 1; return 0; } static int calc_percent(unsigned long long val, unsigned long long total) { return (val * 100 + total / 2) / total; } static int stack_overflows(struct stack_data *stack, int longsize, int level) { return longsize * level > stack->size - longsize; } static unsigned long long stack_value(struct stack_data *stack, int longsize, int level) { void *ptr; ptr = &stack->caller[longsize * level]; return longsize == 8 ? *(u64 *)ptr : *(unsigned *)ptr; } static struct stack_chain * make_stack_chain(struct stack_data **stacks, int cnt, int longsize, int level, int *nr_children) { struct stack_chain *chain; unsigned long long total_time = 0; unsigned long long total_count = 0; unsigned long long time; unsigned long long time_min; unsigned long long ts_min; unsigned long long time_max; unsigned long long ts_max; unsigned long long count; unsigned long long stop = -1ULL; int nr_chains = 0; u64 last = 0; u64 val; int start; int i; int x; if (longsize < 8) stop &= (1ULL << (longsize * 8)) - 1; /* First find out how many diffs there are */ for (i = 0; i < cnt; i++) { if (stack_overflows(stacks[i], longsize, level)) continue; val = stack_value(stacks[i], longsize, level); if (val == stop) continue; if (!nr_chains || val != last) nr_chains++; last = val; } if (!nr_chains) { *nr_children = 0; return NULL; } chain = malloc_or_die(sizeof(*chain) * nr_chains); memset(chain, 0, sizeof(*chain) * nr_chains); x = 0; count = 0; start = 0; time = 0; time_min = 0; time_max = 0; for (i = 0; i < cnt; i++) { if (stack_overflows(stacks[i], longsize, level)) { start = i+1; continue; } val = stack_value(stacks[i], longsize, level); if (val == stop) { start = i+1; continue; } count += stacks[i]->count; time += stacks[i]->time; if (stacks[i]->time_max > time_max) { time_max = stacks[i]->time_max; ts_max = stacks[i]->ts_max; } if (i == start || stacks[i]->time_min < time_min) { time_min = stacks[i]->time_min; ts_min = stacks[i]->ts_min; } if (i == cnt - 1 || stack_overflows(stacks[i+1], longsize, level) || val != stack_value(stacks[i+1], longsize, level)) { total_time += time; total_count += count; chain[x].val = val; chain[x].time_avg = time / count; chain[x].count = count; chain[x].time = time; chain[x].time_min = time_min; chain[x].ts_min = ts_min; chain[x].time_max = time_max; chain[x].ts_max = ts_max; chain[x].children = make_stack_chain(&stacks[start], (i - start) + 1, longsize, level+1, &chain[x].nr_children); x++; start = i + 1; count = 0; time = 0; time_min = 0; time_max = 0; } } qsort(chain, nr_chains, sizeof(*chain), compare_chains); *nr_children = nr_chains; /* Should never happen */ if (!total_time && !total_count) return chain; /* Now calculate percentage */ time = 0; for (i = 0; i < nr_chains; i++) { if (total_time) chain[i].percent = calc_percent(chain[i].time, total_time); /* In case stacks don't have time */ else if (total_count) chain[i].percent = calc_percent(chain[i].count, total_count); } return chain; } static void free_chain(struct stack_chain *chain, int nr_chains) { int i; if (!chain) return; for (i = 0; i < nr_chains; i++) free_chain(chain[i].children, chain[i].nr_children); free(chain); } #define INDENT 5 static void print_indent(int level, unsigned long long mask) { char line; int p; for (p = 0; p < level + 1; p++) { if (mask & (1ULL << p)) line = '|'; else line = ' '; printf("%*c ", INDENT, line); } } static void print_chain_func(struct pevent *pevent, struct stack_chain *chain) { unsigned long long val = chain->val; const char *func; func = pevent_find_function(pevent, val); if (func) printf("%s (0x%llx)\n", func, val); else printf("0x%llx\n", val); } static void output_chain(struct pevent *pevent, struct stack_chain *chain, int level, int nr_chains, unsigned long long *mask) { struct stack_chain *child; int nr_children; int i; char line = '|'; if (!nr_chains) return; *mask |= (1ULL << (level + 1)); print_indent(level + 1, *mask); printf("\n"); for (i = 0; i < nr_chains; i++) { print_indent(level, *mask); printf("%*c ", INDENT, '+'); if (i == nr_chains - 1) { *mask &= ~(1ULL << (level + 1)); line = ' '; } print_chain_func(pevent, &chain[i]); print_indent(level, *mask); printf("%*c ", INDENT, line); printf(" %d%% (%lld)", chain[i].percent, chain[i].count); if (chain[i].time) printf(" time:%lld max:%lld(ts:%lld.%06lld) min:%lld(ts:%lld.%06lld) avg:%lld", chain[i].time, chain[i].time_max, nsecs_per_sec(chain[i].ts_max), mod_to_usec(chain[i].ts_max), chain[i].time_min, nsecs_per_sec(chain[i].ts_min), mod_to_usec(chain[i].ts_min), chain[i].time_avg); printf("\n"); for (child = chain[i].children, nr_children = chain[i].nr_children; child && nr_children == 1; nr_children = child->nr_children, child = child->children) { print_indent(level, *mask); printf("%*c ", INDENT, line); printf(" "); print_chain_func(pevent, child); } if (child) output_chain(pevent, child, level+1, nr_children, mask); print_indent(level + 1, *mask); printf("\n"); } *mask &= ~(1ULL << (level + 1)); print_indent(level, *mask); printf("\n"); } static int compare_stacks(const void *a, const void *b) { struct stack_data * const *A = a; struct stack_data * const *B = b; unsigned int sa, sb; int size; int i; /* only compare up to the smaller size of the two */ if ((*A)->size > (*B)->size) size = (*B)->size; else size = (*A)->size; for (i = 0; i < size; i += sizeof(sa)) { sa = *(unsigned *)&(*A)->caller[i]; sb = *(unsigned *)&(*B)->caller[i]; if (sa > sb) return 1; if (sa < sb) return -1; } /* They are the same up to size. Then bigger size wins */ if ((*A)->size > (*B)->size) return 1; if ((*A)->size < (*B)->size) return -1; return 0; } static void output_stacks(struct pevent *pevent, struct trace_hash *stack_hash) { struct trace_hash_item **bucket; struct trace_hash_item *item; struct stack_data **stacks; struct stack_chain *chain; unsigned long long mask = 0; int nr_chains; int longsize = pevent_get_long_size(pevent); int nr_stacks; int i; nr_stacks = 0; trace_hash_for_each_bucket(bucket, stack_hash) { trace_hash_for_each_item(item, bucket) { nr_stacks++; } } stacks = malloc_or_die(sizeof(*stacks) * nr_stacks); nr_stacks = 0; trace_hash_for_each_bucket(bucket, stack_hash) { trace_hash_for_each_item(item, bucket) { stacks[nr_stacks++] = stack_from_item(item); } } qsort(stacks, nr_stacks, sizeof(*stacks), compare_stacks); chain = make_stack_chain(stacks, nr_stacks, longsize, 0, &nr_chains); output_chain(pevent, chain, 0, nr_chains, &mask); if (0) for (i = 0; i < nr_stacks; i++) output_event_stack(pevent, stacks[i]); free(stacks); free_chain(chain, nr_chains); } static void output_event(struct event_hash *event_hash) { struct event_data *event_data = event_hash->event_data; struct pevent *pevent = event_data->event->pevent; struct trace_seq s; trace_seq_init(&s); if (event_data->print_func) event_data->print_func(&s, event_hash); else if (event_data->type == EVENT_TYPE_FUNC) func_print(&s, event_hash); else trace_seq_printf(&s, "%s:0x%llx", event_data->event->name, event_hash->val); trace_seq_terminate(&s); printf(" Event: %s (%lld)", s.buffer, event_hash->count); trace_seq_destroy(&s); if (event_hash->time_total) { event_hash->time_avg = event_hash->time_total / event_hash->count; printf(" Total: %lld Avg: %lld Max: %lld(ts:%lld.%06lld) Min:%lld(ts:%lld.%06lld)", event_hash->time_total, event_hash->time_avg, event_hash->time_max, nsecs_per_sec(event_hash->ts_max), mod_to_usec(event_hash->ts_max), event_hash->time_min, nsecs_per_sec(event_hash->ts_min), mod_to_usec(event_hash->ts_min)); } printf("\n"); output_stacks(pevent, &event_hash->stacks); } static int compare_events(const void *a, const void *b) { struct event_hash * const *A = a; struct event_hash * const *B = b; const struct event_data *event_data_a = (*A)->event_data; const struct event_data *event_data_b = (*B)->event_data; /* Schedule switch goes first */ if (event_data_a->type == EVENT_TYPE_SCHED_SWITCH) { if (event_data_b->type != EVENT_TYPE_SCHED_SWITCH) return -1; /* lower the state the better */ if ((*A)->val > (*B)->val) return 1; if ((*A)->val < (*B)->val) return -1; return 0; } else if (event_data_b->type == EVENT_TYPE_SCHED_SWITCH) return 1; /* Wakeups are next */ if (event_data_a->type == EVENT_TYPE_WAKEUP) { if (event_data_b->type != EVENT_TYPE_WAKEUP) return -1; return 0; } else if (event_data_b->type == EVENT_TYPE_WAKEUP) return 1; if (event_data_a->id > event_data_b->id) return 1; if (event_data_a->id < event_data_b->id) return -1; return 0; } static void output_task(struct handle_data *h, struct task_data *task) { struct trace_hash_item **bucket; struct trace_hash_item *item; struct event_hash **events; const char *comm; int nr_events = 0; int i; if (task->comm) comm = task->comm; else comm = pevent_data_comm_from_pid(h->pevent, task->pid); printf("\ntask: %s-%d\n", comm, task->pid); trace_hash_for_each_bucket(bucket, &task->event_hash) { trace_hash_for_each_item(item, bucket) { nr_events++; } } events = malloc_or_die(sizeof(*events) * nr_events); i = 0; trace_hash_for_each_bucket(bucket, &task->event_hash) { trace_hash_for_each_item(item, bucket) { events[i++] = event_from_item(item); } } qsort(events, nr_events, sizeof(*events), compare_events); for (i = 0; i < nr_events; i++) output_event(events[i]); free(events); } static int compare_tasks(const void *a, const void *b) { struct task_data * const *A = a; struct task_data * const *B = b; if ((*A)->pid > (*B)->pid) return 1; else if ((*A)->pid < (*B)->pid) return -1; return 0; } static void free_event_hash(struct event_hash *event_hash) { struct trace_hash_item **bucket; struct trace_hash_item *item; struct stack_data *stack; trace_hash_for_each_bucket(bucket, &event_hash->stacks) { trace_hash_while_item(item, bucket) { stack = stack_from_item(item); trace_hash_del(&stack->hash); free(stack); } } trace_hash_free(&event_hash->stacks); free(event_hash); } static void free_task(struct task_data *task) { struct trace_hash_item **bucket; struct trace_hash_item *item; struct start_data *start; struct event_hash *event_hash; free(task->comm); trace_hash_for_each_bucket(bucket, &task->start_hash) { trace_hash_while_item(item, bucket) { start = start_from_item(item); if (start->stack.record) free_record(start->stack.record); list_del(&start->list); trace_hash_del(item); free(start); } } trace_hash_free(&task->start_hash); trace_hash_for_each_bucket(bucket, &task->event_hash) { trace_hash_while_item(item, bucket) { event_hash = event_from_item(item); trace_hash_del(item); free_event_hash(event_hash); } } trace_hash_free(&task->event_hash); if (task->last_stack) free_record(task->last_stack); free(task); } static void output_handle(struct handle_data *h) { struct trace_hash_item **bucket; struct trace_hash_item *item; struct task_data **tasks; int nr_tasks = 0; int i; trace_hash_for_each_bucket(bucket, &h->task_hash) { trace_hash_for_each_item(item, bucket) { nr_tasks++; } } tasks = malloc_or_die(sizeof(*tasks) * nr_tasks); nr_tasks = 0; trace_hash_for_each_bucket(bucket, &h->task_hash) { trace_hash_while_item(item, bucket) { tasks[nr_tasks++] = task_from_item(item); trace_hash_del(item); } } qsort(tasks, nr_tasks, sizeof(*tasks), compare_tasks); for (i = 0; i < nr_tasks; i++) { output_task(h, tasks[i]); free_task(tasks[i]); } free(tasks); } int trace_profile(void) { struct handle_data *h; for (h = handles; h; h = h->next) { output_handle(h); trace_hash_free(&h->task_hash); } return 0; } trace-cmd-2.5.3/trace-read.c000066400000000000000000001047521246701203100155400ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-local.h" #include "trace-hash.h" #include "list.h" static struct filter_str { struct filter_str *next; char *filter; int neg; } *filter_strings; static struct filter_str **filter_next = &filter_strings; struct filter { struct filter *next; struct event_filter *filter; }; struct event_str { struct event_str *next; const char *event; }; struct handle_list { struct list_head list; struct tracecmd_input *handle; const char *file; int cpus; int done; struct pevent_record *record; struct filter *event_filters; struct filter *event_filter_out; }; static struct list_head handle_list; struct input_files { struct list_head list; const char *file; }; static struct list_head input_files; struct pid_list { struct pid_list *next; char *pid; int free; } *pid_list; struct pid_list *comm_list; static unsigned int page_size; static int input_fd; static const char *default_input_file = "trace.dat"; static const char *input_file; static int multi_inputs; static int max_file_size; static int instances; static int *filter_cpus; static int nr_filter_cpus; static int show_wakeup; static int wakeup_id; static int wakeup_new_id; static int sched_id; static int stacktrace_id; static int profile; static int buffer_breaks = 0; static struct format_field *wakeup_task; static struct format_field *wakeup_success; static struct format_field *wakeup_new_task; static struct format_field *wakeup_new_success; static struct format_field *sched_task; static struct format_field *sched_prio; static unsigned long long total_wakeup_lat; static unsigned long wakeup_lat_count; static unsigned long long total_wakeup_rt_lat; static unsigned long wakeup_rt_lat_count; struct wakeup_info { struct trace_hash_item hash; unsigned long long start; int pid; }; static struct hook_list *hooks; static struct hook_list *last_hook; #define WAKEUP_HASH_SIZE 1024 static struct trace_hash wakeup_hash; /* Debug variables for testing tracecmd_read_at */ #define TEST_READ_AT 0 #if TEST_READ_AT #define DO_TEST static off64_t test_read_at_offset; static int test_read_at_copy = 100; static int test_read_at_index; static void show_test(struct tracecmd_input *handle) { struct pevent *pevent; struct pevent_record *record; struct trace_seq s; int cpu; if (!test_read_at_offset) { printf("\nNO RECORD COPIED\n"); return; } pevent = tracecmd_get_pevent(handle); record = tracecmd_read_at(handle, test_read_at_offset, &cpu); printf("\nHERE'S THE COPY RECORD\n"); trace_seq_init(&s); pevent_print_event(pevent, &s, cpu, record->data, record->size, record->ts); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf("\n"); free_record(record); } static void test_save(struct pevent_record *record, int cpu) { if (test_read_at_index++ == test_read_at_copy) { test_read_at_offset = record->offset; printf("\nUSING THIS RECORD\n"); } } #endif /* TEST_READ_AT */ /* Debug variables for testing tracecmd_set_cpu_at_timestamp */ #define TEST_AT_TIMESTAMP 0 #if TEST_AT_TIMESTAMP #define DO_TEST static unsigned long long test_at_timestamp_ts; static int test_at_timestamp_copy = 100; static int test_at_timestamp_cpu = -1; static int test_at_timestamp_index; static void show_test(struct tracecmd_input *handle) { struct pevent *pevent; struct pevent_record *record; struct trace_seq s; int cpu = test_at_timestamp_cpu; if (!test_at_timestamp_ts) { printf("\nNO RECORD COPIED\n"); return; } pevent = tracecmd_get_pevent(handle); if (tracecmd_set_cpu_to_timestamp(handle, cpu, test_at_timestamp_ts)) return; record = tracecmd_read_data(handle, cpu); printf("\nHERE'S THE COPY RECORD with page %p offset=%p\n", (void *)(record->offset & ~(page_size - 1)), (void *)record->offset); trace_seq_init(&s); pevent_print_event(pevent, &s, cpu, record->data, record->size, record->ts); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf("\n"); free_record(record); } static void test_save(struct pevent_record *record, int cpu) { if (test_at_timestamp_index++ == test_at_timestamp_copy) { test_at_timestamp_ts = record->ts; test_at_timestamp_cpu = cpu; printf("\nUSING THIS RECORD page=%p offset=%p\n", (void *)(record->offset & ~(page_size - 1)), (void *)record->offset); } } #endif /* TEST_AT_TIMESTAMP */ #define TEST_FIRST_LAST 0 #if TEST_FIRST_LAST #define DO_TEST static void show_test(struct tracecmd_input *handle) { struct pevent *pevent; struct pevent_record *record; struct trace_seq s; int cpu = 0; pevent = tracecmd_get_pevent(handle); record = tracecmd_read_cpu_first(handle, cpu); if (!record) { printf("No first record?\n"); return; } printf("\nHERE'S THE FIRST RECORD with offset %p\n", (void *)record->offset); trace_seq_init(&s); pevent_print_event(pevent, &s, cpu, record->data, record->size, record->ts); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf("\n"); free_record(record); record = tracecmd_read_cpu_last(handle, cpu); if (!record) { printf("No last record?\n"); return; } printf("\nHERE'S THE LAST RECORD with offset %p\n", (void *)record->offset); trace_seq_init(&s); pevent_print_event(pevent, &s, cpu, record->data, record->size, record->ts); trace_seq_do_printf(&s); trace_seq_destroy(&s); printf("\n"); free_record(record); } static void test_save(struct pevent_record *record, int cpu) { } #endif /* TEST_FIRST_LAST */ #ifndef DO_TEST static void show_test(struct tracecmd_input *handle) { } static void test_save(struct pevent_record *record, int cpu) { } #endif static void add_input(const char *file) { struct input_files *item; item = malloc_or_die(sizeof(*item)); item->file = file; list_add_tail(&item->list, &input_files); } static void add_handle(struct tracecmd_input *handle, const char *file) { struct handle_list *item; item = malloc_or_die(sizeof(*item)); memset(item, 0, sizeof(*item)); item->handle = handle; if (file) { item->file = file + strlen(file); /* we want just the base name */ while (item->file >= file && *item->file != '/') item->file--; item->file++; if (strlen(item->file) > max_file_size) max_file_size = strlen(item->file); } list_add_tail(&item->list, &handle_list); } static void free_inputs(void) { struct input_files *item; while (!list_empty(&input_files)) { item = container_of(input_files.next, struct input_files, list); list_del(&item->list); free(item); } } static void free_handles(void) { struct handle_list *item; while (!list_empty(&handle_list)) { item = container_of(handle_list.next, struct handle_list, list); list_del(&item->list); free(item); } } static void add_filter(const char *filter, int neg) { struct filter_str *ftr; ftr = malloc_or_die(sizeof(*ftr)); ftr->filter = strdup(filter); if (!ftr->filter) die("malloc"); ftr->next = NULL; ftr->neg = neg; /* must maintain order of command line */ *filter_next = ftr; filter_next = &ftr->next; } static void __add_filter(struct pid_list **head, const char *arg) { struct pid_list *list; char *pids = strdup(arg); char *pid; char *sav; int free = 1; if (!pids) die("malloc"); pid = strtok_r(pids, ",", &sav); while (pid) { list = malloc_or_die(sizeof(*list)); list->pid = pid; list->free = free; list->next = *head; *head = list; /* The first pid needs to be freed */ free = 0; pid = strtok_r(NULL, ",", &sav); } } static void add_comm_filter(const char *arg) { __add_filter(&comm_list, arg); } static void add_pid_filter(const char *arg) { __add_filter(&pid_list, arg); } static char *append_pid_filter(char *curr_filter, char *pid) { char *filter; int len; #define FILTER_FMT "(common_pid==" __STR ")||(pid==" __STR ")||(next_pid==" __STR ")" #undef __STR #define __STR "" /* strlen(".*:") > strlen("||") */ len = strlen(".*:" FILTER_FMT) + strlen(pid) * 3 + 1; #undef __STR #define __STR "%s" if (!curr_filter) { filter = malloc_or_die(len); sprintf(filter, ".*:" FILTER_FMT, pid, pid, pid); } else { len += strlen(curr_filter); filter = realloc(curr_filter, len); if (!filter) die("realloc"); sprintf(filter, "%s||" FILTER_FMT, filter, pid, pid, pid); } return filter; } static void convert_comm_filter(struct tracecmd_input *handle) { struct pevent *pevent; struct pid_list *list; struct cmdline *cmdline; char pidstr[100]; if (!comm_list) return; pevent = tracecmd_get_pevent(handle); /* Seach for comm names and get their pids */ for (list = comm_list; list; list = list->next) { cmdline = pevent_data_pid_from_comm(pevent, list->pid, NULL); if (!cmdline) { warning("comm: %s not in cmdline list", list->pid); continue; } do { sprintf(pidstr, "%d", pevent_cmdline_pid(pevent, cmdline)); add_pid_filter(pidstr); cmdline = pevent_data_pid_from_comm(pevent, list->pid, cmdline); } while (cmdline); } while (comm_list) { list = comm_list; comm_list = comm_list->next; if (list->free) free(list->pid); free(list); } } static void make_pid_filter(struct tracecmd_input *handle) { struct pid_list *list; char *str = NULL; convert_comm_filter(handle); if (!pid_list) return; /* First do all common pids */ for (list = pid_list; list; list = list->next) { str = append_pid_filter(str, list->pid); } add_filter(str, 0); free(str); while (pid_list) { list = pid_list; pid_list = pid_list->next; if (list->free) free(list->pid); free(list); } } static void process_filters(struct handle_list *handles) { struct filter **filter_next = &handles->event_filters; struct filter **filter_out_next = &handles->event_filter_out; struct filter *event_filter; struct filter_str *filter; struct pevent *pevent; char errstr[200]; int ret; pevent = tracecmd_get_pevent(handles->handle); make_pid_filter(handles->handle); while (filter_strings) { filter = filter_strings; filter_strings = filter->next; event_filter = malloc_or_die(sizeof(*event_filter)); event_filter->next = NULL; event_filter->filter = pevent_filter_alloc(pevent); if (!event_filter->filter) die("malloc"); ret = pevent_filter_add_filter_str(event_filter->filter, filter->filter); if (ret < 0) { pevent_strerror(pevent, ret, errstr, sizeof(errstr)); die("Error filtering: %s\n%s", filter->filter, errstr); } if (filter->neg) { *filter_out_next = event_filter; filter_out_next = &event_filter->next; } else { *filter_next = event_filter; filter_next = &event_filter->next; } free(filter->filter); free(filter); } } static void init_wakeup(struct tracecmd_input *handle) { struct event_format *event; struct pevent *pevent; if (!show_wakeup) return; pevent = tracecmd_get_pevent(handle); trace_hash_init(&wakeup_hash, WAKEUP_HASH_SIZE); event = pevent_find_event_by_name(pevent, "sched", "sched_wakeup"); if (!event) goto fail; wakeup_id = event->id; wakeup_task = pevent_find_field(event, "pid"); if (!wakeup_task) goto fail; wakeup_success = pevent_find_field(event, "success"); event = pevent_find_event_by_name(pevent, "sched", "sched_switch"); if (!event) goto fail; sched_id = event->id; sched_task = pevent_find_field(event, "next_pid"); if (!sched_task) goto fail; sched_prio = pevent_find_field(event, "next_prio"); if (!sched_prio) goto fail; wakeup_new_id = -1; event = pevent_find_event_by_name(pevent, "sched", "sched_wakeup_new"); if (!event) goto skip; wakeup_new_id = event->id; wakeup_new_task = pevent_find_field(event, "pid"); if (!wakeup_new_task) goto fail; wakeup_new_success = pevent_find_field(event, "success"); skip: return; fail: show_wakeup = 0; } static void add_wakeup(unsigned int val, unsigned long long start) { unsigned int key = trace_hash(val); struct wakeup_info *info; struct trace_hash_item *item; item = trace_hash_find(&wakeup_hash, key, NULL, NULL); if (item) { info = container_of(item, struct wakeup_info, hash); /* Hmm, double wakeup? */ info->start = start; return; } info = malloc_or_die(sizeof(*info)); info->hash.key = val; info->start = start; trace_hash_add(&wakeup_hash, &info->hash); } static unsigned long long max_lat = 0; static unsigned long long max_time; static unsigned long long min_lat = -1; static unsigned long long min_time; static unsigned long long max_rt_lat = 0; static unsigned long long max_rt_time; static unsigned long long min_rt_lat = -1; static unsigned long long min_rt_time; static void add_sched(unsigned int val, unsigned long long end, int rt) { struct trace_hash_item *item; unsigned int key = trace_hash(val); struct wakeup_info *info; unsigned long long cal; item = trace_hash_find(&wakeup_hash, key, NULL, NULL); if (!item) return; info = container_of(item, struct wakeup_info, hash); cal = end - info->start; if (cal > max_lat) { max_lat = cal; max_time = end; } if (cal < min_lat) { min_lat = cal; min_time = end; } if (rt) { if (cal > max_rt_lat) { max_rt_lat = cal; max_rt_time = end; } if (cal < min_rt_lat) { min_rt_lat = cal; min_rt_time = end; } } printf(" Latency: %llu.%03llu usecs", cal / 1000, cal % 1000); total_wakeup_lat += cal; wakeup_lat_count++; if (rt) { total_wakeup_rt_lat += cal; wakeup_rt_lat_count++; } trace_hash_del(item); free(info); } static void process_wakeup(struct pevent *pevent, struct pevent_record *record) { unsigned long long val; int id; if (!show_wakeup) return; id = pevent_data_type(pevent, record); if (id == wakeup_id) { if (pevent_read_number_field(wakeup_success, record->data, &val) == 0) { if (!val) return; } if (pevent_read_number_field(wakeup_task, record->data, &val)) return; add_wakeup(val, record->ts); } else if (id == wakeup_new_id) { if (pevent_read_number_field(wakeup_new_success, record->data, &val) == 0) { if (!val) return; } if (pevent_read_number_field(wakeup_new_task, record->data, &val)) return; add_wakeup(val, record->ts); } else if (id == sched_id) { int rt = 1; if (pevent_read_number_field(sched_prio, record->data, &val)) return; if (val > 99) rt = 0; if (pevent_read_number_field(sched_task, record->data, &val)) return; add_sched(val, record->ts, rt); } } static void show_wakeup_timings(unsigned long long total, unsigned long count, unsigned long long lat_max, unsigned long long time_max, unsigned long long lat_min, unsigned long long time_min) { total /= count; printf("\nAverage wakeup latency: %llu.%03llu usecs\n", total / 1000, total % 1000); printf("Maximum Latency: %llu.%03llu usecs at ", lat_max / 1000, lat_max % 1000); printf("timestamp: %llu.%06llu\n", time_max / 1000000000, ((time_max + 500) % 1000000000) / 1000); printf("Minimum Latency: %llu.%03llu usecs at ", lat_min / 1000, lat_min % 1000); printf("timestamp: %llu.%06llu\n\n", time_min / 1000000000, ((time_min + 500) % 1000000000) / 1000); } static void finish_wakeup(void) { struct wakeup_info *info; struct trace_hash_item **bucket; struct trace_hash_item *item; if (!show_wakeup || !wakeup_lat_count) return; show_wakeup_timings(total_wakeup_lat, wakeup_lat_count, max_lat, max_time, min_lat, min_time); if (wakeup_rt_lat_count) { printf("RT task timings:\n"); show_wakeup_timings(total_wakeup_rt_lat, wakeup_rt_lat_count, max_rt_lat, max_rt_time, min_rt_lat, min_rt_time); } trace_hash_for_each_bucket(bucket, &wakeup_hash) { trace_hash_while_item(item, bucket) { trace_hash_del(item); info = container_of(item, struct wakeup_info, hash); free(info); } } trace_hash_free(&wakeup_hash); } void trace_show_data(struct tracecmd_input *handle, struct pevent_record *record, int profile) { struct pevent *pevent; struct trace_seq s; int cpu = record->cpu; bool use_trace_clock; pevent = tracecmd_get_pevent(handle); test_save(record, cpu); if (profile) { trace_profile_record(handle, record, cpu); return; } trace_seq_init(&s); if (record->missed_events > 0) trace_seq_printf(&s, "CPU:%d [%lld EVENTS DROPPED]\n", cpu, record->missed_events); else if (record->missed_events < 0) trace_seq_printf(&s, "CPU:%d [EVENTS DROPPED]\n", cpu); if (buffer_breaks && tracecmd_record_at_buffer_start(handle, record)) trace_seq_printf(&s, "CPU:%d [SUBBUFFER START]\n", cpu); use_trace_clock = tracecmd_get_use_trace_clock(handle); pevent_print_event(pevent, &s, record, use_trace_clock); if (s.len && *(s.buffer + s.len - 1) == '\n') s.len--; trace_seq_do_printf(&s); trace_seq_destroy(&s); process_wakeup(pevent, record); printf("\n"); } static void read_rest(void) { char buf[BUFSIZ + 1]; int r; do { r = read(input_fd, buf, BUFSIZ); if (r > 0) { buf[r] = 0; printf("%s", buf); } } while (r > 0); } static int test_filters(struct filter *event_filters, struct pevent_record *record, int neg) { int found = 0; int ret = FILTER_NONE; while (event_filters) { ret = pevent_filter_match(event_filters->filter, record); switch (ret) { case FILTER_NONE: case FILTER_MATCH: found = 1; } /* We need to test all negative filters */ if (!neg && found) break; event_filters = event_filters->next; } return ret; } struct stack_info_cpu { int cpu; int last_printed; }; struct stack_info { struct stack_info *next; struct handle_list *handles; struct stack_info_cpu *cpus; int stacktrace_id; int nr_cpus; }; static int test_stacktrace(struct handle_list *handles, struct pevent_record *record, int last_printed) { static struct stack_info *infos; struct stack_info *info; struct stack_info_cpu *cpu_info; struct handle_list *h; struct tracecmd_input *handle; struct event_format *event; struct pevent *pevent; static int init; int ret; int id; if (!init) { init = 1; list_for_each_entry(h, &handle_list, list) { info = malloc_or_die(sizeof(*info)); info->handles = h; info->nr_cpus = tracecmd_cpus(h->handle); info->cpus = malloc_or_die(sizeof(*info->cpus) * info->nr_cpus); memset(info->cpus, 0, sizeof(*info->cpus)); pevent = tracecmd_get_pevent(h->handle); event = pevent_find_event_by_name(pevent, "ftrace", "kernel_stack"); if (event) info->stacktrace_id = event->id; else info->stacktrace_id = 0; info->next = infos; infos = info; } } handle = handles->handle; pevent = tracecmd_get_pevent(handle); for (info = infos; info; info = info->next) if (info->handles == handles) break; if (!info->stacktrace_id) return 0; cpu_info = &info->cpus[record->cpu]; id = pevent_data_type(pevent, record); /* * Print the stack trace if the previous event was printed. * But do not print the stack trace if it is explicitly * being filtered out. */ if (id == info->stacktrace_id) { ret = test_filters(handles->event_filter_out, record, 1); if (ret != FILTER_MATCH) return cpu_info->last_printed; return 0; } cpu_info->last_printed = last_printed; return 0; } static struct pevent_record *get_next_record(struct handle_list *handles) { struct pevent_record *record; int found = 0; int cpu; int ret; if (handles->record) return handles->record; if (handles->done) return NULL; do { if (filter_cpus) { long long last_stamp = -1; struct pevent_record *precord; int first_record = 1; int next_cpu = -1; int i; for (i = 0; (cpu = filter_cpus[i]) >= 0; i++) { precord = tracecmd_peek_data(handles->handle, cpu); if (precord && (first_record || precord->ts < last_stamp)) { next_cpu = cpu; last_stamp = precord->ts; first_record = 0; } } if (!first_record) record = tracecmd_read_data(handles->handle, next_cpu); else record = NULL; } else record = tracecmd_read_next_data(handles->handle, &cpu); if (record) { ret = test_filters(handles->event_filters, record, 0); switch (ret) { case FILTER_NOEXIST: /* Stack traces may still filter this */ if (stacktrace_id && test_stacktrace(handles, record, 0)) found = 1; else free_record(record); break; case FILTER_NONE: case FILTER_MATCH: /* Test the negative filters (-v) */ ret = test_filters(handles->event_filter_out, record, 1); if (ret != FILTER_MATCH) { found = 1; break; } /* fall through */ default: free_record(record); } } } while (record && !found); if (record && stacktrace_id) test_stacktrace(handles, record, 1); handles->record = record; if (!record) handles->done = 1; return record; } static void free_handle_record(struct handle_list *handles) { if (!handles->record) return; free_record(handles->record); handles->record = NULL; } static void print_handle_file(struct handle_list *handles) { /* Only print file names if more than one file is read */ if (!multi_inputs && !instances) return; if (handles->file) printf("%*s: ", max_file_size, handles->file); else printf("%*s ", max_file_size, ""); } static void free_filters(struct filter *event_filter) { struct filter *filter; while (event_filter) { filter = event_filter; event_filter = filter->next; pevent_filter_free(filter->filter); free(filter); } } enum output_type { OUTPUT_NORMAL, OUTPUT_STAT_ONLY, OUTPUT_UNAME_ONLY, }; static void read_data_info(struct list_head *handle_list, enum output_type otype) { struct handle_list *handles; struct handle_list *last_handle; struct pevent_record *record; struct pevent_record *last_record; struct event_format *event; struct pevent *pevent; int cpus; int ret; list_for_each_entry(handles, handle_list, list) { /* Don't process instances that we added here */ if (tracecmd_is_buffer_instance(handles->handle)) continue; ret = tracecmd_init_data(handles->handle); if (ret < 0) die("failed to init data"); cpus = tracecmd_cpus(handles->handle); handles->cpus = cpus; print_handle_file(handles); printf("cpus=%d\n", cpus); /* Latency trace is just all ASCII */ if (ret > 0) { if (multi_inputs) die("latency traces do not work with multiple inputs"); read_rest(); return; } switch (otype) { case OUTPUT_NORMAL: break; case OUTPUT_STAT_ONLY: printf("\nKernel buffer statistics:\n" " Note: \"entries\" are the entries left in the kernel ring buffer and are not\n" " recorded in the trace data. They should all be zero.\n\n"); tracecmd_print_stats(handles->handle); continue; case OUTPUT_UNAME_ONLY: tracecmd_print_uname(handles->handle); continue; } /* Find the kernel_stacktrace if available */ pevent = tracecmd_get_pevent(handles->handle); event = pevent_find_event_by_name(pevent, "ftrace", "kernel_stack"); if (event) stacktrace_id = event->id; init_wakeup(handles->handle); if (last_hook) last_hook->next = tracecmd_hooks(handles->handle); trace_init_profile(handles->handle, hooks); process_filters(handles); /* If this file has buffer instances, get the handles for them */ instances = tracecmd_buffer_instances(handles->handle); if (instances) { struct tracecmd_input *new_handle; const char *name; int i; for (i = 0; i < instances; i++) { name = tracecmd_buffer_instance_name(handles->handle, i); if (!name) die("error in reading buffer instance"); new_handle = tracecmd_buffer_instance_handle(handles->handle, i); if (!new_handle) { warning("could not retreive handle %s", name); continue; } add_handle(new_handle, name); } } } if (otype != OUTPUT_NORMAL) return; do { last_handle = NULL; last_record = NULL; list_for_each_entry(handles, handle_list, list) { record = get_next_record(handles); if (!last_record || (record && record->ts < last_record->ts)) { last_record = record; last_handle = handles; } } if (last_record) { print_handle_file(last_handle); trace_show_data(last_handle->handle, last_record, profile); free_handle_record(last_handle); } } while (last_record); if (profile) trace_profile(); list_for_each_entry(handles, handle_list, list) { free_filters(handles->event_filters); free_filters(handles->event_filter_out); show_test(handles->handle); } } struct tracecmd_input *read_trace_header(const char *file) { input_fd = open(file, O_RDONLY); if (input_fd < 0) die("opening '%s'\n", file); return tracecmd_alloc_fd(input_fd); } static void sig_end(int sig) { fprintf(stderr, "trace-cmd: Received SIGINT\n"); exit(0); } static const char *skip_space_and_test_digit(const char *p, const char *cpu_str) { while (isspace(*p)) p++; if (!isdigit(*p)) die("invalid character '%c' in cpu string '%s'", *p, cpu_str); return p; } static void __add_cpu(int cpu) { filter_cpus = tracecmd_add_id(filter_cpus, cpu, nr_filter_cpus++); } static void parse_cpulist(const char *cpu_str) { unsigned a, b; const char *s = cpu_str; do { s = skip_space_and_test_digit(s, cpu_str); b = a = strtoul(s, (char **)&s, 10); if (*s == '-') { s = skip_space_and_test_digit(s + 1, cpu_str); b = strtoul(s, (char **)&s, 10); } if (!(a <= b)) die("range of cpu numbers must be lower to greater"); while (a <= b) { __add_cpu(a); a++; } if (*s == ',' || *s == ':') s++; } while (*s != '\0'); } static void read_file_fd(int fd, char *dst, int len) { size_t size = 0; int r; do { r = read(fd, dst+size, len); if (r > 0) { size += r; len -= r; } } while (r > 0); } static void add_functions(struct pevent *pevent, const char *file) { struct stat st; char *buf; int ret; int fd; fd = open(file, O_RDONLY); if (fd < 0) die("Can't read file %s", file); ret = fstat(fd, &st); if (ret < 0) die("Can't stat file %s", file); buf = malloc_or_die(st.st_size); read_file_fd(fd, buf, st.st_size); close(fd); parse_proc_kallsyms(pevent, buf, st.st_size); free(buf); } static void process_plugin_option(char *option) { char *name = option; char *val = NULL; char *p; if ((p = strstr(name, "="))) { *p = '\0'; val = p+1; } trace_util_add_option(name, val); } static void set_event_flags(struct pevent *pevent, struct event_str *list, unsigned int flag) { struct event_format **events; struct event_format *event; struct event_str *str; regex_t regex; int ret; int i; if (!list) return; events = pevent_list_events(pevent, 0); for (str = list; str; str = str->next) { char *match; match = malloc_or_die(strlen(str->event) + 3); sprintf(match, "^%s$", str->event); ret = regcomp(®ex, match, REG_ICASE|REG_NOSUB); if (ret < 0) die("Can't parse '%s'", str->event); free(match); for (i = 0; events[i]; i++) { event = events[i]; if (!regexec(®ex, event->name, 0, NULL, 0) || !regexec(®ex, event->system, 0, NULL, 0)) event->flags |= flag; } } } static void add_hook(const char *arg) { struct hook_list *hook; hook = tracecmd_create_event_hook(arg); hook->next = hooks; hooks = hook; if (!last_hook) last_hook = hook; } enum { OPT_uname = 244, OPT_profile = 245, OPT_event = 246, OPT_comm = 247, OPT_boundary = 248, OPT_stat = 249, OPT_pid = 250, OPT_nodate = 251, OPT_check_event_parsing = 252, OPT_kallsyms = 253, OPT_events = 254, OPT_cpu = 255, }; void trace_report (int argc, char **argv) { struct tracecmd_input *handle; struct pevent *pevent; struct event_str *raw_events = NULL; struct event_str *nohandler_events = NULL; struct event_str **raw_ptr = &raw_events; struct event_str **nohandler_ptr = &nohandler_events; const char *functions = NULL; const char *print_event = NULL; struct input_files *inputs; struct handle_list *handles; enum output_type otype; int show_stat = 0; int show_funcs = 0; int show_endian = 0; int show_page_size = 0; int show_printk = 0; int show_uname = 0; int latency_format = 0; int show_events = 0; int print_events = 0; int test_filters = 0; int nanosec = 0; int no_date = 0; int raw = 0; int neg = 0; int ret = 0; int check_event_parsing = 0; int c; list_head_init(&handle_list); list_head_init(&input_files); if (argc < 2) usage(argv); if (strcmp(argv[1], "report") != 0) usage(argv); signal(SIGINT, sig_end); for (;;) { int option_index = 0; static struct option long_options[] = { {"cpu", required_argument, NULL, OPT_cpu}, {"events", no_argument, NULL, OPT_events}, {"event", required_argument, NULL, OPT_event}, {"filter-test", no_argument, NULL, 'T'}, {"kallsyms", required_argument, NULL, OPT_kallsyms}, {"pid", required_argument, NULL, OPT_pid}, {"comm", required_argument, NULL, OPT_comm}, {"check-events", no_argument, NULL, OPT_check_event_parsing}, {"nodate", no_argument, NULL, OPT_nodate}, {"stat", no_argument, NULL, OPT_stat}, {"boundary", no_argument, NULL, OPT_boundary}, {"profile", no_argument, NULL, OPT_profile}, {"uname", no_argument, NULL, OPT_uname}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; c = getopt_long (argc-1, argv+1, "+hi:H:fepRr:tPNn:LlEwF:VvTqO:", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'i': if (input_file) { if (!multi_inputs) add_input(input_file); multi_inputs++; add_input(optarg); } else input_file = optarg; break; case 'F': add_filter(optarg, neg); break; case 'H': add_hook(optarg); break; case 'T': test_filters = 1; break; case 'f': show_funcs = 1; break; case 'P': show_printk = 1; break; case 'L': tracecmd_disable_sys_plugins = 1; break; case 'N': tracecmd_disable_plugins = 1; break; case 'n': *nohandler_ptr = malloc_or_die(sizeof(struct event_str)); (*nohandler_ptr)->event = optarg; (*nohandler_ptr)->next = NULL; nohandler_ptr = &(*nohandler_ptr)->next; break; case 'e': show_endian = 1; break; case 'p': show_page_size = 1; break; case 'E': show_events = 1; break; case 'R': raw = 1; break; case 'r': *raw_ptr = malloc_or_die(sizeof(struct event_str)); (*raw_ptr)->event = optarg; (*raw_ptr)->next = NULL; raw_ptr = &(*raw_ptr)->next; break; case 't': nanosec = 1; break; case 'w': show_wakeup = 1; break; case 'l': latency_format = 1; break; case 'O': process_plugin_option(optarg); break; case 'v': if (neg) die("Only 1 -v can be used"); neg = 1; break; case 'V': show_status = 1; break; case 'q': silence_warnings = 1; break; case OPT_cpu: parse_cpulist(optarg); break; case OPT_events: print_events = 1; break; case OPT_event: print_event = optarg; break; case OPT_kallsyms: functions = optarg; break; case OPT_pid: add_pid_filter(optarg); break; case OPT_comm: add_comm_filter(optarg); break; case OPT_check_event_parsing: check_event_parsing = 1; break; case OPT_nodate: no_date = 1; break; case OPT_stat: show_stat = 1; break; case OPT_boundary: /* Debug to look at buffer breaks */ buffer_breaks = 1; break; case OPT_profile: profile = 1; break; case OPT_uname: show_uname = 1; break; default: usage(argv); } } if ((argc - optind) >= 2) { if (input_file) usage(argv); input_file = argv[optind + 1]; } if (!input_file) input_file = default_input_file; if (!multi_inputs) add_input(input_file); else if (show_wakeup) die("Wakeup tracing can only be done on a single input file"); list_for_each_entry(inputs, &input_files, list) { handle = read_trace_header(inputs->file); if (!handle) die("error reading header for %s", inputs->file); /* If used with instances, top instance will have no tag */ add_handle(handle, multi_inputs ? inputs->file : NULL); if (no_date) tracecmd_set_flag(handle, TRACECMD_FL_IGNORE_DATE); page_size = tracecmd_page_size(handle); if (show_page_size) { printf("file page size is %d, and host page size is %d\n", page_size, getpagesize()); return; } pevent = tracecmd_get_pevent(handle); if (nanosec) pevent->flags |= PEVENT_NSEC_OUTPUT; if (raw) pevent->print_raw = 1; if (test_filters) pevent->test_filters = 1; if (functions) add_functions(pevent, functions); if (show_endian) { printf("file is %s endian and host is %s endian\n", pevent_is_file_bigendian(pevent) ? "big" : "little", pevent_is_host_bigendian(pevent) ? "big" : "little"); return; } if (print_events) { tracecmd_print_events(handle, NULL); return; } if (print_event) { tracecmd_print_events(handle, print_event); return; } ret = tracecmd_read_headers(handle); if (check_event_parsing) { if (ret || pevent->parsing_failures) exit(EINVAL); else exit(0); } else { if (ret) return; } if (show_funcs) { pevent_print_funcs(pevent); return; } if (show_printk) { pevent_print_printk(pevent); return; } if (show_events) { struct event_format **events; struct event_format *event; int i; events = pevent_list_events(pevent, EVENT_SORT_SYSTEM); for (i = 0; events[i]; i++) { event = events[i]; if (event->system) printf("%s:", event->system); printf("%s\n", event->name); } return; } set_event_flags(pevent, nohandler_events, EVENT_FL_NOHANDLE); set_event_flags(pevent, raw_events, EVENT_FL_PRINTRAW); } if (latency_format) pevent_set_latency_format(pevent, latency_format); otype = OUTPUT_NORMAL; if (show_stat) otype = OUTPUT_STAT_ONLY; /* yeah yeah, uname overrides stat */ if (show_uname) otype = OUTPUT_UNAME_ONLY; read_data_info(&handle_list, otype); list_for_each_entry(handles, &handle_list, list) { tracecmd_close(handles->handle); } free_handles(); free_inputs(); finish_wakeup(); return; } trace-cmd-2.5.3/trace-record.c000066400000000000000000002571031246701203100161020ustar00rootroot00000000000000/* * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_PTRACE #include #else #ifdef WARN_NO_PTRACE #warning ptrace not supported. -c feature will not work #endif #endif #include #include #include #include #include #include #include #include #include "trace-local.h" #define _STR(x) #x #define STR(x) _STR(x) #define MAX_PATH 256 #define TRACE_CTRL "tracing_on" #define TRACE "trace" #define AVAILABLE "available_tracers" #define CURRENT "current_tracer" #define ITER_CTRL "trace_options" #define MAX_LATENCY "tracing_max_latency" #define STAMP "stamp" #define FUNC_STACK_TRACE "func_stack_trace" #define UDP_MAX_PACKET (65536 - 20) enum trace_type { TRACE_TYPE_RECORD = 1, TRACE_TYPE_START = (1 << 1), TRACE_TYPE_STREAM = (1 << 2), TRACE_TYPE_EXTRACT = (1 << 3), TRACE_TYPE_PROFILE = (1 << 4) | TRACE_TYPE_STREAM, }; static int rt_prio; static int use_tcp; static int keep; static unsigned int page_size; static const char *output_file = "trace.dat"; static int latency; static int sleep_time = 1000; static int cpu_count; static int recorder_threads; static struct pid_record_data *pids; static int buffers; /* Clear all function filters */ static int clear_function_filters; static char *host; static int *client_ports; static int sfd; static struct tracecmd_output *network_handle; /* Max size to let a per cpu file get */ static int max_kb; static int do_ptrace; static int filter_task; static int filter_pid = -1; static int finished; /* setting of /proc/sys/kernel/ftrace_enabled */ static int fset; static unsigned recorder_flags; /* Try a few times to get an accurate date */ static int date2ts_tries = 5; static struct func_list *graph_funcs; static int func_stack; static int save_stdout = -1; struct filter_pids { struct filter_pids *next; int pid; }; static struct filter_pids *filter_pids; static int nr_filter_pids; static int len_filter_pids; struct opt_list { struct opt_list *next; const char *option; }; static struct opt_list *options; static struct hook_list *hooks; static char *common_pid_filter; struct event_list { struct event_list *next; const char *event; char *trigger; char *filter; char *pid_filter; char *filter_file; char *trigger_file; char *enable_file; int neg; }; struct tracecmd_event_list *listed_events; struct events { struct events *sibling; struct events *children; struct events *next; char *name; }; /* Files to be reset when done recording */ struct reset_file { struct reset_file *next; char *path; char *reset; int prio; }; static struct reset_file *reset_files; /* Triggers need to be cleared in a special way */ static struct reset_file *reset_triggers; struct buffer_instance top_instance = { .keep = 1 }; struct buffer_instance *buffer_instances; struct buffer_instance *first_instance = &top_instance; static struct tracecmd_recorder *recorder; static int ignore_event_not_found = 0; static inline int is_top_instance(struct buffer_instance *instance) { return instance == &top_instance; } static inline int no_top_instance(void) { return first_instance != &top_instance; } static void init_instance(struct buffer_instance *instance) { instance->event_next = &instance->events; } enum { RESET_DEFAULT_PRIO = 0, RESET_HIGH_PRIO = 100000, }; static void add_reset_file(const char *file, const char *val, int prio) { struct reset_file *reset; struct reset_file **last = &reset_files; /* Only reset if we are not keeping the state */ if (keep) return; reset = malloc_or_die(sizeof(*reset)); reset->path = strdup(file); reset->reset = strdup(val); reset->prio = prio; if (!reset->path || !reset->reset) die("malloc"); while (*last && (*last)->prio > prio) last = &(*last)->next; reset->next = *last; *last = reset; } static void add_reset_trigger(const char *file) { struct reset_file *reset; /* Only reset if we are not keeping the state */ if (keep) return; reset = malloc_or_die(sizeof(*reset)); reset->path = strdup(file); reset->next = reset_triggers; reset_triggers = reset; } /* To save the contents of the file */ static void reset_save_file(const char *file, int prio) { char *content; content = get_file_content(file); add_reset_file(file, content, prio); free(content); } /* * @file: the file to check * @nop: If the content of the file is this, use the reset value * @reset: What to write if the file == @nop */ static void reset_save_file_cond(const char *file, int prio, const char *nop, const char *reset) { char *content; char *cond; if (keep) return; content = get_file_content(file); cond = strstrip(content); if (strcmp(cond, nop) == 0) add_reset_file(file, reset, prio); else add_reset_file(file, content, prio); free(content); } /** * add_instance - add a buffer instance to the internal list * @instance: The buffer instance to add */ void add_instance(struct buffer_instance *instance) { init_instance(instance); instance->next = buffer_instances; buffer_instances = instance; buffers++; } /** * create_instance - allocate a new buffer instance * @name: The name of the instance (instance will point to this) * * Returns a newly allocated instance. Note that @name will not be * copied, and the instance buffer will point to the string itself. */ struct buffer_instance *create_instance(char *name) { struct buffer_instance *instance; instance = malloc_or_die(sizeof(*instance)); memset(instance, 0, sizeof(*instance)); instance->name = optarg; return instance; } /** * tracecmd_stat_cpu - show the buffer stats of a particular CPU * @s: the trace_seq to record the data in. * @cpu: the CPU to stat * */ void tracecmd_stat_cpu_instance(struct buffer_instance *instance, struct trace_seq *s, int cpu) { char buf[BUFSIZ]; char *path; char *file; int fd; int r; file = malloc(40); if (!file) return; snprintf(file, 40, "per_cpu/cpu%d/stats", cpu); path = get_instance_file(instance, file); free(file); fd = open(path, O_RDONLY); tracecmd_put_tracing_file(path); if (fd < 0) return; while ((r = read(fd, buf, BUFSIZ)) > 0) trace_seq_printf(s, "%.*s", r, buf); close(fd); } /** * tracecmd_stat_cpu - show the buffer stats of a particular CPU * @s: the trace_seq to record the data in. * @cpu: the CPU to stat * */ void tracecmd_stat_cpu(struct trace_seq *s, int cpu) { tracecmd_stat_cpu_instance(&top_instance, s, cpu); } static void add_event(struct buffer_instance *instance, struct event_list *event) { *instance->event_next = event; instance->event_next = &event->next; event->next = NULL; } static void reset_event_list(struct buffer_instance *instance) { instance->events = NULL; init_instance(instance); } static char *get_temp_file(struct buffer_instance *instance, int cpu) { const char *name = instance->name; char *file = NULL; int size; if (name) { size = snprintf(file, 0, "%s.%s.cpu%d", output_file, name, cpu); file = malloc_or_die(size + 1); sprintf(file, "%s.%s.cpu%d", output_file, name, cpu); } else { size = snprintf(file, 0, "%s.cpu%d", output_file, cpu); file = malloc_or_die(size + 1); sprintf(file, "%s.cpu%d", output_file, cpu); } return file; } static void put_temp_file(char *file) { free(file); } static void delete_temp_file(struct buffer_instance *instance, int cpu) { const char *name = instance->name; char file[MAX_PATH]; if (name) snprintf(file, MAX_PATH, "%s.%s.cpu%d", output_file, name, cpu); else snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu); unlink(file); } static int kill_thread_instance(int start, struct buffer_instance *instance) { int n = start; int i; for (i = 0; i < cpu_count; i++) { if (pids[n].pid > 0) { kill(pids[n].pid, SIGKILL); delete_temp_file(instance, i); pids[n].pid = 0; if (pids[n].brass[0] >= 0) close(pids[n].brass[0]); } n++; } return n; } static void kill_threads(void) { struct buffer_instance *instance; int i = 0; if (!recorder_threads || !pids) return; for_all_instances(instance) i = kill_thread_instance(i, instance); } void die(const char *fmt, ...) { va_list ap; int ret = errno; if (errno) perror("trace-cmd"); else ret = -1; kill_threads(); va_start(ap, fmt); fprintf(stderr, " "); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(ret); } static int delete_thread_instance(int start, struct buffer_instance *instance) { int n = start; int i; for (i = 0; i < cpu_count; i++) { if (pids) { if (pids[n].pid) { delete_temp_file(instance, i); if (pids[n].pid < 0) pids[n].pid = 0; } n++; } else /* Extract does not allocate pids */ delete_temp_file(instance, i); } return n; } static void delete_thread_data(void) { struct buffer_instance *instance; int i = 0; for_all_instances(instance) i = delete_thread_instance(i, instance); /* * Top instance temp files are still created even if it * isn't used. */ if (no_top_instance()) { for (i = 0; i < cpu_count; i++) delete_temp_file(&top_instance, i); } } static void stop_threads(enum trace_type type) { struct timeval tv = { 0, 0 }; int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; int ret; int i; if (!cpu_count) return; /* Tell all threads to finish up */ for (i = 0; i < recorder_threads; i++) { if (pids[i].pid > 0) { kill(pids[i].pid, SIGINT); } } /* Flush out the pipes */ if (type & TRACE_TYPE_STREAM) { do { ret = trace_stream_read(pids, recorder_threads, &tv, profile); } while (ret > 0); } for (i = 0; i < recorder_threads; i++) { if (pids[i].pid > 0) { waitpid(pids[i].pid, NULL, 0); pids[i].pid = -1; } } } static int create_recorder(struct buffer_instance *instance, int cpu, enum trace_type type, int *brass); static void flush_threads(void) { long ret; int i; if (!cpu_count) return; for (i = 0; i < cpu_count; i++) { /* Extract doesn't support sub buffers yet */ ret = create_recorder(&top_instance, i, TRACE_TYPE_EXTRACT, NULL); if (ret < 0) die("error reading ring buffer"); } } static int set_ftrace_enable(const char *path, int set) { struct stat st; int fd; char *val = set ? "1" : "0"; int ret; /* if ftace_enable does not exist, simply ignore it */ fd = stat(path, &st); if (fd < 0) return -ENODEV; reset_save_file(path, RESET_DEFAULT_PRIO); ret = -1; fd = open(path, O_WRONLY); if (fd < 0) goto out; /* Now set or clear the function option */ ret = write(fd, val, 1); close(fd); out: return ret < 0 ? ret : 0; } static int set_ftrace_proc(int set) { const char *path = "/proc/sys/kernel/ftrace_enabled"; int ret; ret = set_ftrace_enable(path, set); if (ret == -1) die ("Can't %s ftrace", set ? "enable" : "disable"); return ret; } static int set_ftrace(int set, int use_proc) { char *path; int ret; /* First check if the function-trace option exists */ path = tracecmd_get_tracing_file("options/function-trace"); ret = set_ftrace_enable(path, set); tracecmd_put_tracing_file(path); /* Always enable ftrace_enable proc file when set is true */ if (ret < 0 || set || use_proc) ret = set_ftrace_proc(set); return 0; } /** * get_instance_file - return the path to a instance file. * @instance: buffer instance for the file * @file: name of file to return * * Returns the path name of the @file for the given @instance. * * Must use tracecmd_put_tracing_file() to free the returned string. */ char * get_instance_file(struct buffer_instance *instance, const char *file) { char *buf; char *path; if (instance->name) { buf = malloc_or_die(strlen(instance->name) + strlen(file) + strlen("instances//") + 1); sprintf(buf, "instances/%s/%s", instance->name, file); path = tracecmd_get_tracing_file(buf); free(buf); } else path = tracecmd_get_tracing_file(file); return path; } static char * get_instance_dir(struct buffer_instance *instance) { char *buf; char *path; /* only works for instances */ if (!instance->name) return NULL; buf = malloc_or_die(strlen(instance->name) + strlen("instances/") + 1); sprintf(buf, "instances/%s", instance->name); path = tracecmd_get_tracing_file(buf); free(buf); return path; } static void __clear_trace(struct buffer_instance *instance) { FILE *fp; char *path; /* reset the trace */ path = get_instance_file(instance, "trace"); fp = fopen(path, "w"); if (!fp) die("writing to '%s'", path); tracecmd_put_tracing_file(path); fwrite("0", 1, 1, fp); fclose(fp); } static void clear_trace(void) { struct buffer_instance *instance; for_all_instances(instance) __clear_trace(instance); } static void reset_max_latency(void) { FILE *fp; char *path; /* reset the trace */ path = tracecmd_get_tracing_file("tracing_max_latency"); fp = fopen(path, "w"); if (!fp) die("writing to '%s'", path); tracecmd_put_tracing_file(path); fwrite("0", 1, 1, fp); fclose(fp); } static void add_filter_pid(int pid) { struct filter_pids *p; char buf[100]; p = malloc_or_die(sizeof(*p)); p->next = filter_pids; p->pid = pid; filter_pids = p; nr_filter_pids++; len_filter_pids += sprintf(buf, "%d", pid); } static void update_ftrace_pid(const char *pid, int reset) { static char *path; int ret; static int fd = -1; static int first = 1; struct stat st; if (!pid) { if (fd >= 0) close(fd); if (path) tracecmd_put_tracing_file(path); fd = -1; path = NULL; return; } /* Force reopen on reset */ if (reset && fd >= 0) { close(fd); fd = -1; } if (fd < 0) { if (!path) path = tracecmd_get_tracing_file("set_ftrace_pid"); if (!path) return; ret = stat(path, &st); if (ret < 0) return; if (first) { first = 0; reset_save_file_cond(path, RESET_DEFAULT_PRIO, "no pid", ""); } fd = open(path, O_WRONLY | O_CLOEXEC | (reset ? O_TRUNC : 0)); if (fd < 0) return; } ret = write(fd, pid, strlen(pid)); /* * Older kernels required "-1" to disable pid */ if (ret < 0 && !strlen(pid)) ret = write(fd, "-1", 2); if (ret < 0) die("error writing to %s", path); /* add whitespace in case another pid is written */ write(fd, " ", 1); } static void update_ftrace_pids(int reset) { char buf[100]; struct filter_pids *pid; for (pid = filter_pids; pid; pid = pid->next) { snprintf(buf, 100, "%d ", pid->pid); update_ftrace_pid(buf, reset); /* Only reset the first entry */ reset = 0; } } static void update_event_filters(struct buffer_instance *instance); static void update_pid_event_filters(struct buffer_instance *instance); static void enable_tracing(void); /** * make_pid_filter - create a filter string to all pids against @field * @curr_filter: Append to a previous filter (may realloc). Can be NULL * @field: The fild to compare the pids against * * Creates a new string or appends to an existing one if @curr_filter * is not NULL. The new string will contain a filter with all pids * in pid_filter list with the format (@field == pid) || .. * If @curr_filter is not NULL, it will add this string as: * (@curr_filter) && ((@field == pid) || ...) */ static char *make_pid_filter(char *curr_filter, const char *field) { struct filter_pids *p; char *filter; char *orit; char *str; int curr_len = 0; int len; len = len_filter_pids + (strlen(field) + strlen("(==)||")) * nr_filter_pids; if (curr_filter) { curr_len = strlen(curr_filter); filter = realloc(curr_filter, curr_len + len + strlen("(&&())")); if (!filter) die("realloc"); memmove(filter+1, curr_filter, curr_len); filter[0] = '('; strcat(filter, ")&&("); curr_len = strlen(filter); } else filter = malloc_or_die(len); /* Last '||' that is not used will cover the \0 */ str = filter + curr_len; for (p = filter_pids; p; p = p->next) { if (p == filter_pids) orit = ""; else orit = "||"; len = sprintf(str, "%s(%s==%d)", orit, field, p->pid); str += len; } if (curr_len) sprintf(str, ")"); return filter; } static void update_task_filter(void) { struct buffer_instance *instance; int pid = getpid(); if (filter_task) add_filter_pid(pid); if (!filter_pids) return; common_pid_filter = make_pid_filter(NULL, "common_pid"); update_ftrace_pids(1); for_all_instances(instance) update_pid_event_filters(instance); } static pid_t trace_waitpid(enum trace_type type, pid_t pid, int *status, int options) { struct timeval tv = { 1, 0 }; int ret; int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; if (type & TRACE_TYPE_STREAM) options |= WNOHANG; do { ret = waitpid(pid, status, options); if (ret != 0) return ret; if (type & TRACE_TYPE_STREAM) trace_stream_read(pids, recorder_threads, &tv, profile); } while (1); } #ifndef NO_PTRACE /** * append_pid_filter - add a new pid to an existing filter * @curr_filter: the filter to append to. If NULL, then allocate one * @field: The fild to compare the pid to * @pid: The pid to add to. */ static char *append_pid_filter(char *curr_filter, const char *field, int pid) { char *filter; int len; len = snprintf(NULL, 0, "(%s==%d)||", field, pid); if (!curr_filter) { /* No need for +1 as we don't use the "||" */ filter = malloc_or_die(len); sprintf(filter, "(%s==%d)", field, pid); } else { int indx = strlen(curr_filter); len += indx; filter = realloc(curr_filter, len + indx + 1); if (!filter) die("realloc"); sprintf(filter + indx, "||(%s==%d)", field, pid); } return filter; } static void append_sched_event(struct event_list *event, const char *field, int pid) { if (!event || !event->pid_filter) return; event->pid_filter = append_pid_filter(event->pid_filter, field, pid); } static void update_sched_events(struct buffer_instance *instance, int pid) { /* * Also make sure that the sched_switch to this pid * and wakeups of this pid are also traced. * Only need to do this if the events are active. */ append_sched_event(instance->sched_switch_event, "next_pid", pid); append_sched_event(instance->sched_wakeup_event, "pid", pid); append_sched_event(instance->sched_wakeup_new_event, "pid", pid); } static void add_new_filter_pid(int pid) { struct buffer_instance *instance; char buf[100]; add_filter_pid(pid); sprintf(buf, "%d", pid); update_ftrace_pid(buf, 0); common_pid_filter = append_pid_filter(common_pid_filter, "common_pid", pid); for_all_instances(instance) { update_sched_events(instance, pid); update_event_filters(instance); } } static void ptrace_attach(int pid) { int ret; ret = ptrace(PTRACE_ATTACH, pid, NULL, 0); if (ret < 0) { warning("Unable to trace process %d children", pid); do_ptrace = 0; return; } add_filter_pid(pid); } static void enable_ptrace(void) { if (!do_ptrace || !filter_task) return; ptrace(PTRACE_TRACEME, 0, NULL, 0); } static void ptrace_wait(enum trace_type type, int main_pid) { unsigned long send_sig; unsigned long child; siginfo_t sig; int cstatus; int status; int event; int pid; int ret; do { ret = trace_waitpid(type, -1, &status, WSTOPPED | __WALL); if (ret < 0) continue; pid = ret; if (WIFSTOPPED(status)) { event = (status >> 16) & 0xff; ptrace(PTRACE_GETSIGINFO, pid, NULL, &sig); send_sig = sig.si_signo; /* Don't send ptrace sigs to child */ if (send_sig == SIGTRAP || send_sig == SIGSTOP) send_sig = 0; switch (event) { case PTRACE_EVENT_FORK: case PTRACE_EVENT_VFORK: case PTRACE_EVENT_CLONE: /* forked a child */ ptrace(PTRACE_GETEVENTMSG, pid, NULL, &child); ptrace(PTRACE_SETOPTIONS, child, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT); add_new_filter_pid(child); ptrace(PTRACE_CONT, child, NULL, 0); break; case PTRACE_EVENT_EXIT: ptrace(PTRACE_GETEVENTMSG, pid, NULL, &cstatus); ptrace(PTRACE_DETACH, pid, NULL, NULL); break; } ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT); ptrace(PTRACE_CONT, pid, NULL, send_sig); } } while (!finished && ret > 0 && (!WIFEXITED(status) || pid != main_pid)); } #else static inline void ptrace_wait(int main_pid) { } static inline void enable_ptrace(void) { } static inline void ptrace_attach(int pid) { } #endif /* NO_PTRACE */ static void trace_or_sleep(enum trace_type type) { struct timeval tv = { 1 , 0 }; int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; if (do_ptrace && filter_pid >= 0) ptrace_wait(type, filter_pid); else if (type & TRACE_TYPE_STREAM) trace_stream_read(pids, recorder_threads, &tv, profile); else sleep(10); } static void run_cmd(enum trace_type type, int argc, char **argv) { int status; int pid; if ((pid = fork()) < 0) die("failed to fork"); if (!pid) { /* child */ update_task_filter(); enable_tracing(); enable_ptrace(); /* * If we are using stderr for stdout, switch * it back to the saved stdout for the code we run. */ if (save_stdout >= 0) { close(1); dup2(save_stdout, 1); close(save_stdout); } if (execvp(argv[0], argv)) { fprintf(stderr, "\n********************\n"); fprintf(stderr, " Unable to exec %s\n", argv[0]); fprintf(stderr, "********************\n"); die("Failed to exec %s", argv[0]); } } if (do_ptrace) { add_filter_pid(pid); ptrace_wait(type, pid); } else trace_waitpid(type, pid, &status, 0); } static void set_plugin_instance(struct buffer_instance *instance, const char *name) { FILE *fp; char *path; char zero = '0'; path = get_instance_file(instance, "current_tracer"); fp = fopen(path, "w"); if (!fp) { /* * Legacy kernels do not have current_tracer file, and they * always use nop. So, it doesn't need to try to change the * plugin for those if name is "nop". */ if (!strncmp(name, "nop", 3)) { tracecmd_put_tracing_file(path); return; } die("writing to '%s'", path); } tracecmd_put_tracing_file(path); fwrite(name, 1, strlen(name), fp); fclose(fp); if (strncmp(name, "function", 8) != 0) return; /* Make sure func_stack_trace option is disabled */ /* First try instance file, then top level */ path = get_instance_file(instance, "options/func_stack_trace"); fp = fopen(path, "w"); if (!fp) { tracecmd_put_tracing_file(path); path = tracecmd_get_tracing_file("options/func_stack_trace"); fp = fopen(path, "w"); if (!fp) { tracecmd_put_tracing_file(path); return; } } /* * Always reset func_stack_trace to zero. Don't bother saving * the original content. */ add_reset_file(path, "0", RESET_HIGH_PRIO); tracecmd_put_tracing_file(path); fwrite(&zero, 1, 1, fp); fclose(fp); } static void set_plugin(const char *name) { struct buffer_instance *instance; for_all_instances(instance) set_plugin_instance(instance, name); } static void save_option(const char *option) { struct opt_list *opt; opt = malloc_or_die(sizeof(*opt)); opt->next = options; options = opt; opt->option = option; } static int set_option(const char *option) { FILE *fp; char *path; path = tracecmd_get_tracing_file("trace_options"); fp = fopen(path, "w"); if (!fp) warning("writing to '%s'", path); tracecmd_put_tracing_file(path); if (!fp) return -1; fwrite(option, 1, strlen(option), fp); fclose(fp); return 0; } static char *read_instance_file(struct buffer_instance *instance, char *file, int *psize); static void disable_func_stack_trace_instance(struct buffer_instance *instance) { struct stat st; char *content; char *path; char *cond; int size; int ret; path = get_instance_file(instance, "current_tracer"); ret = stat(path, &st); tracecmd_put_tracing_file(path); if (ret < 0) return; content = read_instance_file(instance, "current_tracer", &size); cond = strstrip(content); if (memcmp(cond, "function", size - (cond - content)) !=0) goto out; set_option("nofunc_stack_trace"); out: free(content); } static void disable_func_stack_trace(void) { struct buffer_instance *instance; for_all_instances(instance) disable_func_stack_trace_instance(instance); } static void add_reset_options(void) { struct opt_list *opt; const char *option; char *content; char *path; char *ptr; int len; if (keep) return; path = tracecmd_get_tracing_file("trace_options"); content = get_file_content(path); for (opt = options; opt; opt = opt->next) { option = opt->option; len = strlen(option); ptr = content; again: ptr = strstr(ptr, option); if (ptr) { /* First make sure its the option we want */ if (ptr[len] != '\n') { ptr += len; goto again; } if (ptr - content >= 2 && strncmp(ptr - 2, "no", 2) == 0) { /* Make sure this isn't ohno-option */ if (ptr > content + 2 && *(ptr - 3) != '\n') { ptr += len; goto again; } /* we enabled it */ ptr[len] = 0; add_reset_file(path, ptr-2, RESET_DEFAULT_PRIO); ptr[len] = '\n'; continue; } /* make sure this is our option */ if (ptr > content && *(ptr - 1) != '\n') { ptr += len; goto again; } /* this option hasn't changed, ignore it */ continue; } /* ptr is NULL, not found, maybe option is a no */ if (strncmp(option, "no", 2) != 0) /* option is really not found? */ continue; option += 2; len = strlen(option); ptr = content; loop: ptr = strstr(content, option); if (!ptr) /* Really not found? */ continue; /* make sure this is our option */ if (ptr[len] != '\n') { ptr += len; goto loop; } if (ptr > content && *(ptr - 1) != '\n') { ptr += len; goto loop; } add_reset_file(path, option, RESET_DEFAULT_PRIO); } tracecmd_put_tracing_file(path); free(content); } static void set_options(void) { struct opt_list *opt; int ret; add_reset_options(); while (options) { opt = options; options = opt->next; ret = set_option(opt->option); if (ret < 0) exit(-1); free(opt); } } static int trace_check_file_exists(struct buffer_instance *instance, char *file) { struct stat st; char *path; int ret; path = get_instance_file(instance, file); ret = stat(path, &st); tracecmd_put_tracing_file(path); return ret < 0 ? 0 : 1; } static int use_old_event_method(void) { static int old_event_method; static int processed; if (processed) return old_event_method; /* Check if the kernel has the events/enable file */ if (!trace_check_file_exists(&top_instance, "events/enable")) old_event_method = 1; processed = 1; return old_event_method; } static void old_update_events(const char *name, char update) { char *path; FILE *fp; int ret; if (strcmp(name, "all") == 0) name = "*:*"; /* need to use old way */ path = tracecmd_get_tracing_file("set_event"); fp = fopen(path, "w"); if (!fp) die("opening '%s'", path); tracecmd_put_tracing_file(path); /* Disable the event with "!" */ if (update == '0') fwrite("!", 1, 1, fp); ret = fwrite(name, 1, strlen(name), fp); if (ret < 0) die("bad event '%s'", name); ret = fwrite("\n", 1, 1, fp); if (ret < 0) die("bad event '%s'", name); fclose(fp); return; } static void reset_events_instance(struct buffer_instance *instance) { glob_t globbuf; char *path; char c; int fd; int i; int ret; if (use_old_event_method()) { /* old way only had top instance */ if (!is_top_instance(instance)) return; old_update_events("all", '0'); return; } c = '0'; path = get_instance_file(instance, "events/enable"); fd = open(path, O_WRONLY); if (fd < 0) die("opening to '%s'", path); ret = write(fd, &c, 1); close(fd); tracecmd_put_tracing_file(path); path = get_instance_file(instance, "events/*/filter"); globbuf.gl_offs = 0; ret = glob(path, 0, NULL, &globbuf); tracecmd_put_tracing_file(path); if (ret < 0) return; for (i = 0; i < globbuf.gl_pathc; i++) { path = globbuf.gl_pathv[i]; fd = open(path, O_WRONLY); if (fd < 0) die("opening to '%s'", path); ret = write(fd, &c, 1); close(fd); } globfree(&globbuf); } static void reset_events(void) { struct buffer_instance *instance; for_all_instances(instance) reset_events_instance(instance); } static int write_file(const char *file, const char *str, const char *type) { char buf[BUFSIZ]; int fd; int ret; fd = open(file, O_WRONLY); if (fd < 0) die("opening to '%s'", file); ret = write(fd, str, strlen(str)); close(fd); if (ret < 0 && type) { /* write failed */ fd = open(file, O_RDONLY); if (fd < 0) die("writing to '%s'", file); /* the filter has the error */ while ((ret = read(fd, buf, BUFSIZ)) > 0) fprintf(stderr, "%.*s", ret, buf); die("Failed %s of %s\n", type, file); close(fd); } return ret; } static int write_instance_file(struct buffer_instance *instance, const char *file, const char *str, const char *type) { char *path; int ret; path = get_instance_file(instance, file); ret = write_file(path, str, type); tracecmd_put_tracing_file(path); return ret; } enum { STATE_NEWLINE, STATE_SKIP, STATE_COPY, }; static int find_trigger(const char *file, char *buf, int size, int fields) { FILE *fp; int state = STATE_NEWLINE; int ch; int len = 0; fp = fopen(file, "r"); if (!fp) return 0; while ((ch = fgetc(fp)) != EOF) { if (ch == '\n') { if (state == STATE_COPY) break; state = STATE_NEWLINE; continue; } if (state == STATE_SKIP) continue; if (state == STATE_NEWLINE && ch == '#') { state = STATE_SKIP; continue; } if (state == STATE_COPY && ch == ':' && --fields < 1) break; state = STATE_COPY; buf[len++] = ch; if (len == size - 1) break; } buf[len] = 0; fclose(fp); return len; } static void write_filter(const char *file, const char *filter) { write_file(file, filter, "filter"); } static void clear_filter(const char *file) { write_filter(file, "0"); } static void write_trigger(const char *file, const char *trigger) { write_file(file, trigger, "trigger"); } static void write_func_filter(const char *file, const char *trigger) { write_file(file, trigger, "function filter"); } static void clear_trigger(const char *file) { char trigger[BUFSIZ]; int len; trigger[0] = '!'; /* * To delete a trigger, we need to write a '!trigger' * to the file for each trigger. */ do { len = find_trigger(file, trigger+1, BUFSIZ-1, 1); if (len) write_trigger(file, trigger); } while (len); } static void clear_func_filter(const char *file) { char trigger[BUFSIZ]; struct stat st; char *p; int len; int ret; int fd; /* Function filters may not exist */ ret = stat(file, &st); if (ret < 0) return; /* First zero out normal filters */ fd = open(file, O_WRONLY | O_TRUNC); if (fd < 0) die("opening to '%s'", file); close(fd); /* Now remove triggers */ trigger[0] = '!'; /* * To delete a trigger, we need to write a '!trigger' * to the file for each trigger. */ do { len = find_trigger(file, trigger+1, BUFSIZ-1, 3); if (len) { /* * To remove "unlimited" triggers, we must remove * the ":unlimited" from what we write. */ if ((p = strstr(trigger, ":unlimited"))) { *p = '\0'; len = p - trigger; } /* * The write to this file expects white space * at the end :-p */ trigger[len] = '\n'; trigger[len+1] = '\0'; write_func_filter(file, trigger); } } while (len > 0); } static void update_reset_triggers(void) { struct reset_file *reset; while (reset_triggers) { reset = reset_triggers; reset_triggers = reset->next; clear_trigger(reset->path); free(reset->path); free(reset); } } static void update_reset_files(void) { struct reset_file *reset; while (reset_files) { reset = reset_files; reset_files = reset->next; write_file(reset->path, reset->reset, "reset"); free(reset->path); free(reset->reset); free(reset); } } static void update_event(struct event_list *event, const char *filter, int filter_only, char update) { const char *name = event->event; FILE *fp; char *path; int ret; if (use_old_event_method()) { if (filter_only) return; old_update_events(name, update); return; } if (filter && event->filter_file) { add_reset_file(event->filter_file, "0", RESET_DEFAULT_PRIO); write_filter(event->filter_file, filter); } if (event->trigger_file) { add_reset_trigger(event->trigger_file); clear_trigger(event->trigger_file); write_trigger(event->trigger_file, event->trigger); /* Make sure we don't write this again */ free(event->trigger_file); free(event->trigger); event->trigger_file = NULL; event->trigger = NULL; } if (filter_only || !event->enable_file) return; path = event->enable_file; fp = fopen(path, "w"); if (!fp) die("writing to '%s'", path); ret = fwrite(&update, 1, 1, fp); fclose(fp); if (ret < 0) die("writing to '%s'", path); } /* * The debugfs file tracing_enabled needs to be deprecated. * But just in case anyone fiddled with it. If it exists, * make sure it is one. * No error checking needed here. */ static void check_tracing_enabled(void) { static int fd = -1; char *path; if (fd < 0) { path = tracecmd_get_tracing_file("tracing_enabled"); fd = open(path, O_WRONLY | O_CLOEXEC); tracecmd_put_tracing_file(path); if (fd < 0) return; } write(fd, "1", 1); } static int open_tracing_on(struct buffer_instance *instance) { int fd = instance->tracing_on_fd; char *path; /* OK, we keep zero for stdin */ if (fd > 0) return fd; path = get_instance_file(instance, "tracing_on"); fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) { /* instances may not be created yet */ if (is_top_instance(instance)) die("opening '%s'", path); return fd; } tracecmd_put_tracing_file(path); instance->tracing_on_fd = fd; return fd; } static void write_tracing_on(struct buffer_instance *instance, int on) { int ret; int fd; fd = open_tracing_on(instance); if (fd < 0) return; if (on) ret = write(fd, "1", 1); else ret = write(fd, "0", 1); if (ret < 0) die("writing 'tracing_on'"); } static int read_tracing_on(struct buffer_instance *instance) { int fd; char buf[10]; int ret; fd = open_tracing_on(instance); if (fd < 0) return fd; ret = read(fd, buf, 10); if (ret <= 0) die("Reading 'tracing_on'"); buf[9] = 0; ret = atoi(buf); return ret; } static void enable_tracing(void) { struct buffer_instance *instance; check_tracing_enabled(); for_all_instances(instance) write_tracing_on(instance, 1); if (latency) reset_max_latency(); } static void disable_tracing(void) { struct buffer_instance *instance; for_all_instances(instance) write_tracing_on(instance, 0); } static void disable_all(int disable_tracer) { disable_tracing(); if (disable_tracer) { disable_func_stack_trace(); set_plugin("nop"); } reset_events(); /* Force close and reset of ftrace pid file */ update_ftrace_pid("", 1); update_ftrace_pid(NULL, 0); clear_trace(); } static void update_sched_event(struct event_list *event, const char *field) { if (!event) return; event->pid_filter = make_pid_filter(event->pid_filter, field); } static void update_event_filters(struct buffer_instance *instance) { struct event_list *event; char *event_filter; int free_it; int len; int common_len = 0; if (common_pid_filter) common_len = strlen(common_pid_filter); for (event = instance->events; event; event = event->next) { if (!event->neg) { free_it = 0; if (event->filter) { if (!common_pid_filter) /* * event->pid_filter is only created if * common_pid_filter is. No need to check that. * Just use the current event->filter. */ event_filter = event->filter; else if (event->pid_filter) { free_it = 1; len = common_len + strlen(event->pid_filter) + strlen(event->filter) + strlen("()&&(||)") + 1; event_filter = malloc_or_die(len); sprintf(event_filter, "(%s)&&(%s||%s)", event->filter, common_pid_filter, event->pid_filter); } else { free_it = 1; len = common_len + strlen(event->filter) + strlen("()&&()") + 1; event_filter = malloc_or_die(len); sprintf(event_filter, "(%s)&&(%s)", event->filter, common_pid_filter); } } else { /* event->pid_filter only exists when common_pid_filter does */ if (!common_pid_filter) continue; if (event->pid_filter) { free_it = 1; len = common_len + strlen(event->pid_filter) + strlen("||") + 1; event_filter = malloc_or_die(len); sprintf(event_filter, "%s||%s", common_pid_filter, event->pid_filter); } else event_filter = common_pid_filter; } update_event(event, event_filter, 1, '1'); if (free_it) free(event_filter); } } } static void update_pid_event_filters(struct buffer_instance *instance) { /* * Also make sure that the sched_switch to this pid * and wakeups of this pid are also traced. * Only need to do this if the events are active. */ update_sched_event(instance->sched_switch_event, "next_pid"); update_sched_event(instance->sched_wakeup_event, "pid"); update_sched_event(instance->sched_wakeup_new_event, "pid"); update_event_filters(instance); } static void set_mask(struct buffer_instance *instance) { const char *mask = instance->cpumask; struct stat st; char cpumask[4096]; /* Don't expect more than 32768 CPUS */ char *path; int fd; int ret; if (!mask) return; if (strcmp(mask, "-1") == 0) { /* set all CPUs */ int bytes = (cpu_count + 7) / 8; int last = cpu_count % 8; int i; if (bytes > 4095) { warning("cpumask can't handle more than 32768 CPUS!"); bytes = 4095; } sprintf(cpumask, "%x", (1 << last) - 1); for (i = 1; i < bytes; i++) cpumask[i] = 'f'; cpumask[i+1] = 0; mask = cpumask; } path = get_instance_file(instance, "tracing_cpumask"); if (!path) die("could not allocate path"); ret = stat(path, &st); if (ret < 0) { if (mask) warning("%s not found", path); goto out; } fd = open(path, O_WRONLY | O_TRUNC); if (fd < 0) die("could not open %s\n", path); if (mask) write(fd, mask, strlen(mask)); close(fd); out: tracecmd_put_tracing_file(path); } static void enable_events(struct buffer_instance *instance) { struct event_list *event; for (event = instance->events; event; event = event->next) { if (!event->neg) update_event(event, event->filter, 0, '1'); } /* Now disable any events */ for (event = instance->events; event; event = event->next) { if (event->neg) update_event(event, NULL, 0, '0'); } } static void set_clock(struct buffer_instance *instance) { char *path; char *content; char *str; if (!instance->clock) return; /* The current clock is in brackets, reset it when we are done */ content = read_instance_file(instance, "trace_clock", NULL); /* check if first clock is set */ if (*content == '[') str = strtok(content+1, "]"); else { str = strtok(content, "["); if (!str) die("Can not find clock in trace_clock"); str = strtok(NULL, "]"); } path = get_instance_file(instance, "trace_clock"); add_reset_file(path, str, RESET_DEFAULT_PRIO); free(content); tracecmd_put_tracing_file(path); write_instance_file(instance, "trace_clock", instance->clock, "clock"); } static struct event_list * create_event(struct buffer_instance *instance, char *path, struct event_list *old_event) { struct event_list *event; struct stat st; char *p; int ret; event = malloc_or_die(sizeof(*event)); *event = *old_event; add_event(instance, event); if (event->filter || filter_task || filter_pid) { event->filter_file = strdup(path); if (!event->filter_file) die("malloc filter file"); } for (p = path + strlen(path) - 1; p > path; p--) if (*p == '/') break; *p = '\0'; p = malloc_or_die(strlen(path) + strlen("/enable") + 1); sprintf(p, "%s/enable", path); ret = stat(p, &st); if (ret >= 0) event->enable_file = p; else free(p); if (event->trigger) { p = malloc_or_die(strlen(path) + strlen("/trigger") + 1); sprintf(p, "%s/trigger", path); ret = stat(p, &st); if (ret > 0) die("trigger specified but not supported by this kernel"); event->trigger_file = p; } return event; } static void make_sched_event(struct buffer_instance *instance, struct event_list **event, struct event_list *sched, const char *sched_path) { char *path; char *p; /* Do nothing if the event already exists */ if (*event) return; path = malloc_or_die(strlen(sched->filter_file) + strlen(sched_path) + 2); sprintf(path, "%s", sched->filter_file); /* Remove the /filter from filter file */ p = path + strlen(path) - strlen("filter"); sprintf(p, "%s/filter", sched_path); *event = create_event(instance, path, sched); free(path); } static void test_event(struct event_list *event, const char *path, const char *name, struct event_list **save, int len) { path += len - strlen(name); if (strcmp(path, name) != 0) return; *save = event; } static int expand_event_files(struct buffer_instance *instance, const char *file, struct event_list *old_event) { struct event_list **save_event_tail = instance->event_next; struct event_list *sched_event = NULL; struct event_list *event; glob_t globbuf; char *path; char *p; int ret; int i; p = malloc_or_die(strlen(file) + strlen("events//filter") + 1); sprintf(p, "events/%s/filter", file); path = get_instance_file(instance, p); printf("%s\n", path); globbuf.gl_offs = 0; ret = glob(path, 0, NULL, &globbuf); tracecmd_put_tracing_file(path); free(p); if (ret < 0) die("No filters found"); for (i = 0; i < globbuf.gl_pathc; i++) { int len; path = globbuf.gl_pathv[i]; event = create_event(instance, path, old_event); len = strlen(path); test_event(event, path, "sched", &sched_event, len); test_event(event, path, "sched/sched_switch", &instance->sched_switch_event, len); test_event(event, path, "sched/sched_wakeup_new", &instance->sched_wakeup_new_event, len); test_event(event, path, "sched/sched_wakeup", &instance->sched_wakeup_event, len); } if (sched_event && sched_event->filter_file) { /* make sure all sched events exist */ make_sched_event(instance, &instance->sched_switch_event, sched_event, "sched_switch"); make_sched_event(instance, &instance->sched_wakeup_event, sched_event, "sched_wakeup"); make_sched_event(instance, &instance->sched_wakeup_new_event, sched_event, "sched_wakeup_new"); } globfree(&globbuf); /* If the event list tail changed, that means events were added */ return save_event_tail == instance->event_next; } static void expand_event(struct buffer_instance *instance, struct event_list *event) { const char *name = event->event; char *str; char *ptr; int len; int ret; int ret2; /* * We allow the user to use "all" to enable all events. * Expand event_selection to all systems. */ if (strcmp(name, "all") == 0) { expand_event_files(instance, "*", event); return; } ptr = strchr(name, ':'); if (ptr) { len = ptr - name; str = malloc_or_die(strlen(name) + 1); /* may add '*' */ strcpy(str, name); str[len] = '/'; ptr++; if (!strlen(ptr)) { str[len + 1] = '*'; str[len + 2] = '\0'; } ret = expand_event_files(instance, str, event); if (!ignore_event_not_found && ret) die("No events enabled with %s", name); free(str); return; } /* No ':' so enable all matching systems and events */ ret = expand_event_files(instance, name, event); len = strlen(name) + strlen("*/") + 1; str = malloc_or_die(len); snprintf(str, len, "*/%s", name); ret2 = expand_event_files(instance, str, event); free(str); if (!ignore_event_not_found && ret && ret2) die("No events enabled with %s", name); } static void expand_event_instance(struct buffer_instance *instance) { struct event_list *compressed_list = instance->events; struct event_list *event; reset_event_list(instance); while (compressed_list) { event = compressed_list; compressed_list = event->next; expand_event(instance, event); free(event); } } static void expand_event_list(void) { struct buffer_instance *instance; if (use_old_event_method()) return; for_all_instances(instance) expand_event_instance(instance); } int count_cpus(void) { FILE *fp; char buf[1024]; int cpus = 0; char *pbuf; size_t *pn; size_t n; int r; cpus = sysconf(_SC_NPROCESSORS_CONF); if (cpus > 0) return cpus; warning("sysconf could not determine number of CPUS"); /* Do the hack to figure out # of CPUS */ n = 1024; pn = &n; pbuf = buf; fp = fopen("/proc/cpuinfo", "r"); if (!fp) die("Can not read cpuinfo"); while ((r = getline(&pbuf, pn, fp)) >= 0) { char *p; if (strncmp(buf, "processor", 9) != 0) continue; for (p = buf+9; isspace(*p); p++) ; if (*p == ':') cpus++; } fclose(fp); return cpus; } static void finish(int sig) { /* all done */ if (recorder) tracecmd_stop_recording(recorder); finished = 1; } static void flush(int sig) { if (recorder) tracecmd_stop_recording(recorder); } static void connect_port(int cpu) { struct addrinfo hints; struct addrinfo *results, *rp; int s; char buf[BUFSIZ]; snprintf(buf, BUFSIZ, "%d", client_ports[cpu]); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = use_tcp ? SOCK_STREAM : SOCK_DGRAM; s = getaddrinfo(host, buf, &hints, &results); if (s != 0) die("connecting to %s server %s:%s", use_tcp ? "TCP" : "UDP", host, buf); for (rp = results; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; close(sfd); } if (rp == NULL) die("Can not connect to %s server %s:%s", use_tcp ? "TCP" : "UDP", host, buf); freeaddrinfo(results); client_ports[cpu] = sfd; } static void set_prio(int prio) { struct sched_param sp; memset(&sp, 0, sizeof(sp)); sp.sched_priority = prio; if (sched_setscheduler(0, SCHED_FIFO, &sp) < 0) warning("failed to set priority"); } static struct tracecmd_recorder * create_recorder_instance_pipe(struct buffer_instance *instance, int cpu, int *brass) { struct tracecmd_recorder *recorder; unsigned flags = recorder_flags | TRACECMD_RECORD_BLOCK; char *path; if (instance->name) path = get_instance_dir(instance); else path = tracecmd_find_tracing_dir(); if (!path) die("malloc"); /* This is already the child */ close(brass[0]); recorder = tracecmd_create_buffer_recorder_fd(brass[1], cpu, flags, path); if (instance->name) tracecmd_put_tracing_file(path); return recorder; } static struct tracecmd_recorder * create_recorder_instance(struct buffer_instance *instance, const char *file, int cpu, int *brass) { struct tracecmd_recorder *record; char *path; if (brass) return create_recorder_instance_pipe(instance, cpu, brass); if (!instance->name) return tracecmd_create_recorder_maxkb(file, cpu, recorder_flags, max_kb); path = get_instance_dir(instance); record = tracecmd_create_buffer_recorder_maxkb(file, cpu, recorder_flags, path, max_kb); tracecmd_put_tracing_file(path); return record; } /* * If extract is set, then this is going to set up the recorder, * connections and exit as the tracing is serialized by a single thread. */ static int create_recorder(struct buffer_instance *instance, int cpu, enum trace_type type, int *brass) { long ret; char *file; int pid; /* network for buffer instances not supported yet */ if (client_ports && instance->name) return 0; if (type != TRACE_TYPE_EXTRACT) { signal(SIGUSR1, flush); pid = fork(); if (pid < 0) die("fork"); if (pid) return pid; if (rt_prio) set_prio(rt_prio); /* do not kill tasks on error */ cpu_count = 0; } if (client_ports) { connect_port(cpu); recorder = tracecmd_create_recorder_fd(client_ports[cpu], cpu, recorder_flags); } else { file = get_temp_file(instance, cpu); recorder = create_recorder_instance(instance, file, cpu, brass); put_temp_file(file); } if (!recorder) die ("can't create recorder"); if (type == TRACE_TYPE_EXTRACT) { ret = tracecmd_flush_recording(recorder); tracecmd_free_recorder(recorder); return ret; } while (!finished) { if (tracecmd_start_recording(recorder, sleep_time) < 0) break; } tracecmd_free_recorder(recorder); exit(0); } static void communicate_with_listener(int fd) { char buf[BUFSIZ]; ssize_t n; int cpu, i; n = read(fd, buf, 8); /* Make sure the server is the tracecmd server */ if (memcmp(buf, "tracecmd", 8) != 0) die("server not tracecmd server"); /* write the number of CPUs we have (in ASCII) */ sprintf(buf, "%d", cpu_count); /* include \0 */ write(fd, buf, strlen(buf)+1); /* write the pagesize (in ASCII) */ sprintf(buf, "%d", page_size); /* include \0 */ write(fd, buf, strlen(buf)+1); /* * If we are using IPV4 and our page size is greater than * or equal to 64K, we need to punt and use TCP. :-( */ /* TODO, test for ipv4 */ if (page_size >= UDP_MAX_PACKET) { warning("page size too big for UDP using TCP in live read"); use_tcp = 1; } if (use_tcp) { /* Send one option */ write(fd, "1", 2); /* Size 4 */ write(fd, "4", 2); /* use TCP */ write(fd, "TCP", 4); } else /* No options */ write(fd, "0", 2); client_ports = malloc_or_die(sizeof(int) * cpu_count); /* * Now we will receive back a comma deliminated list * of client ports to connect to. */ for (cpu = 0; cpu < cpu_count; cpu++) { for (i = 0; i < BUFSIZ; i++) { n = read(fd, buf+i, 1); if (n != 1) die("Error, reading server ports"); if (!buf[i] || buf[i] == ',') break; } if (i == BUFSIZ) die("read bad port number"); buf[i] = 0; client_ports[cpu] = atoi(buf); } } static void setup_network(void) { struct addrinfo hints; struct addrinfo *result, *rp; int sfd, s; char *server; char *port; char *p; if (!strchr(host, ':')) { server = strdup("localhost"); if (!server) die("alloctating server"); port = host; host = server; } else { host = strdup(host); if (!host) die("alloctating server"); server = strtok_r(host, ":", &p); port = strtok_r(NULL, ":", &p); } memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; s = getaddrinfo(server, port, &hints, &result); if (s != 0) die("getaddrinfo: %s", gai_strerror(s)); for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; close(sfd); } if (!rp) die("Can not connect to %s:%s", server, port); freeaddrinfo(result); communicate_with_listener(sfd); /* Now create the handle through this socket */ network_handle = tracecmd_create_init_fd_glob(sfd, listed_events); /* OK, we are all set, let'r rip! */ } static void finish_network(void) { close(sfd); free(host); } static void start_threads(enum trace_type type) { int profile = (type & TRACE_TYPE_PROFILE) == TRACE_TYPE_PROFILE; struct buffer_instance *instance; int *brass = NULL; int i = 0; int ret; if (host) setup_network(); /* make a thread for every CPU we have */ pids = malloc_or_die(sizeof(*pids) * cpu_count * (buffers + 1)); memset(pids, 0, sizeof(*pids) * cpu_count * (buffers + 1)); for_all_instances(instance) { int x; for (x = 0; x < cpu_count; x++) { if (type & TRACE_TYPE_STREAM) { brass = pids[i].brass; ret = pipe(brass); if (ret < 0) die("pipe"); pids[i].stream = trace_stream_init(instance, x, brass[0], cpu_count, profile, hooks); if (!pids[i].stream) die("Creating stream for %d", i); } else pids[i].brass[0] = -1; pids[i].cpu = x; pids[i].instance = instance; /* Make sure all output is flushed before forking */ fflush(stdout); pids[i++].pid = create_recorder(instance, x, type, brass); if (brass) close(brass[1]); } } recorder_threads = i; } static void append_buffer(struct tracecmd_output *handle, struct tracecmd_option *buffer_option, struct buffer_instance *instance, char **temp_files) { int i; for (i = 0; i < cpu_count; i++) temp_files[i] = get_temp_file(instance, i); tracecmd_append_buffer_cpu_data(handle, buffer_option, cpu_count, temp_files); for (i = 0; i < cpu_count; i++) put_temp_file(temp_files[i]); } static void add_buffer_stat(struct tracecmd_output *handle, struct buffer_instance *instance) { struct trace_seq s; int i; trace_seq_init(&s); trace_seq_printf(&s, "\nBuffer: %s\n\n", instance->name); tracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT, s.len+1, s.buffer); trace_seq_destroy(&s); for (i = 0; i < cpu_count; i++) tracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT, instance->s[i].len+1, instance->s[i].buffer); } static void add_option_hooks(struct tracecmd_output *handle) { struct hook_list *hook; int len; for (hook = hooks; hook; hook = hook->next) { len = strlen(hook->hook); tracecmd_add_option(handle, TRACECMD_OPTION_HOOK, len + 1, hook->hook); } } static void add_uname(struct tracecmd_output *handle) { struct utsname buf; char *str; int len; int ret; ret = uname(&buf); /* if this fails for some reason, just ignore it */ if (ret < 0) return; len = strlen(buf.sysname) + strlen(buf.nodename) + strlen(buf.release) + strlen(buf.machine) + 4; str = malloc(len); if (!str) return; sprintf(str, "%s %s %s %s", buf.sysname, buf.nodename, buf.release, buf.machine); tracecmd_add_option(handle, TRACECMD_OPTION_UNAME, len, str); free(str); } static void touch_file(const char *file) { int fd; fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) die("could not create file %s\n", file); close(fd); } static void record_data(char *date2ts) { struct tracecmd_option **buffer_options; struct tracecmd_output *handle; struct buffer_instance *instance; char **temp_files; int i; if (host) { finish_network(); return; } if (latency) handle = tracecmd_create_file_latency(output_file, cpu_count); else { if (!cpu_count) return; temp_files = malloc_or_die(sizeof(*temp_files) * cpu_count); for (i = 0; i < cpu_count; i++) temp_files[i] = get_temp_file(&top_instance, i); /* * If top_instance was not used, we still need to create * empty trace.dat files for it. */ if (no_top_instance()) { for (i = 0; i < cpu_count; i++) touch_file(temp_files[i]); } handle = tracecmd_create_init_file_glob(output_file, listed_events); if (!handle) die("Error creating output file"); if (date2ts) tracecmd_add_option(handle, TRACECMD_OPTION_DATE, strlen(date2ts)+1, date2ts); /* Only record the top instance under TRACECMD_OPTION_CPUSTAT*/ if (!no_top_instance()) { struct trace_seq *s = top_instance.s; for (i = 0; i < cpu_count; i++) tracecmd_add_option(handle, TRACECMD_OPTION_CPUSTAT, s[i].len+1, s[i].buffer); } tracecmd_add_option(handle, TRACECMD_OPTION_TRACECLOCK, 0, NULL); add_option_hooks(handle); add_uname(handle); if (buffers) { buffer_options = malloc_or_die(sizeof(*buffer_options) * buffers); i = 0; for_each_instance(instance) { buffer_options[i++] = tracecmd_add_buffer_option(handle, instance->name); add_buffer_stat(handle, instance); } } tracecmd_append_cpu_data(handle, cpu_count, temp_files); for (i = 0; i < cpu_count; i++) put_temp_file(temp_files[i]); if (buffers) { i = 0; for_each_instance(instance) { append_buffer(handle, buffer_options[i++], instance, temp_files); } } free(temp_files); } if (!handle) die("could not write to file"); tracecmd_output_close(handle); } static void write_func_file(struct buffer_instance *instance, const char *file, struct func_list **list) { struct func_list *item; char *path; int fd; int ret; path = get_instance_file(instance, file); fd = open(path, O_WRONLY | O_TRUNC); if (fd < 0) goto free; while (*list) { item = *list; *list = item->next; ret = write(fd, item->func, strlen(item->func)); if (ret < 0) goto failed; ret = write(fd, " ", 1); if (ret < 0) goto failed; free(item); } close(fd); free: tracecmd_put_tracing_file(path); return; failed: die("Failed to write %s to %s.\n" "Perhaps this function is not available for tracing.\n" "run 'trace-cmd list -f %s' to see if it is.", item->func, file, item->func); } static int functions_filtered(struct buffer_instance *instance) { char buf[1] = { '#' }; char *path; int fd; path = get_instance_file(instance, "set_ftrace_filter"); fd = open(path, O_RDONLY); tracecmd_put_tracing_file(path); if (fd < 0) { if (is_top_instance(instance)) warning("Can not set set_ftrace_filter"); else warning("Can not set set_ftrace_filter for %s", instance->name); return 0; } /* * If functions are not filtered, than the first character * will be '#'. Make sure it is not an '#' and also not space. */ read(fd, buf, 1); close(fd); if (buf[0] == '#' || isspace(buf[0])) return 0; return 1; } static void set_funcs(struct buffer_instance *instance) { write_func_file(instance, "set_ftrace_filter", &instance->filter_funcs); write_func_file(instance, "set_ftrace_notrace", &instance->notrace_funcs); /* graph tracing currently only works for top instance */ if (is_top_instance(instance)) write_func_file(instance, "set_graph_function", &graph_funcs); /* make sure we are filtering functions */ if (func_stack && is_top_instance(instance)) { if (!functions_filtered(instance)) die("Function stack trace set, but functions not filtered"); save_option(FUNC_STACK_TRACE); } clear_function_filters = 1; } static void add_func(struct func_list **list, const char *func) { struct func_list *item; item = malloc_or_die(sizeof(*item)); item->func = func; item->next = *list; *list = item; } static unsigned long long find_ts_in_page(struct pevent *pevent, void *page, int size) { struct event_format *event; struct format_field *field; struct pevent_record *last_record = NULL; struct pevent_record *record; unsigned long long ts = 0; int id; if (size <= 0) return 0; while (!ts) { record = tracecmd_read_page_record(pevent, page, size, last_record); if (!record) break; free_record(last_record); id = pevent_data_type(pevent, record); event = pevent_data_event_from_type(pevent, id); if (event) { /* Make sure this is our event */ field = pevent_find_field(event, "buf"); /* the trace_marker adds a '\n' */ if (field && strcmp(STAMP"\n", record->data + field->offset) == 0) ts = record->ts; } last_record = record; } free_record(last_record); return ts; } static unsigned long long find_time_stamp(struct pevent *pevent) { struct dirent *dent; unsigned long long ts = 0; void *page; char *path; char *file; DIR *dir; int len; int fd; int r; path = tracecmd_get_tracing_file("per_cpu"); if (!path) return 0; dir = opendir(path); if (!dir) goto out; len = strlen(path); file = malloc_or_die(len + strlen("trace_pipe_raw") + 32); page = malloc_or_die(page_size); while ((dent = readdir(dir))) { const char *name = dent->d_name; if (strncmp(name, "cpu", 3) != 0) continue; sprintf(file, "%s/%s/trace_pipe_raw", path, name); fd = open(file, O_RDONLY | O_NONBLOCK); if (fd < 0) continue; do { r = read(fd, page, page_size); ts = find_ts_in_page(pevent, page, r); if (ts) break; } while (r > 0); if (ts) break; } free(file); free(page); closedir(dir); out: tracecmd_put_tracing_file(path); return ts; } static char *read_instance_file(struct buffer_instance *instance, char *file, int *psize) { char buffer[BUFSIZ]; char *path; char *buf; int size = 0; int fd; int r; path = get_instance_file(instance, file); fd = open(path, O_RDONLY); tracecmd_put_tracing_file(path); if (fd < 0) { warning("%s not found, --date ignored", file); return NULL; } do { r = read(fd, buffer, BUFSIZ); if (r <= 0) continue; if (size) { buf = realloc(buf, size+r+1); if (!buf) die("malloc"); } else buf = malloc_or_die(r+1); memcpy(buf+size, buffer, r); size += r; } while (r); buf[size] = '\0'; if (psize) *psize = size; return buf; } static char *read_file(char *file, int *psize) { return read_instance_file(&top_instance, file, psize); } /* * Try to write the date into the ftrace buffer and then * read it back, mapping the timestamp to the date. */ static char *get_date_to_ts(void) { unsigned long long min = -1ULL; unsigned long long diff; unsigned long long stamp; unsigned long long min_stamp; unsigned long long min_ts; unsigned long long ts; struct pevent *pevent; struct timeval start; struct timeval end; char *date2ts = NULL; char *path; char *buf; int size; int tfd; int ret; int i; /* Set up a pevent to read the raw format */ pevent = pevent_alloc(); if (!pevent) { warning("failed to alloc pevent, --date ignored"); return NULL; } buf = read_file("events/header_page", &size); if (!buf) goto out_pevent; ret = pevent_parse_header_page(pevent, buf, size, sizeof(unsigned long)); free(buf); if (ret < 0) { warning("Can't parse header page, --date ignored"); goto out_pevent; } /* Find the format for ftrace:print. */ buf = read_file("events/ftrace/print/format", &size); if (!buf) goto out_pevent; ret = pevent_parse_event(pevent, buf, size, "ftrace"); free(buf); if (ret < 0) { warning("Can't parse print event, --date ignored"); goto out_pevent; } path = tracecmd_get_tracing_file("trace_marker"); tfd = open(path, O_WRONLY); tracecmd_put_tracing_file(path); if (tfd < 0) { warning("Can not open 'trace_marker', --date ignored"); goto out_pevent; } for (i = 0; i < date2ts_tries; i++) { disable_tracing(); clear_trace(); enable_tracing(); gettimeofday(&start, NULL); write(tfd, STAMP, 5); gettimeofday(&end, NULL); disable_tracing(); ts = find_time_stamp(pevent); if (!ts) continue; diff = (unsigned long long)end.tv_sec * 1000000; diff += (unsigned long long)end.tv_usec; stamp = diff; diff -= (unsigned long long)start.tv_sec * 1000000; diff -= (unsigned long long)start.tv_usec; if (diff < min) { min_ts = ts; min_stamp = stamp - diff / 2; min = diff; } } close(tfd); if (min == -1ULL) { warning("Failed to make date offset, --date ignored"); goto out_pevent; } /* 16 hex chars + 0x + \0 */ date2ts = malloc(19); if (!date2ts) goto out_pevent; /* * The difference between the timestamp and the gtod is * stored as an ASCII string in hex. */ snprintf(date2ts, 19, "0x%llx", min_stamp - min_ts / 1000); out_pevent: pevent_free(pevent); return date2ts; } static void set_buffer_size_instance(struct buffer_instance *instance) { int buffer_size = instance->buffer_size; char buf[BUFSIZ]; char *path; int ret; int fd; if (!buffer_size) return; if (buffer_size < 0) die("buffer size must be positive"); snprintf(buf, BUFSIZ, "%d", buffer_size); path = get_instance_file(instance, "buffer_size_kb"); fd = open(path, O_WRONLY); if (fd < 0) { warning("can't open %s", path); goto out; } ret = write(fd, buf, strlen(buf)); if (ret < 0) warning("Can't write to %s", path); close(fd); out: tracecmd_put_tracing_file(path); } void set_buffer_size(void) { struct buffer_instance *instance; for_all_instances(instance) set_buffer_size_instance(instance); } static void process_event_trigger(char *path, struct event_iter *iter, enum event_process *processed) { const char *system = iter->system_dent->d_name; const char *event = iter->event_dent->d_name; struct stat st; char *trigger = NULL; char *file; int ret; path = append_file(path, system); file = append_file(path, event); free(path); ret = stat(file, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) goto out; trigger = append_file(file, "trigger"); ret = stat(trigger, &st); if (ret < 0) goto out; clear_trigger(trigger); out: free(trigger); free(file); } static void clear_instance_triggers(struct buffer_instance *instance) { struct event_iter *iter; char *path; char *system; enum event_iter_type type; enum event_process processed = PROCESSED_NONE; path = get_instance_file(instance, "events"); if (!path) die("malloc"); iter = trace_event_iter_alloc(path); processed = PROCESSED_NONE; system = NULL; while ((type = trace_event_iter_next(iter, path, system))) { if (type == EVENT_ITER_SYSTEM) { system = iter->system_dent->d_name; continue; } process_event_trigger(path, iter, &processed); } trace_event_iter_free(iter); tracecmd_put_tracing_file(path); } static void process_event_filter(char *path, struct event_iter *iter, enum event_process *processed) { const char *system = iter->system_dent->d_name; const char *event = iter->event_dent->d_name; struct stat st; char *filter = NULL; char *file; int ret; path = append_file(path, system); file = append_file(path, event); free(path); ret = stat(file, &st); if (ret < 0 || !S_ISDIR(st.st_mode)) goto out; filter = append_file(file, "filter"); ret = stat(filter, &st); if (ret < 0) goto out; clear_filter(filter); out: free(filter); free(file); } static void clear_instance_filters(struct buffer_instance *instance) { struct event_iter *iter; char *path; char *system; enum event_iter_type type; enum event_process processed = PROCESSED_NONE; path = get_instance_file(instance, "events"); if (!path) die("malloc"); iter = trace_event_iter_alloc(path); processed = PROCESSED_NONE; system = NULL; while ((type = trace_event_iter_next(iter, path, system))) { if (type == EVENT_ITER_SYSTEM) { system = iter->system_dent->d_name; continue; } process_event_filter(path, iter, &processed); } trace_event_iter_free(iter); tracecmd_put_tracing_file(path); } static void clear_filters(void) { struct buffer_instance *instance; for_all_instances(instance) clear_instance_filters(instance); } static void clear_triggers(void) { struct buffer_instance *instance; for_all_instances(instance) clear_instance_triggers(instance); } static void clear_func_filters(void) { struct buffer_instance *instance; char *path; int i; const char const *files[] = { "set_ftrace_filter", "set_ftrace_notrace", "set_graph_function", "set_graph_notrace", NULL }; for_all_instances(instance) { for (i = 0; files[i]; i++) { path = get_instance_file(instance, files[i]); clear_func_filter(path); tracecmd_put_tracing_file(path); } } } static void make_instances(void) { struct buffer_instance *instance; struct stat st; char *path; int ret; for_each_instance(instance) { path = get_instance_dir(instance); ret = stat(path, &st); if (ret < 0) { ret = mkdir(path, 0777); if (ret < 0) die("mkdir %s", path); } else /* Don't delete instances that already exist */ instance->keep = 1; tracecmd_put_tracing_file(path); } } static void remove_instances(void) { struct buffer_instance *instance; char *path; int ret; for_each_instance(instance) { /* Only delete what we created */ if (instance->keep) continue; if (instance->tracing_on_fd > 0) { close(instance->tracing_on_fd); instance->tracing_on_fd = 0; } path = get_instance_dir(instance); ret = rmdir(path); if (ret < 0) die("rmdir %s", path); tracecmd_put_tracing_file(path); } } static void check_plugin(const char *plugin) { char *buf; char *str; char *tok; /* * nop is special. We may want to just trace * trace_printks, that are in the kernel. */ if (strcmp(plugin, "nop") == 0) return; buf = read_file("available_tracers", NULL); if (!buf) die("No plugins available"); str = buf; while ((tok = strtok(str, " "))) { str = NULL; if (strcmp(tok, plugin) == 0) goto out; } die ("Plugin '%s' does not exist", plugin); out: fprintf(stderr, " plugin '%s'\n", plugin); free(buf); } static void check_function_plugin(void) { const char *plugin; /* We only care about the top_instance */ if (no_top_instance()) return; plugin = top_instance.plugin; if (!plugin) return; if (plugin && strncmp(plugin, "function", 8) == 0 && func_stack && !top_instance.filter_funcs) die("Must supply function filtering with --func-stack\n"); } static int __check_doing_something(struct buffer_instance *instance) { return instance->profile || instance->plugin || instance->events; } static void check_doing_something(void) { struct buffer_instance *instance; for_all_instances(instance) { if (__check_doing_something(instance)) return; } die("no event or plugin was specified... aborting"); } static void update_plugin_instance(struct buffer_instance *instance, enum trace_type type) { const char *plugin = instance->plugin; if (!plugin) return; check_plugin(plugin); /* * Latency tracers just save the trace and kill * the threads. */ if (strcmp(plugin, "irqsoff") == 0 || strcmp(plugin, "preemptoff") == 0 || strcmp(plugin, "preemptirqsoff") == 0 || strcmp(plugin, "wakeup") == 0 || strcmp(plugin, "wakeup_rt") == 0) { latency = 1; if (host) die("Network tracing not available with latency tracer plugins"); if (type & TRACE_TYPE_STREAM) die("Streaming is not available with latency tracer plugins"); } else if (type == TRACE_TYPE_RECORD) { if (latency) die("Can not record latency tracer and non latency trace together"); } if (fset < 0 && (strcmp(plugin, "function") == 0 || strcmp(plugin, "function_graph") == 0)) die("function tracing not configured on this kernel"); if (type != TRACE_TYPE_EXTRACT) set_plugin_instance(instance, plugin); } static void update_plugins(enum trace_type type) { struct buffer_instance *instance; for_all_instances(instance) update_plugin_instance(instance, type); } static void allocate_seq(void) { struct buffer_instance *instance; for_all_instances(instance) instance->s = malloc_or_die(sizeof(struct trace_seq) * cpu_count); } static void record_stats(void) { struct buffer_instance *instance; struct trace_seq *s; int cpu; for_all_instances(instance) { s = instance->s; for (cpu = 0; cpu < cpu_count; cpu++) { trace_seq_init(&s[cpu]); trace_seq_printf(&s[cpu], "CPU: %d\n", cpu); tracecmd_stat_cpu_instance(instance, &s[cpu], cpu); } } } static void print_stats(void) { struct buffer_instance *instance; int cpu; for_all_instances(instance) { if (!is_top_instance(instance)) { if (instance != first_instance) printf("\n"); printf("Buffer: %s\n\n", instance->name); } for (cpu = 0; cpu < cpu_count; cpu++) { trace_seq_do_printf(&instance->s[cpu]); printf("\n"); } } } static void destroy_stats(void) { struct buffer_instance *instance; int cpu; for_all_instances(instance) { for (cpu = 0; cpu < cpu_count; cpu++) trace_seq_destroy(&instance->s[cpu]); } } static void list_event(const char *event) { struct tracecmd_event_list *list; list = malloc_or_die(sizeof(*list)); list->next = listed_events; list->glob = event; listed_events = list; } #define ALL_EVENTS "*/*" static void record_all_events(void) { struct tracecmd_event_list *list; while (listed_events) { list = listed_events; listed_events = list->next; free(list); } list = malloc_or_die(sizeof(*list)); list->next = NULL; list->glob = ALL_EVENTS; listed_events = list; } static int recording_all_events(void) { return listed_events && strcmp(listed_events->glob, ALL_EVENTS) == 0; } static void add_trigger(struct event_list *event, const char *trigger) { if (event->trigger) { event->trigger = realloc(event->trigger, strlen(event->trigger) + strlen("\n") + strlen(trigger) + 1); strcat(event->trigger, "\n"); strcat(event->trigger, trigger); } else { event->trigger = malloc_or_die(strlen(trigger) + 1); sprintf(event->trigger, "%s", trigger); } } static int test_stacktrace_trigger(struct buffer_instance *instance) { char *path; int ret = 0; int fd; path = get_instance_file(instance, "events/sched/sched_switch/trigger"); clear_trigger(path); fd = open(path, O_WRONLY); if (fd < 0) goto out; ret = write(fd, "stacktrace", 10); if (ret != 10) ret = 0; else ret = 1; close(fd); out: tracecmd_put_tracing_file(path); return ret; } static int profile_add_event(struct buffer_instance *instance, const char *event_str, int stack) { struct event_list *event; char buf[BUFSIZ]; char *p; strcpy(buf, "events/"); strncpy(buf + 7, event_str, BUFSIZ - 7); buf[BUFSIZ-1] = 0; if ((p = strstr(buf, ":"))) { *p = '/'; p++; } if (!trace_check_file_exists(instance, buf)) return -1; /* Only add event if it isn't already added */ for (event = instance->events; event; event = event->next) { if (p && strcmp(event->event, p) == 0) break; if (strcmp(event->event, event_str) == 0) break; } if (!event) { event = malloc_or_die(sizeof(*event)); memset(event, 0, sizeof(*event)); event->event = event_str; add_event(instance, event); } if (!recording_all_events()) list_event(event_str); if (stack) { if (!event->trigger || !strstr(event->trigger, "stacktrace")) add_trigger(event, "stacktrace"); } return 0; } static void enable_profile(struct buffer_instance *instance) { int stacktrace = 0; int ret; int i; char *trigger_events[] = { "sched:sched_switch", "sched:sched_wakeup", NULL, }; char *events[] = { "exceptions:page_fault_user", "irq:irq_handler_entry", "irq:irq_handler_exit", "irq:softirq_entry", "irq:softirq_exit", "irq:softirq_raise", "sched:sched_process_exec", "raw_syscalls", NULL, }; if (!instance->plugin) { if (trace_check_file_exists(instance, "max_graph_depth")) { instance->plugin = "function_graph"; ret = write_instance_file(instance, "max_graph_depth", "1", NULL); if (ret < 0) die("could not write to max_graph_depth"); } else warning("Kernel does not support max_graph_depth\n" " Skipping user/kernel profiling"); } if (test_stacktrace_trigger(instance)) stacktrace = 1; else /* * The stacktrace trigger is not implemented with this * kernel, then we need to default to the stack trace option. * This is less efficient but still works. */ save_option("stacktrace"); for (i = 0; trigger_events[i]; i++) profile_add_event(instance, trigger_events[i], stacktrace); for (i = 0; events[i]; i++) profile_add_event(instance, events[i], 0); } static struct event_list * create_hook_event(struct buffer_instance *instance, const char *system, const char *event) { struct event_list *event_list; char *event_name; int len; if (!system) system = "*"; len = strlen(event); len += strlen(system) + 2; event_name = malloc_or_die(len); sprintf(event_name, "%s:%s", system, event); event_list = malloc_or_die(sizeof(*event_list)); memset(event_list, 0, sizeof(*event_list)); event_list->event = event_name; add_event(instance, event_list); list_event(event_name); return event_list; } static void add_hook(struct buffer_instance *instance, const char *arg) { struct event_list *event; struct hook_list *hook; hook = tracecmd_create_event_hook(arg); hook->instance = instance; hook->next = hooks; hooks = hook; /* Make sure the event is enabled */ event = create_hook_event(instance, hook->start_system, hook->start_event); create_hook_event(instance, hook->end_system, hook->end_event); if (hook->stack) { if (!event->trigger || !strstr(event->trigger, "stacktrace")) add_trigger(event, "stacktrace"); } } enum { OPT_stderr = 251, OPT_profile = 252, OPT_nosplice = 253, OPT_funcstack = 254, OPT_date = 255, }; void trace_record (int argc, char **argv) { const char *plugin = NULL; const char *output = NULL; const char *option; struct event_list *event = NULL; struct event_list *last_event; struct buffer_instance *instance = &top_instance; enum trace_type type = 0; char *pids; char *pid; char *sav; char *date2ts = NULL; int record_all = 0; int total_disable = 0; int disable = 0; int events = 0; int record = 0; int extract = 0; int stream = 0; int profile = 0; int start = 0; int run_command = 0; int neg_event = 0; int date = 0; int manual = 0; int c; init_instance(instance); cpu_count = count_cpus(); if ((record = (strcmp(argv[1], "record") == 0))) ; /* do nothing */ else if ((start = strcmp(argv[1], "start") == 0)) ; /* do nothing */ else if ((extract = strcmp(argv[1], "extract") == 0)) ; /* do nothing */ else if ((stream = strcmp(argv[1], "stream") == 0)) ; /* do nothing */ else if ((profile = strcmp(argv[1], "profile") == 0)) { events = 1; } else if (strcmp(argv[1], "stop") == 0) { int topt = 0; for (;;) { int c; c = getopt(argc-1, argv+1, "tB:"); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'B': instance = create_instance(optarg); add_instance(instance); /* top instance requires direct access */ if (!topt && is_top_instance(first_instance)) first_instance = instance; break; case 't': /* Force to use top instance */ topt = 1; instance = &top_instance; first_instance = instance; break; default: usage(argv); } } disable_tracing(); exit(0); } else if (strcmp(argv[1], "restart") == 0) { int topt = 0; for (;;) { int c; c = getopt(argc-1, argv+1, "tB:"); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'B': instance = create_instance(optarg); add_instance(instance); /* top instance requires direct access */ if (!topt && is_top_instance(first_instance)) first_instance = instance; break; case 't': /* Force to use top instance */ topt = 1; instance = &top_instance; first_instance = instance; break; default: usage(argv); } } enable_tracing(); exit(0); } else if (strcmp(argv[1], "reset") == 0) { int topt = 0; while ((c = getopt(argc-1, argv+1, "b:B:td")) >= 0) { switch (c) { case 'b': instance->buffer_size = atoi(optarg); /* Min buffer size is 1 */ if (strcmp(optarg, "0") == 0) instance->buffer_size = 1; break; case 'B': instance = create_instance(optarg); add_instance(instance); /* -d will remove keep */ instance->keep = 1; /* top instance requires direct access */ if (!topt && is_top_instance(first_instance)) first_instance = instance; break; case 't': /* Force to use top instance */ topt = 1; instance = &top_instance; first_instance = instance; break; case 'd': if (is_top_instance(instance)) die("Can not delete top level buffer"); instance->keep = 0; break; } } disable_all(1); set_buffer_size(); clear_filters(); clear_triggers(); remove_instances(); clear_func_filters(); exit(0); } else usage(argv); for (;;) { int option_index = 0; const char *opts; static struct option long_options[] = { {"date", no_argument, NULL, OPT_date}, {"func-stack", no_argument, NULL, OPT_funcstack}, {"nosplice", no_argument, NULL, OPT_nosplice}, {"profile", no_argument, NULL, OPT_profile}, {"stderr", no_argument, NULL, OPT_stderr}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; if (extract) opts = "+haf:Fp:co:O:sr:g:l:n:P:N:tb:ksiT"; else opts = "+hae:f:Fp:cC:dDo:O:s:r:vg:l:n:P:N:tb:R:B:ksSiTm:M:H:"; c = getopt_long (argc-1, argv+1, opts, long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'a': if (!extract) { record_all = 1; record_all_events(); } break; case 'e': events = 1; event = malloc_or_die(sizeof(*event)); memset(event, 0, sizeof(*event)); event->event = optarg; add_event(instance, event); event->neg = neg_event; event->filter = NULL; last_event = event; if (!record_all) list_event(optarg); break; case 'f': if (!last_event) die("filter must come after event"); if (last_event->filter) { last_event->filter = realloc(last_event->filter, strlen(last_event->filter) + strlen("&&()") + strlen(optarg) + 1); strcat(last_event->filter, "&&("); strcat(last_event->filter, optarg); strcat(last_event->filter, ")"); } else { last_event->filter = malloc_or_die(strlen(optarg) + strlen("()") + 1); sprintf(last_event->filter, "(%s)", optarg); } break; case 'R': if (!last_event) die("trigger must come after event"); add_trigger(event, optarg); break; case 'F': filter_task = 1; break; case 'P': pids = strdup(optarg); if (!pids) die("strdup"); pid = strtok_r(pids, ",", &sav); while (pid) { add_filter_pid(atoi(pid)); pid = strtok_r(NULL, ",", &sav); } free(pids); break; case 'c': #ifdef NO_PTRACE die("-c invalid: ptrace not supported"); #endif do_ptrace = 1; break; case 'C': instance->clock = optarg; break; case 'v': neg_event = 1; break; case 'l': add_func(&instance->filter_funcs, optarg); break; case 'n': add_func(&instance->notrace_funcs, optarg); break; case 'g': add_func(&graph_funcs, optarg); break; case 'p': if (instance->plugin) die("only one plugin allowed"); for (plugin = optarg; isspace(*plugin); plugin++) ; instance->plugin = plugin; for (optarg += strlen(optarg) - 1; optarg > plugin && isspace(*optarg); optarg--) ; optarg++; optarg[0] = '\0'; break; case 'D': total_disable = 1; /* fall through */ case 'd': disable = 1; break; case 'o': if (host) die("-o incompatible with -N"); if (start) die("start does not take output\n" "Did you mean 'record'?"); if (stream) die("stream does not take output\n" "Did you mean 'record'?"); if (output) die("only one output file allowed"); output = optarg; if (profile) { int fd; /* pipe the output to this file instead of stdout */ save_stdout = dup(1); close(1); fd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) die("can't write to %s", optarg); if (fd != 1) { dup2(fd, 1); close(fd); } } break; case 'O': option = optarg; save_option(option); break; case 'T': save_option("stacktrace"); break; case 'H': add_hook(instance, optarg); events = 1; break; case 's': if (extract) { if (optarg) usage(argv); recorder_flags |= TRACECMD_RECORD_SNAPSHOT; break; } if (!optarg) usage(argv); sleep_time = atoi(optarg); break; case 'S': manual = 1; /* User sets events for profiling */ if (!event) events = 0; break; case 'r': rt_prio = atoi(optarg); break; case 'N': if (!record) die("-N only available with record"); if (output) die("-N incompatible with -o"); host = optarg; break; case 'm': if (max_kb) die("-m can only be specified once"); if (!record) die("only record take 'm' option"); max_kb = atoi(optarg); break; case 'M': instance->cpumask = optarg; break; case 't': use_tcp = 1; break; case 'b': instance->buffer_size = atoi(optarg); break; case 'B': instance = create_instance(optarg); add_instance(instance); if (profile) instance->profile = 1; break; case 'k': keep = 1; break; case 'i': ignore_event_not_found = 1; break; case OPT_date: date = 1; break; case OPT_funcstack: func_stack = 1; break; case OPT_nosplice: recorder_flags |= TRACECMD_RECORD_NOSPLICE; break; case OPT_profile: instance->profile = 1; events = 1; break; case OPT_stderr: /* if -o was used (for profile), ignore this */ if (save_stdout >= 0) break; save_stdout = dup(1); close(1); dup2(2, 1); break; default: usage(argv); } } if (do_ptrace && !filter_task && (filter_pid < 0)) die(" -c can only be used with -F or -P"); if ((argc - optind) >= 2) { if (start) die("Command start does not take any commands\n" "Did you mean 'record'?"); if (extract) die("Command extract does not take any commands\n" "Did you mean 'record'?"); run_command = 1; } /* * If this is a profile run, and no instances were set, * then enable profiling on the top instance. */ if (profile && !buffer_instances) top_instance.profile = 1; /* * If top_instance doesn't have any plugins or events, then * remove it from being processed. */ if (!extract && !__check_doing_something(&top_instance)) { if (!buffer_instances) die("No instances reference??"); first_instance = buffer_instances; } if (!extract) check_doing_something(); check_function_plugin(); if (output) output_file = output; /* Save the state of tracing_on before starting */ for_all_instances(instance) { if (!manual && instance->profile) enable_profile(instance); instance->tracing_on_init_val = read_tracing_on(instance); /* Some instances may not be created yet */ if (instance->tracing_on_init_val < 0) instance->tracing_on_init_val = 1; } /* Extracting data records all events in the system. */ if (extract && !record_all) record_all_events(); if (!extract) make_instances(); if (events) expand_event_list(); page_size = getpagesize(); if (!extract) { fset = set_ftrace(!disable, total_disable); disable_all(1); for_all_instances(instance) set_clock(instance); /* Record records the date first */ if (record && date) date2ts = get_date_to_ts(); for_all_instances(instance) { set_funcs(instance); set_mask(instance); } if (events) { for_all_instances(instance) enable_events(instance); } set_buffer_size(); } if (record) type = TRACE_TYPE_RECORD; else if (stream) type = TRACE_TYPE_STREAM; else if (extract) type = TRACE_TYPE_EXTRACT; else if (profile) /* PROFILE includes the STREAM bit */ type = TRACE_TYPE_PROFILE; else type = TRACE_TYPE_START; update_plugins(type); set_options(); allocate_seq(); if (type & (TRACE_TYPE_RECORD | TRACE_TYPE_STREAM)) { signal(SIGINT, finish); if (!latency) start_threads(type); } if (extract) { flush_threads(); } else { if (!(type & (TRACE_TYPE_RECORD | TRACE_TYPE_STREAM))) { update_task_filter(); enable_tracing(); exit(0); } if (run_command) run_cmd(type, (argc - optind) - 1, &argv[optind + 1]); else { update_task_filter(); enable_tracing(); /* We don't ptrace ourself */ if (do_ptrace && filter_pid >= 0) ptrace_attach(filter_pid); /* sleep till we are woken with Ctrl^C */ printf("Hit Ctrl^C to stop recording\n"); while (!finished) trace_or_sleep(type); } disable_tracing(); if (!latency) stop_threads(type); } record_stats(); if (!keep) disable_all(0); printf("Kernel buffer statistics:\n" " Note: \"entries\" are the entries left in the kernel ring buffer and are not\n" " recorded in the trace data. They should all be zero.\n\n"); print_stats(); /* extract records the date after extraction */ if (extract && date) { /* * We need to start tracing, don't let other traces * screw with our trace_marker. */ disable_all(1); date2ts = get_date_to_ts(); } if (record || extract) { record_data(date2ts); delete_thread_data(); } destroy_stats(); if (keep) exit(0); update_reset_files(); update_reset_triggers(); if (clear_function_filters) clear_func_filters(); set_plugin("nop"); remove_instances(); /* If tracing_on was enabled before we started, set it on now */ for_all_instances(instance) { if (instance->keep) write_tracing_on(instance, instance->tracing_on_init_val); } if (host) tracecmd_output_close(network_handle); if (profile) trace_profile(); exit(0); } trace-cmd-2.5.3/trace-recorder.c000066400000000000000000000242601246701203100164250ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd.h" struct tracecmd_recorder { int fd; int fd1; int fd2; int trace_fd; int brass[2]; 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->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 ret; recorder = malloc_or_die(sizeof(*recorder)); if (!recorder) return NULL; recorder->cpu = cpu; recorder->flags = flags; recorder->fd_flags = 1; /* SPLICE_F_MOVE */ if (!(recorder->flags & TRACECMD_RECORD_BLOCK)) recorder->fd_flags |= 2; /* and NON_BLOCK */ /* 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; path = malloc_or_die(strlen(buffer) + 40); if (!path) goto out_free; if (flags & TRACECMD_RECORD_SNAPSHOT) sprintf(path, "%s/per_cpu/cpu%d/snapshot_raw", buffer, cpu); else sprintf(path, "%s/per_cpu/cpu%d/trace_pipe_raw", buffer, cpu); recorder->trace_fd = open(path, O_RDONLY); if (recorder->trace_fd < 0) goto out_free; free(path); if ((recorder->flags & TRACECMD_RECORD_NOSPLICE) == 0) { ret = pipe(recorder->brass); if (ret < 0) goto out_free; } 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 ret; ret = splice(recorder->trace_fd, NULL, recorder->brass[1], NULL, recorder->page_size, 1 /* SPLICE_F_MOVE */); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in splice input"); return -1; } if (errno == EINTR) return 0; } else if (ret == 0) return 0; ret = splice(recorder->brass[0], NULL, recorder->fd, NULL, recorder->page_size, recorder->fd_flags); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in splice output"); return -1; } ret = 0; } else update_fd(recorder, ret); return ret; } /* * Returns -1 on error. * or bytes of data read. */ static long read_data(struct tracecmd_recorder *recorder) { char buf[recorder->page_size]; long ret; ret = read(recorder->trace_fd, buf, recorder->page_size); if (ret < 0) { if (errno != EAGAIN && errno != EINTR) { warning("recorder error in read output"); return -1; } ret = 0; } if (ret > 0) { write(recorder->fd, buf, ret); update_fd(recorder, ret); } return ret; } 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 |= 2; /* NON_BLOCK */ } 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; long read = 1; long ret; recorder->stop = 0; do { /* Only sleep if we did not read anything last time */ if (!read && sleep) { req.tv_sec = sleep / 1000000; req.tv_nsec = (sleep % 1000000) * 1000; 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.5.3/trace-restore.c000066400000000000000000000071361246701203100163060ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-local.h" void trace_restore (int argc, char **argv) { struct tracecmd_output *handle; const char *output_file = "trace.dat"; const char *output = NULL; const char *input = NULL; const char *tracing_dir = NULL; const char *kallsyms = NULL; struct stat st1; struct stat st2; int first_arg; int create_only = 0; int args; int c; if (argc < 2) usage(argv); if (strcmp(argv[1], "restore") != 0) usage(argv); while ((c = getopt(argc-1, argv+1, "+hco:i:t:k:")) >= 0) { switch (c) { case 'h': usage(argv); break; case 'c': if (input) die("-c and -i are incompatible"); create_only = 1; /* make output default to partial */ output_file = "trace-partial.dat"; break; case 't': tracing_dir = optarg; break; case 'k': kallsyms = optarg; break; case 'o': if (output) die("only one output file allowed"); output = optarg; break; case 'i': if (input) die("only one input file allowed"); if (create_only) die("-c and -i are incompatible"); input = optarg; break; default: usage(argv); } } if (!output) output = output_file; if ((argc - optind) <= 1) { if (!create_only) { warning("No data files found"); usage(argv); } handle = tracecmd_create_init_file_override(output, tracing_dir, kallsyms); if (!handle) die("Unabled to create output file %s", output); tracecmd_output_close(handle); exit(0); } first_arg = optind + 1; args = argc - first_arg; printf("first = %d %s args=%d\n", first_arg, argv[first_arg], args); /* Make sure input and output are not the same file */ if (input && output) { if (stat(input, &st1) < 0) die("%s:", input); /* output exists? otherwise we don't care */ if (stat(output, &st2) == 0) { if (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev) die("input and output file are the same"); } } if (input) { struct tracecmd_input *ihandle; ihandle = tracecmd_alloc(input); if (!ihandle) die("error reading file %s", input); /* make sure headers are ok */ if (tracecmd_read_headers(ihandle) < 0) die("error reading file %s headers", input); handle = tracecmd_copy(ihandle, output); tracecmd_close(ihandle); } else handle = tracecmd_create_init_file(output); if (!handle) die("error writing to %s", output); if (tracecmd_append_cpu_data(handle, args, &argv[first_arg]) < 0) die("failed to append data"); return; } trace-cmd-2.5.3/trace-seq.c000066400000000000000000000133521246701203100154100ustar00rootroot00000000000000/* * Copyright (C) 2009 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include "bug.h" #include "event-parse.h" #include "event-utils.h" /* * The TRACE_SEQ_POISON is to catch the use of using * a trace_seq structure after it was destroyed. */ #define TRACE_SEQ_POISON ((void *)0xdeadbeef) #define TRACE_SEQ_CHECK(s) \ do { \ if (WARN_ONCE((s)->buffer == TRACE_SEQ_POISON, \ "Usage of trace_seq after it was destroyed")) \ (s)->state = TRACE_SEQ__BUFFER_POISONED; \ } while (0) #define TRACE_SEQ_CHECK_RET_N(s, n) \ do { \ TRACE_SEQ_CHECK(s); \ if ((s)->state != TRACE_SEQ__GOOD) \ return n; \ } while (0) #define TRACE_SEQ_CHECK_RET(s) TRACE_SEQ_CHECK_RET_N(s, ) #define TRACE_SEQ_CHECK_RET0(s) TRACE_SEQ_CHECK_RET_N(s, 0) /** * trace_seq_init - initialize the trace_seq structure * @s: a pointer to the trace_seq structure to initialize */ void trace_seq_init(struct trace_seq *s) { s->len = 0; s->readpos = 0; s->buffer_size = TRACE_SEQ_BUF_SIZE; s->buffer = malloc(s->buffer_size); if (s->buffer != NULL) s->state = TRACE_SEQ__GOOD; else s->state = TRACE_SEQ__MEM_ALLOC_FAILED; } /** * trace_seq_reset - re-initialize the trace_seq structure * @s: a pointer to the trace_seq structure to reset */ void trace_seq_reset(struct trace_seq *s) { if (!s) return; TRACE_SEQ_CHECK(s); s->len = 0; s->readpos = 0; } /** * trace_seq_destroy - free up memory of a trace_seq * @s: a pointer to the trace_seq to free the buffer * * Only frees the buffer, not the trace_seq struct itself. */ void trace_seq_destroy(struct trace_seq *s) { if (!s) return; TRACE_SEQ_CHECK_RET(s); free(s->buffer); s->buffer = TRACE_SEQ_POISON; } static void expand_buffer(struct trace_seq *s) { char *buf; buf = realloc(s->buffer, s->buffer_size + TRACE_SEQ_BUF_SIZE); if (WARN_ONCE(!buf, "Can't allocate trace_seq buffer memory")) { s->state = TRACE_SEQ__MEM_ALLOC_FAILED; return; } s->buffer = buf; s->buffer_size += TRACE_SEQ_BUF_SIZE; } /** * trace_seq_printf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * It returns 0 if the trace oversizes the buffer's free * space, 1 otherwise. * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) { va_list ap; int len; int ret; try_again: TRACE_SEQ_CHECK_RET0(s); len = (s->buffer_size - 1) - s->len; va_start(ap, fmt); ret = vsnprintf(s->buffer + s->len, len, fmt, ap); va_end(ap); if (ret >= len) { expand_buffer(s); goto try_again; } s->len += ret; return 1; } /** * trace_seq_vprintf - sequence printing of trace information * @s: trace sequence descriptor * @fmt: printf format string * * The tracer may use either sequence operations or its own * copy to user routines. To simplify formating of a trace * trace_seq_printf is used to store strings into a special * buffer (@s). Then the output may be either used by * the sequencer or pulled into another buffer. */ int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) { int len; int ret; try_again: TRACE_SEQ_CHECK_RET0(s); len = (s->buffer_size - 1) - s->len; ret = vsnprintf(s->buffer + s->len, len, fmt, args); if (ret >= len) { expand_buffer(s); goto try_again; } s->len += ret; return len; } /** * trace_seq_puts - trace sequence printing of simple string * @s: trace sequence descriptor * @str: simple string to record * * The tracer may use either the sequence operations or its own * copy to user routines. This function records a simple string * into a special buffer (@s) for later retrieval by a sequencer * or other mechanism. */ int trace_seq_puts(struct trace_seq *s, const char *str) { int len; TRACE_SEQ_CHECK_RET0(s); len = strlen(str); while (len > ((s->buffer_size - 1) - s->len)) expand_buffer(s); TRACE_SEQ_CHECK_RET0(s); memcpy(s->buffer + s->len, str, len); s->len += len; return len; } int trace_seq_putc(struct trace_seq *s, unsigned char c) { TRACE_SEQ_CHECK_RET0(s); while (s->len >= (s->buffer_size - 1)) expand_buffer(s); TRACE_SEQ_CHECK_RET0(s); s->buffer[s->len++] = c; return 1; } void trace_seq_terminate(struct trace_seq *s) { TRACE_SEQ_CHECK_RET(s); /* There's always one character left on the buffer */ s->buffer[s->len] = 0; } int trace_seq_do_printf(struct trace_seq *s) { TRACE_SEQ_CHECK(s); switch (s->state) { case TRACE_SEQ__GOOD: return printf("%.*s", s->len, s->buffer); case TRACE_SEQ__BUFFER_POISONED: puts("Usage of trace_seq after it was destroyed"); break; case TRACE_SEQ__MEM_ALLOC_FAILED: puts("Can't allocate trace_seq buffer memory"); break; } return -1; } trace-cmd-2.5.3/trace-snapshot.c000066400000000000000000000055421246701203100164610ustar00rootroot00000000000000/* * Copyright (C) 2013 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include "trace-local.h" static void write_file(const char *name, char *val) { char *path; int fd; ssize_t n; path = tracecmd_get_tracing_file(name); fd = open(path, O_WRONLY); if (fd < 0) die("writing %s", path); n = write(fd, val, strlen(val)); if (n < 0) die("failed to write '%d' to %s\n", path); tracecmd_put_tracing_file(path); close(fd); } void trace_snapshot (int argc, char **argv) { const char *buffer = NULL; const char *file = "snapshot"; struct stat st; char *name; char cpu_path[128]; int take_snap = 0; int reset_snap = 0; int free_snap = 0; int cpu = -1; int ret; int c; if (argc < 2) usage(argv); if (strcmp(argv[1], "snapshot") != 0) usage(argv); while ((c = getopt(argc-1, argv+1, "srfB:c:")) >= 0) { switch (c) { case 'h': usage(argv); break; case 's': take_snap = 1; if (free_snap) die("can't take snapshot and free it at the same time"); break; case 'f': free_snap = 1; if (take_snap) die("can't take snapshot and free it at the same time"); break; case 'r': reset_snap = 1; break; case 'B': if (buffer) die("Can only do one buffer at a time"); buffer = optarg; break; case 'c': if (cpu >= 0) die("Can only do one CPU (or all) at a time"); cpu = atoi(optarg); break; default: usage(argv); } } if (cpu >= 0) { snprintf(cpu_path, 128, "per_cpu/cpu%d/%s", cpu, file); file = cpu_path; } name = tracecmd_get_tracing_file(file); ret = stat(name, &st); if (ret < 0) die("Snapshot feature is not supported by this kernel"); tracecmd_put_tracing_file(name); if (!reset_snap && !take_snap && !free_snap) { show_file(file); exit(0); } if (reset_snap) write_file(file, "2"); if (free_snap) write_file(file, "0"); if (take_snap) write_file(file, "1"); } trace-cmd-2.5.3/trace-split.c000066400000000000000000000277521246701203100157640ustar00rootroot00000000000000/* * Copyright (C) 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-local.h" static unsigned int page_size; static const char *default_input_file = "trace.dat"; static const char *input_file; enum split_types { SPLIT_NONE, /* The order of these must be reverse of the case statement in the options */ SPLIT_SECONDS, SPLIT_MSECS, SPLIT_USECS, SPLIT_EVENTS, SPLIT_PAGES, SPLIT_NR_TYPES, }; struct cpu_data { unsigned long long ts; unsigned long long offset; struct pevent_record *record; int cpu; int fd; int index; void *commit; void *page; char *file; }; static int create_type_len(struct pevent *pevent, int time, int len) { static int bigendian = -1; char *ptr; int test; if (bigendian < 0) { test = 0x4321; ptr = (char *)&test; if (*ptr == 0x21) bigendian = 0; else bigendian = 1; } if (pevent->file_bigendian) time |= (len << 27); else time = (time << 5) | len; return __data2host4(pevent, time); } static int write_record(struct tracecmd_input *handle, struct pevent_record *record, struct cpu_data *cpu_data, enum split_types type) { unsigned long long diff; struct pevent *pevent; void *page; int len; char *ptr; int index = 0; int time; page = cpu_data->page; pevent = tracecmd_get_pevent(handle); ptr = page + cpu_data->index; diff = record->ts - cpu_data->ts; if (diff > (1 << 27)) { /* Add a time stamp */ len = RINGBUF_TYPE_TIME_EXTEND; time = (unsigned int)(diff & ((1ULL << 27) - 1)); time = create_type_len(pevent, time, len); *(unsigned *)ptr = time; ptr += 4; time = (unsigned int)(diff >> 27); *(unsigned *)ptr = __data2host4(pevent, time); cpu_data->ts = record->ts; cpu_data->index += 8; return 0; } if (record->size) { if (record->size < 28 * 4) len = record->size / 4; else len = 0; } time = (unsigned)diff; time = create_type_len(pevent, time, len); memcpy(ptr, &time, 4); ptr += 4; index = 4; if (!len) { len = record->size + 4; *(unsigned *)ptr = __data2host4(pevent, len); ptr += 4; index += 4; } len = (record->size + 3) & ~3; index += len; memcpy(ptr, record->data, len); cpu_data->index += index; cpu_data->ts = record->ts; return 1; } static void write_page(struct pevent *pevent, struct cpu_data *cpu_data, int long_size) { if (long_size == 8) *(unsigned long long *)cpu_data->commit = __data2host8(pevent, (unsigned long long)cpu_data->index - 16); else *(unsigned int *)cpu_data->commit = __data2host4(pevent, cpu_data->index - 12); write(cpu_data->fd, cpu_data->page, page_size); } static struct pevent_record *read_record(struct tracecmd_input *handle, int percpu, int *cpu) { if (percpu) return tracecmd_read_data(handle, *cpu); return tracecmd_read_next_data(handle, cpu); } static void set_cpu_time(struct tracecmd_input *handle, int percpu, unsigned long long start, int cpu, int cpus) { if (percpu) { tracecmd_set_cpu_to_timestamp(handle, cpu, start); return; } for (cpu = 0; cpu < cpus; cpu++) tracecmd_set_cpu_to_timestamp(handle, cpu, start); return; } static int parse_cpu(struct tracecmd_input *handle, struct cpu_data *cpu_data, unsigned long long start, unsigned long long end, int count_limit, int percpu, int cpu, enum split_types type) { struct pevent_record *record; struct pevent *pevent; void *ptr; int page_size; int long_size = 0; int cpus; int count = 0; int pages = 0; cpus = tracecmd_cpus(handle); long_size = tracecmd_long_size(handle); page_size = tracecmd_page_size(handle); pevent = tracecmd_get_pevent(handle); /* Force new creation of first page */ if (percpu) { cpu_data[cpu].index = page_size + 1; cpu_data[cpu].page = NULL; } else { for (cpu = 0; cpu < cpus; cpu++) { cpu_data[cpu].index = page_size + 1; cpu_data[cpu].page = NULL; } } /* * Get the cpu pointers up to the start of the * start time stamp. */ record = read_record(handle, percpu, &cpu); if (start) { set_cpu_time(handle, percpu, start, cpu, cpus); while (record && record->ts < start) { free_record(record); record = read_record(handle, percpu, &cpu); } } else if (record) start = record->ts; while (record && (!end || record->ts <= end)) { if (cpu_data[cpu].index + record->record_size > page_size) { if (type == SPLIT_PAGES && ++pages > count_limit) break; if (cpu_data[cpu].page) write_page(pevent, &cpu_data[cpu], long_size); else cpu_data[cpu].page = malloc_or_die(page_size); memset(cpu_data[cpu].page, 0, page_size); ptr = cpu_data[cpu].page; *(unsigned long long*)ptr = __data2host8(pevent, record->ts); cpu_data[cpu].ts = record->ts; ptr += 8; cpu_data[cpu].commit = ptr; ptr += long_size; cpu_data[cpu].index = 8 + long_size; } cpu_data[cpu].offset = record->offset; if (write_record(handle, record, &cpu_data[cpu], type)) { free_record(record); record = read_record(handle, percpu, &cpu); /* if we hit the end of the cpu, clear the offset */ if (!record) { if (percpu) cpu_data[cpu].offset = 0; else for (cpu = 0; cpu < cpus; cpu++) cpu_data[cpu].offset = 0; } switch (type) { case SPLIT_NONE: break; case SPLIT_SECONDS: if (record && record->ts > (start + (unsigned long long)count_limit * 1000000000ULL)) { free_record(record); record = NULL; } break; case SPLIT_MSECS: if (record && record->ts > (start + (unsigned long long)count_limit * 1000000ULL)) { free_record(record); record = NULL; } break; case SPLIT_USECS: if (record && record->ts > (start + (unsigned long long)count_limit * 1000ULL)) { free_record(record); record = NULL; } break; case SPLIT_EVENTS: if (++count >= count_limit) { free_record(record); record = NULL; } break; default: break; } } } if (record) free_record(record); if (percpu) { if (cpu_data[cpu].page) { write_page(pevent, &cpu_data[cpu], long_size); free(cpu_data[cpu].page); cpu_data[cpu].page = NULL; } } else { for (cpu = 0; cpu < cpus; cpu++) { if (cpu_data[cpu].page) { write_page(pevent, &cpu_data[cpu], long_size); free(cpu_data[cpu].page); cpu_data[cpu].page = NULL; } } } return 0; } static double parse_file(struct tracecmd_input *handle, const char *output_file, unsigned long long start, unsigned long long end, int percpu, int only_cpu, int count, enum split_types type) { unsigned long long current; struct tracecmd_output *ohandle; struct cpu_data *cpu_data; struct pevent_record *record; char **cpu_list; char *output; char *base; char *file; char *dir; int cpus; int cpu; int fd; output = strdup(output_file); dir = dirname(output); base = basename(output); ohandle = tracecmd_copy(handle, output_file); cpus = tracecmd_cpus(handle); cpu_data = malloc_or_die(sizeof(*cpu_data) * cpus); for (cpu = 0; cpu < cpus; cpu++) { file = malloc_or_die(strlen(output_file) + 50); sprintf(file, "%s/.tmp.%s.%d", dir, base, cpu); fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); cpu_data[cpu].cpu = cpu; cpu_data[cpu].fd = fd; cpu_data[cpu].file = file; cpu_data[cpu].offset = 0; if (start) tracecmd_set_cpu_to_timestamp(handle, cpu, start); } if (only_cpu >= 0) { parse_cpu(handle, cpu_data, start, end, count, 1, only_cpu, type); } else if (percpu) { for (cpu = 0; cpu < cpus; cpu++) parse_cpu(handle, cpu_data, start, end, count, percpu, cpu, type); } else parse_cpu(handle, cpu_data, start, end, count, percpu, -1, type); cpu_list = malloc_or_die(sizeof(*cpu_list) * cpus); for (cpu = 0; cpu < cpus; cpu ++) cpu_list[cpu] = cpu_data[cpu].file; tracecmd_append_cpu_data(ohandle, cpus, cpu_list); current = end; for (cpu = 0; cpu < cpus; cpu++) { /* Set the tracecmd cursor to the next set of records */ if (cpu_data[cpu].offset) { record = tracecmd_read_at(handle, cpu_data[cpu].offset, NULL); if (record && (!current || record->ts > current)) current = record->ts + 1; free_record(record); } unlink(cpu_data[cpu].file); free(cpu_data[cpu].file); } free(cpu_data); free(cpu_list); free(output); tracecmd_output_close(ohandle); return current; } void trace_split (int argc, char **argv) { struct tracecmd_input *handle; unsigned long long start_ns = 0, end_ns = 0; unsigned long long current; double start, end; char *endptr; char *output = NULL; char *output_file; enum split_types split_type = SPLIT_NONE; enum split_types type = SPLIT_NONE; int count; int repeat = 0; int percpu = 0; int cpu = -1; int ac; int c; if (strcmp(argv[1], "split") != 0) usage(argv); while ((c = getopt(argc-1, argv+1, "+ho:i:s:m:u:e:p:rcC:")) >= 0) { switch (c) { case 'h': usage(argv); break; case 'p': type++; case 'e': type++; case 'u': type++; case 'm': type++; case 's': type++; if (split_type != SPLIT_NONE) die("Only one type of split is allowed"); count = atoi(optarg); if (count <= 0) die("Units must be greater than 0"); split_type = type; /* Spliting by pages only makes sense per cpu */ if (type == SPLIT_PAGES) percpu = 1; break; case 'r': repeat = 1; break; case 'c': percpu = 1; break; case 'C': cpu = atoi(optarg); break; case 'o': if (output) die("only one output file allowed"); output = strdup(optarg); break; case 'i': input_file = optarg; break; default: usage(argv); } } ac = (argc - optind); if (ac >= 2) { optind++; start = strtod(argv[optind], &endptr); if (ac > 3) usage(argv); /* Make sure a true start value was entered */ if (*endptr != 0) die("Start value not floating point: %s", argv[optind]); start_ns = (unsigned long long)(start * 1000000000.0); optind++; if (ac == 3) { end = strtod(argv[optind], &endptr); /* Make sure a true end value was entered */ if (*endptr != 0) die("End value not floating point: %s", argv[optind]); end_ns = (unsigned long long)(end * 1000000000.0); if (end_ns < start_ns) die("Error: end is less than start"); } } if (!input_file) input_file = default_input_file; handle = tracecmd_open(input_file); if (!handle) die("error reading %s", input_file); page_size = tracecmd_page_size(handle); if (!output) output = strdup(input_file); if (!repeat) { output = realloc(output, strlen(output) + 3); strcat(output, ".1"); } current = start_ns; output_file = malloc_or_die(strlen(output) + 50); c = 1; do { if (repeat) sprintf(output_file, "%s.%04d", output, c++); else strcpy(output_file, output); current = parse_file(handle, output_file, start_ns, end_ns, percpu, cpu, count, type); if (!repeat) break; start_ns = 0; } while (current && (!end_ns || current < end_ns)); free(output); free(output_file); tracecmd_close(handle); return; } trace-cmd-2.5.3/trace-stack.c000066400000000000000000000100601246701203100157160ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include "trace-local.h" #define PROC_FILE "/proc/sys/kernel/stack_tracer_enabled" enum stack_type { STACK_START, STACK_STOP, STACK_RESET, STACK_REPORT }; static void test_available(void) { struct stat buf; int fd; fd = stat(PROC_FILE, &buf); if (fd < 0) die("stack tracer not configured on running kernel"); } static char read_proc(void) { char buf[1]; int fd; int n; fd = open(PROC_FILE, O_RDONLY); if (fd < 0) die("reading %s", PROC_FILE); n = read(fd, buf, 1); close(fd); if (n != 1) die("error reading %s", PROC_FILE); return buf[0]; } static void start_stop_trace(char val) { char buf[1]; int fd; int n; buf[0] = read_proc(); if (buf[0] == val) return; fd = open(PROC_FILE, O_WRONLY); if (fd < 0) die("writing %s", PROC_FILE); buf[0] = val; n = write(fd, buf, 1); if (n < 0) die("writing into %s", PROC_FILE); close(fd); } static void start_trace(void) { start_stop_trace('1'); } static void stop_trace(void) { start_stop_trace('0'); } static void reset_trace(void) { char *path; char buf[1]; int fd; int n; path = tracecmd_get_tracing_file("stack_max_size"); fd = open(path, O_WRONLY); if (fd < 0) die("writing %s", path); buf[0] = '0'; n = write(fd, buf, 1); if (n < 0) die("writing into %s", path); tracecmd_put_tracing_file(path); close(fd); } static void read_trace(void) { FILE *fp; char *path; char *buf = NULL; size_t n; int r; if (read_proc() == '1') printf("(stack tracer running)\n"); else printf("(stack tracer not running)\n"); path = tracecmd_get_tracing_file("stack_trace"); fp = fopen(path, "r"); if (!fp) die("reading to '%s'", path); tracecmd_put_tracing_file(path); while ((r = getline(&buf, &n, fp)) >= 0) { /* * Skip any line that starts with a '#'. * Those talk about how to enable stack tracing * within the debugfs system. We don't care about that. */ if (buf[0] != '#') printf("%s", buf); free(buf); buf = NULL; } fclose(fp); } enum { OPT_reset = 253, OPT_stop = 254, OPT_start = 255, }; void trace_stack (int argc, char **argv) { enum stack_type trace_type = STACK_REPORT; int c; if (argc < 2) usage(argv); if (strcmp(argv[1], "stack") != 0) usage(argv); for (;;) { int option_index = 0; static struct option long_options[] = { {"start", no_argument, NULL, OPT_start}, {"stop", no_argument, NULL, OPT_stop}, {"reset", no_argument, NULL, OPT_reset}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; c = getopt_long (argc-1, argv+1, "+h?", long_options, &option_index); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case OPT_start: trace_type = STACK_START; break; case OPT_stop: trace_type = STACK_STOP; break; case OPT_reset: trace_type = STACK_RESET; break; default: usage(argv); } } test_available(); switch (trace_type) { case STACK_START: start_trace(); break; case STACK_STOP: stop_trace(); break; case STACK_RESET: reset_trace(); break; default: read_trace(); break; } return; } trace-cmd-2.5.3/trace-stat.c000066400000000000000000000411051246701203100155700ustar00rootroot00000000000000/* * Copyright (C) 2014 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include "trace-local.h" #ifndef BUFSIZ #define BUFSIZ 1024 #endif static inline int is_top_instance(struct buffer_instance *instance) { return instance == &top_instance; } static int get_instance_file_fd(struct buffer_instance *instance, const char *file) { char *path; int fd; path = get_instance_file(instance, file); fd = open(path, O_RDONLY); tracecmd_put_tracing_file(path); return fd; } char *strstrip(char *str) { char *s; if (!str) return NULL; s = str + strlen(str) - 1; while (s >= str && isspace(*s)) s--; s++; *s = '\0'; for (s = str; *s && isspace(*s); s++) ; return s; } char *append_file(const char *dir, const char *name) { char *file; file = malloc_or_die(strlen(dir) + strlen(name) + 2); if (!file) die("malloc"); sprintf(file, "%s/%s", dir, name); return file; } static char *get_fd_content(int fd, const char *file) { char *str = NULL; int cnt = 0; int ret; for (;;) { str = realloc(str, BUFSIZ * ++cnt); if (!str) die("malloc"); ret = read(fd, str + BUFSIZ * (cnt - 1), BUFSIZ); if (ret < 0) die("reading %s\n", file); if (ret < BUFSIZ) break; } str[BUFSIZ * (cnt-1) + ret] = 0; return str; } char *get_file_content(const char *file) { char *str; int fd; fd = open(file, O_RDONLY); if (fd < 0) return NULL; str = get_fd_content(fd, file); close(fd); return str; } static char *get_instance_file_content(struct buffer_instance *instance, const char *file) { char *str = NULL; int fd; fd = get_instance_file_fd(instance, file); if (fd < 0) return NULL; str = get_fd_content(fd, file); close(fd); return str; } static void report_plugin(struct buffer_instance *instance) { char *str; char *cont; str = get_instance_file_content(instance, "current_tracer"); if (!str) return; cont = strstrip(str); /* We only care if the plugin is something other than nop */ if (strcmp(cont, "nop") == 0) goto out; printf("\nTracer: %s\n", cont); out: free(str); } struct event_iter *trace_event_iter_alloc(const char *path) { struct event_iter *iter; iter = malloc_or_die(sizeof(*iter)); memset(iter, 0, sizeof(*iter)); iter->system_dir = opendir(path); if (!iter->system_dir) die("opendir"); return iter; } enum event_iter_type trace_event_iter_next(struct event_iter *iter, const char *path, const char *system) { struct dirent *dent; if (system && !iter->event_dir) { char *event; struct stat st; event = append_file(path, system); stat(event, &st); if (!S_ISDIR(st.st_mode)) { free(event); goto do_system; } iter->event_dir = opendir(event); if (!iter->event_dir) die("opendir %s", event); free(event); } if (iter->event_dir) { while ((dent = readdir(iter->event_dir))) { const char *name = dent->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; iter->event_dent = dent; return EVENT_ITER_EVENT; } closedir(iter->event_dir); iter->event_dir = NULL; } do_system: while ((dent = readdir(iter->system_dir))) { const char *name = dent->d_name; if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue; iter->system_dent = dent; return EVENT_ITER_SYSTEM; } return EVENT_ITER_NONE; } void trace_event_iter_free(struct event_iter *iter) { if (!iter) return; if (iter->event_dir) closedir(iter->event_dir); closedir(iter->system_dir); free(iter); } static void reset_event_iter(struct event_iter *iter) { if (iter->event_dir) { closedir(iter->event_dir); iter->event_dir = NULL; } rewinddir(iter->system_dir); } static int process_individual_events(const char *path, struct event_iter *iter) { struct stat st; const char *system = iter->system_dent->d_name; char *file; char *enable = NULL; char *str; int ret = 0; file = append_file(path, system); stat(file, &st); if (!S_ISDIR(st.st_mode)) goto out; enable = append_file(file, "enable"); str = get_file_content(enable); if (!str) goto out; if (*str != '1' && *str != '0') ret = 1; free(str); out: free(enable); free(file); return ret; } static void process_event_enable(char *path, const char *system, const char *name, enum event_process *processed) { struct stat st; char *enable = NULL; char *file; char *str; if (system) path = append_file(path, system); file = append_file(path, name); if (system) free(path); stat(file, &st); if (!S_ISDIR(st.st_mode)) goto out; enable = append_file(file, "enable"); str = get_file_content(enable); if (!str) goto out; if (*str == '1') { if (!system) { if (!*processed) printf(" Individual systems:\n"); printf( " %s\n", name); *processed = PROCESSED_SYSTEM; } else { if (!*processed) { printf(" Individual events:\n"); *processed = PROCESSED_SYSTEM; } if (*processed == PROCESSED_SYSTEM) { printf(" %s\n", system); *processed = PROCESSED_EVENT; } printf( " %s\n", name); } } free(str); out: free(enable); free(file); } static void report_events(struct buffer_instance *instance) { struct event_iter *iter; char *str; char *cont; char *path; char *system; enum event_iter_type type; enum event_process processed = PROCESSED_NONE; str = get_instance_file_content(instance, "events/enable"); if (!str) return; cont = strstrip(str); printf("\nEvents:\n"); switch(*cont) { case '1': printf(" All enabled\n"); free(str); return; case '0': printf(" All disabled\n"); free(str); return; } free(str); path = get_instance_file(instance, "events"); if (!path) die("malloc"); iter = trace_event_iter_alloc(path); while (trace_event_iter_next(iter, path, NULL)) { process_event_enable(path, NULL, iter->system_dent->d_name, &processed); } reset_event_iter(iter); processed = PROCESSED_NONE; system = NULL; while ((type = trace_event_iter_next(iter, path, system))) { if (type == EVENT_ITER_SYSTEM) { /* Only process systems that are not fully enabled */ if (!process_individual_events(path, iter)) continue; system = iter->system_dent->d_name; if (processed) processed = PROCESSED_SYSTEM; continue; } process_event_enable(path, iter->system_dent->d_name, iter->event_dent->d_name, &processed); } trace_event_iter_free(iter); if (!processed) printf(" (none enabled)\n"); tracecmd_put_tracing_file(path); } static void process_event_filter(char *path, struct event_iter *iter, enum event_process *processed) { const char *system = iter->system_dent->d_name; const char *event = iter->event_dent->d_name; struct stat st; char *filter = NULL; char *file; char *str; char *cont; path = append_file(path, system); file = append_file(path, event); free(path); stat(file, &st); if (!S_ISDIR(st.st_mode)) goto out; filter = append_file(file, "filter"); str = get_file_content(filter); if (!str) goto out; cont = strstrip(str); if (strcmp(cont, "none") == 0) { free(str); goto out; } if (!*processed) printf("\nFilters:\n"); printf( " %s:%s \"%s\"\n", system, event, cont); *processed = PROCESSED_SYSTEM; free(str); out: free(filter); free(file); } static void report_event_filters(struct buffer_instance *instance) { struct event_iter *iter; char *path; char *system; enum event_iter_type type; enum event_process processed = PROCESSED_NONE; path = get_instance_file(instance, "events"); if (!path) die("malloc"); iter = trace_event_iter_alloc(path); processed = PROCESSED_NONE; system = NULL; while ((type = trace_event_iter_next(iter, path, system))) { if (type == EVENT_ITER_SYSTEM) { system = iter->system_dent->d_name; continue; } process_event_filter(path, iter, &processed); } trace_event_iter_free(iter); tracecmd_put_tracing_file(path); } static void process_event_trigger(char *path, struct event_iter *iter, enum event_process *processed) { const char *system = iter->system_dent->d_name; const char *event = iter->event_dent->d_name; struct stat st; char *trigger = NULL; char *file; char *str; char *cont; path = append_file(path, system); file = append_file(path, event); free(path); stat(file, &st); if (!S_ISDIR(st.st_mode)) goto out; trigger = append_file(file, "trigger"); str = get_file_content(trigger); if (!str) goto out; cont = strstrip(str); if (cont[0] == '#') { free(str); goto out; } if (!*processed) printf("\nTriggers:\n"); printf( " %s:%s \"%s\"\n", system, event, cont); *processed = PROCESSED_SYSTEM; free(str); out: free(trigger); free(file); } static void report_event_triggers(struct buffer_instance *instance) { struct event_iter *iter; char *path; char *system; enum event_iter_type type; enum event_process processed = PROCESSED_NONE; path = get_instance_file(instance, "events"); if (!path) die("malloc"); iter = trace_event_iter_alloc(path); processed = PROCESSED_NONE; system = NULL; while ((type = trace_event_iter_next(iter, path, system))) { if (type == EVENT_ITER_SYSTEM) { system = iter->system_dent->d_name; continue; } process_event_trigger(path, iter, &processed); } trace_event_iter_free(iter); tracecmd_put_tracing_file(path); } enum func_states { FUNC_STATE_START, FUNC_STATE_SKIP, FUNC_STATE_PRINT, }; static void list_functions(const char *path, char *string) { enum func_states state; struct stat st; char *str; int ret = 0; int len; int i; int first = 0; /* Ignore if it does not exist. */ ret = stat(path, &st); if (ret < 0) return; str = get_file_content(path); if (!str) return; len = strlen(str); state = FUNC_STATE_START; /* Skip all lines that start with '#' */ for (i = 0; i < len; i++) { if (state == FUNC_STATE_PRINT) putchar(str[i]); if (str[i] == '\n') { state = FUNC_STATE_START; continue; } if (state == FUNC_STATE_SKIP) continue; if (state == FUNC_STATE_START && str[i] == '#') { state = FUNC_STATE_SKIP; continue; } if (!first) { printf("\n%s:\n", string); first = 1; } if (state != FUNC_STATE_PRINT) { state = FUNC_STATE_PRINT; printf(" "); putchar(str[i]); } } free(str); } static void report_graph_funcs(struct buffer_instance *instance) { char *path; path = get_instance_file(instance, "set_graph_function"); if (!path) die("malloc"); list_functions(path, "Function Graph Filter"); tracecmd_put_tracing_file(path); path = get_instance_file(instance, "set_graph_notrace"); if (!path) die("malloc"); list_functions(path, "Function Graph No Trace"); tracecmd_put_tracing_file(path); } static void report_ftrace_filters(struct buffer_instance *instance) { char *path; path = get_instance_file(instance, "set_ftrace_filter"); if (!path) die("malloc"); list_functions(path, "Function Filter"); tracecmd_put_tracing_file(path); path = get_instance_file(instance, "set_ftrace_notrace"); if (!path) die("malloc"); list_functions(path, "Function No Trace"); tracecmd_put_tracing_file(path); } static void report_buffers(struct buffer_instance *instance) { #define FILE_SIZE 100 char *str; char *cont; char file[FILE_SIZE]; int cpu; str = get_instance_file_content(instance, "buffer_size_kb"); if (!str) return; cont = strstrip(str); /* If it's not expanded yet, just skip */ if (strstr(cont, "expanded") != NULL) goto out; if (strcmp(cont, "X") != 0) { printf("\nBuffer size in kilobytes (per cpu):\n"); printf(" %s\n", str); goto total; } /* Read the sizes of each CPU buffer */ for (cpu = 0; ; cpu++) { snprintf(file, FILE_SIZE, "per_cpu/cpu%d/buffer_size_kb", cpu); str = get_instance_file_content(instance, file); if (!str) break; cont = strstrip(str); if (!cpu) putchar('\n'); printf("CPU %d buffer size (kb): %s\n", cpu, cont); free(str); } total: free(str); str = get_instance_file_content(instance, "buffer_total_size_kb"); if (!str) return; cont = strstrip(str); printf("\nBuffer total size in kilobytes:\n"); printf(" %s\n", str); out: free(str); } static void report_clock(struct buffer_instance *instance) { char *str; char *cont; char *clock; str = get_instance_file_content(instance, "trace_clock"); if (!str) return; clock = strstr(str, "["); if (!clock) goto out; clock++; cont = strstr(clock, "]"); if (!cont) /* should never happen */ goto out; *cont = '\0'; /* Default clock is "local", only show others */ if (strcmp(clock, "local") == 0) goto out; printf("\nClock: %s\n", clock); out: free(str); } static void report_cpumask(struct buffer_instance *instance) { char *str; char *cont; int cpus; int n; int i; str = get_instance_file_content(instance, "tracing_cpumask"); if (!str) return; cont = strstrip(str); /* check to make sure all CPUs on this machine are set */ cpus = count_cpus(); for (i = strlen(cont) - 1; i >= 0 && cpus > 0; i--) { if (cont[i] == ',') continue; if (cont[i] == 'f') { cpus -= 4; continue; } if (cpus >= 4) break; if (cont[i] >= '0' && cont[i] <= '9') n = cont[i] - '0'; else n = 10 + (cont[i] - 'a'); while (cpus > 0) { if (!(n & 1)) break; n >>= 1; cpus--; } break; } /* If cpus is greater than zero, one isn't set */ if (cpus > 0) printf("\nCPU mask: %s\n", cont); free(str); } static void report_latency(struct buffer_instance *instance) { char *str; char *cont; str = get_instance_file_content(instance, "tracing_max_latency"); if (!str) return; cont = strstrip(str); if (strcmp(cont, "0") != 0) printf("\nMax Latency: %s\n", cont); free(str); } static void report_probes(struct buffer_instance *instance, const char *file, const char *string) { char *str; char *cont; int newline; int i; str = get_instance_file_content(instance, file); if (!str) return; cont = strstrip(str); if (strlen(cont) == 0) goto out; printf("\n%s:\n", string); newline = 1; for (i = 0; cont[i]; i++) { if (newline) printf(" "); putchar(cont[i]); if (cont[i] == '\n') newline = 1; else newline = 0; } putchar('\n'); out: free(str); } static void report_kprobes(struct buffer_instance *instance) { report_probes(instance, "kprobe_events", "Kprobe events"); } static void report_uprobes(struct buffer_instance *instance) { report_probes(instance, "uprobe_events", "Uprobe events"); } static void report_traceon(struct buffer_instance *instance) { char *str; char *cont; str = get_instance_file_content(instance, "tracing_on"); if (!str) return; cont = strstrip(str); /* double newline as this is the last thing printed */ if (strcmp(cont, "0") == 0) printf("\nTracing is disabled\n\n"); else printf("\nTracing is enabled\n\n"); free(str); } static void stat_instance(struct buffer_instance *instance) { report_plugin(instance); report_events(instance); report_event_filters(instance); report_event_triggers(instance); report_ftrace_filters(instance); report_graph_funcs(instance); report_buffers(instance); report_clock(instance); report_cpumask(instance); report_latency(instance); report_kprobes(instance); report_uprobes(instance); report_traceon(instance); } void trace_stat (int argc, char **argv) { struct buffer_instance *instance = &top_instance; int topt = 0; int c; for (;;) { c = getopt(argc-1, argv+1, "tB:"); if (c == -1) break; switch (c) { case 'h': usage(argv); break; case 'B': instance = create_instance(optarg); add_instance(instance); /* top instance requires direct access */ if (!topt && is_top_instance(first_instance)) first_instance = instance; break; case 't': /* Force to use top instance */ topt = 1; instance = &top_instance; first_instance = instance; break; default: usage(argv); } } for_all_instances(instance) { stat_instance(instance); } exit(0); } trace-cmd-2.5.3/trace-stream.c000066400000000000000000000067321246701203100161170ustar00rootroot00000000000000/* * Copyright (C) 2014 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include "trace-local.h" /* * Stream runs for a single machine. We are going to cheat * and use the trace-output and trace-input code to create * our pevent. First just create a trace.dat file and then read * it to create the pevent and handle. */ struct tracecmd_input * trace_stream_init(struct buffer_instance *instance, int cpu, int fd, int cpus, int profile, struct hook_list *hooks) { struct tracecmd_input *trace_input; struct tracecmd_output *trace_output; static FILE *fp = NULL; static int tfd; static int ofd; long flags; if (instance->handle) { trace_input = instance->handle; goto make_pipe; } if (!fp) { fp = tmpfile(); if (!fp) return NULL; tfd = fileno(fp); ofd = dup(tfd); trace_output = tracecmd_create_init_fd(ofd); if (!trace_output) { fclose(fp); return NULL; } tracecmd_output_free(trace_output); } lseek(ofd, 0, SEEK_SET); trace_input = tracecmd_alloc_fd(ofd); if (!trace_input) { close(ofd); goto fail; } if (tracecmd_read_headers(trace_input) < 0) goto fail_free_input; if (profile) trace_init_profile(trace_input, hooks); make_pipe: /* Do not block on this pipe */ flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (tracecmd_make_pipe(trace_input, cpu, fd, cpus) < 0) goto fail_free_input; instance->handle = trace_input; return trace_input; fail_free_input: tracecmd_close(trace_input); fail: fclose(fp); return NULL; } int trace_stream_read(struct pid_record_data *pids, int nr_pids, struct timeval *tv, int profile) { struct pevent_record *record; struct pid_record_data *pid; struct pid_record_data *last_pid; fd_set rfds; int top_rfd = 0; int ret; int i; last_pid = NULL; again: for (i = 0; i < nr_pids; i++) { pid = &pids[i]; if (!pid->record) pid->record = tracecmd_read_data(pid->instance->handle, pid->cpu); record = pid->record; if (!record && errno == EINVAL) /* pipe has closed */ pid->closed = 1; if (record && (!last_pid || record->ts < last_pid->record->ts)) last_pid = pid; } if (last_pid) { trace_show_data(last_pid->instance->handle, last_pid->record, profile); free_record(last_pid->record); last_pid->record = NULL; return 1; } FD_ZERO(&rfds); for (i = 0; i < nr_pids; i++) { /* Do not process closed pipes */ if (pids[i].closed) continue; if (pids[i].brass[0] > top_rfd) top_rfd = pids[i].brass[0]; FD_SET(pids[i].brass[0], &rfds); } ret = select(top_rfd + 1, &rfds, NULL, NULL, tv); if (ret > 0) goto again; return ret; } trace-cmd-2.5.3/trace-usage.c000066400000000000000000000264721246701203100157330ustar00rootroot00000000000000#include #include #include #include #include "trace-local.h" #include "version.h" struct usage_help { char *name; char *short_help; char *long_help; }; static struct usage_help usage_help[] = { { "record", "record a trace into a trace.dat file", " %s record [-v][-e event [-f filter]][-p plugin][-F][-d][-D][-o file] \\\n" " [-s usecs][-O option ][-l func][-g func][-n func] \\\n" " [-P pid][-N host:port][-t][-r prio][-b size][-B buf][command ...]\n" " [-m max][-C clock]\n" " -e run command with event enabled\n" " -f filter for previous -e event\n" " -R trigger for previous -e event\n" " -p run command with plugin enabled\n" " -F filter only on the given process\n" " -P trace the given pid like -F for the command\n" " -c also trace the childen of -F\n" " -C set the trace clock\n" " -T do a stacktrace on all events\n" " -l filter function name\n" " -g set graph function\n" " -n do not trace function\n" " -m max size per CPU in kilobytes\n" " -M set CPU mask to trace\n" " -v will negate all -e after it (disable those events)\n" " -d disable function tracer when running\n" " -D Full disable of function tracing (for all users)\n" " -o data output file [default trace.dat]\n" " -O option to enable (or disable)\n" " -r real time priority to run the capture threads\n" " -s sleep interval between recording (in usecs) [default: 1000]\n" " -S used with --profile, to enable only events in command line\n" " -N host:port to connect to (see listen)\n" " -t used with -N, forces use of tcp in live trace\n" " -b change kernel buffersize (in kilobytes per CPU)\n" " -B create sub buffer and folling events will be enabled here\n" " -k do not reset the buffers after tracing.\n" " -i do not fail if an event is not found\n" " --profile enable tracing options needed for report --profile\n" " --func-stack perform a stack trace for function tracer\n" " (use with caution)\n" }, { "start", "start tracing without recording into a file", " %s start [-e event][-p plugin][-d][-O option ][-P pid]\n" " Uses same options as record, but does not run a command.\n" " It only enables the tracing and exits\n" }, { "extract", "extract a trace from the kernel", " %s extract [-p plugin][-O option][-o file][-s]\n" " Uses same options as record, but only reads an existing trace.\n" " -s : extract the snapshot instead of the main buffer\n" }, { "stop", "stop the kernel from recording trace data", " %s stop [-B buf [-B buf]..] [-t]\n" " Stops the tracer from recording more data.\n" " Used in conjunction with start\n" " -B stop a given buffer (more than one may be specified)\n " " -t stop the top level buffer (needed if -B is specified)\n" }, { "restart", "restart the kernel trace data recording", " %s restart [-B buf [-B buf]..] [-t]\n" " Restarts recording after a trace-cmd stop.\n" " Used in conjunction with stop\n" " -B restart a given buffer (more than one may be specified)\n " " -t restart the top level buffer (needed if -B is specified)\n" }, { "show", "show the contents of the kernel tracing buffer", " %s show [-p|-s][-c cpu][-B buf][options]\n" " Basically, this is a cat of the trace file.\n" " -p read the trace_pipe file instead\n" " -s read the snapshot file instance\n" " (Can't have both -p and -s)\n" " -c just show the file associated with a given CPU\n" " -B read from a tracing buffer instance.\n" " -f display the file path that is being dumped\n" " The following options shows the corresponding file name\n" " and then exits.\n" " --tracing_on\n" " --current_tracer\n" " --buffer_size (for buffer_size_kb)\n" " --buffer_total_size (for buffer_total_size_kb)\n" " --ftrace_filter (for set_ftrace_filter)\n" " --ftrace_notrace (for set_ftrace_notrace)\n" " --ftrace_pid (for set_ftrace_pid)\n" " --graph_function (for set_graph_function)\n" " --graph_notrace (for set_graph_notrace)\n" " --cpumask (for tracing_cpumask)\n" }, { "reset", "disable all kernel tracing and clear the trace buffers", " %s reset [-b size][-B buf][-d][-t]\n" " Disables the tracer (may reset trace file)\n" " Used in conjunction with start\n" " -b change the kernel buffer size (in kilobytes per CPU)\n" " -B reset the given buffer instance (top instance ignored)\n" " -d delete the previous specified instance\n" " -t still reset the top instance if -B option is given\n" }, { "report", "read out the trace stored in a trace.dat file", " %s report [-i file] [--cpu cpu] [-e][-f][-l][-P][-L][-N][-R][-E]\\\n" " [-r events][-n events][-F filter][-v][-V][-T][-O option]\n" " [-H [start_system:]start_event,start_match[,pid]/[end_system:]end_event,end_match[,flags]\n" " -i input file [default trace.dat]\n" " -e show file endianess\n" " -f show function list\n" " -P show printk list\n" " -E show event files stored\n" " -F filter to filter output on\n" " -t print out full timestamp. Do not truncate to 6 places.\n" " -R raw format: ignore print format and only show field data\n" " -r raw format the events that match the option\n" " -v will negate all -F after it (Not show matches)\n" " -T print out the filter strings created and exit\n" " -V verbose (shows plugins being loaded)\n" " -L load only local (~/.trace-cmd/plugins) plugins\n" " -N do not load any plugins\n" " -n ignore plugin handlers for events that match the option\n" " -w show wakeup latencies\n" " -l show latency format (default with latency tracers)\n" " -O plugin option -O [plugin:]var[=val]\n" " --check-events return whether all event formats can be parsed\n" " --stat - show the buffer stats that were reported at the end of the record.\n" " --uname - show uname of the record, if it was saved\n" " --profile report stats on where tasks are blocked and such\n" " -H Allows users to hook two events together for timings\n" " (used with --profile)\n" }, { "stream", "Start tracing and read the output directly", " %s stream [-e event][-p plugin][-d][-O option ][-P pid]\n" " Uses same options as record but does not write to files or the network.\n" }, { "profile", "Start profiling and read the output directly", " %s profile [-e event][-p plugin][-d][-O option ][-P pid][-S][-o output]\n" " [-H [start_system:]start_event,start_match[,pid]/[end_system:]end_event,end_match[,flags]\n\n" " Uses same options as record --profile.\n" " -H Allows users to hook two events together for timings\n" }, { "hist", "show a historgram of the trace.dat information", " %s hist [-i file][-P] [file]" " -P ignore pids (compact all functions)\n" }, { "stat", "show the status of the running tracing (ftrace) system", " %s stat" }, { "split", "parse a trace.dat file into smaller file(s)", " %s split [options] -o file [start [end]]\n" " -o output file to write to (file.1, file.2, etc)\n" " -s n split file up by n seconds\n" " -m n split file up by n milliseconds\n" " -u n split file up by n microseconds\n" " -e n split file up by n events\n" " -p n split file up by n pages\n" " -r repeat from start to end\n" " -c per cpu, that is -p 2 will be 2 pages for each CPU\n" " if option is specified, it will split the file\n" " up starting at start, and ending at end\n" " start - decimal start time in seconds (ex: 75678.923853)\n" " if left out, will start at beginning of file\n" " end - decimal end time in seconds\n" }, { "options", "list the plugin options available for trace-cmd report", " %s options\n" }, { "listen", "listen on a network socket for trace clients", " %s listen -p port[-D][-o file][-d dir][-l logfile]\n" " Creates a socket to listen for clients.\n" " -D create it in daemon mode.\n" " -o file name to use for clients.\n" " -d diretory to store client files.\n" " -l logfile to write messages to.\n" }, { "list", "list the available events, plugins or options", " %s list [-e [regex]][-t][-o][-f [regex]]\n" " -e list available events\n" " -F show event format\n" " -R show event triggers\n" " -l show event filters\n" " -t list available tracers\n" " -o list available options\n" " -f [regex] list available functions to filter on\n" " -P list loaded plugin files (by path)\n" " -O list plugin options\n" " -B list defined buffer instances\n" " -C list the defined clocks (and active one)\n" }, { "restore", "restore a crashed record", " %s restore [-c][-o file][-i file] cpu-file [cpu-file ...]\n" " -c create a partial trace.dat file only\n" " -o output file\n" " -i parital trace.dat file for input\n" }, { "snapshot", "take snapshot of running trace", " %s snapshot [-s][-r][-f][-B buf][-c cpu]\n" " -s take a snapshot of the trace buffer\n" " -r reset current snapshot\n" " -f free the snapshot buffer\n" " without the above three options, display snapshot\n" " -c operate on the snapshot buffer for the given CPU\n" " -B operate on the snapshot buffer for a tracing buffer instance.\n" }, { "stack", "output, enable or disable kernel stack tracing", " %s stack [--start][--stop][--reset]\n" " --start enable the stack tracer\n" " --stop disable the stack tracer\n" " --reset reset the maximum stack found\n" }, { "check-events", "parse trace event formats", " %s check-format [-N]\n" " -N do not load any plugins\n" }, { NULL, NULL, NULL } }; static struct usage_help *find_help(char *cmd) { struct usage_help *help; help = usage_help; while (help->name) { if (strcmp(cmd, help->name) == 0) return help; help++; } return NULL; } void usage(char **argv) { struct usage_help *help = NULL; char *arg = argv[0]; char *p; p = basename(arg); printf("\n" "%s version %s\n\n" "usage:\n", p, VERSION_STRING); if (argv[1]) help = find_help(argv[1]); if (help) { printf(help->long_help, p); goto out; } printf(" %s [COMMAND] ...\n\n" " commands:\n", p); help = usage_help; while (help->name) { printf(" %s - %s\n", help->name, help->short_help); help++; } out: printf("\n"); exit(-1); } trace-cmd-2.5.3/trace-util.c000066400000000000000000001005261246701203100155750ustar00rootroot00000000000000/* * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * This program 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; * version 2.1 of the License (not later!) * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "trace-cmd.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 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) #ifndef MAX_PATH # define MAX_PATH 1024 #endif struct plugin_list { struct 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 :