From 068048faa53b96265855dba5175f0a2df1497864 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 17:30:53 +0100 Subject: gator: Version 5.10 New gator release (build 1385) for ARM DS-5 v5.10 Signed-off-by: Jon Medhurst --- drivers/gator/LICENSE | 339 +++ drivers/gator/Makefile | 55 + drivers/gator/gator.h | 100 + drivers/gator/gator_annotate.c | 149 ++ drivers/gator/gator_annotate_kernel.c | 90 + drivers/gator/gator_backtrace.c | 114 + drivers/gator/gator_cookies.c | 401 +++ drivers/gator/gator_ebs.c | 158 ++ drivers/gator/gator_events.sh | 19 + drivers/gator/gator_events_armv6.c | 244 ++ drivers/gator/gator_events_armv7.c | 319 +++ drivers/gator/gator_events_block.c | 175 ++ drivers/gator/gator_events_irq.c | 188 ++ drivers/gator/gator_events_l2c-310.c | 179 ++ drivers/gator/gator_events_mali.c | 724 ++++++ drivers/gator/gator_events_meminfo.c | 230 ++ drivers/gator/gator_events_mmaped.c | 231 ++ drivers/gator/gator_events_net.c | 157 ++ drivers/gator/gator_events_perf_pmu.c | 306 +++ drivers/gator/gator_events_sched.c | 114 + drivers/gator/gator_events_scorpion.c | 678 +++++ drivers/gator/gator_fs.c | 292 +++ drivers/gator/gator_hrtimer_gator.c | 97 + drivers/gator/gator_hrtimer_perf.c | 113 + drivers/gator/gator_main.c | 1086 ++++++++ drivers/gator/gator_marshaling.c | 239 ++ drivers/gator/gator_pack.c | 164 ++ drivers/gator/gator_trace_gpu.c | 126 + drivers/gator/gator_trace_gpu.h | 79 + drivers/gator/gator_trace_power.c | 160 ++ drivers/gator/gator_trace_sched.c | 191 ++ tools/gator/daemon/Android.mk | 41 + tools/gator/daemon/CapturedXML.cpp | 123 + tools/gator/daemon/CapturedXML.h | 25 + tools/gator/daemon/Child.cpp | 312 +++ tools/gator/daemon/Child.h | 31 + tools/gator/daemon/Collector.cpp | 291 +++ tools/gator/daemon/Collector.h | 37 + tools/gator/daemon/ConfigurationXML.cpp | 188 ++ tools/gator/daemon/ConfigurationXML.h | 29 + tools/gator/daemon/Fifo.cpp | 126 + tools/gator/daemon/Fifo.h | 33 + tools/gator/daemon/LocalCapture.cpp | 214 ++ tools/gator/daemon/LocalCapture.h | 27 + tools/gator/daemon/Logging.cpp | 79 + tools/gator/daemon/Logging.h | 45 + tools/gator/daemon/Makefile | 46 + tools/gator/daemon/OlySocket.cpp | 261 ++ tools/gator/daemon/OlySocket.h | 36 + tools/gator/daemon/OlyUtility.cpp | 254 ++ tools/gator/daemon/OlyUtility.h | 38 + tools/gator/daemon/Sender.cpp | 110 + tools/gator/daemon/Sender.h | 38 + tools/gator/daemon/SessionData.cpp | 112 + tools/gator/daemon/SessionData.h | 75 + tools/gator/daemon/SessionXML.cpp | 142 ++ tools/gator/daemon/SessionXML.h | 40 + tools/gator/daemon/StreamlineSetup.cpp | 331 +++ tools/gator/daemon/StreamlineSetup.h | 46 + tools/gator/daemon/configuration.xml | 51 + tools/gator/daemon/events-ARM11.xml | 43 + tools/gator/daemon/events-ARM11MPCore.xml | 30 + tools/gator/daemon/events-Cortex-A15.xml | 76 + tools/gator/daemon/events-Cortex-A5.xml | 39 + tools/gator/daemon/events-Cortex-A7.xml | 49 + tools/gator/daemon/events-Cortex-A8.xml | 57 + tools/gator/daemon/events-Cortex-A9.xml | 66 + tools/gator/daemon/events-Krait-architected.xml | 27 + tools/gator/daemon/events-L2C-310.xml | 21 + tools/gator/daemon/events-Linux.xml | 17 + tools/gator/daemon/events-Mali-400.xml | 397 +++ tools/gator/daemon/events-Scorpion.xml | 112 + tools/gator/daemon/events-ScorpionMP.xml | 95 + tools/gator/daemon/events_footer.xml | 1 + tools/gator/daemon/events_header.xml | 2 + tools/gator/daemon/main.cpp | 335 +++ tools/gator/daemon/mxml/COPYING | 507 ++++ tools/gator/daemon/mxml/config.h | 96 + tools/gator/daemon/mxml/mxml-attr.c | 319 +++ tools/gator/daemon/mxml/mxml-entity.c | 460 ++++ tools/gator/daemon/mxml/mxml-file.c | 3080 +++++++++++++++++++++++ tools/gator/daemon/mxml/mxml-get.c | 471 ++++ tools/gator/daemon/mxml/mxml-index.c | 662 +++++ tools/gator/daemon/mxml/mxml-node.c | 807 ++++++ tools/gator/daemon/mxml/mxml-private.c | 331 +++ tools/gator/daemon/mxml/mxml-private.h | 50 + tools/gator/daemon/mxml/mxml-search.c | 287 +++ tools/gator/daemon/mxml/mxml-set.c | 349 +++ tools/gator/daemon/mxml/mxml-string.c | 476 ++++ tools/gator/daemon/mxml/mxml.h | 329 +++ 90 files changed, 20289 insertions(+) create mode 100644 drivers/gator/LICENSE create mode 100644 drivers/gator/Makefile create mode 100644 drivers/gator/gator.h create mode 100644 drivers/gator/gator_annotate.c create mode 100755 drivers/gator/gator_annotate_kernel.c create mode 100644 drivers/gator/gator_backtrace.c create mode 100644 drivers/gator/gator_cookies.c create mode 100644 drivers/gator/gator_ebs.c create mode 100755 drivers/gator/gator_events.sh create mode 100644 drivers/gator/gator_events_armv6.c create mode 100644 drivers/gator/gator_events_armv7.c create mode 100644 drivers/gator/gator_events_block.c create mode 100644 drivers/gator/gator_events_irq.c create mode 100644 drivers/gator/gator_events_l2c-310.c create mode 100755 drivers/gator/gator_events_mali.c create mode 100644 drivers/gator/gator_events_meminfo.c create mode 100644 drivers/gator/gator_events_mmaped.c create mode 100644 drivers/gator/gator_events_net.c create mode 100755 drivers/gator/gator_events_perf_pmu.c create mode 100644 drivers/gator/gator_events_sched.c create mode 100644 drivers/gator/gator_events_scorpion.c create mode 100644 drivers/gator/gator_fs.c create mode 100755 drivers/gator/gator_hrtimer_gator.c create mode 100755 drivers/gator/gator_hrtimer_perf.c create mode 100644 drivers/gator/gator_main.c create mode 100755 drivers/gator/gator_marshaling.c create mode 100644 drivers/gator/gator_pack.c create mode 100644 drivers/gator/gator_trace_gpu.c create mode 100644 drivers/gator/gator_trace_gpu.h create mode 100755 drivers/gator/gator_trace_power.c create mode 100644 drivers/gator/gator_trace_sched.c create mode 100755 tools/gator/daemon/Android.mk create mode 100644 tools/gator/daemon/CapturedXML.cpp create mode 100644 tools/gator/daemon/CapturedXML.h create mode 100644 tools/gator/daemon/Child.cpp create mode 100644 tools/gator/daemon/Child.h create mode 100644 tools/gator/daemon/Collector.cpp create mode 100644 tools/gator/daemon/Collector.h create mode 100644 tools/gator/daemon/ConfigurationXML.cpp create mode 100644 tools/gator/daemon/ConfigurationXML.h create mode 100644 tools/gator/daemon/Fifo.cpp create mode 100644 tools/gator/daemon/Fifo.h create mode 100644 tools/gator/daemon/LocalCapture.cpp create mode 100644 tools/gator/daemon/LocalCapture.h create mode 100755 tools/gator/daemon/Logging.cpp create mode 100755 tools/gator/daemon/Logging.h create mode 100644 tools/gator/daemon/Makefile create mode 100755 tools/gator/daemon/OlySocket.cpp create mode 100755 tools/gator/daemon/OlySocket.h create mode 100755 tools/gator/daemon/OlyUtility.cpp create mode 100755 tools/gator/daemon/OlyUtility.h create mode 100644 tools/gator/daemon/Sender.cpp create mode 100644 tools/gator/daemon/Sender.h create mode 100644 tools/gator/daemon/SessionData.cpp create mode 100644 tools/gator/daemon/SessionData.h create mode 100644 tools/gator/daemon/SessionXML.cpp create mode 100644 tools/gator/daemon/SessionXML.h create mode 100644 tools/gator/daemon/StreamlineSetup.cpp create mode 100644 tools/gator/daemon/StreamlineSetup.h create mode 100644 tools/gator/daemon/configuration.xml create mode 100644 tools/gator/daemon/events-ARM11.xml create mode 100644 tools/gator/daemon/events-ARM11MPCore.xml create mode 100644 tools/gator/daemon/events-Cortex-A15.xml create mode 100644 tools/gator/daemon/events-Cortex-A5.xml create mode 100755 tools/gator/daemon/events-Cortex-A7.xml create mode 100644 tools/gator/daemon/events-Cortex-A8.xml create mode 100644 tools/gator/daemon/events-Cortex-A9.xml create mode 100755 tools/gator/daemon/events-Krait-architected.xml create mode 100644 tools/gator/daemon/events-L2C-310.xml create mode 100644 tools/gator/daemon/events-Linux.xml create mode 100644 tools/gator/daemon/events-Mali-400.xml create mode 100644 tools/gator/daemon/events-Scorpion.xml create mode 100644 tools/gator/daemon/events-ScorpionMP.xml create mode 100644 tools/gator/daemon/events_footer.xml create mode 100644 tools/gator/daemon/events_header.xml create mode 100644 tools/gator/daemon/main.cpp create mode 100644 tools/gator/daemon/mxml/COPYING create mode 100644 tools/gator/daemon/mxml/config.h create mode 100644 tools/gator/daemon/mxml/mxml-attr.c create mode 100644 tools/gator/daemon/mxml/mxml-entity.c create mode 100644 tools/gator/daemon/mxml/mxml-file.c create mode 100644 tools/gator/daemon/mxml/mxml-get.c create mode 100644 tools/gator/daemon/mxml/mxml-index.c create mode 100644 tools/gator/daemon/mxml/mxml-node.c create mode 100644 tools/gator/daemon/mxml/mxml-private.c create mode 100644 tools/gator/daemon/mxml/mxml-private.h create mode 100644 tools/gator/daemon/mxml/mxml-search.c create mode 100644 tools/gator/daemon/mxml/mxml-set.c create mode 100644 tools/gator/daemon/mxml/mxml-string.c create mode 100644 tools/gator/daemon/mxml/mxml.h diff --git a/drivers/gator/LICENSE b/drivers/gator/LICENSE new file mode 100644 index 00000000000..d159169d105 --- /dev/null +++ b/drivers/gator/LICENSE @@ -0,0 +1,339 @@ + 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. diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile new file mode 100644 index 00000000000..667637e50c4 --- /dev/null +++ b/drivers/gator/Makefile @@ -0,0 +1,55 @@ +ifneq ($(KERNELRELEASE),) + +# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c +# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING + +obj-m := gator.o + +gator-y := gator_main.o \ + gator_events_irq.o \ + gator_events_sched.o \ + gator_events_net.o \ + gator_events_block.o \ + gator_events_meminfo.o \ + gator_events_perf_pmu.o + +gator-y += gator_events_mmaped.o + +ifneq ($(GATOR_WITH_MALI_SUPPORT),) +ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) +gator-y += gator_events_mali_t6xx.o +else +gator-y += gator_events_mali.o +endif +EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) +endif + +gator-$(CONFIG_ARM) += gator_events_armv6.o \ + gator_events_armv7.o \ + gator_events_l2c-310.o \ + gator_events_scorpion.o + +$(obj)/gator_main.o: gator_events.h + +clean-files := gator_events.h + + chk_events.h = : + quiet_chk_events.h = echo ' CHK $@' +silent_chk_events.h = : +gator_events.h: FORCE + @$($(quiet)chk_events.h) + $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@ + +else + +all: + @echo + @echo "usage:" + @echo " make -C M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" + @echo + $(error) + +clean: + rm -f *.o .*.cmd gator_events.h modules.order Module.symvers gator.ko gator.mod.c + +endif diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h new file mode 100644 index 00000000000..6b961099531 --- /dev/null +++ b/drivers/gator/gator.h @@ -0,0 +1,100 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef GATOR_H_ +#define GATOR_H_ + +#include +#include +#include +#include + +#define GATOR_PERF_SUPPORT LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#define GATOR_PERF_PMU_SUPPORT GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && defined(CONFIG_HW_PERF_EVENTS) +#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT)) +#define GATOR_CPU_FREQ_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ) + +// cpu ids +#define ARM1136 0xb36 +#define ARM1156 0xb56 +#define ARM1176 0xb76 +#define ARM11MPCORE 0xb02 +#define CORTEX_A5 0xc05 +#define CORTEX_A7 0xc07 +#define CORTEX_A8 0xc08 +#define CORTEX_A9 0xc09 +#define CORTEX_A15 0xc0f +#define SCORPION 0x00f +#define SCORPIONMP 0x02d +#define KRAITSIM 0x049 +#define KRAIT 0x04d + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops, int perm); + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name); + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +void gator_op_create_files(struct super_block *sb, struct dentry *root); + +/****************************************************************************** + * Tracepoints + ******************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +# error Kernels prior to 2.6.32 not supported +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name, NULL) +#endif + +/****************************************************************************** + * Events + ******************************************************************************/ +struct gator_interface { + int (*create_files)(struct super_block *sb, struct dentry *root); + int (*start)(void); + void (*stop)(void); + int (*online)(int** buffer); + int (*offline)(int** buffer); + void (*online_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' + void (*offline_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' + int (*read)(int **buffer); + int (*read64)(long long **buffer); + struct list_head list; +}; + +// gator_events_init is used as a search term in gator_events.sh +#define gator_events_init(initfn) \ + static inline int __gator_events_init_test(void) \ + { return initfn(); } + +int gator_events_install(struct gator_interface *interface); +int gator_events_get_key(void); +extern u32 gator_cpuid(void); + +#endif // GATOR_H_ diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c new file mode 100644 index 00000000000..b2288b3ad81 --- /dev/null +++ b/drivers/gator/gator_annotate.c @@ -0,0 +1,149 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(annotate_lock); +static bool collect_annotations = false; + +static int annotate_copy(struct file *file, char const __user *buf, size_t count) +{ + int cpu = 0; + int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF]; + + if (file == NULL) { + // copy from kernel + memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count); + } else { + // copy from user space + if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0) + return -1; + } + per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF]; + + return 0; +} + +static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset) +{ + int tid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff; + + if (*offset) + return -EINVAL; + + if (!collect_annotations) { + return count_orig; + } + + cpu = 0; // Annotation only uses a single per-cpu buffer as the data must be in order to the engine + + if (file == NULL) { + tid = -1; // set the thread id to the kernel thread + } else { + tid = current->pid; + } + + // synchronize between cores + spin_lock(&annotate_lock); + + // determine total size of the payload + header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64; + available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size; + size = count < available ? count : available; + + if (size <= 0) { + size = 0; + goto annotate_write_out; + } + + // synchronize shared variables annotateBuf and annotatePos + if (collect_annotations && per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, smp_processor_id()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, tid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size); + + // determine the sizes to capture, length1 + length2 will equal size + contiguous = contiguous_space_available(cpu, ANNOTATE_BUF); + if (size < contiguous) { + length1 = size; + length2 = 0; + } else { + length1 = contiguous; + length2 = size - contiguous; + } + + if (annotate_copy(file, buf, length1) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ANNOTATE_BUF); + } + +annotate_write_out: + spin_unlock(&annotate_lock); + + // return the number of bytes written + return size; +} + +#include "gator_annotate_kernel.c" + +static int annotate_release(struct inode *inode, struct file *file) +{ + int cpu = 0; + + // synchronize between cores + spin_lock(&annotate_lock); + + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + uint32_t tid = current->pid; + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, tid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size + } + + spin_unlock(&annotate_lock); + + return 0; +} + +static const struct file_operations annotate_fops = { + .write = annotate_write, + .release = annotate_release +}; + +static int gator_annotate_create_files(struct super_block *sb, struct dentry *root) +{ + return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); +} + +static int gator_annotate_start(void) +{ + collect_annotations = true; + return 0; +} + +static void gator_annotate_stop(void) +{ + collect_annotations = false; +} diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c new file mode 100755 index 00000000000..ffab08795b7 --- /dev/null +++ b/drivers/gator/gator_annotate_kernel.c @@ -0,0 +1,90 @@ +/** + * Copyright (C) ARM Limited 2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static void kannotate_write(char* ptr, unsigned int size) +{ + int retval; + int pos = 0; + loff_t offset = 0; + while (pos < size) { + retval = annotate_write(NULL, &ptr[pos], size - pos, &offset); + if (retval < 0) { + printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval); + return; + } + pos += retval; + } +} + +// String annotation +void gator_annotate(char* string) +{ + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate); + +// String annotation with color +void gator_annotate_color(int color, char* string) +{ + kannotate_write((char*)&color, sizeof(color)); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_color); + +// Terminate an annotation +void gator_annotate_end(void) +{ + char nul = 0; + kannotate_write(&nul, sizeof(nul)); +} +EXPORT_SYMBOL(gator_annotate_end); + +// Image annotation with optional string +void gator_annotate_visual(char* data, unsigned int length, char* string) +{ + long long visual_annotation = 0x011c | (strlen(string) << 16) | ((long long)length << 32); + kannotate_write((char*)&visual_annotation, 8); + kannotate_write(string, strlen(string)); + kannotate_write(data, length); +} +EXPORT_SYMBOL(gator_annotate_visual); + +// Marker annotation +void gator_annotate_marker(void) +{ + int marker_annotation = 0x00021c; + kannotate_write((char*)&marker_annotation, 3); +} +EXPORT_SYMBOL(gator_annotate_marker); + +// Marker annotation with a string +void gator_annotate_marker_str(char* string) +{ + int marker_annotation = 0x021c; + kannotate_write((char*)&marker_annotation, 2); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_marker_str); + +// Marker annotation with a color +void gator_annotate_marker_color(int color) +{ + long long marker_annotation = (0x021c | ((long long)color << 16)) & 0x0000ffffffffffffLL; + kannotate_write((char*)&marker_annotation, 7); +} +EXPORT_SYMBOL(gator_annotate_marker_color); + +// Marker annotationw ith a string and color +void gator_annotate_marker_color_str(int color, char* string) +{ + long long marker_annotation = 0x021c | ((long long)color << 16); + kannotate_write((char*)&marker_annotation, 6); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_marker_color_str); diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c new file mode 100644 index 00000000000..50783d69594 --- /dev/null +++ b/drivers/gator/gator_backtrace.c @@ -0,0 +1,114 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +/* + * EABI backtrace stores {fp,lr} on the stack. + */ +struct frame_tail_eabi { + unsigned long fp; // points to prev_lr + unsigned long lr; +}; + +static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs, unsigned int depth) +{ +#if defined(__arm__) + struct frame_tail_eabi *tail; + struct frame_tail_eabi *next; + struct frame_tail_eabi *ptrtail; + struct frame_tail_eabi buftail; + unsigned long fp = regs->ARM_fp; + unsigned long sp = regs->ARM_sp; + unsigned long lr = regs->ARM_lr; + int is_user_mode = user_mode(regs); + + if (!is_user_mode) { + return; + } + + /* entry preamble may not have executed */ + gator_add_trace(cpu, buftype, lr); + + /* check tail is valid */ + if (fp == 0 || fp < sp) { + return; + } + + tail = (struct frame_tail_eabi *)(fp - 4); + + while (depth-- && tail && !((unsigned long) tail & 3)) { + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi))) + return; + if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi))) + return; + ptrtail = &buftail; + + lr = ptrtail[0].lr; + gator_add_trace(cpu, buftype, lr); + + /* frame pointers should progress back up the stack, towards higher addresses */ + next = (struct frame_tail_eabi *)(lr - 4); + if (tail >= next || lr == 0) { + fp = ptrtail[0].fp; + next = (struct frame_tail_eabi *)(fp - 4); + /* check tail is valid */ + if (tail >= next || fp == 0) { + return; + } + } + + tail = next; + } +#endif +} + +#if defined(__arm__) +static DEFINE_PER_CPU(int, backtrace_buffer); +static int report_trace(struct stackframe *frame, void *d) +{ + struct module *mod; + unsigned int *depth = d, addr = frame->pc, cookie = NO_COOKIE, cpu = smp_processor_id(); + + if (*depth) { + mod = __module_address(addr); + if (mod) { + cookie = get_cookie(cpu, per_cpu(backtrace_buffer, cpu), current, NULL, mod, true); + addr = addr - (unsigned long)mod->module_core; + } + marshal_backtrace(addr & ~1, cookie); + (*depth)--; + } + + return *depth == 0; +} +#endif + +// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile +// #define GATOR_KERNEL_STACK_UNWINDING +static void kernel_backtrace(int cpu, int buftype, struct pt_regs * const regs) +{ +#if defined(__arm__) +#ifdef GATOR_KERNEL_STACK_UNWINDING + int depth = gator_backtrace_depth; +#else + int depth = 1; +#endif + struct stackframe frame; + if (depth == 0) + depth = 1; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + per_cpu(backtrace_buffer, cpu) = buftype; + walk_stackframe(&frame, report_trace, &depth); +#else + marshal_backtrace(PC_REG & ~1, NO_COOKIE); +#endif +} diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c new file mode 100644 index 00000000000..7b5091696bf --- /dev/null +++ b/drivers/gator/gator_cookies.c @@ -0,0 +1,401 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ +#define TRANSLATE_SIZE 256 +#define MAX_COLLISIONS 2 + +static uint32_t *gator_crc32_table; +static uint32_t translate_buffer_mask; + +static DEFINE_PER_CPU(char *, translate_text); +static DEFINE_PER_CPU(uint32_t, cookie_next_key); +static DEFINE_PER_CPU(uint64_t *, cookie_keys); +static DEFINE_PER_CPU(uint32_t *, cookie_values); +static DEFINE_PER_CPU(int, translate_buffer_read); +static DEFINE_PER_CPU(int, translate_buffer_write); +static DEFINE_PER_CPU(unsigned int *, translate_buffer); + +static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task, struct vm_area_struct *vma, struct module *mod, bool in_interrupt); +static void wq_cookie_handler(struct work_struct *unused); +DECLARE_WORK(cookie_work, wq_cookie_handler); + +static uint32_t cookiemap_code(uint64_t value64) { + uint32_t value = (uint32_t)((value64 >> 32) + value64); + uint32_t cookiecode = (value >> 24) & 0xff; + cookiecode = cookiecode * 31 + ((value >> 16) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 8) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 0) & 0xff); + cookiecode &= (COOKIEMAP_ENTRIES-1); + return cookiecode * MAX_COLLISIONS; +} + +static uint32_t gator_chksum_crc32(char *data) +{ + register unsigned long crc; + unsigned char *block = data; + int i, length = strlen(data); + + crc = 0xFFFFFFFF; + for (i = 0; i < length; i++) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF]; + } + + return (crc ^ 0xFFFFFFFF); +} + +/* + * Exists + * Pre: [0][1][v][3]..[n-1] + * Post: [v][0][1][3]..[n-1] + */ +static uint32_t cookiemap_exists(uint64_t key) { + unsigned long x, flags, retval = 0; + int cpu = smp_processor_id(); + uint32_t cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + + // Can be called from interrupt handler or from work queue + local_irq_save(flags); + for (x = 0; x < MAX_COLLISIONS; x++) { + if (keys[x] == key) { + uint32_t value = values[x]; + for (; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = values[x-1]; + } + keys[0] = key; + values[0] = value; + retval = value; + break; + } + } + local_irq_restore(flags); + + return retval; +} + +/* + * Add + * Pre: [0][1][2][3]..[n-1] + * Post: [v][0][1][2]..[n-2] + */ +static void cookiemap_add(uint64_t key, uint32_t value) { + int cpu = smp_processor_id(); + int cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + int x; + + for (x = MAX_COLLISIONS-1; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = values[x-1]; + } + keys[0] = key; + values[0] = value; +} + +static void translate_buffer_write_int(int cpu, unsigned int x) +{ + per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; + per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; +} + +static unsigned int translate_buffer_read_int(int cpu) +{ + unsigned int value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++]; + per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask; + return value; +} + +static void wq_cookie_handler(struct work_struct *unused) +{ + struct task_struct *task; + struct vm_area_struct *vma; + int cpu = smp_processor_id(); + unsigned int cookie, commit; + + mutex_lock(&start_mutex); + + if (gator_started != 0) { + commit = per_cpu(translate_buffer_write, cpu); + while (per_cpu(translate_buffer_read, cpu) != commit) { + task = (struct task_struct *)translate_buffer_read_int(cpu); + vma = (struct vm_area_struct *)translate_buffer_read_int(cpu); + cookie = get_cookie(cpu, BACKTRACE_BUF, task, vma, NULL, false); + } + } + + mutex_unlock(&start_mutex); +} + +// Retrieve full name from proc/pid/cmdline for java processes on Android +static int translate_app_process(char** text, int cpu, struct task_struct * task, struct vm_area_struct *vma, bool in_interrupt) +{ + void *maddr; + unsigned int len; + unsigned long addr; + struct mm_struct *mm; + struct page *page = NULL; + struct vm_area_struct *page_vma; + int bytes, offset, retval = 0, ptr; + char * buf = per_cpu(translate_text, cpu); + + // Push work into a work queue if in atomic context as the kernel functions below might sleep + // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems + // inconsistent during a context switch between android/linux versions + if (in_interrupt) { + // Check if already in buffer + ptr = per_cpu(translate_buffer_read, cpu); + while (ptr != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[ptr] == (int)task) + goto out; + ptr = (ptr + 2) & translate_buffer_mask; + } + + translate_buffer_write_int(cpu, (unsigned int)task); + translate_buffer_write_int(cpu, (unsigned int)vma); + schedule_work(&cookie_work); + goto out; + } + + mm = get_task_mm(task); + if (!mm) + goto out; + if (!mm->arg_end) + goto outmm; + addr = mm->arg_start; + len = mm->arg_end - mm->arg_start; + + if (len > TRANSLATE_SIZE) + len = TRANSLATE_SIZE; + + down_read(&mm->mmap_sem); + while (len) { + if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0) + goto outsem; + + maddr = kmap(page); + offset = addr & (PAGE_SIZE-1); + bytes = len; + if (bytes > PAGE_SIZE - offset) + bytes = PAGE_SIZE - offset; + + copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes); + + kunmap(page); // release page allocated by get_user_pages() + page_cache_release(page); + + len -= bytes; + buf += bytes; + addr += bytes; + + *text = per_cpu(translate_text, cpu); + retval = 1; + } + + // On app_process startup, /proc/pid/cmdline is initially "zygote" then "" but changes after an initial startup period + if (strcmp(*text, "zygote") == 0 || strcmp(*text, "") == 0) + retval = 0; + +outsem: + up_read(&mm->mmap_sem); +outmm: + mmput(mm); +out: + return retval; +} + +static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task, struct vm_area_struct *vma, struct module *mod, bool in_interrupt) +{ + unsigned long flags, cookie; + struct path *path; + uint64_t key; + char *text; + + if (mod) { + text = mod->name; + } else { + if (!vma || !vma->vm_file) { + return INVALID_COOKIE; + } + path = &vma->vm_file->f_path; + if (!path || !path->dentry) { + return INVALID_COOKIE; + } + + text = (char*)path->dentry->d_name.name; + } + + key = gator_chksum_crc32(text); + key = (key << 32) | (uint32_t)task->tgid; + + cookie = cookiemap_exists(key); + if (cookie) { + return cookie; + } + + if (strcmp(text, "app_process") == 0 && !mod) { + if (!translate_app_process(&text, cpu, task, vma, in_interrupt)) + return INVALID_COOKIE; + } + + // Can be called from interrupt handler or from work queue or from scheduler trace + local_irq_save(flags); + + cookie = INVALID_COOKIE; + if (marshal_cookie_header(text)) { + cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids; + cookiemap_add(key, cookie); + marshal_cookie(cookie, text); + } + + local_irq_restore(flags); + + return cookie; +} + +static int get_exec_cookie(int cpu, int buftype, struct task_struct *task) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + // kernel threads have no address space + if (!mm) + return cookie; + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (!vma->vm_file) + continue; + if (!(vma->vm_flags & VM_EXECUTABLE)) + continue; + cookie = get_cookie(cpu, buftype, task, vma, NULL, true); + break; + } + + return cookie; +} + +static unsigned long get_address_cookie(int cpu, int buftype, struct task_struct *task, unsigned long addr, off_t *offset) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + if (!mm) + return cookie; + + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { + if (addr < vma->vm_start || addr >= vma->vm_end) + continue; + + if (vma->vm_file) { + cookie = get_cookie(cpu, buftype, task, vma, NULL, true); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + + break; + } + + if (!vma) + cookie = INVALID_COOKIE; + + return cookie; +} + +static int cookies_initialize(void) +{ + uint32_t crc, poly; + int i, j, cpu, size, err = 0; + + int translate_buffer_size = 512; // must be a power of 2 + translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; + + for_each_present_cpu(cpu) { + per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_keys, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_keys, cpu), 0, size); + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t); + per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_values, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_values, cpu), 0, size); + + per_cpu(translate_buffer, cpu) = (unsigned int *)kmalloc(translate_buffer_size, GFP_KERNEL); + if (!per_cpu(translate_buffer, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + + per_cpu(translate_buffer_write, cpu) = 0; + per_cpu(translate_buffer_read, cpu) = 0; + + per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL); + if (!per_cpu(translate_text, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + } + + // build CRC32 table + poly = 0x04c11db7; + gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) { + crc = (crc >> 1) ^ poly; + } else { + crc >>= 1; + } + } + gator_crc32_table[i] = crc; + } + +cookie_setup_error: + return err; +} + +static void cookies_release(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + kfree(per_cpu(cookie_keys, cpu)); + per_cpu(cookie_keys, cpu) = NULL; + + kfree(per_cpu(cookie_values, cpu)); + per_cpu(cookie_values, cpu) = NULL; + + kfree(per_cpu(translate_buffer, cpu)); + per_cpu(translate_buffer, cpu) = NULL; + per_cpu(translate_buffer_read, cpu) = 0; + per_cpu(translate_buffer_write, cpu) = 0; + + kfree(per_cpu(translate_text, cpu)); + per_cpu(translate_text, cpu) = NULL; + } + + kfree(gator_crc32_table); + gator_crc32_table = NULL; +} diff --git a/drivers/gator/gator_ebs.c b/drivers/gator/gator_ebs.c new file mode 100644 index 00000000000..1208d69fbfd --- /dev/null +++ b/drivers/gator/gator_ebs.c @@ -0,0 +1,158 @@ +/** + * Copyright (C) ARM Limited 2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#if defined(__arm__) && (GATOR_PERF_PMU_SUPPORT) +#include +#include +#include + +#include + +static DEFINE_MUTEX(perf_mutex); + +extern int pmnc_counters; +extern int ccnt; +extern unsigned long pmnc_enabled[]; +extern unsigned long pmnc_event[]; +extern unsigned long pmnc_count[]; +extern unsigned long pmnc_key[]; + +static DEFINE_PER_CPU(struct perf_event *, pevent); +static DEFINE_PER_CPU(struct perf_event_attr *, pevent_attr); +static DEFINE_PER_CPU(int, key); +static DEFINE_PER_CPU(unsigned int, prev_value); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ + int cpu = smp_processor_id(); + + if (event != per_cpu(pevent, cpu)) + return; + + // Output backtrace + gator_add_sample(cpu, BACKTRACE_BUF, regs); + + // Collect counters + collect_counters(); +} + +static void gator_event_sampling_online_dispatch(int cpu) +{ + struct perf_event * ev; + + if (!event_based_sampling) + return; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler); +#else + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler, 0); +#endif + + if (IS_ERR(ev)) { + pr_err("gator: unable to start event-based-sampling"); + return; + } + + if (ev->state != PERF_EVENT_STATE_ACTIVE) { + pr_err("gator: unable to start event-based-sampling"); + perf_event_release_kernel(ev); + return; + } + + ev->pmu->read(ev); + per_cpu(prev_value, cpu) = local64_read(&ev->count); +} + +static void gator_event_sampling_offline_dispatch(int cpu) +{ + struct perf_event * pe = NULL; + + mutex_lock(&perf_mutex); + if (per_cpu(pevent, cpu)) { + pe = per_cpu(pevent, cpu); + per_cpu(pevent, cpu) = NULL; + } + mutex_unlock(&perf_mutex); + + if (pe) { + perf_event_release_kernel(pe); + } +} + +static int gator_event_sampling_start(void) +{ + int cnt, event = 0, count = 0, ebs_key = 0, cpu; + + for_each_present_cpu(cpu) { + per_cpu(pevent, cpu) = NULL; + per_cpu(pevent_attr, cpu) = NULL; + } + + event_based_sampling = false; + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_count[cnt] > 0) { + event_based_sampling = true; + event = pmnc_event[cnt]; + count = pmnc_count[cnt]; + ebs_key = pmnc_key[cnt]; + break; + } + } + + if (!event_based_sampling) + return 0; + + for_each_present_cpu(cpu) { + u32 size = sizeof(struct perf_event_attr); + per_cpu(pevent_attr, cpu) = kmalloc(size, GFP_KERNEL); + if (!per_cpu(pevent_attr, cpu)) + return -1; + + memset(per_cpu(pevent_attr, cpu), 0, size); + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_RAW; + per_cpu(pevent_attr, cpu)->size = size; + per_cpu(pevent_attr, cpu)->config = event; + per_cpu(pevent_attr, cpu)->sample_period = count; + per_cpu(pevent_attr, cpu)->pinned = 1; + + // handle special case for ccnt + if (cnt == ccnt) { + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_HARDWARE; + per_cpu(pevent_attr, cpu)->config = PERF_COUNT_HW_CPU_CYCLES; + } + + per_cpu(key, cpu) = ebs_key; + } + + return 0; +} + +static void gator_event_sampling_stop(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(pevent_attr, cpu)) { + kfree(per_cpu(pevent_attr, cpu)); + per_cpu(pevent_attr, cpu) = NULL; + } + } +} + +#else +static void gator_event_sampling_online_dispatch(int cpu) {} +static void gator_event_sampling_offline_dispatch(int cpu) {} +static int gator_event_sampling_start(void) {return 0;} +static void gator_event_sampling_stop(void) {} +#endif diff --git a/drivers/gator/gator_events.sh b/drivers/gator/gator_events.sh new file mode 100755 index 00000000000..5467dd6d17d --- /dev/null +++ b/drivers/gator/gator_events.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +EVENTS=`grep gator_events_init *.c | sed 's/.\+gator_events_init(\(.\+\)).\+/\1/'` + +( + echo /\* This file is auto generated \*/ + echo + for EVENT in $EVENTS; do + echo __weak int $EVENT\(void\)\; + done + echo + echo static int \(*gator_events_list[]\)\(void\) = { + for EVENT in $EVENTS; do + echo \ $EVENT, + done + echo }\; +) > $1.tmp + +cmp -s $1 $1.tmp && rm $1.tmp || mv $1.tmp $1 diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c new file mode 100644 index 00000000000..5f989ba4e86 --- /dev/null +++ b/drivers/gator/gator_events_armv6.c @@ -0,0 +1,244 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; + +/* + * Per-CPU PMCR + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ +#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ +#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ + +#define PMN0 0 +#define PMN1 1 +#define CCNT 2 +#define CNTMAX (CCNT+1) + +static int pmnc_counters = 0; +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +static inline void armv6_pmnc_write(u32 val) +{ + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0x0ffff77f; + asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); +} + +static inline u32 armv6_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); + return val; +} + +static void armv6_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + switch (cnt) { + case CCNT: + asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); + break; + case PMN0: + asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); + break; + case PMN1: + asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); + break; + } +} + +int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + pmnc_counters = 3; + + for (i = PMN0; i <= CCNT; i++) { + char buf[40]; + if (i == CCNT) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv6_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + u32 pmnc; + + if (armv6_pmnc_read() & PMCR_E) { + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + } + + /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ + armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | + PMCR_C | PMCR_P); + + /* configure control register */ + for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters) + if (cnt == PMN0) { + pmnc |= event << 20; + } else if (cnt == PMN1) { + pmnc |= event << 12; + } + + // Reset counter + armv6_pmnc_reset_counter(cnt); + } + armv6_pmnc_write(pmnc | PMCR_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv6_offline(int** buffer) +{ + unsigned int cnt; + + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + for (cnt = PMN0; cnt <= CCNT; cnt++) { + armv6_pmnc_reset_counter(cnt); + } + + return 0; +} + +static void gator_events_armv6_stop(void) +{ + unsigned int cnt; + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv6_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv6_pmnc_read() & PMCR_E)) { + return 0; + } + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + u32 value = 0; + switch (cnt) { + case CCNT: + asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value)); + break; + case PMN0: + asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value)); + break; + case PMN1: + asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value)); + break; + } + armv6_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv6_interface = { + .create_files = gator_events_armv6_create_files, + .stop = gator_events_armv6_stop, + .online = gator_events_armv6_online, + .offline = gator_events_armv6_offline, + .read = gator_events_armv6_read, +}; + +int gator_events_armv6_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM11"; + break; + case ARM11MPCORE: + pmnc_name = "ARM11MPCore"; + break; + default: + return -1; + } + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv6_interface); +} + +gator_events_init(gator_events_armv6_init); + +#else +int gator_events_armv6_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c new file mode 100644 index 00000000000..590421d110c --- /dev/null +++ b/drivers/gator/gator_events_armv7.c @@ -0,0 +1,319 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Disabling interrupts + * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions + * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves + * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used + * as these functions are being called from interrupt context. + */ + +#include "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (6+1) + +static const char *pmnc_name; +static int pmnc_counters; + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +inline void armv7_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +inline u32 armv7_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +inline u32 armv7_ccnt_read(u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 den = CCNT_REG; + u32 val; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval));// new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return val; +} + +inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 sel = (cnt - CNT0); + u32 den = 1 << sel; + u32 oldval; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval));// new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return oldval; +} + +static inline void armv7_pmnc_disable_interrupt(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31); + asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); +} + +inline u32 armv7_pmnc_reset_interrupt(void) +{ + // Get and reset overflow status flags + u32 flags; + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags)); + flags &= 0x8000003f; + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags)); + return flags; +} + +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + return cnt; +} + +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + return cnt; +} + +static inline int armv7_pmnc_select_counter(unsigned int cnt) +{ + u32 val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + return cnt; +} + +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (armv7_pmnc_select_counter(cnt) == cnt) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } +} + +static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv7_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (armv7_pmnc_read() & PMNC_E) { + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + } + + // Initialize & Reset PMNC: C bit and P bit + armv7_pmnc_write(PMNC_P | PMNC_C); + + // Reset overflow flags + armv7_pmnc_reset_interrupt(); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + // Disable counter + armv7_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count + if (cnt != CCNT) + armv7_pmnc_write_evtsel(cnt, event); + + armv7_pmnc_disable_interrupt(cnt); + + // Reset counter + cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0); + + // Enable counter + armv7_pmnc_enable_counter(cnt); + } + + // enable + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv7_offline(int** buffer) +{ + // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + + return 0; +} + +static void gator_events_armv7_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv7_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv7_pmnc_read() & PMNC_E)) { + return 0; + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = armv7_ccnt_read(0); + } else { + value = armv7_cntn_read(cnt, 0); + } + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv7_interface = { + .create_files = gator_events_armv7_create_files, + .stop = gator_events_armv7_stop, + .online = gator_events_armv7_online, + .offline = gator_events_armv7_offline, + .read = gator_events_armv7_read, +}; + +int gator_events_armv7_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case CORTEX_A5: + pmnc_name = "Cortex-A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "Cortex-A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "Cortex-A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "Cortex-A9"; + pmnc_counters = 6; + break; + case CORTEX_A15: + pmnc_name = "Cortex-A15"; + pmnc_counters = 6; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv7_interface); +} + +gator_events_init(gator_events_armv7_init); + +#else +int gator_events_armv7_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c new file mode 100644 index 00000000000..a8b811413f3 --- /dev/null +++ b/drivers/gator/gator_events_block.c @@ -0,0 +1,175 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define BLOCK_RQ_WR 0 +#define BLOCK_RQ_RD 1 + +#define BLOCK_TOTAL (BLOCK_RQ_RD+1) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +#define EVENTWRITE REQ_RW +#else +#define EVENTWRITE REQ_WRITE +#endif + +static ulong block_rq_wr_enabled; +static ulong block_rq_rd_enabled; +static ulong block_rq_wr_key; +static ulong block_rq_rd_key; +static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt); +static DEFINE_PER_CPU(int[BLOCK_TOTAL * 4], blockGet); +static DEFINE_PER_CPU(bool, new_data_avail); + +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +{ + unsigned long flags; + int write, size; + int cpu = smp_processor_id(); + + if (!rq) + return; + + write = rq->cmd_flags & EVENTWRITE; + size = rq->resid_len; + + if (!size) + return; + + // disable interrupts to synchronize with gator_events_block_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + if (write) + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] += size; + else + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] += size; + local_irq_restore(flags); + + per_cpu(new_data_avail, cpu) = true; +} + +static int gator_events_block_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* block_complete_wr */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key); + + /* block_complete_rd */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key); + + return 0; +} + +static int gator_events_block_start(void) +{ + int cpu; + + for_each_present_cpu(cpu) + per_cpu(new_data_avail, cpu) = true; + + // register tracepoints + if (block_rq_wr_enabled || block_rq_rd_enabled) + if (GATOR_REGISTER_TRACE(block_rq_complete)) + goto fail_block_rq_exit; + pr_debug("gator: registered block event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_block_rq_exit: + pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_block_stop(void) +{ + if (block_rq_wr_enabled || block_rq_rd_enabled) + GATOR_UNREGISTER_TRACE(block_rq_complete); + pr_debug("gator: unregistered block event tracepoints\n"); + + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; +} + +static int gator_events_block_read(int **buffer) +{ + unsigned long flags; + int len, value, cpu, data = 0; + cpu = smp_processor_id(); + + if (per_cpu(new_data_avail, cpu) == false) + return 0; + + per_cpu(new_data_avail, cpu) = false; + + len = 0; + if (block_rq_wr_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_WR]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; + per_cpu(blockGet, cpu)[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message + per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + if (block_rq_rd_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_RD]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; + per_cpu(blockGet, cpu)[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message + per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + + if (data != 0) + per_cpu(new_data_avail, cpu) = true; + + if (buffer) + *buffer = per_cpu(blockGet, cpu); + + return len; +} + +static struct gator_interface gator_events_block_interface = { + .create_files = gator_events_block_create_files, + .start = gator_events_block_start, + .stop = gator_events_block_stop, + .read = gator_events_block_read, +}; + +int gator_events_block_init(void) +{ + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; + + block_rq_wr_key = gator_events_get_key(); + block_rq_rd_key = gator_events_get_key(); + + return gator_events_install(&gator_events_block_interface); +} +gator_events_init(gator_events_block_init); diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c new file mode 100644 index 00000000000..435bc86fb5d --- /dev/null +++ b/drivers/gator/gator_events_irq.c @@ -0,0 +1,188 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define HARDIRQ 0 +#define SOFTIRQ 1 +#define TOTALIRQ (SOFTIRQ+1) + +static ulong hardirq_enabled; +static ulong softirq_enabled; +static ulong hardirq_key; +static ulong softirq_key; +static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); +static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); + +GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq, + struct irqaction *action, int ret)) +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, smp_processor_id())[HARDIRQ]++; + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec)) +#else +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, smp_processor_id())[SOFTIRQ]++; + local_irq_restore(flags); +} + +static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_irq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key); + + /* soft irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key); + + return 0; +} + +static int gator_events_irq_online(int** buffer) +{ + int len = 0, cpu = smp_processor_id(); + unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt + + // synchronization with the irq_exit functions is not necessary as the values are being reset + if (hardirq_enabled) { + local_irq_save(flags); + per_cpu(irqCnt, cpu)[HARDIRQ] = 0; + local_irq_restore(flags); + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (softirq_enabled) { + local_irq_save(flags); + per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; + local_irq_restore(flags); + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static int gator_events_irq_start(void) +{ + // register tracepoints + if (hardirq_enabled) + if (GATOR_REGISTER_TRACE(irq_handler_exit)) + goto fail_hardirq_exit; + if (softirq_enabled) + if (GATOR_REGISTER_TRACE(softirq_exit)) + goto fail_softirq_exit; + pr_debug("gator: registered irq tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_softirq_exit: + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); +fail_hardirq_exit: + pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_irq_stop(void) +{ + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); + if (softirq_enabled) + GATOR_UNREGISTER_TRACE(softirq_exit); + pr_debug("gator: unregistered irq tracepoints\n"); + + hardirq_enabled = 0; + softirq_enabled = 0; +} + +static int gator_events_irq_read(int **buffer) +{ + unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt + int len, value; + int cpu = smp_processor_id(); + + len = 0; + if (hardirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[HARDIRQ]; + per_cpu(irqCnt, cpu)[HARDIRQ] = 0; + local_irq_restore(flags); + + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + + if (softirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[SOFTIRQ]; + per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; + local_irq_restore(flags); + + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static struct gator_interface gator_events_irq_interface = { + .create_files = gator_events_irq_create_files, + .online = gator_events_irq_online, + .start = gator_events_irq_start, + .stop = gator_events_irq_stop, + .read = gator_events_irq_read, +}; + +int gator_events_irq_init(void) +{ + hardirq_key = gator_events_get_key(); + softirq_key = gator_events_get_key(); + + hardirq_enabled = 0; + softirq_enabled = 0; + + return gator_events_install(&gator_events_irq_interface); +} +gator_events_init(gator_events_irq_init); diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c new file mode 100644 index 00000000000..bd1c48a64e2 --- /dev/null +++ b/drivers/gator/gator_events_l2c-310.c @@ -0,0 +1,179 @@ +/** + * l2c310 (L2 Cache Controller) event counters for gator + * + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "gator.h" + +#define L2C310_COUNTERS_NUM 2 + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} l2c310_counters[L2C310_COUNTERS_NUM]; + +static int l2c310_buffer[L2C310_COUNTERS_NUM * 2]; + +static void __iomem *l2c310_base; + + + +static void gator_events_l2c310_reset_counters(void) +{ + u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL); + + val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1; + + writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + + +static int gator_events_l2c310_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &l2c310_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &l2c310_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &l2c310_counters[i].key); + } + + return 0; +} + +static int gator_events_l2c310_start(void) +{ + static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_CFG, + L2X0_EVENT_CNT1_CFG, + }; + int i; + + /* Counter event sources */ + for (i = 0; i < L2C310_COUNTERS_NUM; i++) + writel((l2c310_counters[i].event & 0xf) << 2, + l2c310_base + l2x0_event_cntx_cfg[i]); + + gator_events_l2c310_reset_counters(); + + /* Event counter enable */ + writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL); + + return 0; +} + +static void gator_events_l2c310_stop(void) +{ + /* Event counter disable */ + writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + +static int gator_events_l2c310_read(int **buffer) +{ + static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_VAL, + L2X0_EVENT_CNT1_VAL, + }; + int i; + int len = 0; + + if (smp_processor_id()) + return 0; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + if (l2c310_counters[i].enabled) { + l2c310_buffer[len++] = l2c310_counters[i].key; + l2c310_buffer[len++] = readl(l2c310_base + + l2x0_event_cntx_val[i]); + } + } + + /* l2c310 counters are saturating, not wrapping in case of overflow */ + gator_events_l2c310_reset_counters(); + + if (buffer) + *buffer = l2c310_buffer; + + return len; +} + +static struct gator_interface gator_events_l2c310_interface = { + .create_files = gator_events_l2c310_create_files, + .start = gator_events_l2c310_start, + .stop = gator_events_l2c310_stop, + .read = gator_events_l2c310_read, +}; + +static void __maybe_unused gator_events_l2c310_probe(unsigned long phys) +{ + if (l2c310_base) + return; + + l2c310_base = ioremap(phys, SZ_4K); + if (l2c310_base) { + u32 cache_id = readl(l2c310_base + L2X0_CACHE_ID); + + if ((cache_id & 0xff0003c0) != 0x410000c0) { + iounmap(l2c310_base); + l2c310_base = NULL; + } + } +} + +int gator_events_l2c310_init(void) +{ + int i; + + if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9) + return -1; + +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310) + gator_events_l2c310_probe(0x10502000); +#endif +#if defined(CONFIG_ARCH_OMAP4) + gator_events_l2c310_probe(0x48242000); +#endif +#if defined(CONFIG_ARCH_TEGRA) + gator_events_l2c310_probe(0x50043000); +#endif +#if defined(CONFIG_ARCH_U8500) + gator_events_l2c310_probe(0xa0412000); +#endif +#if defined(CONFIG_ARCH_VEXPRESS) + // A9x4 core tile (HBI-0191) + gator_events_l2c310_probe(0x1e00a000); + // New memory map tiles + gator_events_l2c310_probe(0x2c0f0000); +#endif + if (!l2c310_base) + return -1; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + l2c310_counters[i].enabled = 0; + l2c310_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_l2c310_interface); +} +gator_events_init(gator_events_l2c310_init); diff --git a/drivers/gator/gator_events_mali.c b/drivers/gator/gator_events_mali.c new file mode 100755 index 00000000000..21a63246f02 --- /dev/null +++ b/drivers/gator/gator_events_mali.c @@ -0,0 +1,724 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +#include +#include +#include + +#include "linux/mali_linux_trace.h" + +/* + * There are (currently) three different variants of the comms between gator and Mali: + * 1 (deprecated): No software counter support + * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears + * 3 (default): Single tracepoint for all s/w counters in a bundle. + * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when + * existing Mali DDKs are upgraded. + */ + +#if !defined(GATOR_MALI_INTERFACE_STYLE) +#define GATOR_MALI_INTERFACE_STYLE (3) +#endif + +#define MALI_200 (0x0a07) +#define MALI_300 (0x0b06) //This is not actually true; Mali-300 is also 0x0b07 +#define MALI_400 (0x0b07) +#define MALI_T6xx (0x0056) + +/* + * List of possible actions allowing DDK to be controlled by Streamline. + * The following numbers are used by DDK to control the frame buffer dumping. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_EVENTS_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if !defined(MALI_SUPPORT) +#error MALI_SUPPORT not defined! +#elif (MALI_SUPPORT != MALI_200) && (MALI_SUPPORT != MALI_300) && (MALI_SUPPORT != MALI_400) && (MALI_SUPPORT != MALI_T6xx) +#error MALI_SUPPORT set to an invalid device code +#endif + +static const char *mali_name; + +enum counters { + /* Timeline activity */ + ACTIVITY_VP = 0, + ACTIVITY_FP0, + ACTIVITY_FP1, + ACTIVITY_FP2, + ACTIVITY_FP3, + + /* L2 cache counters */ + COUNTER_L2_C0, + COUNTER_L2_C1, + + /* Vertex processor counters */ + COUNTER_VP_C0, + COUNTER_VP_C1, + + /* Fragment processor counters */ + COUNTER_FP0_C0, + COUNTER_FP0_C1, + COUNTER_FP1_C0, + COUNTER_FP1_C1, + COUNTER_FP2_C0, + COUNTER_FP2_C1, + COUNTER_FP3_C0, + COUNTER_FP3_C1, + + /* EGL Software Counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES Software Counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +}; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP +#define LAST_ACTIVITY_EVENT ACTIVITY_FP3 + +#define FIRST_HW_COUNTER COUNTER_L2_C0 +#define LAST_HW_COUNTER COUNTER_FP3_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP + +/* gatorfs variables for counter enable state, + * the event the counter should count and the + * 'key' (a unique id set by gatord and returned + * by gator.ko) + */ +static unsigned long counter_enabled[NUMBER_OF_EVENTS]; +static unsigned long counter_event[NUMBER_OF_EVENTS]; +static unsigned long counter_key[NUMBER_OF_EVENTS]; + +/* The data we have recorded */ +static u32 counter_data[NUMBER_OF_EVENTS]; +/* The address to sample (or 0 if samples are sent to us) */ +static u32* counter_address[NUMBER_OF_EVENTS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; +static unsigned long counter_prev[NUMBER_OF_EVENTS]; + +/* Note whether tracepoints have been registered */ +static int trace_registered; + +/** + * Calculate the difference and handle the overflow. + */ +static u32 get_difference(u32 start, u32 end) +{ + if (start - end >= 0) + { + return start - end; + } + + // Mali counters are unsigned 32 bit values that wrap. + return (4294967295u - end) + start; +} + +/** + * Returns non-zero if the given counter ID is an activity counter. + */ +static inline int is_activity_counter(unsigned int event_id) +{ + return (event_id >= FIRST_ACTIVITY_EVENT && + event_id <= LAST_ACTIVITY_EVENT); +} + +/** + * Returns non-zero if the given counter ID is a hardware counter. + */ +static inline int is_hw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER); +} + +/** + * Returns non-zero if the given counter ID is a software counter. + */ +static inline int is_sw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); +} + +/* + * The Mali DDK uses s64 types to contain software counter values, but gator + * can only use a maximum of 32 bits. This function scales a software counter + * to an appopriate range. + */ +static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) +{ + u32 scaled_value; + + switch (event_id) { + case COUNTER_GLES_UPLOAD_TEXTURE_TIME: + case COUNTER_GLES_UPLOAD_VBO_TIME: + scaled_value = (u32)div_s64(value, 1000000); + break; + default: + scaled_value = (u32)value; + break; + } + + return scaled_value; +} + +/* Probe for continuously sampled counter */ +#if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING +GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32* addr)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr); + if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) { + counter_address[event_id] = addr; + } +} +#endif + +/* Probe for hardware counter events */ +GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value); + if (is_hw_counter(event_id)) { + counter_data[event_id] = value; + } +} + +#if GATOR_MALI_INTERFACE_STYLE == 2 +GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) +{ + if (is_sw_counter(event_id)) { + counter_data[event_id] = scale_sw_counter_value(event_id, value); + } +} +#endif /* GATOR_MALI_INTERFACE_STYLE == 2 */ + + +#if GATOR_MALI_INTERFACE_STYLE == 3 +GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void * surface_id, unsigned int * counters)) +{ + u32 i; + + /* Copy over the values for those counters which are enabled. */ + for(i=FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if(counter_enabled[i]) + { + counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); + } + } +} +#endif /* GATOR_MALI_INTERFACE_STYLE == 3 */ + +//TODO need to work out how many fp units we have +u32 gator_mali_get_n_fp(void) { + return 4; +} + +//TODO need to work out what kind of Mali we are looking at +u32 gator_mali_get_id(void) { + return MALI_SUPPORT; +} + +int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) { + struct dentry *dir; + int event; + int n_fp = gator_mali_get_n_fp(); + + /* + * Create the filesystem entries for vertex processor, fragement processor + * and L2 cache timeline and hardware counters. Software counters get + * special handling after this block. + */ + for (event = FIRST_ACTIVITY_EVENT; event <= LAST_HW_COUNTER; event++) + { + char buf[40]; + + /* + * We can skip this event if it's for a non-existent fragment + * processor. + */ + if (((event - ACTIVITY_FP0 >= n_fp) && (event < COUNTER_L2_C0)) || + (((event - COUNTER_FP0_C0)/2 >= n_fp))) + { + continue; + } + + /* Otherwise, set up the filesystem entry for this event. */ + switch (event) { + case ACTIVITY_VP: + snprintf(buf, sizeof buf, "ARM_%s_VP_active", mali_name); + break; + case ACTIVITY_FP0: + case ACTIVITY_FP1: + case ACTIVITY_FP2: + case ACTIVITY_FP3: + snprintf(buf, sizeof buf, "ARM_%s_FP%d_active", + mali_name, event - ACTIVITY_FP0); + break; + case COUNTER_L2_C0: + case COUNTER_L2_C1: + snprintf(buf, sizeof buf, "ARM_%s_L2_cnt%d", + mali_name, event - COUNTER_L2_C0); + break; + case COUNTER_VP_C0: + case COUNTER_VP_C1: + snprintf(buf, sizeof buf, "ARM_%s_VP_cnt%d", + mali_name, event - COUNTER_VP_C0); + break; + case COUNTER_FP0_C0: + case COUNTER_FP0_C1: + case COUNTER_FP1_C0: + case COUNTER_FP1_C1: + case COUNTER_FP2_C0: + case COUNTER_FP2_C1: + case COUNTER_FP3_C0: + case COUNTER_FP3_C1: + snprintf(buf, sizeof buf, "ARM_%s_FP%d_cnt%d", mali_name, + (event - COUNTER_FP0_C0) / 2, (event - COUNTER_FP0_C0) % 2); + break; + default: + printk("gator: trying to create file for non-existent counter (%d)\n", event); + continue; + } + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + + /* Only create an event node for counters that can change what they count */ + if (event >= COUNTER_L2_C0) { + gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); + } + + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + /* Now set up the software counter entries */ + for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) + { + char buf[40]; + + snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event); + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + /* Now set up the special counter entries */ + for (event = FIRST_SPECIAL_COUNTER; event <= LAST_SPECIAL_COUNTER; event++) + { + char buf[40]; + + snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip", mali_name); + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + + return 0; +} + +//TODO +void _mali_profiling_set_event(unsigned int, unsigned int); +void _mali_osk_fb_control_set(unsigned int, unsigned int); +void _mali_profiling_control(unsigned int, unsigned int); + +void _mali_profiling_get_counters(unsigned int*, unsigned int*, unsigned int*, unsigned int*); +void (*_mali_profiling_get_counters_function_pointer)(unsigned int*, unsigned int*, unsigned int*, unsigned int*); + +/* + * Examine list of software counters and determine if any one is enabled. + * Returns 1 if any counter is enabled, 0 if none is. + */ +static int is_any_sw_counter_enabled(void) +{ + unsigned int i; + + for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if (counter_enabled[i]) + { + return 1; /* At least one counter is enabled */ + } + } + + return 0; /* No s/w counters enabled */ +} + +static void mali_counter_initialize(void) +{ + /* If a Mali driver is present and exporting the appropriate symbol + * then we can request the HW counters (of which there are only 2) + * be configured to count the desired events + */ + void (*set_hw_event)(unsigned int, unsigned int); + void (*set_fb_event)(unsigned int, unsigned int); + void (*mali_control)(unsigned int, unsigned int); + + set_hw_event = symbol_get(_mali_profiling_set_event); + + if (set_hw_event) { + int i; + + pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n",set_hw_event); + + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + if (counter_enabled[i]) { + set_hw_event(i, counter_event[i]); + } else { + set_hw_event(i, 0xFFFFFFFF); + } + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali online _mali_profiling_set_event symbol not found\n"); + } + + set_fb_event = symbol_get(_mali_osk_fb_control_set); + + if (set_fb_event) { + pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + + set_fb_event(0,(counter_enabled[COUNTER_FILMSTRIP]?1:0)); + + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); + } + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff; + unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + + mali_control(SW_EVENTS_ENABLE, (is_any_sw_counter_enabled()?1:0)); + mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP]?1:0)); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP]?1:0), rate); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + + _mali_profiling_get_counters_function_pointer = symbol_get(_mali_profiling_get_counters); + if (_mali_profiling_get_counters_function_pointer){ + pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", _mali_profiling_get_counters_function_pointer); + counter_prev[COUNTER_L2_C0] = 0; + counter_prev[COUNTER_L2_C1] = 0; + } + else{ + pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); + } +} + +static void mali_counter_deinitialize(void) +{ + void (*set_hw_event)(unsigned int, unsigned int); + void (*set_fb_event)(unsigned int, unsigned int); + void (*mali_control)(unsigned int, unsigned int); + + set_hw_event = symbol_get(_mali_profiling_set_event); + + if (set_hw_event) { + int i; + + pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n",set_hw_event); + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + set_hw_event(i, 0xFFFFFFFF); + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); + } + + set_fb_event = symbol_get(_mali_osk_fb_control_set); + + if (set_fb_event) { + pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + + set_fb_event(0,0); + + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); + } + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", set_fb_event); + + /* Reset the DDK state - disable counter collection */ + mali_control(SW_EVENTS_ENABLE, 0); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } + + if (_mali_profiling_get_counters_function_pointer){ + symbol_put(_mali_profiling_get_counters); + } + +} + +static int gator_events_mali_start(void) { + // register tracepoints + if (GATOR_REGISTER_TRACE(mali_hw_counter)) { + printk("gator: mali_hw_counter tracepoint failed to activate\n"); + return -1; + } + +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ + if (GATOR_REGISTER_TRACE(mali_sw_counter)) { + printk("gator: mali_sw_counter tracepoint failed to activate\n"); + return -1; + } +#elif GATOR_MALI_INTERFACE_STYLE == 3 +/* For Mali drivers with built-in support. */ + if (GATOR_REGISTER_TRACE(mali_sw_counters)) { + printk("gator: mali_sw_counters tracepoint failed to activate\n"); + return -1; + } +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + + trace_registered = 1; + + mali_counter_initialize(); + return 0; +} + +static void gator_events_mali_stop(void) { + unsigned int cnt; + + pr_debug("gator: mali stop\n"); + + if (trace_registered) { + GATOR_UNREGISTER_TRACE(mali_hw_counter); + +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ + GATOR_UNREGISTER_TRACE(mali_sw_counter); +#elif GATOR_MALI_INTERFACE_STYLE == 3 + /* For Mali drivers with built-in support. */ + GATOR_UNREGISTER_TRACE(mali_sw_counters); +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + + pr_debug("gator: mali timeline tracepoint deactivated\n"); + + trace_registered = 0; + } + + for (cnt = FIRST_ACTIVITY_EVENT; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_address[cnt] = NULL; + } + + mali_counter_deinitialize(); +} + +static int gator_events_mali_read(int **buffer) { + int cnt, len = 0; + + if (smp_processor_id()) return 0; + + // Read the L2 C0 and C1 here. + if (counter_enabled[COUNTER_L2_C0] || counter_enabled[COUNTER_L2_C1] ) { + u32 src0 = 0; + u32 val0 = 0; + u32 src1 = 0; + u32 val1 = 0; + + // Poke the driver to get the counter values + if (_mali_profiling_get_counters_function_pointer){ + _mali_profiling_get_counters_function_pointer(&src0, &val0, &src1, &val1); + } + + if (counter_enabled[COUNTER_L2_C0]) + { + // Calculate and save src0's counter val0 + counter_dump[len++] = counter_key[COUNTER_L2_C0]; + counter_dump[len++] = get_difference(val0, counter_prev[COUNTER_L2_C0]); + } + + if (counter_enabled[COUNTER_L2_C1]) + { + // Calculate and save src1's counter val1 + counter_dump[len++] = counter_key[COUNTER_L2_C1]; + counter_dump[len++] = get_difference(val1, counter_prev[COUNTER_L2_C1]); + } + + // Save the previous values for the counters. + counter_prev[COUNTER_L2_C0] = val0; + counter_prev[COUNTER_L2_C1] = val1; + } + + // Process other (non-timeline) counters. + for (cnt = COUNTER_VP_C0; cnt <= LAST_SW_COUNTER; cnt++) { + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + + counter_data[cnt] = 0; + } + } + + if (buffer) { + *buffer = (int*) counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_interface = { + .create_files = gator_events_mali_create_files, + .start = gator_events_mali_start, + .stop = gator_events_mali_stop, + .read = gator_events_mali_read, +}; + +int gator_events_mali_init(void) +{ + unsigned int cnt; + u32 id = gator_mali_get_id(); + + switch (id) { + case MALI_T6xx: + mali_name = "Mali-T6xx"; + break; + case MALI_400: + mali_name = "Mali-400"; + break; + case MALI_300: + mali_name = "Mali-300"; + break; + case MALI_200: + mali_name = "Mali-200"; + break; + default: + printk("Unknown Mali ID (%d)\n", id); + return -1; + } + + pr_debug("gator: mali init\n"); + + for (cnt = FIRST_ACTIVITY_EVENT; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_key[cnt] = gator_events_get_key(); + counter_address[cnt] = NULL; + counter_data[cnt] = 0; + } + + trace_registered = 0; + + return gator_events_install(&gator_events_mali_interface); +} +gator_events_init(gator_events_mali_init); diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c new file mode 100644 index 00000000000..ad552ef6c6e --- /dev/null +++ b/drivers/gator/gator_events_meminfo.c @@ -0,0 +1,230 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include +#include +#include + +#define MEMINFO_MEMFREE 0 +#define MEMINFO_MEMUSED 1 +#define MEMINFO_BUFFERRAM 2 +#define MEMINFO_TOTAL 3 + +static ulong meminfo_global_enabled; +static ulong meminfo_enabled[MEMINFO_TOTAL]; +static ulong meminfo_key[MEMINFO_TOTAL]; +static unsigned long long meminfo_buffer[MEMINFO_TOTAL * 2]; +static int meminfo_length = 0; +static unsigned int mem_event = 0; +static bool new_data_avail; + +static void wq_sched_handler(struct work_struct *wsptr); + +DECLARE_WORK(work, wq_sched_handler); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) +GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { +#else +GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) { +#endif + mem_event++; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) +GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) { +#else +GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) { +#endif + mem_event++; +} + +GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) { + mem_event++; +} + +static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < MEMINFO_TOTAL; i++) { + switch (i) { + case MEMINFO_MEMFREE: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree"); + break; + case MEMINFO_MEMUSED: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused"); + break; + case MEMINFO_BUFFERRAM: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram"); + break; + default: + return -1; + } + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]); + } + + return 0; +} + +static int gator_events_meminfo_start(void) +{ + int i; + + new_data_avail = true; + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + meminfo_global_enabled = 1; + } + } + + if (meminfo_global_enabled == 0) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + if (GATOR_REGISTER_TRACE(mm_page_free_direct)) +#else + if (GATOR_REGISTER_TRACE(mm_page_free)) +#endif + goto mm_page_free_exit; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + if (GATOR_REGISTER_TRACE(mm_pagevec_free)) +#else + if (GATOR_REGISTER_TRACE(mm_page_free_batched)) +#endif + goto mm_page_free_batched_exit; + if (GATOR_REGISTER_TRACE(mm_page_alloc)) + goto mm_page_alloc_exit; + + return 0; + +mm_page_alloc_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +#else + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif +mm_page_free_batched_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_page_free_direct); +#else + GATOR_UNREGISTER_TRACE(mm_page_free); +#endif +mm_page_free_exit: + return -1; +} + +static void gator_events_meminfo_stop(void) +{ + int i; + + if (meminfo_global_enabled) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) + GATOR_UNREGISTER_TRACE(mm_page_free_direct); + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +#else + GATOR_UNREGISTER_TRACE(mm_page_free); + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif + GATOR_UNREGISTER_TRACE(mm_page_alloc); + } + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + } +} + +// Must be run in process context (work queue) as the kernel function si_meminfo() can sleep +static void wq_sched_handler(struct work_struct *wsptr) +{ + struct sysinfo info; + int i, len; + unsigned long long value; + + meminfo_length = len = 0; + + si_meminfo(&info); + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + switch (i) { + case MEMINFO_MEMFREE: + value = info.freeram * PAGE_SIZE; + break; + case MEMINFO_MEMUSED: + value = (info.totalram - info.freeram) * PAGE_SIZE; + break; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = (unsigned long long)meminfo_key[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; +} + +static int gator_events_meminfo_read(long long **buffer) +{ + static unsigned int last_mem_event = 0; + + if (smp_processor_id() || !meminfo_global_enabled) + return 0; + + if (last_mem_event != mem_event) { + last_mem_event = mem_event; + if (in_interrupt()) { + schedule_work(&work); + } else { + wq_sched_handler(NULL); + } + } + + if (!new_data_avail) + return 0; + + new_data_avail = false; + + if (buffer) + *buffer = meminfo_buffer; + + return meminfo_length; +} + +static struct gator_interface gator_events_meminfo_interface = { + .create_files = gator_events_meminfo_create_files, + .start = gator_events_meminfo_start, + .stop = gator_events_meminfo_stop, + .read64 = gator_events_meminfo_read, +}; + +int gator_events_meminfo_init(void) +{ + int i; + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + meminfo_key[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_meminfo_interface); +} +gator_events_init(gator_events_meminfo_init); diff --git a/drivers/gator/gator_events_mmaped.c b/drivers/gator/gator_events_mmaped.c new file mode 100644 index 00000000000..f81c40240d9 --- /dev/null +++ b/drivers/gator/gator_events_mmaped.c @@ -0,0 +1,231 @@ +/* + * Example events provider + * + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Similar entries to those below must be present in the events.xml file. + * To add them to the events.xml, create an events-mmap.xml with the + * following contents and rebuild gatord: + * + * + * + * + * + * + * + * + * + * + * + */ + +#include +#include +#include + +#include "gator.h" + +#define MMAPED_COUNTERS_NUM 3 + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} mmaped_counters[MMAPED_COUNTERS_NUM]; + +static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2]; + +#ifdef TODO +static void __iomem *mmaped_base; +#endif + +#ifndef TODO +static s64 prev_time; +#endif + +/* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/events */ +static int gator_events_mmaped_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "mmaped_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &mmaped_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &mmaped_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &mmaped_counters[i].key); + } + + return 0; +} + +static int gator_events_mmaped_start(void) +{ +#ifdef TODO + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) + writel(mmaped_counters[i].event, + mmaped_base + COUNTERS_CONFIG_OFFSET[i]); + + writel(ENABLED, COUNTERS_CONTROL_OFFSET); +#endif + +#ifndef TODO + struct timespec ts; + getnstimeofday(&ts); + prev_time = timespec_to_ns(&ts); +#endif + + return 0; +} + +static void gator_events_mmaped_stop(void) +{ +#ifdef TODO + writel(DISABLED, COUNTERS_CONTROL_OFFSET); +#endif +} + +#ifndef TODO +/* This function "simulates" counters, generating values of fancy + * functions like sine or triangle... */ +static int mmaped_simulate(int counter, int delta_in_us) +{ + int result = 0; + + switch (counter) { + case 0: /* sort-of-sine */ + { + static int t = 0; + int x; + + t += delta_in_us; + if (t > 2048000) + t = 0; + + if (t % 1024000 < 512000) + x = 512000 - (t % 512000); + else + x = t % 512000; + + result = 32 * x / 512000; + result = result * result; + + if (t < 1024000) + result = 1922 - result; + } + break; + case 1: /* triangle */ + { + static int v, d = 1; + + v = v + d * delta_in_us; + if (v < 0) { + v = 0; + d = 1; + } else if (v > 1000000) { + v = 1000000; + d = -1; + } + + result = v; + } + break; + case 2: /* PWM signal */ + { + static int t, dc, x; + + t += delta_in_us; + if (t > 1000000) + t = 0; + if (x / 1000000 != (x + delta_in_us) / 1000000) + dc = (dc + 100000) % 1000000; + x += delta_in_us; + + result = t < dc ? 0 : 10; + } + break; + } + + return result; +} +#endif + +static int gator_events_mmaped_read(int **buffer) +{ + int i; + int len = 0; +#ifndef TODO + int delta_in_us; + struct timespec ts; + s64 time; +#endif + + /* System wide counters - read from one core only */ + if (smp_processor_id()) + return 0; + +#ifndef TODO + getnstimeofday(&ts); + time = timespec_to_ns(&ts); + delta_in_us = (int)(time - prev_time) / 1000; + prev_time = time; +#endif + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + if (mmaped_counters[i].enabled) { + mmaped_buffer[len++] = mmaped_counters[i].key; +#ifdef TODO + mmaped_buffer[len++] = readl(mmaped_base + + COUNTERS_VALUE_OFFSET[i]); +#else + mmaped_buffer[len++] = mmaped_simulate( + mmaped_counters[i].event, delta_in_us); +#endif + } + } + + if (buffer) + *buffer = mmaped_buffer; + + return len; +} + +static struct gator_interface gator_events_mmaped_interface = { + .create_files = gator_events_mmaped_create_files, + .start = gator_events_mmaped_start, + .stop = gator_events_mmaped_stop, + .read = gator_events_mmaped_read, +}; + +/* Must not be static! */ +int __init gator_events_mmaped_init(void) +{ + int i; + +#ifdef TODO + mmaped_base = ioremap(COUNTERS_PHYS_ADDR, SZ_4K); + if (!mmaped_base) + return -ENOMEM; +#endif + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + mmaped_counters[i].enabled = 0; + mmaped_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_mmaped_interface); +} +gator_events_init(gator_events_mmaped_init); diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c new file mode 100644 index 00000000000..9298905e626 --- /dev/null +++ b/drivers/gator/gator_events_net.c @@ -0,0 +1,157 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include +#include + +#define NETRX 0 +#define NETTX 1 +#define TOTALNET 2 + +static ulong netrx_enabled; +static ulong nettx_enabled; +static ulong netrx_key; +static ulong nettx_key; +static int rx_total, tx_total; +static ulong netPrev[TOTALNET]; +static int netGet[TOTALNET * 4]; + +static void get_network_stats(struct work_struct *wsptr) { + int rx = 0, tx = 0; + struct net_device *dev; + + for_each_netdev(&init_net, dev) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + const struct net_device_stats *stats = dev_get_stats(dev); +#else + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); +#endif + rx += stats->rx_bytes; + tx += stats->tx_bytes; + } + rx_total = rx; + tx_total = tx; +} +DECLARE_WORK(wq_get_stats, get_network_stats); + +static void calculate_delta(int *rx, int *tx) +{ + int rx_calc, tx_calc; + + rx_calc = (int)(rx_total - netPrev[NETRX]); + if (rx_calc < 0) + rx_calc = 0; + netPrev[NETRX] += rx_calc; + + tx_calc = (int)(tx_total - netPrev[NETTX]); + if (tx_calc < 0) + tx_calc = 0; + netPrev[NETTX] += tx_calc; + + *rx = rx_calc; + *tx = tx_calc; +} + +static int gator_events_net_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_net_rx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key); + + dir = gatorfs_mkdir(sb, root, "Linux_net_tx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key); + + return 0; +} + +static int gator_events_net_start(void) +{ + get_network_stats(NULL); + netPrev[NETRX] = rx_total; + netPrev[NETTX] = tx_total; + return 0; +} + +static void gator_events_net_stop(void) +{ + netrx_enabled = 0; + nettx_enabled = 0; +} + +static int gator_events_net_read(int **buffer) +{ + int len, rx_delta, tx_delta; + static int last_rx_delta = 0, last_tx_delta = 0; + + if (smp_processor_id() != 0) + return 0; + + if (!netrx_enabled && !nettx_enabled) + return 0; + + if (in_interrupt()){ + schedule_work(&wq_get_stats); + } else { + get_network_stats(NULL); + } + + calculate_delta(&rx_delta, &tx_delta); + + len = 0; + if (netrx_enabled && last_rx_delta != rx_delta) { + last_rx_delta = rx_delta; + netGet[len++] = netrx_key; + netGet[len++] = 0; // indicates to Streamline that rx_delta bytes were transmitted now, not since the last message + netGet[len++] = netrx_key; + netGet[len++] = rx_delta; + } + + if (nettx_enabled && last_tx_delta != tx_delta) { + last_tx_delta = tx_delta; + netGet[len++] = nettx_key; + netGet[len++] = 0; // indicates to Streamline that tx_delta bytes were transmitted now, not since the last message + netGet[len++] = nettx_key; + netGet[len++] = tx_delta; + } + + if (buffer) + *buffer = netGet; + + return len; +} + +static struct gator_interface gator_events_net_interface = { + .create_files = gator_events_net_create_files, + .start = gator_events_net_start, + .stop = gator_events_net_stop, + .read = gator_events_net_read, +}; + +int gator_events_net_init(void) +{ + netrx_key = gator_events_get_key(); + nettx_key = gator_events_get_key(); + + netrx_enabled = 0; + nettx_enabled = 0; + + return gator_events_install(&gator_events_net_interface); +} +gator_events_init(gator_events_net_init); diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c new file mode 100755 index 00000000000..da76a9cb409 --- /dev/null +++ b/drivers/gator/gator_events_perf_pmu.c @@ -0,0 +1,306 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "gator.h" + +// gator_events_armvX.c is used for Linux 2.6.x +#if GATOR_PERF_PMU_SUPPORT + +static const char *pmnc_name; +int pmnc_counters; +int ccnt = 0; + +#define CNTMAX (6+1) + +static DEFINE_MUTEX(perf_mutex); + +unsigned long pmnc_enabled[CNTMAX]; +unsigned long pmnc_event[CNTMAX]; +unsigned long pmnc_count[CNTMAX]; +unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfCurr); +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX], perfPrevDelta); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); +static DEFINE_PER_CPU(struct perf_event *[CNTMAX], pevent); +static DEFINE_PER_CPU(struct perf_event_attr *[CNTMAX], pevent_attr); + +static void gator_events_perf_pmu_stop(void); + +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ulong(sb, dir, "count", &pmnc_count[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ +// Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll +} + +static int gator_events_perf_pmu_online(int** buffer) +{ + int cnt, len = 0, cpu = smp_processor_id(); + + // read the counters and toss the invalid data, return zero instead + for (cnt = 0; cnt < pmnc_counters; cnt++) { + struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); + per_cpu(perfPrevDelta, cpu)[cnt] = 0; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static void gator_events_perf_pmu_online_dispatch(int cpu) +{ + int cnt; + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (per_cpu(pevent, cpu)[cnt] != NULL || per_cpu(pevent_attr, cpu)[cnt] == 0) + continue; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler); +#else + per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler, 0); +#endif + if (IS_ERR(per_cpu(pevent, cpu)[cnt])) { + pr_debug("gator: unable to online a counter on cpu %d\n", cpu); + per_cpu(pevent, cpu)[cnt] = NULL; + continue; + } + + if (per_cpu(pevent, cpu)[cnt]->state != PERF_EVENT_STATE_ACTIVE) { + pr_debug("gator: inactive counter on cpu %d\n", cpu); + perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]); + per_cpu(pevent, cpu)[cnt] = NULL; + continue; + } + } +} + +static void gator_events_perf_pmu_offline_dispatch(int cpu) +{ + int cnt; + struct perf_event * pe; + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + pe = NULL; + mutex_lock(&perf_mutex); + if (per_cpu(pevent, cpu)[cnt]) { + pe = per_cpu(pevent, cpu)[cnt]; + per_cpu(pevent, cpu)[cnt] = NULL; + } + mutex_unlock(&perf_mutex); + + if (pe) { + perf_event_release_kernel(pe); + } + } +} + +static int gator_events_perf_pmu_start(void) +{ + int cnt, cpu; + u32 size = sizeof(struct perf_event_attr); + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < pmnc_counters; cnt++) { + per_cpu(pevent, cpu)[cnt] = NULL; + if (!pmnc_enabled[cnt]) // Skip disabled counters + continue; + + per_cpu(perfPrev, cpu)[cnt] = 0; + per_cpu(perfCurr, cpu)[cnt] = 0; + per_cpu(perfPrevDelta, cpu)[cnt] = 0; + per_cpu(pevent_attr, cpu)[cnt] = kmalloc(size, GFP_KERNEL); + if (!per_cpu(pevent_attr, cpu)[cnt]) { + gator_events_perf_pmu_stop(); + return -1; + } + + memset(per_cpu(pevent_attr, cpu)[cnt], 0, size); + per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_RAW; + per_cpu(pevent_attr, cpu)[cnt]->size = size; + per_cpu(pevent_attr, cpu)[cnt]->config = pmnc_event[cnt]; + per_cpu(pevent_attr, cpu)[cnt]->sample_period = 0; + per_cpu(pevent_attr, cpu)[cnt]->pinned = 1; + + // handle special case for ccnt + if (cnt == ccnt) { + per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_HARDWARE; + per_cpu(pevent_attr, cpu)[cnt]->config = PERF_COUNT_HW_CPU_CYCLES; + } + } + } + + return 0; +} + +static void gator_events_perf_pmu_stop(void) +{ + unsigned int cnt, cpu; + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (per_cpu(pevent_attr, cpu)[cnt]) { + kfree(per_cpu(pevent_attr, cpu)[cnt]); + per_cpu(pevent_attr, cpu)[cnt] = NULL; + } + } + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_count[cnt] = 0; + } +} + +static int gator_events_perf_pmu_read(int **buffer) +{ + int cnt, delta, len = 0; + int cpu = smp_processor_id(); + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); + delta = per_cpu(perfCurr, cpu)[cnt] - per_cpu(perfPrev, cpu)[cnt]; + if (delta != per_cpu(perfPrevDelta, cpu)[cnt]) { + per_cpu(perfPrevDelta, cpu)[cnt] = delta; + per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt]; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + if (delta < 0) + delta *= -1; + per_cpu(perfCnt, cpu)[len++] = delta; + } + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_perf_pmu_interface = { + .create_files = gator_events_perf_pmu_create_files, + .start = gator_events_perf_pmu_start, + .stop = gator_events_perf_pmu_stop, + .online = gator_events_perf_pmu_online, + .online_dispatch = gator_events_perf_pmu_online_dispatch, + .offline_dispatch = gator_events_perf_pmu_offline_dispatch, + .read = gator_events_perf_pmu_read, +}; + +int gator_events_perf_pmu_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM_ARM11"; + pmnc_counters = 3; + ccnt = 2; + break; + case ARM11MPCORE: + pmnc_name = "ARM_ARM11MPCore"; + pmnc_counters = 3; + break; + case CORTEX_A5: + pmnc_name = "ARM_Cortex-A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "ARM_Cortex-A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "ARM_Cortex-A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "ARM_Cortex-A9"; + pmnc_counters = 6; + break; + case CORTEX_A15: + pmnc_name = "ARM_Cortex-A15"; + pmnc_counters = 6; + break; + case SCORPION: + pmnc_name = "Scorpion"; + pmnc_counters = 4; + break; + case SCORPIONMP: + pmnc_name = "ScorpionMP"; + pmnc_counters = 4; + break; + case KRAITSIM: + case KRAIT: + pmnc_name = "Krait"; + pmnc_counters = 4; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = 0; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_count[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_perf_pmu_interface); +} + +gator_events_init(gator_events_perf_pmu_init); +#endif diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c new file mode 100644 index 00000000000..9bed3641dbb --- /dev/null +++ b/drivers/gator/gator_events_sched.c @@ -0,0 +1,114 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include "gator.h" +#include + +#define SCHED_SWITCH 0 +#define SCHED_TOTAL (SCHED_SWITCH+1) + +static ulong sched_switch_enabled; +static ulong sched_switch_key; +static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); +static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_sched_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(schedCnt, smp_processor_id())[SCHED_SWITCH]++; + local_irq_restore(flags); +} + +static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* switch */ + dir = gatorfs_mkdir(sb, root, "Linux_sched_switch"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key); + + return 0; +} + +static int gator_events_sched_start(void) +{ + // register tracepoints + if (sched_switch_enabled) + if (GATOR_REGISTER_TRACE(sched_switch)) + goto sched_switch_exit; + pr_debug("gator: registered scheduler event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +sched_switch_exit: + pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_sched_stop(void) +{ + if (sched_switch_enabled) + GATOR_UNREGISTER_TRACE(sched_switch); + pr_debug("gator: unregistered scheduler event tracepoints\n"); + + sched_switch_enabled = 0; +} + +static int gator_events_sched_read(int **buffer) +{ + unsigned long flags; + int len, value; + int cpu = smp_processor_id(); + + len = 0; + if (sched_switch_enabled) { + local_irq_save(flags); + value = per_cpu(schedCnt, cpu)[SCHED_SWITCH]; + per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0; + local_irq_restore(flags); + per_cpu(schedGet, cpu)[len++] = sched_switch_key; + per_cpu(schedGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(schedGet, cpu); + + return len; +} + +static struct gator_interface gator_events_sched_interface = { + .create_files = gator_events_sched_create_files, + .start = gator_events_sched_start, + .stop = gator_events_sched_stop, + .read = gator_events_sched_read, +}; + +int gator_events_sched_init(void) +{ + sched_switch_enabled = 0; + + sched_switch_key = gator_events_get_key(); + + return gator_events_install(&gator_events_sched_interface); +} +gator_events_init(gator_events_sched_init); diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c new file mode 100644 index 00000000000..ed0d8de6b9f --- /dev/null +++ b/drivers/gator/gator_events_scorpion.c @@ -0,0 +1,678 @@ +/** + * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; +static int pmnc_counters; + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ +#define PMNC_X (1 << 4) /* Export to ETM */ +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (4+1) + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +enum scorpion_perf_types { + SCORPION_ICACHE_EXPL_INV = 0x4c, + SCORPION_ICACHE_MISS = 0x4d, + SCORPION_ICACHE_ACCESS = 0x4e, + SCORPION_ICACHE_CACHEREQ_L2 = 0x4f, + SCORPION_ICACHE_NOCACHE_L2 = 0x50, + SCORPION_HIQUP_NOPED = 0x51, + SCORPION_DATA_ABORT = 0x52, + SCORPION_IRQ = 0x53, + SCORPION_FIQ = 0x54, + SCORPION_ALL_EXCPT = 0x55, + SCORPION_UNDEF = 0x56, + SCORPION_SVC = 0x57, + SCORPION_SMC = 0x58, + SCORPION_PREFETCH_ABORT = 0x59, + SCORPION_INDEX_CHECK = 0x5a, + SCORPION_NULL_CHECK = 0x5b, + SCORPION_EXPL_ICIALLU = 0x5c, + SCORPION_IMPL_ICIALLU = 0x5d, + SCORPION_NONICIALLU_BTAC_INV = 0x5e, + SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f, + SCORPION_SPIPE_ONLY_CYCLES = 0x60, + SCORPION_XPIPE_ONLY_CYCLES = 0x61, + SCORPION_DUAL_CYCLES = 0x62, + SCORPION_DISPATCH_ANY_CYCLES = 0x63, + SCORPION_FIFO_FULLBLK_CMT = 0x64, + SCORPION_FAIL_COND_INST = 0x65, + SCORPION_PASS_COND_INST = 0x66, + SCORPION_ALLOW_VU_CLK = 0x67, + SCORPION_VU_IDLE = 0x68, + SCORPION_ALLOW_L2_CLK = 0x69, + SCORPION_L2_IDLE = 0x6a, + SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b, + SCORPION_DTLB_EXPL_INV = 0x6c, + SCORPION_DTLB_MISS = 0x6d, + SCORPION_DTLB_ACCESS = 0x6e, + SCORPION_ITLB_MISS = 0x6f, + SCORPION_ITLB_IMPL_INV = 0x70, + SCORPION_ITLB_EXPL_INV = 0x71, + SCORPION_UTLB_D_MISS = 0x72, + SCORPION_UTLB_D_ACCESS = 0x73, + SCORPION_UTLB_I_MISS = 0x74, + SCORPION_UTLB_I_ACCESS = 0x75, + SCORPION_UTLB_INV_ASID = 0x76, + SCORPION_UTLB_INV_MVA = 0x77, + SCORPION_UTLB_INV_ALL = 0x78, + SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79, + SCORPION_S2_HOLD = 0x7a, + SCORPION_S2_HOLD_DEV_OP = 0x7b, + SCORPION_S2_HOLD_ORDER = 0x7c, + SCORPION_S2_HOLD_BARRIER = 0x7d, + SCORPION_VIU_DUAL_CYCLE = 0x7e, + SCORPION_VIU_SINGLE_CYCLE = 0x7f, + SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80, + SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81, + SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82, + SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83, + SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84, + SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85, + SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86, + SCORPION_EXCEPTIONS_INV_OPERATION = 0x87, + SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88, + SCORPION_COND_INST_FAIL_VX_PIPE = 0x89, + SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a, + SCORPION_EXCEPTIONS_OVERFLOW = 0x8b, + SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c, + SCORPION_EXCEPTIONS_DENORM = 0x8d, +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + SCORPIONMP_NUM_BARRIERS = 0x8e, + SCORPIONMP_BARRIER_CYCLES = 0x8f, +#else + SCORPION_BANK_AB_HIT = 0x8e, + SCORPION_BANK_AB_ACCESS = 0x8f, + SCORPION_BANK_CD_HIT = 0x90, + SCORPION_BANK_CD_ACCESS = 0x91, + SCORPION_BANK_AB_DSIDE_HIT = 0x92, + SCORPION_BANK_AB_DSIDE_ACCESS = 0x93, + SCORPION_BANK_CD_DSIDE_HIT = 0x94, + SCORPION_BANK_CD_DSIDE_ACCESS = 0x95, + SCORPION_BANK_AB_ISIDE_HIT = 0x96, + SCORPION_BANK_AB_ISIDE_ACCESS = 0x97, + SCORPION_BANK_CD_ISIDE_HIT = 0x98, + SCORPION_BANK_CD_ISIDE_ACCESS = 0x99, + SCORPION_ISIDE_RD_WAIT = 0x9a, + SCORPION_DSIDE_RD_WAIT = 0x9b, + SCORPION_BANK_BYPASS_WRITE = 0x9c, + SCORPION_BANK_AB_NON_CASTOUT = 0x9d, + SCORPION_BANK_AB_L2_CASTOUT = 0x9e, + SCORPION_BANK_CD_NON_CASTOUT = 0x9f, + SCORPION_BANK_CD_L2_CASTOUT = 0xa0, +#endif + MSM_MAX_EVT +}; + +struct scorp_evt { + u32 evt_type; + u32 val; + u8 grp; + u32 evt_type_act; +}; + +static const struct scorp_evt sc_evt[] = { + {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d}, + {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e}, + {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f}, + {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f}, + {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f}, + {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e}, + {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c}, + {SCORPION_IRQ, 0x80000a00, 0, 0x4d}, + {SCORPION_FIQ, 0x800a0000, 0, 0x4e}, + {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f}, + {SCORPION_UNDEF, 0x8000000b, 0, 0x4c}, + {SCORPION_SVC, 0x80000b00, 0, 0x4d}, + {SCORPION_SMC, 0x800b0000, 0, 0x4e}, + {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f}, + {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c}, + {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d}, + {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c}, + {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d}, + {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e}, + {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f}, + + {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51}, + {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52}, + {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53}, + {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53}, + {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50}, + {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52}, + {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53}, + {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50}, + {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51}, + {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52}, + {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53}, + + {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54}, + {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55}, + {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56}, + {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57}, + {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55}, + {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56}, + {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57}, + {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54}, + {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55}, + {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56}, + {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57}, + {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55}, + {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56}, + {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57}, + {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55}, + {SCORPION_S2_HOLD, 0x88000000, 2, 0x57}, + {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55}, + {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56}, + {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57}, + + {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c}, + {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d}, + {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c}, + {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d}, + {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e}, + {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c}, + {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c}, + {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d}, + {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e}, + {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c}, + {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d}, + {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e}, + {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f}, + {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c}, + {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d}, + {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f}, + +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59}, + {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a}, +#else + {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58}, + {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59}, + {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a}, + {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b}, + {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58}, + {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59}, + {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a}, + {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b}, + {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58}, + {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59}, + {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a}, + {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b}, + {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58}, + {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a}, + {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58}, + {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58}, + {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59}, + {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a}, + {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b}, +#endif +}; + +static inline void scorpion_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +static inline u32 scorpion_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_ccnt_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_cntn_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); + return val; +} + +static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + + return cnt; +} + +static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + + return cnt; +} + +static inline int scorpion_pmnc_select_counter(unsigned int cnt) +{ + u32 val; + + if ((cnt == CCNT) || (cnt >= CNTMAX)) { + pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + + return cnt; +} + +static u32 scorpion_read_lpm0(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm0(u32 val) +{ + asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm1(void) +{ + u32 val; + asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm1(u32 val) +{ + asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm2(void) +{ + u32 val; + asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm2(u32 val) +{ + asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_l2lpm(void) +{ + u32 val; + asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_l2lpm(u32 val) +{ + asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val)); +} + +static u32 scorpion_read_vlpm(void) +{ + u32 val; + asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_vlpm(u32 val) +{ + asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val)); +} + +struct scorpion_access_funcs { + u32 (*read) (void); + void (*write) (u32); +}; + +struct scorpion_access_funcs scor_func[] = { + {scorpion_read_lpm0, scorpion_write_lpm0}, + {scorpion_read_lpm1, scorpion_write_lpm1}, + {scorpion_read_lpm2, scorpion_write_lpm2}, + {scorpion_read_l2lpm, scorpion_write_l2lpm}, + {scorpion_read_vlpm, scorpion_write_vlpm}, +}; + +u32 venum_orig_val; +u32 fp_orig_val; + +static void scorpion_pre_vlpm(void) +{ + u32 venum_new_val; + u32 fp_new_val; + + /* CPACR Enable CP10 access*/ + asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val)); + venum_new_val = venum_orig_val | 0x00300000; + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val)); + /* Enable FPEXC */ + asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val)); + fp_new_val = fp_orig_val | 0x40000000; + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val)); +} + +static void scorpion_post_vlpm(void) +{ + /* Restore FPEXC*/ + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val)); + /* Restore CPACR*/ + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val)); +} + +#define COLMN0MASK 0x000000ff +#define COLMN1MASK 0x0000ff00 +#define COLMN2MASK 0x00ff0000 +static u32 scorpion_get_columnmask(u32 setval) +{ + if (setval & COLMN0MASK) + return 0xffffff00; + else if (setval & COLMN1MASK) + return 0xffff00ff; + else if (setval & COLMN2MASK) + return 0xff00ffff; + else + return 0x80ffffff; +} + +static void scorpion_evt_setup(u32 gr, u32 setval) +{ + u32 val; + if (gr == 4) + scorpion_pre_vlpm(); + val = scorpion_get_columnmask(setval) & scor_func[gr].read(); + val = val | setval; + scor_func[gr].write(val); + if (gr == 4) + scorpion_post_vlpm(); +} + +static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo) +{ + u32 idx; + if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT)) + return 0; + idx = evt_type - 0x4c; + if (sc_evt[idx].evt_type == evt_type) { + evtinfo->val = sc_evt[idx].val; + evtinfo->grp = sc_evt[idx].grp; + evtinfo->evt_type_act = sc_evt[idx].evt_type_act; + return 1; + } + return 0; +} + +static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (scorpion_pmnc_select_counter(cnt) == cnt) { + if (val < 0x40) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } else { + u32 zero = 0; + struct scorp_evt evtinfo; + // extract evtinfo.grp and evtinfo.tevt_type_act from val + if (get_scorpion_evtinfo(val, &evtinfo) == 0) return; + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act)); + asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero)); + scorpion_evt_setup(evtinfo.grp, val); + } + } +} + +static void scorpion_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + + if (cnt == CCNT) { + scorpion_pmnc_disable_counter(cnt); + + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + + } else if (cnt >= CNTMAX) { + pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt); + } else { + scorpion_pmnc_disable_counter(cnt); + + if (scorpion_pmnc_select_counter(cnt) == cnt) + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + } +} + +static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_scorpion_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (scorpion_pmnc_read() & PMNC_E) { + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + } + + /* Initialize & Reset PMNC: C bit and P bit */ + scorpion_pmnc_write(PMNC_P | PMNC_C); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + if (!pmnc_enabled[cnt]) + continue; + + // disable counter + scorpion_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count + if (cnt != CCNT) + scorpion_pmnc_write_evtsel(cnt, event); + + // reset counter + scorpion_pmnc_reset_counter(cnt); + + // Enable counter, do not enable interrupt for this counter + scorpion_pmnc_enable_counter(cnt); + } + + // enable + scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E); + + // read the counters and toss the invalid data, return zero instead + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + value = scorpion_cntn_read(); + } else { + value = 0; + } + scorpion_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_scorpion_offline(int** buffer) +{ + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + return 0; +} + +static void gator_events_scorpion_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_scorpion_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(scorpion_pmnc_read() & PMNC_E)) { + return 0; + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + value = scorpion_cntn_read(); + } else { + value = 0; + } + scorpion_pmnc_reset_counter(cnt); + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_scorpion_interface = { + .create_files = gator_events_scorpion_create_files, + .stop = gator_events_scorpion_stop, + .online = gator_events_scorpion_online, + .offline = gator_events_scorpion_offline, + .read = gator_events_scorpion_read, +}; + +int gator_events_scorpion_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case SCORPION: + pmnc_name = "Scorpion"; + pmnc_counters = 4; + break; + case SCORPIONMP: + pmnc_name = "ScorpionMP"; + pmnc_counters = 4; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_scorpion_interface); +} + +gator_events_init(gator_events_scorpion_init); + +#else +int gator_events_scorpion_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c new file mode 100644 index 00000000000..39adfbecd06 --- /dev/null +++ b/drivers/gator/gator_fs.c @@ -0,0 +1,292 @@ +/** + * @file gatorfs.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * A simple filesystem for configuration and + * access of oprofile. + */ + +#include +#include +#include +#include +#include + +#define gatorfs_MAGIC 0x24051020 +#define TMPBUFSIZE 50 +DEFINE_SPINLOCK(gatorfs_lock); + +static struct inode *gatorfs_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + + if (inode) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + inode->i_ino = get_next_ino(); +#endif + inode->i_mode = mode; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +static const struct super_operations s_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset) +{ + return simple_read_from_buffer(buf, count, offset, str, strlen(str)); +} + +ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); +} + +int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) +{ + char tmpbuf[TMPBUFSIZE]; + unsigned long flags; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gatorfs_lock, flags); + *val = simple_strtoul(tmpbuf, NULL, 0); + spin_unlock_irqrestore(&gatorfs_lock, flags); + return 0; +} + +static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + unsigned long *val = file->private_data; + return gatorfs_ulong_to_user(*val, buf, count, offset); +} + +static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + +static int default_open(struct inode *inode, struct file *filp) +{ + if (inode->i_private) + filp->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ulong_fops = { + .read = ulong_read_file, + .write = ulong_write_file, + .open = default_open, +}; + +static const struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + +static struct dentry *__gatorfs_create_file(struct super_block *sb, + struct dentry *root, char const *name, const struct file_operations *fops, + int perm) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFREG | perm); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_fop = fops; + d_add(dentry, inode); + return dentry; +} + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + atomic_t *val = file->private_data; + return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset); +} + +static const struct file_operations atomic_ro_fops = { + .read = atomic_read_file, + .open = default_open, +}; + +int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root, + char const *name, atomic_t *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &atomic_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_file(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops) +{ + if (!__gatorfs_create_file(sb, root, name, fops, 0644)) + return -EFAULT; + return 0; +} + +int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops, int perm) +{ + if (!__gatorfs_create_file(sb, root, name, fops, perm)) + return -EFAULT; + return 0; +} + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); + return dentry; +} + +static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = gatorfs_MAGIC; + sb->s_op = &s_ops; + sb->s_time_gran = 1; + + root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + root_dentry = d_alloc_root(root_inode); +#else + root_dentry = d_make_root(root_inode); +#endif + + if (!root_dentry) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + iput(root_inode); +#endif + return -ENOMEM; + } + + sb->s_root = root_dentry; + + gator_op_create_files(sb, root_dentry); + + // FIXME: verify kill_litter_super removes our dentries + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) +static int gatorfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt); +} +#else +static struct dentry *gatorfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_nodev(fs_type, flags, data, gatorfs_fill_super); +} +#endif + +static struct file_system_type gatorfs_type = { + .owner = THIS_MODULE, + .name = "gatorfs", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + .get_sb = gatorfs_get_sb, +#else + .mount = gatorfs_mount, +#endif + + .kill_sb = kill_litter_super, +}; + +int __init gatorfs_register(void) +{ + return register_filesystem(&gatorfs_type); +} + +void gatorfs_unregister(void) +{ + unregister_filesystem(&gatorfs_type); +} diff --git a/drivers/gator/gator_hrtimer_gator.c b/drivers/gator/gator_hrtimer_gator.c new file mode 100755 index 00000000000..846fba44cff --- /dev/null +++ b/drivers/gator/gator_hrtimer_gator.c @@ -0,0 +1,97 @@ +/** + * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +// gator_hrtimer_perf.c is used if perf is supported +// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers +#if 1 + +void (*callback)(void); +DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +DEFINE_PER_CPU(int, hrtimer_is_active); +static ktime_t profiling_interval; +static void gator_hrtimer_online(int cpu); +static void gator_hrtimer_offline(int cpu); + +static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) +{ + hrtimer_forward_now(hrtimer, profiling_interval); + (*callback)(); + return HRTIMER_RESTART; +} + +static void gator_hrtimer_switch_cpus_online(void *unused) +{ + gator_hrtimer_online(smp_processor_id()); +} + +static void gator_hrtimer_online(int cpu) +{ + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + if (cpu != smp_processor_id()) { + smp_call_function_single(cpu, gator_hrtimer_switch_cpus_online, NULL, 1); + return; + } + + if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0) + return; + + per_cpu(hrtimer_is_active, cpu) = 1; + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = gator_hrtimer_notify; + hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED); +} + +static void gator_hrtimer_switch_cpus_offline(void *unused) +{ + gator_hrtimer_offline(smp_processor_id()); +} + +static void gator_hrtimer_offline(int cpu) +{ + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + if (cpu != smp_processor_id()) { + smp_call_function_single(cpu, gator_hrtimer_switch_cpus_offline, NULL, 1); + return; + } + + if (!per_cpu(hrtimer_is_active, cpu)) + return; + + per_cpu(hrtimer_is_active, cpu) = 0; + hrtimer_cancel(hrtimer); +} + +static int gator_hrtimer_init(int interval, void (*func)(void)) +{ + int cpu; + + (callback) = (func); + + for_each_present_cpu(cpu) { + per_cpu(hrtimer_is_active, cpu) = 0; + } + + // calculate profiling interval + if (interval > 0) { + profiling_interval = ns_to_ktime(1000000000UL / interval); + } else { + profiling_interval.tv64 = 0; + } + + return 0; +} + +static void gator_hrtimer_shutdown(void) +{ + /* empty */ +} + +#endif diff --git a/drivers/gator/gator_hrtimer_perf.c b/drivers/gator/gator_hrtimer_perf.c new file mode 100755 index 00000000000..7c0333f6445 --- /dev/null +++ b/drivers/gator/gator_hrtimer_perf.c @@ -0,0 +1,113 @@ +/** + * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +// gator_hrtimer_gator.c is used if perf is not supported +// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers +#if 0 + +// Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36 +// not relevant as this code is not active until 3.0.0, but wanted to document the issue + +void (*callback)(void); +static int profiling_interval; +static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer); +static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr); + +static void gator_hrtimer_shutdown(void); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ + (*callback)(); +} + +static int gator_online_single_hrtimer(int cpu) +{ + if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler); +#else + per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0); +#endif + if (IS_ERR(per_cpu(perf_hrtimer, cpu))) { + per_cpu(perf_hrtimer, cpu) = NULL; + return -1; + } + + if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) { + perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); + per_cpu(perf_hrtimer, cpu) = NULL; + return -1; + } + + return 0; +} + +static void gator_hrtimer_online(int cpu) +{ + if (gator_online_single_hrtimer(cpu) < 0) { + pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu); + } +} + +static void gator_hrtimer_offline(int cpu) +{ + if (per_cpu(perf_hrtimer, cpu)) { + perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); + per_cpu(perf_hrtimer, cpu) = NULL; + } +} + +static int gator_hrtimer_init(int interval, void (*func)(void)) +{ + u32 size = sizeof(struct perf_event_attr); + int cpu; + + callback = func; + + // calculate profiling interval + profiling_interval = 1000000000 / interval; + + for_each_present_cpu(cpu) { + per_cpu(perf_hrtimer, cpu) = 0; + per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL); + if (per_cpu(perf_hrtimer_attr, cpu) == 0) { + gator_hrtimer_shutdown(); + return -1; + } + + memset(per_cpu(perf_hrtimer_attr, cpu), 0, size); + per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE; + per_cpu(perf_hrtimer_attr, cpu)->size = size; + per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK; + per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval; + per_cpu(perf_hrtimer_attr, cpu)->pinned = 1; + } + + return 0; +} + +static void gator_hrtimer_shutdown(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(perf_hrtimer_attr, cpu)) { + kfree(per_cpu(perf_hrtimer_attr, cpu)); + per_cpu(perf_hrtimer_attr, cpu) = NULL; + } + } +} + +#endif diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c new file mode 100644 index 00000000000..7d4881278dd --- /dev/null +++ b/drivers/gator/gator_main.c @@ -0,0 +1,1086 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static unsigned long gator_protocol_version = 9; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gator.h" +#include "gator_events.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#error kernels prior to 2.6.32 are not supported +#endif + +#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING) +#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined +#endif + +#ifndef CONFIG_PROFILING +#error gator requires the kernel to have CONFIG_PROFILING defined +#endif + +#ifndef CONFIG_HIGH_RES_TIMERS +#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling +#endif + +#if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) +#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems +#endif + +#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) +#ifndef CONFIG_PERF_EVENTS +#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#elif !defined CONFIG_HW_PERF_EVENTS +#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#endif +#endif + +/****************************************************************************** + * DEFINES + ******************************************************************************/ +#define BACKTRACE_BUFFER_SIZE (128*1024) +#define COUNTER_BUFFER_SIZE (128*1024) +#define ANNOTATE_BUFFER_SIZE (64*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded +#define SCHED_TRACE_BUFFER_SIZE (128*1024) +#define GPU_TRACE_BUFFER_SIZE (64*1024) +#define COUNTER2_BUFFER_SIZE (64*1024) // counters2 counters have the core as part of the data and the core value in the frame header may be discarded +#define WFI_BUFFER_SIZE (32*1024) // wfi counters have the core as part of the data and the core value in the frame header may be discarded + +#define NO_COOKIE 0UL +#define INVALID_COOKIE ~0UL + +#define FRAME_BACKTRACE 1 +#define FRAME_COUNTER 2 +#define FRAME_ANNOTATE 3 +#define FRAME_SCHED_TRACE 4 +#define FRAME_GPU_TRACE 5 +#define FRAME_COUNTER2 6 +#define FRAME_WFI 7 + +#define MESSAGE_COOKIE 1 +#define MESSAGE_START_BACKTRACE 5 +#define MESSAGE_END_BACKTRACE 7 +#define MESSAGE_SUMMARY 9 +#define MESSAGE_PID_NAME 11 + +#define MAXSIZE_PACK32 5 +#define MAXSIZE_PACK64 9 + +#if defined(__arm__) +#define PC_REG regs->ARM_pc +#else +#define PC_REG regs->ip +#endif + +enum {BACKTRACE_BUF, COUNTER_BUF, SCHED_TRACE_BUF, GPU_TRACE_BUF, ANNOTATE_BUF, COUNTER2_BUF, WFI_BUF, NUM_GATOR_BUFS}; + +/****************************************************************************** + * Globals + ******************************************************************************/ +static unsigned long gator_cpu_cores; +static unsigned long userspace_buffer_size; +static unsigned long gator_backtrace_depth; + +static unsigned long gator_started; +static unsigned long gator_buffer_opened; +static unsigned long gator_timer_count; +static unsigned long gator_response_type; +static DEFINE_MUTEX(start_mutex); +static DEFINE_MUTEX(gator_buffer_mutex); + +bool event_based_sampling; + +static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); +static LIST_HEAD(gator_events); + +/****************************************************************************** + * Prototypes + ******************************************************************************/ +static void buffer_check(int cpu, int buftype); +static int buffer_bytes_available(int cpu, int buftype); +static bool buffer_check_space(int cpu, int buftype, int bytes); +static int contiguous_space_available(int cpu, int bufytpe); +static void gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x); +static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x); +static void gator_buffer_write_bytes(int cpu, int buftype, char *x, int len); +static void gator_buffer_write_string(int cpu, int buftype, char *x); +static void gator_add_trace(int cpu, int buftype, unsigned int address); +static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs); +static uint64_t gator_get_time(void); + +static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; +static uint32_t gator_buffer_mask[NUM_GATOR_BUFS]; +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); +static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); + +/****************************************************************************** + * Application Includes + ******************************************************************************/ +#include "gator_marshaling.c" +#include "gator_hrtimer_perf.c" +#include "gator_hrtimer_gator.c" +#include "gator_cookies.c" +#include "gator_trace_sched.c" +#include "gator_trace_power.c" +#include "gator_trace_gpu.c" +#include "gator_backtrace.c" +#include "gator_annotate.c" +#include "gator_fs.c" +#include "gator_ebs.c" +#include "gator_pack.c" + +/****************************************************************************** + * Misc + ******************************************************************************/ +#if defined(__arm__) +u32 gator_cpuid(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); + return (val >> 4) & 0xfff; +} +#endif + +/****************************************************************************** + * Commit interface + ******************************************************************************/ +static bool buffer_commit_ready(int* cpu, int* buftype) +{ + int cpu_x, x; + for_each_present_cpu(cpu_x) { + for (x = 0; x < NUM_GATOR_BUFS; x++) + if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) { + *cpu = cpu_x; + *buftype = x; + return true; + } + } + return false; +} + +/****************************************************************************** + * Buffer management + ******************************************************************************/ +static int buffer_bytes_available(int cpu, int buftype) +{ + int remaining, filled; + + filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + + remaining = gator_buffer_size[buftype] - filled; + + if (per_cpu(buffer_space_available, cpu)[buftype]) { + // Give some extra room; also allows space to insert the overflow error packet + remaining -= 200; + } else { + // Hysteresis, prevents multiple overflow messages + remaining -= 2000; + } + + return remaining; +} + +static int contiguous_space_available(int cpu, int buftype) +{ + int remaining = buffer_bytes_available(cpu, buftype); + int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype]; + if (remaining < contiguous) + return remaining; + else + return contiguous; +} + +static bool buffer_check_space(int cpu, int buftype, int bytes) +{ + int remaining = buffer_bytes_available(cpu, buftype); + + if (remaining < bytes) { + per_cpu(buffer_space_available, cpu)[buftype] = false; + } else { + per_cpu(buffer_space_available, cpu)[buftype] = true; + } + + return per_cpu(buffer_space_available, cpu)[buftype]; +} + +static void gator_buffer_write_bytes(int cpu, int buftype, char *x, int len) +{ + int i; + u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; + u32 mask = gator_buffer_mask[buftype]; + char* buffer = per_cpu(gator_buffer, cpu)[buftype]; + + for (i = 0; i < len; i++) { + buffer[write] = x[i]; + write = (write + 1) & mask; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = write; +} + +static void gator_buffer_write_string(int cpu, int buftype, char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, buftype, len); + gator_buffer_write_bytes(cpu, buftype, x, len); +} + +static void gator_buffer_header(int cpu, int buftype) +{ + int frame; + + if (buftype == BACKTRACE_BUF) + frame = FRAME_BACKTRACE; + else if (buftype == COUNTER_BUF) + frame = FRAME_COUNTER; + else if (buftype == ANNOTATE_BUF) + frame = FRAME_ANNOTATE; + else if (buftype == SCHED_TRACE_BUF) + frame = FRAME_SCHED_TRACE; + else if (buftype == GPU_TRACE_BUF) + frame = FRAME_GPU_TRACE; + else if (buftype == COUNTER2_BUF) + frame = FRAME_COUNTER2; + else if (buftype == WFI_BUF) + frame = FRAME_WFI; + else + frame = -1; + + if (per_cpu(gator_buffer, cpu)[buftype]) { + marshal_frame(cpu, buftype, frame); + } +} + +static void gator_commit_buffer(int cpu, int buftype) +{ + if (!per_cpu(gator_buffer, cpu)[buftype]) + return; + + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; + gator_buffer_header(cpu, buftype); + wake_up(&gator_buffer_wait); +} + +static void buffer_check(int cpu, int buftype) +{ + int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) { + gator_commit_buffer(cpu, buftype); + } +} + +static void gator_add_trace(int cpu, int buftype, unsigned int address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, buftype, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) { + offset = address; + } + + marshal_backtrace(offset & ~1, cookie); +} + +static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) +{ + int inKernel = regs ? !user_mode(regs) : 1; + unsigned long exec_cookie = inKernel ? NO_COOKIE : get_exec_cookie(cpu, buftype, current); + + if (!regs) + return; + + if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel)) + return; + + if (inKernel) { + kernel_backtrace(cpu, buftype, regs); + } else { + // Cookie+PC + gator_add_trace(cpu, buftype, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth); + } + + marshal_backtrace_footer(); +} + +/****************************************************************************** + * hrtimer interrupt processing + ******************************************************************************/ +static void gator_timer_interrupt(void) +{ + struct pt_regs * const regs = get_irq_regs(); + int cpu = smp_processor_id(); + + // Output backtrace + gator_add_sample(cpu, BACKTRACE_BUF, regs); + + // Collect counters + collect_counters(); +} + +static int gator_running; + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_offline(void* unused) +{ + struct gator_interface *gi; + int i, len, cpu = smp_processor_id(); + int* buffer; + + gator_trace_sched_offline(); + gator_trace_power_offline(); + + gator_hrtimer_offline(cpu); + + // Offline any events and output counters + if (marshal_event_header()) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline) { + len = gi->offline(&buffer); + marshal_event(len, buffer); + } + } + } + + // Flush all buffers on this core + for (i = 0; i < NUM_GATOR_BUFS; i++) + gator_commit_buffer(cpu, i); +} + +// This function runs in process context and may be running on a core other than core 'cpu' +static void gator_timer_offline_dispatch(int cpu) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->offline_dispatch) + gi->offline_dispatch(cpu); + + gator_event_sampling_offline_dispatch(cpu); +} + +static void gator_timer_stop(void) +{ + int cpu; + + if (gator_running) { + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(cpu); + } + + gator_running = 0; + gator_hrtimer_shutdown(); + } +} + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_online(void* unused) +{ + struct gator_interface *gi; + int len, cpu = smp_processor_id(); + int* buffer; + + gator_trace_power_online(); + + // online any events and output counters + if (marshal_event_header()) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->online) { + len = gi->online(&buffer); + marshal_event(len, buffer); + } + } + } + + gator_hrtimer_online(cpu); +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_online_dispatch(int cpu) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->online_dispatch) + gi->online_dispatch(cpu); + + gator_event_sampling_online_dispatch(cpu); +} + +int gator_timer_start(unsigned long sample_rate) +{ + int cpu; + + if (gator_running) { + pr_notice("gator: already running\n"); + return 0; + } + + gator_running = 1; + + // event based sampling trumps hr timer based sampling + if (event_based_sampling) + sample_rate = 0; + + if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1) + return -1; + + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(cpu); + } + on_each_cpu(gator_timer_online, NULL, 1); + + return 0; +} + +static uint64_t gator_get_time(void) +{ + struct timespec ts; + uint64_t timestamp; + + getnstimeofday(&ts); + timestamp = timespec_to_ns(&ts); + + return timestamp; +} + +/****************************************************************************** + * cpu hotplug and pm notifiers + ******************************************************************************/ +static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +{ + long cpu = (long)hcpu; + + switch (action) { + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + smp_call_function_single(cpu, gator_timer_offline, NULL, 1); + gator_timer_offline_dispatch(cpu); + break; + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + gator_timer_online_dispatch(cpu); + smp_call_function_single(cpu, gator_timer_online, NULL, 1); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __refdata gator_hotcpu_notifier = { + .notifier_call = gator_hotcpu_notify, +}; + +// n.b. calling "on_each_cpu" only runs on those that are online +// Registered linux events are not disabled, so their counters will continue to collect +static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) +{ + int cpu; + + switch (event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + unregister_hotcpu_notifier(&gator_hotcpu_notifier); + unregister_scheduler_tracepoints(); + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(cpu); + } + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(cpu); + } + on_each_cpu(gator_timer_online, NULL, 1); + register_scheduler_tracepoints(); + register_hotcpu_notifier(&gator_hotcpu_notifier); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block gator_pm_notifier = { + .notifier_call = gator_pm_notify, +}; + +static int gator_notifier_start(void) +{ + int retval; + retval = register_hotcpu_notifier(&gator_hotcpu_notifier); + if (retval == 0) + retval = register_pm_notifier(&gator_pm_notifier); + return retval; +} + +static void gator_notifier_stop(void) +{ + unregister_pm_notifier(&gator_pm_notifier); + unregister_hotcpu_notifier(&gator_hotcpu_notifier); +} + +/****************************************************************************** + * Main + ******************************************************************************/ +static void gator_summary(void) +{ + uint64_t timestamp, uptime = 0; + struct timespec uptime_ts; + void (*m2b)(struct timespec *ts); + + timestamp = gator_get_time(); + + do_posix_clock_monotonic_gettime(&uptime_ts); + m2b = symbol_get(monotonic_to_bootbased); + if (m2b) { + m2b(&uptime_ts); + uptime = (long long)uptime_ts.tv_sec * 1000000000 + uptime_ts.tv_nsec; + } + + marshal_summary(timestamp, uptime); +} + +int gator_events_install(struct gator_interface *interface) +{ + list_add_tail(&interface->list, &gator_events); + + return 0; +} + +int gator_events_get_key(void) +{ + // key of zero is reserved as a timestamp + static int key = 1; + + return key++; +} + +static int gator_init(void) +{ + int i; + + // events sources (gator_events.h, generated by gator_events.sh) + for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) + if (gator_events_list[i]) + gator_events_list[i](); + + gator_trace_power_init(); + + return 0; +} + +static int gator_start(void) +{ + unsigned long cpu, i; + struct gator_interface *gi; + + // Initialize the buffer with the frame type and core + for_each_present_cpu(cpu) { + for (i = 0; i < NUM_GATOR_BUFS; i++) { + gator_buffer_header(cpu, i); + } + } + + // Capture the start time + gator_summary(); + + // start all events + list_for_each_entry(gi, &gator_events, list) { + if (gi->start && gi->start() != 0) { + struct list_head *ptr = gi->list.prev; + + while (ptr != &gator_events) { + gi = list_entry(ptr, struct gator_interface, list); + + if (gi->stop) + gi->stop(); + + ptr = ptr->prev; + } + goto events_failure; + } + } + + // cookies shall be initialized before trace_sched_start() and gator_timer_start() + if (cookies_initialize()) + goto cookies_failure; + if (gator_annotate_start()) + goto annotate_failure; + if (gator_trace_sched_start()) + goto sched_failure; + if (gator_trace_power_start()) + goto power_failure; + if (gator_trace_gpu_start()) + goto gpu_failure; + if (gator_event_sampling_start()) + goto event_sampling_failure; + if (gator_timer_start(gator_timer_count)) + goto timer_failure; + if (gator_notifier_start()) + goto notifier_failure; + + return 0; + +notifier_failure: + gator_timer_stop(); +timer_failure: + gator_event_sampling_stop(); +event_sampling_failure: + gator_trace_gpu_stop(); +gpu_failure: + gator_trace_power_stop(); +power_failure: + gator_trace_sched_stop(); +sched_failure: + gator_annotate_stop(); +annotate_failure: + cookies_release(); +cookies_failure: + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); +events_failure: + + return -1; +} + +static void gator_stop(void) +{ + struct gator_interface *gi; + + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); + + gator_annotate_stop(); + gator_trace_sched_stop(); + gator_trace_power_stop(); + gator_trace_gpu_stop(); + gator_event_sampling_stop(); + + // stop all interrupt callback reads before tearing down other interfaces + gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined + gator_timer_stop(); +} + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +/* fopen("buffer") */ +static int gator_op_setup(void) +{ + int err = 0; + int cpu, i; + + mutex_lock(&start_mutex); + + gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE; + gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1; + + gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE; + gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE; + gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1; + + gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE; + gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1; + + gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE; + gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1; + + gator_buffer_size[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE; + gator_buffer_mask[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE - 1; + + gator_buffer_size[WFI_BUF] = WFI_BUFFER_SIZE; + gator_buffer_mask[WFI_BUF] = WFI_BUFFER_SIZE - 1; + + // Initialize percpu per buffer variables + for (i = 0; i < NUM_GATOR_BUFS; i++) { + // Verify buffers are a power of 2 + if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) { + err = -ENOEXEC; + goto setup_error; + } + + for_each_present_cpu(cpu) { + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + + // Annotation is a special case that only uses a single buffer + if (cpu > 0 && i == ANNOTATE_BUF) { + per_cpu(gator_buffer, cpu)[i] = NULL; + continue; + } + + per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]); + if (!per_cpu(gator_buffer, cpu)[i]) { + err = -ENOMEM; + goto setup_error; + } + } + } + +setup_error: + mutex_unlock(&start_mutex); + return err; +} + +/* Actually start profiling (echo 1>/dev/gator/enable) */ +static int gator_op_start(void) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started || gator_start()) + err = -EINVAL; + else + gator_started = 1; + + mutex_unlock(&start_mutex); + + return err; +} + +/* echo 0>/dev/gator/enable */ +static void gator_op_stop(void) +{ + mutex_lock(&start_mutex); + + if (gator_started) { + gator_stop(); + + mutex_lock(&gator_buffer_mutex); + + gator_started = 0; + cookies_release(); + wake_up(&gator_buffer_wait); + + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static void gator_shutdown(void) +{ + int cpu, i; + + mutex_lock(&start_mutex); + + for_each_present_cpu(cpu) { + mutex_lock(&gator_buffer_mutex); + for (i = 0; i < NUM_GATOR_BUFS; i++) { + vfree(per_cpu(gator_buffer, cpu)[i]); + per_cpu(gator_buffer, cpu)[i] = NULL; + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + } + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static int gator_set_backtrace(unsigned long val) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started) + err = -EBUSY; + else + gator_backtrace_depth = val; + + mutex_unlock(&start_mutex); + + return err; +} + +static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_started, buf, count, offset); +} + +static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (val) + retval = gator_op_start(); + else + gator_op_stop(); + + if (retval) + return retval; + return count; +} + +static const struct file_operations enable_fops = { + .read = enable_read, + .write = enable_write, +}; + +static int userspace_buffer_open(struct inode *inode, struct file *file) +{ + int err = -EPERM; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (test_and_set_bit_lock(0, &gator_buffer_opened)) + return -EBUSY; + + if ((err = gator_op_setup())) + goto fail; + + /* NB: the actual start happens from userspace + * echo 1 >/dev/gator/enable + */ + + return 0; + +fail: + __clear_bit_unlock(0, &gator_buffer_opened); + return err; +} + +static int userspace_buffer_release(struct inode *inode, struct file *file) +{ + gator_op_stop(); + gator_shutdown(); + __clear_bit_unlock(0, &gator_buffer_opened); + return 0; +} + +static ssize_t userspace_buffer_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + int retval = -EINVAL; + int commit = 0, length, length1, length2, read, byte, type_length; + char *buffer1; + char *buffer2 = NULL; + int cpu, buftype; + + /* do not handle partial reads */ + if (count != userspace_buffer_size || *offset) + return -EINVAL; + + // sleep until the condition is true or a signal is received + // the condition is checked each time gator_buffer_wait is woken up + buftype = cpu = -1; + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started); + + if (signal_pending(current)) + return -EINTR; + + length2 = 0; + retval = -EFAULT; + + mutex_lock(&gator_buffer_mutex); + + if (buftype == -1 || cpu == -1) { + retval = 0; + goto out; + } + + read = per_cpu(gator_buffer_read, cpu)[buftype]; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + + /* May happen if the buffer is freed during pending reads. */ + if (!per_cpu(gator_buffer, cpu)[buftype]) { + retval = -EFAULT; + goto out; + } + + /* determine the size of two halves */ + length1 = commit - read; + buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); + buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); + if (length1 < 0) { + length1 = gator_buffer_size[buftype] - read; + length2 = commit; + } + + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + type_length = gator_response_type ? 1 : 0; + length = length1 + length2 - type_length - sizeof(int); + for (byte = 0; byte < sizeof(int); byte++) { + per_cpu(gator_buffer, cpu)[buftype][(read + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; + } + + /* start, middle or end */ + if (length1 > 0) { + if (copy_to_user(&buf[0], buffer1, length1)) { + goto out; + } + } + + /* possible wrap around */ + if (length2 > 0) { + if (copy_to_user(&buf[length1], buffer2, length2)) { + goto out; + } + } + + per_cpu(gator_buffer_read, cpu)[buftype] = commit; + retval = length1 + length2; + + /* kick just in case we've lost an SMP event */ + wake_up(&gator_buffer_wait); + +out: + mutex_unlock(&gator_buffer_mutex); + return retval; +} + +const struct file_operations gator_event_buffer_fops = { + .open = userspace_buffer_open, + .release = userspace_buffer_release, + .read = userspace_buffer_read, +}; + +static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, + offset); +} + +static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = gator_set_backtrace(val); + + if (retval) + return retval; + return count; +} + +static const struct file_operations depth_fops = { + .read = depth_read, + .write = depth_write +}; + +void gator_op_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + struct gator_interface *gi; + int cpu; + + /* reinitialize default values */ + gator_cpu_cores = 0; + for_each_present_cpu(cpu) { + gator_cpu_cores++; + } + userspace_buffer_size = BACKTRACE_BUFFER_SIZE; + gator_response_type = 1; + + gatorfs_create_file(sb, root, "enable", &enable_fops); + gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); + gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops); + gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores); + gatorfs_create_ulong(sb, root, "buffer_size", &userspace_buffer_size); + gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); + gatorfs_create_ulong(sb, root, "response_type", &gator_response_type); + gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); + + // Annotate interface + gator_annotate_create_files(sb, root); + + // Linux Events + dir = gatorfs_mkdir(sb, root, "events"); + list_for_each_entry(gi, &gator_events, list) + if (gi->create_files) + gi->create_files(sb, dir); + + // Power interface + gator_trace_power_create_files(sb, dir); +} + +/****************************************************************************** + * Module + ******************************************************************************/ +static int __init gator_module_init(void) +{ + if (gatorfs_register()) { + return -1; + } + + if (gator_init()) { + gatorfs_unregister(); + return -1; + } + + return 0; +} + +static void __exit gator_module_exit(void) +{ + tracepoint_synchronize_unregister(); + gatorfs_unregister(); +} + +module_init(gator_module_init); +module_exit(gator_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Gator system profiler"); diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c new file mode 100755 index 00000000000..630d142bf70 --- /dev/null +++ b/drivers/gator/gator_marshaling.c @@ -0,0 +1,239 @@ +/** + * Copyright (C) ARM Limited 2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static void marshal_summary(long long timestamp, long long uptime) { + int cpu = 0; + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_SUMMARY); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, timestamp); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, uptime); +} + +static bool marshal_cookie_header(char* text) { + int cpu = smp_processor_id(); + return buffer_check_space(cpu, BACKTRACE_BUF, strlen(text) + 2 * MAXSIZE_PACK32); +} + +static void marshal_cookie(int cookie, char* text) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_COOKIE); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); + gator_buffer_write_string(cpu, BACKTRACE_BUF, text); +} + +static void marshal_pid_name(int pid, char* name) { + unsigned long flags, cpu; + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, BACKTRACE_BUF, TASK_COMM_LEN + 2 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_PID_NAME); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); + gator_buffer_write_string(cpu, BACKTRACE_BUF, name); + } + local_irq_restore(flags); +} + +static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel) { + int cpu = smp_processor_id(); + if (buffer_check_space(cpu, BACKTRACE_BUF, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_START_BACKTRACE); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel); + return true; + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF); + + return false; +} + +static void marshal_backtrace(int address, int cookie) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, address); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); +} + +static void marshal_backtrace_footer(void) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_END_BACKTRACE); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF); +} + +static bool marshal_event_header(void) { + unsigned long flags, cpu = smp_processor_id(); + bool retval = false; + + local_irq_save(flags); + if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, COUNTER_BUF, 0); // key of zero indicates a timestamp + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, gator_get_time()); + retval = true; + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); + + return retval; +} + +static void marshal_event(int len, int* buffer) { + unsigned long i, flags, cpu = smp_processor_id(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + for (i = 0; i < len; i += 2) { + local_irq_save(flags); + if (!buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK32 * 2)) { + local_irq_restore(flags); + break; + } + gator_buffer_write_packed_int(cpu, COUNTER_BUF, buffer[i]); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, buffer[i + 1]); + local_irq_restore(flags); + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); +} + +static void marshal_event64(int len, long long* buffer64) { + unsigned long i, flags, cpu = smp_processor_id(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + for (i = 0; i < len; i += 2) { + local_irq_save(flags); + if (!buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 * 2)) { + local_irq_restore(flags); + break; + } + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, buffer64[i]); + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, buffer64[i + 1]); + local_irq_restore(flags); + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_event_single(int core, int key, int value) { + unsigned long flags, cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, COUNTER2_BUF, MAXSIZE_PACK64 + MAXSIZE_PACK32 * 3)) { + gator_buffer_write_packed_int64(cpu, COUNTER2_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, key); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, value); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER2_BUF); +} +#endif + +static void marshal_sched_gpu(int type, int unit, int core, int tgid, int pid) { + unsigned long cpu = smp_processor_id(), flags; + + if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) + return; + + local_irq_save(flags); + if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, type); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, GPU_TRACE_BUF); +} + +static void marshal_sched_trace(int type, int pid, int tgid, int cookie, int state) { + unsigned long cpu = smp_processor_id(), flags; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, type); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_wfi(int core, int state) { + unsigned long flags, cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, WFI_BUF, MAXSIZE_PACK64 + MAXSIZE_PACK32 * 2)) { + gator_buffer_write_packed_int64(cpu, WFI_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, WFI_BUF, core); + gator_buffer_write_packed_int(cpu, WFI_BUF, state); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, WFI_BUF); +} +#endif + +static void marshal_frame(int cpu, int buftype, int frame) { + // add response type + if (gator_response_type > 0) { + gator_buffer_write_packed_int(cpu, buftype, gator_response_type); + } + + // leave space for 4-byte unpacked length + per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + 4) & gator_buffer_mask[buftype]; + + // add frame type and core number + gator_buffer_write_packed_int(cpu, buftype, frame); + gator_buffer_write_packed_int(cpu, buftype, cpu); +} diff --git a/drivers/gator/gator_pack.c b/drivers/gator/gator_pack.c new file mode 100644 index 00000000000..925469a5b89 --- /dev/null +++ b/drivers/gator/gator_pack.c @@ -0,0 +1,164 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static void gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int write0 = (write + 0) & mask; + int write1 = (write + 1) & mask; + + if ((x & 0xffffff80) == 0) { + buffer[write0] = x & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write1; + } else if ((x & 0xffffc000) == 0) { + int write2 = (write + 2) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write2; + } else if ((x & 0xffe00000) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write3; + } else if ((x & 0xf0000000) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write4; + } else { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) & 0x0f; + per_cpu(gator_buffer_write, cpu)[buftype] = write5; + } +} + +static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int write0 = (write + 0) & mask; + int write1 = (write + 1) & mask; + + if ((x & 0xffffffffffffff80LL) == 0) { + buffer[write0] = x & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write1; + } else if ((x & 0xffffffffffffc000LL) == 0) { + int write2 = (write + 2) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write2; + } else if ((x & 0xffffffffffe00000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write3; + } else if ((x & 0xfffffffff0000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write4; + } else if ((x & 0xfffffff800000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write5; + } else if ((x & 0xfffffc0000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write6; + } else if ((x & 0xfffe000000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write7; + } else if ((x & 0xff00000000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + int write8 = (write + 8) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) | 0x80; + buffer[write7] = (x>>49) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write8; + } else { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + int write8 = (write + 8) & mask; + int write9 = (write + 9) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) | 0x80; + buffer[write7] = (x>>49) | 0x80; + buffer[write8] = (x>>56) & 0xff; + per_cpu(gator_buffer_write, cpu)[buftype] = write9; + } +} diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c new file mode 100644 index 00000000000..921932c1fb1 --- /dev/null +++ b/drivers/gator/gator_trace_gpu.c @@ -0,0 +1,126 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "gator.h" + +#include +#include +#include +#include + +#ifdef MALI_SUPPORT +#include "linux/mali_linux_trace.h" +#endif +#include "gator_trace_gpu.h" + +#define ACTIVITY_START 1 +#define ACTIVITY_STOP 2 + +/* Note whether tracepoints have been registered */ +static int mali_trace_registered; +static int gpu_trace_registered; + +#define GPU_START 1 +#define GPU_STOP 2 + +#define GPU_UNIT_VP 1 +#define GPU_UNIT_FP 2 +#define GPU_UNIT_CL 3 + +#ifdef MALI_SUPPORT + +enum components { + COMPONENT_VP0 = 1, + COMPONENT_FP0 = 5, + COMPONENT_FP1, + COMPONENT_FP2, + COMPONENT_FP3, + COMPONENT_FP4, + COMPONENT_FP5, + COMPONENT_FP6, + COMPONENT_FP7, +}; + +GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) +{ + unsigned int component, state; + int tgid = 0, pid = 0; + + // do as much work as possible before disabling interrupts + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field + + if ((component == COMPONENT_VP0) || (component >= COMPONENT_FP0 && component <= COMPONENT_FP7)) { + if (state == ACTIVITY_START || state == ACTIVITY_STOP) { + unsigned int type = (state == ACTIVITY_START) ? GPU_START : GPU_STOP; + unsigned int unit = (component < COMPONENT_FP0) ? GPU_UNIT_VP : GPU_UNIT_FP; + unsigned int core = (component < COMPONENT_FP0) ? component - COMPONENT_VP0 : component - COMPONENT_FP0; + if (state == ACTIVITY_START) { + tgid = d0; + pid = d1; + } + + marshal_sched_gpu(type, unit, core, tgid, pid); + } + } +} +#endif + +GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) +{ + marshal_sched_gpu(GPU_START, gpu_unit, gpu_core, (int)p->tgid, (int)p->pid); +} + +GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) +{ + marshal_sched_gpu(GPU_STOP, gpu_unit, gpu_core, 0, 0); +} + +int gator_trace_gpu_start(void) +{ + /* + * Returns nonzero for installation failed + * Absence of gpu trace points is not an error + */ + + gpu_trace_registered = mali_trace_registered = 0; + +#ifdef MALI_SUPPORT + if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { + mali_trace_registered = 1; + } +#endif + + if (!mali_trace_registered) { + if (GATOR_REGISTER_TRACE(gpu_activity_start)) { + return 0; + } + if (GATOR_REGISTER_TRACE(gpu_activity_stop)) { + GATOR_UNREGISTER_TRACE(gpu_activity_start); + return 0; + } + gpu_trace_registered = 1; + } + + return 0; +} + +void gator_trace_gpu_stop(void) +{ +#ifdef MALI_SUPPORT + if (mali_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } +#endif + if (gpu_trace_registered) { + GATOR_UNREGISTER_TRACE(gpu_activity_stop); + GATOR_UNREGISTER_TRACE(gpu_activity_start); + } + + gpu_trace_registered = mali_trace_registered = 0; +} diff --git a/drivers/gator/gator_trace_gpu.h b/drivers/gator/gator_trace_gpu.h new file mode 100644 index 00000000000..894289b4316 --- /dev/null +++ b/drivers/gator/gator_trace_gpu.h @@ -0,0 +1,79 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#undef TRACE_GPU +#define TRACE_GPU gpu + +#if !defined(_TRACE_GPU_H) +#define _TRACE_GPU_H + +#include + +/* + * UNIT - the GPU processor type + * 1 = Vertex Processor + * 2 = Fragment Processor + * + * CORE - the GPU processor core number + * this is not the CPU core number + */ + +/* + * Tracepoint for calling GPU unit start activity on core + */ +TRACE_EVENT(gpu_activity_start, + + TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p), + + TP_ARGS(gpu_unit, gpu_core, p), + + TP_STRUCT__entry( + __field( int, gpu_unit ) + __field( int, gpu_core ) + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + memcpy(__entry->comm, p->comm, TASK_COMM_LEN); + __entry->pid = p->pid; + ), + + TP_printk("unit=%d core=%d comm=%s pid=%d", + __entry->gpu_unit, __entry->gpu_core, __entry->comm, __entry->pid) +); + +/* + * Tracepoint for calling GPU unit stop activity on core + */ +TRACE_EVENT(gpu_activity_stop, + + TP_PROTO(int gpu_unit, int gpu_core), + + TP_ARGS(gpu_unit, gpu_core), + + TP_STRUCT__entry( + __field( int, gpu_unit ) + __field( int, gpu_core ) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + ), + + TP_printk("unit=%d core=%d", + __entry->gpu_unit, __entry->gpu_core) +); + +#endif /* _TRACE_GPU_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c new file mode 100755 index 00000000000..ca89b19908f --- /dev/null +++ b/drivers/gator/gator_trace_power.c @@ -0,0 +1,160 @@ +/** + * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include + +// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38 +// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86 +#if GATOR_CPU_FREQ_SUPPORT +enum { + POWER_CPU_FREQ, + POWER_CPU_IDLE, + POWER_TOTAL +}; + +static DEFINE_PER_CPU(ulong, idle_prev_state); +static ulong power_cpu_enabled[POWER_TOTAL]; +static ulong power_cpu_key[POWER_TOTAL]; + +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + // cpu_frequency + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]); + + // cpu_idle + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]); + + return 0; +} + +// 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change +GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) +{ + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000); +} + +#define WFI_ACTIVE_THRESHOLD 2 // may vary on platform/OS +#define WFI_EXIT 0 +#define WFI_ENTER 1 +GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) +{ + // the streamline engine treats all counter values as unsigned + if (state & 0x80000000) { + state = 0; + } + + if (state == per_cpu(idle_prev_state, cpu)) { + return; + } + + if (state < WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) >= WFI_ACTIVE_THRESHOLD) { + // transition from wfi to non-wfi + marshal_wfi(cpu, WFI_EXIT); + } else if (state >= WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) < WFI_ACTIVE_THRESHOLD) { + // transition from non-wfi to wfi + marshal_wfi(cpu, WFI_ENTER); + } + + per_cpu(idle_prev_state, cpu) = state; + + if (power_cpu_enabled[POWER_CPU_IDLE]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state); + } +} + +static void gator_trace_power_online(void) +{ + int cpu = smp_processor_id(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000); + } +} + +static void gator_trace_power_offline(void) +{ + // Set frequency to zero on an offline + int cpu = smp_processor_id(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0); + } +} + +static int gator_trace_power_start(void) +{ + int cpu; + + // register tracepoints + if (power_cpu_enabled[POWER_CPU_FREQ]) + if (GATOR_REGISTER_TRACE(cpu_frequency)) + goto fail_cpu_frequency_exit; + + // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE] + if (GATOR_REGISTER_TRACE(cpu_idle)) + goto fail_cpu_idle_exit; + pr_debug("gator: registered power event tracepoints\n"); + + for_each_present_cpu(cpu) { + per_cpu(idle_prev_state, cpu) = 0; + } + + return 0; + + // unregister tracepoints on error +fail_cpu_idle_exit: + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); +fail_cpu_frequency_exit: + pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_trace_power_stop(void) +{ + int i; + + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); + GATOR_UNREGISTER_TRACE(cpu_idle); + pr_debug("gator: unregistered power event tracepoints\n"); + + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + } +} + +void gator_trace_power_init(void) +{ + int i; + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + power_cpu_key[i] = gator_events_get_key(); + } +} +#else +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) {return 0;} +static void gator_trace_power_online(void) {} +static void gator_trace_power_offline(void) {} +static int gator_trace_power_start(void) {return 0;} +static void gator_trace_power_stop(void) {} +void gator_trace_power_init(void) {} +#endif diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c new file mode 100644 index 00000000000..08b02709485 --- /dev/null +++ b/drivers/gator/gator_trace_sched.c @@ -0,0 +1,191 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include "gator.h" + +#define SCHED_SWITCH 1 +#define SCHED_PROCESS_EXIT 2 + +#define TASK_MAP_ENTRIES 1024 /* must be power of 2 */ +#define TASK_MAX_COLLISIONS 2 + +static DEFINE_PER_CPU(uint64_t *, taskname_keys); + +enum { + STATE_WAIT_ON_OTHER = 0, + STATE_CONTENTION, + STATE_WAIT_ON_IO, + STATE_WAIT_ON_MUTEX, +}; + +void emit_pid_name(struct task_struct* task) +{ + bool found = false; + char taskcomm[TASK_COMM_LEN + 3]; + unsigned long x, cpu = smp_processor_id(); + uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]); + uint64_t value; + + value = gator_chksum_crc32(task->comm); + value = (value << 32) | (uint32_t)task->pid; + + // determine if the thread name was emitted already + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + if (keys[x] == value) { + found = true; + break; + } + } + + if (!found) { + // shift values, new value always in front + uint64_t oldv, newv = value; + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + oldv = keys[x]; + keys[x] = newv; + newv = oldv; + } + + // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions + if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) { + // append ellipses if task->comm has length of TASK_COMM_LEN - 1 + strcat(taskcomm, "..."); + } + + marshal_pid_name(task->pid, taskcomm); + } +} + + +static void collect_counters(void) +{ + int *buffer, len; + long long *buffer64; + struct gator_interface *gi; + + if (marshal_event_header()) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->read) { + len = gi->read(&buffer); + marshal_event(len, buffer); + } else if (gi->read64) { + len = gi->read64(&buffer64); + marshal_event64(len, buffer64); + } + } + } +} + +static void probe_sched_write(int type, struct task_struct* task, struct task_struct* old_task) +{ + int cookie = 0, state = 0; + int cpu = smp_processor_id(); + int pid = task->pid; + int tgid = task->tgid; + + if (type == SCHED_SWITCH) { + // do as much work as possible before disabling interrupts + cookie = get_exec_cookie(cpu, BACKTRACE_BUF, task); + emit_pid_name(task); + if (old_task->state == TASK_RUNNING) { + state = STATE_CONTENTION; + } else if (old_task->in_iowait) { + state = STATE_WAIT_ON_IO; +#ifdef CONFIG_DEBUG_MUTEXES + } else if (old_task->blocked_on) { + state = STATE_WAIT_ON_MUTEX; +#endif + } else { + state = STATE_WAIT_ON_OTHER; + } + + collect_counters(); + } + + // marshal_sched_trace() disables interrupts as the free may trigger while switch is writing to the buffer; disabling preemption is not sufficient + // is disable interrupts necessary now that exit is used instead of free? + marshal_sched_trace(type, pid, tgid, cookie, state); +} + +// special case used during a suspend of the system +static void trace_sched_insert_idle(void) +{ + marshal_sched_trace(SCHED_SWITCH, 0, 0, 0, 0); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + probe_sched_write(SCHED_SWITCH, next, prev); +} + +GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p)) +{ + probe_sched_write(SCHED_PROCESS_EXIT, p, 0); +} + +static int register_scheduler_tracepoints(void) { + // register tracepoints + if (GATOR_REGISTER_TRACE(sched_switch)) + goto fail_sched_switch; + if (GATOR_REGISTER_TRACE(sched_process_exit)) + goto fail_sched_process_exit; + pr_debug("gator: registered tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_sched_process_exit: + GATOR_UNREGISTER_TRACE(sched_switch); +fail_sched_switch: + pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +int gator_trace_sched_start(void) +{ + int cpu, size; + + for_each_present_cpu(cpu) { + size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(taskname_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(taskname_keys, cpu)) + return -1; + memset(per_cpu(taskname_keys, cpu), 0, size); + } + + return register_scheduler_tracepoints(); +} + +void gator_trace_sched_offline(void) +{ + trace_sched_insert_idle(); +} + +static void unregister_scheduler_tracepoints(void) +{ + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_process_exit); + pr_debug("gator: unregistered tracepoints\n"); +} + +void gator_trace_sched_stop(void) +{ + int cpu; + unregister_scheduler_tracepoints(); + + for_each_present_cpu(cpu) { + kfree(per_cpu(taskname_keys, cpu)); + } +} diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk new file mode 100755 index 00000000000..3ab55b91ad5 --- /dev/null +++ b/tools/gator/daemon/Android.mk @@ -0,0 +1,41 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +$(shell cd $(LOCAL_PATH);cat events_header.xml events-*\.xml events_footer.xml > events.xml;xxd -i events.xml > events_xml.h;xxd -i configuration.xml > configuration_xml.h) + +LOCAL_CFLAGS += -Wall -O3 -ftree-vectorize + +LOCAL_SRC_FILES:= \ + CapturedXML.cpp \ + Child.cpp \ + Collector.cpp \ + ConfigurationXML.cpp \ + Fifo.cpp \ + LocalCapture.cpp \ + Logging.cpp \ + main.cpp \ + OlySocket.cpp \ + OlyUtility.cpp \ + Sender.cpp \ + SessionData.cpp \ + SessionXML.cpp \ + StreamlineSetup.cpp \ + mxml/mxml-attr.c \ + mxml/mxml-entity.c \ + mxml/mxml-file.c \ + mxml/mxml-get.c \ + mxml/mxml-index.c \ + mxml/mxml-node.c \ + mxml/mxml-private.c \ + mxml/mxml-search.c \ + mxml/mxml-set.c \ + mxml/mxml-string.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_MODULE:= gatord +LOCAL_MODULE_TAGS:= optional + +LOCAL_LDLIBS := -lz -llog + +include $(BUILD_EXECUTABLE) diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp new file mode 100644 index 00000000000..f02ed2a0155 --- /dev/null +++ b/tools/gator/daemon/CapturedXML.cpp @@ -0,0 +1,123 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "SessionData.h" +#include "CapturedXML.h" +#include "Logging.h" +#include "OlyUtility.h" + +extern void handleException(); + +CapturedXML::CapturedXML() { +} + +CapturedXML::~CapturedXML() { +} + +mxml_node_t* CapturedXML::getTree() { + bool perfCounters = false; + mxml_node_t *xml; + mxml_node_t *captured; + mxml_node_t *target; + mxml_node_t *counters; + mxml_node_t *counter; + int x; + + for (x=0; xmPerfCounterEnabled[x]) { + perfCounters = true; + break; + } + } + + xml = mxmlNewXML("1.0"); + + captured = mxmlNewElement(xml, "captured"); + mxmlElementSetAttr(captured, "version", "1"); + mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); + if (gSessionData->mBytes > 0) { // Send the following only after the capture is complete + if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) + mxmlElementSetAttrf(captured, "created", "%lu", time(NULL)); // Valid until the year 2038 + } + mxmlElementSetAttrf(captured, "bytes", "%d", gSessionData->mBytes); + } + + target = mxmlNewElement(captured, "target"); + mxmlElementSetAttr(target, "name", gSessionData->mCoreName); + mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); + mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); + + if (perfCounters) { + counters = mxmlNewElement(captured, "counters"); + for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { + if (gSessionData->mPerfCounterEnabled[x]) { + counter = mxmlNewElement(counters, "counter"); + mxmlElementSetAttr(counter, "title", gSessionData->mPerfCounterTitle[x]); + mxmlElementSetAttr(counter, "name", gSessionData->mPerfCounterName[x]); + mxmlElementSetAttrf(counter, "color", "0x%08x", gSessionData->mPerfCounterColor[x]); + mxmlElementSetAttrf(counter, "key", "0x%08x", gSessionData->mPerfCounterKey[x]); + mxmlElementSetAttr(counter, "type", gSessionData->mPerfCounterType[x]); + mxmlElementSetAttrf(counter, "event", "0x%08x", gSessionData->mPerfCounterEvent[x]); + if (gSessionData->mPerfCounterPerCPU[x]) { + mxmlElementSetAttr(counter, "per_cpu", "yes"); + } + if (strlen(gSessionData->mPerfCounterOperation[x]) > 0) { + mxmlElementSetAttr(counter, "operation", gSessionData->mPerfCounterOperation[x]); + } + if (gSessionData->mPerfCounterCount[x] > 0) { + mxmlElementSetAttrf(counter, "count", "%d", gSessionData->mPerfCounterCount[x]); + } + if (gSessionData->mPerfCounterLevel[x]) { + mxmlElementSetAttr(counter, "level", "yes"); + } + if (strlen(gSessionData->mPerfCounterAlias[x]) > 0) { + mxmlElementSetAttr(counter, "alias", gSessionData->mPerfCounterAlias[x]); + } + if (strlen(gSessionData->mPerfCounterDisplay[x]) > 0) { + mxmlElementSetAttr(counter, "display", gSessionData->mPerfCounterDisplay[x]); + } + if (strlen(gSessionData->mPerfCounterUnits[x]) > 0) { + mxmlElementSetAttr(counter, "units", gSessionData->mPerfCounterUnits[x]); + } + if (gSessionData->mPerfCounterAverageSelection[x]) { + mxmlElementSetAttr(counter, "average_selection", "yes"); + } + mxmlElementSetAttr(counter, "description", gSessionData->mPerfCounterDescription[x]); + } + } + } + + return xml; +} + +char* CapturedXML::getXML() { + char* xml_string; + mxml_node_t *xml = getTree(); + xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + return xml_string; +} + +void CapturedXML::write(char* path) { + char *file = (char*)malloc(PATH_MAX); + + // Set full path + snprintf(file, PATH_MAX, "%s/captured.xml", path); + + char* xml = getXML(); + if (util->writeToDisk(file, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(xml); + free(file); +} diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h new file mode 100644 index 00000000000..984d1f2d13e --- /dev/null +++ b/tools/gator/daemon/CapturedXML.h @@ -0,0 +1,25 @@ +/** + + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CAPTURED_XML_H__ +#define __CAPTURED_XML_H__ + +#include "mxml/mxml.h" + +class CapturedXML { +public: + CapturedXML(); + ~CapturedXML(); + char* getXML(); // the string should be freed by the caller + void write(char* path); +private: + mxml_node_t* getTree(); +}; + +#endif //__CAPTURED_XML_H__ diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp new file mode 100644 index 00000000000..bcc868a553c --- /dev/null +++ b/tools/gator/daemon/Child.cpp @@ -0,0 +1,312 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "Logging.h" +#include "CapturedXML.h" +#include "SessionData.h" +#include "Child.h" +#include "LocalCapture.h" +#include "Collector.h" +#include "Sender.h" +#include "OlyUtility.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" + +static sem_t haltPipeline, senderThreadStarted, startProfile; // Shared by Child and spawned threads +static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads +static Sender* sender = NULL; // Shared by Child.cpp and spawned threads +Collector* collector = NULL; +Child* child = NULL; // shared by Child.cpp and main.cpp + +extern void cleanUp(); +void handleException() { + if (child && child->numExceptions++ > 0) { + // it is possible one of the below functions itself can cause an exception, thus allow only one exception + logg->logMessage("Received multiple exceptions, terminating the child"); + exit(1); + } + fprintf(stderr, "%s", logg->getLastError()); + + if (child && child->socket) { + if (sender) { + // send the error, regardless of the command sent by Streamline + sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR); + + // cannot close the socket before Streamline issues the command, so wait for the command before exiting + if (gSessionData->mWaitingOnCommand) { + char discard; + child->socket->receiveNBytes(&discard, 1); + } + + // this indirectly calls close socket which will ensure the data has been sent + delete sender; + } + } + + if (gSessionData->mLocalCapture) + cleanUp(); + + exit(1); +} + +// CTRL C Signal Handler for child process +void child_handler(int signum) { + static bool beenHere = false; + if (beenHere == true) { + logg->logMessage("Gator is being forced to shut down."); + exit(1); + } + beenHere = true; + logg->logMessage("Gator is shutting down."); + if (signum == SIGALRM || !collector) { + exit(1); + } else { + child->endSession(); + alarm(5); // Safety net in case endSession does not complete within 5 seconds + } +} + +void* durationThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned int)&"gatord-duration", 0, 0, 0); + sem_wait(&startProfile); + if (gSessionData->mSessionIsActive) { + // Time out after duration seconds + // Add a second for host-side filtering + sleep(gSessionData->mDuration + 1); + if (gSessionData->mSessionIsActive) { + logg->logMessage("Duration expired."); + child->endSession(); + } + } + logg->logMessage("Exit duration thread"); + return 0; +} + +void* stopThread(void* pVoid) { + int length; + char type; + OlySocket* socket = child->socket; + + prctl(PR_SET_NAME, (unsigned int)&"gatord-stopper", 0, 0, 0); + while (gSessionData->mSessionIsActive) { + // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected + if (socket->receiveNBytes(&type, sizeof(type)) > 0) { + if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) { + logg->logMessage("INVESTIGATE: Received unknown command type %d", type); + } else { + // verify a length of zero + if (socket->receiveNBytes((char*)&length, sizeof(length)) < 0) { + break; + } + + if (length == 0) { + if (type == COMMAND_APC_STOP) { + logg->logMessage("Stop command received."); + child->endSession(); + } else { + // Ping is used to make sure gator is alive and requires an ACK as the response + logg->logMessage("Ping command received."); + sender->writeData(NULL, 0, RESPONSE_ACK); + } + } else { + logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length); + } + } + } + } + + logg->logMessage("Exit stop thread"); + return 0; +} + +void* senderThread(void* pVoid) { + int length; + char* data; + char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; + + sem_post(&senderThreadStarted); + prctl(PR_SET_NAME, (unsigned int)&"gatord-sender", 0, 0, 0); + sem_wait(&haltPipeline); + + do { + data = collectorFifo->read(&length); + sender->writeData(data, length, RESPONSE_APC_DATA); + } while (length > 0); + + // write end-of-capture sequence + if (!gSessionData->mLocalCapture) { + sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA); + } + + logg->logMessage("Exit sender thread"); + return 0; +} + +Child::Child() { + initialization(); + gSessionData->mLocalCapture = true; +} + +Child::Child(OlySocket* sock, int conn) { + initialization(); + socket = sock; + mNumConnections = conn; +} + +Child::~Child() { +} + +void Child::initialization() { + // Set up different handlers for signals + gSessionData->mSessionIsActive = true; + signal(SIGINT, child_handler); + signal(SIGTERM, child_handler); + signal(SIGABRT, child_handler); + signal(SIGALRM, child_handler); + socket = NULL; + numExceptions = 0; + mNumConnections = 0; + + // Initialize semaphores + sem_init(&senderThreadStarted, 0, 0); + sem_init(&startProfile, 0, 0); +} + +void Child::endSession() { + gSessionData->mSessionIsActive = false; + collector->stop(); + sem_post(&haltPipeline); +} + +void Child::run() { + char* collectBuffer; + int bytesCollected = 0; + LocalCapture* localCapture = NULL; + pthread_t durationThreadID, stopThreadID, senderThreadID; + + prctl(PR_SET_NAME, (unsigned int)&"gatord-child", 0, 0, 0); + + // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually + mxmlSetWrapMargin(0); + + // Instantiate the Sender - must be done first, after which error messages can be sent + sender = new Sender(socket); + + if (mNumConnections > 1) { + logg->logError(__FILE__, __LINE__, "Session already in progress"); + handleException(); + } + + // Populate gSessionData with the configuration + new ConfigurationXML(); + + // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated + collector = new Collector(); + + // Start up and parse session xml + if (socket) { + // Respond to Streamline requests + StreamlineSetup ss(socket); + } else { + char* xmlString; + xmlString = util->readFromDisk(gSessionData->mSessionXMLPath); + if (xmlString == 0) { + logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath); + handleException(); + } + gSessionData->parseSessionXML(xmlString); + localCapture = new LocalCapture(); + localCapture->createAPCDirectory(gSessionData->mTargetPath, gSessionData->mTitle); + localCapture->copyImages(gSessionData->mImages); + localCapture->write(xmlString); + sender->createDataFile(gSessionData->mAPCDir); + free(xmlString); + } + + // Write configuration into the driver + collector->setupPerfCounters(); + + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); + collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize* 1024 * 1024); + + // Get the initial pointer to the collect buffer + collectBuffer = collectorFifo->start(); + + // Sender thread shall be halted until it is signaled for one shot mode + sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); + + // Create the duration, stop, and sender threads + bool thread_creation_success = true; + if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) { + thread_creation_success = false; + } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) { + thread_creation_success = false; + } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){ + thread_creation_success = false; + } + + if (!thread_creation_success) { + logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); + handleException(); + } + + // Wait until thread has started + sem_wait(&senderThreadStarted); + + // Start profiling + logg->logMessage("********** Profiling started **********"); + collector->start(); + sem_post(&startProfile); + + // Collect Data + do { + // This command will stall until data is received from the driver + bytesCollected = collector->collect(collectBuffer); + + // In one shot mode, stop collection once all the buffers are filled + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { + logg->logMessage("One shot"); + endSession(); + } + } + collectBuffer = collectorFifo->write(bytesCollected); + } while (bytesCollected > 0); + logg->logMessage("Exit collect data loop"); + + // Wait for the other threads to exit + pthread_join(senderThreadID, NULL); + + // Shutting down the connection should break the stop thread which is stalling on the socket recv() function + if (socket) { + logg->logMessage("Waiting on stop thread"); + socket->shutdownConnection(); + pthread_join(stopThreadID, NULL); + } + + // Write the captured xml file + if (gSessionData->mLocalCapture) { + CapturedXML capturedXML; + capturedXML.write(gSessionData->mAPCDir); + } + + logg->logMessage("Profiling ended."); + + delete collectorFifo; + delete sender; + delete collector; + delete localCapture; +} diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h new file mode 100644 index 00000000000..612ca7cf3e2 --- /dev/null +++ b/tools/gator/daemon/Child.h @@ -0,0 +1,31 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CHILD_H__ +#define __CHILD_H__ + +#include +#include "Fifo.h" +#include "OlySocket.h" + +class Child { +public: + Child(); + Child(OlySocket* sock, int numConnections); + ~Child(); + void run(); + OlySocket *socket; + void endSession(); + int numExceptions; +private: + int mNumConnections; + + void initialization(); +}; + +#endif //__CHILD_H__ diff --git a/tools/gator/daemon/Collector.cpp b/tools/gator/daemon/Collector.cpp new file mode 100644 index 00000000000..db7b9c08e77 --- /dev/null +++ b/tools/gator/daemon/Collector.cpp @@ -0,0 +1,291 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "Collector.h" +#include "SessionData.h" +#include "Logging.h" +#include "Sender.h" + +extern void handleException(); + +// Driver initialization independent of session settings +Collector::Collector() { + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// + + mBufferFD = 0; + + checkVersion(); + + int enable = -1; + if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { + logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + handleException(); + } + + readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); + if (gSessionData->mCores == 0) { + gSessionData->mCores = 1; + } + + mBufferSize = 0; + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + handleException(); + } + + getCoreName(); + + enablePerfCounters(); + + // Read unchanging keys from driver which are created at insmod'ing of gator.ko + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + if (gSessionData->mPerfCounterEnabled[i]) { + snprintf(text, sizeof(text), "/dev/gator/events/%s/key", gSessionData->mPerfCounterType[i]); + readIntDriver(text, &gSessionData->mPerfCounterKey[i]); + } + } +} + +Collector::~Collector() { + // Write zero for safety, as a zero should have already been written + writeDriver("/dev/gator/enable", "0"); + + // Calls event_buffer_release in the driver + if (mBufferFD) { + close(mBufferFD); + } +} + +#include +void Collector::enablePerfCounters() { + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events//enabled + + // Initialize all perf counters in the driver, i.e. set enabled to zero + struct dirent *ent; + DIR* dir = opendir("/dev/gator/events"); + if (dir) { + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", ent->d_name); + writeDriver(text, 0); + } + closedir (dir); + } + + for (int i=0; imPerfCounterEnabled[i]) { + continue; + } + snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", gSessionData->mPerfCounterType[i]); + if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { + // Disable those events that don't exist on this hardware platform even though they exist in configuration.xml + gSessionData->mPerfCounterEnabled[i] = 0; + continue; + } + } +} + +void Collector::setupPerfCounters() { + char base[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all /dev/gator/events/ + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// + + for (int i=0; imPerfCounterEnabled[i]) { + continue; + } + snprintf(base, sizeof(base), "/dev/gator/events/%s", gSessionData->mPerfCounterType[i]); + snprintf(text, sizeof(text), "%s/event", base); + writeDriver(text, gSessionData->mPerfCounterEvent[i]); + if (gSessionData->mPerfCounterEBSCapable[i]) { + snprintf(text, sizeof(text), "%s/count", base); + if (access(text, F_OK) == 0) { + if (writeReadDriver(text, &gSessionData->mPerfCounterCount[i]) && gSessionData->mPerfCounterCount[i] > 0) { + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%s with a count of %d\n", gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]); + handleException(); + } + } else if (gSessionData->mPerfCounterCount[i] > 0) { + logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); + handleException(); + } + } + } +} + +void Collector::checkVersion() { + int driver_version = 0; + + if (readIntDriver("/dev/gator/version", &driver_version) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driver_version != PROTOCOL_VERSION) { + if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg->logError(__FILE__, __LINE__, + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driver_version, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg->logError(__FILE__, __LINE__, + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); + handleException(); + } + } +} + +void Collector::start() { + // Set the maximum backtrace depth + if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); + handleException(); + } + + // open the buffer which calls userspace_buffer_open() in the driver + mBufferFD = open("/dev/gator/buffer", O_RDONLY); + if (mBufferFD < 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + // set the tick rate of the profiling timer + if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); + handleException(); + } + + // notify the kernel of the response type + int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; + if (writeDriver("/dev/gator/response_type", response_type)) { + logg->logError(__FILE__, __LINE__, "Unable to write the response type"); + handleException(); + } + + logg->logMessage("Start the driver"); + + // This command makes the driver start profiling by calling gator_op_start() in the driver + if (writeDriver("/dev/gator/enable", "1") != 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + lseek(mBufferFD, 0, SEEK_SET); +} + +// These commands should cause the read() function in collect() to return +void Collector::stop() { + // This will stop the driver from profiling + if (writeDriver("/dev/gator/enable", "0") != 0) { + logg->logMessage("Stopping kernel failed"); + } +} + +int Collector::collect(char* buffer) { + // Calls event_buffer_read in the driver + int bytesRead = read(mBufferFD, buffer, mBufferSize); + + // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data + if (bytesRead == -1 && errno == EINTR) { + bytesRead = read(mBufferFD, buffer, mBufferSize); + } + + // return the total bytes written + logg->logMessage("Driver read of %d bytes", bytesRead); + return bytesRead; +} + +void Collector::getCoreName() { + char temp[256]; // arbitrarily large amount + strcpy(gSessionData->mCoreName, "unknown"); + + FILE* f = fopen("/proc/cpuinfo", "r"); + if (f == NULL) { + logg->logMessage("Error opening /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + + while (fgets(temp, sizeof(temp), f)) { + if (strlen(temp) > 0) { + temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + } + + if (strstr(temp, "Hardware") != 0) { + char* position = strchr(temp, ':'); + if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { + logg->logMessage("Unknown format of /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + strncpy(gSessionData->mCoreName, (char*)((int)position + 2), sizeof(gSessionData->mCoreName)); + gSessionData->mCoreName[sizeof(gSessionData->mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string + fclose(f); + return; + } + } + + logg->logMessage("Could not determine core name from /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + fclose(f); +} + +int Collector::readIntDriver(const char* fullpath, int* value) { + FILE* file = fopen(fullpath, "r"); + if (file == NULL) { + return -1; + } + if (fscanf(file, "%u", value) != 1) { + fclose(file); + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + fclose(file); + return 0; +} + +int Collector::writeDriver(const char* path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int Collector::writeDriver(const char* fullpath, const char* data) { + int fd = open(fullpath, O_WRONLY); + if (fd < 0) { + return -1; + } + if (write(fd, data, strlen(data)) < 0) { + close(fd); + logg->logMessage("Opened but could not write to %s", fullpath); + return -1; + } + close(fd); + return 0; +} + +int Collector::writeReadDriver(const char* path, int* value) { + if (writeDriver(path, *value) || readIntDriver(path, value)) { + return -1; + } + return 0; +} diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h new file mode 100644 index 00000000000..5977a725ecc --- /dev/null +++ b/tools/gator/daemon/Collector.h @@ -0,0 +1,37 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __COLLECTOR_H__ +#define __COLLECTOR_H__ + +#include + +class Collector { +public: + Collector(); + ~Collector(); + void start(); + void stop(); + int collect(char* buffer); + void enablePerfCounters(); + void setupPerfCounters(); + int getBufferSize() {return mBufferSize;} +private: + int mBufferSize; + int mBufferFD; + + void checkVersion(); + void getCoreName(); + + int readIntDriver(const char* path, int* value); + int writeDriver(const char* path, int value); + int writeDriver(const char* path, const char* data); + int writeReadDriver(const char* path, int* value); +}; + +#endif //__COLLECTOR_H__ diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp new file mode 100644 index 00000000000..145ddbdd736 --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -0,0 +1,188 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "ConfigurationXML.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +extern void handleException(); + +static const char* ATTR_COUNTER = "counter"; +static const char* ATTR_REVISION = "revision"; +static const char* ATTR_TITLE = "title"; +static const char* ATTR_NAME = "name"; +static const char* ATTR_EVENT = "event"; +static const char* ATTR_COLOR = "color"; +static const char* ATTR_COUNT = "count"; +static const char* ATTR_OPERATION = "operation"; +static const char* ATTR_PER_CPU = "per_cpu"; +static const char* ATTR_DESCRIPTION = "description"; +static const char* ATTR_EBS = "event_based_sampling"; +static const char* ATTR_LEVEL = "level"; +static const char* ATTR_ALIAS = "alias"; +static const char* ATTR_DISPLAY = "display"; +static const char* ATTR_UNITS = "units"; +static const char* ATTR_AVERAGE_SELECTION = "average_selection"; + +ConfigurationXML::ConfigurationXML() { +#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len + mIndex = 0; + char* path = (char*)malloc(PATH_MAX); + + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + } else { + if (util->getApplicationFullPath(path, PATH_MAX) != 0) { + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + mConfigurationXML = util->readFromDisk(path); + + if (mConfigurationXML == NULL) { + logg->logMessage("Unable to locate configuration.xml, using default in binary"); + // null-terminate configuration_xml + mConfigurationXML = (char*)malloc(configuration_xml_len + 1); + memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len); + mConfigurationXML[configuration_xml_len] = 0; + } + + // disable all counters prior to parsing the configuration xml + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + gSessionData->mPerfCounterEnabled[i] = 0; + } + + int ret = parse(mConfigurationXML); + if (ret == 1) { + // remove configuration.xml on disk to use the default + if (remove(path) != 0) { + logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + handleException(); + } + logg->logMessage("Invalid configuration.xml file detected and removed"); + } + + validate(); + + free(path); +} + +ConfigurationXML::~ConfigurationXML() { + if (mConfigurationXML) { + free((void*)mConfigurationXML); + } +} + +int ConfigurationXML::parse(const char* configurationXML) { + mxml_node_t *tree, *node; + int ret; + + tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK); + + node = mxmlGetFirstChild(tree); + while (node && mxmlGetType(node) != MXML_ELEMENT) + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + + ret = configurationsTag(node); + + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; + } + configurationTag(node); + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + } + + mxmlDelete(tree); + + return ret; +} + +void ConfigurationXML::validate(void) { + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + if (gSessionData->mPerfCounterEnabled[i]) { + if (strcmp(gSessionData->mPerfCounterType[i], "") == 0) { + logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n title=\"%s\"\n name=\"%s\"\n event=%d\n", gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterEvent[i]); + handleException(); + } + + // iterate through the remaining enabled performance counters + for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { + if (gSessionData->mPerfCounterEnabled[j]) { + // check if the types are the same + if (strcmp(gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterType[j]) == 0) { + logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", gSessionData->mPerfCounterType[i]); + handleException(); + } + } + } + } + } +} + +#define CONFIGURATION_REVISION 1 +int ConfigurationXML::configurationsTag(mxml_node_t *node) { + const char* revision_string; + + revision_string = mxmlElementGetAttr(node, ATTR_REVISION); + if (!revision_string) { + return 1; //revision issue; + } + + int revision = strtol(revision_string, NULL, 10); + if (revision < CONFIGURATION_REVISION) { + return 1; // revision issue + } + + return 0; +} + +void ConfigurationXML::configurationTag(mxml_node_t *node) { + // handle all other performance counters + if (mIndex >= MAX_PERFORMANCE_COUNTERS) { + logg->logError(__FILE__, __LINE__, "Exceeded maximum number of %d performance counters", MAX_PERFORMANCE_COUNTERS); + handleException(); + } + + // read attributes + if (mxmlElementGetAttr(node, ATTR_COUNTER)) strncpy(gSessionData->mPerfCounterType[mIndex], mxmlElementGetAttr(node, ATTR_COUNTER), sizeof(gSessionData->mPerfCounterType[mIndex])); + if (mxmlElementGetAttr(node, ATTR_TITLE)) strncpy(gSessionData->mPerfCounterTitle[mIndex], mxmlElementGetAttr(node, ATTR_TITLE), sizeof(gSessionData->mPerfCounterTitle[mIndex])); + if (mxmlElementGetAttr(node, ATTR_NAME)) strncpy(gSessionData->mPerfCounterName[mIndex], mxmlElementGetAttr(node, ATTR_NAME), sizeof(gSessionData->mPerfCounterName[mIndex])); + if (mxmlElementGetAttr(node, ATTR_DESCRIPTION)) strncpy(gSessionData->mPerfCounterDescription[mIndex], mxmlElementGetAttr(node, ATTR_DESCRIPTION), sizeof(gSessionData->mPerfCounterDescription[mIndex])); + if (mxmlElementGetAttr(node, ATTR_EVENT)) gSessionData->mPerfCounterEvent[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16); + if (mxmlElementGetAttr(node, ATTR_COUNT)) gSessionData->mPerfCounterCount[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10); + if (mxmlElementGetAttr(node, ATTR_COLOR)) gSessionData->mPerfCounterColor[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_COLOR), NULL, 16); + if (mxmlElementGetAttr(node, ATTR_PER_CPU)) gSessionData->mPerfCounterPerCPU[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_PER_CPU), false); + if (mxmlElementGetAttr(node, ATTR_EBS)) gSessionData->mPerfCounterEBSCapable[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_EBS), false); + if (mxmlElementGetAttr(node, ATTR_OPERATION)) strncpy(gSessionData->mPerfCounterOperation[mIndex], mxmlElementGetAttr(node, ATTR_OPERATION), sizeof(gSessionData->mPerfCounterOperation[mIndex])); + if (mxmlElementGetAttr(node, ATTR_LEVEL)) gSessionData->mPerfCounterLevel[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_LEVEL), false); + if (mxmlElementGetAttr(node, ATTR_ALIAS)) strncpy(gSessionData->mPerfCounterAlias[mIndex], mxmlElementGetAttr(node, ATTR_ALIAS), sizeof(gSessionData->mPerfCounterAlias[mIndex])); + if (mxmlElementGetAttr(node, ATTR_DISPLAY)) strncpy(gSessionData->mPerfCounterDisplay[mIndex], mxmlElementGetAttr(node, ATTR_DISPLAY), sizeof(gSessionData->mPerfCounterDisplay[mIndex])); + if (mxmlElementGetAttr(node, ATTR_UNITS)) strncpy(gSessionData->mPerfCounterUnits[mIndex], mxmlElementGetAttr(node, ATTR_UNITS), sizeof(gSessionData->mPerfCounterUnits[mIndex])); + if (mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION)) gSessionData->mPerfCounterAverageSelection[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION), false); + gSessionData->mPerfCounterEnabled[mIndex] = true; + + // strncpy does not guarantee a null-termianted string + gSessionData->mPerfCounterType[mIndex][sizeof(gSessionData->mPerfCounterType[mIndex]) - 1] = 0; + gSessionData->mPerfCounterTitle[mIndex][sizeof(gSessionData->mPerfCounterTitle[mIndex]) - 1] = 0; + gSessionData->mPerfCounterName[mIndex][sizeof(gSessionData->mPerfCounterName[mIndex]) - 1] = 0; + gSessionData->mPerfCounterDescription[mIndex][sizeof(gSessionData->mPerfCounterDescription[mIndex]) - 1] = 0; + gSessionData->mPerfCounterOperation[mIndex][sizeof(gSessionData->mPerfCounterOperation[mIndex]) - 1] = 0; + gSessionData->mPerfCounterAlias[mIndex][sizeof(gSessionData->mPerfCounterAlias[mIndex]) - 1] = 0; + gSessionData->mPerfCounterDisplay[mIndex][sizeof(gSessionData->mPerfCounterDisplay[mIndex]) - 1] = 0; + gSessionData->mPerfCounterUnits[mIndex][sizeof(gSessionData->mPerfCounterUnits[mIndex]) - 1] = 0; + + // update counter index + mIndex++; +} diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h new file mode 100644 index 00000000000..b48d32f978d --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.h @@ -0,0 +1,29 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef COUNTERS_H +#define COUNTERS_H + +#include "mxml/mxml.h" + +class ConfigurationXML { +public: + ConfigurationXML(); + ~ConfigurationXML(); + const char* getConfigurationXML() {return mConfigurationXML;} + void validate(void); +private: + char* mConfigurationXML; + int mIndex; + + int parse(const char* xmlFile); + int configurationsTag(mxml_node_t *node); + void configurationTag(mxml_node_t *node); +}; + +#endif // COUNTERS_H diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp new file mode 100644 index 00000000000..bbfc9198b30 --- /dev/null +++ b/tools/gator/daemon/Fifo.cpp @@ -0,0 +1,126 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "Fifo.h" +#include "Logging.h" + +extern void handleException(); + +// bufferSize is the amount of data to be filled +// singleBufferSize is the maximum size that may be filled during a single write +// (bufferSize + singleBufferSize) will be allocated +Fifo::Fifo(int singleBufferSize, int bufferSize) { + mWrite = mRead = mReadCommit = mRaggedEnd = 0; + mWrapThreshold = bufferSize; + mSingleBufferSize = singleBufferSize; + mBuffer = (char*)valloc(bufferSize + singleBufferSize); + mEnd = false; + + if (mBuffer == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize); + handleException(); + } + + if (sem_init(&mWaitForSpaceSem, 0, 0) || sem_init(&mWaitForDataSem, 0, 0)) { + logg->logError(__FILE__, __LINE__, "sem_init() failed"); + handleException(); + } +} + +Fifo::~Fifo() { + free(mBuffer); +} + +int Fifo::numBytesFilled() { + return mWrite - mRead + mRaggedEnd; +} + +char* Fifo::start() { + return mBuffer; +} + +bool Fifo::isEmpty() { + return mRead == mWrite; +} + +bool Fifo::isFull() { + return willFill(0); +} + +// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer +// comparisons use '<', read and write pointers must never equal when not empty +// 'full' means there is less than singleBufferSize bytes available; it does not mean there are zero bytes available +bool Fifo::willFill(int additional) { + if (mWrite > mRead) { + if (numBytesFilled() + additional < mWrapThreshold) { + return false; + } + } else { + if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) { + return false; + } + } + return true; +} + +// This function will stall until contiguous singleBufferSize bytes are available +char* Fifo::write(int length) { + if (length <= 0) { + length = 0; + mEnd = true; + } + + // update the write pointer + mWrite += length; + + // handle the wrap-around + if (mWrite >= mWrapThreshold) { + mRaggedEnd = mWrite; + mWrite = 0; + } + + // send a notification that data is ready + sem_post(&mWaitForDataSem); + + // wait for space + while (isFull()) { + sem_wait(&mWaitForSpaceSem); + } + + return &mBuffer[mWrite]; +} + +// This function will stall until data is available +char* Fifo::read(int* length) { + // update the read pointer now that the data has been handled + mRead = mReadCommit; + + // handle the wrap-around + if (mRead >= mWrapThreshold) { + mRaggedEnd = mRead = mReadCommit = 0; + } + + // send a notification that data is free (space is available) + sem_post(&mWaitForSpaceSem); + + // wait for data + while (isEmpty() && !mEnd) { + sem_wait(&mWaitForDataSem); + } + + // obtain the length + do { + mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite; + *length = mReadCommit - mRead; + } while (*length < 0); // plugs race condition without using semaphores + + return &mBuffer[mRead]; +} diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h new file mode 100644 index 00000000000..548ba278806 --- /dev/null +++ b/tools/gator/daemon/Fifo.h @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __FIFO_H__ +#define __FIFO_H__ + +#include + +class Fifo { +public: + Fifo(int singleBufferSize, int totalBufferSize); + ~Fifo(); + int numBytesFilled(); + bool isEmpty(); + bool isFull(); + bool willFill(int additional); + char* start(); + char* write(int length); + char* read(int* length); + +private: + int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold; + sem_t mWaitForSpaceSem, mWaitForDataSem; + char* mBuffer; + bool mEnd; +}; + +#endif //__FIFO_H__ diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp new file mode 100644 index 00000000000..6449d035214 --- /dev/null +++ b/tools/gator/daemon/LocalCapture.cpp @@ -0,0 +1,214 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "LocalCapture.h" +#include "SessionData.h" +#include "Logging.h" +#include "OlyUtility.h" + +extern void handleException(); + +LocalCapture::LocalCapture() {} + +LocalCapture::~LocalCapture() {} + +void LocalCapture::createAPCDirectory(char* target_path, char* name) { + gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc", name); + if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { + logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir); + handleException(); + } +} + +void LocalCapture::write(char* string) { + char* file = (char*)malloc(PATH_MAX); + + // Set full path + snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir); + + // Write the file + if (util->writeToDisk(file, string) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(file); +} + +char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending, char* title) { + int i; + char* output; + char* path = (char*)malloc(PATH_MAX); + + // Ensure the path is an absolute path, i.e. starts with a slash + if (initialPath == 0 || strlen(initialPath) == 0) { + if (getcwd(path, PATH_MAX) == 0) { + logg->logMessage("Unable to retrive the current working directory"); + } + strncat(path, "/@F_@N", PATH_MAX - strlen(path) - 1); + } else if (initialPath[0] != '/') { + if (getcwd(path, PATH_MAX) == 0) { + logg->logMessage("Unable to retrive the current working directory"); + } + strncat(path, "/", PATH_MAX - strlen(path) - 1); + strncat(path, initialPath, PATH_MAX - strlen(path) - 1); + } else { + strncpy(path, initialPath, PATH_MAX); + path[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // Convert to uppercase + replaceAll(path, "@f", "@F", PATH_MAX); + replaceAll(path, "@n", "@N", PATH_MAX); + + // Replace @F with the session xml title + replaceAll(path, "@F", title, PATH_MAX); + + // Add ending if it is not already there + if (strcmp(&path[strlen(path) - strlen(ending)], ending) != 0) { + strncat(path, ending, PATH_MAX - strlen(path) - 1); + } + + // Replace @N with a unique integer + if (strstr(path, "@N")) { + char* tempPath = (char*)malloc(PATH_MAX); + for (i = 1; i < 1000; i++) { + char number[4]; + snprintf(number, sizeof(number), "%03d", i); + strcpy(tempPath, path); + replaceAll(tempPath, "@N", number, PATH_MAX); + struct stat mFileInfo; + if (stat(tempPath, &mFileInfo) != 0) { + // if the direcotry does not exist, break + break; + } + } + + if (i == 1000) { + logg->logError(__FILE__, __LINE__, "Unable to create .apc directory, please delete older directories."); + handleException(); + } + + output = strdup(tempPath); + free(tempPath); + } else { + output = strdup(path); + } + + free(path); + return output; +} + +//Replaces all occurrences of in with provided enough is available +void LocalCapture::replaceAll(char* target, const char* find, const char* replace, unsigned int size) { + char* nextOccurrence; + unsigned int count = 0; + + // Duplicate the original string + char* original = strdup(target); + char* ptr = original; + + // Determine number of s + ptr = strstr(ptr, find); + while (ptr) { + count++; + ptr += strlen(find); + ptr = strstr(ptr, find); + } + + // Is there enough space available + if (strlen(target) + (strlen(replace) - strlen(find)) * count > size - 1) { + free(original); + return; + } + + // Reset + ptr = original; + + nextOccurrence = strstr(ptr, find); + while (nextOccurrence) { + // Move pointers to location of replace + int length = nextOccurrence - ptr; + target += length; + ptr += length; + + // Replace with + memcpy(target, replace, strlen(replace)); + + // Increment over / + target += strlen(replace); + ptr += strlen(find); + + // Copy remainder of ptr + strcpy(target, ptr); + + // Get next occurrence + nextOccurrence = strstr(ptr, find); + } + + free(original); +} + +int LocalCapture::removeDirAndAllContents(char* path) { + int error = 0; + struct stat mFileInfo; + // Does the path exist? + if (stat(path, &mFileInfo) == 0) { + // Is it a directory? + if (mFileInfo.st_mode & S_IFDIR) { + DIR * dir = opendir(path); + dirent* entry = readdir(dir); + while (entry) { + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + char* newpath = (char*)malloc(strlen(path) + strlen(entry->d_name) + 2); + sprintf(newpath, "%s/%s", path, entry->d_name); + error = removeDirAndAllContents(newpath); + free(newpath); + if (error) { + break; + } + } + entry = readdir(dir); + } + closedir(dir); + if (error == 0) { + error = rmdir(path); + } + } else { + error = remove(path); + } + } + return error; +} + +void LocalCapture::copyImages(ImageLinkList* ptr) { + char* dstfilename = (char*)malloc(PATH_MAX); + + while (ptr) { + strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX); + dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') { + strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1); + } + strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); + if (util->copyFile(ptr->path, dstfilename)) { + logg->logMessage("copied file %s to %s", ptr->path, dstfilename); + } else { + logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename); + } + + ptr = ptr->next; + } + free(dstfilename); +} diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h new file mode 100644 index 00000000000..ca37f6e7b51 --- /dev/null +++ b/tools/gator/daemon/LocalCapture.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LOCAL_CAPTURE_H__ +#define __LOCAL_CAPTURE_H__ + +#include "SessionXML.h" + +class LocalCapture { +public: + LocalCapture(); + ~LocalCapture(); + void write(char* string); + void copyImages(ImageLinkList* ptr); + void createAPCDirectory(char* target_path, char* name); +private: + char* createUniqueDirectory(const char* path, const char* ending, char* title); + void replaceAll(char* target, const char* find, const char* replace, unsigned int size); + int removeDirAndAllContents(char* path); +}; + +#endif //__LOCAL_CAPTURE_H__ diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp new file mode 100755 index 00000000000..3e6f8a388a4 --- /dev/null +++ b/tools/gator/daemon/Logging.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include "OlyUtility.h" + +#ifdef WIN32 +#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL); +#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF); +#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex); +#define snprintf _snprintf +#else +#include +#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL) +#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex) +#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) +#endif + +#include "Logging.h" + +// Global thread-safe logging +Logging* logg = NULL; + +Logging::Logging(bool debug) { + mDebug = debug; + MUTEX_INIT(); + + strcpy(mErrBuf, "Unknown Error"); + strcpy(mLogBuf, "Unknown Message"); +} + +Logging::~Logging() { +} + +void Logging::logError(const char* file, int line, const char* fmt, ...) { + va_list args; + + MUTEX_LOCK(); + if (mDebug) { + snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line); + } else { + mErrBuf[0] = 0; + } + + va_start(args, fmt); + vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + + if (strlen(mErrBuf) > 0) { + strcat(mErrBuf, "\n"); + } + MUTEX_UNLOCK(); +} + +void Logging::logMessage(const char* fmt, ...) { + if (mDebug) { + va_list args; + + MUTEX_LOCK(); + strcpy(mLogBuf, "INFO: "); + + va_start(args, fmt); + vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + strcat(mLogBuf, "\n"); + + fprintf(stdout, "%s", mLogBuf); + fflush(stdout); + MUTEX_UNLOCK(); + } +} diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h new file mode 100755 index 00000000000..424796658f7 --- /dev/null +++ b/tools/gator/daemon/Logging.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LOGGING_H__ +#define __LOGGING_H__ + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +#define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" + +class Logging { +public: + Logging(bool debug); + ~Logging(); + void logError(const char* file, int line, const char* fmt, ...); + void logMessage(const char* fmt, ...); + char* getLastError() {return mErrBuf;} + char* getLastMessage() {return mLogBuf;} + +private: + char mErrBuf[4096]; // Arbitrarily large buffer to hold a string + char mLogBuf[4096]; // Arbitrarily large buffer to hold a string + bool mDebug; +#ifdef WIN32 + HANDLE mLoggingMutex; +#else + pthread_mutex_t mLoggingMutex; +#endif +}; + +extern Logging* logg; + +#endif //__LOGGING_H__ diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile new file mode 100644 index 00000000000..aa1c0056a8a --- /dev/null +++ b/tools/gator/daemon/Makefile @@ -0,0 +1,46 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/arm-none-linux-gnueabi- +ARCH=arm + +CPP=$(CROSS_COMPILE)g++ +GCC=$(CROSS_COMPILE)gcc + +# -g produces debugging information +# -O3 maximum optimization +# -O0 no optimization, used for debugging +# -Wall enables most warnings +# -Werror treats warnings as errors +# -std=c++0x is the planned new c++ standard +# -std=c++98 is the 1998 c++ standard +# -march=armv5t is required to set the minimum architecture +# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc +CFLAGS=-O3 -Wall -Werror -march=armv5t -mthumb-interwork +TARGET=gatord +C_SRC = $(wildcard mxml/*.c) +CPP_SRC = $(wildcard *.cpp) +TGT_OBJS = $(CPP_SRC:%.cpp=%.o) \ + $(C_SRC:%.c=%.o) + +all: $(TARGET) + +%.o: %.c *.h + $(GCC) -c $(CFLAGS) -o $@ $< + +%.o: %.cpp *.h + $(CPP) -c $(CFLAGS) -o $@ $< + +$(TARGET): convert $(TGT_OBJS) + $(CPP) -s -o $@ $(TGT_OBJS) -lc -lrt -lpthread + rm events_xml.h configuration_xml.h + +convert: + cat events_header.xml events-*\.xml events_footer.xml > events.xml + xxd -i events.xml > events_xml.h + xxd -i configuration.xml > configuration_xml.h + +clean: + rm -f *.o mxml/*.o $(TARGET) events.xml events_xml.h configuration_xml.h diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp new file mode 100755 index 00000000000..a3bf74647cd --- /dev/null +++ b/tools/gator/daemon/OlySocket.cpp @@ -0,0 +1,261 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif +#include "OlySocket.h" +#include "Logging.h" + +#ifdef WIN32 +#define CLOSE_SOCKET(x) closesocket(x) +#define SHUTDOWN_RX_TX SD_BOTH +#define snprintf _snprintf +#else +#define CLOSE_SOCKET(x) close(x) +#define SHUTDOWN_RX_TX SHUT_RDWR +#endif + +extern void handleException(); + +OlySocket::OlySocket(int port, bool multiple) { +#ifdef WIN32 + WSADATA wsaData; + if (WSAStartup(0x0202, &wsaData) != 0) { + logg->logError(__FILE__, __LINE__, "Windows socket initialization failed"); + handleException(); + } +#endif + + if (multiple) { + createServerSocket(port); + } else { + createSingleServerConnection(port); + } +} + +OlySocket::OlySocket(int port, char* host) { + mFDServer = 0; + createClientSocket(host, port); +} + +OlySocket::~OlySocket() { + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + } +} + +void OlySocket::shutdownConnection() { + // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions + shutdown(mSocketID, SHUTDOWN_RX_TX); +} + +void OlySocket::closeSocket() { + // Used for closing an accepted socket but keeping the server socket active + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + mSocketID = -1; + } +} + +void OlySocket::closeServerSocket() { + if (CLOSE_SOCKET(mFDServer) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to close server socket."); + handleException(); + } + mFDServer = 0; +} + +void OlySocket::createClientSocket(char* hostname, int portno) { +#ifdef WIN32 + // TODO: Implement for Windows +#else + char buf[32]; + struct addrinfo hints, *res, *res0; + + snprintf(buf, sizeof(buf), "%d", portno); + mSocketID = -1; + memset((void*)&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(hostname, buf, &hints, &res0)) { + logg->logError(__FILE__, __LINE__, "Client socket failed to get address info for %s", hostname); + handleException(); + } + for (res=res0; res!=NULL; res = res->ai_next) { + if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) { + continue; + } + mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (mSocketID < 0) { + continue; + } + if (connect(mSocketID, res->ai_addr, res->ai_addrlen) < 0) { + close(mSocketID); + mSocketID = -1; + } + if (mSocketID > 0) { + break; + } + } + freeaddrinfo(res0); + if (mSocketID <= 0) { + logg->logError(__FILE__, __LINE__, "Could not connect to client socket. Ensure ARM Streamline is running."); + handleException(); + } +#endif +} + +void OlySocket::createSingleServerConnection(int port) { + createServerSocket(port); + + mSocketID = acceptConnection(); + closeServerSocket(); +} + +void OlySocket::createServerSocket(int port) { + // Create socket + mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits + int on = 1; + if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { + logg->logError(__FILE__, __LINE__, "Setting server socket options failed"); + handleException(); + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_in sockaddr; + memset((void*)&sockaddr, 0, sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + // Bind the socket to an address + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?"); + handleException(); + } + + // Listen for connections on this socket + if (listen(mFDServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); + } +} + +// mSocketID is always set to the most recently accepted connection +// The user of this class should maintain the different socket connections, e.g. by forking the process +int OlySocket::acceptConnection() { + if (mFDServer <= 0) { + logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); + handleException(); + } + + // Accept a connection, note that this call blocks until a client connects + mSocketID = accept(mFDServer, NULL, NULL); + if (mSocketID < 0) { + logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); + handleException(); + } + return mSocketID; +} + +void OlySocket::send(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return; + } + + while (size > 0) { + int n = ::send(mSocketID, buffer, size, 0); + if (n < 0) { + logg->logError(__FILE__, __LINE__, "Socket send error"); + handleException(); + } + size -= n; + buffer += n; + } +} + +// Returns the number of bytes received +int OlySocket::receive(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return 0; + } + + int bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + return bytes; +} + +// Receive exactly size bytes of data. Note, this function will block until all bytes are received +int OlySocket::receiveNBytes(char* buffer, int size) { + int bytes = 0; + while (size > 0 && buffer != NULL) { + bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + buffer += bytes; + size -= bytes; + } + return bytes; +} + +// Receive data until a carriage return, line feed, or null is encountered, or the buffer fills +int OlySocket::receiveString(char* buffer, int size) { + int bytes_received = 0; + bool found = false; + + if (buffer == 0) { + return 0; + } + + while (!found && bytes_received < size) { + // Receive a single character + int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + + // Replace carriage returns and line feeds with zero + if (buffer[bytes_received] == '\n' || buffer[bytes_received] == '\r' || buffer[bytes_received] == '\0') { + buffer[bytes_received] = '\0'; + found = true; + } + + bytes_received++; + } + + return bytes_received; +} diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h new file mode 100755 index 00000000000..266a831a56b --- /dev/null +++ b/tools/gator/daemon/OlySocket.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __OLY_SOCKET_H__ +#define __OLY_SOCKET_H__ + +#include + +class OlySocket { +public: + OlySocket(int port, bool multipleConnections = false); + OlySocket(int port, char* hostname); + ~OlySocket(); + int acceptConnection(); + void closeSocket(); + void closeServerSocket(); + void shutdownConnection(); + void send(char* buffer, int size); + void sendString(const char* string) {send((char*)string, strlen(string));} + int receive(char* buffer, int size); + int receiveNBytes(char* buffer, int size); + int receiveString(char* buffer, int size); + int getSocketID() {return mSocketID;} +private: + int mSocketID, mFDServer; + void createClientSocket(char* hostname, int port); + void createSingleServerConnection(int port); + void createServerSocket(int port); +}; + +#endif //__OLY_SOCKET_H__ diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp new file mode 100755 index 00000000000..ec852df5ed8 --- /dev/null +++ b/tools/gator/daemon/OlyUtility.cpp @@ -0,0 +1,254 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#else +#include +#endif + +#include "OlyUtility.h" + +OlyUtility* util = NULL; + +bool OlyUtility::stringToBool(const char* string, bool defValue) { + char value[32]; + + strncpy(value, string, sizeof(value)); + if (value[0] == 0) { + return defValue; + } + value[sizeof(value) - 1] = 0; // strncpy does not guarantee a null-terminated string + + // Convert to lowercase + int i = 0; + while (value[i]) { + value[i] = tolower(value[i]); + i++; + } + + if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) { + return true; + } else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) { + return false; + } else { + return defValue; + } +} + +void OlyUtility::stringToLower(char* string) { + if (string == NULL) { + return; + } + + while (*string) { + *string = tolower(*string); + string++; + } +} + +// Modifies fullpath with the path part including the trailing path separator +int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { + memset(fullpath, 0, sizeOfPath); +#ifdef WIN32 + int length = GetModuleFileName(NULL, fullpath, sizeOfPath); +#else + int length = readlink("/proc/self/exe", fullpath, sizeOfPath); +#endif + + if (length == sizeOfPath) { + return -1; + } + + fullpath[length] = 0; + fullpath = getPathPart(fullpath); + + return 0; +} + +char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) { + // Open the file + FILE* pFile = fopen(file, "rb"); + if (pFile==NULL) { + return NULL; + } + + // Obtain file size + fseek(pFile , 0 , SEEK_END); + unsigned int lSize = ftell(pFile); + rewind(pFile); + + // Allocate memory to contain the whole file + char* buffer = (char*)malloc(lSize + (int)appendNull); + if (buffer == NULL) { + fclose(pFile); + return NULL; + } + + // Copy the file into the buffer + if (fread(buffer, 1, lSize, pFile) != lSize) { + free(buffer); + fclose(pFile); + return NULL; + } + + // Terminate + fclose(pFile); + + if (appendNull) { + buffer[lSize] = 0; + } + + if (size) { + *size = lSize; + } + + return buffer; +} + +int OlyUtility::writeToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "wb"); + if (pFile == NULL) { + return -1; + } + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } + + // Terminate + fclose(pFile); + return 0; +} + +int OlyUtility::appendToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "a"); + if (pFile == NULL) { + return -1; + } + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } + + // Terminate + fclose(pFile); + return 0; +} + +/** + * Copies the srcFile into dstFile in 1kB chunks. + * The dstFile will be overwritten if it exists. + * 0 is returned on an error; otherwise 1. + */ +#define TRANSFER_SIZE 1024 +int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { + char* buffer = (char*)malloc(TRANSFER_SIZE); + FILE * f_src = fopen(srcFile,"rb"); + if (!f_src) { + return 0; + } + FILE * f_dst = fopen(dstFile,"wb"); + if (!f_dst) { + fclose(f_src); + return 0; + } + while (!feof(f_src)) { + int num_bytes_read = fread(buffer, 1, TRANSFER_SIZE, f_src); + if (num_bytes_read < TRANSFER_SIZE && !feof(f_src)) { + fclose(f_src); + fclose(f_dst); + return 0; + } + int num_bytes_written = fwrite(buffer, 1, num_bytes_read, f_dst); + if (num_bytes_written != num_bytes_read) { + fclose(f_src); + fclose(f_dst); + return 0; + } + } + fclose(f_src); + fclose(f_dst); + free(buffer); + return 1; +} + +const char* OlyUtility::getFilePart(const char* path) { + const char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return path; + } + + return (const char*)((int)last_sep + 1); +} + +// getPathPart may modify the contents of path +// returns the path including the trailing path separator +char* OlyUtility::getPathPart(char* path) { + char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return 0; + } + *(char*)((int)last_sep + 1) = 0; + + return (path); +} + +// whitespace callback utility function used with mini-xml +const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) { + const char *name; + + name = mxmlGetElement(node); + + if (loc == MXML_WS_BEFORE_OPEN) { + // Single indentation + if (!strcmp(name, "target") || !strcmp(name, "counters")) + return("\n "); + + // Double indentation + if (!strcmp(name, "counter")) + return("\n "); + + // Avoid a carriage return on the first line of the xml file + if (!strncmp(name, "?xml", 4)) + return(NULL); + + // Default - no indentation + return("\n"); + } + + if (loc == MXML_WS_BEFORE_CLOSE) { + // No indentation + if (!strcmp(name, "captured")) + return("\n"); + + // Single indentation + if (!strcmp(name, "counters")) + return("\n "); + + // Default - no carriage return + return(NULL); + } + + return(NULL); +} diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h new file mode 100755 index 00000000000..793a733d8d4 --- /dev/null +++ b/tools/gator/daemon/OlyUtility.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef OLY_UTILITY_H +#define OLY_UTILITY_H + +#ifdef WIN32 +#define PATH_SEPARATOR '\\' +#else +#define PATH_SEPARATOR '/' +#endif + +class OlyUtility { +public: + OlyUtility() {}; + ~OlyUtility() {}; + bool stringToBool(const char* string, bool defValue); + void stringToLower(char* string); + int getApplicationFullPath(char* path, int sizeOfPath); + char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); + int writeToDisk(const char* path, const char* file); + int appendToDisk(const char* path, const char* file); + int copyFile(const char* srcFile, const char* dstFile); + const char* getFilePart(const char* path); + char* getPathPart(char* path); +private: +}; + +#include "mxml/mxml.h" +const char * mxmlWhitespaceCB(mxml_node_t *node, int where); +extern OlyUtility* util; + +#endif // OLY_UTILITY_H diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp new file mode 100644 index 00000000000..efff7532a0d --- /dev/null +++ b/tools/gator/daemon/Sender.cpp @@ -0,0 +1,110 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "Sender.h" +#include "Logging.h" +#include "SessionData.h" + +extern void handleException(); + +Sender::Sender(OlySocket* socket) { + mDataFile = NULL; + mDataSocket = NULL; + + // Set up the socket connection + if (socket) { + char streamline[64] = {0}; + mDataSocket = socket; + + // Receive magic sequence - can wait forever + // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+ + while (strcmp("STREAMLINE", streamline) != 0) { + if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) { + logg->logError(__FILE__, __LINE__, "Socket disconnected"); + handleException(); + } + } + + // Send magic sequence - must be done first, afterwhich error messages can be sent + char magic[] = {'G', 'A', 'T', 'O', 'R', '\n'}; + mDataSocket->send(magic, sizeof(magic)); + + gSessionData->mWaitingOnCommand = true; + logg->logMessage("Completed magic sequence"); + } + + pthread_mutex_init(&mSendMutex, NULL); +} + +Sender::~Sender() { + delete mDataSocket; + mDataSocket = NULL; + if (mDataFile) { + fclose(mDataFile); + } +} + +void Sender::createDataFile(char* apcDir) { + if (apcDir == NULL) { + return; + } + + mDataFileName = (char*)malloc(strlen(apcDir) + 12); + sprintf(mDataFileName, "%s/0000000000", apcDir); + mDataFile = fopen(mDataFileName, "wb"); + if (!mDataFile) { + logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName); + handleException(); + } +} + +void Sender::writeData(const char* data, int length, int type) { + if (length < 0 || (data == NULL && length > 0)) { + return; + } + + // Multiple threads call writeData() + pthread_mutex_lock(&mSendMutex); + + // Send data over the socket connection + if (mDataSocket) { + // Start alarm + alarm(8); + + // Send data over the socket, sending the type and size first + logg->logMessage("Sending data with length %d", length); + if (type != RESPONSE_APC_DATA) { + // type and length already added by the Collector for apc data + mDataSocket->send((char*)&type, 1); + mDataSocket->send((char*)&length, sizeof(length)); + } + mDataSocket->send((char*)data, length); + + // Stop alarm + alarm(0); + } + + // Write data to disk as long as it is not meta data + if (mDataFile && type == RESPONSE_APC_DATA) { + logg->logMessage("Writing data with length %d", length); + // Send data to the data file + if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) { + logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName); + handleException(); + } + } + + pthread_mutex_unlock(&mSendMutex); +} diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h new file mode 100644 index 00000000000..98467e025c1 --- /dev/null +++ b/tools/gator/daemon/Sender.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __SENDER_H__ +#define __SENDER_H__ + +#include +#include +#include "OlySocket.h" + +enum { + RESPONSE_END = 0, // unused + RESPONSE_XML = 1, + RESPONSE_APC_DATA = 3, + RESPONSE_ACK = 4, + RESPONSE_NAK = 5, + RESPONSE_ERROR = 0xFF +}; + +class Sender { +public: + Sender(OlySocket* socket); + ~Sender(); + void writeData(const char* data, int length, int type); + void createDataFile(char* apcDir); +private: + OlySocket* mDataSocket; + FILE* mDataFile; + char* mDataFileName; + pthread_mutex_t mSendMutex; +}; + +#endif //__SENDER_H__ diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp new file mode 100644 index 00000000000..a245369a37a --- /dev/null +++ b/tools/gator/daemon/SessionData.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "SessionData.h" +#include "SessionXML.h" +#include "Logging.h" +extern void handleException(); + +SessionData* gSessionData = NULL; + +SessionData::SessionData() { + initialize(); +} + +SessionData::~SessionData() { +} + +void SessionData::initialize() { + mWaitingOnCommand = false; + mSessionIsActive = false; + mLocalCapture = false; + mOneShot = false; + strcpy(mCoreName, "unknown"); + mConfigurationXMLPath = NULL; + mSessionXMLPath = NULL; + mEventsXMLPath = NULL; + mAPCDir = NULL; + mSampleRate = 0; + mDuration = 0; + mBytes = 0; + mBacktraceDepth = 0; + mTotalBufferSize = 0; + mCores = 1; + + initializeCounters(); +} + +void SessionData::initializeCounters() { + // PMU Counters + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + mPerfCounterType[i][0] = 0; + mPerfCounterTitle[i][0] = 0; + mPerfCounterName[i][0] = 0; + mPerfCounterDescription[i][0] = 0; + mPerfCounterOperation[i][0] = 0; + mPerfCounterAlias[i][0] = 0; + mPerfCounterDisplay[i][0] = 0; + mPerfCounterUnits[i][0] = 0; + mPerfCounterEnabled[i] = 0; + mPerfCounterEvent[i] = 0; + mPerfCounterColor[i] = 0; + mPerfCounterKey[i] = 0; + mPerfCounterCount[i] = 0; + mPerfCounterPerCPU[i] = false; + mPerfCounterEBSCapable[i] = false; + mPerfCounterLevel[i] = false; + mPerfCounterAverageSelection[i] = false; + } +} + +void SessionData::parseSessionXML(char* xmlString) { + SessionXML session(xmlString); + session.parse(); + + // Parameter error checking + if (session.parameters.output_path == 0 && session.parameters.target_path == 0) { + logg->logError(__FILE__, __LINE__, "No capture path (target or host) was provided."); + handleException(); + } else if (gSessionData->mLocalCapture && session.parameters.target_path == 0) { + logg->logError(__FILE__, __LINE__, "Missing target_path tag in session xml required for a local capture."); + handleException(); + } + + // Set session data values + if (strcmp(session.parameters.sample_rate, "high") == 0) { + gSessionData->mSampleRate = 10000; + } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { + gSessionData->mSampleRate = 1000; + } else if (strcmp(session.parameters.sample_rate, "low") == 0) { + gSessionData->mSampleRate = 100; + } else { + gSessionData->mSampleRate = 0; + } + gSessionData->mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; + gSessionData->mDuration = session.parameters.duration; + + // Determine buffer size (in MB) based on buffer mode + gSessionData->mOneShot = true; + if (strcmp(session.parameters.buffer_mode, "streaming") == 0) { + gSessionData->mOneShot = false; + gSessionData->mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "small") == 0) { + gSessionData->mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) { + gSessionData->mTotalBufferSize = 4; + } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { + gSessionData->mTotalBufferSize = 16; + } else { + logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); + handleException(); + } + + gSessionData->mImages = session.parameters.images; + gSessionData->mTargetPath = session.parameters.target_path; + gSessionData->mTitle = session.parameters.title; +} diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h new file mode 100644 index 00000000000..7daee72a664 --- /dev/null +++ b/tools/gator/daemon/SessionData.h @@ -0,0 +1,75 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SESSION_DATA_H +#define SESSION_DATA_H + +#define MAX_PERFORMANCE_COUNTERS 50 +#define MAX_STRING_LEN 80 +#define MAX_DESCRIPTION_LEN 400 + +#define PROTOCOL_VERSION 9 +#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions + +struct ImageLinkList { + char* path; + struct ImageLinkList *next; +}; + +class SessionData { +public: + SessionData(); + ~SessionData(); + void initialize(); + void initializeCounters(); + void parseSessionXML(char* xmlString); + + char mCoreName[MAX_STRING_LEN]; + struct ImageLinkList *mImages; + char* mConfigurationXMLPath; + char* mSessionXMLPath; + char* mEventsXMLPath; + char* mTargetPath; + char* mAPCDir; + char* mTitle; + + bool mWaitingOnCommand; + bool mSessionIsActive; + bool mLocalCapture; + bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + + int mBacktraceDepth; + int mTotalBufferSize; // number of MB to use for the entire collection buffer + int mSampleRate; + int mDuration; + int mCores; + int mBytes; + + // PMU Counters + char mPerfCounterType[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterTitle[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterName[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterDescription[MAX_PERFORMANCE_COUNTERS][MAX_DESCRIPTION_LEN]; + char mPerfCounterOperation[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterAlias[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterDisplay[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterUnits[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + int mPerfCounterEnabled[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterEvent[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterColor[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterCount[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterKey[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterPerCPU[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterEBSCapable[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterLevel[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterAverageSelection[MAX_PERFORMANCE_COUNTERS]; +}; + +extern SessionData* gSessionData; + +#endif // SESSION_DATA_H diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp new file mode 100644 index 00000000000..f1a8258b525 --- /dev/null +++ b/tools/gator/daemon/SessionXML.cpp @@ -0,0 +1,142 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "SessionXML.h" +#include "Logging.h" +#include "OlyUtility.h" + +extern void handleException(); + +static const char* TAG_SESSION = "session"; +static const char* TAG_IMAGE = "image"; + +static const char* ATTR_VERSION = "version"; +static const char* ATTR_TITLE = "title"; +static const char* ATTR_UUID = "uuid"; +static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding"; +static const char* ATTR_BUFFER_MODE = "buffer_mode"; +static const char* ATTR_SAMPLE_RATE = "sample_rate"; +static const char* ATTR_TARGET_PATH = "target_path"; +static const char* ATTR_OUTPUT_PATH = "output_path"; +static const char* ATTR_DURATION = "duration"; +static const char* ATTR_PATH = "path"; + +SessionXML::SessionXML(const char* str) { + parameters.title = 0; + parameters.uuid[0] = 0; + parameters.target_path = 0; + parameters.output_path = 0; + parameters.buffer_mode[0] = 0; + parameters.sample_rate[0] = 0; + parameters.duration = 0; + parameters.call_stack_unwinding = false; + parameters.images = NULL; + mPath = 0; + mSessionXML = (char*)str; + logg->logMessage(mSessionXML); +} + +SessionXML::~SessionXML() { + if (mPath != 0) { + free(mSessionXML); + } +} + +void SessionXML::parse() { + mxml_node_t *tree; + mxml_node_t *node; + + tree = mxmlLoadString(NULL, mSessionXML, MXML_NO_CALLBACK); + node = mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND); + + if (node) { + sessionTag(tree, node); + mxmlDelete(tree); + return; + } + + logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file"); + handleException(); +} + +void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { + int version = 0; + if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10); + if (version != 1) { + logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version); + handleException(); + } + + // allocate strings + if (mxmlElementGetAttr(node, ATTR_TITLE)) { + parameters.title = strdup(mxmlElementGetAttr(node, ATTR_TITLE)); // freed when the child process exits + if (parameters.title == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.title"); + handleException(); + } + } + if (mxmlElementGetAttr(node, ATTR_TARGET_PATH)) { + parameters.target_path = strdup(mxmlElementGetAttr(node, ATTR_TARGET_PATH)); // freed when the child process exits + if (parameters.target_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.target_path"); + handleException(); + } + } + if (mxmlElementGetAttr(node, ATTR_OUTPUT_PATH)) { + parameters.output_path = strdup(mxmlElementGetAttr(node, ATTR_OUTPUT_PATH)); // freed when the child process exits + if (parameters.output_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.output_path"); + handleException(); + } + } + + // copy to pre-allocated strings + if (mxmlElementGetAttr(node, ATTR_UUID)) { + strncpy(parameters.uuid, mxmlElementGetAttr(node, ATTR_UUID), sizeof(parameters.uuid)); + parameters.uuid[sizeof(parameters.uuid) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + if (mxmlElementGetAttr(node, ATTR_BUFFER_MODE)) { + strncpy(parameters.buffer_mode, mxmlElementGetAttr(node, ATTR_BUFFER_MODE), sizeof(parameters.buffer_mode)); + parameters.buffer_mode[sizeof(parameters.buffer_mode) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + if (mxmlElementGetAttr(node, ATTR_SAMPLE_RATE)) { + strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate)); + parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // integers/bools + parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false); + if (mxmlElementGetAttr(node, ATTR_DURATION)) parameters.duration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10); + + // parse subtags + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; + } + if (strcmp(TAG_IMAGE, mxmlGetElement(node)) == 0) { + sessionImage(node); + } + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + } +} + +void SessionXML::sessionImage(mxml_node_t *node) { + int length = strlen(mxmlElementGetAttr(node, ATTR_PATH)); + struct ImageLinkList *image; + + image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList)); + image->path = (char*)malloc(length + 1); + image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH)); + image->next = parameters.images; + parameters.images = image; +} diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h new file mode 100644 index 00000000000..c2b54894441 --- /dev/null +++ b/tools/gator/daemon/SessionXML.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SESSION_XML_H +#define SESSION_XML_H + +#include "mxml/mxml.h" +#include "SessionData.h" + +struct ConfigParameters { + char* title; // status title + char uuid[64]; // universal unique identifier + char* target_path; // target path of where to write to disk + char* output_path; // host path of where to write to disk + char buffer_mode[64]; // buffer mode, "streaming", "low", "normal", "high" defines oneshot and buffer size + char sample_rate[64]; // capture mode, "high", "normal", or "low" + int duration; // length of profile in seconds + bool call_stack_unwinding; // whether stack unwinding is performed + struct ImageLinkList *images; // linked list of image strings +}; + +class SessionXML { +public: + SessionXML(const char* str); + ~SessionXML(); + void parse(); + ConfigParameters parameters; +private: + char* mSessionXML; + char* mPath; + void sessionTag(mxml_node_t *tree, mxml_node_t *node); + void sessionImage(mxml_node_t *node); +}; + +#endif // SESSION_XML_H diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp new file mode 100644 index 00000000000..53729ab0f6e --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -0,0 +1,331 @@ +/** + * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Sender.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" +#include "CapturedXML.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" + +extern void handleException(); + +static const char* TAG_SESSION = "session"; +static const char* TAG_REQUEST = "request"; +static const char* TAG_CONFIGURATIONS = "configurations"; + +static const char* ATTR_PROTOCOL = "protocol"; +static const char* ATTR_EVENTS = "events"; +static const char* ATTR_CONFIGURATION = "configuration"; +static const char* ATTR_COUNTERS = "counters"; +static const char* ATTR_SESSION = "session"; +static const char* ATTR_CAPTURED = "captured"; +static const char* ATTR_DEFAULTS = "defaults"; + +StreamlineSetup::StreamlineSetup(OlySocket* s) { + bool ready = false; + char* data = NULL; + int type; + + mSocket = s; + mSessionXML = NULL; + + // Receive commands from Streamline (master) + while (!ready) { + // receive command over socket + gSessionData->mWaitingOnCommand = true; + data = readCommand(&type); + + // parse and handle data + switch (type) { + case COMMAND_REQUEST_XML: + handleRequest(data); + break; + case COMMAND_DELIVER_XML: + handleDeliver(data); + break; + case COMMAND_APC_START: + logg->logMessage("Received apc start request"); + ready = true; + break; + case COMMAND_APC_STOP: + logg->logMessage("Received apc stop request before apc start request"); + exit(0); + break; + case COMMAND_DISCONNECT: + logg->logMessage("Received disconnect command"); + exit(0); + break; + case COMMAND_PING: + logg->logMessage("Received ping command"); + sendData(NULL, 0, RESPONSE_ACK); + break; + default: + logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type); + handleException(); + } + + free(data); + } +} + +StreamlineSetup::~StreamlineSetup() { + if (mSessionXML) { + free(mSessionXML); + } +} + +char* StreamlineSetup::readCommand(int* command) { + char type; + char* data; + int response, length; + + // receive type + response = mSocket->receiveNBytes(&type, sizeof(type)); + + // After receiving a single byte, we are no longer waiting on a command + gSessionData->mWaitingOnCommand = false; + + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // receive length + response = mSocket->receiveNBytes((char*)&length, sizeof(length)); + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // add artificial limit + if ((length < 0) || length > 1024 * 1024) { + logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length); + handleException(); + } + + // allocate memory to contain the xml file, size of zero returns a zero size object + data = (char*)calloc(length + 1, 1); + if (data == NULL) { + logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml"); + handleException(); + } + + // receive data + response = mSocket->receiveNBytes(data, length); + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // null terminate the data for string parsing + if (length > 0) { + data[length] = 0; + } + + *command = type; + return data; +} + +void StreamlineSetup::handleRequest(char* xml) { + mxml_node_t *tree, *node; + + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_PROTOCOL, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_PROTOCOL), false)) { + sendProtocol(); + logg->logMessage("Sent protocol xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_EVENTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_EVENTS), false)) { + sendEvents(); + logg->logMessage("Sent events xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CONFIGURATION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CONFIGURATION), false)) { + sendConfiguration(); + logg->logMessage("Sent configuration xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_COUNTERS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_COUNTERS), false)) { + sendCounters(); + logg->logMessage("Sent counters xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_SESSION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_SESSION), false)) { + sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML); + logg->logMessage("Sent session xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CAPTURED, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CAPTURED), false)) { + CapturedXML capturedXML; + char* capturedText = capturedXML.getXML(); + sendData(capturedText, strlen(capturedText), RESPONSE_XML); + free(capturedText); + logg->logMessage("Sent captured xml response"); + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_DEFAULTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_DEFAULTS), false)) { + sendDefaults(); + logg->logMessage("Sent default configuration xml response"); + } else { + char error[] = "Unknown request"; + sendData(error, strlen(error), RESPONSE_NAK); + logg->logMessage("Received unknown request:\n%s", xml); + } + + mxmlDelete(tree); +} + +void StreamlineSetup::handleDeliver(char* xml) { + mxml_node_t *tree; + + // Determine xml type + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { + // Session XML + gSessionData->parseSessionXML(xml); + + // Save xml + mSessionXML = strdup(xml); + if (mSessionXML == NULL) { + logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1); + handleException(); + } + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received session xml"); + } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) { + // Configuration XML + writeConfiguration(xml); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received configuration xml"); + } else { + // Unknown XML + logg->logMessage("Received unknown XML delivery type"); + sendData(NULL, 0, RESPONSE_NAK); + } + + mxmlDelete(tree); +} + +void StreamlineSetup::sendData(const char* data, int length, int type) { + mSocket->send((char*)&type, 1); + mSocket->send((char*)&length, sizeof(length)); + mSocket->send((char*)data, length); +} + +void StreamlineSetup::sendProtocol() { + mxml_node_t *xml; + mxml_node_t *protocol; + + xml = mxmlNewXML("1.0"); + protocol = mxmlNewElement(xml, "protocol"); + mxmlElementSetAttrf(protocol, "version", "%d", PROTOCOL_VERSION); + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); +} + +void StreamlineSetup::sendEvents() { +#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len + char* path = (char*)malloc(PATH_MAX);; + char* buffer; + unsigned int size = 0; + + if (gSessionData->mEventsXMLPath) { + strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + } + buffer = util->readFromDisk(path, &size); + if (buffer == NULL) { + logg->logMessage("Unable to locate events.xml, using default"); + buffer = (char*)events_xml; + size = events_xml_len; + } + + sendData(buffer, size, RESPONSE_XML); + if (buffer != (char*)events_xml) { + free(buffer); + } + free(path); +} + +void StreamlineSetup::sendConfiguration() { + ConfigurationXML xml; + + const char* string = xml.getConfigurationXML(); + sendData(string, strlen(string), RESPONSE_XML); +} + +void StreamlineSetup::sendDefaults() { +#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len + // Send the config built into the binary + char* xml = (char*)configuration_xml; + unsigned int size = configuration_xml_len; + + // Artificial size restriction + if (size > 1024*1024) { + logg->logError(__FILE__, __LINE__, "Corrupt default configuration file"); + handleException(); + } + + sendData(xml, size, RESPONSE_XML); +} + +#include +void StreamlineSetup::sendCounters() { + struct dirent *ent; + mxml_node_t *xml; + mxml_node_t *counters; + mxml_node_t *counter; + + // counters.xml is simply a file listing of /dev/gator/events + DIR* dir = opendir("/dev/gator/events"); + if (dir == NULL) { + logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); + handleException(); + } + + xml = mxmlNewXML("1.0"); + counters = mxmlNewElement(xml, "counters"); + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + counter = mxmlNewElement(counters, "counter"); + mxmlElementSetAttr(counter, "name", ent->d_name); + } + closedir (dir); + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); +} + +void StreamlineSetup::writeConfiguration(char* xml) { + char* path = (char*)malloc(PATH_MAX); + + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + + if (util->writeToDisk(path, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path); + handleException(); + } + + // Re-populate gSessionData with the configuration, as it has now changed + new ConfigurationXML(); + free(path); +} diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h new file mode 100644 index 00000000000..c46ae0891a3 --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __STREAMLINE_SETUP_H__ +#define __STREAMLINE_SETUP_H__ + +#include "OlySocket.h" + +// Commands from Streamline +enum { + COMMAND_REQUEST_XML = 0, + COMMAND_DELIVER_XML = 1, + COMMAND_APC_START = 2, + COMMAND_APC_STOP = 3, + COMMAND_DISCONNECT = 4, + COMMAND_PING = 5 +}; + +class StreamlineSetup { +public: + StreamlineSetup(OlySocket *socket); + ~StreamlineSetup(); +private: + int mNumConnections; + OlySocket* mSocket; + char* mSessionXML; + + char* readCommand(int*); + void handleRequest(char* xml); + void handleDeliver(char* xml); + void sendData(const char* data, int length, int type); + void sendString(const char* string, int type) {sendData(string, strlen(string), type);} + void sendProtocol(); + void sendEvents(); + void sendConfiguration(); + void sendDefaults(); + void sendCounters(); + void writeConfiguration(char* xml); +}; + +#endif //__STREAMLINE_SETUP_H__ diff --git a/tools/gator/daemon/configuration.xml b/tools/gator/daemon/configuration.xml new file mode 100644 index 00000000000..9d083d55c24 --- /dev/null +++ b/tools/gator/daemon/configuration.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml new file mode 100644 index 00000000000..c9d188f81a1 --- /dev/null +++ b/tools/gator/daemon/events-ARM11.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ARM11MPCore.xml b/tools/gator/daemon/events-ARM11MPCore.xml new file mode 100644 index 00000000000..74a29e68032 --- /dev/null +++ b/tools/gator/daemon/events-ARM11MPCore.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml new file mode 100644 index 00000000000..74c7290dad0 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A15.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml new file mode 100644 index 00000000000..0a70a62123b --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A5.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml new file mode 100755 index 00000000000..1a55817b8f5 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A7.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml new file mode 100644 index 00000000000..02260ba311e --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A8.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml new file mode 100644 index 00000000000..363862ce3e4 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A9.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Krait-architected.xml b/tools/gator/daemon/events-Krait-architected.xml new file mode 100755 index 00000000000..cc3f4e56093 --- /dev/null +++ b/tools/gator/daemon/events-Krait-architected.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml new file mode 100644 index 00000000000..35592cf8448 --- /dev/null +++ b/tools/gator/daemon/events-L2C-310.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml new file mode 100644 index 00000000000..85ce63c1dd9 --- /dev/null +++ b/tools/gator/daemon/events-Linux.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-400.xml b/tools/gator/daemon/events-Mali-400.xml new file mode 100644 index 00000000000..895d0c893cc --- /dev/null +++ b/tools/gator/daemon/events-Mali-400.xml @@ -0,0 +1,397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Scorpion.xml b/tools/gator/daemon/events-Scorpion.xml new file mode 100644 index 00000000000..6c2b6f8c9f7 --- /dev/null +++ b/tools/gator/daemon/events-Scorpion.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ScorpionMP.xml b/tools/gator/daemon/events-ScorpionMP.xml new file mode 100644 index 00000000000..87f5657f796 --- /dev/null +++ b/tools/gator/daemon/events-ScorpionMP.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events_footer.xml b/tools/gator/daemon/events_footer.xml new file mode 100644 index 00000000000..cd2b44665ba --- /dev/null +++ b/tools/gator/daemon/events_footer.xml @@ -0,0 +1 @@ + diff --git a/tools/gator/daemon/events_header.xml b/tools/gator/daemon/events_header.xml new file mode 100644 index 00000000000..38ec4c03246 --- /dev/null +++ b/tools/gator/daemon/events_header.xml @@ -0,0 +1,2 @@ + + diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp new file mode 100644 index 00000000000..df6913b92cb --- /dev/null +++ b/tools/gator/daemon/main.cpp @@ -0,0 +1,335 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Child.h" +#include "SessionData.h" +#include "OlySocket.h" +#include "Logging.h" +#include "OlyUtility.h" + +#define DEBUG false + +extern Child* child; +extern void handleException(); +int shutdownFilesystem(); +static pthread_mutex_t numSessions_mutex; +static int numSessions = 0; +static OlySocket* socket = NULL; +static bool driverRunningAtStart = false; +static bool driverMountedAtStart = false; + +struct cmdline_t { + int port; + char* module; +}; + +void cleanUp() { + if (shutdownFilesystem() == -1) { + logg->logMessage("Error shutting down gator filesystem"); + } + delete socket; + delete util; + delete logg; +} + +// CTRL C Signal Handler +void handler(int signum) { + logg->logMessage("Received signal %d, gator daemon exiting", signum); + + // Case 1: both child and parent receive the signal + if (numSessions > 0) { + // Arbitrary sleep of 1 second to give time for the child to exit; + // if something bad happens, continue the shutdown process regardless + sleep(1); + } + + // Case 2: only the parent received the signal + if (numSessions > 0) { + // Kill child threads - the first signal exits gracefully + logg->logMessage("Killing process group as %d child was running when signal was received", numSessions); + kill(0, SIGINT); + + // Give time for the child to exit + sleep(1); + + if (numSessions > 0) { + // The second signal force kills the child + logg->logMessage("Force kill the child"); + kill(0, SIGINT); + // Again, sleep for 1 second + sleep(1); + + if (numSessions > 0) { + // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open + printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n"); + } + } + } + + cleanUp(); + exit(0); +} + +// Child exit Signal Handler +void child_exit(int signum) { + int status; + int pid = wait(&status); + if (pid != -1) { + pthread_mutex_lock(&numSessions_mutex); + numSessions--; + pthread_mutex_unlock(&numSessions_mutex); + logg->logMessage("Child process %d exited with status %d", pid, status); + } +} + +// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted +int mountGatorFS() { + // If already mounted, + if (access("/dev/gator/buffer", F_OK) == 0) { + return 0; + } + + // else, mount the filesystem + mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) { + return -1; + } else { + return 1; + } +} + +int setupFilesystem(char* module) { + int retval; + + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + + if (module) { + // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running + shutdownFilesystem(); + + // if still mounted + if (access("/dev/gator/buffer", F_OK) == 0) { + logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline"); + handleException(); + } + } + + retval = mountGatorFS(); + if (retval == 1) { + logg->logMessage("Driver already running at startup"); + driverRunningAtStart = true; + } else if (retval == 0) { + logg->logMessage("Driver already mounted at startup"); + driverRunningAtStart = driverMountedAtStart = true; + } else { + char command[256]; // arbitrarily large amount + char location[256]; // arbitrarily large amount + + if (module) { + strncpy(location, module, sizeof(location)); + } else { + // Is the driver co-located in the same directory? + if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1); + } + + if (access(location, F_OK) == -1) { + logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); + handleException(); + } + + // Load driver + snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location); + if (system(command) != 0) { + logg->logMessage("Unable to load gator.ko driver with command: %s", command); + logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details"); + handleException(); + } + + if (mountGatorFS() == -1) { + logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling."); + handleException(); + } + } + + return 0; +} + +int shutdownFilesystem() { + if (driverMountedAtStart == false) { + umount("/dev/gator"); + } + if (driverRunningAtStart == false) { + if (system("rmmod gator >/dev/null 2>&1") != 0) { + return -1; + } + } + + return 0; // success +} + +struct cmdline_t parseCommandLine(int argc, char** argv) { + struct cmdline_t cmdline; + cmdline.port = 8080; + cmdline.module = NULL; + int c; + + while ((c = getopt(argc, argv, "hvp:s:c:e:m:")) != -1) { + switch(c) { + case 'c': + gSessionData->mConfigurationXMLPath = optarg; + break; + case 'e': + gSessionData->mEventsXMLPath = optarg; + break; + case 'm': + cmdline.module = optarg; + break; + case 'p': + cmdline.port = strtol(optarg, NULL, 10); + break; + case 's': + gSessionData->mSessionXMLPath = optarg; + break; + case 'h': + case '?': + logg->logError(__FILE__, __LINE__, + "Streamline gatord version %d. All parameters are optional:\n" + "-c config_xml\tpath and filename of the configuration.xml to use\n" + "-e events_xml\tpath and filename of the events.xml to use\n" + "-h\t\tthis help page\n" + "-m module\tpath and filename of gator.ko\n" + "-p port_number\tport upon which the server listens; default is 8080\n" + "-s session_xml\tpath and filename of a session xml used for local capture\n" + "-v\t\tversion information\n" + , PROTOCOL_VERSION); + handleException(); + break; + case 'v': + logg->logError(__FILE__, __LINE__, "Streamline gatord version %d", PROTOCOL_VERSION); + handleException(); + break; + } + } + + // Error checking + if (cmdline.port != 8080 && gSessionData->mSessionXMLPath != NULL) { + logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); + handleException(); + } + + if (optind < argc) { + logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]); + handleException(); + } + + return cmdline; +} + +// Gator data flow: collector -> collector fifo -> sender +int main(int argc, char** argv, char* envp[]) { + gSessionData = new SessionData(); // Global data class + logg = new Logging(DEBUG); // Set up global thread-safe logging + util = new OlyUtility(); // Set up global utility class + + prctl(PR_SET_NAME, (unsigned int)&"gatord-main", 0, 0, 0); + pthread_mutex_init(&numSessions_mutex, NULL); + + signal(SIGINT, handler); + signal(SIGTERM, handler); + signal(SIGABRT, handler); + + // Set to high priority + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) { + logg->logMessage("setpriority() failed"); + } + + // Initialize session data + gSessionData->initialize(); + + // Parse the command line parameters + struct cmdline_t cmdline = parseCommandLine(argc, argv); + + // Call before setting up the SIGCHLD handler, as system() spawns child processes + setupFilesystem(cmdline.module); + + // Handle child exit codes + signal(SIGCHLD, child_exit); + + // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal + // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler + signal(SIGPIPE, SIG_IGN); + + // If the command line argument is a session xml file, no need to open a socket + if (gSessionData->mSessionXMLPath) { + child = new Child(); + child->run(); + delete child; + } else { + socket = new OlySocket(cmdline.port, true); + // Forever loop, can be exited via a signal or exception + while (1) { + logg->logMessage("Waiting on connection..."); + socket->acceptConnection(); + + int pid = fork(); + if (pid < 0) { + // Error + logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists."); + } else if (pid == 0) { + // Child + socket->closeServerSocket(); + child = new Child(socket, numSessions + 1); + child->run(); + delete child; + exit(0); + } else { + // Parent + socket->closeSocket(); + + pthread_mutex_lock(&numSessions_mutex); + numSessions++; + pthread_mutex_unlock(&numSessions_mutex); + + // Maximum number of connections is 2 + int wait = 0; + while (numSessions > 1) { + // Throttle until one of the children exits before continuing to accept another socket connection + logg->logMessage("%d sessions active!", numSessions); + if (wait++ >= 10) { // Wait no more than 10 seconds + // Kill last created child + kill(pid, SIGALRM); + break; + } + sleep(1); + } + } + } + } + + cleanUp(); + return 0; +} diff --git a/tools/gator/daemon/mxml/COPYING b/tools/gator/daemon/mxml/COPYING new file mode 100644 index 00000000000..4d0aa78af22 --- /dev/null +++ b/tools/gator/daemon/mxml/COPYING @@ -0,0 +1,507 @@ + Mini-XML License + September 18, 2010 + + +The Mini-XML library and included programs are provided under the +terms of the GNU Library General Public License version 2 (LGPL2) +with the following exceptions: + + 1. Static linking of applications to the Mini-XML library +does not constitute a derivative work and does not require +the author to provide source code for the application, use +the shared Mini-XML libraries, or link their applications +against a user-supplied version of Mini-XML. + +If you link the application to a modified version of +Mini-XML, then the changes to Mini-XML must be provided +under the terms of the LGPL2 in sections 1, 2, and 4. + + 2. You do not have to provide a copy of the Mini-XML license +with programs that are linked to the Mini-XML library, nor +do you have to identify the Mini-XML license in your +program or documentation as required by section 6 of the +LGPL2. + + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 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 library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + 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 Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 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 a program 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. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + 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, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile 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) 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. + + c) 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. + + d) 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 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: 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 Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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! diff --git a/tools/gator/daemon/mxml/config.h b/tools/gator/daemon/mxml/config.h new file mode 100644 index 00000000000..1f59ba34a47 --- /dev/null +++ b/tools/gator/daemon/mxml/config.h @@ -0,0 +1,96 @@ +/* config.h. Generated from config.h.in by configure. */ +/* + * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $" + * + * Configuration file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include +#include +#include +#include +#include + + +/* + * Version number... + */ + +#define MXML_VERSION "Mini-XML v2.7" + + +/* + * Inline function support... + */ + +#define inline + + +/* + * Long long support... + */ + +#define HAVE_LONG_LONG 1 + + +/* + * Do we have the snprintf() and vsnprintf() functions? + */ + +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 + + +/* + * Do we have the strXXX() functions? + */ + +#define HAVE_STRDUP 1 + + +/* + * Do we have threading support? + */ + +#define HAVE_PTHREAD_H 1 + + +/* + * Define prototypes for string functions as needed... + */ + +# ifndef HAVE_STRDUP +extern char *_mxml_strdup(const char *); +# define strdup _mxml_strdup +# endif /* !HAVE_STRDUP */ + +extern char *_mxml_strdupf(const char *, ...); +extern char *_mxml_vstrdupf(const char *, va_list); + +# ifndef HAVE_SNPRINTF +extern int _mxml_snprintf(char *, size_t, const char *, ...); +# define snprintf _mxml_snprintf +# endif /* !HAVE_SNPRINTF */ + +# ifndef HAVE_VSNPRINTF +extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); +# define vsnprintf _mxml_vsnprintf +# endif /* !HAVE_VSNPRINTF */ + +/* + * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-attr.c b/tools/gator/daemon/mxml/mxml-attr.c new file mode 100644 index 00000000000..c9950f5fb73 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-attr.c @@ -0,0 +1,319 @@ +/* + * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $" + * + * Attribute support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlElementDeleteAttr() - Delete an attribute. + * mxmlElementGetAttr() - Get an attribute. + * mxmlElementSetAttr() - Set an attribute. + * mxmlElementSetAttrf() - Set an attribute with a formatted value. + * mxml_set_attr() - Set or add an attribute name/value pair. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static int mxml_set_attr(mxml_node_t *node, const char *name, + char *value); + + +/* + * 'mxmlElementDeleteAttr()' - Delete an attribute. + * + * @since Mini-XML 2.4@ + */ + +void +mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ + const char *name)/* I - Attribute name */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { + /* + * Delete this attribute... + */ + + free(attr->name); + free(attr->value); + + i --; + if (i > 0) + memmove(attr, attr + 1, i * sizeof(mxml_attr_t)); + + node->value.element.num_attrs --; + return; + } + } +} + + +/* + * 'mxmlElementGetAttr()' - Get an attribute. + * + * This function returns NULL if the node is not an element or the + * named attribute does not exist. + */ + +const char * /* O - Attribute value or NULL */ +mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ + const char *name) /* I - Name of attribute */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (NULL); + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { +#ifdef DEBUG + printf(" Returning \"%s\"!\n", attr->value); +#endif /* DEBUG */ + return (attr->value); + } + } + + /* + * Didn't find attribute, so return NULL... + */ + +#ifdef DEBUG + puts(" Returning NULL!\n"); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlElementSetAttr()' - Set an attribute. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new string value. The string value is copied + * into the element node. This function does nothing if the node is + * not an element. + */ + +void +mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *value) /* I - Attribute value */ +{ + char *valuec; /* Copy of value */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", + node, name ? name : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + if (value) + valuec = strdup(value); + else + valuec = NULL; + + if (mxml_set_attr(node, name, valuec)) + free(valuec); +} + + +/* + * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new formatted string. The formatted string value is + * copied into the element node. This function does nothing if the node + * is not an element. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *format,/* I - Printf-style attribute value */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument pointer */ + char *value; /* Value */ + + +#ifdef DEBUG + fprintf(stderr, + "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", + node, name ? name : "(null)", format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name || !format) + return; + + /* + * Format the value... + */ + + va_start(ap, format); + value = _mxml_vstrdupf(format, ap); + va_end(ap); + + if (!value) + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + else if (mxml_set_attr(node, name, value)) + free(value); +} + + +/* + * 'mxml_set_attr()' - Set or add an attribute name/value pair. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_set_attr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Attribute name */ + char *value) /* I - Attribute value */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* New attribute */ + + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + if (!strcmp(attr->name, name)) + { + /* + * Free the old value as needed... + */ + + if (attr->value) + free(attr->value); + + attr->value = value; + + return (0); + } + + /* + * Add a new attribute... + */ + + if (node->value.element.num_attrs == 0) + attr = malloc(sizeof(mxml_attr_t)); + else + attr = realloc(node->value.element.attrs, + (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t)); + + if (!attr) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + node->value.element.attrs = attr; + attr += node->value.element.num_attrs; + + if ((attr->name = strdup(name)) == NULL) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + attr->value = value; + + node->value.element.num_attrs ++; + + return (0); +} + + +/* + * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-entity.c b/tools/gator/daemon/mxml/mxml-entity.c new file mode 100644 index 00000000000..c5c9f61f73c --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-entity.c @@ -0,0 +1,460 @@ +/* + * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $" + * + * Character entity support code for Mini-XML, a small XML-like + * file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlEntityAddCallback() - Add a callback to convert entities to + * Unicode. + * mxmlEntityGetName() - Get the name that corresponds to the + * character value. + * mxmlEntityGetValue() - Get the character corresponding to a named + * entity. + * mxmlEntityRemoveCallback() - Remove a callback. + * _mxml_entity_cb() - Lookup standard (X)HTML entities. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlEntityAddCallback( + mxml_entity_cb_t cb) /* I - Callback function to add */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) + { + global->entity_cbs[global->num_entity_cbs] = cb; + global->num_entity_cbs ++; + + return (0); + } + else + { + mxml_error("Unable to add entity callback!"); + + return (-1); + } +} + + +/* + * 'mxmlEntityGetName()' - Get the name that corresponds to the character value. + * + * If val does not need to be represented by a named entity, NULL is returned. + */ + +const char * /* O - Entity name or NULL */ +mxmlEntityGetName(int val) /* I - Character value */ +{ + switch (val) + { + case '&' : + return ("amp"); + + case '<' : + return ("lt"); + + case '>' : + return ("gt"); + + case '\"' : + return ("quot"); + + default : + return (NULL); + } +} + + +/* + * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity. + * + * The entity name can also be a numeric constant. -1 is returned if the + * name is not known. + */ + +int /* O - Character value or -1 on error */ +mxmlEntityGetValue(const char *name) /* I - Entity name */ +{ + int i; /* Looping var */ + int ch; /* Character value */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if ((ch = (global->entity_cbs[i])(name)) >= 0) + return (ch); + + return (-1); +} + + +/* + * 'mxmlEntityRemoveCallback()' - Remove a callback. + */ + +void +mxmlEntityRemoveCallback( + mxml_entity_cb_t cb) /* I - Callback function to remove */ +{ + int i; /* Looping var */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if (cb == global->entity_cbs[i]) + { + /* + * Remove the callback... + */ + + global->num_entity_cbs --; + + if (i < global->num_entity_cbs) + memmove(global->entity_cbs + i, global->entity_cbs + i + 1, + (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); + + return; + } +} + + +/* + * '_mxml_entity_cb()' - Lookup standard (X)HTML entities. + */ + +int /* O - Unicode value or -1 */ +_mxml_entity_cb(const char *name) /* I - Entity name */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + static const struct + { + const char *name; /* Entity name */ + int val; /* Character value */ + } entities[] = + { + { "AElig", 198 }, + { "Aacute", 193 }, + { "Acirc", 194 }, + { "Agrave", 192 }, + { "Alpha", 913 }, + { "Aring", 197 }, + { "Atilde", 195 }, + { "Auml", 196 }, + { "Beta", 914 }, + { "Ccedil", 199 }, + { "Chi", 935 }, + { "Dagger", 8225 }, + { "Delta", 916 }, + { "Dstrok", 208 }, + { "ETH", 208 }, + { "Eacute", 201 }, + { "Ecirc", 202 }, + { "Egrave", 200 }, + { "Epsilon", 917 }, + { "Eta", 919 }, + { "Euml", 203 }, + { "Gamma", 915 }, + { "Iacute", 205 }, + { "Icirc", 206 }, + { "Igrave", 204 }, + { "Iota", 921 }, + { "Iuml", 207 }, + { "Kappa", 922 }, + { "Lambda", 923 }, + { "Mu", 924 }, + { "Ntilde", 209 }, + { "Nu", 925 }, + { "OElig", 338 }, + { "Oacute", 211 }, + { "Ocirc", 212 }, + { "Ograve", 210 }, + { "Omega", 937 }, + { "Omicron", 927 }, + { "Oslash", 216 }, + { "Otilde", 213 }, + { "Ouml", 214 }, + { "Phi", 934 }, + { "Pi", 928 }, + { "Prime", 8243 }, + { "Psi", 936 }, + { "Rho", 929 }, + { "Scaron", 352 }, + { "Sigma", 931 }, + { "THORN", 222 }, + { "Tau", 932 }, + { "Theta", 920 }, + { "Uacute", 218 }, + { "Ucirc", 219 }, + { "Ugrave", 217 }, + { "Upsilon", 933 }, + { "Uuml", 220 }, + { "Xi", 926 }, + { "Yacute", 221 }, + { "Yuml", 376 }, + { "Zeta", 918 }, + { "aacute", 225 }, + { "acirc", 226 }, + { "acute", 180 }, + { "aelig", 230 }, + { "agrave", 224 }, + { "alefsym", 8501 }, + { "alpha", 945 }, + { "amp", '&' }, + { "and", 8743 }, + { "ang", 8736 }, + { "apos", '\'' }, + { "aring", 229 }, + { "asymp", 8776 }, + { "atilde", 227 }, + { "auml", 228 }, + { "bdquo", 8222 }, + { "beta", 946 }, + { "brkbar", 166 }, + { "brvbar", 166 }, + { "bull", 8226 }, + { "cap", 8745 }, + { "ccedil", 231 }, + { "cedil", 184 }, + { "cent", 162 }, + { "chi", 967 }, + { "circ", 710 }, + { "clubs", 9827 }, + { "cong", 8773 }, + { "copy", 169 }, + { "crarr", 8629 }, + { "cup", 8746 }, + { "curren", 164 }, + { "dArr", 8659 }, + { "dagger", 8224 }, + { "darr", 8595 }, + { "deg", 176 }, + { "delta", 948 }, + { "diams", 9830 }, + { "die", 168 }, + { "divide", 247 }, + { "eacute", 233 }, + { "ecirc", 234 }, + { "egrave", 232 }, + { "empty", 8709 }, + { "emsp", 8195 }, + { "ensp", 8194 }, + { "epsilon", 949 }, + { "equiv", 8801 }, + { "eta", 951 }, + { "eth", 240 }, + { "euml", 235 }, + { "euro", 8364 }, + { "exist", 8707 }, + { "fnof", 402 }, + { "forall", 8704 }, + { "frac12", 189 }, + { "frac14", 188 }, + { "frac34", 190 }, + { "frasl", 8260 }, + { "gamma", 947 }, + { "ge", 8805 }, + { "gt", '>' }, + { "hArr", 8660 }, + { "harr", 8596 }, + { "hearts", 9829 }, + { "hellip", 8230 }, + { "hibar", 175 }, + { "iacute", 237 }, + { "icirc", 238 }, + { "iexcl", 161 }, + { "igrave", 236 }, + { "image", 8465 }, + { "infin", 8734 }, + { "int", 8747 }, + { "iota", 953 }, + { "iquest", 191 }, + { "isin", 8712 }, + { "iuml", 239 }, + { "kappa", 954 }, + { "lArr", 8656 }, + { "lambda", 955 }, + { "lang", 9001 }, + { "laquo", 171 }, + { "larr", 8592 }, + { "lceil", 8968 }, + { "ldquo", 8220 }, + { "le", 8804 }, + { "lfloor", 8970 }, + { "lowast", 8727 }, + { "loz", 9674 }, + { "lrm", 8206 }, + { "lsaquo", 8249 }, + { "lsquo", 8216 }, + { "lt", '<' }, + { "macr", 175 }, + { "mdash", 8212 }, + { "micro", 181 }, + { "middot", 183 }, + { "minus", 8722 }, + { "mu", 956 }, + { "nabla", 8711 }, + { "nbsp", 160 }, + { "ndash", 8211 }, + { "ne", 8800 }, + { "ni", 8715 }, + { "not", 172 }, + { "notin", 8713 }, + { "nsub", 8836 }, + { "ntilde", 241 }, + { "nu", 957 }, + { "oacute", 243 }, + { "ocirc", 244 }, + { "oelig", 339 }, + { "ograve", 242 }, + { "oline", 8254 }, + { "omega", 969 }, + { "omicron", 959 }, + { "oplus", 8853 }, + { "or", 8744 }, + { "ordf", 170 }, + { "ordm", 186 }, + { "oslash", 248 }, + { "otilde", 245 }, + { "otimes", 8855 }, + { "ouml", 246 }, + { "para", 182 }, + { "part", 8706 }, + { "permil", 8240 }, + { "perp", 8869 }, + { "phi", 966 }, + { "pi", 960 }, + { "piv", 982 }, + { "plusmn", 177 }, + { "pound", 163 }, + { "prime", 8242 }, + { "prod", 8719 }, + { "prop", 8733 }, + { "psi", 968 }, + { "quot", '\"' }, + { "rArr", 8658 }, + { "radic", 8730 }, + { "rang", 9002 }, + { "raquo", 187 }, + { "rarr", 8594 }, + { "rceil", 8969 }, + { "rdquo", 8221 }, + { "real", 8476 }, + { "reg", 174 }, + { "rfloor", 8971 }, + { "rho", 961 }, + { "rlm", 8207 }, + { "rsaquo", 8250 }, + { "rsquo", 8217 }, + { "sbquo", 8218 }, + { "scaron", 353 }, + { "sdot", 8901 }, + { "sect", 167 }, + { "shy", 173 }, + { "sigma", 963 }, + { "sigmaf", 962 }, + { "sim", 8764 }, + { "spades", 9824 }, + { "sub", 8834 }, + { "sube", 8838 }, + { "sum", 8721 }, + { "sup", 8835 }, + { "sup1", 185 }, + { "sup2", 178 }, + { "sup3", 179 }, + { "supe", 8839 }, + { "szlig", 223 }, + { "tau", 964 }, + { "there4", 8756 }, + { "theta", 952 }, + { "thetasym", 977 }, + { "thinsp", 8201 }, + { "thorn", 254 }, + { "tilde", 732 }, + { "times", 215 }, + { "trade", 8482 }, + { "uArr", 8657 }, + { "uacute", 250 }, + { "uarr", 8593 }, + { "ucirc", 251 }, + { "ugrave", 249 }, + { "uml", 168 }, + { "upsih", 978 }, + { "upsilon", 965 }, + { "uuml", 252 }, + { "weierp", 8472 }, + { "xi", 958 }, + { "yacute", 253 }, + { "yen", 165 }, + { "yuml", 255 }, + { "zeta", 950 }, + { "zwj", 8205 }, + { "zwnj", 8204 } + }; + + + /* + * Do a binary search for the named entity... + */ + + first = 0; + last = (int)(sizeof(entities) / sizeof(entities[0]) - 1); + + while ((last - first) > 1) + { + current = (first + last) / 2; + + if ((diff = strcmp(name, entities[current].name)) == 0) + return (entities[current].val); + else if (diff < 0) + last = current; + else + first = current; + } + + /* + * If we get here, there is a small chance that there is still + * a match; check first and last... + */ + + if (!strcmp(name, entities[first].name)) + return (entities[first].val); + else if (!strcmp(name, entities[last].name)) + return (entities[last].val); + else + return (-1); +} + + +/* + * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-file.c b/tools/gator/daemon/mxml/mxml-file.c new file mode 100644 index 00000000000..992704037b0 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-file.c @@ -0,0 +1,3080 @@ +/* + * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $" + * + * File loading code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlLoadFd() - Load a file descriptor into an XML node tree. + * mxmlLoadFile() - Load a file into an XML node tree. + * mxmlLoadString() - Load a string into an XML node tree. + * mxmlSaveAllocString() - Save an XML tree to an allocated string. + * mxmlSaveFd() - Save an XML tree to a file descriptor. + * mxmlSaveFile() - Save an XML tree to a file. + * mxmlSaveString() - Save an XML node tree to a string. + * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree + * using a SAX callback. + * mxmlSAXLoadFile() - Load a file into an XML node tree + * using a SAX callback. + * mxmlSAXLoadString() - Load a string into an XML node tree + * using a SAX callback. + * mxmlSetCustomHandlers() - Set the handling functions for custom data. + * mxmlSetErrorCallback() - Set the error message callback. + * mxmlSetWrapMargin() - Set the wrap margin when saving XML data. + * mxml_add_char() - Add a character to a buffer, expanding as needed. + * mxml_fd_getc() - Read a character from a file descriptor. + * mxml_fd_putc() - Write a character to a file descriptor. + * mxml_fd_read() - Read a buffer of data from a file descriptor. + * mxml_fd_write() - Write a buffer of data to a file descriptor. + * mxml_file_getc() - Get a character from a file. + * mxml_file_putc() - Write a character to a file. + * mxml_get_entity() - Get the character corresponding to an entity... + * mxml_load_data() - Load data into an XML node tree. + * mxml_parse_element() - Parse an element for any attributes... + * mxml_string_getc() - Get a character from a string. + * mxml_string_putc() - Write a character to a string. + * mxml_write_name() - Write a name string. + * mxml_write_node() - Save an XML node to a file. + * mxml_write_string() - Write a string, escaping & and < as needed. + * mxml_write_ws() - Do whitespace callback... + */ + +/* + * Include necessary headers... + */ + +#ifndef WIN32 +# include +#endif /* !WIN32 */ +#include "mxml-private.h" + + +/* + * Character encoding... + */ + +#define ENCODE_UTF8 0 /* UTF-8 */ +#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */ +#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */ + + +/* + * Macro to test for a bad XML character... + */ + +#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t') + + +/* + * Types and structures... + */ + +typedef int (*_mxml_getc_cb_t)(void *, int *); +typedef int (*_mxml_putc_cb_t)(int, void *); + +typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ +{ + int fd; /* File descriptor */ + unsigned char *current, /* Current position in buffer */ + *end, /* End of buffer */ + buffer[8192]; /* Character buffer */ +} _mxml_fdbuf_t; + + +/* + * Local functions... + */ + +static int mxml_add_char(int ch, char **ptr, char **buffer, + int *bufsize); +static int mxml_fd_getc(void *p, int *encoding); +static int mxml_fd_putc(int ch, void *p); +static int mxml_fd_read(_mxml_fdbuf_t *buf); +static int mxml_fd_write(_mxml_fdbuf_t *buf); +static int mxml_file_getc(void *p, int *encoding); +static int mxml_file_putc(int ch, void *p); +static int mxml_get_entity(mxml_node_t *parent, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static inline int mxml_isspace(int ch) + { + return (ch == ' ' || ch == '\t' || ch == '\r' || + ch == '\n'); + } +static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, + mxml_load_cb_t cb, + _mxml_getc_cb_t getc_cb, + mxml_sax_cb_t sax_cb, void *sax_data); +static int mxml_parse_element(mxml_node_t *node, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static int mxml_string_getc(void *p, int *encoding); +static int mxml_string_putc(int ch, void *p); +static int mxml_write_name(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_node(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int col, + _mxml_putc_cb_t putc_cb, + _mxml_global_t *global); +static int mxml_write_string(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_ws(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int ws, + int col, _mxml_putc_cb_t putc_cb); + + +/* + * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadFile()' - Load a file into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFile(mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadString()' - Load a string into an XML node tree. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlLoadString(mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK, + NULL)); +} + + +/* + * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string. + * + * This function returns a pointer to a string containing the textual + * representation of the XML node tree. The string should be freed + * using the free() function when you are done with it. NULL is returned + * if the node would produce an empty string or if the string cannot be + * allocated. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +char * /* O - Allocated string or NULL */ +mxmlSaveAllocString( + mxml_node_t *node, /* I - Node to write */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int bytes; /* Required bytes */ + char buffer[8192]; /* Temporary buffer */ + char *s; /* Allocated string */ + + + /* + * Write the node to the temporary buffer... + */ + + bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); + + if (bytes <= 0) + return (NULL); + + if (bytes < (int)(sizeof(buffer) - 1)) + { + /* + * Node fit inside the buffer, so just duplicate that string and + * return... + */ + + return (strdup(buffer)); + } + + /* + * Allocate a buffer of the required size and save the node to the + * new buffer... + */ + + if ((s = malloc(bytes + 1)) == NULL) + return (NULL); + + mxmlSaveString(node, s, bytes + 1, cb); + + /* + * Return the allocated string... + */ + + return (s); +} + + +/* + * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ + int fd, /* I - File descriptor to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer + sizeof(buf.buffer); + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0) + return (-1); + + if (col > 0) + if (mxml_fd_putc('\n', &buf) < 0) + return (-1); + + /* + * Flush and return... + */ + + return (mxml_fd_write(&buf)); +} + + +/* + * 'mxmlSaveFile()' - Save an XML tree to a file. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ + FILE *fp, /* I - File to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0) + return (-1); + + if (col > 0) + if (putc('\n', fp) < 0) + return (-1); + + /* + * Return 0 (success)... + */ + + return (0); +} + + +/* + * 'mxmlSaveString()' - Save an XML node tree to a string. + * + * This function returns the total number of bytes that would be + * required for the string but only copies (bufsize - 1) characters + * into the specified buffer. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - Size of string */ +mxmlSaveString(mxml_node_t *node, /* I - Node to write */ + char *buffer, /* I - String buffer */ + int bufsize, /* I - Size of string buffer */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + char *ptr[2]; /* Pointers for putc_cb */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + ptr[0] = buffer; + ptr[1] = buffer + bufsize; + + if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0) + return (-1); + + if (col > 0) + mxml_string_putc('\n', ptr); + + /* + * Nul-terminate the buffer... + */ + + if (ptr[0] >= ptr[1]) + buffer[bufsize - 1] = '\0'; + else + ptr[0][0] = '\0'; + + /* + * Return the number of characters... + */ + + return (ptr[0] - buffer); +} + + +/* + * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadFile()' - Load a file into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFile( + mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadString()' - Load a string into an XML node tree + * using a SAX callback. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlSAXLoadString( + mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. + * + * The load function accepts a node pointer and a data string and must + * return 0 on success and non-zero on error. + * + * The save function accepts a node pointer and must return a malloc'd + * string on success and NULL on error. + * + */ + +void +mxmlSetCustomHandlers( + mxml_custom_load_cb_t load, /* I - Load function */ + mxml_custom_save_cb_t save) /* I - Save function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->custom_load_cb = load; + global->custom_save_cb = save; +} + + +/* + * 'mxmlSetErrorCallback()' - Set the error message callback. + */ + +void +mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->error_cb = cb; +} + + +/* + * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data. + * + * Wrapping is disabled when "column" is 0. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->wrap = column; +} + + +/* + * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_add_char(int ch, /* I - Character to add */ + char **bufptr, /* IO - Current position in buffer */ + char **buffer, /* IO - Current buffer */ + int *bufsize) /* IO - Current buffer size */ +{ + char *newbuffer; /* New buffer value */ + + + if (*bufptr >= (*buffer + *bufsize - 4)) + { + /* + * Increase the size of the buffer... + */ + + if (*bufsize < 1024) + (*bufsize) *= 2; + else + (*bufsize) += 1024; + + if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) + { + free(*buffer); + + mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); + + return (-1); + } + + *bufptr = newbuffer + (*bufptr - *buffer); + *buffer = newbuffer; + } + + if (ch < 0x80) + { + /* + * Single byte ASCII... + */ + + *(*bufptr)++ = ch; + } + else if (ch < 0x800) + { + /* + * Two-byte UTF-8... + */ + + *(*bufptr)++ = 0xc0 | (ch >> 6); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else if (ch < 0x10000) + { + /* + * Three-byte UTF-8... + */ + + *(*bufptr)++ = 0xe0 | (ch >> 12); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else + { + /* + * Four-byte UTF-8... + */ + + *(*bufptr)++ = 0xf0 | (ch >> 18); + *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + + return (0); +} + + +/* + * 'mxml_fd_getc()' - Read a character from a file descriptor. + */ + +static int /* O - Character or EOF */ +mxml_fd_getc(void *p, /* I - File descriptor buffer */ + int *encoding) /* IO - Encoding */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + int ch, /* Current character */ + temp; /* Temporary character */ + + + /* + * Grab the next character in the buffer... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_fd_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch = (ch << 8) | temp; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch = (lch << 8) | temp; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch |= (temp << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch |= (temp << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_fd_putc()' - Write a character to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_putc(int ch, /* I - Character */ + void *p) /* I - File descriptor buffer */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + + + /* + * Flush the write buffer as needed... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_write(buf) < 0) + return (-1); + + *(buf->current)++ = ch; + + /* + * Return successfully... + */ + + return (0); +} + + +/* + * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes read... */ + + + /* + * Range check input... + */ + + if (!buf) + return (-1); + + /* + * Read from the file descriptor... + */ + + while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0) +#ifdef EINTR + if (errno != EAGAIN && errno != EINTR) +#else + if (errno != EAGAIN) +#endif /* EINTR */ + return (-1); + + if (bytes == 0) + return (-1); + + /* + * Update the pointers and return success... + */ + + buf->current = buf->buffer; + buf->end = buf->buffer + bytes; + + return (0); +} + + +/* + * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes written */ + unsigned char *ptr; /* Pointer into buffer */ + + + /* + * Range check... + */ + + if (!buf) + return (-1); + + /* + * Return 0 if there is nothing to write... + */ + + if (buf->current == buf->buffer) + return (0); + + /* + * Loop until we have written everything... + */ + + for (ptr = buf->buffer; ptr < buf->current; ptr += bytes) + if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0) + return (-1); + + /* + * All done, reset pointers and return success... + */ + + buf->current = buf->buffer; + + return (0); +} + + +/* + * 'mxml_file_getc()' - Get a character from a file. + */ + +static int /* O - Character or EOF */ +mxml_file_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch, /* Character from file */ + temp; /* Temporary character */ + FILE *fp; /* Pointer to file */ + + + /* + * Read a character from the file and see if it is EOF or ASCII... + */ + + fp = (FILE *)p; + ch = getc(fp); + + if (ch == EOF) + return (EOF); + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_file_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | getc(fp); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = (getc(fp) << 8) | getc(fp); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch |= (getc(fp) << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = getc(fp) | (getc(fp) << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_file_putc()' - Write a character to a file. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_file_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to file */ +{ + return (putc(ch, (FILE *)p) == EOF ? -1 : 0); +} + + +/* + * 'mxml_get_entity()' - Get the character corresponding to an entity... + */ + +static int /* O - Character value or EOF on error */ +mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ + void *p, /* I - Pointer to source */ + int *encoding, /* IO - Character encoding */ + int (*getc_cb)(void *, int *)) + /* I - Get character function */ +{ + int ch; /* Current character */ + char entity[64], /* Entity string */ + *entptr; /* Pointer into entity */ + + + entptr = entity; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch > 126 || (!isalnum(ch) && ch != '#')) + break; + else if (entptr < (entity + sizeof(entity) - 1)) + *entptr++ = ch; + else + { + mxml_error("Entity name too long under parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + *entptr = '\0'; + + if (ch != ';') + { + mxml_error("Character entity \"%s\" not terminated under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + return (EOF); + } + + if (entity[0] == '#') + { + if (entity[1] == 'x') + ch = strtol(entity + 2, NULL, 16); + else + ch = strtol(entity + 1, NULL, 10); + } + else if ((ch = mxmlEntityGetValue(entity)) < 0) + mxml_error("Entity name \"%s;\" not supported under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", + ch, parent ? parent->value.element.name : "null"); + return (EOF); + } + + return (ch); +} + + +/* + * 'mxml_load_data()' - Load data into an XML node tree. + */ + +static mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_load_data( + mxml_node_t *top, /* I - Top node */ + void *p, /* I - Pointer to data */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + _mxml_getc_cb_t getc_cb, /* I - Read function */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + mxml_node_t *node, /* Current node */ + *first, /* First node added */ + *parent; /* Current parent node */ + int ch, /* Character from file */ + whitespace; /* Non-zero if whitespace seen */ + char *buffer, /* String buffer */ + *bufptr; /* Pointer into buffer */ + int bufsize; /* Size of buffer */ + mxml_type_t type; /* Current node type */ + int encoding; /* Character encoding */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + static const char * const types[] = /* Type strings... */ + { + "MXML_ELEMENT", /* XML element with attributes */ + "MXML_INTEGER", /* Integer value */ + "MXML_OPAQUE", /* Opaque string */ + "MXML_REAL", /* Real value */ + "MXML_TEXT", /* Text fragment */ + "MXML_CUSTOM" /* Custom data */ + }; + + + /* + * Read elements and other nodes from the file... + */ + + if ((buffer = malloc(64)) == NULL) + { + mxml_error("Unable to allocate string buffer!"); + return (NULL); + } + + bufsize = 64; + bufptr = buffer; + parent = top; + first = NULL; + whitespace = 0; + encoding = ENCODE_UTF8; + + if (cb && parent) + type = (*cb)(parent); + else + type = MXML_TEXT; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if ((ch == '<' || + (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) && + bufptr > buffer) + { + /* + * Add a new value node... + */ + + *bufptr = '\0'; + + switch (type) + { + case MXML_INTEGER : + node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); + break; + + case MXML_OPAQUE : + node = mxmlNewOpaque(parent, buffer); + break; + + case MXML_REAL : + node = mxmlNewReal(parent, strtod(buffer, &bufptr)); + break; + + case MXML_TEXT : + node = mxmlNewText(parent, whitespace, buffer); + break; + + case MXML_CUSTOM : + if (global->custom_load_cb) + { + /* + * Use the callback to fill in the custom data... + */ + + node = mxmlNewCustom(parent, NULL, NULL); + + if ((*global->custom_load_cb)(node, buffer)) + { + mxml_error("Bad custom value '%s' in parent <%s>!", + buffer, parent ? parent->value.element.name : "null"); + mxmlDelete(node); + node = NULL; + } + break; + } + + default : /* Ignore... */ + node = NULL; + break; + } + + if (*bufptr) + { + /* + * Bad integer/real number value... + */ + + mxml_error("Bad %s value '%s' in parent <%s>!", + type == MXML_INTEGER ? "integer" : "real", buffer, + parent ? parent->value.element.name : "null"); + break; + } + + bufptr = buffer; + whitespace = mxml_isspace(ch) && type == MXML_TEXT; + + if (!node && type != MXML_IGNORE) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add value node of type %s to parent <%s>!", + types[type], parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + else if (mxml_isspace(ch) && type == MXML_TEXT) + whitespace = 1; + + /* + * Add lone whitespace node if we have an element and existing + * whitespace... + */ + + if (ch == '<' && whitespace && type == MXML_TEXT) + { + if (parent) + { + node = mxmlNewText(parent, whitespace, ""); + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + + whitespace = 0; + } + + if (ch == '<') + { + /* + * Start of open/close tag... + */ + + bufptr = buffer; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) + break; + else if (ch == '<') + { + mxml_error("Bare < in element!"); + goto error; + } + else if (ch == '&') + { + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + else if (((bufptr - buffer) == 1 && buffer[0] == '?') || + ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || + ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) + break; + + *bufptr = '\0'; + + if (!strcmp(buffer, "!--")) + { + /* + * Gather rest of comment... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > (buffer + 4) && + bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in comment node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add comment node to parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_COMMENT, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (!strcmp(buffer, "![CDATA[")) + { + /* + * Gather CDATA section... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in CDATA node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add CDATA node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_CDATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (buffer[0] == '?') + { + /* + * Gather rest of processing instruction... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole processing instruction... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in processing instruction node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add processing instruction node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '!') + { + /* + * Gather rest of declaration... + */ + + do + { + if (ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + while ((ch = (*getc_cb)(p, &encoding)) != EOF); + + /* + * Error out if we didn't get the whole declaration... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in declaration node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add declaration node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '/') + { + /* + * Handle close tag... + */ + + if (!parent || strcmp(buffer + 1, parent->value.element.name)) + { + /* + * Close tag doesn't match tree; print an error for now... + */ + + mxml_error("Mismatched close tag <%s> under parent <%s>!", + buffer, parent ? parent->value.element.name : "(null)"); + goto error; + } + + /* + * Keep reading until we see >... + */ + + while (ch != '>' && ch != EOF) + ch = (*getc_cb)(p, &encoding); + + node = parent; + parent = parent->parent; + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + + /* + * Ascend into the parent and set the value type as needed... + */ + + if (cb && parent) + type = (*cb)(parent); + } + else + { + /* + * Handle open tag... + */ + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add element node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (mxml_isspace(ch)) + { + if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF) + goto error; + } + else if (ch == '/') + { + if ((ch = (*getc_cb)(p, &encoding)) != '>') + { + mxml_error("Expected > but got '%c' instead for element <%s/>!", + ch, buffer); + mxmlDelete(node); + goto error; + } + + ch = '/'; + } + + if (sax_cb) + (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data); + + if (!first) + first = node; + + if (ch == EOF) + break; + + if (ch != '/') + { + /* + * Descend into this node, setting the value type as needed... + */ + + parent = node; + + if (cb && parent) + type = (*cb)(parent); + } + else if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + } + + bufptr = buffer; + } + else if (ch == '&') + { + /* + * Add character entity to current buffer... + */ + + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch)) + { + /* + * Add character to current buffer... + */ + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + + /* + * Free the string buffer - we don't need it anymore... + */ + + free(buffer); + + /* + * Find the top element and return it... + */ + + if (parent) + { + node = parent; + + while (parent->parent != top && parent->parent) + parent = parent->parent; + + if (node != parent) + { + mxml_error("Missing close tag under parent <%s>!", + node->value.element.name, + node->parent ? node->parent->value.element.name : "(null)"); + + mxmlDelete(first); + + return (NULL); + } + } + + if (parent) + return (parent); + else + return (first); + + /* + * Common error return... + */ + +error: + + mxmlDelete(first); + + free(buffer); + + return (NULL); +} + + +/* + * 'mxml_parse_element()' - Parse an element for any attributes... + */ + +static int /* O - Terminating character */ +mxml_parse_element( + mxml_node_t *node, /* I - Element node */ + void *p, /* I - Data to read from */ + int *encoding, /* IO - Encoding */ + _mxml_getc_cb_t getc_cb) /* I - Data callback */ +{ + int ch, /* Current character in file */ + quote; /* Quoting character */ + char *name, /* Attribute name */ + *value, /* Attribute value */ + *ptr; /* Pointer into name/value */ + int namesize, /* Size of name string */ + valsize; /* Size of value string */ + + + /* + * Initialize the name and value buffers... + */ + + if ((name = malloc(64)) == NULL) + { + mxml_error("Unable to allocate memory for name!"); + return (EOF); + } + + namesize = 64; + + if ((value = malloc(64)) == NULL) + { + free(name); + mxml_error("Unable to allocate memory for value!"); + return (EOF); + } + + valsize = 64; + + /* + * Loop until we hit a >, /, ?, or EOF... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { +#if DEBUG > 1 + fprintf(stderr, "parse_element: ch='%c'\n", ch); +#endif /* DEBUG > 1 */ + + /* + * Skip leading whitespace... + */ + + if (mxml_isspace(ch)) + continue; + + /* + * Stop at /, ?, or >... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + goto error; + } + + break; + } + else if (ch == '<') + { + mxml_error("Bare < in element %s!", node->value.element.name); + goto error; + } + else if (ch == '>') + break; + + /* + * Read the attribute name... + */ + + name[0] = ch; + ptr = name + 1; + + if (ch == '\"' || ch == '\'') + { + /* + * Name is in quotes, so get a quoted string... + */ + + quote = ch; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + + if (ch == quote) + break; + } + } + else + { + /* + * Grab an normal, non-quoted name... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || + ch == '?') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + } + } + + *ptr = '\0'; + + if (mxmlElementGetAttr(node, name)) + goto error; + + while (ch != EOF && mxml_isspace(ch)) + ch = (*getc_cb)(p, encoding); + + if (ch == '=') + { + /* + * Read the attribute value... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)); + + if (ch == EOF) + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + if (ch == '\'' || ch == '\"') + { + /* + * Read quoted value... + */ + + quote = ch; + ptr = value; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch == quote) + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + else + { + /* + * Read unquoted value... + */ + + value[0] = ch; + ptr = value + 1; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + + /* + * Set the attribute with the given string value... + */ + + mxmlElementSetAttr(node, name, value); + } + else + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + /* + * Check the end character... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + ch = EOF; + } + + break; + } + else if (ch == '>') + break; + } + + /* + * Free the name and value buffers and return... + */ + + free(name); + free(value); + + return (ch); + + /* + * Common error return point... + */ + +error: + + free(name); + free(value); + + return (EOF); +} + + +/* + * 'mxml_string_getc()' - Get a character from a string. + */ + +static int /* O - Character or EOF */ +mxml_string_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch; /* Character */ + const char **s; /* Pointer to string pointer */ + + + s = (const char **)p; + + if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE) + { + /* + * Got character; convert UTF-8 to integer and return... + */ + + (*s)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (((*s)[0] & 255) != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (((*s)[0] & 255) != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); + + (*s)++; + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80) + return (EOF); + + ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); + + (*s) += 2; + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_string_getc(p, encoding)); + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80 || + ((*s)[2] & 0xc0) != 0x80) + return (EOF); + + ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | + ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); + + (*s) += 3; + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else + return (EOF); + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | ((*s)[0] & 255); + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[0]) + return (EOF); + + lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch = ch | (((*s)[0] & 255) << 8); + + if (!ch) + { + (*s) --; + return (EOF); + } + + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[1]) + return (EOF); + + lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + } + + return (EOF); +} + + +/* + * 'mxml_string_putc()' - Write a character to a string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_string_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to string pointers */ +{ + char **pp; /* Pointer to string pointers */ + + + pp = (char **)p; + + if (pp[0] < pp[1]) + pp[0][0] = ch; + + pp[0] ++; + + return (0); +} + + +/* + * 'mxml_write_name()' - Write a name string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_name(const char *s, /* I - Name to write */ + void *p, /* I - Write pointer */ + int (*putc_cb)(int, void *)) + /* I - Write callback */ +{ + char quote; /* Quote character */ + const char *name; /* Entity name */ + + + if (*s == '\"' || *s == '\'') + { + /* + * Write a quoted name string... + */ + + if ((*putc_cb)(*s, p) < 0) + return (-1); + + quote = *s++; + + while (*s && *s != quote) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + /* + * Write the end quote... + */ + + if ((*putc_cb)(quote, p) < 0) + return (-1); + } + else + { + /* + * Write a non-quoted name string... + */ + + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + } + + return (0); +} + + +/* + * 'mxml_write_node()' - Save an XML node to a file. + */ + +static int /* O - Column or -1 on error */ +mxml_write_node(mxml_node_t *node, /* I - Node to write */ + void *p, /* I - File to write to */ + mxml_save_cb_t cb, /* I - Whitespace callback */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb,/* I - Output callback */ + _mxml_global_t *global)/* I - Global data */ +{ + int i, /* Looping var */ + width; /* Width of attr + value */ + mxml_attr_t *attr; /* Current attribute */ + char s[255]; /* Temporary string */ + + + /* + * Print the node value... + */ + + switch (node->type) + { + case MXML_ELEMENT : + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if (node->value.element.name[0] == '?' || + !strncmp(node->value.element.name, "!--", 3) || + !strncmp(node->value.element.name, "![CDATA[", 8)) + { + /* + * Comments, CDATA, and processing instructions do not + * use character entities. + */ + + const char *ptr; /* Pointer into name */ + + + for (ptr = node->value.element.name; *ptr; ptr ++) + if ((*putc_cb)(*ptr, p) < 0) + return (-1); + } + else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.element.name) + 1; + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { + width = strlen(attr->name); + + if (attr->value) + width += strlen(attr->value) + 3; + + if (global->wrap > 0 && (col + width) > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + + col ++; + } + + if (mxml_write_name(attr->name, p, putc_cb) < 0) + return (-1); + + if (attr->value) + { + if ((*putc_cb)('=', p) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + if (mxml_write_string(attr->value, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + } + + col += width; + } + + if (node->child) + { + /* + * Write children... + */ + + mxml_node_t *child; /* Current child */ + + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + + for (child = node->child; child; child = child->next) + { + if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0) + return (-1); + } + + /* + * The ? and ! elements are special-cases and have no end tags... + */ + + if (node->value.element.name[0] != '!' && + node->value.element.name[0] != '?') + { + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if (mxml_write_string(node->value.element.name, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += strlen(node->value.element.name) + 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); + } + } + else if (node->value.element.name[0] == '!' || + node->value.element.name[0] == '?') + { + /* + * The ? and ! elements are special-cases... + */ + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + break; + + case MXML_INTEGER : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%d", node->value.integer); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_OPAQUE : + if (mxml_write_string(node->value.opaque, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.opaque); + break; + + case MXML_REAL : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%f", node->value.real); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_TEXT : + if (node->value.text.whitespace && col > 0) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.text.string); + break; + + case MXML_CUSTOM : + if (global->custom_save_cb) + { + char *data; /* Custom data string */ + const char *newline; /* Last newline in string */ + + + if ((data = (*global->custom_save_cb)(node)) == NULL) + return (-1); + + if (mxml_write_string(data, p, putc_cb) < 0) + return (-1); + + if ((newline = strrchr(data, '\n')) == NULL) + col += strlen(data); + else + col = strlen(newline); + + free(data); + break; + } + + default : /* Should never happen */ + return (-1); + } + + return (col); +} + + +/* + * 'mxml_write_string()' - Write a string, escaping & and < as needed. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_string( + const char *s, /* I - String to write */ + void *p, /* I - Write pointer */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *name; /* Entity name, if any */ + + + while (*s) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + return (0); +} + + +/* + * 'mxml_write_ws()' - Do whitespace callback... + */ + +static int /* O - New column */ +mxml_write_ws(mxml_node_t *node, /* I - Current node */ + void *p, /* I - Write pointer */ + mxml_save_cb_t cb, /* I - Callback function */ + int ws, /* I - Where value */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *s; /* Whitespace string */ + + + if (cb && (s = (*cb)(node, ws)) != NULL) + { + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + else if (*s == '\n') + col = 0; + else if (*s == '\t') + { + col += MXML_TAB; + col = col - (col % MXML_TAB); + } + else + col ++; + + s ++; + } + } + + return (col); +} + + +/* + * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-get.c b/tools/gator/daemon/mxml/mxml-get.c new file mode 100644 index 00000000000..a5356d57e18 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-get.c @@ -0,0 +1,471 @@ +/* + * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $" + * + * Node get functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlGetCDATA() - Get the value for a CDATA node. + * mxmlGetCustom() - Get the value for a custom node. + * mxmlGetElement() - Get the name for an element node. + * mxmlGetFirstChild() - Get the first child of an element node. + * mxmlGetInteger() - Get the integer value from the specified node or its + * first child. + * mxmlGetLastChild() - Get the last child of an element node. + * mxmlGetNextSibling() - Get the next node for the current parent. + * mxmlGetOpaque() - Get an opaque string value for a node or its first + * child. + * mxmlGetParent() - Get the parent node. + * mxmlGetPrevSibling() - Get the previous node for the current parent. + * mxmlGetReal() - Get the real value for a node or its first child. + * mxmlGetText() - Get the text value for a node or its first child. + * mxmlGetType() - Get the node type. + * mxmlGetUserData() - Get the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlGetCDATA()' - Get the value for a CDATA node. + * + * @code NULL@ is returned if the node is not a CDATA element. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - CDATA value or NULL */ +mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (NULL); + + /* + * Return the text following the CDATA declaration... + */ + + return (node->value.element.name + 8); +} + + +/* + * 'mxmlGetCustom()' - Get the value for a custom node. + * + * @code NULL@ is returned if the node (or its first child) is not a custom + * value node. + * + * @since Mini-XML 2.7@ + */ + +const void * /* O - Custom value or NULL */ +mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_CUSTOM) + return (node->value.custom.data); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_CUSTOM) + return (node->child->value.custom.data); + else + return (NULL); +} + + +/* + * 'mxmlGetElement()' - Get the name for an element node. + * + * @code NULL@ is returned if the node is not an element node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Element name or NULL */ +mxmlGetElement(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the element name... + */ + + return (node->value.element.name); +} + + +/* + * 'mxmlGetFirstChild()' - Get the first child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - First child or NULL */ +mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the first child node... + */ + + return (node->child); +} + + +/* + * 'mxmlGetInteger()' - Get the integer value from the specified node or its + * first child. + * + * 0 is returned if the node (or its first child) is not an integer value node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - Integer value or 0 */ +mxmlGetInteger(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_INTEGER) + return (node->value.integer); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_INTEGER) + return (node->child->value.integer); + else + return (0); +} + + +/* + * 'mxmlGetLastChild()' - Get the last child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Last child or NULL */ +mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the node type... + */ + + return (node->last_child); +} + + +/* + * 'mxmlGetNextSibling()' - Get the next node for the current parent. + * + * @code NULL@ is returned if this is the last child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * +mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->next); +} + + +/* + * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not an opaque + * value node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Opaque string or NULL */ +mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_OPAQUE) + return (node->value.opaque); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_OPAQUE) + return (node->child->value.opaque); + else + return (NULL); +} + + +/* + * 'mxmlGetParent()' - Get the parent node. + * + * @code NULL@ is returned for a root node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Parent node or NULL */ +mxmlGetParent(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->parent); +} + + +/* + * 'mxmlGetPrevSibling()' - Get the previous node for the current parent. + * + * @code NULL@ is returned if this is the first child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->prev); +} + + +/* + * 'mxmlGetReal()' - Get the real value for a node or its first child. + * + * 0.0 is returned if the node (or its first child) is not a real value node. + * + * @since Mini-XML 2.7@ + */ + +double /* O - Real value or 0.0 */ +mxmlGetReal(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0.0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_REAL) + return (node->value.real); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_REAL) + return (node->child->value.real); + else + return (0.0); +} + + +/* + * 'mxmlGetText()' - Get the text value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not a text node. + * The "whitespace" argument can be NULL. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Text string or NULL */ +mxmlGetText(mxml_node_t *node, /* I - Node to get */ + int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */ +{ + /* + * Range check input... + */ + + if (!node) + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } + + /* + * Return the integer value... + */ + + if (node->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->value.text.whitespace; + + return (node->value.text.string); + } + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->child->value.text.whitespace; + + return (node->child->value.text.string); + } + else + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } +} + + +/* + * 'mxmlGetType()' - Get the node type. + * + * @code MXML_IGNORE@ is returned if "node" is @code NULL@. + * + * @since Mini-XML 2.7@ + */ + +mxml_type_t /* O - Type of node */ +mxmlGetType(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (MXML_IGNORE); + + /* + * Return the node type... + */ + + return (node->type); +} + + +/* + * 'mxmlGetUserData()' - Get the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +void * /* O - User data pointer */ +mxmlGetUserData(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the user data pointer... + */ + + return (node->user_data); +} + + +/* + * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-index.c b/tools/gator/daemon/mxml/mxml-index.c new file mode 100644 index 00000000000..b6efc66f055 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-index.c @@ -0,0 +1,662 @@ +/* + * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $" + * + * Index support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Sort functions... + */ + +static int index_compare(mxml_index_t *ind, mxml_node_t *first, + mxml_node_t *second); +static int index_find(mxml_index_t *ind, const char *element, + const char *value, mxml_node_t *node); +static void index_sort(mxml_index_t *ind, int left, int right); + + +/* + * 'mxmlIndexDelete()' - Delete an index. + */ + +void +mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */ +{ + /* + * Range check input.. + */ + + if (!ind) + return; + + /* + * Free memory... + */ + + if (ind->attr) + free(ind->attr); + + if (ind->alloc_nodes) + free(ind->nodes); + + free(ind); +} + + +/* + * 'mxmlIndexEnum()' - Return the next node in the index. + * + * Nodes are returned in the sorted order of the index. + */ + +mxml_node_t * /* O - Next node or NULL if there is none */ +mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ +{ + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Return the next node... + */ + + if (ind->cur_node < ind->num_nodes) + return (ind->nodes[ind->cur_node ++]); + else + return (NULL); +} + + +/* + * 'mxmlIndexFind()' - Find the next matching node. + * + * You should call mxmlIndexReset() prior to using this function for + * the first time with a particular set of "element" and "value" + * strings. Passing NULL for both "element" and "value" is equivalent + * to calling mxmlIndexEnum(). + */ + +mxml_node_t * /* O - Node or NULL if none found */ +mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ + const char *element, /* I - Element name to find, if any */ + const char *value) /* I - Attribute value, if any */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + + +#ifdef DEBUG + printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", + ind, element ? element : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind || (!ind->attr && value)) + { +#ifdef DEBUG + puts(" returning NULL..."); + printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If both element and value are NULL, just enumerate the nodes in the + * index... + */ + + if (!element && !value) + return (mxmlIndexEnum(ind)); + + /* + * If there are no nodes in the index, return NULL... + */ + + if (!ind->num_nodes) + { +#ifdef DEBUG + puts(" returning NULL..."); + puts(" no nodes!"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If cur_node == 0, then find the first matching node... + */ + + if (ind->cur_node == 0) + { + /* + * Find the first node using a modified binary search algorithm... + */ + + first = 0; + last = ind->num_nodes - 1; + +#ifdef DEBUG + printf(" find first time, num_nodes=%d...\n", ind->num_nodes); +#endif /* DEBUG */ + + while ((last - first) > 1) + { + current = (first + last) / 2; + +#ifdef DEBUG + printf(" first=%d, last=%d, current=%d\n", first, last, current); +#endif /* DEBUG */ + + if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0) + { + /* + * Found a match, move back to find the first... + */ + +#ifdef DEBUG + puts(" match!"); +#endif /* DEBUG */ + + while (current > 0 && + !index_find(ind, element, value, ind->nodes[current - 1])) + current --; + +#ifdef DEBUG + printf(" returning first match=%d\n", current); +#endif /* DEBUG */ + + /* + * Return the first match and save the index to the next... + */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + else if (diff < 0) + last = current; + else + first = current; + +#ifdef DEBUG + printf(" diff=%d\n", diff); +#endif /* DEBUG */ + } + + /* + * If we get this far, then we found exactly 0 or 1 matches... + */ + + for (current = first; current <= last; current ++) + if (!index_find(ind, element, value, ind->nodes[current])) + { + /* + * Found exactly one (or possibly two) match... + */ + +#ifdef DEBUG + printf(" returning only match %d...\n", current); +#endif /* DEBUG */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + + /* + * No matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); + } + else if (ind->cur_node < ind->num_nodes && + !index_find(ind, element, value, ind->nodes[ind->cur_node])) + { + /* + * Return the next matching node... + */ + +#ifdef DEBUG + printf(" returning next match %d...\n", ind->cur_node); +#endif /* DEBUG */ + + return (ind->nodes[ind->cur_node ++]); + } + + /* + * If we get this far, then we have no matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlIndexGetCount()' - Get the number of nodes in an index. + * + * @since Mini-XML 2.7@ + */ + +int /* I - Number of nodes in index */ +mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ +{ + /* + * Range check input... + */ + + if (!ind) + return (0); + + /* + * Return the number of nodes in the index... + */ + + return (ind->num_nodes); +} + + +/* + * 'mxmlIndexNew()' - Create a new index. + * + * The index will contain all nodes that contain the named element and/or + * attribute. If both "element" and "attr" are NULL, then the index will + * contain a sorted list of the elements in the node tree. Nodes are + * sorted by element name and optionally by attribute value if the "attr" + * argument is not NULL. + */ + +mxml_index_t * /* O - New index */ +mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ + const char *element, /* I - Element to index or NULL for all */ + const char *attr) /* I - Attribute to index or NULL for none */ +{ + mxml_index_t *ind; /* New index */ + mxml_node_t *current, /* Current node in index */ + **temp; /* Temporary node pointer array */ + + + /* + * Range check input... + */ + +#ifdef DEBUG + printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", + node, element ? element : "(null)", attr ? attr : "(null)"); +#endif /* DEBUG */ + + if (!node) + return (NULL); + + /* + * Create a new index... + */ + + if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL) + { + mxml_error("Unable to allocate %d bytes for index - %s", + sizeof(mxml_index_t), strerror(errno)); + return (NULL); + } + + if (attr) + ind->attr = strdup(attr); + + if (!element && !attr) + current = node; + else + current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND); + + while (current) + { + if (ind->num_nodes >= ind->alloc_nodes) + { + if (!ind->alloc_nodes) + temp = malloc(64 * sizeof(mxml_node_t *)); + else + temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *)); + + if (!temp) + { + /* + * Unable to allocate memory for the index, so abort... + */ + + mxml_error("Unable to allocate %d bytes for index: %s", + (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), + strerror(errno)); + + mxmlIndexDelete(ind); + return (NULL); + } + + ind->nodes = temp; + ind->alloc_nodes += 64; + } + + ind->nodes[ind->num_nodes ++] = current; + + current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND); + } + + /* + * Sort nodes based upon the search criteria... + */ + +#ifdef DEBUG + { + int i; /* Looping var */ + + + printf("%d node(s) in index.\n\n", ind->num_nodes); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + if (ind->num_nodes > 1) + index_sort(ind, 0, ind->num_nodes - 1); + +#ifdef DEBUG + { + int i; /* Looping var */ + + + puts("After sorting:\n"); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + /* + * Return the new index... + */ + + return (ind); +} + + +/* + * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and + * return the first node in the index. + * + * This function should be called prior to using mxmlIndexEnum() or + * mxmlIndexFind() for the first time. + */ + +mxml_node_t * /* O - First node or NULL if there is none */ +mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */ +{ +#ifdef DEBUG + printf("mxmlIndexReset(ind=%p)\n", ind); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Set the index to the first element... + */ + + ind->cur_node = 0; + + /* + * Return the first node... + */ + + if (ind->num_nodes) + return (ind->nodes[0]); + else + return (NULL); +} + + +/* + * 'index_compare()' - Compare two nodes. + */ + +static int /* O - Result of comparison */ +index_compare(mxml_index_t *ind, /* I - Index */ + mxml_node_t *first, /* I - First node */ + mxml_node_t *second) /* I - Second node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if ((diff = strcmp(first->value.element.name, + second->value.element.name)) != 0) + return (diff); + + /* + * Check the attribute value... + */ + + if (ind->attr) + { + if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr), + mxmlElementGetAttr(second, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_find()' - Compare a node with index values. + */ + +static int /* O - Result of comparison */ +index_find(mxml_index_t *ind, /* I - Index */ + const char *element, /* I - Element name or NULL */ + const char *value, /* I - Attribute value or NULL */ + mxml_node_t *node) /* I - Node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if (element) + { + if ((diff = strcmp(element, node->value.element.name)) != 0) + return (diff); + } + + /* + * Check the attribute value... + */ + + if (value) + { + if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_sort()' - Sort the nodes in the index... + * + * This function implements the classic quicksort algorithm... + */ + +static void +index_sort(mxml_index_t *ind, /* I - Index to sort */ + int left, /* I - Left node in partition */ + int right) /* I - Right node in partition */ +{ + mxml_node_t *pivot, /* Pivot node */ + *temp; /* Swap node */ + int templ, /* Temporary left node */ + tempr; /* Temporary right node */ + + + /* + * Loop until we have sorted all the way to the right... + */ + + do + { + /* + * Sort the pivot in the current partition... + */ + + pivot = ind->nodes[left]; + + for (templ = left, tempr = right; templ < tempr;) + { + /* + * Move left while left node <= pivot node... + */ + + while ((templ < right) && + index_compare(ind, ind->nodes[templ], pivot) <= 0) + templ ++; + + /* + * Move right while right node > pivot node... + */ + + while ((tempr > left) && + index_compare(ind, ind->nodes[tempr], pivot) > 0) + tempr --; + + /* + * Swap nodes if needed... + */ + + if (templ < tempr) + { + temp = ind->nodes[templ]; + ind->nodes[templ] = ind->nodes[tempr]; + ind->nodes[tempr] = temp; + } + } + + /* + * When we get here, the right (tempr) node is the new position for the + * pivot node... + */ + + if (index_compare(ind, pivot, ind->nodes[tempr]) > 0) + { + ind->nodes[left] = ind->nodes[tempr]; + ind->nodes[tempr] = pivot; + } + + /* + * Recursively sort the left partition as needed... + */ + + if (left < (tempr - 1)) + index_sort(ind, left, tempr - 1); + } + while (right > (left = tempr + 1)); +} + + +/* + * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-node.c b/tools/gator/daemon/mxml/mxml-node.c new file mode 100644 index 00000000000..44af759f9de --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-node.c @@ -0,0 +1,807 @@ +/* + * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $" + * + * Node support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlAdd() - Add a node to a tree. + * mxmlDelete() - Delete a node and all of its children. + * mxmlGetRefCount() - Get the current reference (use) count for a node. + * mxmlNewCDATA() - Create a new CDATA node. + * mxmlNewCustom() - Create a new custom data node. + * mxmlNewElement() - Create a new element node. + * mxmlNewInteger() - Create a new integer node. + * mxmlNewOpaque() - Create a new opaque string. + * mxmlNewReal() - Create a new real number node. + * mxmlNewText() - Create a new text fragment node. + * mxmlNewTextf() - Create a new formatted text fragment node. + * mxmlRemove() - Remove a node from its parent. + * mxmlNewXML() - Create a new XML document tree. + * mxmlRelease() - Release a node. + * mxmlRetain() - Retain a node. + * mxml_new() - Create a new node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); + + +/* + * 'mxmlAdd()' - Add a node to a tree. + * + * Adds the specified node to the parent. If the child argument is not + * NULL, puts the new node before or after the specified child depending + * on the value of the where argument. If the child argument is NULL, + * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) + * or at the end of the child list (MXML_ADD_AFTER). The constant + * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. + */ + +void +mxmlAdd(mxml_node_t *parent, /* I - Parent node */ + int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ + mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ + mxml_node_t *node) /* I - Node to add */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, + where, child, node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!parent || !node) + return; + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child); + fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); + fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ + + /* + * Remove the node from any existing parent... + */ + + if (node->parent) + mxmlRemove(node); + + /* + * Reset pointers... + */ + + node->parent = parent; + + switch (where) + { + case MXML_ADD_BEFORE : + if (!child || child == parent->child || child->parent != parent) + { + /* + * Insert as first node under parent... + */ + + node->next = parent->child; + + if (parent->child) + parent->child->prev = node; + else + parent->last_child = node; + + parent->child = node; + } + else + { + /* + * Insert node before this child... + */ + + node->next = child; + node->prev = child->prev; + + if (child->prev) + child->prev->next = node; + else + parent->child = node; + + child->prev = node; + } + break; + + case MXML_ADD_AFTER : + if (!child || child == parent->last_child || child->parent != parent) + { + /* + * Insert as last node under parent... + */ + + node->parent = parent; + node->prev = parent->last_child; + + if (parent->last_child) + parent->last_child->next = node; + else + parent->child = node; + + parent->last_child = node; + } + else + { + /* + * Insert node after this child... + */ + + node->prev = child; + node->next = child->next; + + if (child->next) + child->next->prev = node; + else + parent->last_child = node; + + child->next = node; + } + break; + } + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " AFTER: parent->child=%p\n", parent->child); + fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); + fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlDelete()' - Delete a node and all of its children. + * + * If the specified node has a parent, this function first removes the + * node from its parent using the mxmlRemove() function. + */ + +void +mxmlDelete(mxml_node_t *node) /* I - Node to delete */ +{ + int i; /* Looping var */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlDelete(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node) + return; + + /* + * Remove the node from its parent, if any... + */ + + mxmlRemove(node); + + /* + * Delete children... + */ + + while (node->child) + mxmlDelete(node->child); + + /* + * Now delete any node data... + */ + + switch (node->type) + { + case MXML_ELEMENT : + if (node->value.element.name) + free(node->value.element.name); + + if (node->value.element.num_attrs) + { + for (i = 0; i < node->value.element.num_attrs; i ++) + { + if (node->value.element.attrs[i].name) + free(node->value.element.attrs[i].name); + if (node->value.element.attrs[i].value) + free(node->value.element.attrs[i].value); + } + + free(node->value.element.attrs); + } + break; + case MXML_INTEGER : + /* Nothing to do */ + break; + case MXML_OPAQUE : + if (node->value.opaque) + free(node->value.opaque); + break; + case MXML_REAL : + /* Nothing to do */ + break; + case MXML_TEXT : + if (node->value.text.string) + free(node->value.text.string); + break; + case MXML_CUSTOM : + if (node->value.custom.data && + node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + break; + default : + break; + } + + /* + * Free this node... + */ + + free(node); +} + + +/* + * 'mxmlGetRefCount()' - Get the current reference (use) count for a node. + * + * The initial reference count of new nodes is 1. Use the @link mxmlRetain@ + * and @link mxmlRelease@ functions to increment and decrement a node's + * reference count. + * + * @since Mini-XML 2.7@. + */ + +int /* O - Reference count */ +mxmlGetRefCount(mxml_node_t *node) /* I - Node */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the reference count... + */ + + return (node->ref_count); +} + + +/* + * 'mxmlNewCDATA()' - Create a new CDATA node. + * + * The new CDATA node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * CDATA node has no parent. The data string must be nul-terminated and + * is copied into the new node. CDATA nodes use the MXML_ELEMENT type. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *data) /* I - Data string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n", + parent, data ? data : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!data) + return (NULL); + + /* + * Create the node and set the name value... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (node); +} + + +/* + * 'mxmlNewCustom()' - Create a new custom data node. + * + * The new custom node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. NULL can be passed when the data in the + * node is not dynamically allocated or is separately managed. + * + * @since Mini-XML 2.1@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCustom( + mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + void *data, /* I - Pointer to data */ + mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, + data, destroy); +#endif /* DEBUG */ + + /* + * Create the node and set the value... + */ + + if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL) + { + node->value.custom.data = data; + node->value.custom.destroy = destroy; + } + + return (node); +} + + +/* + * 'mxmlNewElement()' - Create a new element node. + * + * The new element node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *name) /* I - Name of element */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, + name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!name) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = strdup(name); + + return (node); +} + + +/* + * 'mxmlNewInteger()' - Create a new integer node. + * + * The new integer node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * integer node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int integer) /* I - Integer value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_INTEGER)) != NULL) + node->value.integer = integer; + + return (node); +} + + +/* + * 'mxmlNewOpaque()' - Create a new opaque string. + * + * The new opaque node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * opaque node has no parent. The opaque string must be nul-terminated and + * is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *opaque) /* I - Opaque string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, + opaque ? opaque : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!opaque) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) + node->value.opaque = strdup(opaque); + + return (node); +} + + +/* + * 'mxmlNewReal()' - Create a new real number node. + * + * The new real number node is added to the end of the specified parent's + * child list. The constant MXML_NO_PARENT can be used to specify that + * the new real number node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + double real) /* I - Real number value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_REAL)) != NULL) + node->value.real = real; + + return (node); +} + + +/* + * 'mxmlNewText()' - Create a new text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The text + * string must be nul-terminated and is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", + parent, whitespace, string ? string : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!string) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + } + + return (node); +} + + +/* + * 'mxmlNewTextf()' - Create a new formatted text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The format + * string must be nul-terminated and is formatted into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style frmat string */ + ...) /* I - Additional args as needed */ +{ + mxml_node_t *node; /* New node */ + va_list ap; /* Pointer to arguments */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", + parent, whitespace, format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!format) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_vstrdupf(format, ap); + + va_end(ap); + } + + return (node); +} + + +/* + * 'mxmlRemove()' - Remove a node from its parent. + * + * Does not free memory used by the node - use mxmlDelete() for that. + * This function does nothing if the node has no parent. + */ + +void +mxmlRemove(mxml_node_t *node) /* I - Node to remove */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlRemove(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || !node->parent) + return; + + /* + * Remove from parent... + */ + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " BEFORE: node->child=%p\n", node->child); + fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); + fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); + fprintf(stderr, " BEFORE: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ + + if (node->prev) + node->prev->next = node->next; + else + node->parent->child = node->next; + + if (node->next) + node->next->prev = node->prev; + else + node->parent->last_child = node->prev; + + node->parent = NULL; + node->prev = NULL; + node->next = NULL; + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " AFTER: node->child=%p\n", node->child); + fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); + fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); + fprintf(stderr, " AFTER: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlNewXML()' - Create a new XML document tree. + * + * The "version" argument specifies the version number to put in the + * ?xml element node. If NULL, version 1.0 is assumed. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New ?xml node */ +mxmlNewXML(const char *version) /* I - Version number to use */ +{ + char element[1024]; /* Element text */ + + + snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?", + version ? version : "1.0"); + + return (mxmlNewElement(NULL, element)); +} + + +/* + * 'mxmlRelease()' - Release a node. + * + * When the reference count reaches zero, the node (and any children) + * is deleted via mxmlDelete(). + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRelease(mxml_node_t *node) /* I - Node */ +{ + if (node) + { + if ((-- node->ref_count) <= 0) + { + mxmlDelete(node); + return (0); + } + else + return (node->ref_count); + } + else + return (-1); +} + + +/* + * 'mxmlRetain()' - Retain a node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRetain(mxml_node_t *node) /* I - Node */ +{ + if (node) + return (++ node->ref_count); + else + return (-1); +} + + +/* + * 'mxml_new()' - Create a new node. + */ + +static mxml_node_t * /* O - New node */ +mxml_new(mxml_node_t *parent, /* I - Parent node */ + mxml_type_t type) /* I - Node type */ +{ + mxml_node_t *node; /* New node */ + + +#if DEBUG > 1 + fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); +#endif /* DEBUG > 1 */ + + /* + * Allocate memory for the node... + */ + + if ((node = calloc(1, sizeof(mxml_node_t))) == NULL) + { +#if DEBUG > 1 + fputs(" returning NULL\n", stderr); +#endif /* DEBUG > 1 */ + + return (NULL); + } + +#if DEBUG > 1 + fprintf(stderr, " returning %p\n", node); +#endif /* DEBUG > 1 */ + + /* + * Set the node type... + */ + + node->type = type; + node->ref_count = 1; + + /* + * Add to the parent if present... + */ + + if (parent) + mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); + + /* + * Return the new node... + */ + + return (node); +} + + +/* + * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-private.c b/tools/gator/daemon/mxml/mxml-private.c new file mode 100644 index 00000000000..72f3e2320c7 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-private.c @@ -0,0 +1,331 @@ +/* + * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $" + * + * Private functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxml_error() - Display an error message. + * mxml_integer_cb() - Default callback for integer values. + * mxml_opaque_cb() - Default callback for opaque values. + * mxml_real_cb() - Default callback for real number values. + * _mxml_global() - Get global data. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * Some crazy people think that unloading a shared object is a good or safe + * thing to do. Unfortunately, most objects are simply *not* safe to unload + * and bad things *will* happen. + * + * The following mess of conditional code allows us to provide a destructor + * function in Mini-XML for our thread-global storage so that it can possibly + * be unloaded safely, although since there is no standard way to do so I + * can't even provide any guarantees that you can do it safely on all platforms. + * + * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and + * Windows. It might work on the BSDs and IRIX, but I haven't tested that. + */ + +#if defined(__sun) || defined(_AIX) +# pragma fini(_mxml_fini) +# define _MXML_FINI _mxml_fini +#elif defined(__hpux) +# pragma FINI _mxml_fini +# define _MXML_FINI _mxml_fini +#elif defined(__GNUC__) /* Linux and Mac OS X */ +# define _MXML_FINI __attribute((destructor)) _mxml_fini +#else +# define _MXML_FINI _fini +#endif /* __sun */ + + +/* + * 'mxml_error()' - Display an error message. + */ + +void +mxml_error(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + char s[1024]; /* Message string */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Range check input... + */ + + if (!format) + return; + + /* + * Format the error message string... + */ + + va_start(ap, format); + + vsnprintf(s, sizeof(s), format, ap); + + va_end(ap); + + /* + * And then display the error message... + */ + + if (global->error_cb) + (*global->error_cb)(s); + else + fprintf(stderr, "mxml: %s\n", s); +} + + +/* + * 'mxml_ignore_cb()' - Default callback for ignored values. + */ + +mxml_type_t /* O - Node type */ +mxml_ignore_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_IGNORE); +} + + +/* + * 'mxml_integer_cb()' - Default callback for integer values. + */ + +mxml_type_t /* O - Node type */ +mxml_integer_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_INTEGER); +} + + +/* + * 'mxml_opaque_cb()' - Default callback for opaque values. + */ + +mxml_type_t /* O - Node type */ +mxml_opaque_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_OPAQUE); +} + + +/* + * 'mxml_real_cb()' - Default callback for real number values. + */ + +mxml_type_t /* O - Node type */ +mxml_real_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_REAL); +} + + +#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/ +# include + +static pthread_key_t _mxml_key = -1; /* Thread local storage key */ +static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT; + /* One-time initialization object */ +static void _mxml_init(void); +static void _mxml_destructor(void *g); + + +/* + * '_mxml_destructor()' - Free memory used for globals... + */ + +static void +_mxml_destructor(void *g) /* I - Global data */ +{ + free(g); +} + + +/* + * '_mxml_fini()' - Clean up when unloaded. + */ + +static void +_MXML_FINI(void) +{ + _mxml_global_t *global; /* Global data */ + + + if (_mxml_key != -1) + { + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL) + _mxml_destructor(global); + + pthread_key_delete(_mxml_key); + _mxml_key = -1; + } +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + pthread_once(&_mxml_key_once, _mxml_init); + + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + pthread_setspecific(_mxml_key, global); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + } + + return (global); +} + + +/* + * '_mxml_init()' - Initialize global data... + */ + +static void +_mxml_init(void) +{ + pthread_key_create(&_mxml_key, _mxml_destructor); +} + + +#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ +# include + +static DWORD _mxml_tls_index; /* Index for global storage */ + + +/* + * 'DllMain()' - Main entry for library. + */ + +BOOL WINAPI /* O - Success/failure */ +DllMain(HINSTANCE hinst, /* I - DLL module handle */ + DWORD reason, /* I - Reason */ + LPVOID reserved) /* I - Unused */ +{ + _mxml_global_t *global; /* Global data */ + + + (void)hinst; + (void)reserved; + + switch (reason) + { + case DLL_PROCESS_ATTACH : /* Called on library initialization */ + if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) + return (FALSE); + break; + + case DLL_THREAD_DETACH : /* Called when a thread terminates */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + break; + + case DLL_PROCESS_DETACH : /* Called when library is unloaded */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + + TlsFree(_mxml_tls_index); + break; + + default: + break; + } + + return (TRUE); +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + + TlsSetValue(_mxml_tls_index, (LPVOID)global); + } + + return (global); +} + + +#else /**** No threading ****/ +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + static _mxml_global_t global = /* Global data */ + { + NULL, /* error_cb */ + 1, /* num_entity_cbs */ + { _mxml_entity_cb }, /* entity_cbs */ + 72, /* wrap */ + NULL, /* custom_load_cb */ + NULL /* custom_save_cb */ + }; + + + return (&global); +} +#endif /* HAVE_PTHREAD_H */ + + +/* + * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-private.h b/tools/gator/daemon/mxml/mxml-private.h new file mode 100644 index 00000000000..8789e6c52cb --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-private.h @@ -0,0 +1,50 @@ +/* + * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $" + * + * Private definitions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Global, per-thread data... + */ + +typedef struct _mxml_global_s +{ + void (*error_cb)(const char *); + int num_entity_cbs; + int (*entity_cbs[100])(const char *name); + int wrap; + mxml_custom_load_cb_t custom_load_cb; + mxml_custom_save_cb_t custom_save_cb; +} _mxml_global_t; + + +/* + * Functions... + */ + +extern _mxml_global_t *_mxml_global(void); +extern int _mxml_entity_cb(const char *name); + + +/* + * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-search.c b/tools/gator/daemon/mxml/mxml-search.c new file mode 100644 index 00000000000..f975af1543c --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-search.c @@ -0,0 +1,287 @@ +/* + * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $" + * + * Search/navigation functions for Mini-XML, a small XML-like file + * parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlFindElement() - Find the named element. + * mxmlFindValue() - Find a value with the given path. + * mxmlWalkNext() - Walk to the next logical node in the tree. + * mxmlWalkPrev() - Walk to the previous logical node in the tree. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlFindElement()' - Find the named element. + * + * The search is constrained by the name, attribute name, and value; any + * NULL names or values are treated as wildcards, so different kinds of + * searches can be implemented by looking for all elements of a given name + * or all elements with a specific attribute. The descend argument determines + * whether the search descends into child nodes; normally you will use + * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find + * additional direct descendents of the node. The top node argument + * constrains the search to a particular node's children. + */ + +mxml_node_t * /* O - Element node or NULL */ +mxmlFindElement(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + const char *name, /* I - Element name or NULL for any */ + const char *attr, /* I - Attribute name, or NULL for none */ + const char *value, /* I - Attribute value, or NULL for any */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + const char *temp; /* Current attribute value */ + + + /* + * Range check input... + */ + + if (!node || !top || (!attr && value)) + return (NULL); + + /* + * Start with the next node... + */ + + node = mxmlWalkNext(node, top, descend); + + /* + * Loop until we find a matching element... + */ + + while (node != NULL) + { + /* + * See if this node matches... + */ + + if (node->type == MXML_ELEMENT && + node->value.element.name && + (!name || !strcmp(node->value.element.name, name))) + { + /* + * See if we need to check for an attribute... + */ + + if (!attr) + return (node); /* No attribute search, return it... */ + + /* + * Check for the attribute... + */ + + if ((temp = mxmlElementGetAttr(node, attr)) != NULL) + { + /* + * OK, we have the attribute, does it match? + */ + + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ + } + } + + /* + * No match, move on to the next node... + */ + + if (descend == MXML_DESCEND) + node = mxmlWalkNext(node, top, MXML_DESCEND); + else + node = node->next; + } + + return (NULL); +} + + +/* + * 'mxmlFindPath()' - Find a node with the given path. + * + * The "path" is a slash-separated list of element names. The name "*" is + * considered a wildcard for one or more levels of elements. For example, + * "foo/one/two", "bar/two/one", "*\/one", and so forth. + * + * The first child node of the found node is returned if the given node has + * children and the first child is a value node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Found node or NULL */ +mxmlFindPath(mxml_node_t *top, /* I - Top node */ + const char *path) /* I - Path to element */ +{ + mxml_node_t *node; /* Current node */ + char element[256]; /* Current element name */ + const char *pathsep; /* Separator in path */ + int descend; /* mxmlFindElement option */ + + + /* + * Range check input... + */ + + if (!top || !path || !*path) + return (NULL); + + /* + * Search each element in the path... + */ + + node = top; + while (*path) + { + /* + * Handle wildcards... + */ + + if (!strncmp(path, "*/", 2)) + { + path += 2; + descend = MXML_DESCEND; + } + else + descend = MXML_DESCEND_FIRST; + + /* + * Get the next element in the path... + */ + + if ((pathsep = strchr(path, '/')) == NULL) + pathsep = path + strlen(path); + + if (pathsep == path || (pathsep - path) >= sizeof(element)) + return (NULL); + + memcpy(element, path, pathsep - path); + element[pathsep - path] = '\0'; + + if (*pathsep) + path = pathsep + 1; + else + path = pathsep; + + /* + * Search for the element... + */ + + if ((node = mxmlFindElement(node, node, element, NULL, NULL, + descend)) == NULL) + return (NULL); + } + + /* + * If we get this far, return the node or its first child... + */ + + if (node->child && node->child->type != MXML_ELEMENT) + return (node->child); + else + return (node); +} + + +/* + * 'mxmlWalkNext()' - Walk to the next logical node in the tree. + * + * The descend argument controls whether the first child is considered + * to be the next node. The top node argument constrains the walk to + * the node's children. + */ + +mxml_node_t * /* O - Next node or NULL */ +mxmlWalkNext(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node) + return (NULL); + else if (node->child && descend) + return (node->child); + else if (node == top) + return (NULL); + else if (node->next) + return (node->next); + else if (node->parent && node->parent != top) + { + node = node->parent; + + while (!node->next) + if (node->parent == top || !node->parent) + return (NULL); + else + node = node->parent; + + return (node->next); + } + else + return (NULL); +} + + +/* + * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. + * + * The descend argument controls whether the previous node's last child + * is considered to be the previous node. The top node argument constrains + * the walk to the node's children. + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node || node == top) + return (NULL); + else if (node->prev) + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + + node = node->prev->last_child; + + while (node->last_child) + node = node->last_child; + + return (node); + } + else + return (node->prev); + } + else if (node->parent != top) + return (node->parent); + else + return (NULL); +} + + +/* + * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-set.c b/tools/gator/daemon/mxml/mxml-set.c new file mode 100644 index 00000000000..b0bd52790b2 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-set.c @@ -0,0 +1,349 @@ +/* + * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $" + * + * Node set functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlSetCDATA() - Set the element name of a CDATA node. + * mxmlSetCustom() - Set the data and destructor of a custom data node. + * mxmlSetElement() - Set the name of an element node. + * mxmlSetInteger() - Set the value of an integer node. + * mxmlSetOpaque() - Set the value of an opaque node. + * mxmlSetReal() - Set the value of a real number node. + * mxmlSetText() - Set the value of a text node. + * mxmlSetTextf() - Set the value of a text node to a formatted string. + * mxmlSetUserData() - Set the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlSetCDATA()' - Set the element name of a CDATA node. + * + * The node is not changed if it (or its first child) is not a CDATA element node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ + const char *data) /* I - New data string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + strncmp(node->value.element.name, "![CDATA[", 8) && + node->child && node->child->type == MXML_ELEMENT && + !strncmp(node->child->value.element.name, "![CDATA[", 8)) + node = node->child; + + if (!node || node->type != MXML_ELEMENT || !data || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (0); +} + + +/* + * 'mxmlSetCustom()' - Set the data and destructor of a custom data node. + * + * The node is not changed if it (or its first child) is not a custom node. + * + * @since Mini-XML 2.1@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCustom( + mxml_node_t *node, /* I - Node to set */ + void *data, /* I - New data pointer */ + mxml_custom_destroy_cb_t destroy) /* I - New destructor function */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_CUSTOM) + node = node->child; + + if (!node || node->type != MXML_CUSTOM) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.custom.data && node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + + node->value.custom.data = data; + node->value.custom.destroy = destroy; + + return (0); +} + + +/* + * 'mxmlSetElement()' - Set the name of an element node. + * + * The node is not changed if it is not an element node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetElement(mxml_node_t *node, /* I - Node to set */ + const char *name) /* I - New name string */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = strdup(name); + + return (0); +} + + +/* + * 'mxmlSetInteger()' - Set the value of an integer node. + * + * The node is not changed if it (or its first child) is not an integer node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetInteger(mxml_node_t *node, /* I - Node to set */ + int integer) /* I - Integer value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_INTEGER) + node = node->child; + + if (!node || node->type != MXML_INTEGER) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.integer = integer; + + return (0); +} + + +/* + * 'mxmlSetOpaque()' - Set the value of an opaque node. + * + * The node is not changed if it (or its first child) is not an opaque node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ + const char *opaque) /* I - Opaque string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_OPAQUE) + node = node->child; + + if (!node || node->type != MXML_OPAQUE || !opaque) + return (-1); + + /* + * Free any old opaque value and set the new value... + */ + + if (node->value.opaque) + free(node->value.opaque); + + node->value.opaque = strdup(opaque); + + return (0); +} + + +/* + * 'mxmlSetReal()' - Set the value of a real number node. + * + * The node is not changed if it (or its first child) is not a real number node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetReal(mxml_node_t *node, /* I - Node to set */ + double real) /* I - Real number value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_REAL) + node = node->child; + + if (!node || node->type != MXML_REAL) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.real = real; + + return (0); +} + + +/* + * 'mxmlSetText()' - Set the value of a text node. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetText(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !string) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + + return (0); +} + + +/* + * 'mxmlSetTextf()' - Set the value of a text node to a formatted string. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + + + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !format) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_strdupf(format, ap); + + va_end(ap); + + return (0); +} + + +/* + * 'mxmlSetUserData()' - Set the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetUserData(mxml_node_t *node, /* I - Node to set */ + void *data) /* I - User data pointer */ +{ + /* + * Range check input... + */ + + if (!node) + return (-1); + + /* + * Set the user data pointer and return... + */ + + node->user_data = data; + return (0); +} + + +/* + * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml-string.c b/tools/gator/daemon/mxml/mxml-string.c new file mode 100644 index 00000000000..6be42523f95 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-string.c @@ -0,0 +1,476 @@ +/* + * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $" + * + * String functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * _mxml_snprintf() - Format a string. + * _mxml_strdup() - Duplicate a string. + * _mxml_strdupf() - Format and duplicate a string. + * _mxml_vsnprintf() - Format a string into a fixed size buffer. + * _mxml_vstrdupf() - Format and duplicate a string. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" + + +/* + * The va_copy macro is part of C99, but many compilers don't implement it. + * Provide a "direct assignment" implmentation when va_copy isn't defined... + */ + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(dst,src) __va_copy(dst,src) +# else +# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) +# endif /* __va_copy */ +#endif /* va_copy */ + + +#ifndef HAVE_SNPRINTF +/* + * '_mxml_snprintf()' - Format a string. + */ + +int /* O - Number of bytes formatted */ +_mxml_snprintf(char *buffer, /* I - Output buffer */ + size_t bufsize, /* I - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument list */ + int bytes; /* Number of bytes formatted */ + + + va_start(ap, format); + bytes = vsnprintf(buffer, bufsize, format, ap); + va_end(ap); + + return (bytes); +} +#endif /* !HAVE_SNPRINTF */ + + +/* + * '_mxml_strdup()' - Duplicate a string. + */ + +#ifndef HAVE_STRDUP +char * /* O - New string pointer */ +_mxml_strdup(const char *s) /* I - String to duplicate */ +{ + char *t; /* New string pointer */ + + + if (s == NULL) + return (NULL); + + if ((t = malloc(strlen(s) + 1)) == NULL) + return (NULL); + + return (strcpy(t, s)); +} +#endif /* !HAVE_STRDUP */ + + +/* + * '_mxml_strdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_strdupf(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to additional arguments */ + char *s; /* Pointer to formatted string */ + + + /* + * Get a pointer to the additional arguments, format the string, + * and return it... + */ + + va_start(ap, format); + s = _mxml_vstrdupf(format, ap); + va_end(ap); + + return (s); +} + + +#ifndef HAVE_VSNPRINTF +/* + * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. + */ + +int /* O - Number of bytes formatted */ +_mxml_vsnprintf(char *buffer, /* O - Output buffer */ + size_t bufsize, /* O - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + char *bufptr, /* Pointer to position in buffer */ + *bufend, /* Pointer to end of buffer */ + sign, /* Sign of format width */ + size, /* Size character (h, l, L) */ + type; /* Format type character */ + int width, /* Width of field */ + prec; /* Number of characters of precision */ + char tformat[100], /* Temporary format string for sprintf() */ + *tptr, /* Pointer into temporary format */ + temp[1024]; /* Buffer for formatted numbers */ + char *s; /* Pointer to string */ + int slen; /* Length of string */ + int bytes; /* Total number of bytes needed */ + + + /* + * Loop through the format string, formatting as needed... + */ + + bufptr = buffer; + bufend = buffer + bufsize - 1; + bytes = 0; + + while (*format) + { + if (*format == '%') + { + tptr = tformat; + *tptr++ = *format++; + + if (*format == '%') + { + if (bufptr && bufptr < bufend) *bufptr++ = *format; + bytes ++; + format ++; + continue; + } + else if (strchr(" -+#\'", *format)) + { + *tptr++ = *format; + sign = *format++; + } + else + sign = 0; + + if (*format == '*') + { + /* + * Get width from argument... + */ + + format ++; + width = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); + tptr += strlen(tptr); + } + else + { + width = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + width = width * 10 + *format++ - '0'; + } + } + + if (*format == '.') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + format ++; + + if (*format == '*') + { + /* + * Get precision from argument... + */ + + format ++; + prec = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); + tptr += strlen(tptr); + } + else + { + prec = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + prec = prec * 10 + *format++ - '0'; + } + } + } + else + prec = -1; + + if (*format == 'l' && format[1] == 'l') + { + size = 'L'; + + if (tptr < (tformat + sizeof(tformat) - 2)) + { + *tptr++ = 'l'; + *tptr++ = 'l'; + } + + format += 2; + } + else if (*format == 'h' || *format == 'l' || *format == 'L') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + size = *format++; + } + + if (!*format) + break; + + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + type = *format++; + *tptr = '\0'; + + switch (type) + { + case 'E' : /* Floating point formats */ + case 'G' : + case 'e' : + case 'f' : + case 'g' : + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, double)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'B' : /* Integer formats */ + case 'X' : + case 'b' : + case 'd' : + case 'i' : + case 'o' : + case 'u' : + case 'x' : + if ((width + 2) > sizeof(temp)) + break; + +#ifdef HAVE_LONG_LONG + if (size == 'L') + sprintf(temp, tformat, va_arg(ap, long long)); + else +#endif /* HAVE_LONG_LONG */ + sprintf(temp, tformat, va_arg(ap, int)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'p' : /* Pointer value */ + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, void *)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'c' : /* Character or character array */ + bytes += width; + + if (bufptr) + { + if (width <= 1) + *bufptr++ = va_arg(ap, int); + else + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + memcpy(bufptr, va_arg(ap, char *), (size_t)width); + bufptr += width; + } + } + break; + + case 's' : /* String */ + if ((s = va_arg(ap, char *)) == NULL) + s = "(null)"; + + slen = strlen(s); + if (slen > width && prec != width) + width = slen; + + bytes += width; + + if (bufptr) + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + if (slen > width) + slen = width; + + if (sign == '-') + { + strncpy(bufptr, s, (size_t)slen); + memset(bufptr + slen, ' ', (size_t)(width - slen)); + } + else + { + memset(bufptr, ' ', (size_t)(width - slen)); + strncpy(bufptr + width - slen, s, (size_t)slen); + } + + bufptr += width; + } + break; + + case 'n' : /* Output number of chars so far */ + *(va_arg(ap, int *)) = bytes; + break; + } + } + else + { + bytes ++; + + if (bufptr && bufptr < bufend) + *bufptr++ = *format; + + format ++; + } + } + + /* + * Nul-terminate the string and return the number of characters needed. + */ + + *bufptr = '\0'; + + return (bytes); +} +#endif /* !HAVE_VSNPRINTF */ + + +/* + * '_mxml_vstrdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_vstrdupf(const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + int bytes; /* Number of bytes required */ + char *buffer, /* String buffer */ + temp[256]; /* Small buffer for first vsnprintf */ + va_list apcopy; /* Copy of argument list */ + + + /* + * First format with a tiny buffer; this will tell us how many bytes are + * needed... + */ + + va_copy(apcopy, ap); + bytes = vsnprintf(temp, sizeof(temp), format, apcopy); + + if (bytes < sizeof(temp)) + { + /* + * Hey, the formatted string fits in the tiny buffer, so just dup that... + */ + + return (strdup(temp)); + } + + /* + * Allocate memory for the whole thing and reformat to the new, larger + * buffer... + */ + + if ((buffer = calloc(1, bytes + 1)) != NULL) + vsnprintf(buffer, bytes + 1, format, ap); + + /* + * Return the new string... + */ + + return (buffer); +} + + +/* + * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $". + */ diff --git a/tools/gator/daemon/mxml/mxml.h b/tools/gator/daemon/mxml/mxml.h new file mode 100644 index 00000000000..79c711f4c80 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml.h @@ -0,0 +1,329 @@ +/* + * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $" + * + * Header file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Prevent multiple inclusion... + */ + +#ifndef _mxml_h_ +# define _mxml_h_ + +/* + * Include necessary headers... + */ + +# include +# include +# include +# include +# include + + +/* + * Constants... + */ + +# define MXML_TAB 8 /* Tabs every N columns */ + +# define MXML_NO_CALLBACK 0 /* Don't use a type callback */ +# define MXML_INTEGER_CALLBACK mxml_integer_cb + /* Treat all data as integers */ +# define MXML_OPAQUE_CALLBACK mxml_opaque_cb + /* Treat all data as opaque */ +# define MXML_REAL_CALLBACK mxml_real_cb + /* Treat all data as real numbers */ +# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */ +# define MXML_IGNORE_CALLBACK mxml_ignore_cb + /* Ignore all non-element content */ + +# define MXML_NO_PARENT 0 /* No parent for the node */ + +# define MXML_DESCEND 1 /* Descend when finding/walking */ +# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */ +# define MXML_DESCEND_FIRST -1 /* Descend for first find */ + +# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */ +# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */ +# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */ +# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */ + +# define MXML_ADD_BEFORE 0 /* Add node before specified node */ +# define MXML_ADD_AFTER 1 /* Add node after specified node */ +# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */ + + +/* + * Data types... + */ + +typedef enum mxml_sax_event_e /**** SAX event type. ****/ +{ + MXML_SAX_CDATA, /* CDATA node */ + MXML_SAX_COMMENT, /* Comment node */ + MXML_SAX_DATA, /* Data node */ + MXML_SAX_DIRECTIVE, /* Processing directive node */ + MXML_SAX_ELEMENT_CLOSE, /* Element closed */ + MXML_SAX_ELEMENT_OPEN /* Element opened */ +} mxml_sax_event_t; + +typedef enum mxml_type_e /**** The XML node type. ****/ +{ + MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */ + MXML_ELEMENT, /* XML element with attributes */ + MXML_INTEGER, /* Integer value */ + MXML_OPAQUE, /* Opaque string */ + MXML_REAL, /* Real value */ + MXML_TEXT, /* Text fragment */ + MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */ +} mxml_type_t; + +typedef void (*mxml_custom_destroy_cb_t)(void *); + /**** Custom data destructor ****/ + +typedef void (*mxml_error_cb_t)(const char *); + /**** Error callback function ****/ + +typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/ +{ + char *name; /* Attribute name */ + char *value; /* Attribute value */ +} mxml_attr_t; + +typedef struct mxml_element_s /**** An XML element value. @private@ ****/ +{ + char *name; /* Name of element */ + int num_attrs; /* Number of attributes */ + mxml_attr_t *attrs; /* Attributes */ +} mxml_element_t; + +typedef struct mxml_text_s /**** An XML text value. @private@ ****/ +{ + int whitespace; /* Leading whitespace? */ + char *string; /* Fragment string */ +} mxml_text_t; + +typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/ +{ + void *data; /* Pointer to (allocated) custom data */ + mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ +} mxml_custom_t; + +typedef union mxml_value_u /**** An XML node value. @private@ ****/ +{ + mxml_element_t element; /* Element */ + int integer; /* Integer number */ + char *opaque; /* Opaque string */ + double real; /* Real number */ + mxml_text_t text; /* Text fragment */ + mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ +} mxml_value_t; + +struct mxml_node_s /**** An XML node. @private@ ****/ +{ + mxml_type_t type; /* Node type */ + struct mxml_node_s *next; /* Next node under same parent */ + struct mxml_node_s *prev; /* Previous node under same parent */ + struct mxml_node_s *parent; /* Parent node */ + struct mxml_node_s *child; /* First child node */ + struct mxml_node_s *last_child; /* Last child node */ + mxml_value_t value; /* Node value */ + int ref_count; /* Use count */ + void *user_data; /* User data */ +}; + +typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/ + +struct mxml_index_s /**** An XML node index. @private@ ****/ +{ + char *attr; /* Attribute used for indexing or NULL */ + int num_nodes; /* Number of nodes in index */ + int alloc_nodes; /* Allocated nodes in index */ + int cur_node; /* Current node */ + mxml_node_t **nodes; /* Node array */ +}; + +typedef struct mxml_index_s mxml_index_t; + /**** An XML node index. ****/ + +typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *); + /**** Custom data load callback function ****/ + +typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *); + /**** Custom data save callback function ****/ + +typedef int (*mxml_entity_cb_t)(const char *); + /**** Entity callback function */ + +typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *); + /**** Load callback function ****/ + +typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int); + /**** Save callback function ****/ + +typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *); + /**** SAX callback function ****/ + + +/* + * C++ support... + */ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + +/* + * Prototypes... + */ + +extern void mxmlAdd(mxml_node_t *parent, int where, + mxml_node_t *child, mxml_node_t *node); +extern void mxmlDelete(mxml_node_t *node); +extern void mxmlElementDeleteAttr(mxml_node_t *node, + const char *name); +extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name); +extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, + const char *value); +extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlEntityAddCallback(mxml_entity_cb_t cb); +extern const char *mxmlEntityGetName(int val); +extern int mxmlEntityGetValue(const char *name); +extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); +extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, + const char *name, const char *attr, + const char *value, int descend); +extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); +extern const char *mxmlGetCDATA(mxml_node_t *node); +extern const void *mxmlGetCustom(mxml_node_t *node); +extern const char *mxmlGetElement(mxml_node_t *node); +extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); +extern int mxmlGetInteger(mxml_node_t *node); +extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node); +extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); +extern const char *mxmlGetOpaque(mxml_node_t *node); +extern mxml_node_t *mxmlGetParent(mxml_node_t *node); +extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node); +extern double mxmlGetReal(mxml_node_t *node); +extern int mxmlGetRefCount(mxml_node_t *node); +extern const char *mxmlGetText(mxml_node_t *node, int *whitespace); +extern mxml_type_t mxmlGetType(mxml_node_t *node); +extern void *mxmlGetUserData(mxml_node_t *node); +extern void mxmlIndexDelete(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, + const char *element, + const char *value); +extern int mxmlIndexGetCount(mxml_index_t *ind); +extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element, + const char *attr); +extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind); +extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); +extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, + mxml_custom_destroy_cb_t destroy); +extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); +extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer); +extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque); +extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real); +extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, + const char *string); +extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern mxml_node_t *mxmlNewXML(const char *version); +extern int mxmlRelease(mxml_node_t *node); +extern void mxmlRemove(mxml_node_t *node); +extern int mxmlRetain(mxml_node_t *node); +extern char *mxmlSaveAllocString(mxml_node_t *node, + mxml_save_cb_t cb); +extern int mxmlSaveFd(mxml_node_t *node, int fd, + mxml_save_cb_t cb); +extern int mxmlSaveFile(mxml_node_t *node, FILE *fp, + mxml_save_cb_t cb); +extern int mxmlSaveString(mxml_node_t *node, char *buffer, + int bufsize, mxml_save_cb_t cb); +extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern int mxmlSetCDATA(mxml_node_t *node, const char *data); +extern int mxmlSetCustom(mxml_node_t *node, void *data, + mxml_custom_destroy_cb_t destroy); +extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, + mxml_custom_save_cb_t save); +extern int mxmlSetElement(mxml_node_t *node, const char *name); +extern void mxmlSetErrorCallback(mxml_error_cb_t cb); +extern int mxmlSetInteger(mxml_node_t *node, int integer); +extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque); +extern int mxmlSetReal(mxml_node_t *node, double real); +extern int mxmlSetText(mxml_node_t *node, int whitespace, + const char *string); +extern int mxmlSetTextf(mxml_node_t *node, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlSetUserData(mxml_node_t *node, void *data); +extern void mxmlSetWrapMargin(int column); +extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top, + int descend); +extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, + int descend); + + +/* + * Semi-private functions... + */ + +extern void mxml_error(const char *format, ...); +extern mxml_type_t mxml_ignore_cb(mxml_node_t *node); +extern mxml_type_t mxml_integer_cb(mxml_node_t *node); +extern mxml_type_t mxml_opaque_cb(mxml_node_t *node); +extern mxml_type_t mxml_real_cb(mxml_node_t *node); + + +/* + * C++ support... + */ + +# ifdef __cplusplus +} +# endif /* __cplusplus */ +#endif /* !_mxml_h_ */ + + +/* + * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $". + */ -- cgit v1.2.3 From 0abead3d93e8dc45eb430d2c4984b90622dbf6ee Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 17:35:03 +0100 Subject: gator: Add config for building the module in-tree Signed-off-by: Jon Medhurst --- drivers/Kconfig | 2 ++ drivers/Makefile | 2 ++ drivers/gator/Kconfig | 33 +++++++++++++++++++++++++++++++++ drivers/gator/Makefile | 14 +++++++++----- 4 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 drivers/gator/Kconfig diff --git a/drivers/Kconfig b/drivers/Kconfig index 5afe5d1f199..6b7e6f6c102 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -136,4 +136,6 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +source "drivers/gator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index c07be024b96..5c20031df74 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -132,3 +132,5 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_PM_DEVFREQ) += devfreq/ + +obj-$(CONFIG_GATOR) += gator/ diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig new file mode 100644 index 00000000000..14b3d619d18 --- /dev/null +++ b/drivers/gator/Kconfig @@ -0,0 +1,33 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) + select TRACING + +config GATOR_WITH_MALI_SUPPORT + bool + +choice + prompt "Enable Mali GPU support in Gator" + depends on GATOR + optional + +config GATOR_MALI_400MP + bool "Mali-400MP" + select GATOR_WITH_MALI_SUPPORT + +config GATOR_MALI_T6XX + bool "Mali-T604 or Mali-T658" + select GATOR_WITH_MALI_SUPPORT + +endchoice + +config GATOR_MALI_PATH + string "Path to Mali driver" + depends on GATOR_WITH_MALI_SUPPORT + default "drivers/gpu/arm/mali400mp" + help + The gator code adds this to its include path so it can get the Mali + trace headers with: #include "linux/mali_linux_trace.h" diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile index 667637e50c4..5f4ab48452e 100644 --- a/drivers/gator/Makefile +++ b/drivers/gator/Makefile @@ -3,7 +3,7 @@ ifneq ($(KERNELRELEASE),) # Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c # EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING -obj-m := gator.o +obj-$(CONFIG_GATOR) := gator.o gator-y := gator_main.o \ gator_events_irq.o \ @@ -15,13 +15,17 @@ gator-y := gator_main.o \ gator-y += gator_events_mmaped.o -ifneq ($(GATOR_WITH_MALI_SUPPORT),) -ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) +ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) + +ifeq ($(CONFIG_GATOR_MALI_T6XX),y) gator-y += gator_events_mali_t6xx.o else gator-y += gator_events_mali.o endif -EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) + +ccflags-y += -I$(CONFIG_GATOR_MALI_PATH) +ccflags-$(CONFIG_GATOR_MALI_400MP) += -DMALI_SUPPORT=MALI_400 +ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx endif gator-$(CONFIG_ARM) += gator_events_armv6.o \ @@ -38,7 +42,7 @@ clean-files := gator_events.h silent_chk_events.h = : gator_events.h: FORCE @$($(quiet)chk_events.h) - $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@ + $(Q)cd $(srctree)/$(src) ; $(CONFIG_SHELL) gator_events.sh $(objtree)/$(obj)/$@ else -- cgit v1.2.3 From b665aa8925a35e0310a9e9f663ecbe70565a4eeb Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 11 May 2012 15:24:24 +0100 Subject: gator: Default GATOR_MALI_INTERFACE_STYLE to '2' At the moment, we only have users with the old style interface, so make our life easier by making it the default. Signed-off-by: Jon Medhurst --- drivers/gator/gator_events_mali.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gator/gator_events_mali.c b/drivers/gator/gator_events_mali.c index 21a63246f02..e3b6e13923a 100755 --- a/drivers/gator/gator_events_mali.c +++ b/drivers/gator/gator_events_mali.c @@ -14,6 +14,14 @@ #include "linux/mali_linux_trace.h" +#if !defined(GATOR_MALI_INTERFACE_STYLE) +/* + * At the moment, we only have users with the old style interface, so + * make our life easier by making it the default... + */ +#define GATOR_MALI_INTERFACE_STYLE (2) +#endif + /* * There are (currently) three different variants of the comms between gator and Mali: * 1 (deprecated): No software counter support -- cgit v1.2.3 From 30cd9241c00f1a8d71db3d446cc4527101ace7ec Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 17:38:12 +0100 Subject: gator-daemon: Fix building on armhf Remove compiler options for arm arch and interworking as they break the build and are superfluous now we build for armhf. Signed-off-by: Jon Medhurst --- tools/gator/daemon/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile index aa1c0056a8a..950e87060ac 100644 --- a/tools/gator/daemon/Makefile +++ b/tools/gator/daemon/Makefile @@ -18,7 +18,7 @@ GCC=$(CROSS_COMPILE)gcc # -std=c++98 is the 1998 c++ standard # -march=armv5t is required to set the minimum architecture # -mthumb-interwork is required for interworking to ARM or Thumb stdlibc -CFLAGS=-O3 -Wall -Werror -march=armv5t -mthumb-interwork +CFLAGS=-O3 -Wall -Werror TARGET=gatord C_SRC = $(wildcard mxml/*.c) CPP_SRC = $(wildcard *.cpp) -- cgit v1.2.3 From b859a3300808bf89dc475c60e0b97f73103975e8 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 14 May 2012 11:28:08 +0100 Subject: gator-daemon: Fix sequence-point compile error In mxml/mxml-file.c in the expression: (getc(fp) << 8) | getc(fp) the C standard allows right-hand getc() to be called before left hand one, which would cause a bug. The compiler gives an warning about this and we are treating warnings as errors this breaks the build with: error: operation on 'fp->_r' may be undefined [-Werror=sequence-point] We can fix this by rewriting the expressions in two parts, however the code in question has licensing clauses for modified versions which have not yet been resolved. Therefore, for now, we will disable the problematic error in the makefiles. Signed-off-by: Jon Medhurst --- tools/gator/daemon/Android.mk | 2 +- tools/gator/daemon/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk index 3ab55b91ad5..715e5b62ba1 100755 --- a/tools/gator/daemon/Android.mk +++ b/tools/gator/daemon/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) $(shell cd $(LOCAL_PATH);cat events_header.xml events-*\.xml events_footer.xml > events.xml;xxd -i events.xml > events_xml.h;xxd -i configuration.xml > configuration_xml.h) -LOCAL_CFLAGS += -Wall -O3 -ftree-vectorize +LOCAL_CFLAGS += -Wall -O3 -ftree-vectorize -Wno-error=sequence-point LOCAL_SRC_FILES:= \ CapturedXML.cpp \ diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile index 950e87060ac..c517a873728 100644 --- a/tools/gator/daemon/Makefile +++ b/tools/gator/daemon/Makefile @@ -18,7 +18,7 @@ GCC=$(CROSS_COMPILE)gcc # -std=c++98 is the 1998 c++ standard # -march=armv5t is required to set the minimum architecture # -mthumb-interwork is required for interworking to ARM or Thumb stdlibc -CFLAGS=-O3 -Wall -Werror +CFLAGS=-O3 -Wall -Werror -Wno-error=sequence-point TARGET=gatord C_SRC = $(wildcard mxml/*.c) CPP_SRC = $(wildcard *.cpp) -- cgit v1.2.3 From 8bed00134a4b0ba837c1b16effaec86cf35679e6 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 18 May 2012 09:06:15 +0100 Subject: gator: Revert #error about lack of CONFIG_PERF_EVENTS Make this a #warning as in version 5.9. This lets Gator build (with much reduced functionality) on platforms which require CONFIG_PERF_EVENTS to be disabled for whatever reason. Signed-off-by: Jon Medhurst --- drivers/gator/gator_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index 7d4881278dd..988045f187d 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -48,9 +48,9 @@ static unsigned long gator_protocol_version = 9; #if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) #ifndef CONFIG_PERF_EVENTS -#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters #elif !defined CONFIG_HW_PERF_EVENTS -#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters #endif #endif -- cgit v1.2.3 From 09f333ed6069575a71fabc999c4dc39b6594d727 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 4 May 2012 14:39:28 +0100 Subject: gator: ubuntu: Add packaging Files and scripts for creating Debian packages for Gator and ARM DS-5 target. Signed-off-by: Jon Medhurst --- tools/gator/Makefile | 4 ++ tools/gator/debian/changelog | 98 +++++++++++++++++++++++++++++++++ tools/gator/debian/compat | 1 + tools/gator/debian/control | 28 ++++++++++ tools/gator/debian/copyright | 15 +++++ tools/gator/debian/gator-daemon.install | 1 + tools/gator/debian/gator-daemon.postrm | 41 ++++++++++++++ tools/gator/debian/gator-daemon.preinst | 36 ++++++++++++ tools/gator/debian/gator-daemon.upstart | 10 ++++ tools/gator/debian/rules | 15 +++++ tools/gator/debian/scripts/do-packaging | 86 +++++++++++++++++++++++++++++ 11 files changed, 335 insertions(+) create mode 100644 tools/gator/Makefile create mode 100644 tools/gator/debian/changelog create mode 100644 tools/gator/debian/compat create mode 100644 tools/gator/debian/control create mode 100644 tools/gator/debian/copyright create mode 100644 tools/gator/debian/gator-daemon.install create mode 100644 tools/gator/debian/gator-daemon.postrm create mode 100644 tools/gator/debian/gator-daemon.preinst create mode 100644 tools/gator/debian/gator-daemon.upstart create mode 100755 tools/gator/debian/rules create mode 100755 tools/gator/debian/scripts/do-packaging diff --git a/tools/gator/Makefile b/tools/gator/Makefile new file mode 100644 index 00000000000..4ab73b0ebed --- /dev/null +++ b/tools/gator/Makefile @@ -0,0 +1,4 @@ +all: + $(MAKE) -C daemon $@ +clean: + $(MAKE) -C daemon $@ diff --git a/tools/gator/debian/changelog b/tools/gator/debian/changelog new file mode 100644 index 00000000000..dd0b63c549f --- /dev/null +++ b/tools/gator/debian/changelog @@ -0,0 +1,98 @@ +gator (5.10.0) precise; urgency=low + + * New release from ARM. + + -- Jon Medhurst (Tixy) Thu, 10 May 2012 16:47:32 +0000 + +gator (5.9.7) precise; urgency=low + + * New source and binary package layout with automated packaging scripts. + + -- Jon Medhurst (Tixy) Wed, 2 May 2012 17:01:18 +0000 + +gator-daemon (5.9-6) precise; urgency=low + + * Remove Depends: gator-module* as Linaro kernels should + now all contain the required module. + + -- Jon Medhurst (Tixy) Wed, 25 Apr 2012 18:06:28 +0000 + +gator-daemon (5.9-5) precise; urgency=low + + * Move to armhf and Precise + + -- Jon Medhurst (Tixy) Wed, 25 Apr 2012 10:16:00 +0000 + +gator-daemon (5.9-4) oneiric; urgency=low + + * Add alternative Depends on gator-module-in-tree. + + -- Jon Medhurst (Tixy) Fri, 16 Mar 2012 18:24:27 +0000 + +gator-daemon (5.9-3) oneiric; urgency=low + + * Add missing build depends: 'quilt'. + + -- Jon Medhurst (Tixy) Thu, 23 Feb 2012 13:59:08 +0000 + +gator-daemon (5.9-2) oneiric; urgency=low + + [ Wade Cherry ] + * Fix counters initialisation + + -- Jon Medhurst (Tixy) Thu, 23 Feb 2012 12:06:33 +0000 + +gator-daemon (5.9-1) oneiric; urgency=low + + * New upstream release (DS-5.9) + + -- Jon Medhurst (Tixy) Mon, 20 Feb 2012 10:35:47 +0000 + +gator-daemon (5.9~rc1-1) oneiric; urgency=low + + * New upstream release (DS-5.9) + + -- Jon Medhurst (Tixy) Wed, 15 Feb 2012 14:38:26 +0000 + +gator-daemon (5.8-1) oneiric; urgency=low + + * New upstream release + + -- Ricardo Salveti de Araujo Fri, 25 Nov 2011 12:41:57 -0200 + +gator-daemon (5.8~rc2-1) oneiric; urgency=low + + * New upstream version (DS-5.8~rc2) + + -- Ricardo Salveti de Araujo Wed, 16 Nov 2011 15:30:07 -0200 + +gator-daemon (5.7-2) natty; urgency=low + + * Package now depends on the new gator-module meta package + + -- Ricardo Salveti de Araujo Mon, 10 Oct 2011 23:39:34 -0300 + +gator-daemon (5.7-1) natty; urgency=low + + * Updated to DS-5.7 release + * Added sources (GPLv2) + + -- Pawel Moll Fri, 30 Sep 2011 16:02:05 +0100 + +gator-daemon (5.6-2) natty; urgency=low + + * Added dependency on 5.6 version of the gator-module[-dkms] + + -- Pawel Moll Tue, 13 Sep 2011 15:59:37 +0100 + +gator-daemon (5.6-1) natty; urgency=low + + * Updated to DS-5.6 release + + -- Pawel Moll Wed, 03 Aug 2011 11:54:40 +0100 + +gator-daemon (5.5-1) natty; urgency=low + + * Initial release + + -- Pawel Moll Mon, 06 Jun 2011 18:42:31 +0100 diff --git a/tools/gator/debian/compat b/tools/gator/debian/compat new file mode 100644 index 00000000000..45a4fb75db8 --- /dev/null +++ b/tools/gator/debian/compat @@ -0,0 +1 @@ +8 diff --git a/tools/gator/debian/control b/tools/gator/debian/control new file mode 100644 index 00000000000..b6defbc6d02 --- /dev/null +++ b/tools/gator/debian/control @@ -0,0 +1,28 @@ +Source: gator +Section: devel +Priority: extra +Maintainer: Pawel Moll +Build-Depends: debhelper (>= 7.0.5), vim-common +Standards-Version: 3.9.2 +Homepage: http://www.arm.com/ds-5 + +Package: gator +Architecture: armhf +Depends: ${misc:Depends}, gator-daemon (>= 5.9.0) +Description: ARM Streamline gator + This meta-package depends on gator target packages required to use + ARM Streamline Performance Analyzer. + +Package: gator-daemon +Architecture: armhf +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: ARM Streamline gator daemon + The target-side daemon gathering data for ARM Streamline + Performance Analyzer. + +Package: arm-ds5-target +Architecture: armhf +Depends: ${misc:Depends}, gdbserver (>= 6.8), openssh-server | ssh-server, gator (>= 5.9.0) +Description: ARM DS-5 target dependencies + This meta-package depends on all components required to receive + full ARM DS-5 experience. diff --git a/tools/gator/debian/copyright b/tools/gator/debian/copyright new file mode 100644 index 00000000000..3ed568c02e8 --- /dev/null +++ b/tools/gator/debian/copyright @@ -0,0 +1,15 @@ +Files: * +Copyright: 2010-2012 ARM Ltd. +License: GPL-2 + 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. + . + 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 + with your system, in /usr/share/common-licenses/GPL-2. If not, see + . diff --git a/tools/gator/debian/gator-daemon.install b/tools/gator/debian/gator-daemon.install new file mode 100644 index 00000000000..7b0d218c425 --- /dev/null +++ b/tools/gator/debian/gator-daemon.install @@ -0,0 +1 @@ +daemon/gatord /usr/sbin/ diff --git a/tools/gator/debian/gator-daemon.postrm b/tools/gator/debian/gator-daemon.postrm new file mode 100644 index 00000000000..97518f4e1a1 --- /dev/null +++ b/tools/gator/debian/gator-daemon.postrm @@ -0,0 +1,41 @@ +#!/bin/sh +# postrm script for gator-daemon +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + purge) + rm -rf /var/lib/gatord/ + ;; + + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/tools/gator/debian/gator-daemon.preinst b/tools/gator/debian/gator-daemon.preinst new file mode 100644 index 00000000000..bb2dc7e5fe7 --- /dev/null +++ b/tools/gator/debian/gator-daemon.preinst @@ -0,0 +1,36 @@ +#!/bin/sh +# preinst script for gator-daemon +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `install' +# * `install' +# * `upgrade' +# * `abort-upgrade' +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + install|upgrade) + mkdir -p /var/lib/gatord/ + ;; + + abort-upgrade) + ;; + + *) + echo "preinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 diff --git a/tools/gator/debian/gator-daemon.upstart b/tools/gator/debian/gator-daemon.upstart new file mode 100644 index 00000000000..1564efc5339 --- /dev/null +++ b/tools/gator/debian/gator-daemon.upstart @@ -0,0 +1,10 @@ +description "Load gator driver module and launch gator daemon" +author "Pawel Moll " + +start on filesystem + +exec /usr/sbin/gatord -c /var/lib/gatord/configuration.xml + +pre-start script + modprobe -q gator || true +end script diff --git a/tools/gator/debian/rules b/tools/gator/debian/rules new file mode 100755 index 00000000000..c747748ff83 --- /dev/null +++ b/tools/gator/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +DEB_HOST_GNU_TYPE = $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE = $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE)) + CROSS_COMPILE ?= $(DEB_HOST_GNU_TYPE)- +endif + +export CROSS_COMPILE + +export DH_OPTIONS + +%: + dh $@ diff --git a/tools/gator/debian/scripts/do-packaging b/tools/gator/debian/scripts/do-packaging new file mode 100755 index 00000000000..9562f2a685b --- /dev/null +++ b/tools/gator/debian/scripts/do-packaging @@ -0,0 +1,86 @@ +#!/bin/bash + +set -e + +while [ $1 ] +do + OPT=$1 + shift + if [ "$OPT" == "--commit" ] + then + OPT_COMMIT=y + continue + fi + if [ "$OPT" == "--sign" ] + then + OPT_SIGN=y + continue + fi + echo $0: Unknown option: "$OPT" 1>&2 + exit 1 +done + + +# +# Clean working dirctory +# + +git reset --hard HEAD +git clean -dfx .. + + +# +# Update changelog +# + +if [ "$DEBEMAIL" == "" -o "$DEBFULLNAME" == "" ] +then + export DEBEMAIL='jon.medhurst@linaro.org' + export DEBFULLNAME='Jon Medhurst (Tixy)' +fi +dch -i -c debian/changelog CHANGE_MSG + +TIMESTAMP="$(date -u +%Y%m%d%H%M%S)" +sed -i -e "1 s/ubuntu1/-$TIMESTAMP/" debian/changelog + +GIT_COMMIT="$(git rev-parse HEAD)" +GIT_REMOTE_AND_BRANCH="$(git show-ref | sed -n 's,'$GIT_COMMIT'.*refs/remotes/\(.*\),\1,p' | sed -n 1p)" +GIT_REMOTE=$(echo $GIT_REMOTE_AND_BRANCH | awk -F/ '{print $1}') +GIT_BRANCH=$(echo $GIT_REMOTE_AND_BRANCH | awk -F/ '{print $2}') +GIT_URL="$(git remote -v show $GIT_REMOTE | sed -n 's/.*Fetch URL: \(.*\)/\1/p')" +GIT_TAG="$(git describe --exact-match --tags $GIT_COMMIT 2>/dev/null || echo "")" + +CHANGE_MSG="Automatically packaged from git repository:\n" +CHANGE_MSG="$CHANGE_MSG URL: $GIT_URL\n" +CHANGE_MSG="$CHANGE_MSG Tag: $GIT_TAG\n" +CHANGE_MSG="$CHANGE_MSG Branch: $GIT_BRANCH\n" +CHANGE_MSG="$CHANGE_MSG Commit: $GIT_COMMIT" +sed -i -e "s%CHANGE_MSG%$CHANGE_MSG%" debian/changelog + +echo $CHANGE_MSG + +VERSION="$(sed -n '1s/.*(\(.*\)).*/\1/p' debian/changelog)" + + +# +# Commit changes +# + +if [ $OPT_COMMIT ] +then + git add debian/changelog + git commit -s -m "gator-$VERSION: Automated packaging" +fi + + +# +# Build packages +# + +if [ ! $OPT_SIGN ] +then + DEBUILD_SIGN="-us -uc" +fi + +debuild $DEBUILD_SIGN -aarmhf -b +debuild --no-tgz-check $DEBUILD_SIGN -S -- cgit v1.2.3