From e49849053ac342ce82c92aa0d2244e4767fd1fbb Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 4 Aug 2014 11:24:17 +0100 Subject: gator: Version 5.19 Signed-off-by: Jon Medhurst Signed-off-by: Mark Brown --- drivers/gator/Kconfig | 39 + drivers/gator/LICENSE | 339 +++ drivers/gator/Makefile | 81 + drivers/gator/gator.h | 149 + drivers/gator/gator_annotate.c | 186 ++ drivers/gator/gator_annotate_kernel.c | 202 ++ drivers/gator/gator_backtrace.c | 208 ++ drivers/gator/gator_buffer.c | 168 ++ drivers/gator/gator_buffer_write.c | 80 + drivers/gator/gator_cookies.c | 437 +++ drivers/gator/gator_events_armv6.c | 237 ++ drivers/gator/gator_events_armv7.c | 314 ++ drivers/gator/gator_events_block.c | 163 ++ drivers/gator/gator_events_ccn-504.c | 346 +++ drivers/gator/gator_events_irq.c | 165 ++ drivers/gator/gator_events_l2c-310.c | 208 ++ drivers/gator/gator_events_mali_4xx.c | 669 +++++ drivers/gator/gator_events_mali_4xx.h | 18 + drivers/gator/gator_events_mali_common.c | 69 + drivers/gator/gator_events_mali_common.h | 78 + drivers/gator/gator_events_mali_t6xx.c | 567 ++++ drivers/gator/gator_events_mali_t6xx_hw.c | 913 ++++++ drivers/gator/gator_events_mali_t6xx_hw_test.c | 55 + drivers/gator/gator_events_meminfo.c | 409 +++ drivers/gator/gator_events_mmapped.c | 209 ++ drivers/gator/gator_events_net.c | 172 ++ drivers/gator/gator_events_perf_pmu.c | 587 ++++ drivers/gator/gator_events_sched.c | 113 + drivers/gator/gator_events_scorpion.c | 669 +++++ drivers/gator/gator_events_threads.c | 115 + drivers/gator/gator_fs.c | 365 +++ drivers/gator/gator_hrtimer_gator.c | 80 + drivers/gator/gator_iks.c | 197 ++ drivers/gator/gator_main.c | 1460 ++++++++++ drivers/gator/gator_marshaling.c | 362 +++ drivers/gator/gator_trace_gpu.c | 333 +++ drivers/gator/gator_trace_power.c | 203 ++ drivers/gator/gator_trace_sched.c | 297 ++ drivers/gator/mali/mali_dd_gator_api.h | 40 + .../gator/mali/mali_mjollnir_profiling_gator_api.h | 163 ++ .../gator/mali/mali_utgard_profiling_gator_api.h | 201 ++ drivers/gator/mali_t6xx.mk | 39 + tools/gator/daemon/Android.mk | 65 + tools/gator/daemon/Application.mk | 1 + tools/gator/daemon/Buffer.cpp | 391 +++ tools/gator/daemon/Buffer.h | 102 + tools/gator/daemon/CapturedXML.cpp | 144 + tools/gator/daemon/CapturedXML.h | 26 + tools/gator/daemon/Child.cpp | 346 +++ tools/gator/daemon/Child.h | 33 + tools/gator/daemon/Config.h | 17 + tools/gator/daemon/ConfigurationXML.cpp | 217 ++ tools/gator/daemon/ConfigurationXML.h | 38 + tools/gator/daemon/Counter.h | 65 + tools/gator/daemon/Driver.cpp | 15 + tools/gator/daemon/Driver.h | 48 + tools/gator/daemon/DriverSource.cpp | 321 ++ tools/gator/daemon/DriverSource.h | 57 + tools/gator/daemon/DynBuf.cpp | 139 + tools/gator/daemon/DynBuf.h | 52 + tools/gator/daemon/EventsXML.cpp | 76 + tools/gator/daemon/EventsXML.h | 21 + tools/gator/daemon/ExternalSource.cpp | 215 ++ tools/gator/daemon/ExternalSource.h | 49 + tools/gator/daemon/FSDriver.cpp | 212 ++ tools/gator/daemon/FSDriver.h | 44 + tools/gator/daemon/Fifo.cpp | 130 + tools/gator/daemon/Fifo.h | 48 + tools/gator/daemon/Hwmon.cpp | 352 +++ tools/gator/daemon/Hwmon.h | 45 + tools/gator/daemon/KMod.cpp | 110 + tools/gator/daemon/KMod.h | 27 + tools/gator/daemon/LocalCapture.cpp | 131 + tools/gator/daemon/LocalCapture.h | 26 + tools/gator/daemon/Logging.cpp | 78 + tools/gator/daemon/Logging.h | 36 + tools/gator/daemon/Makefile | 25 + tools/gator/daemon/Makefile_aarch64 | 12 + tools/gator/daemon/MaliVideoDriver.cpp | 253 ++ tools/gator/daemon/MaliVideoDriver.h | 50 + tools/gator/daemon/Monitor.cpp | 68 + tools/gator/daemon/Monitor.h | 33 + tools/gator/daemon/OlySocket.cpp | 282 ++ tools/gator/daemon/OlySocket.h | 55 + tools/gator/daemon/OlyUtility.cpp | 227 ++ tools/gator/daemon/OlyUtility.h | 42 + tools/gator/daemon/PerfBuffer.cpp | 139 + tools/gator/daemon/PerfBuffer.h | 39 + tools/gator/daemon/PerfDriver.cpp | 409 +++ tools/gator/daemon/PerfDriver.h | 60 + tools/gator/daemon/PerfGroup.cpp | 226 ++ tools/gator/daemon/PerfGroup.h | 57 + tools/gator/daemon/PerfSource.cpp | 275 ++ tools/gator/daemon/PerfSource.h | 54 + tools/gator/daemon/Proc.cpp | 215 ++ tools/gator/daemon/Proc.h | 17 + tools/gator/daemon/Sender.cpp | 134 + tools/gator/daemon/Sender.h | 42 + tools/gator/daemon/SessionData.cpp | 194 ++ tools/gator/daemon/SessionData.h | 86 + tools/gator/daemon/SessionXML.cpp | 111 + tools/gator/daemon/SessionXML.h | 42 + tools/gator/daemon/Source.cpp | 33 + tools/gator/daemon/Source.h | 40 + tools/gator/daemon/StreamlineSetup.cpp | 272 ++ tools/gator/daemon/StreamlineSetup.h | 50 + tools/gator/daemon/UEvent.cpp | 76 + tools/gator/daemon/UEvent.h | 36 + tools/gator/daemon/UserSpaceSource.cpp | 89 + tools/gator/daemon/UserSpaceSource.h | 38 + tools/gator/daemon/c++.cpp | 40 + tools/gator/daemon/common.mk | 52 + tools/gator/daemon/defaults.xml | 67 + tools/gator/daemon/escape.c | 75 + tools/gator/daemon/events-ARM11.xml | 39 + tools/gator/daemon/events-ARM11MPCore.xml | 26 + tools/gator/daemon/events-CCI-400.xml | 98 + tools/gator/daemon/events-CCN-504.xml | 113 + tools/gator/daemon/events-Cortex-A12.xml | 86 + tools/gator/daemon/events-Cortex-A15.xml | 68 + tools/gator/daemon/events-Cortex-A5.xml | 36 + tools/gator/daemon/events-Cortex-A53.xml | 87 + tools/gator/daemon/events-Cortex-A57.xml | 87 + tools/gator/daemon/events-Cortex-A7.xml | 43 + tools/gator/daemon/events-Cortex-A8.xml | 52 + tools/gator/daemon/events-Cortex-A9.xml | 65 + tools/gator/daemon/events-Filesystem.xml | 11 + tools/gator/daemon/events-Krait-architected.xml | 22 + tools/gator/daemon/events-L2C-310.xml | 18 + tools/gator/daemon/events-Linux.xml | 16 + tools/gator/daemon/events-Mali-4xx.xml | 245 ++ tools/gator/daemon/events-Mali-T6xx.xml | 46 + tools/gator/daemon/events-Mali-T6xx_hw.xml | 91 + tools/gator/daemon/events-Mali-V500.xml | 29 + tools/gator/daemon/events-Perf-Hardware.xml | 12 + tools/gator/daemon/events-Scorpion.xml | 107 + tools/gator/daemon/events-ScorpionMP.xml | 90 + tools/gator/daemon/events_footer.xml | 1 + tools/gator/daemon/events_header.xml | 2 + tools/gator/daemon/k/perf_event.3.12.h | 792 +++++ tools/gator/daemon/k/perf_event.h | 1 + tools/gator/daemon/libsensors/COPYING.LGPL | 502 ++++ tools/gator/daemon/libsensors/access.c | 561 ++++ tools/gator/daemon/libsensors/access.h | 33 + tools/gator/daemon/libsensors/conf-lex.c | 2881 ++++++++++++++++++ tools/gator/daemon/libsensors/conf-lex.l | 372 +++ tools/gator/daemon/libsensors/conf-parse.c | 2042 +++++++++++++ tools/gator/daemon/libsensors/conf-parse.h | 84 + tools/gator/daemon/libsensors/conf-parse.y | 347 +++ tools/gator/daemon/libsensors/conf.h | 34 + tools/gator/daemon/libsensors/data.c | 278 ++ tools/gator/daemon/libsensors/data.h | 184 ++ tools/gator/daemon/libsensors/error.c | 92 + tools/gator/daemon/libsensors/error.h | 74 + tools/gator/daemon/libsensors/general.c | 85 + tools/gator/daemon/libsensors/general.h | 39 + tools/gator/daemon/libsensors/init.c | 341 +++ tools/gator/daemon/libsensors/init.h | 28 + tools/gator/daemon/libsensors/scanner.h | 32 + tools/gator/daemon/libsensors/sensors.h | 311 ++ tools/gator/daemon/libsensors/sysfs.c | 926 ++++++ tools/gator/daemon/libsensors/sysfs.h | 43 + tools/gator/daemon/libsensors/version.h | 1 + tools/gator/daemon/main.cpp | 547 ++++ 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 | 3082 ++++++++++++++++++++ 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 +++ 178 files changed, 39743 insertions(+) create mode 100644 drivers/gator/Kconfig 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 100644 drivers/gator/gator_annotate_kernel.c create mode 100644 drivers/gator/gator_backtrace.c create mode 100644 drivers/gator/gator_buffer.c create mode 100644 drivers/gator/gator_buffer_write.c create mode 100644 drivers/gator/gator_cookies.c 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_ccn-504.c create mode 100644 drivers/gator/gator_events_irq.c create mode 100644 drivers/gator/gator_events_l2c-310.c create mode 100644 drivers/gator/gator_events_mali_4xx.c create mode 100644 drivers/gator/gator_events_mali_4xx.h create mode 100644 drivers/gator/gator_events_mali_common.c create mode 100644 drivers/gator/gator_events_mali_common.h create mode 100644 drivers/gator/gator_events_mali_t6xx.c create mode 100644 drivers/gator/gator_events_mali_t6xx_hw.c create mode 100644 drivers/gator/gator_events_mali_t6xx_hw_test.c create mode 100644 drivers/gator/gator_events_meminfo.c create mode 100644 drivers/gator/gator_events_mmapped.c create mode 100644 drivers/gator/gator_events_net.c create mode 100644 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_events_threads.c create mode 100644 drivers/gator/gator_fs.c create mode 100644 drivers/gator/gator_hrtimer_gator.c create mode 100644 drivers/gator/gator_iks.c create mode 100644 drivers/gator/gator_main.c create mode 100644 drivers/gator/gator_marshaling.c create mode 100644 drivers/gator/gator_trace_gpu.c create mode 100644 drivers/gator/gator_trace_power.c create mode 100644 drivers/gator/gator_trace_sched.c create mode 100644 drivers/gator/mali/mali_dd_gator_api.h create mode 100644 drivers/gator/mali/mali_mjollnir_profiling_gator_api.h create mode 100644 drivers/gator/mali/mali_utgard_profiling_gator_api.h create mode 100644 drivers/gator/mali_t6xx.mk create mode 100644 tools/gator/daemon/Android.mk create mode 100644 tools/gator/daemon/Application.mk create mode 100644 tools/gator/daemon/Buffer.cpp create mode 100644 tools/gator/daemon/Buffer.h 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/Config.h create mode 100644 tools/gator/daemon/ConfigurationXML.cpp create mode 100644 tools/gator/daemon/ConfigurationXML.h create mode 100644 tools/gator/daemon/Counter.h create mode 100644 tools/gator/daemon/Driver.cpp create mode 100644 tools/gator/daemon/Driver.h create mode 100644 tools/gator/daemon/DriverSource.cpp create mode 100644 tools/gator/daemon/DriverSource.h create mode 100644 tools/gator/daemon/DynBuf.cpp create mode 100644 tools/gator/daemon/DynBuf.h create mode 100644 tools/gator/daemon/EventsXML.cpp create mode 100644 tools/gator/daemon/EventsXML.h create mode 100644 tools/gator/daemon/ExternalSource.cpp create mode 100644 tools/gator/daemon/ExternalSource.h create mode 100644 tools/gator/daemon/FSDriver.cpp create mode 100644 tools/gator/daemon/FSDriver.h create mode 100644 tools/gator/daemon/Fifo.cpp create mode 100644 tools/gator/daemon/Fifo.h create mode 100644 tools/gator/daemon/Hwmon.cpp create mode 100644 tools/gator/daemon/Hwmon.h create mode 100644 tools/gator/daemon/KMod.cpp create mode 100644 tools/gator/daemon/KMod.h create mode 100644 tools/gator/daemon/LocalCapture.cpp create mode 100644 tools/gator/daemon/LocalCapture.h create mode 100644 tools/gator/daemon/Logging.cpp create mode 100644 tools/gator/daemon/Logging.h create mode 100644 tools/gator/daemon/Makefile create mode 100644 tools/gator/daemon/Makefile_aarch64 create mode 100644 tools/gator/daemon/MaliVideoDriver.cpp create mode 100644 tools/gator/daemon/MaliVideoDriver.h create mode 100644 tools/gator/daemon/Monitor.cpp create mode 100644 tools/gator/daemon/Monitor.h create mode 100644 tools/gator/daemon/OlySocket.cpp create mode 100644 tools/gator/daemon/OlySocket.h create mode 100644 tools/gator/daemon/OlyUtility.cpp create mode 100644 tools/gator/daemon/OlyUtility.h create mode 100644 tools/gator/daemon/PerfBuffer.cpp create mode 100644 tools/gator/daemon/PerfBuffer.h create mode 100644 tools/gator/daemon/PerfDriver.cpp create mode 100644 tools/gator/daemon/PerfDriver.h create mode 100644 tools/gator/daemon/PerfGroup.cpp create mode 100644 tools/gator/daemon/PerfGroup.h create mode 100644 tools/gator/daemon/PerfSource.cpp create mode 100644 tools/gator/daemon/PerfSource.h create mode 100644 tools/gator/daemon/Proc.cpp create mode 100644 tools/gator/daemon/Proc.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/Source.cpp create mode 100644 tools/gator/daemon/Source.h create mode 100644 tools/gator/daemon/StreamlineSetup.cpp create mode 100644 tools/gator/daemon/StreamlineSetup.h create mode 100644 tools/gator/daemon/UEvent.cpp create mode 100644 tools/gator/daemon/UEvent.h create mode 100644 tools/gator/daemon/UserSpaceSource.cpp create mode 100644 tools/gator/daemon/UserSpaceSource.h create mode 100644 tools/gator/daemon/c++.cpp create mode 100644 tools/gator/daemon/common.mk create mode 100644 tools/gator/daemon/defaults.xml create mode 100644 tools/gator/daemon/escape.c 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-CCI-400.xml create mode 100644 tools/gator/daemon/events-CCN-504.xml create mode 100644 tools/gator/daemon/events-Cortex-A12.xml create mode 100644 tools/gator/daemon/events-Cortex-A15.xml create mode 100644 tools/gator/daemon/events-Cortex-A5.xml create mode 100644 tools/gator/daemon/events-Cortex-A53.xml create mode 100644 tools/gator/daemon/events-Cortex-A57.xml create mode 100644 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 100644 tools/gator/daemon/events-Filesystem.xml create mode 100644 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-4xx.xml create mode 100644 tools/gator/daemon/events-Mali-T6xx.xml create mode 100644 tools/gator/daemon/events-Mali-T6xx_hw.xml create mode 100644 tools/gator/daemon/events-Mali-V500.xml create mode 100644 tools/gator/daemon/events-Perf-Hardware.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/k/perf_event.3.12.h create mode 120000 tools/gator/daemon/k/perf_event.h create mode 100644 tools/gator/daemon/libsensors/COPYING.LGPL create mode 100644 tools/gator/daemon/libsensors/access.c create mode 100644 tools/gator/daemon/libsensors/access.h create mode 100644 tools/gator/daemon/libsensors/conf-lex.c create mode 100644 tools/gator/daemon/libsensors/conf-lex.l create mode 100644 tools/gator/daemon/libsensors/conf-parse.c create mode 100644 tools/gator/daemon/libsensors/conf-parse.h create mode 100644 tools/gator/daemon/libsensors/conf-parse.y create mode 100644 tools/gator/daemon/libsensors/conf.h create mode 100644 tools/gator/daemon/libsensors/data.c create mode 100644 tools/gator/daemon/libsensors/data.h create mode 100644 tools/gator/daemon/libsensors/error.c create mode 100644 tools/gator/daemon/libsensors/error.h create mode 100644 tools/gator/daemon/libsensors/general.c create mode 100644 tools/gator/daemon/libsensors/general.h create mode 100644 tools/gator/daemon/libsensors/init.c create mode 100644 tools/gator/daemon/libsensors/init.h create mode 100644 tools/gator/daemon/libsensors/scanner.h create mode 100644 tools/gator/daemon/libsensors/sensors.h create mode 100644 tools/gator/daemon/libsensors/sysfs.c create mode 100644 tools/gator/daemon/libsensors/sysfs.h create mode 100644 tools/gator/daemon/libsensors/version.h 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/Kconfig b/drivers/gator/Kconfig new file mode 100644 index 000000000000..e46ccb9b8064 --- /dev/null +++ b/drivers/gator/Kconfig @@ -0,0 +1,39 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m if (ARM || ARM64) + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) + depends on PERF_EVENTS + depends on HW_PERF_EVENTS || !(ARM || ARM64) + select TRACING + help + Gator module for ARM's Streamline Performance Analyzer + +config GATOR_WITH_MALI_SUPPORT + bool + +choice + prompt "Enable Mali GPU support in Gator" + depends on GATOR + optional + help + Enable Mali GPU support in Gator + +config GATOR_MALI_4XXMP + bool "Mali-400MP or Mali-450MP" + 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/LICENSE b/drivers/gator/LICENSE new file mode 100644 index 000000000000..d159169d1050 --- /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 000000000000..2f86823313c6 --- /dev/null +++ b/drivers/gator/Makefile @@ -0,0 +1,81 @@ +ifneq ($(KERNELRELEASE),) + +# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c +# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING + +CONFIG_GATOR ?= m +obj-$(CONFIG_GATOR) := gator.o + +gator-y := gator_main.o \ + gator_events_block.o \ + gator_events_irq.o \ + gator_events_meminfo.o \ + gator_events_mmapped.o \ + gator_events_net.o \ + gator_events_perf_pmu.o \ + gator_events_sched.o \ + gator_events_threads.o \ + +# Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags +ifneq ($(GATOR_WITH_MALI_SUPPORT),) + CONFIG_GATOR_WITH_MALI_SUPPORT := y + ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) + CONFIG_GATOR_MALI_4XXMP := n + CONFIG_GATOR_MALI_T6XX := y + else + CONFIG_GATOR_MALI_4XXMP := y + CONFIG_GATOR_MALI_T6XX := n + endif + EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) + ifneq ($(GATOR_MALI_INTERFACE_STYLE),) + EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE) + endif +endif + +ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) + ifeq ($(CONFIG_GATOR_MALI_T6XX),y) + gator-y += gator_events_mali_t6xx.o \ + gator_events_mali_t6xx_hw.o + include $(src)/mali_t6xx.mk + else + gator-y += gator_events_mali_4xx.o + endif + gator-y += gator_events_mali_common.o + + ifneq ($(CONFIG_GATOR_MALI_PATH),) + ccflags-y += -I$(CONFIG_GATOR_MALI_PATH) + endif + ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -DMALI_SUPPORT=MALI_4xx + ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx +endif + +# GATOR_TEST controls whether to include (=1) or exclude (=0) test code. +GATOR_TEST ?= 0 +EXTRA_CFLAGS += -DGATOR_TEST=$(GATOR_TEST) + +# Should the original or new block_rq_complete API be used? +OLD_BLOCK_RQ_COMPLETE := $(shell grep -A3 block_rq_complete include/trace/events/block.h | grep nr_bytes > /dev/null; echo $$?) +EXTRA_CFLAGS += -DOLD_BLOCK_RQ_COMPLETE=$(OLD_BLOCK_RQ_COMPLETE) + +gator-$(CONFIG_ARM) += gator_events_armv6.o \ + gator_events_armv7.o \ + gator_events_ccn-504.o \ + gator_events_l2c-310.o \ + gator_events_scorpion.o + +gator-$(CONFIG_ARM64) += gator_events_ccn-504.o + +else + +all: + @echo + @echo "usage:" + @echo " make -C M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" + @echo + $(error) + +clean: + rm -f *.o .*.cmd modules.order Module.symvers gator.ko gator.mod.c + rm -rf .tmp_versions + +endif diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h new file mode 100644 index 000000000000..5ad0254d86a9 --- /dev/null +++ b/drivers/gator/gator.h @@ -0,0 +1,149 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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(__arm__) || defined(__aarch64__)) || 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) +#define GATOR_IKS_SUPPORT defined(CONFIG_BL_SWITCHER) + +// 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_A12 0xc0d +#define CORTEX_A15 0xc0f +#define CORTEX_A17 0xc0e +#define SCORPION 0x00f +#define SCORPIONMP 0x02d +#define KRAITSIM 0x049 +#define KRAIT 0x04d +#define KRAIT_S4_PRO 0x06f +#define CORTEX_A53 0xd03 +#define CORTEX_A57 0xd07 +#define AARCH64 0xd0f +#define OTHER 0xfff + +// gpu enums +#define MALI_4xx 1 +#define MALI_T6xx 2 + +#define MAXSIZE_CORE_NAME 32 + +struct gator_cpu { + const int cpuid; + // Human readable name + const char core_name[MAXSIZE_CORE_NAME]; + // gatorfs event and Perf PMU name + const char * const pmnc_name; + // compatible from Documentation/devicetree/bindings/arm/cpus.txt + const char * const dt_name; + const int pmnc_counters; +}; + +const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid); +const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name); + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +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); + +/****************************************************************************** + * 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) +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) +# 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) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + extern struct tracepoint *gator_tracepoint_##probe_name; \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + tracepoint_probe_register(gator_tracepoint_##probe_name, probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + tracepoint_probe_unregister(gator_tracepoint_##probe_name, probe_##probe_name, NULL) +#endif + +/****************************************************************************** + * Events + ******************************************************************************/ +struct gator_interface { + void (*shutdown)(void); // Complementary function to init + int (*create_files)(struct super_block *sb, struct dentry *root); + int (*start)(void); + void (*stop)(void); // Complementary function to start + int (*online)(int **buffer, bool migrate); + int (*offline)(int **buffer, bool migrate); + void (*online_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu' + void (*offline_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu' + int (*read)(int **buffer); + int (*read64)(long long **buffer); + int (*read_proc)(long long **buffer, struct task_struct *); + struct list_head list; +}; + +int gator_events_install(struct gator_interface *interface); +int gator_events_get_key(void); +u32 gator_cpuid(void); + +void gator_backtrace_handler(struct pt_regs *const regs); + +void gator_marshal_activity_switch(int core, int key, int activity, int pid); + +#if !GATOR_IKS_SUPPORT + +#define get_physical_cpu() smp_processor_id() +#define lcpu_to_pcpu(lcpu) lcpu +#define pcpu_to_lcpu(pcpu) pcpu + +#else + +#define get_physical_cpu() lcpu_to_pcpu(get_logical_cpu()) +int lcpu_to_pcpu(const int lcpu); +int pcpu_to_lcpu(const int pcpu); + +#endif + +#define get_logical_cpu() smp_processor_id() +#define on_primary_core() (get_logical_cpu() == 0) + +#endif // GATOR_H_ diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c new file mode 100644 index 000000000000..7e2c6e5d8715 --- /dev/null +++ b/drivers/gator/gator_annotate.c @@ -0,0 +1,186 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 pid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff; + bool interrupt_context; + + if (*offset) { + return -EINVAL; + } + + interrupt_context = in_interrupt(); + // Annotations are not supported in interrupt context, but may work if you comment out the the next four lines of code. + // By doing so, annotations in interrupt context can result in deadlocks and lost data. + if (interrupt_context) { + printk(KERN_WARNING "gator: Annotations are not supported in interrupt context. Edit gator_annotate.c in the gator driver to enable annotations in interrupt context.\n"); + return -EINVAL; + } + + retry: + // synchronize between cores and with collect_annotations + spin_lock(&annotate_lock); + + if (!collect_annotations) { + // Not collecting annotations, tell the caller everything was written + size = count_orig; + goto annotate_write_out; + } + + // Annotation only uses a single per-cpu buffer as the data must be in order to the engine + cpu = 0; + + if (current == NULL) { + pid = 0; + } else { + pid = current->pid; + } + + // 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) { + // Buffer is full, wait until space is available + spin_unlock(&annotate_lock); + + // Drop the annotation as blocking is not allowed in interrupt context + if (interrupt_context) { + return -EINVAL; + } + + wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations); + + // Check to see if a signal is pending + if (signal_pending(current)) { + return -EINTR; + } + + goto retry; + } + + // synchronize shared variables annotateBuf and annotatePos + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { + u64 time = gator_get_time(); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 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, time); + } + +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 + 3 * MAXSIZE_PACK32)) { + uint32_t pid = current->pid; + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, get_physical_cpu()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, pid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ANNOTATE_BUF, gator_get_time()); + + 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) +{ + // the spinlock here will ensure that when this function exits, we are not in the middle of an annotation + spin_lock(&annotate_lock); + collect_annotations = false; + wake_up(&gator_annotate_wait); + spin_unlock(&annotate_lock); +} diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c new file mode 100644 index 000000000000..010806825529 --- /dev/null +++ b/drivers/gator/gator_annotate_kernel.c @@ -0,0 +1,202 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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 ESCAPE_CODE 0x1c +#define STRING_ANNOTATION 0x06 +#define NAME_CHANNEL_ANNOTATION 0x07 +#define NAME_GROUP_ANNOTATION 0x08 +#define VISUAL_ANNOTATION 0x04 +#define MARKER_ANNOTATION 0x05 + +static void kannotate_write(const 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; + } +} + +static void marshal_u16(char *buf, u16 val) +{ + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; +} + +static void marshal_u32(char *buf, u32 val) +{ + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; +} + +void gator_annotate_channel(int channel, const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_channel); + +void gator_annotate(const char *str) +{ + gator_annotate_channel(0, str); +} + +EXPORT_SYMBOL(gator_annotate); + +void gator_annotate_channel_color(int channel, int color, const char *str) +{ + const u16 str_size = (strlen(str) + 4) & 0xffff; + char header[12]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + marshal_u32(header + 8, color); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size - 4); +} + +EXPORT_SYMBOL(gator_annotate_channel_color); + +void gator_annotate_color(int color, const char *str) +{ + gator_annotate_channel_color(0, color, str); +} + +EXPORT_SYMBOL(gator_annotate_color); + +void gator_annotate_channel_end(int channel) +{ + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, 0); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_channel_end); + +void gator_annotate_end(void) +{ + gator_annotate_channel_end(0); +} + +EXPORT_SYMBOL(gator_annotate_end); + +void gator_annotate_name_channel(int channel, int group, const char* str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[12]; + header[0] = ESCAPE_CODE; + header[1] = NAME_CHANNEL_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u32(header + 6, group); + marshal_u16(header + 10, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_name_channel); + +void gator_annotate_name_group(int group, const char* str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = NAME_GROUP_ANNOTATION; + marshal_u32(header + 2, group); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_name_group); + +void gator_annotate_visual(const char *data, unsigned int length, const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + char header_length[4]; + header[0] = ESCAPE_CODE; + header[1] = VISUAL_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header_length, length); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); + kannotate_write(header_length, sizeof(header_length)); + kannotate_write(data, length); +} + +EXPORT_SYMBOL(gator_annotate_visual); + +void gator_annotate_marker(void) +{ + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 0); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_marker); + +void gator_annotate_marker_str(const char *str) +{ + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size); +} + +EXPORT_SYMBOL(gator_annotate_marker_str); + +void gator_annotate_marker_color(int color) +{ + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 4); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); +} + +EXPORT_SYMBOL(gator_annotate_marker_color); + +void gator_annotate_marker_color_str(int color, const char *str) +{ + const u16 str_size = (strlen(str) + 4) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); + kannotate_write(str, str_size - 4); +} + +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 000000000000..e03c1653c5b5 --- /dev/null +++ b/drivers/gator/gator_backtrace.c @@ -0,0 +1,208 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 stack_frame_eabi { + union { + struct { + unsigned long fp; + // May be the fp in the case of a leaf function or clang + unsigned long lr; + // If lr is really the fp, lr2 is the corresponding lr + unsigned long lr2; + }; + // Used to read 32 bit fp/lr from a 64 bit kernel + struct { + u32 fp_32; + // same as lr above + u32 lr_32; + // same as lr2 above + u32 lr2_32; + }; + }; +}; + +static void gator_add_trace(int cpu, unsigned long address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) { + offset = address; + } + + marshal_backtrace(offset & ~1, cookie, 0); +} + +static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) +{ +#if defined(__arm__) || defined(__aarch64__) + struct stack_frame_eabi *curr; + struct stack_frame_eabi bufcurr; +#if defined(__arm__) + const bool is_compat = false; + unsigned long fp = regs->ARM_fp; + unsigned long sp = regs->ARM_sp; + unsigned long lr = regs->ARM_lr; + const int gcc_frame_offset = sizeof(unsigned long); +#else + // Is userspace aarch32 (32 bit) + const bool is_compat = compat_user_mode(regs); + unsigned long fp = (is_compat ? regs->regs[11] : regs->regs[29]); + unsigned long sp = (is_compat ? regs->compat_sp : regs->sp); + unsigned long lr = (is_compat ? regs->compat_lr : regs->regs[30]); + const int gcc_frame_offset = (is_compat ? sizeof(u32) : 0); +#endif + // clang frame offset is always zero + int is_user_mode = user_mode(regs); + + // pc (current function) has already been added + + if (!is_user_mode) { + return; + } + + // Add the lr (parent function) + // entry preamble may not have executed + gator_add_trace(cpu, lr); + + // check fp is valid + if (fp == 0 || fp < sp) { + return; + } + + // Get the current stack frame + curr = (struct stack_frame_eabi *)(fp - gcc_frame_offset); + if ((unsigned long)curr & 3) { + return; + } + + while (depth-- && curr) { + if (!access_ok(VERIFY_READ, curr, sizeof(struct stack_frame_eabi)) || + __copy_from_user_inatomic(&bufcurr, curr, sizeof(struct stack_frame_eabi))) { + return; + } + + fp = (is_compat ? bufcurr.fp_32 : bufcurr.fp); + lr = (is_compat ? bufcurr.lr_32 : bufcurr.lr); + +#define calc_next(reg) ((reg) - gcc_frame_offset) + // Returns true if reg is a valid fp +#define validate_next(reg, curr) \ + ((reg) != 0 && (calc_next(reg) & 3) == 0 && (unsigned long)(curr) < calc_next(reg)) + + // Try lr from the stack as the fp because gcc leaf functions do not push lr + // If gcc_frame_offset is non-zero, the lr will also be the clang fp + // This assumes code is at a lower address than the stack + if (validate_next(lr, curr)) { + fp = lr; + lr = (is_compat ? bufcurr.lr2_32 : bufcurr.lr2); + } + + gator_add_trace(cpu, lr); + + if (!validate_next(fp, curr)) { + return; + } + + // Move to the next stack frame + curr = (struct stack_frame_eabi *)calc_next(fp); + } +#endif +} + +#if defined(__arm__) || defined(__aarch64__) +static int report_trace(struct stackframe *frame, void *d) +{ + unsigned int *depth = d, cookie = NO_COOKIE; + unsigned long addr = frame->pc; + + if (*depth) { +#if defined(MODULE) + unsigned int cpu = get_physical_cpu(); + struct module *mod = __module_address(addr); + if (mod) { + cookie = get_cookie(cpu, current, mod->name, false); + addr = addr - (unsigned long)mod->module_core; + } +#endif + marshal_backtrace(addr & ~1, cookie, 1); + (*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 + +#if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING) +// Disabled by default +MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding."); +static bool kernel_stack_unwinding = 0; +module_param(kernel_stack_unwinding, bool, 0644); +#endif + +static void kernel_backtrace(int cpu, struct pt_regs *const regs) +{ +#if defined(__arm__) || defined(__aarch64__) +#ifdef GATOR_KERNEL_STACK_UNWINDING + int depth = gator_backtrace_depth; +#else + int depth = (kernel_stack_unwinding ? gator_backtrace_depth : 1); +#endif + struct stackframe frame; + if (depth == 0) + depth = 1; +#if defined(__arm__) + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; +#else + frame.fp = regs->regs[29]; + frame.sp = regs->sp; + frame.pc = regs->pc; +#endif + walk_stackframe(&frame, report_trace, &depth); +#else + marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1); +#endif +} + +static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time) +{ + bool in_kernel; + unsigned long exec_cookie; + + if (!regs) + return; + + in_kernel = !user_mode(regs); + exec_cookie = get_exec_cookie(cpu, current); + + if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, time)) + return; + + if (in_kernel) { + kernel_backtrace(cpu, regs); + } else { + // Cookie+PC + gator_add_trace(cpu, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, regs, gator_backtrace_depth); + } + + marshal_backtrace_footer(time); +} diff --git a/drivers/gator/gator_buffer.c b/drivers/gator/gator_buffer.c new file mode 100644 index 000000000000..dfbc97d80221 --- /dev/null +++ b/drivers/gator/gator_buffer.c @@ -0,0 +1,168 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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_frame(int cpu, int buftype) +{ + int frame; + + if (!per_cpu(gator_buffer, cpu)[buftype]) { + return; + } + + switch (buftype) { + case SUMMARY_BUF: + frame = FRAME_SUMMARY; + break; + case BACKTRACE_BUF: + frame = FRAME_BACKTRACE; + break; + case NAME_BUF: + frame = FRAME_NAME; + break; + case COUNTER_BUF: + frame = FRAME_COUNTER; + break; + case BLOCK_COUNTER_BUF: + frame = FRAME_BLOCK_COUNTER; + break; + case ANNOTATE_BUF: + frame = FRAME_ANNOTATE; + break; + case SCHED_TRACE_BUF: + frame = FRAME_SCHED_TRACE; + break; + case IDLE_BUF: + frame = FRAME_IDLE; + break; + case ACTIVITY_BUF: + frame = FRAME_ACTIVITY; + break; + default: + frame = -1; + break; + } + + // 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] + sizeof(s32)) & 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); +} + +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 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 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 void gator_commit_buffer(int cpu, int buftype, u64 time) +{ + int type_length, commit, length, byte; + unsigned long flags; + + if (!per_cpu(gator_buffer, cpu)[buftype]) + return; + + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + local_irq_save(flags); + type_length = gator_response_type ? 1 : 0; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + length = per_cpu(gator_buffer_write, cpu)[buftype] - commit; + if (length < 0) { + length += gator_buffer_size[buftype]; + } + length = length - type_length - sizeof(s32); + + if (length <= FRAME_HEADER_SIZE) { + // Nothing to write, only the frame header is present + local_irq_restore(flags); + return; + } + + for (byte = 0; byte < sizeof(s32); byte++) { + per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; + } + + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; + + if (gator_live_rate > 0) { + while (time > per_cpu(gator_buffer_commit_time, cpu)) { + per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate; + } + } + + marshal_frame(cpu, buftype); + local_irq_restore(flags); + + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + if (per_cpu(in_scheduler_context, cpu)) { +#ifndef CONFIG_PREEMPT_RT_FULL + // mod_timer can not be used in interrupt context in RT-Preempt full + mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); +#endif + } else { + up(&gator_buffer_wake_sem); + } +} + +static void buffer_check(int cpu, int buftype, u64 time) +{ + 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, time); + } +} diff --git a/drivers/gator/gator_buffer_write.c b/drivers/gator/gator_buffer_write.c new file mode 100644 index 000000000000..b621ba93ee5e --- /dev/null +++ b/drivers/gator/gator_buffer_write.c @@ -0,0 +1,80 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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, 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 packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} + +static void gator_buffer_write_packed_int64(int cpu, int buftype, 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 packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} + +static void gator_buffer_write_bytes(int cpu, int buftype, const 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, const char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, buftype, len); + gator_buffer_write_bytes(cpu, buftype, x, len); +} diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c new file mode 100644 index 000000000000..5c7d842070e0 --- /dev/null +++ b/drivers/gator/gator_cookies.c @@ -0,0 +1,437 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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_BUFFER_SIZE 512 // must be a power of 2 - 512/4 = 128 entries +#define TRANSLATE_TEXT_SIZE 256 +#define MAX_COLLISIONS 2 + +static uint32_t *gator_crc32_table; +static unsigned int translate_buffer_mask; + +struct cookie_args { + struct task_struct *task; + const char *text; +}; + +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(struct cookie_args *, translate_buffer); + +static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); +static void wq_cookie_handler(struct work_struct *unused); +static DECLARE_WORK(cookie_work, wq_cookie_handler); +static struct timer_list app_process_wake_up_timer; +static void app_process_wake_up_handler(unsigned long unused_data); + +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(const char *data) +{ + register unsigned long crc; + const 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 = get_physical_cpu(); + 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 = get_physical_cpu(); + 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; +} + +#ifndef CONFIG_PREEMPT_RT_FULL +static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text) +{ + unsigned long flags; + int write; + int next_write; + struct cookie_args *args; + + local_irq_save(flags); + + write = per_cpu(translate_buffer_write, cpu); + next_write = (write + 1) & translate_buffer_mask; + + // At least one entry must always remain available as when read == write, the queue is empty not full + if (next_write != per_cpu(translate_buffer_read, cpu)) { + args = &per_cpu(translate_buffer, cpu)[write]; + args->task = task; + args->text = text; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + get_task_struct(task); +#endif + per_cpu(translate_buffer_write, cpu) = next_write; + } + + local_irq_restore(flags); +} +#endif + +static void translate_buffer_read_args(int cpu, struct cookie_args *args) +{ + unsigned long flags; + int read; + + local_irq_save(flags); + + read = per_cpu(translate_buffer_read, cpu); + *args = per_cpu(translate_buffer, cpu)[read]; + per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask; + + local_irq_restore(flags); +} + +static void wq_cookie_handler(struct work_struct *unused) +{ + struct cookie_args args; + int cpu = get_physical_cpu(), cookie; + + mutex_lock(&start_mutex); + + if (gator_started != 0) { + while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) { + translate_buffer_read_args(cpu, &args); + cookie = get_cookie(cpu, args.task, args.text, true); + marshal_link(cookie, args.task->tgid, args.task->pid); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + put_task_struct(args.task); +#endif + } + } + + mutex_unlock(&start_mutex); +} + +static void app_process_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&cookie_work); +} + +// Retrieve full name from proc/pid/cmdline for java processes on Android +static int translate_app_process(const char **text, int cpu, struct task_struct *task, bool from_wq) +{ + 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; + char *buf = per_cpu(translate_text, cpu); + +#ifndef CONFIG_PREEMPT_RT_FULL + // 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 (!from_wq) { + // Check if already in buffer + int pos = per_cpu(translate_buffer_read, cpu); + while (pos != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[pos].task == task) + goto out; + pos = (pos + 1) & translate_buffer_mask; + } + + translate_buffer_write_args(cpu, task, *text); + + // Not safe to call in RT-Preempt full in schedule switch context + mod_timer(&app_process_wake_up_timer, jiffies + 1); + goto out; + } +#endif + + 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_TEXT_SIZE) + len = TRANSLATE_TEXT_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 uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq) +{ + unsigned long flags, cookie; + uint64_t key; + + 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) { + if (!translate_app_process(&text, cpu, task, from_wq)) + return UNRESOLVED_COOKIE; + } + + // Can be called from interrupt handler or from work queue or from scheduler trace + local_irq_save(flags); + + cookie = UNRESOLVED_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, struct task_struct *task) +{ + struct mm_struct *mm = task->mm; + const char *text; + + // kernel threads have no address space + if (!mm) + return NO_COOKIE; + + if (task && task->mm && task->mm->exe_file) { + text = task->mm->exe_file->f_path.dentry->d_name.name; + return get_cookie(cpu, task, text, false); + } + + return UNRESOLVED_COOKIE; +} + +static unsigned long get_address_cookie(int cpu, 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; + const char *text; + + 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) { + text = vma->vm_file->f_path.dentry->d_name.name; + cookie = get_cookie(cpu, task, text, false); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + + break; + } + + if (!vma) + cookie = UNRESOLVED_COOKIE; + + return cookie; +} + +static int cookies_initialize(void) +{ + uint32_t crc, poly; + int i, j, cpu, size, err = 0; + + 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) = (struct cookie_args *)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_TEXT_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); + if (!gator_crc32_table) { + err = -ENOMEM; + goto cookie_setup_error; + } + 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; + } + + setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0); + +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; + } + + del_timer_sync(&app_process_wake_up_timer); + kfree(gator_crc32_table); + gator_crc32_table = NULL; +} diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c new file mode 100644 index 000000000000..353645622306 --- /dev/null +++ b/drivers/gator/gator_events_armv6.c @@ -0,0 +1,237 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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, bool migrate) +{ + 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, bool migrate) +{ + 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); +} + +#endif diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c new file mode 100644 index 000000000000..bd8a9ba24e99 --- /dev/null +++ b/drivers/gator/gator_events_armv7.c @@ -0,0 +1,314 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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, "%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_armv7_online(int **buffer, bool migrate) +{ + 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, bool migrate) +{ + // disable 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 = "ARMv7_Cortex_A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "ARMv7_Cortex_A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "ARMv7_Cortex_A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "ARMv7_Cortex_A9"; + pmnc_counters = 6; + break; + // ARM Cortex A12 is not supported by version of Linux before 3.0 + case CORTEX_A15: + pmnc_name = "ARMv7_Cortex_A15"; + pmnc_counters = 6; + break; + // ARM Cortex A17 is not supported by version of Linux before 3.0 + 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); +} + +#endif diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c new file mode 100644 index 000000000000..03eed4fb9ebb --- /dev/null +++ b/drivers/gator/gator_events_block.c @@ -0,0 +1,163 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 atomic_t blockCnt[BLOCK_TOTAL]; +static int blockGet[BLOCK_TOTAL * 4]; + +// Tracepoint changed in 3.15 backported to older kernels. The Makefile tries to autodetect the correct value, but if it fails change the #if below +#if OLD_BLOCK_RQ_COMPLETE +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +#else +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq, unsigned int nr_bytes)) +#endif +{ + int write; + unsigned int size; + + if (!rq) + return; + + write = rq->cmd_flags & EVENTWRITE; +#if OLD_BLOCK_RQ_COMPLETE + size = rq->resid_len; +#else + size = nr_bytes; +#endif + + if (!size) + return; + + if (write) { + if (block_rq_wr_enabled) { + atomic_add(size, &blockCnt[BLOCK_RQ_WR]); + } + } else { + if (block_rq_rd_enabled) { + atomic_add(size, &blockCnt[BLOCK_RQ_RD]); + } + } +} + +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) +{ + // 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) +{ + int len, value, data = 0; + + if (!on_primary_core()) { + return 0; + } + + len = 0; + if (block_rq_wr_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_WR])) > 0) { + atomic_sub(value, &blockCnt[BLOCK_RQ_WR]); + blockGet[len++] = block_rq_wr_key; + blockGet[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message + blockGet[len++] = block_rq_wr_key; + blockGet[len++] = value; + data += value; + } + if (block_rq_rd_enabled && (value = atomic_read(&blockCnt[BLOCK_RQ_RD])) > 0) { + atomic_sub(value, &blockCnt[BLOCK_RQ_RD]); + blockGet[len++] = block_rq_rd_key; + blockGet[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message + blockGet[len++] = block_rq_rd_key; + blockGet[len++] = value; + data += value; + } + + if (buffer) + *buffer = blockGet; + + 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); +} diff --git a/drivers/gator/gator_events_ccn-504.c b/drivers/gator/gator_events_ccn-504.c new file mode 100644 index 000000000000..024ffc2856aa --- /dev/null +++ b/drivers/gator/gator_events_ccn-504.c @@ -0,0 +1,346 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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" + +#define NUM_REGIONS 256 +#define REGION_SIZE (64*1024) +#define REGION_DEBUG 1 +#define REGION_XP 64 +#define NUM_XPS 11 + +// DT (Debug) region +#define PMEVCNTSR0 0x0150 +#define PMCCNTRSR 0x0190 +#define PMCR 0x01A8 +#define PMSR 0x01B0 +#define PMSR_REQ 0x01B8 +#define PMSR_CLR 0x01C0 + +// XP region +#define DT_CONFIG 0x0300 +#define DT_CONTROL 0x0370 + +// Multiple +#define PMU_EVENT_SEL 0x0600 +#define OLY_ID 0xFF00 + +#define CCNT 4 +#define CNTMAX (CCNT + 1) + +#define get_pmu_event_id(event) (((event) >> 0) & 0xFF) +#define get_node_type(event) (((event) >> 8) & 0xFF) +#define get_region(event) (((event) >> 16) & 0xFF) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + if (!val) return -EINVAL; \ + ret = strtolfn(val, 0, &l); \ + if (ret == -EINVAL || ((type)l != l)) \ + return -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, struct kernel_param *kp) \ + { \ + return sprintf(buffer, format, *((type *)kp->arg)); \ + } + +#else + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, const struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + ret = strtolfn(val, 0, &l); \ + if (ret < 0 || ((type)l != l)) \ + return ret < 0 ? ret : -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, const struct kernel_param *kp) \ + { \ + return scnprintf(buffer, PAGE_SIZE, format, \ + *((type *)kp->arg)); \ + } \ + struct kernel_param_ops param_ops_##name = { \ + .set = param_set_##name, \ + .get = param_get_##name, \ + }; \ + EXPORT_SYMBOL(param_set_##name); \ + EXPORT_SYMBOL(param_get_##name); \ + EXPORT_SYMBOL(param_ops_##name) + +#endif + +STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull); + +// From include/linux/moduleparam.h +#define param_check_u64(name, p) __param_check(name, p, u64) + +MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address"); +static u64 ccn504_addr = 0; +module_param(ccn504_addr, u64, 0444); + +static void __iomem *gator_events_ccn504_base; +static bool gator_events_ccn504_global_enabled; +static unsigned long gator_events_ccn504_enabled[CNTMAX]; +static unsigned long gator_events_ccn504_event[CNTMAX]; +static unsigned long gator_events_ccn504_key[CNTMAX]; +static int gator_events_ccn504_buffer[2*CNTMAX]; +static int gator_events_ccn504_prev[CNTMAX]; + +static void gator_events_ccn504_create_shutdown(void) +{ + if (gator_events_ccn504_base != NULL) { + iounmap(gator_events_ccn504_base); + } +} + +static int gator_events_ccn504_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + char buf[32]; + + for (i = 0; i < CNTMAX; ++i) { + if (i == CCNT) { + snprintf(buf, sizeof(buf), "CCN-504_ccnt"); + } else { + snprintf(buf, sizeof(buf), "CCN-504_cnt%i", i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &gator_events_ccn504_enabled[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &gator_events_ccn504_event[i]); + } + gatorfs_create_ro_ulong(sb, dir, "key", &gator_events_ccn504_key[i]); + } + + return 0; +} + +static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int value) +{ + u32 dt_config; + + dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); + dt_config |= (value + event_num) << (4*event_num); + writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); +} + +static int gator_events_ccn504_start(void) +{ + int i; + + gator_events_ccn504_global_enabled = 0; + for (i = 0; i < CNTMAX; ++i) { + if (gator_events_ccn504_enabled[i]) { + gator_events_ccn504_global_enabled = 1; + break; + } + } + + if (!gator_events_ccn504_global_enabled) { + return 0; + } + + memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev)); + + // Disable INTREQ on overflow + // [6] ovfl_intr_en = 0 + // perhaps set to 1? + // [5] cntr_rst = 0 + // No register paring + // [4:1] cntcfg = 0 + // Enable PMU features + // [0] pmu_en = 1 + writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR); + + // Configure the XPs + for (i = 0; i < NUM_XPS; ++i) { + int dt_control; + + // Pass on all events + writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + + // Enable PMU capability + // [0] dt_enable = 1 + dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + dt_control |= 0x1; + writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + } + + // Assume no other pmu_event_sel registers are set + + // cycle counter does not need to be enabled + for (i = 0; i < CCNT; ++i) { + int pmu_event_id; + int node_type; + int region; + u32 pmu_event_sel; + u32 oly_id_whole; + u32 oly_id; + u32 node_id; + + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]); + node_type = get_node_type(gator_events_ccn504_event[i]); + region = get_region(gator_events_ccn504_event[i]); + + // Verify the node_type + oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID); + oly_id = oly_id_whole & 0x1F; + node_id = (oly_id_whole >> 8) & 0x7F; + if ((oly_id != node_type) || + ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) { + printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type); + return -1; + } + + // Set the control register + pmu_event_sel = readl(gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + switch (node_type) { + case 0x08: // XP + pmu_event_sel |= pmu_event_id << (7*i); + gator_events_ccn504_set_dt_config(node_id, i, 0x4); + break; + case 0x04: // HN-F + case 0x16: // RN-I + case 0x10: // SBAS + pmu_event_sel |= pmu_event_id << (4*i); + gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC); + break; + } + writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + } + + return 0; +} + +static void gator_events_ccn504_stop(void) +{ + int i; + + if (!gator_events_ccn504_global_enabled) { + return; + } + + // cycle counter does not need to be disabled + for (i = 0; i < CCNT; ++i) { + int region; + + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + region = get_region(gator_events_ccn504_event[i]); + + writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); + } + + // Clear dt_config + for (i = 0; i < NUM_XPS; ++i) { + writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + } +} + +static int gator_events_ccn504_read(int **buffer) +{ + int i; + int len = 0; + int value; + + if (!on_primary_core() || !gator_events_ccn504_global_enabled) { + return 0; + } + + // Verify the pmsr register is zero + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0); + + // Request a PMU snapshot + writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ); + + // Wait for the snapshot + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0); + + // Read the shadow registers + for (i = 0; i < CNTMAX; ++i) { + if (!gator_events_ccn504_enabled[i]) { + continue; + } + + value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i)); + if (gator_events_ccn504_prev[i] != 0x80808080) { + gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i]; + gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i]; + } + gator_events_ccn504_prev[i] = value; + + // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does? + } + + // Clear the PMU snapshot status + writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR); + + if (buffer) + *buffer = gator_events_ccn504_buffer; + + return len; +} + +static struct gator_interface gator_events_ccn504_interface = { + .shutdown = gator_events_ccn504_create_shutdown, + .create_files = gator_events_ccn504_create_files, + .start = gator_events_ccn504_start, + .stop = gator_events_ccn504_stop, + .read = gator_events_ccn504_read, +}; + +int gator_events_ccn504_init(void) +{ + int i; + + if (ccn504_addr == 0) { + return -1; + } + + gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE); + if (gator_events_ccn504_base == NULL) { + printk(KERN_ERR "gator: ioremap returned NULL\n"); + return -1; + } + + for (i = 0; i < CNTMAX; ++i) { + gator_events_ccn504_enabled[i] = 0; + gator_events_ccn504_event[i] = 0; + gator_events_ccn504_key[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_ccn504_interface); +} diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c new file mode 100644 index 000000000000..facbdd62325e --- /dev/null +++ b/drivers/gator/gator_events_irq.c @@ -0,0 +1,165 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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(atomic_t[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)) +{ + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]); +} + +#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 +{ + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]); +} + +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, bool migrate) +{ + int len = 0, cpu = get_physical_cpu(); + + // synchronization with the irq_exit functions is not necessary as the values are being reset + if (hardirq_enabled) { + atomic_set(&per_cpu(irqCnt, cpu)[HARDIRQ], 0); + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (softirq_enabled) { + atomic_set(&per_cpu(irqCnt, cpu)[SOFTIRQ], 0); + 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) +{ + int len, value; + int cpu = get_physical_cpu(); + + len = 0; + if (hardirq_enabled) { + value = atomic_read(&per_cpu(irqCnt, cpu)[HARDIRQ]); + atomic_sub(value, &per_cpu(irqCnt, cpu)[HARDIRQ]); + + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + + if (softirq_enabled) { + value = atomic_read(&per_cpu(irqCnt, cpu)[SOFTIRQ]); + atomic_sub(value, &per_cpu(irqCnt, cpu)[SOFTIRQ]); + + 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); +} diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c new file mode 100644 index 000000000000..553f9707bdbf --- /dev/null +++ b/drivers/gator/gator_events_l2c-310.c @@ -0,0 +1,208 @@ +/** + * l2c310 (L2 Cache Controller) event counters for gator + * + * Copyright (C) ARM Limited 2010-2014. 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 +#if defined(CONFIG_OF) +#include +#include +#endif +#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 (!on_primary_core()) + 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, +}; + +#define L2C310_ADDR_PROBE (~0) + +MODULE_PARM_DESC(l2c310_addr, "L2C310 physical base address (0 to disable)"); +static unsigned long l2c310_addr = L2C310_ADDR_PROBE; +module_param(l2c310_addr, ulong, 0444); + +static void __iomem *gator_events_l2c310_probe(void) +{ + phys_addr_t variants[] = { +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310) + 0x10502000, +#endif +#if defined(CONFIG_ARCH_OMAP4) + 0x48242000, +#endif +#if defined(CONFIG_ARCH_TEGRA) + 0x50043000, +#endif +#if defined(CONFIG_ARCH_U8500) + 0xa0412000, +#endif +#if defined(CONFIG_ARCH_VEXPRESS) + 0x1e00a000, // A9x4 core tile (HBI-0191) + 0x2c0f0000, // New memory map tiles +#endif + }; + int i; + void __iomem *base; +#if defined(CONFIG_OF) + struct device_node *node = of_find_all_nodes(NULL); + + if (node) { + of_node_put(node); + + node = of_find_compatible_node(NULL, NULL, "arm,pl310-cache"); + base = of_iomap(node, 0); + of_node_put(node); + + return base; + } +#endif + + for (i = 0; i < ARRAY_SIZE(variants); i++) { + base = ioremap(variants[i], SZ_4K); + if (base) { + u32 cache_id = readl(base + L2X0_CACHE_ID); + + if ((cache_id & 0xff0003c0) == 0x410000c0) + return base; + + iounmap(base); + } + } + + return NULL; +} + +int gator_events_l2c310_init(void) +{ + int i; + + if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9) + return -1; + + if (l2c310_addr == L2C310_ADDR_PROBE) + l2c310_base = gator_events_l2c310_probe(); + else if (l2c310_addr) + l2c310_base = ioremap(l2c310_addr, SZ_4K); + + 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); +} diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c new file mode 100644 index 000000000000..9e1c7064bd73 --- /dev/null +++ b/drivers/gator/gator_events_mali_4xx.c @@ -0,0 +1,669 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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" + +#include "gator_events_mali_common.h" +#include "gator_events_mali_4xx.h" + +/* +* There have been four different variants of the comms between gator and Mali depending on driver version: +* # | DDK vsn range | Support | Notes +* +* 1 | (obsolete) | No software counter support | Obsolete patches +* 2 | (obsolete) | Tracepoint called for each separate s/w counter value as it appears | Obsolete patches +* 3 | r3p0-04rel0 - r3p2-01rel2 | Single tracepoint for all s/w counters in a bundle. | +* 4 | r3p2-01rel3 - date | As above but with extensions for MP devices (Mali-450) | At least r4p0-00rel1 +*/ + +#if !defined(GATOR_MALI_INTERFACE_STYLE) +#define GATOR_MALI_INTERFACE_STYLE (4) +#endif + +#if GATOR_MALI_INTERFACE_STYLE == 1 +#error GATOR_MALI_INTERFACE_STYLE 1 is obsolete +#elif GATOR_MALI_INTERFACE_STYLE == 2 +#error GATOR_MALI_INTERFACE_STYLE 2 is obsolete +#elif GATOR_MALI_INTERFACE_STYLE >= 3 +// Valid GATOR_MALI_INTERFACE_STYLE +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + +#if GATOR_MALI_INTERFACE_STYLE < 4 +#include "mali/mali_mjollnir_profiling_gator_api.h" +#else +#include "mali/mali_utgard_profiling_gator_api.h" +#endif + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if (MALI_SUPPORT != MALI_4xx) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_4xx +#endif + +static const char mali_name[] = "Mali-4xx"; + +/* 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]; +static bool prev_set[NUMBER_OF_EVENTS]; + +/* Note whether tracepoints have been registered */ +static int trace_registered; + +/* + * These numbers define the actual numbers of each block type that exist in the system. Initially + * these are set to the maxima defined above; if the driver is capable of being queried (newer + * drivers only) then the values may be revised. + */ +static unsigned int n_vp_cores = MAX_NUM_VP_CORES; +static unsigned int n_l2_cores = MAX_NUM_L2_CACHE_CORES; +static unsigned int n_fp_cores = MAX_NUM_FP_CORES; + +extern mali_counter mali_activity[2]; +static const char* const mali_activity_names[] = { + "fragment", + "vertex", +}; + +/** + * 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); +} + +/* + * These are provided for utgard compatibility. + */ +typedef void _mali_profiling_get_mali_version_type(struct _mali_profiling_mali_version *values); +typedef u32 _mali_profiling_get_l2_counters_type(_mali_profiling_l2_counter_values *values); + +/* 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; + } +} + +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]); + } + } +} + +/** + * Create a single filesystem entry for a specified event. + * @param sb the superblock + * @param root Filesystem root + * @param name The name of the entry to create + * @param event The ID of the event + * @param create_event_item boolean indicating whether to create an 'event' filesystem entry. True to create. + * + * @return 0 if ok, non-zero if the create failed. + */ +static int create_fs_entry(struct super_block *sb, struct dentry *root, const char *name, int event, int create_event_item) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, name); + + if (!dir) { + return -1; + } + + if (create_event_item) { + 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; +} + +#if GATOR_MALI_INTERFACE_STYLE > 3 +/* + * Read the version info structure if available + */ +static void initialise_version_info(void) +{ + _mali_profiling_get_mali_version_type *mali_profiling_get_mali_version_symbol; + + mali_profiling_get_mali_version_symbol = symbol_get(_mali_profiling_get_mali_version); + + if (mali_profiling_get_mali_version_symbol) { + struct _mali_profiling_mali_version version_info; + + pr_debug("gator: mali online _mali_profiling_get_mali_version symbol @ %p\n", + mali_profiling_get_mali_version_symbol); + + /* + * Revise the number of each different core type using information derived from the DDK. + */ + mali_profiling_get_mali_version_symbol(&version_info); + + n_fp_cores = version_info.num_of_fp_cores; + n_vp_cores = version_info.num_of_vp_cores; + n_l2_cores = version_info.num_of_l2_cores; + + /* Release the function - we're done with it. */ + symbol_put(_mali_profiling_get_mali_version); + } else { + printk("gator: mali online _mali_profiling_get_mali_version symbol not found\n"); + printk("gator: check your Mali DDK version versus the GATOR_MALI_INTERFACE_STYLE setting\n"); + } +} +#endif + +static int create_files(struct super_block *sb, struct dentry *root) +{ + int event; + + char buf[40]; + int core_id; + int counter_number; + + pr_debug("gator: Initialising counters with style = %d\n", GATOR_MALI_INTERFACE_STYLE); + +#if GATOR_MALI_INTERFACE_STYLE > 3 + /* + * Initialise first: this sets up the number of cores available (on compatible DDK versions). + * Ideally this would not need guarding but other parts of the code depend on the interface style being set + * correctly; if it is not then the system can enter an inconsistent state. + */ + initialise_version_info(); +#endif + + mali_activity[0].cores = n_fp_cores; + mali_activity[1].cores = n_vp_cores; + for (event = 0; event < ARRAY_SIZE(mali_activity); event++) { + if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) { + return -1; + } + } + + /* Vertex processor counters */ + for (core_id = 0; core_id < n_vp_cores; core_id++) { + int activity_counter_id = ACTIVITY_VP_0; + snprintf(buf, sizeof buf, "ARM_%s_VP_%d_active", mali_name, core_id); + if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { + return -1; + } + + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_VP_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_VP_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* Fragment processors' counters */ + for (core_id = 0; core_id < n_fp_cores; core_id++) { + int activity_counter_id = ACTIVITY_FP_0 + core_id; + + snprintf(buf, sizeof buf, "ARM_%s_FP_%d_active", mali_name, core_id); + if (create_fs_entry(sb, root, buf, activity_counter_id, 0) != 0) { + return -1; + } + + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_FP_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_FP_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* L2 Cache counters */ + for (core_id = 0; core_id < n_l2_cores; core_id++) { + for (counter_number = 0; counter_number < 2; counter_number++) { + int counter_id = COUNTER_L2_0_C0 + (2 * core_id) + counter_number; + + snprintf(buf, sizeof buf, "ARM_%s_L2_%d_cnt%d", mali_name, core_id, counter_number); + if (create_fs_entry(sb, root, buf, counter_id, 1) != 0) { + return -1; + } + } + } + + /* Now set up the software counter entries */ + for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) { + snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event - FIRST_SW_COUNTER); + + if (create_fs_entry(sb, root, buf, event, 0) != 0) { + return -1; + } + } + + /* Now set up the special counter entries */ + snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip_cnt0", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_FILMSTRIP, 1) != 0) { + return -1; + } + +#ifdef DVFS_REPORTED_BY_DDK + snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_FREQUENCY, 1) != 0) { + return -1; + } + + snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name); + if (create_fs_entry(sb, root, buf, COUNTER_VOLTAGE, 1) != 0) { + return -1; + } +#endif + + return 0; +} + +/* + * Local store for the get_counters entry point into the DDK. + * This is stored here since it is used very regularly. + */ +static mali_profiling_get_counters_type *mali_get_counters = NULL; +static _mali_profiling_get_l2_counters_type *mali_get_l2_counters = NULL; + +/* + * Examine list of counters between two index limits and determine if any one is enabled. + * Returns 1 if any counter is enabled, 0 if none is. + */ +static int is_any_counter_enabled(unsigned int first_counter, unsigned int last_counter) +{ + unsigned int i; + + for (i = first_counter; i <= last_counter; i++) { + if (counter_enabled[i]) { + return 1; /* At least one counter is enabled */ + } + } + + return 0; /* No s/w counters enabled */ +} + +static void init_counters(unsigned int from_counter, unsigned int to_counter) +{ + unsigned int counter_id; + + /* 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 + */ + mali_profiling_set_event_type *mali_set_hw_event; + + mali_set_hw_event = symbol_get(_mali_profiling_set_event); + + if (mali_set_hw_event) { + pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); + + for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { + if (counter_enabled[counter_id]) { + mali_set_hw_event(counter_id, counter_event[counter_id]); + } else { + mali_set_hw_event(counter_id, 0xFFFFFFFF); + } + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali online _mali_profiling_set_event symbol not found\n"); + } +} + +static void mali_counter_initialize(void) +{ + int i; + + mali_profiling_control_type *mali_control; + + init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1); + init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1); + init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1); + + /* 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_COUNTER_ENABLE, (is_any_counter_enabled(FIRST_SW_COUNTER, LAST_SW_COUNTER) ? 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_get_counters = symbol_get(_mali_profiling_get_counters); + if (mali_get_counters) { + pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters); + + } else { + pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); + } + + mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters); + if (mali_get_l2_counters) { + pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters); + + } else { + pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined"); + } + + if (!mali_get_counters && !mali_get_l2_counters) { + pr_debug("gator: WARNING: no L2 counters available"); + n_l2_cores = 0; + } + + /* Clear counters in the start */ + for (i = 0; i < NUMBER_OF_EVENTS; i++) { + counter_data[i] = 0; + prev_set[i] = false; + } +} + +static void mali_counter_deinitialize(void) +{ + mali_profiling_set_event_type *mali_set_hw_event; + mali_profiling_control_type *mali_control; + + mali_set_hw_event = symbol_get(_mali_profiling_set_event); + + if (mali_set_hw_event) { + int i; + + pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n", mali_set_hw_event); + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + mali_set_hw_event(i, 0xFFFFFFFF); + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali offline _mali_profiling_set_event 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", mali_control); + + /* Reset the DDK state - disable counter collection */ + mali_control(SW_COUNTER_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_get_counters) { + symbol_put(_mali_profiling_get_counters); + } + + if (mali_get_l2_counters) { + symbol_put(_mali_profiling_get_l2_counters); + } +} + +static int start(void) +{ + // register tracepoints + if (GATOR_REGISTER_TRACE(mali_hw_counter)) { + printk("gator: mali_hw_counter tracepoint failed to activate\n"); + return -1; + } + + /* 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; + } + + trace_registered = 1; + + mali_counter_initialize(); + return 0; +} + +static void stop(void) +{ + unsigned int cnt; + + pr_debug("gator: mali stop\n"); + + if (trace_registered) { + GATOR_UNREGISTER_TRACE(mali_hw_counter); + + /* For Mali drivers with built-in support. */ + GATOR_UNREGISTER_TRACE(mali_sw_counters); + + pr_debug("gator: mali timeline tracepoint deactivated\n"); + + trace_registered = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_address[cnt] = NULL; + } + + mali_counter_deinitialize(); +} + +static void dump_counters(unsigned int from_counter, unsigned int to_counter, unsigned int *len) +{ + unsigned int counter_id; + + for (counter_id = from_counter; counter_id <= to_counter; counter_id++) { + if (counter_enabled[counter_id]) { + counter_dump[(*len)++] = counter_key[counter_id]; + counter_dump[(*len)++] = counter_data[counter_id]; + + counter_data[counter_id] = 0; + } + } +} + +static int read(int **buffer) +{ + int len = 0; + + if (!on_primary_core()) + return 0; + + // Read the L2 C0 and C1 here. + if (n_l2_cores > 0 && is_any_counter_enabled(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores))) { + unsigned int unavailable_l2_caches = 0; + _mali_profiling_l2_counter_values cache_values; + unsigned int cache_id; + struct _mali_profiling_core_counters *per_core; + + /* Poke the driver to get the counter values - older style; only one L2 cache */ + if (mali_get_l2_counters) { + unavailable_l2_caches = mali_get_l2_counters(&cache_values); + } else if (mali_get_counters) { + per_core = &cache_values.cores[0]; + mali_get_counters(&per_core->source0, &per_core->value0, &per_core->source1, &per_core->value1); + } else { + /* This should never happen, as n_l2_caches is only set > 0 if one of the above functions is found. */ + } + + /* Fill in the two cache counter values for each cache block. */ + for (cache_id = 0; cache_id < n_l2_cores; cache_id++) { + unsigned int counter_id_0 = COUNTER_L2_0_C0 + (2 * cache_id); + unsigned int counter_id_1 = counter_id_0 + 1; + + if ((1 << cache_id) & unavailable_l2_caches) { + continue; /* This cache is unavailable (powered-off, possibly). */ + } + + per_core = &cache_values.cores[cache_id]; + + if (counter_enabled[counter_id_0] && prev_set[counter_id_0]) { + // Calculate and save src0's counter val0 + counter_dump[len++] = counter_key[counter_id_0]; + counter_dump[len++] = per_core->value0 - counter_prev[counter_id_0]; + } + + if (counter_enabled[counter_id_1] && prev_set[counter_id_1]) { + // Calculate and save src1's counter val1 + counter_dump[len++] = counter_key[counter_id_1]; + counter_dump[len++] = per_core->value1 - counter_prev[counter_id_1]; + } + + // Save the previous values for the counters. + counter_prev[counter_id_0] = per_core->value0; + prev_set[counter_id_0] = true; + counter_prev[counter_id_1] = per_core->value1; + prev_set[counter_id_1] = true; + } + } + + /* Process other (non-timeline) counters. */ + dump_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1, &len); + dump_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1, &len); + + dump_counters(FIRST_SW_COUNTER, LAST_SW_COUNTER, &len); + +#ifdef DVFS_REPORTED_BY_DDK + { + int cnt; + /* + * Add in the voltage and frequency counters if enabled. Note that, since these are + * actually passed as events, the counter value should not be cleared. + */ + cnt = COUNTER_FREQUENCY; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + + cnt = COUNTER_VOLTAGE; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + } +#endif + + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read, +}; + +extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv) +{ +#ifdef DVFS_REPORTED_BY_DDK + counter_data[COUNTER_FREQUENCY] = frequency_mhz; + counter_data[COUNTER_VOLTAGE] = voltage_mv; +#endif +} + +int gator_events_mali_init(void) +{ + unsigned int cnt; + + pr_debug("gator: mali init\n"); + + gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity)); + + for (cnt = 0; 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); +} diff --git a/drivers/gator/gator_events_mali_4xx.h b/drivers/gator/gator_events_mali_4xx.h new file mode 100644 index 000000000000..976ca8c4cfa1 --- /dev/null +++ b/drivers/gator/gator_events_mali_4xx.h @@ -0,0 +1,18 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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. + * + */ + +/* + * Header contains common definitions for the Mali-4xx processors. + */ +#if !defined(GATOR_EVENTS_MALI_4xx_H) +#define GATOR_EVENTS_MALI_4xx_H + +extern void gator_events_mali_log_dvfs_event(unsigned int d0, unsigned int d1); + +#endif /* GATOR_EVENTS_MALI_4xx_H */ diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c new file mode 100644 index 000000000000..4f2cce4ce67b --- /dev/null +++ b/drivers/gator/gator_events_mali_common.c @@ -0,0 +1,69 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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_events_mali_common.h" + +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event) +{ + int err; + char buf[255]; + struct dentry *dir; + + /* If the counter name is empty ignore it */ + if (strlen(event_name) != 0) { + /* Set up the filesystem entry for this event. */ + snprintf(buf, sizeof(buf), "ARM_%s_%s", mali_name, event_name); + + dir = gatorfs_mkdir(sb, root, buf); + + if (dir == NULL) { + pr_debug("gator: %s: error creating file system for: %s (%s)", mali_name, event_name, buf); + return -1; + } + + err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled); + if (err != 0) { + pr_debug("gator: %s: error calling gatorfs_create_ulong for: %s (%s)", mali_name, event_name, buf); + return -1; + } + err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key); + if (err != 0) { + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); + return -1; + } + if (counter->cores != -1) { + err = gatorfs_create_ro_ulong(sb, dir, "cores", &counter->cores); + if (err != 0) { + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); + return -1; + } + } + if (event != NULL) { + err = gatorfs_create_ulong(sb, dir, "event", event); + if (err != 0) { + pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)", mali_name, event_name, buf); + return -1; + } + } + } + + return 0; +} + +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters) +{ + unsigned int cnt; + + for (cnt = 0; cnt < n_counters; cnt++) { + mali_counter *counter = &counters[cnt]; + + counter->key = gator_events_get_key(); + counter->enabled = 0; + counter->cores = -1; + } +} diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h new file mode 100644 index 000000000000..91d871bc915a --- /dev/null +++ b/drivers/gator/gator_events_mali_common.h @@ -0,0 +1,78 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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(GATOR_EVENTS_MALI_COMMON_H) +#define GATOR_EVENTS_MALI_COMMON_H + +#include "gator.h" + +#include +#include +#include +#include +#include + +/* Ensure that MALI_SUPPORT has been defined to something. */ +#ifndef MALI_SUPPORT +#error MALI_SUPPORT not defined! +#endif + +/* Values for the supported activity event types */ +#define ACTIVITY_START (1) +#define ACTIVITY_STOP (2) + +/* + * Runtime state information for a counter. + */ +typedef struct { + // 'key' (a unique id set by gatord and returned by gator.ko) + unsigned long key; + // counter enable state + unsigned long enabled; + // for activity counters, the number of cores, otherwise -1 + unsigned long cores; +} mali_counter; + +/* + * Mali-4xx + */ +typedef int mali_profiling_set_event_type(unsigned int, int); +typedef void mali_profiling_control_type(unsigned int, unsigned int); +typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, unsigned int *, unsigned int *); + +/* + * Driver entry points for functions called directly by gator. + */ +extern int _mali_profiling_set_event(unsigned int, int); +extern void _mali_profiling_control(unsigned int, unsigned int); +extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *); + +/** + * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and + * associate the key/enable values with this entry point. + * + * @param event_name The name of the event. + * @param sb Linux super block + * @param root Directory under which the entry will be created. + * @param counter_key Ptr to location which will be associated with the counter key. + * @param counter_enabled Ptr to location which will be associated with the counter enable state. + * + * @return 0 if entry point was created, non-zero if not. + */ +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event); + +/** + * Initializes the counter array. + * + * @param keys The array of counters + * @param n_counters The number of entries in each of the arrays. + */ +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters); + +#endif /* GATOR_EVENTS_MALI_COMMON_H */ diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c new file mode 100644 index 000000000000..e56ba84aefb8 --- /dev/null +++ b/drivers/gator/gator_events_mali_t6xx.c @@ -0,0 +1,567 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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 +#include + +#ifdef MALI_DIR_MIDGARD +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ +#include "linux/mali_linux_trace.h" +#endif + +#include "gator_events_mali_common.h" + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if (MALI_SUPPORT != MALI_T6xx) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx +#endif + +static const char mali_name[] = "Mali-T6xx"; + +/* Counters for Mali-T6xx: + * + * - Timeline events + * They are tracepoints, but instead of reporting a number they report a START/STOP event. + * They are reported in Streamline as number of microseconds while that particular counter was active. + * + * - SW counters + * They are tracepoints reporting a particular number. + * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed. + * + * - Accumulators + * They are the same as software counters but their value is not zeroed. + */ + +/* Timeline (start/stop) activity */ +static const char *timeline_event_names[] = { + "PM_SHADER_0", + "PM_SHADER_1", + "PM_SHADER_2", + "PM_SHADER_3", + "PM_SHADER_4", + "PM_SHADER_5", + "PM_SHADER_6", + "PM_SHADER_7", + "PM_TILER_0", + "PM_L2_0", + "PM_L2_1", + "MMU_AS_0", + "MMU_AS_1", + "MMU_AS_2", + "MMU_AS_3" +}; + +enum { + PM_SHADER_0 = 0, + PM_SHADER_1, + PM_SHADER_2, + PM_SHADER_3, + PM_SHADER_4, + PM_SHADER_5, + PM_SHADER_6, + PM_SHADER_7, + PM_TILER_0, + PM_L2_0, + PM_L2_1, + MMU_AS_0, + MMU_AS_1, + MMU_AS_2, + MMU_AS_3 +}; +/* The number of shader blocks in the enum above */ +#define NUM_PM_SHADER (8) + +/* Software Counters */ +static const char *software_counter_names[] = { + "MMU_PAGE_FAULT_0", + "MMU_PAGE_FAULT_1", + "MMU_PAGE_FAULT_2", + "MMU_PAGE_FAULT_3" +}; + +enum { + MMU_PAGE_FAULT_0 = 0, + MMU_PAGE_FAULT_1, + MMU_PAGE_FAULT_2, + MMU_PAGE_FAULT_3 +}; + +/* Software Counters */ +static const char *accumulators_names[] = { + "TOTAL_ALLOC_PAGES" +}; + +enum { + TOTAL_ALLOC_PAGES = 0 +}; + +#define FIRST_TIMELINE_EVENT (0) +#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0])) +#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS) +#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0])) +#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS) +#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0])) +#define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS) +#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1) + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_EVENTS]; +static unsigned long filmstrip_event; + +/* 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]; + +/* + * Array holding counter start times (in ns) for each counter. A zero here + * indicates that the activity monitored by this counter is not running. + */ +static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS]; + +/* The data we have recorded */ +static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS]; +static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS]; +static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS]; + +/* Hold the previous timestamp, used to calculate the sample interval. */ +static struct timespec prev_timestamp; + +/** + * Returns the timespan (in microseconds) between the two specified timestamps. + * + * @param start Ptr to the start timestamp + * @param end Ptr to the end timestamp + * + * @return Number of microseconds between the two timestamps (can be negative if start follows end). + */ +static inline long get_duration_us(const struct timespec *start, const struct timespec *end) +{ + long event_duration_us = (end->tv_nsec - start->tv_nsec) / 1000; + event_duration_us += (end->tv_sec - start->tv_sec) * 1000000; + + return event_duration_us; +} + +static void record_timeline_event(unsigned int timeline_index, unsigned int type) +{ + struct timespec event_timestamp; + struct timespec *event_start = &timeline_event_starttime[timeline_index]; + + switch (type) { + case ACTIVITY_START: + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Remember the start time if the activity is not already started */ + if (event_start->tv_sec == 0) { + *event_start = event_timestamp; /* Structure copy */ + } + break; + + case ACTIVITY_STOP: + /* if the counter was started... */ + if (event_start->tv_sec != 0) { + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Accumulate the duration in us */ + timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp); + + /* Reset the start time to indicate the activity is stopped. */ + event_start->tv_sec = 0; + } + break; + + default: + /* Other activity events are ignored. */ + break; + } +} + +/* + * Documentation about the following tracepoints is in mali_linux_trace.h + */ + +GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value)) +{ +#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */ +#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */ +#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ +#define BIT_AT(value, pos) ((value >> pos) & 1) + + static unsigned long long previous_shader_bitmask = 0; + static unsigned long long previous_tiler_bitmask = 0; + static unsigned long long previous_l2_bitmask = 0; + + switch (event_id) { + case SHADER_PRESENT_LO: + { + unsigned long long changed_bitmask = previous_shader_bitmask ^ value; + int pos; + + for (pos = 0; pos < NUM_PM_SHADER; ++pos) { + if (BIT_AT(changed_bitmask, pos)) { + record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP); + } + } + + previous_shader_bitmask = value; + break; + } + + case TILER_PRESENT_LO: + { + unsigned long long changed = previous_tiler_bitmask ^ value; + + if (BIT_AT(changed, 0)) { + record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_tiler_bitmask = value; + break; + } + + case L2_PRESENT_LO: + { + unsigned long long changed = previous_l2_bitmask ^ value; + + if (BIT_AT(changed, 0)) { + record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + if (BIT_AT(changed, 4)) { + record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_l2_bitmask = value; + break; + } + + default: + /* No other blocks are supported at present */ + break; + } + +#undef SHADER_PRESENT_LO +#undef TILER_PRESENT_LO +#undef L2_PRESENT_LO +#undef BIT_AT +} + +GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value)) +{ + /* We add to the previous since we may receive many tracepoints in one sample period */ + sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value; +} + +GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START); +} + +GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP); +} + +GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id)) +{ + accumulators_data[TOTAL_ALLOC_PAGES] = event_id; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + mali_profiling_control_type *mali_control; + + for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) { + if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) { + if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) { + if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0) { + return -1; + } + counter_index++; + } + + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) { + return -1; + } + symbol_put(_mali_profiling_control); + } + + return 0; +} + +static int register_tracepoints(void) +{ + if (GATOR_REGISTER_TRACE(mali_pm_status)) { + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) { + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) { + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) { + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) { + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n"); + return 0; + } + + pr_debug("gator: Mali-T6xx: start\n"); + pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change); + + return 1; +} + +static int start(void) +{ + unsigned int cnt; + mali_profiling_control_type *mali_control; + + /* Clean all data for the next capture */ + for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) { + timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0; + timeline_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) { + sw_counter_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) { + accumulators_data[cnt] = 0; + } + + /* Register tracepoints */ + if (register_tracepoints() == 0) { + return -1; + } + + /* 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 enabled = counters[FILMSTRIP].enabled ? 1 : 0; + unsigned int rate = filmstrip_event & 0xff; + unsigned int resize_factor = (filmstrip_event >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + mali_control(FBDUMP_CONTROL_ENABLE, enabled); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + + /* + * Set the first timestamp for calculating the sample interval. The first interval could be quite long, + * since it will be the time between 'start' and the first 'read'. + * This means that timeline values will be divided by a big number for the first sample. + */ + getnstimeofday(&prev_timestamp); + + return 0; +} + +static void stop(void) +{ + mali_profiling_control_type *mali_control; + + pr_debug("gator: Mali-T6xx: stop\n"); + + /* + * It is safe to unregister traces even if they were not successfully + * registered, so no need to check. + */ + GATOR_UNREGISTER_TRACE(mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\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", mali_control); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } +} + +static int read(int **buffer) +{ + int cnt; + int len = 0; + long sample_interval_us = 0; + struct timespec read_timestamp; + + if (!on_primary_core()) { + return 0; + } + + /* Get the start of this sample period. */ + getnstimeofday(&read_timestamp); + + /* + * Calculate the sample interval if the previous sample time is valid. + * We use tv_sec since it will not be 0. + */ + if (prev_timestamp.tv_sec != 0) { + sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp); + } + + /* Structure copy. Update the previous timestamp. */ + prev_timestamp = read_timestamp; + + /* + * Report the timeline counters (ACTIVITY_START/STOP) + */ + for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) { + mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_TIMELINE_EVENT; + unsigned int value; + + /* If the activity is still running, reset its start time to the start of this sample period + * to correct the count. Add the time up to the end of the sample onto the count. */ + if (timeline_event_starttime[index].tv_sec != 0) { + const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp); + timeline_data[index] += event_duration; + timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */ + } + + if (sample_interval_us != 0) { + /* Convert the counter to a percent-of-sample value */ + value = (timeline_data[index] * 100) / sample_interval_us; + } else { + pr_debug("gator: Mali-T6xx: setting value to zero\n"); + value = 0; + } + + /* Clear the counter value ready for the next sample. */ + timeline_data[index] = 0; + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + + /* Report the software counters */ + for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_SOFTWARE_COUNTER; + counter_dump[len++] = counter->key; + counter_dump[len++] = sw_counter_data[index]; + /* Set the value to zero for the next time */ + sw_counter_data[index] = 0; + } + } + + /* Report the accumulators */ + for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int index = cnt - FIRST_ACCUMULATOR; + counter_dump[len++] = counter->key; + counter_dump[len++] = accumulators_data[index]; + /* Do not zero the accumulator */ + } + } + + /* Update the buffer */ + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +extern int gator_events_mali_t6xx_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + + gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c new file mode 100644 index 000000000000..3a072bb6ac06 --- /dev/null +++ b/drivers/gator/gator_events_mali_t6xx_hw.c @@ -0,0 +1,913 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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 +#include + +/* Mali T6xx DDK includes */ +#if defined(MALI_SIMPLE_API) +/* Header with wrapper functions to kbase structures and functions */ +#include "mali/mali_dd_gator_api.h" +#elif defined(MALI_DIR_MIDGARD) +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#include "mali_kbase.h" +#include "mali_kbase_mem_linux.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ +#include "linux/mali_linux_trace.h" +#include "kbase/src/common/mali_kbase.h" +#include "kbase/src/linux/mali_kbase_mem_linux.h" +#endif + +/* If API version is not specified then assume API version 1. */ +#ifndef MALI_DDK_GATOR_API_VERSION +#define MALI_DDK_GATOR_API_VERSION 1 +#endif + +#if (MALI_DDK_GATOR_API_VERSION != 1) && (MALI_DDK_GATOR_API_VERSION != 2) && (MALI_DDK_GATOR_API_VERSION != 3) +#error MALI_DDK_GATOR_API_VERSION is invalid (must be 1 for r1/r2 DDK, or 2 for r3 DDK, or 3 for r? DDK). +#endif + +#include "gator_events_mali_common.h" + +/* + * Mali-T6xx + */ +#if MALI_DDK_GATOR_API_VERSION == 3 +typedef uint32_t kbase_dd_instr_hwcnt_dump_irq_type(struct mali_dd_hwcnt_handles *); +typedef uint32_t kbase_dd_instr_hwcnt_dump_complete_type(struct mali_dd_hwcnt_handles *, uint32_t *); +typedef struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init_type(struct mali_dd_hwcnt_info *); +typedef void mali_dd_hwcnt_clear_type(struct mali_dd_hwcnt_info *, struct mali_dd_hwcnt_handles *); + +static kbase_dd_instr_hwcnt_dump_irq_type *kbase_dd_instr_hwcnt_dump_irq_symbol; +static kbase_dd_instr_hwcnt_dump_complete_type *kbase_dd_instr_hwcnt_dump_complete_symbol; +static mali_dd_hwcnt_init_type *mali_dd_hwcnt_init_symbol; +static mali_dd_hwcnt_clear_type *mali_dd_hwcnt_clear_symbol; + +#else +typedef struct kbase_device *kbase_find_device_type(int); +typedef struct kbase_context *kbase_create_context_type(struct kbase_device *); +typedef void kbase_destroy_context_type(struct kbase_context *); + +#if MALI_DDK_GATOR_API_VERSION == 1 +typedef void *kbase_va_alloc_type(struct kbase_context *, u32); +typedef void kbase_va_free_type(struct kbase_context *, void *); +#elif MALI_DDK_GATOR_API_VERSION == 2 +typedef void *kbase_va_alloc_type(struct kbase_context *, u32, kbase_hwc_dma_mapping * handle); +typedef void kbase_va_free_type(struct kbase_context *, kbase_hwc_dma_mapping * handle); +#endif + +typedef mali_error kbase_instr_hwcnt_enable_type(struct kbase_context *, struct kbase_uk_hwcnt_setup *); +typedef mali_error kbase_instr_hwcnt_disable_type(struct kbase_context *); +typedef mali_error kbase_instr_hwcnt_clear_type(struct kbase_context *); +typedef mali_error kbase_instr_hwcnt_dump_irq_type(struct kbase_context *); +typedef mali_bool kbase_instr_hwcnt_dump_complete_type(struct kbase_context *, mali_bool *); + +static kbase_find_device_type *kbase_find_device_symbol; +static kbase_create_context_type *kbase_create_context_symbol; +static kbase_va_alloc_type *kbase_va_alloc_symbol; +static kbase_instr_hwcnt_enable_type *kbase_instr_hwcnt_enable_symbol; +static kbase_instr_hwcnt_clear_type *kbase_instr_hwcnt_clear_symbol; +static kbase_instr_hwcnt_dump_irq_type *kbase_instr_hwcnt_dump_irq_symbol; +static kbase_instr_hwcnt_dump_complete_type *kbase_instr_hwcnt_dump_complete_symbol; +static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol; +static kbase_va_free_type *kbase_va_free_symbol; +static kbase_destroy_context_type *kbase_destroy_context_symbol; +#endif + +static long shader_present_low = 0; + +/** The interval between reads, in ns. + * + * Earlier we introduced + * a 'hold off for 1ms after last read' to resolve MIDBASE-2178 and MALINE-724. + * However, the 1ms hold off is too long if no context switches occur as there is a race + * between this value and the tick of the read clock in gator which is also 1ms. If we 'miss' the + * current read, the counter values are effectively 'spread' over 2ms and the values seen are half + * what they should be (since Streamline averages over sample time). In the presence of context switches + * this spread can vary and markedly affect the counters. Currently there is no 'proper' solution to + * this, but empirically we have found that reducing the minimum read interval to 950us causes the + * counts to be much more stable. + */ +static const int READ_INTERVAL_NSEC = 950000; + +#if GATOR_TEST +#include "gator_events_mali_t6xx_hw_test.c" +#endif + +/* Blocks for HW counters */ +enum { + JM_BLOCK = 0, + TILER_BLOCK, + SHADER_BLOCK, + MMU_BLOCK +}; + +static const char mali_name[] = "Mali-T6xx"; + +/* Counters for Mali-T6xx: + * + * - HW counters, 4 blocks + * For HW counters we need strings to create /dev/gator/events files. + * Enums are not needed because the position of the HW name in the array is the same + * of the corresponding value in the received block of memory. + * HW counters are requested by calculating a bitmask, passed then to the driver. + * Every millisecond a HW counters dump is requested, and if the previous has been completed they are read. + */ + +/* Hardware Counters */ +static const char *const hardware_counter_names[] = { + /* Job Manager */ + "", + "", + "", + "", + "MESSAGES_SENT", + "MESSAGES_RECEIVED", + "GPU_ACTIVE", /* 6 */ + "IRQ_ACTIVE", + "JS0_JOBS", + "JS0_TASKS", + "JS0_ACTIVE", + "", + "JS0_WAIT_READ", + "JS0_WAIT_ISSUE", + "JS0_WAIT_DEPEND", + "JS0_WAIT_FINISH", + "JS1_JOBS", + "JS1_TASKS", + "JS1_ACTIVE", + "", + "JS1_WAIT_READ", + "JS1_WAIT_ISSUE", + "JS1_WAIT_DEPEND", + "JS1_WAIT_FINISH", + "JS2_JOBS", + "JS2_TASKS", + "JS2_ACTIVE", + "", + "JS2_WAIT_READ", + "JS2_WAIT_ISSUE", + "JS2_WAIT_DEPEND", + "JS2_WAIT_FINISH", + "JS3_JOBS", + "JS3_TASKS", + "JS3_ACTIVE", + "", + "JS3_WAIT_READ", + "JS3_WAIT_ISSUE", + "JS3_WAIT_DEPEND", + "JS3_WAIT_FINISH", + "JS4_JOBS", + "JS4_TASKS", + "JS4_ACTIVE", + "", + "JS4_WAIT_READ", + "JS4_WAIT_ISSUE", + "JS4_WAIT_DEPEND", + "JS4_WAIT_FINISH", + "JS5_JOBS", + "JS5_TASKS", + "JS5_ACTIVE", + "", + "JS5_WAIT_READ", + "JS5_WAIT_ISSUE", + "JS5_WAIT_DEPEND", + "JS5_WAIT_FINISH", + "JS6_JOBS", + "JS6_TASKS", + "JS6_ACTIVE", + "", + "JS6_WAIT_READ", + "JS6_WAIT_ISSUE", + "JS6_WAIT_DEPEND", + "JS6_WAIT_FINISH", + + /*Tiler */ + "", + "", + "", + "JOBS_PROCESSED", + "TRIANGLES", + "QUADS", + "POLYGONS", + "POINTS", + "LINES", + "VCACHE_HIT", + "VCACHE_MISS", + "FRONT_FACING", + "BACK_FACING", + "PRIM_VISIBLE", + "PRIM_CULLED", + "PRIM_CLIPPED", + "LEVEL0", + "LEVEL1", + "LEVEL2", + "LEVEL3", + "LEVEL4", + "LEVEL5", + "LEVEL6", + "LEVEL7", + "COMMAND_1", + "COMMAND_2", + "COMMAND_3", + "COMMAND_4", + "COMMAND_4_7", + "COMMAND_8_15", + "COMMAND_16_63", + "COMMAND_64", + "COMPRESS_IN", + "COMPRESS_OUT", + "COMPRESS_FLUSH", + "TIMESTAMPS", + "PCACHE_HIT", + "PCACHE_MISS", + "PCACHE_LINE", + "PCACHE_STALL", + "WRBUF_HIT", + "WRBUF_MISS", + "WRBUF_LINE", + "WRBUF_PARTIAL", + "WRBUF_STALL", + "ACTIVE", + "LOADING_DESC", + "INDEX_WAIT", + "INDEX_RANGE_WAIT", + "VERTEX_WAIT", + "PCACHE_WAIT", + "WRBUF_WAIT", + "BUS_READ", + "BUS_WRITE", + "", + "", + "", + "", + "", + "UTLB_STALL", + "UTLB_REPLAY_MISS", + "UTLB_REPLAY_FULL", + "UTLB_NEW_MISS", + "UTLB_HIT", + + /* Shader Core */ + "", + "", + "", + "SHADER_CORE_ACTIVE", + "FRAG_ACTIVE", + "FRAG_PRIMATIVES", + "FRAG_PRIMATIVES_DROPPED", + "FRAG_CYCLE_DESC", + "FRAG_CYCLES_PLR", + "FRAG_CYCLES_VERT", + "FRAG_CYCLES_TRISETUP", + "FRAG_CYCLES_RAST", + "FRAG_THREADS", + "FRAG_DUMMY_THREADS", + "FRAG_QUADS_RAST", + "FRAG_QUADS_EZS_TEST", + "FRAG_QUADS_EZS_KILLED", + "FRAG_QUADS_LZS_TEST", + "FRAG_QUADS_LZS_KILLED", + "FRAG_CYCLE_NO_TILE", + "FRAG_NUM_TILES", + "FRAG_TRANS_ELIM", + "COMPUTE_ACTIVE", + "COMPUTE_TASKS", + "COMPUTE_THREADS", + "COMPUTE_CYCLES_DESC", + "TRIPIPE_ACTIVE", + "ARITH_WORDS", + "ARITH_CYCLES_REG", + "ARITH_CYCLES_L0", + "ARITH_FRAG_DEPEND", + "LS_WORDS", + "LS_ISSUES", + "LS_RESTARTS", + "LS_REISSUES_MISS", + "LS_REISSUES_VD", + "LS_REISSUE_ATTRIB_MISS", + "LS_NO_WB", + "TEX_WORDS", + "TEX_BUBBLES", + "TEX_WORDS_L0", + "TEX_WORDS_DESC", + "TEX_THREADS", + "TEX_RECIRC_FMISS", + "TEX_RECIRC_DESC", + "TEX_RECIRC_MULTI", + "TEX_RECIRC_PMISS", + "TEX_RECIRC_CONF", + "LSC_READ_HITS", + "LSC_READ_MISSES", + "LSC_WRITE_HITS", + "LSC_WRITE_MISSES", + "LSC_ATOMIC_HITS", + "LSC_ATOMIC_MISSES", + "LSC_LINE_FETCHES", + "LSC_DIRTY_LINE", + "LSC_SNOOPS", + "AXI_TLB_STALL", + "AXI_TLB_MIESS", + "AXI_TLB_TRANSACTION", + "LS_TLB_MISS", + "LS_TLB_HIT", + "AXI_BEATS_READ", + "AXI_BEATS_WRITTEN", + + /*L2 and MMU */ + "", + "", + "", + "", + "MMU_HIT", + "MMU_NEW_MISS", + "MMU_REPLAY_FULL", + "MMU_REPLAY_MISS", + "MMU_TABLE_WALK", + "", + "", + "", + "", + "", + "", + "", + "UTLB_HIT", + "UTLB_NEW_MISS", + "UTLB_REPLAY_FULL", + "UTLB_REPLAY_MISS", + "UTLB_STALL", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "L2_WRITE_BEATS", + "L2_READ_BEATS", + "L2_ANY_LOOKUP", + "L2_READ_LOOKUP", + "L2_SREAD_LOOKUP", + "L2_READ_REPLAY", + "L2_READ_SNOOP", + "L2_READ_HIT", + "L2_CLEAN_MISS", + "L2_WRITE_LOOKUP", + "L2_SWRITE_LOOKUP", + "L2_WRITE_REPLAY", + "L2_WRITE_SNOOP", + "L2_WRITE_HIT", + "L2_EXT_READ_FULL", + "L2_EXT_READ_HALF", + "L2_EXT_WRITE_FULL", + "L2_EXT_WRITE_HALF", + "L2_EXT_READ", + "L2_EXT_READ_LINE", + "L2_EXT_WRITE", + "L2_EXT_WRITE_LINE", + "L2_EXT_WRITE_SMALL", + "L2_EXT_BARRIER", + "L2_EXT_AR_STALL", + "L2_EXT_R_BUF_FULL", + "L2_EXT_RD_BUF_FULL", + "L2_EXT_R_RAW", + "L2_EXT_W_STALL", + "L2_EXT_W_BUF_FULL", + "L2_EXT_R_W_HAZARD", + "L2_TAG_HAZARD", + "L2_SNOOP_FULL", + "L2_REPLAY_FULL" +}; + +#define NUMBER_OF_HARDWARE_COUNTERS (sizeof(hardware_counter_names) / sizeof(hardware_counter_names[0])) + +#define GET_HW_BLOCK(c) (((c) >> 6) & 0x3) +#define GET_COUNTER_OFFSET(c) ((c) & 0x3f) + +#if MALI_DDK_GATOR_API_VERSION == 3 +/* Opaque handles for kbase_context and kbase_hwc_dma_mapping */ +static struct mali_dd_hwcnt_handles *handles; + +/* Information about hardware counters */ +static struct mali_dd_hwcnt_info *in_out_info; + +#else +/* Memory to dump hardware counters into */ +static void *kernel_dump_buffer; + +#if MALI_DDK_GATOR_API_VERSION == 2 +/* DMA state used to manage lifetime of the buffer */ +kbase_hwc_dma_mapping kernel_dump_buffer_handle; +#endif + +/* kbase context and device */ +static struct kbase_context *kbcontext = NULL; +static struct kbase_device *kbdevice = NULL; +#endif + +static volatile bool kbase_device_busy = false; +static unsigned int num_hardware_counters_enabled; + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; + +extern mali_counter mali_activity[3]; +static const char* const mali_activity_names[] = { + "fragment", + "vertex", + "opencl", +}; + +#define SYMBOL_GET(FUNCTION, ERROR_COUNT) \ + if(FUNCTION ## _symbol) \ + { \ + printk("gator: mali " #FUNCTION " symbol was already registered\n"); \ + (ERROR_COUNT)++; \ + } \ + else \ + { \ + FUNCTION ## _symbol = symbol_get(FUNCTION); \ + if(! FUNCTION ## _symbol) \ + { \ + printk("gator: mali online " #FUNCTION " symbol not found\n"); \ + (ERROR_COUNT)++; \ + } \ + } + +#define SYMBOL_CLEANUP(FUNCTION) \ + if(FUNCTION ## _symbol) \ + { \ + symbol_put(FUNCTION); \ + FUNCTION ## _symbol = NULL; \ + } + +/** + * Execute symbol_get for all the Mali symbols and check for success. + * @return the number of symbols not loaded. + */ +static int init_symbols(void) +{ + int error_count = 0; +#if MALI_DDK_GATOR_API_VERSION == 3 + SYMBOL_GET(kbase_dd_instr_hwcnt_dump_irq, error_count); + SYMBOL_GET(kbase_dd_instr_hwcnt_dump_complete, error_count); + SYMBOL_GET(mali_dd_hwcnt_init, error_count); + SYMBOL_GET(mali_dd_hwcnt_clear, error_count); +#else + SYMBOL_GET(kbase_find_device, error_count); + SYMBOL_GET(kbase_create_context, error_count); + SYMBOL_GET(kbase_va_alloc, error_count); + SYMBOL_GET(kbase_instr_hwcnt_enable, error_count); + SYMBOL_GET(kbase_instr_hwcnt_clear, error_count); + SYMBOL_GET(kbase_instr_hwcnt_dump_irq, error_count); + SYMBOL_GET(kbase_instr_hwcnt_dump_complete, error_count); + SYMBOL_GET(kbase_instr_hwcnt_disable, error_count); + SYMBOL_GET(kbase_va_free, error_count); + SYMBOL_GET(kbase_destroy_context, error_count); +#endif + + return error_count; +} + +/** + * Execute symbol_put for all the registered Mali symbols. + */ +static void clean_symbols(void) +{ +#if MALI_DDK_GATOR_API_VERSION == 3 + SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_irq); + SYMBOL_CLEANUP(kbase_dd_instr_hwcnt_dump_complete); + SYMBOL_CLEANUP(mali_dd_hwcnt_init); + SYMBOL_CLEANUP(mali_dd_hwcnt_clear); +#else + SYMBOL_CLEANUP(kbase_find_device); + SYMBOL_CLEANUP(kbase_create_context); + SYMBOL_CLEANUP(kbase_va_alloc); + SYMBOL_CLEANUP(kbase_instr_hwcnt_enable); + SYMBOL_CLEANUP(kbase_instr_hwcnt_clear); + SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_irq); + SYMBOL_CLEANUP(kbase_instr_hwcnt_dump_complete); + SYMBOL_CLEANUP(kbase_instr_hwcnt_disable); + SYMBOL_CLEANUP(kbase_va_free); + SYMBOL_CLEANUP(kbase_destroy_context); +#endif +} + +/** + * Determines whether a read should take place + * @param current_time The current time, obtained from getnstimeofday() + * @param prev_time_s The number of seconds at the previous read attempt. + * @param next_read_time_ns The time (in ns) when the next read should be allowed. + * + * Note that this function has been separated out here to allow it to be tested. + */ +static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns) +{ + /* If the current ns count rolls over a second, roll the next read time too. */ + if (current_time->tv_sec != *prev_time_s) { + *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC; + } + + /* Abort the read if the next read time has not arrived. */ + if (current_time->tv_nsec < *next_read_time_ns) { + return 0; + } + + /* Set the next read some fixed time after this one, and update the read timestamp. */ + *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC; + + *prev_time_s = current_time->tv_sec; + return 1; +} + +static int start(void) +{ +#if MALI_DDK_GATOR_API_VERSION < 3 + struct kbase_uk_hwcnt_setup setup; + unsigned long long shadersPresent = 0; + u16 bitmask[] = { 0, 0, 0, 0 }; + mali_error err; +#endif + int cnt; + + /* Setup HW counters */ + num_hardware_counters_enabled = 0; + + if (NUMBER_OF_HARDWARE_COUNTERS != 256) { + pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS); + } + +#if MALI_DDK_GATOR_API_VERSION == 3 + /* Declare and initialise mali_dd_hwcnt_info structure */ + in_out_info = kmalloc(sizeof(struct mali_dd_hwcnt_info), GFP_KERNEL); + for (cnt = 0; cnt < 4; cnt++){ + in_out_info->bitmask[cnt] = 0; + } +#endif + /* Calculate enable bitmasks based on counters_enabled array */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + int block = GET_HW_BLOCK(cnt); + int enable_bit = GET_COUNTER_OFFSET(cnt) / 4; +#if MALI_DDK_GATOR_API_VERSION == 3 + in_out_info->bitmask[block] |= (1 << enable_bit); +#else + bitmask[block] |= (1 << enable_bit); +#endif + pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt); + num_hardware_counters_enabled++; + } + } + +#if MALI_DDK_GATOR_API_VERSION == 3 + /* Create a kbase context for HW counters */ + if (num_hardware_counters_enabled > 0) { + if (init_symbols() > 0) { + clean_symbols(); + /* No Mali driver code entrypoints found - not a fault. */ + return 0; + } + + handles = mali_dd_hwcnt_init_symbol(in_out_info); + + if(handles == NULL) { + goto out; + } + + /* See if we can get the number of shader cores */ + shader_present_low = (unsigned long)in_out_info->shader_present_bitmap; + + kbase_device_busy = false; + } + + return 0; +#else + /* Create a kbase context for HW counters */ + if (num_hardware_counters_enabled > 0) { + if (init_symbols() > 0) { + clean_symbols(); + /* No Mali driver code entrypoints found - not a fault. */ + return 0; + } + + kbdevice = kbase_find_device_symbol(-1); + + /* If we already got a context, fail */ + if (kbcontext) { + pr_debug("gator: Mali-T6xx: error context already present\n"); + goto out; + } + + /* kbcontext will only be valid after all the Mali symbols are loaded successfully */ + kbcontext = kbase_create_context_symbol(kbdevice); + if (!kbcontext) { + pr_debug("gator: Mali-T6xx: error creating kbase context\n"); + goto out; + } + + + /* See if we can get the number of shader cores */ + shadersPresent = kbdevice->shader_present_bitmap; + shader_present_low = (unsigned long)shadersPresent; + + /* + * The amount of memory needed to store the dump (bytes) + * DUMP_SIZE = number of core groups + * * number of blocks (always 8 for midgard) + * * number of counters per block (always 64 for midgard) + * * number of bytes per counter (always 4 in midgard) + * For a Mali-T6xx with a single core group = 1 * 8 * 64 * 4 = 2048 + * For a Mali-T6xx with a dual core group = 2 * 8 * 64 * 4 = 4096 + */ +#if MALI_DDK_GATOR_API_VERSION == 1 + kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle); +#endif + if (!kernel_dump_buffer) { + pr_debug("gator: Mali-T6xx: error trying to allocate va\n"); + goto destroy_context; + } + + setup.dump_buffer = (uintptr_t)kernel_dump_buffer; + setup.jm_bm = bitmask[JM_BLOCK]; + setup.tiler_bm = bitmask[TILER_BLOCK]; + setup.shader_bm = bitmask[SHADER_BLOCK]; + setup.mmu_l2_bm = bitmask[MMU_BLOCK]; + /* These counters do not exist on Mali-T60x */ + setup.l3_cache_bm = 0; + + /* Use kbase API to enable hardware counters and provide dump buffer */ + err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup); + if (err != MALI_ERROR_NONE) { + pr_debug("gator: Mali-T6xx: can't setup hardware counters\n"); + goto free_buffer; + } + pr_debug("gator: Mali-T6xx: hardware counters enabled\n"); + kbase_instr_hwcnt_clear_symbol(kbcontext); + pr_debug("gator: Mali-T6xx: hardware counters cleared \n"); + + kbase_device_busy = false; + } + + return 0; + +free_buffer: +#if MALI_DDK_GATOR_API_VERSION == 1 + kbase_va_free_symbol(kbcontext, kernel_dump_buffer); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kbase_va_free_symbol(kbcontext, &kernel_dump_buffer_handle); +#endif + +destroy_context: + kbase_destroy_context_symbol(kbcontext); +#endif + +out: + clean_symbols(); + return -1; +} + +static void stop(void) +{ + unsigned int cnt; +#if MALI_DDK_GATOR_API_VERSION == 3 + struct mali_dd_hwcnt_handles *temp_hand; +#else + struct kbase_context *temp_kbcontext; +#endif + + pr_debug("gator: Mali-T6xx: stop\n"); + + /* Set all counters as disabled */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + counters[cnt].enabled = 0; + } + + /* Destroy the context for HW counters */ +#if MALI_DDK_GATOR_API_VERSION == 3 + if (num_hardware_counters_enabled > 0 && handles != NULL) { + /* + * Set the global variable to NULL before destroying it, because + * other function will check this before using it. + */ + temp_hand = handles; + handles = NULL; + + mali_dd_hwcnt_clear_symbol(in_out_info, temp_hand); + + kfree(in_out_info); + +#else + if (num_hardware_counters_enabled > 0 && kbcontext != NULL) { + /* + * Set the global variable to NULL before destroying it, because + * other function will check this before using it. + */ + temp_kbcontext = kbcontext; + kbcontext = NULL; + + kbase_instr_hwcnt_disable_symbol(temp_kbcontext); + +#if MALI_DDK_GATOR_API_VERSION == 1 + kbase_va_free_symbol(temp_kbcontext, kernel_dump_buffer); +#elif MALI_DDK_GATOR_API_VERSION == 2 + kbase_va_free_symbol(temp_kbcontext, &kernel_dump_buffer_handle); +#endif + + kbase_destroy_context_symbol(temp_kbcontext); +#endif + + pr_debug("gator: Mali-T6xx: hardware counters stopped\n"); + + clean_symbols(); + } +} + +static int read(int **buffer) +{ + int cnt; + int len = 0; + u32 value = 0; + uint32_t success; + + struct timespec current_time; + static u32 prev_time_s = 0; + static s32 next_read_time_ns = 0; + + if (!on_primary_core()) { + return 0; + } + + getnstimeofday(¤t_time); + + /* + * Discard reads unless a respectable time has passed. This reduces the load on the GPU without sacrificing + * accuracy on the Streamline display. + */ + if (!is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns)) { + return 0; + } + + /* + * Report the HW counters + * Only process hardware counters if at least one of the hardware counters is enabled. + */ + if (num_hardware_counters_enabled > 0) { + const unsigned int vithar_blocks[] = { + 0x700, /* VITHAR_JOB_MANAGER, Block 0 */ + 0x400, /* VITHAR_TILER, Block 1 */ + 0x000, /* VITHAR_SHADER_CORE, Block 2 */ + 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ + }; + +#if MALI_DDK_GATOR_API_VERSION == 3 + if (!handles) { + return -1; + } + + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_dd_instr_hwcnt_dump_complete_symbol(handles, &success) == MALI_TRUE) { +#else + if (!kbcontext) { + return -1; + } + + /* Mali symbols can be called safely since a kbcontext is valid */ + if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success) == MALI_TRUE) { +#endif + kbase_device_busy = false; + + if (success == MALI_TRUE) { + /* Cycle through hardware counters and accumulate totals */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) { + const int block = GET_HW_BLOCK(cnt); + const int counter_offset = GET_COUNTER_OFFSET(cnt); + +#if MALI_DDK_GATOR_API_VERSION == 3 + const char* block_base_address = (char*)in_out_info->kernel_dump_buffer + vithar_blocks[block]; +#else + const char* block_base_address = (char*)kernel_dump_buffer + vithar_blocks[block]; +#endif + + /* If counter belongs to shader block need to take into account all cores */ + if (block == SHADER_BLOCK) { + int i = 0; + int shader_core_count = 0; + value = 0; + + for (i = 0; i < 4; i++) { + if ((shader_present_low >> i) & 1) { + value += *((u32*) (block_base_address + (0x100 * i)) + counter_offset); + shader_core_count++; + } + } + + for (i = 0; i < 4; i++) { + if((shader_present_low >> (i+4)) & 1) { + value += *((u32*)(block_base_address + (0x100 * i) + 0x800) + counter_offset); + shader_core_count++; + } + } + + /* Need to total by number of cores to produce an average */ + if (shader_core_count != 0) { + value /= shader_core_count; + } + } else { + value = *((u32*)block_base_address + counter_offset); + } + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + } + } + + if (!kbase_device_busy) { + kbase_device_busy = true; +#if MALI_DDK_GATOR_API_VERSION == 3 + kbase_dd_instr_hwcnt_dump_irq_symbol(handles); +#else + kbase_instr_hwcnt_dump_irq_symbol(kbcontext); +#endif + } + } + + /* Update the buffer */ + if (buffer) { + *buffer = (int *)counter_dump; + } + + return len; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + unsigned int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + + for (event = 0; event < ARRAY_SIZE(mali_activity); event++) { + if (gator_mali_create_file_system(mali_name, mali_activity_names[event], sb, root, &mali_activity[event], NULL) != 0) { + return -1; + } + } + + for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) { + if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) + return -1; + counter_index++; + } + + return 0; +} + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +int gator_events_mali_t6xx_hw_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + +#if GATOR_TEST + test_all_is_read_scheduled(); +#endif + + gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity)); + gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} diff --git a/drivers/gator/gator_events_mali_t6xx_hw_test.c b/drivers/gator/gator_events_mali_t6xx_hw_test.c new file mode 100644 index 000000000000..ba6553f3540f --- /dev/null +++ b/drivers/gator/gator_events_mali_t6xx_hw_test.c @@ -0,0 +1,55 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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. + * + */ + +/** + * Test functions for mali_t600_hw code. + */ + +static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns); + +static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns) +{ + struct timespec current_time; + u32 prev_time_s = prev_s; + s32 next_read_time_ns = next_ns; + + current_time.tv_sec = s; + current_time.tv_nsec = ns; + + if (is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns) != expected_result) { + printk("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result); + return 0; + } + + if (next_read_time_ns != expected_next_ns) { + printk("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns); + return 0; + } + + return 1; +} + +static void test_all_is_read_scheduled(void) +{ + const int HIGHEST_NS = 999999999; + int n_tests_passed = 0; + + printk("gator: running tests on %s\n", __FILE__); + + n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */ + n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */ + + n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500); + n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC); + n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC); + + n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC); + + printk("gator: %d tests passed\n", n_tests_passed); +} diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c new file mode 100644 index 000000000000..c633dfdce306 --- /dev/null +++ b/drivers/gator/gator_events_meminfo.c @@ -0,0 +1,409 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 +#include +#include + +enum { + MEMINFO_MEMFREE, + MEMINFO_MEMUSED, + MEMINFO_BUFFERRAM, + MEMINFO_TOTAL, +}; + +enum { + PROC_SIZE, + PROC_SHARE, + PROC_TEXT, + PROC_DATA, + PROC_COUNT, +}; + +static const char * const meminfo_names[] = { + "Linux_meminfo_memfree", + "Linux_meminfo_memused", + "Linux_meminfo_bufferram", +}; + +static const char * const proc_names[] = { + "Linux_proc_statm_size", + "Linux_proc_statm_share", + "Linux_proc_statm_text", + "Linux_proc_statm_data", +}; + +static bool meminfo_global_enabled; +static ulong meminfo_enabled[MEMINFO_TOTAL]; +static ulong meminfo_keys[MEMINFO_TOTAL]; +static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)]; +static int meminfo_length = 0; +static bool new_data_avail; + +static bool proc_global_enabled; +static ulong proc_enabled[PROC_COUNT]; +static ulong proc_keys[PROC_COUNT]; +static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]); + +static int gator_meminfo_func(void *data); +static bool gator_meminfo_run; +// Initialize semaphore unlocked to initialize memory values +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static DECLARE_MUTEX(gator_meminfo_sem); +#else +static DEFINE_SEMAPHORE(gator_meminfo_sem); +#endif + +#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 +{ + up(&gator_meminfo_sem); +} + +#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 +{ + up(&gator_meminfo_sem); +} + +GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) +{ + up(&gator_meminfo_sem); +} + +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++) { + dir = gatorfs_mkdir(sb, root, meminfo_names[i]); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]); + } + + for (i = 0; i < PROC_COUNT; ++i) { + dir = gatorfs_mkdir(sb, root, proc_names[i]); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]); + } + + return 0; +} + +static int gator_events_meminfo_start(void) +{ + int i; + + new_data_avail = false; + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + meminfo_global_enabled = 1; + break; + } + } + + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + proc_global_enabled = 1; + break; + } + } + if (meminfo_enabled[MEMINFO_MEMUSED]) { + proc_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; + + // Start worker thread + gator_meminfo_run = true; + // Since the mutex starts unlocked, memory values will be initialized + if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo"))) + goto kthread_run_exit; + + return 0; + +kthread_run_exit: + GATOR_UNREGISTER_TRACE(mm_page_alloc); +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) +{ + 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); + + // Stop worker thread + gator_meminfo_run = false; + up(&gator_meminfo_sem); + } +} + +// Must be run in process context as the kernel function si_meminfo() can sleep +static int gator_meminfo_func(void *data) +{ + struct sysinfo info; + int i, len; + unsigned long long value; + + for (;;) { + if (down_killable(&gator_meminfo_sem)) { + break; + } + + // Eat up any pending events + while (!down_trylock(&gator_meminfo_sem)); + + if (!gator_meminfo_run) { + break; + } + + 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: + // pid -1 means system wide + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = -1; + // Emit value + meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED]; + meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE; + // Clear pid + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = 0; + continue; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = meminfo_keys[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; + } + + return 0; +} + +static int gator_events_meminfo_read(long long **buffer) +{ + if (!on_primary_core() || !meminfo_global_enabled) + return 0; + + if (!new_data_avail) + return 0; + + new_data_avail = false; + + if (buffer) + *buffer = meminfo_buffer; + + return meminfo_length; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + +static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member) +{ +#ifdef SPLIT_RSS_COUNTING + long val = atomic_long_read(&mm->rss_stat.count[member]); + if (val < 0) + val = 0; + return (unsigned long)val; +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) + return mm->rss_stat.count[member]; +#else + return atomic_long_read(&mm->rss_stat.count[member]); +#endif +#endif +} + +#define get_mm_counter(mm, member) gator_get_mm_counter(mm, member) + +#endif + +static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task) +{ + struct mm_struct *mm; + u64 share = 0; + int i; + long long value; + int len = 0; + int cpu = get_physical_cpu(); + long long *buf = per_cpu(proc_buffer, cpu); + + if (!proc_global_enabled) { + return 0; + } + + // Collect the memory stats of the process instead of the thread + if (task->group_leader != NULL) { + task = task->group_leader; + } + + // get_task_mm/mmput is not needed in this context because the task and it's mm are required as part of the sched_switch + mm = task->mm; + if (mm == NULL) { + return 0; + } + + // Derived from task_statm in fs/proc/task_mmu.c + if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) { + share = get_mm_counter(mm, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + file_rss +#else + MM_FILEPAGES +#endif + ); + } + + // key of 1 indicates a pid + buf[len++] = 1; + buf[len++] = task->pid; + + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + switch (i) { + case PROC_SIZE: + value = mm->total_vm; + break; + case PROC_SHARE: + value = share; + break; + case PROC_TEXT: + value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; + break; + case PROC_DATA: + value = mm->total_vm - mm->shared_vm; + break; + } + + buf[len++] = proc_keys[i]; + buf[len++] = value * PAGE_SIZE; + } + } + + if (meminfo_enabled[MEMINFO_MEMUSED]) { + value = share + get_mm_counter(mm, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + anon_rss +#else + MM_ANONPAGES +#endif + ); + // Send resident for this pid + buf[len++] = meminfo_keys[MEMINFO_MEMUSED]; + buf[len++] = value * PAGE_SIZE; + } + + // Clear pid + buf[len++] = 1; + buf[len++] = 0; + + if (buffer) + *buffer = buf; + + return len; +} + +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, + .read_proc = gator_events_meminfo_read_proc, +}; + +int gator_events_meminfo_init(void) +{ + int i; + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + meminfo_keys[i] = gator_events_get_key(); + } + + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + proc_enabled[i] = 0; + proc_keys[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_meminfo_interface); +} diff --git a/drivers/gator/gator_events_mmapped.c b/drivers/gator/gator_events_mmapped.c new file mode 100644 index 000000000000..5bc01c42c3a2 --- /dev/null +++ b/drivers/gator/gator_events_mmapped.c @@ -0,0 +1,209 @@ +/* + * Example events provider + * + * Copyright (C) ARM Limited 2010-2014. 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: + * + * + * + * + * + * + * + * When adding custom events, be sure to do the following: + * - add any needed .c files to the gator driver Makefile + * - call gator_events_install in the events init function + * - add the init function to GATOR_EVENTS_LIST in gator_main.c + * - add a new events-*.xml file to the gator daemon and rebuild + * + * Troubleshooting: + * - verify the new events are part of events.xml, which is created when building the daemon + * - verify the new events exist at /dev/gator/events/ once gatord is launched + * - verify the counter name in the XML matches the name at /dev/gator/events + */ + +#include +#include +#include + +#include "gator.h" + +#define MMAPPED_COUNTERS_NUM 3 + +static int mmapped_global_enabled; + +static struct { + unsigned long enabled; + unsigned long key; +} mmapped_counters[MMAPPED_COUNTERS_NUM]; + +static int mmapped_buffer[MMAPPED_COUNTERS_NUM * 2]; + +static s64 prev_time; + +/* Adds mmapped_cntX directories and enabled, event, and key files to /dev/gator/events */ +static int gator_events_mmapped_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "mmapped_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &mmapped_counters[i].enabled); + gatorfs_create_ro_ulong(sb, dir, "key", + &mmapped_counters[i].key); + } + + return 0; +} + +static int gator_events_mmapped_start(void) +{ + int i; + struct timespec ts; + + getnstimeofday(&ts); + prev_time = timespec_to_ns(&ts); + + mmapped_global_enabled = 0; + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_global_enabled = 1; + break; + } + } + + return 0; +} + +static void gator_events_mmapped_stop(void) +{ +} + +/* This function "simulates" counters, generating values of fancy + * functions like sine or triangle... */ +static int mmapped_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 dc, x, t = 0; + + 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; +} + +static int gator_events_mmapped_read(int **buffer) +{ + int i; + int len = 0; + int delta_in_us; + struct timespec ts; + s64 time; + + /* System wide counters - read from one core only */ + if (!on_primary_core() || !mmapped_global_enabled) + return 0; + + getnstimeofday(&ts); + time = timespec_to_ns(&ts); + delta_in_us = (int)(time - prev_time) / 1000; + prev_time = time; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_buffer[len++] = mmapped_counters[i].key; + mmapped_buffer[len++] = + mmapped_simulate(i, delta_in_us); + } + } + + if (buffer) + *buffer = mmapped_buffer; + + return len; +} + +static struct gator_interface gator_events_mmapped_interface = { + .create_files = gator_events_mmapped_create_files, + .start = gator_events_mmapped_start, + .stop = gator_events_mmapped_stop, + .read = gator_events_mmapped_read, +}; + +/* Must not be static! */ +int __init gator_events_mmapped_init(void) +{ + int i; + + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + mmapped_counters[i].enabled = 0; + mmapped_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_mmapped_interface); +} diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c new file mode 100644 index 000000000000..11c10e375511 --- /dev/null +++ b/drivers/gator/gator_events_net.c @@ -0,0 +1,172 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 struct timer_list net_wake_up_timer; + +// Must be run in process context as the kernel function dev_get_stats() can sleep +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 net_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&wq_get_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) +{ + // Network counters are not currently supported in RT-Preempt full because mod_timer is used +#ifndef CONFIG_PREEMPT_RT_FULL + 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); +#endif + + return 0; +} + +static int gator_events_net_start(void) +{ + get_network_stats(0); + netPrev[NETRX] = rx_total; + netPrev[NETTX] = tx_total; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + setup_timer(&net_wake_up_timer, net_wake_up_handler, 0); +#else + setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0); +#endif + return 0; +} + +static void gator_events_net_stop(void) +{ + del_timer_sync(&net_wake_up_timer); + 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 (!on_primary_core()) + return 0; + + if (!netrx_enabled && !nettx_enabled) + return 0; + + mod_timer(&net_wake_up_timer, jiffies + 1); + + 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); +} diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c new file mode 100644 index 000000000000..06bbad5b10c3 --- /dev/null +++ b/drivers/gator/gator_events_perf_pmu.c @@ -0,0 +1,587 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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_armvX.c is used for Linux 2.6.x +#if GATOR_PERF_PMU_SUPPORT + +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include + +extern bool event_based_sampling; + +// Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE +#define CNTMAX 16 +#define CCI_400 4 +// Maximum number of uncore counters +// + 1 for the cci-400 cycles counter +#define UCCNT (CCI_400 + 1) + +// Default to 0 if unable to probe the revision which was the previous behavior +#define DEFAULT_CCI_REVISION 0 + +// A gator_attr is needed for every counter +struct gator_attr { + // Set once in gator_events_perf_pmu_*_init - the name of the event in the gatorfs + char name[40]; + // Exposed in gatorfs - set by gatord to enable this counter + unsigned long enabled; + // Set once in gator_events_perf_pmu_*_init - the perf type to use, see perf_type_id in the perf_event.h header file. + unsigned long type; + // Exposed in gatorfs - set by gatord to select the event to collect + unsigned long event; + // Exposed in gatorfs - set by gatord with the sample period to use and enable EBS for this counter + unsigned long count; + // Exposed as read only in gatorfs - set once in __attr_init as the key to use in the APC data + unsigned long key; +}; + +// Per-core counter attributes +static struct gator_attr attrs[CNTMAX]; +// Number of initialized per-core counters +static int attr_count; +// Uncore counter attributes +static struct gator_attr uc_attrs[UCCNT]; +// Number of initialized uncore counters +static int uc_attr_count; + +struct gator_event { + int curr; + int prev; + int prev_delta; + bool zero; + struct perf_event *pevent; + struct perf_event_attr *pevent_attr; +}; + +static DEFINE_PER_CPU(struct gator_event[CNTMAX], events); +static struct gator_event uc_events[UCCNT]; +static DEFINE_PER_CPU(int[(CNTMAX + UCCNT)*2], perf_cnt); + +static void gator_events_perf_pmu_stop(void); + +static int __create_files(struct super_block *sb, struct dentry *root, struct gator_attr *const attr) +{ + struct dentry *dir; + + if (attr->name[0] == '\0') { + return 0; + } + dir = gatorfs_mkdir(sb, root, attr->name); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &attr->enabled); + gatorfs_create_ulong(sb, dir, "count", &attr->count); + gatorfs_create_ro_ulong(sb, dir, "key", &attr->key); + gatorfs_create_ulong(sb, dir, "event", &attr->event); + + return 0; +} + +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +{ + int cnt; + + for (cnt = 0; cnt < attr_count; cnt++) { + if (__create_files(sb, root, &attrs[cnt]) != 0) { + return -1; + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__create_files(sb, root, &uc_attrs[cnt]) != 0) { + return -1; + } + } + + return 0; +} + +#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 +{ + gator_backtrace_handler(regs); +} + +#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_read(int **buffer); + +static int gator_events_perf_pmu_online(int **buffer, bool migrate) +{ + return gator_events_perf_pmu_read(buffer); +} + +static void __online_dispatch(int cpu, bool migrate, struct gator_attr *const attr, struct gator_event *const event) +{ + perf_overflow_handler_t handler; + + event->zero = true; + + if (event->pevent != NULL || event->pevent_attr == 0 || migrate) { + return; + } + + if (attr->count > 0) { + handler = ebs_overflow_handler; + } else { + handler = dummy_handler; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler); +#else + event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0); +#endif + if (IS_ERR(event->pevent)) { + pr_debug("gator: unable to online a counter on cpu %d\n", cpu); + event->pevent = NULL; + return; + } + + if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) { + pr_debug("gator: inactive counter on cpu %d\n", cpu); + perf_event_release_kernel(event->pevent); + event->pevent = NULL; + return; + } +} + +static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate) +{ + int cnt; + + cpu = pcpu_to_lcpu(cpu); + + for (cnt = 0; cnt < attr_count; cnt++) { + __online_dispatch(cpu, migrate, &attrs[cnt], &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __online_dispatch(cpu, migrate, &uc_attrs[cnt], &uc_events[cnt]); + } + } +} + +static void __offline_dispatch(int cpu, struct gator_event *const event) +{ + struct perf_event *pe = NULL; + + if (event->pevent) { + pe = event->pevent; + event->pevent = NULL; + } + + if (pe) { + perf_event_release_kernel(pe); + } +} + +static void gator_events_perf_pmu_offline_dispatch(int cpu, bool migrate) +{ + int cnt; + + if (migrate) { + return; + } + cpu = pcpu_to_lcpu(cpu); + + for (cnt = 0; cnt < attr_count; cnt++) { + __offline_dispatch(cpu, &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __offline_dispatch(cpu, &uc_events[cnt]); + } + } +} + +static int __check_ebs(struct gator_attr *const attr) +{ + if (attr->count > 0) { + if (!event_based_sampling) { + event_based_sampling = true; + } else { + printk(KERN_WARNING "gator: Only one ebs counter is allowed\n"); + return -1; + } + } + + return 0; +} + +static int __start(struct gator_attr *const attr, struct gator_event *const event) +{ + u32 size = sizeof(struct perf_event_attr); + + event->pevent = NULL; + if (!attr->enabled) { // Skip disabled counters + return 0; + } + + event->prev = 0; + event->curr = 0; + event->prev_delta = 0; + event->pevent_attr = kmalloc(size, GFP_KERNEL); + if (!event->pevent_attr) { + gator_events_perf_pmu_stop(); + return -1; + } + + memset(event->pevent_attr, 0, size); + event->pevent_attr->type = attr->type; + event->pevent_attr->size = size; + event->pevent_attr->config = attr->event; + event->pevent_attr->sample_period = attr->count; + event->pevent_attr->pinned = 1; + + return 0; +} + +static int gator_events_perf_pmu_start(void) +{ + int cnt, cpu; + + event_based_sampling = false; + for (cnt = 0; cnt < attr_count; cnt++) { + if (__check_ebs(&attrs[cnt]) != 0) { + return -1; + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__check_ebs(&uc_attrs[cnt]) != 0) { + return -1; + } + } + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < attr_count; cnt++) { + if (__start(&attrs[cnt], &per_cpu(events, cpu)[cnt]) != 0) { + return -1; + } + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__start(&uc_attrs[cnt], &uc_events[cnt]) != 0) { + return -1; + } + } + + return 0; +} + +static void __event_stop(struct gator_event *const event) +{ + if (event->pevent_attr) { + kfree(event->pevent_attr); + event->pevent_attr = NULL; + } +} + +static void __attr_stop(struct gator_attr *const attr) +{ + attr->enabled = 0; + attr->event = 0; + attr->count = 0; +} + +static void gator_events_perf_pmu_stop(void) +{ + unsigned int cnt, cpu; + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < attr_count; cnt++) { + __event_stop(&per_cpu(events, cpu)[cnt]); + } + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __event_stop(&uc_events[cnt]); + } + + for (cnt = 0; cnt < attr_count; cnt++) { + __attr_stop(&attrs[cnt]); + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __attr_stop(&uc_attrs[cnt]); + } +} + +static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event) +{ + int delta; + + struct perf_event *const ev = event->pevent; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + /* After creating the perf counter in __online_dispatch, there + * is a race condition between gator_events_perf_pmu_online and + * gator_events_perf_pmu_read. So have + * gator_events_perf_pmu_online call gator_events_perf_pmu_read + * and in __read check to see if it's the first call after + * __online_dispatch and if so, run the online code. + */ + if (event->zero) { + ev->pmu->read(ev); + event->prev = event->curr = local64_read(&ev->count); + event->prev_delta = 0; + per_cpu(perf_cnt, cpu)[(*len)++] = attr->key; + per_cpu(perf_cnt, cpu)[(*len)++] = 0; + event->zero = false; + } else { + ev->pmu->read(ev); + event->curr = local64_read(&ev->count); + delta = event->curr - event->prev; + if (delta != 0 || delta != event->prev_delta) { + event->prev_delta = delta; + event->prev = event->curr; + per_cpu(perf_cnt, cpu)[(*len)++] = attr->key; + if (delta < 0) { + delta *= -1; + } + per_cpu(perf_cnt, cpu)[(*len)++] = delta; + } + } + } +} + +static int gator_events_perf_pmu_read(int **buffer) +{ + int cnt, len = 0; + const int cpu = get_logical_cpu(); + + for (cnt = 0; cnt < attr_count; cnt++) { + __read(&len, cpu, &attrs[cnt], &per_cpu(events, cpu)[cnt]); + } + + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __read(&len, cpu, &uc_attrs[cnt], &uc_events[cnt]); + } + } + + if (buffer) { + *buffer = per_cpu(perf_cnt, 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, +}; + +static void __attr_init(struct gator_attr *const attr) +{ + attr->name[0] = '\0'; + attr->enabled = 0; + attr->type = 0; + attr->event = 0; + attr->count = 0; + attr->key = gator_events_get_key(); +} + +#ifdef CONFIG_OF + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400" }, + {}, +}; + +static int probe_cci_revision(void) +{ + struct device_node *np; + struct resource res; + void __iomem *cci_ctrl_base; + int rev; + int ret = DEFAULT_CCI_REVISION; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) { + return ret; + } + + if (of_address_to_resource(np, 0, &res)) { + goto node_put; + } + + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + + rev = (readl_relaxed(cci_ctrl_base + 0xfe8) >> 4) & 0xf; + + if (rev <= 4) { + ret = 0; + } else if (rev <= 6) { + ret = 1; + } + + iounmap(cci_ctrl_base); + + node_put: + of_node_put(np); + + return ret; +} + +#else + +static int probe_cci_revision(void) +{ + return DEFAULT_CCI_REVISION; +} + +#endif + +static void gator_events_perf_pmu_cci_init(const int type) +{ + int cnt; + const char *cci_name; + + switch (probe_cci_revision()) { + case 0: + cci_name = "CCI_400"; + break; + case 1: + cci_name = "CCI_400-r1"; + break; + default: + pr_debug("gator: unrecognized cci-400 revision\n"); + return; + } + + snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", cci_name); + uc_attrs[uc_attr_count].type = type; + ++uc_attr_count; + + for (cnt = 0; cnt < CCI_400; ++cnt, ++uc_attr_count) { + struct gator_attr *const attr = &uc_attrs[uc_attr_count]; + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", cci_name, cnt); + attr->type = type; + } +} + +static void gator_events_perf_pmu_cpu_init(const struct gator_cpu *const gator_cpu, const int type) +{ + int cnt; + + snprintf(attrs[attr_count].name, sizeof(attrs[attr_count].name), "%s_ccnt", gator_cpu->pmnc_name); + attrs[attr_count].type = type; + ++attr_count; + + for (cnt = 0; cnt < gator_cpu->pmnc_counters; ++cnt, ++attr_count) { + struct gator_attr *const attr = &attrs[attr_count]; + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", gator_cpu->pmnc_name, cnt); + attr->type = type; + } +} + +int gator_events_perf_pmu_init(void) +{ + struct perf_event_attr pea; + struct perf_event *pe; + const struct gator_cpu *gator_cpu; + int type; + int cpu; + int cnt; + bool found_cpu = false; + + for (cnt = 0; cnt < CNTMAX; cnt++) { + __attr_init(&attrs[cnt]); + } + for (cnt = 0; cnt < UCCNT; cnt++) { + __attr_init(&uc_attrs[cnt]); + } + + memset(&pea, 0, sizeof(pea)); + pea.size = sizeof(pea); + pea.config = 0xFF; + attr_count = 0; + uc_attr_count = 0; + for (type = PERF_TYPE_MAX; type < 0x20; ++type) { + pea.type = type; + + // A particular PMU may work on some but not all cores, so try on each core + pe = NULL; + for_each_present_cpu(cpu) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler); +#else + pe = perf_event_create_kernel_counter(&pea, cpu, 0, dummy_handler, 0); +#endif + if (!IS_ERR(pe)) { + break; + } + } + // Assume that valid PMUs are contiguous + if (IS_ERR(pe)) { + break; + } + + if (pe->pmu != NULL && type == pe->pmu->type) { + if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400-r1", pe->pmu->name) == 0) { + gator_events_perf_pmu_cci_init(type); + } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) { + found_cpu = true; + gator_events_perf_pmu_cpu_init(gator_cpu, type); + } + // Initialize gator_attrs for dynamic PMUs here + } + + perf_event_release_kernel(pe); + } + + if (!found_cpu) { + const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(gator_cpuid()); + if (gator_cpu == NULL) { + return -1; + } + gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW); + } + + // Initialize gator_attrs for non-dynamic PMUs here + + if (attr_count > CNTMAX) { + printk(KERN_ERR "gator: Too many perf counters\n"); + return -1; + } + + if (uc_attr_count > UCCNT) { + printk(KERN_ERR "gator: Too many perf uncore counters\n"); + return -1; + } + + return gator_events_install(&gator_events_perf_pmu_interface); +} + +#endif diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c new file mode 100644 index 000000000000..9e3915830182 --- /dev/null +++ b/drivers/gator/gator_events_sched.c @@ -0,0 +1,113 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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, get_physical_cpu())[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 = get_physical_cpu(); + + 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); +} diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c new file mode 100644 index 000000000000..2e5be8d50e9d --- /dev/null +++ b/drivers/gator/gator_events_scorpion.c @@ -0,0 +1,669 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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, bool migrate) +{ + 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]) { + if (cnt == CCNT) { + scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + scorpion_cntn_read(); + } + 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, bool migrate) +{ + 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); +} + +#endif diff --git a/drivers/gator/gator_events_threads.c b/drivers/gator/gator_events_threads.c new file mode 100644 index 000000000000..9de85862fe6c --- /dev/null +++ b/drivers/gator/gator_events_threads.c @@ -0,0 +1,115 @@ +/* + * Sample activity provider + * + * Copyright (C) ARM Limited 2014. 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. + * + * See gator_events_mmapped.c for additional directions and + * troubleshooting. + * + * For this sample to work these entries must be present in the + * events.xml file. So create an events-threads.xml in the gator + * daemon source directory with the following contents and rebuild + * gatord: + * + * + * + * + */ + +#include + +#include "gator.h" + +static ulong threads_enabled; +static ulong threads_key; +static ulong threads_cores; + +#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 +{ + int cpu = get_physical_cpu(); + int pid = next->pid; + if (pid == 0) { + // idle + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } else if (pid & 1) { + // odd + gator_marshal_activity_switch(cpu, threads_key, 1, pid); + } else { + // even + //gator_marshal_activity_switch(cpu, threads_key, 2, current->pid); + // Multiple activities are not yet supported so emit idle + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } +} + +// Adds Linux_threads directory and enabled, key, and cores files to /dev/gator/events +static int gator_events_threads_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_threads"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &threads_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &threads_key); + // Number of cores associated with this activity + gatorfs_create_ro_ulong(sb, dir, "cores", &threads_cores); + + return 0; +} + +static int gator_events_threads_start(void) +{ + int cpu; + + if (threads_enabled) { + preempt_disable(); + for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { + gator_marshal_activity_switch(cpu, threads_key, 0, 0); + } + preempt_enable(); + + if (GATOR_REGISTER_TRACE(sched_switch)) { + goto fail_sched_switch; + } + } + + return 0; + +fail_sched_switch: + return -1; +} + +static void gator_events_threads_stop(void) +{ + if (threads_enabled) { + GATOR_UNREGISTER_TRACE(sched_switch); + } + + threads_enabled = 0; +} + +static struct gator_interface gator_events_threads_interface = { + .create_files = gator_events_threads_create_files, + .start = gator_events_threads_start, + .stop = gator_events_threads_stop, +}; + +// Must not be static. Ensure that this init function is added to GATOR_EVENTS_LIST in gator_main.c +int __init gator_events_threads_init(void) +{ + threads_enabled = 0; + threads_key = gator_events_get_key(); + threads_cores = nr_cpu_ids; + + return gator_events_install(&gator_events_threads_interface); +} diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c new file mode 100644 index 000000000000..166cfe7d681d --- /dev/null +++ b/drivers/gator/gator_fs.c @@ -0,0 +1,365 @@ +/** + * @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, +}; + +static 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); +} + +static ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); +} + +static 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 int gatorfs_u64_from_user(u64 *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_strtoull(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 u64_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + u64 *val = file->private_data; + return gatorfs_u64_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 ssize_t u64_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + u64 *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_u64_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 u64_fops = { + .read = u64_read_file, + .write = u64_write_file, + .open = default_open, +}; + +static const struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + +static const struct file_operations u64_ro_fops = { + .read = u64_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; +} + +static int gatorfs_create_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &u64_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 int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 * val) +{ + struct dentry *d = + __gatorfs_create_file(sb, root, name, &u64_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, +}; + +static 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; +} + +static 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); + + 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, +}; + +static int __init gatorfs_register(void) +{ + return register_filesystem(&gatorfs_type); +} + +static 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 100644 index 000000000000..76584554b00f --- /dev/null +++ b/drivers/gator/gator_hrtimer_gator.c @@ -0,0 +1,80 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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. + * + */ + +void (*callback)(void); +DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +DEFINE_PER_CPU(ktime_t, hrtimer_expire); +DEFINE_PER_CPU(int, hrtimer_is_active); +static ktime_t profiling_interval; +static void gator_hrtimer_online(void); +static void gator_hrtimer_offline(void); + +static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) +{ + int cpu = get_logical_cpu(); + hrtimer_forward(hrtimer, per_cpu(hrtimer_expire, cpu), profiling_interval); + per_cpu(hrtimer_expire, cpu) = ktime_add(per_cpu(hrtimer_expire, cpu), profiling_interval); + (*callback)(); + return HRTIMER_RESTART; +} + +static void gator_hrtimer_online(void) +{ + int cpu = get_logical_cpu(); + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + 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_ABS); + hrtimer->function = gator_hrtimer_notify; +#ifdef CONFIG_PREEMPT_RT_BASE + hrtimer->irqsafe = 1; +#endif + per_cpu(hrtimer_expire, cpu) = ktime_add(hrtimer->base->get_time(), profiling_interval); + hrtimer_start(hrtimer, per_cpu(hrtimer_expire, cpu), HRTIMER_MODE_ABS_PINNED); +} + +static void gator_hrtimer_offline(void) +{ + int cpu = get_logical_cpu(); + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + + 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 */ +} diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c new file mode 100644 index 000000000000..9180b874457a --- /dev/null +++ b/drivers/gator/gator_iks.c @@ -0,0 +1,197 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 GATOR_IKS_SUPPORT + +#include +#include +#include +#include + +static bool map_cpuids; +static int mpidr_cpuids[NR_CPUS]; +static const struct gator_cpu * mpidr_cpus[NR_CPUS]; +static int __lcpu_to_pcpu[NR_CPUS]; + +static const struct gator_cpu *gator_find_cpu_by_dt_name(const char *const name) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->dt_name != NULL && strcmp(gator_cpu->dt_name, name) == 0) { + return gator_cpu; + } + } + + return NULL; +} + +static void calc_first_cluster_size(void) +{ + int len; + const u32 *val; + const char *compatible; + struct device_node *cn = NULL; + int mpidr_cpuids_count = 0; + + // Zero is a valid cpuid, so initialize the array to 0xff's + memset(&mpidr_cpuids, 0xff, sizeof(mpidr_cpuids)); + memset(&mpidr_cpus, 0, sizeof(mpidr_cpus)); + + while ((cn = of_find_node_by_type(cn, "cpu"))) { + BUG_ON(mpidr_cpuids_count >= NR_CPUS); + + val = of_get_property(cn, "reg", &len); + if (!val || len != 4) { + pr_err("%s missing reg property\n", cn->full_name); + continue; + } + compatible = of_get_property(cn, "compatible", NULL); + if (compatible == NULL) { + pr_err("%s missing compatible property\n", cn->full_name); + continue; + } + + mpidr_cpuids[mpidr_cpuids_count] = be32_to_cpup(val); + mpidr_cpus[mpidr_cpuids_count] = gator_find_cpu_by_dt_name(compatible); + ++mpidr_cpuids_count; + } + + map_cpuids = (mpidr_cpuids_count == nr_cpu_ids); +} + +static int linearize_mpidr(int mpidr) +{ + int i; + for (i = 0; i < nr_cpu_ids; ++i) { + if (mpidr_cpuids[i] == mpidr) { + return i; + } + } + + BUG(); +} + +int lcpu_to_pcpu(const int lcpu) +{ + int pcpu; + + if (!map_cpuids) + return lcpu; + + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + pcpu = __lcpu_to_pcpu[lcpu]; + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + return pcpu; +} + +int pcpu_to_lcpu(const int pcpu) +{ + int lcpu; + + if (!map_cpuids) + return pcpu; + + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + for (lcpu = 0; lcpu < nr_cpu_ids; ++lcpu) { + if (__lcpu_to_pcpu[lcpu] == pcpu) { + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + return lcpu; + } + } + BUG(); +} + +static void gator_update_cpu_mapping(u32 cpu_hwid) +{ + int lcpu = smp_processor_id(); + int pcpu = linearize_mpidr(cpu_hwid & MPIDR_HWID_BITMASK); + BUG_ON(lcpu >= nr_cpu_ids || lcpu < 0); + BUG_ON(pcpu >= nr_cpu_ids || pcpu < 0); + __lcpu_to_pcpu[lcpu] = pcpu; +} + +GATOR_DEFINE_PROBE(cpu_migrate_begin, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + const int cpu = get_physical_cpu(); + + gator_timer_offline((void *)1); + gator_timer_offline_dispatch(cpu, true); +} + +GATOR_DEFINE_PROBE(cpu_migrate_finish, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + int cpu; + + gator_update_cpu_mapping(cpu_hwid); + + // get_physical_cpu must be called after gator_update_cpu_mapping + cpu = get_physical_cpu(); + gator_timer_online_dispatch(cpu, true); + gator_timer_online((void *)1); +} + +GATOR_DEFINE_PROBE(cpu_migrate_current, TP_PROTO(u64 timestamp, u32 cpu_hwid)) +{ + gator_update_cpu_mapping(cpu_hwid); +} + +static void gator_send_iks_core_names(void) +{ + int cpu; + // Send the cpu names + preempt_disable(); + for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { + if (mpidr_cpus[cpu] != NULL) { + gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid); + } + } + preempt_enable(); +} + +static int gator_migrate_start(void) +{ + int retval = 0; + + if (!map_cpuids) + return retval; + + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_begin); + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_finish); + if (retval == 0) + retval = GATOR_REGISTER_TRACE(cpu_migrate_current); + if (retval == 0) { + // Initialize the logical to physical cpu mapping + memset(&__lcpu_to_pcpu, 0xff, sizeof(__lcpu_to_pcpu)); + bL_switcher_trace_trigger(); + } + return retval; +} + +static void gator_migrate_stop(void) +{ + if (!map_cpuids) + return; + + GATOR_UNREGISTER_TRACE(cpu_migrate_current); + GATOR_UNREGISTER_TRACE(cpu_migrate_finish); + GATOR_UNREGISTER_TRACE(cpu_migrate_begin); +} + +#else + +#define calc_first_cluster_size() +#define gator_send_iks_core_names() +#define gator_migrate_start() 0 +#define gator_migrate_stop() + +#endif diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c new file mode 100644 index 000000000000..0d867f22364f --- /dev/null +++ b/drivers/gator/gator_main.c @@ -0,0 +1,1460 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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. + * + */ + +// This version must match the gator daemon version +#define PROTOCOL_VERSION 19 +static unsigned long gator_protocol_version = PROTOCOL_VERSION; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gator.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#error kernels prior to 2.6.32 are not supported +#endif + +#if defined(MODULE) && !defined(CONFIG_MODULES) +#error Cannot build a module against a kernel that does not support modules. To resolve, either rebuild the kernel to support modules or build gator as part of the kernel. +#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 LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && 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 SUMMARY_BUFFER_SIZE (1*1024) +#define BACKTRACE_BUFFER_SIZE (128*1024) +#define NAME_BUFFER_SIZE (64*1024) +#define COUNTER_BUFFER_SIZE (64*1024) // counters have the core as part of the data and the core value in the frame header may be discarded +#define BLOCK_COUNTER_BUFFER_SIZE (128*1024) +#define ANNOTATE_BUFFER_SIZE (128*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 IDLE_BUFFER_SIZE (32*1024) // idle counters have the core as part of the data and the core value in the frame header may be discarded +#define ACTIVITY_BUFFER_SIZE (128*1024) + +#define NO_COOKIE 0U +#define UNRESOLVED_COOKIE ~0U + +#define FRAME_SUMMARY 1 +#define FRAME_BACKTRACE 2 +#define FRAME_NAME 3 +#define FRAME_COUNTER 4 +#define FRAME_BLOCK_COUNTER 5 +#define FRAME_ANNOTATE 6 +#define FRAME_SCHED_TRACE 7 +#define FRAME_IDLE 9 +#define FRAME_ACTIVITY 13 + +#define MESSAGE_END_BACKTRACE 1 + +// Name Frame Messages +#define MESSAGE_COOKIE 1 +#define MESSAGE_THREAD_NAME 2 +#define MESSAGE_LINK 4 + +// Scheduler Trace Frame Messages +#define MESSAGE_SCHED_SWITCH 1 +#define MESSAGE_SCHED_EXIT 2 + +// Idle Frame Messages +#define MESSAGE_IDLE_ENTER 1 +#define MESSAGE_IDLE_EXIT 2 + +// Summary Frame Messages +#define MESSAGE_SUMMARY 1 +#define MESSAGE_CORE_NAME 3 + +// Activity Frame Messages +#define MESSAGE_SWITCH 2 +#define MESSAGE_EXIT 3 + +#define MAXSIZE_PACK32 5 +#define MAXSIZE_PACK64 10 + +#define FRAME_HEADER_SIZE 3 + +#if defined(__arm__) +#define PC_REG regs->ARM_pc +#elif defined(__aarch64__) +#define PC_REG regs->pc +#else +#define PC_REG regs->ip +#endif + +enum { + SUMMARY_BUF, + BACKTRACE_BUF, + NAME_BUF, + COUNTER_BUF, + BLOCK_COUNTER_BUF, + ANNOTATE_BUF, + SCHED_TRACE_BUF, + IDLE_BUF, + ACTIVITY_BUF, + NUM_GATOR_BUFS +}; + +/****************************************************************************** + * Globals + ******************************************************************************/ +static unsigned long gator_cpu_cores; +// Size of the largest buffer. Effectively constant, set in gator_op_create_files +static unsigned long userspace_buffer_size; +static unsigned long gator_backtrace_depth; +// How often to commit the buffers for live in nanoseconds +static u64 gator_live_rate; + +static unsigned long gator_started; +static u64 gator_monotonic_started; +static u64 gator_hibernate_time; +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 DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); +static struct timer_list gator_buffer_wake_up_timer; +static bool gator_buffer_wake_run; +// Initialize semaphore unlocked to initialize memory values +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static DECLARE_MUTEX(gator_buffer_wake_sem); +#else +static DEFINE_SEMAPHORE(gator_buffer_wake_sem); +#endif +static struct task_struct *gator_buffer_wake_thread; +static LIST_HEAD(gator_events); + +static DEFINE_PER_CPU(u64, last_timestamp); + +static bool printed_monotonic_warning; + +static u32 gator_cpuids[NR_CPUS]; +static bool sent_core_name[NR_CPUS]; + +static DEFINE_PER_CPU(bool, in_scheduler_context); + +/****************************************************************************** + * Prototypes + ******************************************************************************/ +static u64 gator_get_time(void); +static void gator_op_create_files(struct super_block *sb, struct dentry *root); + +// gator_buffer is protected by being per_cpu and by having IRQs disabled when writing to it. +// Most marshal_* calls take care of this except for marshal_cookie*, marshal_backtrace* and marshal_frame where the caller is responsible for doing so. +// No synchronization is needed with the backtrace buffer as it is per cpu and is only used from the hrtimer. +// The annotate_lock must be held when using the annotation buffer as it is not per cpu. +// collect_counters which is the sole writer to the block counter frame is additionally protected by the per cpu collecting flag + +// Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup. +static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; +// gator_buffer_size - 1, bitwise and with pos to get offset into the array. Effectively constant, set in gator_op_setup. +static uint32_t gator_buffer_mask[NUM_GATOR_BUFS]; +// Read position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are read by userspace in userspace_buffer_read +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read); +// Write position in the buffer. Initialized to zero in gator_op_setup and incremented after bytes are written to the buffer +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); +// Commit position in the buffer. Initialized to zero in gator_op_setup and incremented after a frame is ready to be read by userspace +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); +// If set to false, decreases the number of bytes returned by buffer_bytes_available. Set in buffer_check_space if no space is remaining. Initialized to true in gator_op_setup +// This means that if we run out of space, continue to report that no space is available until bytes are read by userspace +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); +// The buffer. Allocated in gator_op_setup +static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); +// The time after which the buffer should be committed for live display +static DEFINE_PER_CPU(u64, gator_buffer_commit_time); + +// List of all gator events - new events must be added to this list +#define GATOR_EVENTS_LIST \ + GATOR_EVENT(gator_events_armv6_init) \ + GATOR_EVENT(gator_events_armv7_init) \ + GATOR_EVENT(gator_events_block_init) \ + GATOR_EVENT(gator_events_ccn504_init) \ + GATOR_EVENT(gator_events_irq_init) \ + GATOR_EVENT(gator_events_l2c310_init) \ + GATOR_EVENT(gator_events_mali_init) \ + GATOR_EVENT(gator_events_mali_t6xx_hw_init) \ + GATOR_EVENT(gator_events_mali_t6xx_init) \ + GATOR_EVENT(gator_events_meminfo_init) \ + GATOR_EVENT(gator_events_mmapped_init) \ + GATOR_EVENT(gator_events_net_init) \ + GATOR_EVENT(gator_events_perf_pmu_init) \ + GATOR_EVENT(gator_events_sched_init) \ + GATOR_EVENT(gator_events_scorpion_init) \ + GATOR_EVENT(gator_events_threads_init) \ + +#define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void); +GATOR_EVENTS_LIST +#undef GATOR_EVENT + +static int (*gator_events_list[])(void) = { +#define GATOR_EVENT(EVENT_INIT) EVENT_INIT, +GATOR_EVENTS_LIST +#undef GATOR_EVENT +}; + +/****************************************************************************** + * Application Includes + ******************************************************************************/ +#include "gator_fs.c" +#include "gator_buffer_write.c" +#include "gator_buffer.c" +#include "gator_marshaling.c" +#include "gator_hrtimer_gator.c" +#include "gator_cookies.c" +#include "gator_annotate.c" +#include "gator_trace_sched.c" +#include "gator_trace_power.c" +#include "gator_trace_gpu.c" +#include "gator_backtrace.c" + +/****************************************************************************** + * Misc + ******************************************************************************/ + +static const struct gator_cpu gator_cpus[] = { + { + .cpuid = ARM1136, + .core_name = "ARM1136", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1136", + .pmnc_counters = 3, + }, + { + .cpuid = ARM1156, + .core_name = "ARM1156", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1156", + .pmnc_counters = 3, + }, + { + .cpuid = ARM1176, + .core_name = "ARM1176", + .pmnc_name = "ARM_ARM11", + .dt_name = "arm,arm1176", + .pmnc_counters = 3, + }, + { + .cpuid = ARM11MPCORE, + .core_name = "ARM11MPCore", + .pmnc_name = "ARM_ARM11MPCore", + .dt_name = "arm,arm11mpcore", + .pmnc_counters = 3, + }, + { + .cpuid = CORTEX_A5, + .core_name = "Cortex-A5", + .pmnc_name = "ARMv7_Cortex_A5", + .dt_name = "arm,cortex-a5", + .pmnc_counters = 2, + }, + { + .cpuid = CORTEX_A7, + .core_name = "Cortex-A7", + .pmnc_name = "ARMv7_Cortex_A7", + .dt_name = "arm,cortex-a7", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A8, + .core_name = "Cortex-A8", + .pmnc_name = "ARMv7_Cortex_A8", + .dt_name = "arm,cortex-a8", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A9, + .core_name = "Cortex-A9", + .pmnc_name = "ARMv7_Cortex_A9", + .dt_name = "arm,cortex-a9", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A12, + .core_name = "Cortex-A12", + .pmnc_name = "ARMv7_Cortex_A12", + .dt_name = "arm,cortex-a12", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A15, + .core_name = "Cortex-A15", + .pmnc_name = "ARMv7_Cortex_A15", + .dt_name = "arm,cortex-a15", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A17, + .core_name = "Cortex-A17", + .pmnc_name = "ARMv7_Cortex_A17", + .dt_name = "arm,cortex-a17", + .pmnc_counters = 6, + }, + { + .cpuid = SCORPION, + .core_name = "Scorpion", + .pmnc_name = "Scorpion", + .pmnc_counters = 4, + }, + { + .cpuid = SCORPIONMP, + .core_name = "ScorpionMP", + .pmnc_name = "ScorpionMP", + .pmnc_counters = 4, + }, + { + .cpuid = KRAITSIM, + .core_name = "KraitSIM", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = KRAIT, + .core_name = "Krait", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = KRAIT_S4_PRO, + .core_name = "Krait S4 Pro", + .pmnc_name = "Krait", + .pmnc_counters = 4, + }, + { + .cpuid = CORTEX_A53, + .core_name = "Cortex-A53", + .pmnc_name = "ARM_Cortex-A53", + .dt_name = "arm,cortex-a53", + .pmnc_counters = 6, + }, + { + .cpuid = CORTEX_A57, + .core_name = "Cortex-A57", + .pmnc_name = "ARM_Cortex-A57", + .dt_name = "arm,cortex-a57", + .pmnc_counters = 6, + }, + { + .cpuid = AARCH64, + .core_name = "AArch64", + .pmnc_name = "ARM_AArch64", + .pmnc_counters = 6, + }, + { + .cpuid = OTHER, + .core_name = "Other", + .pmnc_name = "Other", + .pmnc_counters = 6, + }, + {} +}; + +const struct gator_cpu *gator_find_cpu_by_cpuid(const u32 cpuid) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->cpuid == cpuid) { + return gator_cpu; + } + } + + return NULL; +} + +const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name) +{ + int i; + + for (i = 0; gator_cpus[i].cpuid != 0; ++i) { + const struct gator_cpu *const gator_cpu = &gator_cpus[i]; + if (gator_cpu->pmnc_name != NULL && strcmp(gator_cpu->pmnc_name, name) == 0) { + return gator_cpu; + } + } + + return NULL; +} + +u32 gator_cpuid(void) +{ +#if defined(__arm__) || defined(__aarch64__) + u32 val; +#if !defined(__aarch64__) + asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); +#else + asm volatile("mrs %0, midr_el1" : "=r" (val)); +#endif + return (val >> 4) & 0xfff; +#else + return OTHER; +#endif +} + +static void gator_buffer_wake_up(unsigned long data) +{ + wake_up(&gator_buffer_wait); +} + +static int gator_buffer_wake_func(void *data) +{ + for (;;) { + if (down_killable(&gator_buffer_wake_sem)) { + break; + } + + // Eat up any pending events + while (!down_trylock(&gator_buffer_wake_sem)); + + if (!gator_buffer_wake_run) { + break; + } + + gator_buffer_wake_up(0); + } + + return 0; +} + +/****************************************************************************** + * 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; + } + } + *cpu = -1; + *buftype = -1; + return false; +} + +/****************************************************************************** + * hrtimer interrupt processing + ******************************************************************************/ +static void gator_timer_interrupt(void) +{ + struct pt_regs *const regs = get_irq_regs(); + gator_backtrace_handler(regs); +} + +void gator_backtrace_handler(struct pt_regs *const regs) +{ + u64 time = gator_get_time(); + int cpu = get_physical_cpu(); + + // Output backtrace + gator_add_sample(cpu, regs, time); + + // Collect counters + if (!per_cpu(collecting, cpu)) { + collect_counters(time, NULL); + } + + // No buffer flushing occurs during sched switch for RT-Preempt full. The block counter frame will be flushed by collect_counters, but the sched buffer needs to be explicitly flushed +#ifdef CONFIG_PREEMPT_RT_FULL + buffer_check(cpu, SCHED_TRACE_BUF, time); +#endif +} + +static int gator_running; + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_offline(void *migrate) +{ + struct gator_interface *gi; + int i, len, cpu = get_physical_cpu(); + int *buffer; + u64 time; + + gator_trace_sched_offline(); + gator_trace_power_offline(); + + if (!migrate) { + gator_hrtimer_offline(); + } + + // Offline any events and output counters + time = gator_get_time(); + if (marshal_event_header(time)) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline) { + len = gi->offline(&buffer, migrate); + marshal_event(len, buffer); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + } + + // Flush all buffers on this core + for (i = 0; i < NUM_GATOR_BUFS; i++) + gator_commit_buffer(cpu, i, time); +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_offline_dispatch(int cpu, bool migrate) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline_dispatch) { + gi->offline_dispatch(cpu, migrate); + } + } +} + +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(lcpu_to_pcpu(cpu), false); + } + + gator_running = 0; + gator_hrtimer_shutdown(); + } +} + +static void gator_send_core_name(const int cpu, const u32 cpuid) +{ +#if defined(__arm__) || defined(__aarch64__) + if (!sent_core_name[cpu] || (cpuid != gator_cpuids[cpu])) { + const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(cpuid); + const char *core_name = NULL; + char core_name_buf[32]; + + // Save off this cpuid + gator_cpuids[cpu] = cpuid; + if (gator_cpu != NULL) { + core_name = gator_cpu->core_name; + } else { + if (cpuid == -1) { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown"); + } else { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + } + core_name = core_name_buf; + } + + marshal_core_name(cpu, cpuid, core_name); + sent_core_name[cpu] = true; + } +#endif +} + +static void gator_read_cpuid(void * arg) +{ + gator_cpuids[get_physical_cpu()] = gator_cpuid(); +} + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_online(void *migrate) +{ + struct gator_interface *gi; + int len, cpu = get_physical_cpu(); + int *buffer; + u64 time; + + // Send what is currently running on this core + marshal_sched_trace_switch(current->pid, 0); + + gator_trace_power_online(); + + // online any events and output counters + time = gator_get_time(); + if (marshal_event_header(time)) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->online) { + len = gi->online(&buffer, migrate); + marshal_event(len, buffer); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + } + + if (!migrate) { + gator_hrtimer_online(); + } + + gator_send_core_name(cpu, gator_cpuid()); +} + +// 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, bool migrate) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) { + if (gi->online_dispatch) { + gi->online_dispatch(cpu, migrate); + } + } +} + +#include "gator_iks.c" + +static 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; + + // Send off the previously saved cpuids + for_each_present_cpu(cpu) { + preempt_disable(); + gator_send_core_name(cpu, gator_cpuids[cpu]); + preempt_enable(); + } + + gator_send_iks_core_names(); + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); + } + on_each_cpu(gator_timer_online, NULL, 1); + + return 0; +} + +static u64 gator_get_time(void) +{ + struct timespec ts; + u64 timestamp; + u64 prev_timestamp; + u64 delta; + int cpu = smp_processor_id(); + + // Match clock_gettime(CLOCK_MONOTONIC_RAW, &ts) from userspace + getrawmonotonic(&ts); + timestamp = timespec_to_ns(&ts); + + // getrawmonotonic is not monotonic on all systems. Detect and attempt to correct these cases. + // up to 0.5ms delta has been seen on some systems, which can skew Streamline data when viewing at high resolution. + // This doesn't work well with interrupts, but that it's OK - the real concern is to catch big jumps in time + prev_timestamp = per_cpu(last_timestamp, cpu); + if (prev_timestamp <= timestamp) { + per_cpu(last_timestamp, cpu) = timestamp; + } else { + delta = prev_timestamp - timestamp; + // Log the error once + if (!printed_monotonic_warning && delta > 500000) { + printk(KERN_ERR "%s: getrawmonotonic is not monotonic cpu: %i delta: %lli\nSkew in Streamline data may be present at the fine zoom levels\n", __FUNCTION__, cpu, delta); + printed_monotonic_warning = true; + } + timestamp = prev_timestamp; + } + + return timestamp - gator_monotonic_started; +} + +/****************************************************************************** + * cpu hotplug and pm notifiers + ******************************************************************************/ +static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +{ + int cpu = lcpu_to_pcpu((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, false); + break; + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + gator_timer_online_dispatch(cpu, false); + 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; + struct timespec ts; + + 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(lcpu_to_pcpu(cpu), false); + } + + // Record the wallclock hibernate time + getnstimeofday(&ts); + gator_hibernate_time = timespec_to_ns(&ts) - gator_get_time(); + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + // Adjust gator_monotonic_started for the time spent sleeping, as gator_get_time does not account for it + if (gator_hibernate_time > 0) { + getnstimeofday(&ts); + gator_monotonic_started += gator_hibernate_time + gator_get_time() - timespec_to_ns(&ts); + gator_hibernate_time = 0; + } + + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); + } + 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) +{ + u64 timestamp, uptime; + struct timespec ts; + char uname_buf[512]; + void (*m2b)(struct timespec *ts); + + snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine); + + getnstimeofday(&ts); + timestamp = timespec_to_ns(&ts); + + do_posix_clock_monotonic_gettime(&ts); + // monotonic_to_bootbased is not defined for some versions of Android + m2b = symbol_get(monotonic_to_bootbased); + if (m2b) { + m2b(&ts); + } + uptime = timespec_to_ns(&ts); + + // Disable preemption as gator_get_time calls smp_processor_id to verify time is monotonic + preempt_disable(); + // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started + gator_monotonic_started = 0; + gator_monotonic_started = gator_get_time(); + + marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf); + preempt_enable(); +} + +int gator_events_install(struct gator_interface *interface) +{ + list_add_tail(&interface->list, &gator_events); + + return 0; +} + +int gator_events_get_key(void) +{ + // key 0 is reserved as a timestamp + // key 1 is reserved as the marker for thread specific counters + // Odd keys are assigned by the driver, even keys by the daemon + static int key = 3; + + const int ret = key; + key += 2; + return ret; +} + +static int gator_init(void) +{ + int i; + + calc_first_cluster_size(); + + // events sources + for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) + if (gator_events_list[i]) + gator_events_list[i](); + + gator_trace_sched_init(); + gator_trace_power_init(); + + return 0; +} + +static void gator_exit(void) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->shutdown) + gi->shutdown(); +} + +static int gator_start(void) +{ + unsigned long cpu, i; + struct gator_interface *gi; + + gator_buffer_wake_run = true; + if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) { + goto bwake_failure; + } + + if (gator_migrate_start()) + goto migrate_failure; + + // Initialize the buffer with the frame type and core + for_each_present_cpu(cpu) { + for (i = 0; i < NUM_GATOR_BUFS; i++) { + marshal_frame(cpu, i); + } + per_cpu(last_timestamp, cpu) = 0; + } + printed_monotonic_warning = false; + + // 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_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_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: + gator_migrate_stop(); +migrate_failure: + gator_buffer_wake_run = false; + up(&gator_buffer_wake_sem); + gator_buffer_wake_thread = NULL; +bwake_failure: + + return -1; +} + +static void gator_stop(void) +{ + struct gator_interface *gi; + + gator_annotate_stop(); + gator_trace_sched_stop(); + gator_trace_power_stop(); + gator_trace_gpu_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(); + + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); + + gator_migrate_stop(); + + gator_buffer_wake_run = false; + up(&gator_buffer_wake_sem); + gator_buffer_wake_thread = NULL; +} + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +/* fopen("buffer") */ +static int gator_op_setup(void) +{ + int err = 0; + int cpu, i; + + mutex_lock(&start_mutex); + + gator_buffer_size[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE; + gator_buffer_mask[SUMMARY_BUF] = SUMMARY_BUFFER_SIZE - 1; + + gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE; + gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1; + + gator_buffer_size[NAME_BUF] = NAME_BUFFER_SIZE; + gator_buffer_mask[NAME_BUF] = NAME_BUFFER_SIZE - 1; + + gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE; + gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE; + gator_buffer_mask[BLOCK_COUNTER_BUF] = BLOCK_COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE; + gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_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[IDLE_BUF] = IDLE_BUFFER_SIZE; + gator_buffer_mask[IDLE_BUF] = IDLE_BUFFER_SIZE - 1; + + gator_buffer_size[ACTIVITY_BUF] = ACTIVITY_BUFFER_SIZE; + gator_buffer_mask[ACTIVITY_BUF] = ACTIVITY_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; + per_cpu(gator_buffer_commit_time, cpu) = gator_live_rate; + + // 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; + gator_monotonic_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; + per_cpu(gator_buffer_commit_time, cpu) = 0; + } + mutex_unlock(&gator_buffer_mutex); + } + + memset(&sent_core_name, 0, sizeof(sent_core_name)); + + 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 commit, length1, length2, read; + char *buffer1; + char *buffer2; + int cpu, buftype; + int written = 0; + + // ensure there is enough space for a whole frame + 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 + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started); + + if (signal_pending(current)) { + return -EINTR; + } + + if (buftype == -1 || cpu == -1) { + return 0; + } + + mutex_lock(&gator_buffer_mutex); + + do { + 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]) { + break; + } + + // determine the size of two halves + length1 = commit - read; + length2 = 0; + 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; + } + + if (length1 + length2 > count - written) { + break; + } + + // start, middle or end + if (length1 > 0 && copy_to_user(&buf[written], buffer1, length1)) { + break; + } + + // possible wrap around + if (length2 > 0 && copy_to_user(&buf[written + length1], buffer2, length2)) { + break; + } + + per_cpu(gator_buffer_read, cpu)[buftype] = commit; + written += length1 + length2; + + // Wake up annotate_write if more space is available + if (buftype == ANNOTATE_BUF) { + wake_up(&gator_annotate_wait); + } + } while (buffer_commit_ready(&cpu, &buftype)); + + mutex_unlock(&gator_buffer_mutex); + + // kick just in case we've lost an SMP event + wake_up(&gator_buffer_wait); + + return written > 0 ? written : -EFAULT; +} + +static 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 +}; + +static 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; + gator_live_rate = 0; + + 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_ro_ulong(sb, root, "cpu_cores", &gator_cpu_cores); + gatorfs_create_ro_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); + gatorfs_create_ro_u64(sb, root, "started", &gator_monotonic_started); + gatorfs_create_u64(sb, root, "live_rate", &gator_live_rate); + + // 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); + + // Sched Events + sched_trace_create_files(sb, dir); + + // Power interface + gator_trace_power_create_files(sb, dir); +} + +/****************************************************************************** + * Module + ******************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + +#define GATOR_TRACEPOINTS \ + GATOR_HANDLE_TRACEPOINT(block_rq_complete); \ + GATOR_HANDLE_TRACEPOINT(cpu_frequency); \ + GATOR_HANDLE_TRACEPOINT(cpu_idle); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_begin); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_current); \ + GATOR_HANDLE_TRACEPOINT(cpu_migrate_finish); \ + GATOR_HANDLE_TRACEPOINT(irq_handler_exit); \ + GATOR_HANDLE_TRACEPOINT(mali_hw_counter); \ + GATOR_HANDLE_TRACEPOINT(mali_job_slots_event); \ + GATOR_HANDLE_TRACEPOINT(mali_mmu_as_in_use); \ + GATOR_HANDLE_TRACEPOINT(mali_mmu_as_released); \ + GATOR_HANDLE_TRACEPOINT(mali_page_fault_insert_pages); \ + GATOR_HANDLE_TRACEPOINT(mali_pm_status); \ + GATOR_HANDLE_TRACEPOINT(mali_sw_counter); \ + GATOR_HANDLE_TRACEPOINT(mali_sw_counters); \ + GATOR_HANDLE_TRACEPOINT(mali_timeline_event); \ + GATOR_HANDLE_TRACEPOINT(mali_total_alloc_pages_change); \ + GATOR_HANDLE_TRACEPOINT(mm_page_alloc); \ + GATOR_HANDLE_TRACEPOINT(mm_page_free); \ + GATOR_HANDLE_TRACEPOINT(mm_page_free_batched); \ + GATOR_HANDLE_TRACEPOINT(sched_process_exec); \ + GATOR_HANDLE_TRACEPOINT(sched_process_fork); \ + GATOR_HANDLE_TRACEPOINT(sched_process_free); \ + GATOR_HANDLE_TRACEPOINT(sched_switch); \ + GATOR_HANDLE_TRACEPOINT(softirq_exit); \ + +#define GATOR_HANDLE_TRACEPOINT(probe_name) \ + struct tracepoint *gator_tracepoint_##probe_name +GATOR_TRACEPOINTS; +#undef GATOR_HANDLE_TRACEPOINT + +static void gator_fct(struct tracepoint *tp, void *priv) +{ +#define GATOR_HANDLE_TRACEPOINT(probe_name) \ + if (strcmp(tp->name, #probe_name) == 0) { \ + gator_tracepoint_##probe_name = tp; \ + return; \ + } +GATOR_TRACEPOINTS; +#undef GATOR_HANDLE_TRACEPOINT +} + +#else + +#define for_each_kernel_tracepoint(fct, priv) + +#endif + +static int __init gator_module_init(void) +{ + for_each_kernel_tracepoint(gator_fct, NULL); + + if (gatorfs_register()) { + return -1; + } + + if (gator_init()) { + gatorfs_unregister(); + return -1; + } + + setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); + + // Initialize the list of cpuids + memset(gator_cpuids, -1, sizeof(gator_cpuids)); + on_each_cpu(gator_read_cpuid, NULL, 1); + + return 0; +} + +static void __exit gator_module_exit(void) +{ + del_timer_sync(&gator_buffer_wake_up_timer); + tracepoint_synchronize_unregister(); + gator_exit(); + gatorfs_unregister(); +} + +module_init(gator_module_init); +module_exit(gator_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Gator system profiler"); +#define STRIFY2(ARG) #ARG +#define STRIFY(ARG) STRIFY2(ARG) +MODULE_VERSION(STRIFY(PROTOCOL_VERSION)); diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c new file mode 100644 index 000000000000..97b4ae6f9d4d --- /dev/null +++ b/drivers/gator/gator_marshaling.c @@ -0,0 +1,362 @@ +/** + * Copyright (C) ARM Limited 2012-2014. 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 NEWLINE_CANARY \ + /* Unix */ \ + "1\n" \ + /* Windows */ \ + "2\r\n" \ + /* Mac OS */ \ + "3\r" \ + /* RISC OS */ \ + "4\n\r" \ + /* Add another character so the length isn't 0x0a bytes */ \ + "5" + +#ifdef MALI_SUPPORT +#include "gator_events_mali_common.h" +#endif + +static void marshal_summary(long long timestamp, long long uptime, long long monotonic_delta, const char * uname) +{ + unsigned long flags; + int cpu = 0; + + local_irq_save(flags); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY); + gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime); + gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta); + gator_buffer_write_string(cpu, SUMMARY_BUF, "uname"); + gator_buffer_write_string(cpu, SUMMARY_BUF, uname); +#if GATOR_IKS_SUPPORT + gator_buffer_write_string(cpu, SUMMARY_BUF, "iks"); + gator_buffer_write_string(cpu, SUMMARY_BUF, ""); +#endif + // Let Streamline know which GPU is used so that it can label the GPU Activity appropriately. This is a temporary fix, to be improved in a future release. +#ifdef MALI_SUPPORT + gator_buffer_write_string(cpu, SUMMARY_BUF, "mali_type"); +#if (MALI_SUPPORT == MALI_4xx) + gator_buffer_write_string(cpu, SUMMARY_BUF, "4xx"); +#elif (MALI_SUPPORT == MALI_T6xx) + gator_buffer_write_string(cpu, SUMMARY_BUF, "6xx"); +#else + gator_buffer_write_string(cpu, SUMMARY_BUF, "unknown"); +#endif +#endif + gator_buffer_write_string(cpu, SUMMARY_BUF, ""); + // Commit the buffer now so it can be one of the first frames read by Streamline + local_irq_restore(flags); + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); +} + +static bool marshal_cookie_header(const char *text) +{ + int cpu = get_physical_cpu(); + return buffer_check_space(cpu, NAME_BUF, strlen(text) + 3 * MAXSIZE_PACK32); +} + +static void marshal_cookie(int cookie, const char *text) +{ + int cpu = get_physical_cpu(); + // buffer_check_space already called by marshal_cookie_header + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_COOKIE); + gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); + gator_buffer_write_string(cpu, NAME_BUF, text); + buffer_check(cpu, NAME_BUF, gator_get_time()); +} + +static void marshal_thread_name(int pid, char *name) +{ + unsigned long flags, cpu; + u64 time; + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, NAME_BUF, TASK_COMM_LEN + 3 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_THREAD_NAME); + gator_buffer_write_packed_int64(cpu, NAME_BUF, time); + gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + gator_buffer_write_string(cpu, NAME_BUF, name); + } + local_irq_restore(flags); + buffer_check(cpu, NAME_BUF, time); +} + +static void marshal_link(int cookie, int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_LINK); + gator_buffer_write_packed_int64(cpu, NAME_BUF, time); + gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); + gator_buffer_write_packed_int(cpu, NAME_BUF, tgid); + gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, NAME_BUF, time); +} + +static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, u64 time) +{ + int cpu = get_physical_cpu(); + if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF, time); + + return false; + } + + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, 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); + + return true; +} + +static void marshal_backtrace(unsigned long address, int cookie, int in_kernel) +{ + int cpu = get_physical_cpu(); + if (cookie == 0 && !in_kernel) { + cookie = UNRESOLVED_COOKIE; + } + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address); +} + +static void marshal_backtrace_footer(u64 time) +{ + int cpu = get_physical_cpu(); + 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, time); +} + +static bool marshal_event_header(u64 time) +{ + unsigned long flags, cpu = get_physical_cpu(); + bool retval = false; + + local_irq_save(flags); + if (buffer_check_space(cpu, BLOCK_COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, 0); // key of zero indicates a timestamp + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, time); + retval = true; + } + local_irq_restore(flags); + + return retval; +} + +static void marshal_event(int len, int *buffer) +{ + unsigned long i, flags, cpu = get_physical_cpu(); + + 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 + local_irq_save(flags); + for (i = 0; i < len; i += 2) { + if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK32)) { + break; + } + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i]); + gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i + 1]); + } + local_irq_restore(flags); +} + +static void marshal_event64(int len, long long *buffer64) +{ + unsigned long i, flags, cpu = get_physical_cpu(); + + 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 + local_irq_save(flags); + for (i = 0; i < len; i += 2) { + if (!buffer_check_space(cpu, BLOCK_COUNTER_BUF, 2 * MAXSIZE_PACK64)) { + break; + } + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i]); + gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i + 1]); + } + local_irq_restore(flags); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_event_single(int core, int key, int value) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, key); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, value); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF, time); +} + +static void marshal_event_single64(int core, int key, long long value) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, COUNTER_BUF, 2 * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, time); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, key); + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, value); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF, time); +} +#endif + +static void marshal_sched_trace_switch(int pid, int state) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_SWITCH); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + 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, time); +} + +static void marshal_sched_trace_exit(int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, MESSAGE_SCHED_EXIT); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF, time); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_idle(int core, int state) +{ + unsigned long flags, cpu; + u64 time; + + local_irq_save(flags); + cpu = get_physical_cpu(); + time = gator_get_time(); + if (buffer_check_space(cpu, IDLE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, IDLE_BUF, state); + gator_buffer_write_packed_int64(cpu, IDLE_BUF, time); + gator_buffer_write_packed_int(cpu, IDLE_BUF, core); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, IDLE_BUF, time); +} +#endif + +#if defined(__arm__) || defined(__aarch64__) +static void marshal_core_name(const int core, const int cpuid, const char *name) +{ + int cpu = get_physical_cpu(); + unsigned long flags; + local_irq_save(flags); + if (buffer_check_space(cpu, SUMMARY_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) { + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_CORE_NAME); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, core); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, cpuid); + gator_buffer_write_string(cpu, SUMMARY_BUF, name); + } + // Commit core names now so that they can show up in live + local_irq_restore(flags); + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); +} +#endif + +static void marshal_activity_switch(int core, int key, int activity, int pid, int state) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + if (!per_cpu(gator_buffer, cpu)[ACTIVITY_BUF]) + return; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, ACTIVITY_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, MESSAGE_SWITCH); + gator_buffer_write_packed_int64(cpu, ACTIVITY_BUF, time); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, core); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, key); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, activity); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, pid); + gator_buffer_write_packed_int(cpu, ACTIVITY_BUF, state); + } + local_irq_restore(flags); + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ACTIVITY_BUF, time); +} + +void gator_marshal_activity_switch(int core, int key, int activity, int pid) +{ + // state is reserved for cpu use only + marshal_activity_switch(core, key, activity, pid, 0); +} diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c new file mode 100644 index 000000000000..a8b9e7d61ece --- /dev/null +++ b/drivers/gator/gator_trace_gpu.c @@ -0,0 +1,333 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 +#ifdef MALI_DIR_MIDGARD +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ +#include "linux/mali_linux_trace.h" +#endif +#endif + +/* + * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK. + */ +#define EVENT_TYPE_SINGLE 0 +#define EVENT_TYPE_START 1 +#define EVENT_TYPE_STOP 2 +#define EVENT_TYPE_SUSPEND 3 +#define EVENT_TYPE_RESUME 4 + +/* Note whether tracepoints have been registered */ +static int mali_timeline_trace_registered; +static int mali_job_slots_trace_registered; + +enum { + GPU_UNIT_NONE = 0, + GPU_UNIT_VP, + GPU_UNIT_FP, + GPU_UNIT_CL, + NUMBER_OF_GPU_UNITS +}; + +#if defined(MALI_SUPPORT) + +struct mali_activity { + int core; + int key; + int count; + int last_activity; + int last_pid; +}; + +#define NUMBER_OF_GPU_CORES 16 +static struct mali_activity mali_activities[NUMBER_OF_GPU_UNITS*NUMBER_OF_GPU_CORES]; +static DEFINE_SPINLOCK(mali_activities_lock); + +/* Only one event should be running on a unit and core at a time (ie, a start + * event can only be followed by a stop and vice versa), but because the kernel + * only knows when a job is enqueued and not started, it is possible for a + * start1, start2, stop1, stop2. Change it back into start1, stop1, start2, + * stop2 by queueing up start2 and releasing it when stop1 is received. + */ + +static int mali_activity_index(int core, int key) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mali_activities); ++i) { + if ((mali_activities[i].core == core) && (mali_activities[i].key == key)) { + break; + } + if ((mali_activities[i].core == 0) && (mali_activities[i].key == 0)) { + mali_activities[i].core = core; + mali_activities[i].key = key; + break; + } + } + BUG_ON(i >= ARRAY_SIZE(mali_activities)); + + return i; +} + +static void mali_activity_enqueue(int core, int key, int activity, int pid) +{ + int i; + int count; + + spin_lock(&mali_activities_lock); + i = mali_activity_index(core, key); + + count = mali_activities[i].count; + BUG_ON(count < 0); + ++mali_activities[i].count; + if (count) { + mali_activities[i].last_activity = activity; + mali_activities[i].last_pid = pid; + } + spin_unlock(&mali_activities_lock); + + if (!count) { + gator_marshal_activity_switch(core, key, activity, pid); + } +} + +static void mali_activity_stop(int core, int key) +{ + int i; + int count; + int last_activity = 0; + int last_pid = 0; + + spin_lock(&mali_activities_lock); + i = mali_activity_index(core, key); + + if (mali_activities[i].count == 0) { + spin_unlock(&mali_activities_lock); + return; + } + --mali_activities[i].count; + count = mali_activities[i].count; + if (count) { + last_activity = mali_activities[i].last_activity; + last_pid = mali_activities[i].last_pid; + } + spin_unlock(&mali_activities_lock); + + gator_marshal_activity_switch(core, key, 0, 0); + if (count) { + gator_marshal_activity_switch(core, key, last_activity, last_pid); + } +} + +void mali_activity_clear(mali_counter mali_activity[], size_t mali_activity_size) +{ + int activity; + int cores; + int core; + + for (activity = 0; activity < mali_activity_size; ++activity) { + cores = mali_activity[activity].cores; + if (cores < 0) { + cores = 1; + } + for (core = 0; core < cores; ++core) { + if (mali_activity[activity].enabled) { + gator_marshal_activity_switch(core, mali_activity[activity].key, 0, 0); + } + } + } +} + +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) +#include "gator_events_mali_4xx.h" + +/* + * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK. + */ +enum { + EVENT_CHANNEL_SOFTWARE = 0, + EVENT_CHANNEL_VP0 = 1, + EVENT_CHANNEL_FP0 = 5, + EVENT_CHANNEL_FP1, + EVENT_CHANNEL_FP2, + EVENT_CHANNEL_FP3, + EVENT_CHANNEL_FP4, + EVENT_CHANNEL_FP5, + EVENT_CHANNEL_FP6, + EVENT_CHANNEL_FP7, + EVENT_CHANNEL_GPU = 21 +}; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +enum { + EVENT_REASON_SINGLE_GPU_NONE = 0, + EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, +}; + +mali_counter mali_activity[2]; + +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; + + // 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 + + switch (state) { + case EVENT_TYPE_START: + if (component == EVENT_CHANNEL_VP0) { + /* tgid = d0; pid = d1; */ + if (mali_activity[1].enabled) { + mali_activity_enqueue(0, mali_activity[1].key, 1, d1); + } + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + /* tgid = d0; pid = d1; */ + if (mali_activity[0].enabled) { + mali_activity_enqueue(component - EVENT_CHANNEL_FP0, mali_activity[0].key, 1, d1); + } + } + break; + + case EVENT_TYPE_STOP: + if (component == EVENT_CHANNEL_VP0) { + if (mali_activity[1].enabled) { + mali_activity_stop(0, mali_activity[1].key); + } + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + if (mali_activity[0].enabled) { + mali_activity_stop(component - EVENT_CHANNEL_FP0, mali_activity[0].key); + } + } + break; + + case EVENT_TYPE_SINGLE: + if (component == EVENT_CHANNEL_GPU) { + unsigned int reason = (event_id & 0xffff); + + if (reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) { + gator_events_mali_log_dvfs_event(d0, d1); + } + } + break; + + default: + break; + } +} +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + +mali_counter mali_activity[3]; + +#if defined(MALI_JOB_SLOTS_EVENT_CHANGED) +GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid, unsigned char job_id)) +#else +GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid)) +#endif +{ + unsigned int component, state, unit; +#if !defined(MALI_JOB_SLOTS_EVENT_CHANGED) + unsigned char job_id = 0; +#endif + + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field + + switch (component) { + case 0: + unit = GPU_UNIT_FP; + break; + case 1: + unit = GPU_UNIT_VP; + break; + case 2: + unit = GPU_UNIT_CL; + break; + default: + unit = GPU_UNIT_NONE; + } + + if (unit != GPU_UNIT_NONE) { + switch (state) { + case EVENT_TYPE_START: + if (mali_activity[component].enabled) { + mali_activity_enqueue(0, mali_activity[component].key, 1, (pid != 0 ? pid : tgid)); + } + break; + case EVENT_TYPE_STOP: + default: // Some jobs can be soft-stopped, so ensure that this terminates the activity trace. + if (mali_activity[component].enabled) { + mali_activity_stop(0, mali_activity[component].key); + } + break; + } + } +} +#endif + +static int gator_trace_gpu_start(void) +{ + /* + * Returns nonzero for installation failed + * Absence of gpu trace points is not an error + */ + +#if defined(MALI_SUPPORT) + memset(&mali_activities, 0, sizeof(mali_activities)); +#endif + mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); + if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { + mali_timeline_trace_registered = 1; + } +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + mali_activity_clear(mali_activity, ARRAY_SIZE(mali_activity)); + if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) { + mali_job_slots_trace_registered = 1; + } +#endif + + return 0; +} + +static void gator_trace_gpu_stop(void) +{ +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + if (mali_timeline_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + if (mali_job_slots_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_job_slots_event); + } +#endif + + mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; +} diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c new file mode 100644 index 000000000000..f2754b1c2b56 --- /dev/null +++ b/drivers/gator/gator_trace_power.c @@ -0,0 +1,203 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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 + +#if defined(__arm__) + +#include + +#define implements_wfi() (!machine_is_omap3_beagle()) + +#else + +#define implements_wfi() false + +#endif + +// 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; + int cpu; + bool found_nonzero_freq = false; + + // Even if CONFIG_CPU_FREQ is defined, it still may not be used. Check + // for non-zero values from cpufreq_quick_get + for_each_online_cpu(cpu) { + if (cpufreq_quick_get(cpu) > 0) { + found_nonzero_freq = true; + break; + } + } + + if (found_nonzero_freq) { + // 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)) +{ + cpu = lcpu_to_pcpu(cpu); + marshal_event_single64(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000L); +} + +GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) +{ + cpu = lcpu_to_pcpu(cpu); + + if (state == per_cpu(idle_prev_state, cpu)) { + return; + } + + if (implements_wfi()) { + if (state == PWR_EVENT_EXIT) { + // transition from wfi to non-wfi + marshal_idle(cpu, MESSAGE_IDLE_EXIT); + } else { + // transition from non-wfi to wfi + marshal_idle(cpu, MESSAGE_IDLE_ENTER); + } + } + + per_cpu(idle_prev_state, cpu) = state; + + if (power_cpu_enabled[POWER_CPU_IDLE]) { + // Increment state so that no negative numbers are sent + marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state + 1); + } +} + +static void gator_trace_power_online(void) +{ + int pcpu = get_physical_cpu(); + int lcpu = get_logical_cpu(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single64(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000L); + } +} + +static void gator_trace_power_offline(void) +{ + // Set frequency to zero on an offline + int cpu = get_physical_cpu(); + 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; + } +} + +static 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) +{ +} + +static 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 000000000000..655008628933 --- /dev/null +++ b/drivers/gator/gator_trace_sched.c @@ -0,0 +1,297 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 TASK_MAP_ENTRIES 1024 /* must be power of 2 */ +#define TASK_MAX_COLLISIONS 2 + +enum { + STATE_WAIT_ON_OTHER = 0, + STATE_CONTENTION, + STATE_WAIT_ON_IO, + CPU_WAIT_TOTAL +}; + +static DEFINE_PER_CPU(uint64_t *, taskname_keys); +static DEFINE_PER_CPU(int, collecting); + +// this array is never read as the cpu wait charts are derived counters +// the files are needed, nonetheless, to show that these counters are available +static ulong cpu_wait_enabled[CPU_WAIT_TOTAL]; +static ulong sched_cpu_key[CPU_WAIT_TOTAL]; + +static int sched_trace_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + // CPU Wait - Contention + dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_contention"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_CONTENTION]); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_CONTENTION]); + + // CPU Wait - I/O + dir = gatorfs_mkdir(sb, root, "Linux_cpu_wait_io"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &cpu_wait_enabled[STATE_WAIT_ON_IO]); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_cpu_key[STATE_WAIT_ON_IO]); + + return 0; +} + +static void emit_pid_name(struct task_struct *task) +{ + bool found = false; + char taskcomm[TASK_COMM_LEN + 3]; + unsigned long x, cpu = get_physical_cpu(); + 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_thread_name(task->pid, taskcomm); + } +} + +static void collect_counters(u64 time, struct task_struct *task) +{ + int *buffer, len, cpu = get_physical_cpu(); + long long *buffer64; + struct gator_interface *gi; + + if (marshal_event_header(time)) { + 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); + } + if (gi->read_proc && task != NULL) { + len = gi->read_proc(&buffer64, task); + marshal_event64(len, buffer64); + } + } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + + // Commit buffers on timeout + if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { + static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF, ACTIVITY_BUF }; + int i; + + for (i = 0; i < ARRAY_SIZE(buftypes); ++i) { + gator_commit_buffer(cpu, buftypes[i], time); + } + + // spinlocks are noops on uniprocessor machines and mutexes do not work in sched_switch context in + // RT-Preempt full, so disable proactive flushing of the annotate frame on uniprocessor machines. +#ifdef CONFIG_SMP + // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full + if (on_primary_core() && spin_trylock(&annotate_lock)) { + gator_commit_buffer(0, ANNOTATE_BUF, time); + spin_unlock(&annotate_lock); + } +#endif + } + } +} + +// special case used during a suspend of the system +static void trace_sched_insert_idle(void) +{ + marshal_sched_trace_switch(0, 0); +} + +static void gator_trace_emit_link(struct task_struct *p) +{ + int cookie; + int cpu = get_physical_cpu(); + + cookie = get_exec_cookie(cpu, p); + emit_pid_name(p); + + marshal_link(cookie, p->tgid, p->pid); +} + +GATOR_DEFINE_PROBE(sched_process_fork, TP_PROTO(struct task_struct *parent, struct task_struct *child)) +{ + gator_trace_emit_link(child); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +GATOR_DEFINE_PROBE(sched_process_exec, TP_PROTO(struct task_struct *p, pid_t old_pid, struct linux_binprm *bprm)) +{ + gator_trace_emit_link(p); +} +#endif + +#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 +{ + int state; + int cpu = get_physical_cpu(); + + per_cpu(in_scheduler_context, cpu) = true; + + // do as much work as possible before disabling interrupts + if (prev->state == TASK_RUNNING) { + state = STATE_CONTENTION; + } else if (prev->in_iowait) { + state = STATE_WAIT_ON_IO; + } else { + state = STATE_WAIT_ON_OTHER; + } + + per_cpu(collecting, cpu) = 1; + collect_counters(gator_get_time(), prev); + per_cpu(collecting, cpu) = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + gator_trace_emit_link(next); +#endif + marshal_sched_trace_switch(next->pid, state); + + per_cpu(in_scheduler_context, cpu) = false; +} + +GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) +{ + marshal_sched_trace_exit(p->tgid, p->pid); +} + +static void do_nothing(void *info) +{ + // Intentionally do nothing + (void)info; +} + +static int register_scheduler_tracepoints(void) +{ + // register tracepoints + if (GATOR_REGISTER_TRACE(sched_process_fork)) + goto fail_sched_process_fork; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (GATOR_REGISTER_TRACE(sched_process_exec)) + goto fail_sched_process_exec; +#endif + if (GATOR_REGISTER_TRACE(sched_switch)) + goto fail_sched_switch; + if (GATOR_REGISTER_TRACE(sched_process_free)) + goto fail_sched_process_free; + pr_debug("gator: registered tracepoints\n"); + + // Now that the scheduler tracepoint is registered, force a context switch + // on all cpus to capture what is currently running. + on_each_cpu(do_nothing, NULL, 0); + + return 0; + + // unregister tracepoints on error +fail_sched_process_free: + GATOR_UNREGISTER_TRACE(sched_switch); +fail_sched_switch: + GATOR_UNREGISTER_TRACE(sched_process_fork); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +fail_sched_process_exec: + GATOR_UNREGISTER_TRACE(sched_process_exec); +#endif +fail_sched_process_fork: + pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void unregister_scheduler_tracepoints(void) +{ + GATOR_UNREGISTER_TRACE(sched_process_fork); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + GATOR_UNREGISTER_TRACE(sched_process_exec); +#endif + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_process_free); + pr_debug("gator: unregistered tracepoints\n"); +} + +static void gator_trace_sched_stop(void) +{ + int cpu; + + unregister_scheduler_tracepoints(); + + for_each_present_cpu(cpu) { + kfree(per_cpu(taskname_keys, cpu)); + } +} + +static int gator_trace_sched_start(void) +{ + int cpu, size; + int ret; + + 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); + } + + ret = register_scheduler_tracepoints(); + + return ret; +} + +static void gator_trace_sched_offline(void) +{ + trace_sched_insert_idle(); +} + +static void gator_trace_sched_init(void) +{ + int i; + for (i = 0; i < CPU_WAIT_TOTAL; i++) { + cpu_wait_enabled[i] = 0; + sched_cpu_key[i] = gator_events_get_key(); + } +} diff --git a/drivers/gator/mali/mali_dd_gator_api.h b/drivers/gator/mali/mali_dd_gator_api.h new file mode 100644 index 000000000000..104b34f2d72a --- /dev/null +++ b/drivers/gator/mali/mali_dd_gator_api.h @@ -0,0 +1,40 @@ +#if !defined(MALI_DDK_GATOR_API_VERSION) + #define MALI_DDK_GATOR_API_VERSION 3 +#endif +#if !defined(MALI_TRUE) + #define MALI_TRUE ((unsigned int)1) +#endif + +#if !defined(MALI_FALSE) + #define MALI_FALSE ((unsigned int)0) +#endif + +struct mali_dd_hwcnt_info { + + /* Passed from Gator to kbase */ + //u32 in_mali_dd_hwcnt_version; + unsigned short int bitmask[4]; + + /* Passed from kbase to Gator */ + + /* ptr to counter dump memory */ + void *kernel_dump_buffer; + + /* size of counter dump memory */ + unsigned int size; + + unsigned int gpu_id; + + unsigned int nr_cores; + + unsigned int nr_core_groups; + + /* The cached present bitmaps - these are the same as the corresponding hardware registers*/ + unsigned long int shader_present_bitmap; +}; + +struct mali_dd_hwcnt_handles; +extern struct mali_dd_hwcnt_handles* mali_dd_hwcnt_init(struct mali_dd_hwcnt_info *in_out_info); +extern void mali_dd_hwcnt_clear(struct mali_dd_hwcnt_info *in_out_info, struct mali_dd_hwcnt_handles *opaque_handles); +extern unsigned int kbase_dd_instr_hwcnt_dump_complete(struct mali_dd_hwcnt_handles *opaque_handles, unsigned int * const success); +extern unsigned int kbase_dd_instr_hwcnt_dump_irq(struct mali_dd_hwcnt_handles *opaque_handles); diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h new file mode 100644 index 000000000000..ff00d90cee78 --- /dev/null +++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h @@ -0,0 +1,163 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ +#define __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* + * The number of processor cores. Update to suit your hardware implementation. + */ +#define MAX_NUM_FP_CORES (4) +#define MAX_NUM_VP_CORES (1) +#define MAX_NUM_L2_CACHE_CORES (1) + +enum counters +{ + /* Timeline activity */ + ACTIVITY_VP_0 = 0, + ACTIVITY_FP_0, + ACTIVITY_FP_1, + ACTIVITY_FP_2, + ACTIVITY_FP_3, + + /* L2 cache counters */ + COUNTER_L2_0_C0, + COUNTER_L2_0_C1, + + /* Vertex processor counters */ + COUNTER_VP_0_C0, + COUNTER_VP_0_C1, + + /* Fragment processor counters */ + COUNTER_FP_0_C0, + COUNTER_FP_0_C1, + COUNTER_FP_1_C0, + COUNTER_FP_1_C1, + COUNTER_FP_2_C0, + COUNTER_FP_2_C1, + COUNTER_FP_3_C0, + COUNTER_FP_3_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, + COUNTER_FREQUENCY, + COUNTER_VOLTAGE, + + NUMBER_OF_EVENTS +}; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0 +#define LAST_ACTIVITY_EVENT ACTIVITY_FP_3 + +#define FIRST_HW_COUNTER COUNTER_L2_0_C0 +#define LAST_HW_COUNTER COUNTER_FP_3_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +/* Signifies that the system is able to report voltage and frequency numbers. */ +#define DVFS_REPORTED_BY_DDK 1 + +/** + * Structure to pass performance counter data of a Mali core + */ +typedef struct _mali_profiling_core_counters +{ + u32 source0; + u32 value0; + u32 source1; + u32 value1; +} _mali_profiling_core_counters; + +/* + * For compatibility with utgard. + */ +typedef struct _mali_profiling_l2_counter_values +{ + struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES]; +} _mali_profiling_l2_counter_values; + +typedef struct _mali_profiling_mali_version +{ + u32 mali_product_id; + u32 mali_version_major; + u32 mali_version_minor; + u32 num_of_l2_cores; + u32 num_of_fp_cores; + u32 num_of_vp_cores; +} _mali_profiling_mali_version; + +extern void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values); +extern u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values); + +/* + * 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_COUNTER_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ */ diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h new file mode 100644 index 000000000000..43c576042880 --- /dev/null +++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h @@ -0,0 +1,201 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 __MALI_UTGARD_PROFILING_GATOR_API_H__ +#define __MALI_UTGARD_PROFILING_GATOR_API_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MALI_PROFILING_API_VERSION 4 + +#define MAX_NUM_L2_CACHE_CORES 3 +#define MAX_NUM_FP_CORES 8 +#define MAX_NUM_VP_CORES 1 + +/** The list of events supported by the Mali DDK. */ +typedef enum +{ + /* Vertex processor activity */ + ACTIVITY_VP_0 = 0, + + /* Fragment processor activity */ + ACTIVITY_FP_0, /* 1 */ + ACTIVITY_FP_1, + ACTIVITY_FP_2, + ACTIVITY_FP_3, + ACTIVITY_FP_4, + ACTIVITY_FP_5, + ACTIVITY_FP_6, + ACTIVITY_FP_7, + + /* L2 cache counters */ + COUNTER_L2_0_C0, + COUNTER_L2_0_C1, + COUNTER_L2_1_C0, + COUNTER_L2_1_C1, + COUNTER_L2_2_C0, + COUNTER_L2_2_C1, + + /* Vertex processor counters */ + COUNTER_VP_0_C0, /*15*/ + COUNTER_VP_0_C1, + + /* Fragment processor counters */ + COUNTER_FP_0_C0, + COUNTER_FP_0_C1, + COUNTER_FP_1_C0, + COUNTER_FP_1_C1, + COUNTER_FP_2_C0, + COUNTER_FP_2_C1, + COUNTER_FP_3_C0, + COUNTER_FP_3_C1, + COUNTER_FP_4_C0, + COUNTER_FP_4_C1, + COUNTER_FP_5_C0, + COUNTER_FP_5_C1, + COUNTER_FP_6_C0, + COUNTER_FP_6_C1, + COUNTER_FP_7_C0, + COUNTER_FP_7_C1, /* 32 */ + + /* + * If more hardware counters are added, the _mali_osk_hw_counter_table + * below should also be updated. + */ + + /* 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, + + /* Framebuffer capture pseudo-counter */ + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +} _mali_osk_counter_id; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP_0 +#define LAST_ACTIVITY_EVENT ACTIVITY_FP_7 + +#define FIRST_HW_COUNTER COUNTER_L2_0_C0 +#define LAST_HW_COUNTER COUNTER_FP_7_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 + +/** + * Structure to pass performance counter data of a Mali core + */ +typedef struct _mali_profiling_core_counters +{ + u32 source0; + u32 value0; + u32 source1; + u32 value1; +} _mali_profiling_core_counters; + +/** + * Structure to pass performance counter data of Mali L2 cache cores + */ +typedef struct _mali_profiling_l2_counter_values +{ + struct _mali_profiling_core_counters cores[MAX_NUM_L2_CACHE_CORES]; +} _mali_profiling_l2_counter_values; + +/** + * Structure to pass data defining Mali instance in use: + * + * mali_product_id - Mali product id + * mali_version_major - Mali version major number + * mali_version_minor - Mali version minor number + * num_of_l2_cores - number of L2 cache cores + * num_of_fp_cores - number of fragment processor cores + * num_of_vp_cores - number of vertex processor cores + */ +typedef struct _mali_profiling_mali_version +{ + u32 mali_product_id; + u32 mali_version_major; + u32 mali_version_minor; + u32 num_of_l2_cores; + u32 num_of_fp_cores; + u32 num_of_vp_cores; +} _mali_profiling_mali_version; + +/* + * List of possible actions to be controlled by Streamline. + * The following numbers are used by gator to control the frame buffer dumping and s/w counter reporting. + * We cannot use the enums in mali_uk_types.h because they are unknown inside gator. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_COUNTER_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + +void _mali_profiling_control(u32 action, u32 value); + +u32 _mali_profiling_get_l2_counters(_mali_profiling_l2_counter_values *values); + +int _mali_profiling_set_event(u32 counter_id, s32 event_id); + +u32 _mali_profiling_get_api_version(void); + +void _mali_profiling_get_mali_version(struct _mali_profiling_mali_version *values); + +#ifdef __cplusplus +} +#endif + +#endif /* __MALI_UTGARD_PROFILING_GATOR_API_H__ */ diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk new file mode 100644 index 000000000000..fa7571ded17b --- /dev/null +++ b/drivers/gator/mali_t6xx.mk @@ -0,0 +1,39 @@ +# Defines for Mali-T6xx driver +EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ + -DMALI_LICENSE_IS_GPL=1 \ + -DMALI_BASE_TRACK_MEMLEAK=0 \ + -DMALI_DEBUG=0 \ + -DMALI_ERROR_INJECT_ON=0 \ + -DMALI_CUSTOMER_RELEASE=1 \ + -DMALI_UNIT_TEST=0 \ + -DMALI_BACKEND_KERNEL=1 \ + -DMALI_NO_MALI=0 + +DDK_DIR ?= . +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/t6xx),) +KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase +OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk +endif + +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard),) +KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard +OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk +EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1 +endif + +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard/mali_dd_gator_api.h),) +EXTRA_CFLAGS += -DMALI_SIMPLE_API=1 +endif + +UMP_DIR = $(DDK_DIR)/include/linux + +# Include directories in the DDK +EXTRA_CFLAGS += -I$(KBASE_DIR)/ \ + -I$(KBASE_DIR)/.. \ + -I$(OSK_DIR)/.. \ + -I$(UMP_DIR)/.. \ + -I$(DDK_DIR)/include \ + -I$(KBASE_DIR)/osk/src/linux/include \ + -I$(KBASE_DIR)/platform_dummy \ + -I$(KBASE_DIR)/src + diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk new file mode 100644 index 000000000000..44c069cc7e24 --- /dev/null +++ b/tools/gator/daemon/Android.mk @@ -0,0 +1,65 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) + +LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -pthread -DETCDIR=\"/etc\" -Ilibsensors + +LOCAL_SRC_FILES := \ + Buffer.cpp \ + CapturedXML.cpp \ + Child.cpp \ + ConfigurationXML.cpp \ + Driver.cpp \ + DriverSource.cpp \ + DynBuf.cpp \ + EventsXML.cpp \ + ExternalSource.cpp \ + FSDriver.cpp \ + Fifo.cpp \ + Hwmon.cpp \ + KMod.cpp \ + LocalCapture.cpp \ + Logging.cpp \ + main.cpp \ + MaliVideoDriver.cpp \ + Monitor.cpp \ + OlySocket.cpp \ + OlyUtility.cpp \ + PerfBuffer.cpp \ + PerfDriver.cpp \ + PerfGroup.cpp \ + PerfSource.cpp \ + Proc.cpp \ + Sender.cpp \ + SessionData.cpp \ + SessionXML.cpp \ + Source.cpp \ + StreamlineSetup.cpp \ + UEvent.cpp \ + UserSpaceSource.cpp \ + libsensors/access.c \ + libsensors/conf-lex.c \ + libsensors/conf-parse.c \ + libsensors/data.c \ + libsensors/error.c \ + libsensors/general.c \ + libsensors/init.c \ + libsensors/sysfs.c \ + 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 + +include $(BUILD_EXECUTABLE) diff --git a/tools/gator/daemon/Application.mk b/tools/gator/daemon/Application.mk new file mode 100644 index 000000000000..631ba54148d1 --- /dev/null +++ b/tools/gator/daemon/Application.mk @@ -0,0 +1 @@ +APP_PLATFORM := android-8 diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp new file mode 100644 index 000000000000..dd19f7f8be76 --- /dev/null +++ b/tools/gator/daemon/Buffer.cpp @@ -0,0 +1,391 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "Buffer.h" + +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +#define mask (mSize - 1) + +enum { + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, + CODE_KEYS_OLD = 6, +}; + +// Summary Frame Messages +enum { + MESSAGE_SUMMARY = 1, + MESSAGE_CORE_NAME = 3, +}; + +// From gator_marshaling.c +#define NEWLINE_CANARY \ + /* Unix */ \ + "1\n" \ + /* Windows */ \ + "2\r\n" \ + /* Mac OS */ \ + "3\r" \ + /* RISC OS */ \ + "4\n\r" \ + /* Add another character so the length isn't 0x0a bytes */ \ + "5" + +Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) { + if ((mSize & mask) != 0) { + logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); + handleException(); + } + frame(); +} + +Buffer::~Buffer() { + delete [] mBuf; +} + +void Buffer::write(Sender *const sender) { + if (!commitReady()) { + return; + } + + // determine the size of two halves + int length1 = mCommitPos - mReadPos; + char *buffer1 = mBuf + mReadPos; + int length2 = 0; + char *buffer2 = mBuf; + if (length1 < 0) { + length1 = mSize - mReadPos; + length2 = mCommitPos; + } + + logg->logMessage("Sending data length1: %i length2: %i", length1, length2); + + // start, middle or end + if (length1 > 0) { + sender->writeData(buffer1, length1, RESPONSE_APC_DATA); + } + + // possible wrap around + if (length2 > 0) { + sender->writeData(buffer2, length2, RESPONSE_APC_DATA); + } + + mReadPos = mCommitPos; +} + +bool Buffer::commitReady() const { + return mCommitPos != mReadPos; +} + +int Buffer::bytesAvailable() const { + int filled = mWritePos - mReadPos; + if (filled < 0) { + filled += mSize; + } + + int remaining = mSize - filled; + + if (mAvailable) { + // 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; +} + +bool Buffer::checkSpace(const int bytes) { + const int remaining = bytesAvailable(); + + if (remaining < bytes) { + mAvailable = false; + } else { + mAvailable = true; + } + + return mAvailable; +} + +int Buffer::contiguousSpaceAvailable() const { + int remaining = bytesAvailable(); + int contiguous = mSize - mWritePos; + if (remaining < contiguous) { + return remaining; + } else { + return contiguous; + } +} + +void Buffer::commit(const uint64_t time) { + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + const int typeLength = gSessionData->mLocalCapture ? 0 : 1; + int length = mWritePos - mCommitPos; + if (length < 0) { + length += mSize; + } + length = length - typeLength - sizeof(int32_t); + for (size_t byte = 0; byte < sizeof(int32_t); byte++) { + mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + } + + logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); + mCommitPos = mWritePos; + + if (gSessionData->mLiveRate > 0) { + while (time > mCommitTime) { + mCommitTime += gSessionData->mLiveRate; + } + } + + if (!mIsDone) { + frame(); + } + + // send a notification that data is ready + sem_post(mReaderSem); +} + +void Buffer::check(const uint64_t time) { + int filled = mWritePos - mCommitPos; + if (filled < 0) { + filled += mSize; + } + if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { + commit(time); + } +} + +void Buffer::packInt(char *const buf, const int size, int &writePos, int32_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buf[(writePos + packedBytes) & /*mask*/(size - 1)] = b; + packedBytes++; + } + + writePos = (writePos + packedBytes) & /*mask*/(size - 1); +} + +void Buffer::packInt(int32_t x) { + packInt(mBuf, mSize, mWritePos, x); +} + +void Buffer::packInt64(int64_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + mBuf[(mWritePos + packedBytes) & mask] = b; + packedBytes++; + } + + mWritePos = (mWritePos + packedBytes) & mask; +} + +void Buffer::writeBytes(const void *const data, size_t count) { + size_t i; + for (i = 0; i < count; ++i) { + mBuf[(mWritePos + i) & mask] = static_cast(data)[i]; + } + + mWritePos = (mWritePos + i) & mask; +} + +void Buffer::writeString(const char *const str) { + const int len = strlen(str); + packInt(len); + writeBytes(str, len); +} + +void Buffer::frame() { + if (!gSessionData->mLocalCapture) { + packInt(RESPONSE_APC_DATA); + } + // Reserve space for the length + mWritePos += sizeof(int32_t); + packInt(mBufType); + packInt(mCore); +} + +void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { + packInt(MESSAGE_SUMMARY); + writeString(NEWLINE_CANARY); + packInt64(timestamp); + packInt64(uptime); + packInt64(monotonicDelta); + writeString("uname"); + writeString(uname); + writeString(""); + check(1); +} + +void Buffer::coreName(const int core, const int cpuid, const char *const name) { + if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) { + packInt(MESSAGE_CORE_NAME); + packInt(core); + packInt(cpuid); + writeString(name); + } + check(1); +} + +bool Buffer::eventHeader(const uint64_t curr_time) { + bool retval = false; + if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + packInt(0); // key of zero indicates a timestamp + packInt64(curr_time); + retval = true; + } + + return retval; +} + +bool Buffer::eventTid(const int tid) { + bool retval = false; + if (checkSpace(2 * MAXSIZE_PACK32)) { + packInt(1); // key of 1 indicates a tid + packInt(tid); + retval = true; + } + + return retval; +} + +void Buffer::event(const int32_t key, const int32_t value) { + if (checkSpace(2 * MAXSIZE_PACK32)) { + packInt(key); + packInt(value); + } +} + +void Buffer::event64(const int64_t key, const int64_t value) { + if (checkSpace(2 * MAXSIZE_PACK64)) { + packInt64(key); + packInt64(value); + } +} + +void Buffer::pea(const struct perf_event_attr *const pea, int key) { + if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { + packInt(CODE_PEA); + writeBytes(pea, pea->size); + packInt(key); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + // Don't know the real perf time so use 1 as it will work for now + check(1); +} + +void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) { + if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { + packInt(CODE_KEYS); + packInt(count); + for (int i = 0; i < count; ++i) { + packInt64(ids[i]); + packInt(keys[i]); + } + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf) { + if (checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) { + packInt(CODE_KEYS_OLD); + packInt(keyCount); + for (int i = 0; i < keyCount; ++i) { + packInt(keys[i]); + } + writeBytes(buf, bytes); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::format(const int length, const char *const format) { + if (checkSpace(MAXSIZE_PACK32 + length + 1)) { + packInt(CODE_FORMAT); + writeBytes(format, length + 1); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::maps(const int pid, const int tid, const char *const maps) { + const int mapsLen = strlen(maps) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { + packInt(CODE_MAPS); + packInt(pid); + packInt(tid); + writeBytes(maps, mapsLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) { + const int imageLen = strlen(image) + 1; + const int commLen = strlen(comm) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { + packInt(CODE_COMM); + packInt(pid); + packInt(tid); + writeBytes(image, imageLen); + writeBytes(comm, commLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::setDone() { + mIsDone = true; + commit(0); +} + +bool Buffer::isDone() const { + return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; +} diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h new file mode 100644 index 000000000000..2de1b97ac091 --- /dev/null +++ b/tools/gator/daemon/Buffer.h @@ -0,0 +1,102 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 BUFFER_H +#define BUFFER_H + +#include +#include + +#include "k/perf_event.h" + +class Sender; + +enum { + FRAME_SUMMARY = 1, + FRAME_BLOCK_COUNTER = 5, + FRAME_EXTERNAL = 10, + FRAME_PERF_ATTRS = 11, + FRAME_PERF = 12, +}; + +class Buffer { +public: + static const size_t MAXSIZE_PACK32 = 5; + static const size_t MAXSIZE_PACK64 = 10; + + Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem); + ~Buffer(); + + void write(Sender *sender); + + int bytesAvailable() const; + int contiguousSpaceAvailable() const; + void commit(const uint64_t time); + void check(const uint64_t time); + + void frame(); + + // Summary messages + void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); + void coreName(const int core, const int cpuid, const char *const name); + + // Block Counter messages + bool eventHeader(uint64_t curr_time); + bool eventTid(int tid); + void event(int32_t key, int32_t value); + void event64(int64_t key, int64_t value); + + // Perf Attrs messages + void pea(const struct perf_event_attr *const pea, int key); + void keys(const int count, const __u64 *const ids, const int *const keys); + void keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf); + void format(const int length, const char *const format); + void maps(const int pid, const int tid, const char *const maps); + void comm(const int pid, const int tid, const char *const image, const char *const comm); + + void setDone(); + bool isDone() const; + + // Prefer a new member to using these functions if possible + char *getWritePos() { return mBuf + mWritePos; } + void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } + static void packInt(char *const buf, const int size, int &writePos, int32_t x); + void packInt(int32_t x); + void packInt64(int64_t x); + void writeBytes(const void *const data, size_t count); + void writeString(const char *const str); + + static void writeLEInt(unsigned char *buf, int v) { + buf[0] = (v >> 0) & 0xFF; + buf[1] = (v >> 8) & 0xFF; + buf[2] = (v >> 16) & 0xFF; + buf[3] = (v >> 24) & 0xFF; + } + +private: + bool commitReady() const; + bool checkSpace(int bytes); + + const int32_t mCore; + const int32_t mBufType; + const int mSize; + int mReadPos; + int mWritePos; + int mCommitPos; + bool mAvailable; + bool mIsDone; + char *const mBuf; + uint64_t mCommitTime; + sem_t *const mReaderSem; + + // Intentionally unimplemented + Buffer(const Buffer &); + Buffer &operator=(const Buffer &); +}; + +#endif // BUFFER_H diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp new file mode 100644 index 000000000000..4a11415a00c9 --- /dev/null +++ b/tools/gator/daemon/CapturedXML.cpp @@ -0,0 +1,144 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "CapturedXML.h" + +#include +#include +#include + +#include "SessionData.h" +#include "Logging.h" +#include "OlyUtility.h" + +CapturedXML::CapturedXML() { +} + +CapturedXML::~CapturedXML() { +} + +mxml_node_t* CapturedXML::getTree(bool includeTime) { + mxml_node_t *xml; + mxml_node_t *captured; + mxml_node_t *target; + int x; + + xml = mxmlNewXML("1.0"); + + captured = mxmlNewElement(xml, "captured"); + mxmlElementSetAttr(captured, "version", "1"); + if (gSessionData->perf.isSetup()) { + mxmlElementSetAttr(captured, "type", "Perf"); + } + mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); + if (includeTime) { // 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 + } + } + + target = mxmlNewElement(captured, "target"); + mxmlElementSetAttr(target, "name", gSessionData->mCoreName); + mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); + mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); + + if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { + mxmlElementSetAttr(target, "supports_live", "yes"); + } + + if (gSessionData->mLocalCapture) { + mxmlElementSetAttr(target, "local_capture", "yes"); + } + + mxml_node_t *counters = NULL; + for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { + const Counter & counter = gSessionData->mCounters[x]; + if (counter.isEnabled()) { + if (counters == NULL) { + counters = mxmlNewElement(captured, "counters"); + } + mxml_node_t *const node = mxmlNewElement(counters, "counter"); + mxmlElementSetAttrf(node, "key", "0x%x", counter.getKey()); + mxmlElementSetAttr(node, "type", counter.getType()); + if (counter.getEvent() != -1) { + mxmlElementSetAttrf(node, "event", "0x%x", counter.getEvent()); + } + if (counter.getCount() > 0) { + mxmlElementSetAttrf(node, "count", "%d", counter.getCount()); + } + if (counter.getCores() > 0) { + mxmlElementSetAttrf(node, "cores", "%d", counter.getCores()); + } + } + } + + return xml; +} + +char* CapturedXML::getXML(bool includeTime) { + char* xml_string; + mxml_node_t *xml = getTree(includeTime); + xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + return xml_string; +} + +void CapturedXML::write(char* path) { + char file[PATH_MAX]; + + // Set full path + snprintf(file, PATH_MAX, "%s/captured.xml", path); + + char* xml = getXML(true); + if (util->writeToDisk(file, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(xml); +} + +// 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/CapturedXML.h b/tools/gator/daemon/CapturedXML.h new file mode 100644 index 000000000000..ed08c44bc3ff --- /dev/null +++ b/tools/gator/daemon/CapturedXML.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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(bool includeTime); // the string should be freed by the caller + void write(char* path); +private: + mxml_node_t* getTree(bool includeTime); +}; + +const char * mxmlWhitespaceCB(mxml_node_t *node, int where); + +#endif //__CAPTURED_XML_H__ diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp new file mode 100644 index 000000000000..1901ecc6a724 --- /dev/null +++ b/tools/gator/daemon/Child.cpp @@ -0,0 +1,346 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "Child.h" + +#include +#include +#include +#include +#include + +#include "Logging.h" +#include "CapturedXML.h" +#include "SessionData.h" +#include "LocalCapture.h" +#include "Sender.h" +#include "OlyUtility.h" +#include "OlySocket.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" +#include "Driver.h" +#include "PerfSource.h" +#include "DriverSource.h" +#include "ExternalSource.h" +#include "UserSpaceSource.h" + +static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads +static Source *primarySource = NULL; +static Source *externalSource = NULL; +static Source *userSpaceSource = NULL; +static Sender* sender = NULL; // Shared by Child.cpp and spawned threads +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); + } + + // Ensure all data is flushed + child->socket->shutdownConnection(); + + // 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 +static 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 || !primarySource) { + exit(1); + } else { + child->endSession(); + alarm(5); // Safety net in case endSession does not complete within 5 seconds + } +} + +static void *durationThread(void *) { + prctl(PR_SET_NAME, (unsigned long)&"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; +} + +static void *stopThread(void *) { + OlySocket* socket = child->socket; + + prctl(PR_SET_NAME, (unsigned long)&"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 + unsigned char header[5]; + const int result = socket->receiveNBytes((char*)&header, sizeof(header)); + const char type = header[0]; + const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); + if (result == -1) { + child->endSession(); + } else if (result > 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 (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; +} + +static void *senderThread(void *) { + char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; + + sem_post(&senderThreadStarted); + prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); + sem_wait(&haltPipeline); + + while (!primarySource->isDone() || + !externalSource->isDone() || + (userSpaceSource != NULL && !userSpaceSource->isDone())) { + sem_wait(&senderSem); + + primarySource->write(sender); + externalSource->write(sender); + if (userSpaceSource != NULL) { + userSpaceSource->write(sender); + } + } + + // 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); + sem_init(&senderSem, 0, 0); +} + +void Child::endSession() { + gSessionData->mSessionIsActive = false; + primarySource->interrupt(); + externalSource->interrupt(); + if (userSpaceSource != NULL) { + userSpaceSource->interrupt(); + } + sem_post(&haltPipeline); +} + +void Child::run() { + LocalCapture* localCapture = NULL; + pthread_t durationThreadID, stopThreadID, senderThreadID; + + prctl(PR_SET_NAME, (unsigned long)&"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 + { ConfigurationXML configuration; } + + // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated + if (!gSessionData->perf.isSetup()) { + primarySource = new DriverSource(&senderSem, &startProfile); + } else { + primarySource = new PerfSource(&senderSem, &startProfile); + } + + // Initialize all drivers + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->resetCounters(); + } + + // Set up counters using the associated driver's setup function + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + Counter & counter = gSessionData->mCounters[i]; + if (counter.isEnabled()) { + counter.getDriver()->setupCounter(counter); + } + } + + // 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); + localCapture->copyImages(gSessionData->mImages); + localCapture->write(xmlString); + sender->createDataFile(gSessionData->mAPCDir); + free(xmlString); + } + + // Must be after session XML is parsed + if (!primarySource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + + // 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; + } + + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + externalSource->start(); + + if (gSessionData->hwmon.countersEnabled() || gSessionData->fsDriver.countersEnabled()) { + userSpaceSource = new UserSpaceSource(&senderSem); + if (!userSpaceSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + userSpaceSource->start(); + } + + if (!thread_creation_success) { + logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); + handleException(); + } + + // Wait until thread has started + sem_wait(&senderThreadStarted); + + // Start profiling + primarySource->run(); + + if (userSpaceSource != NULL) { + userSpaceSource->join(); + } + externalSource->join(); + + // 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 userSpaceSource; + delete externalSource; + delete primarySource; + delete sender; + delete localCapture; +} diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h new file mode 100644 index 000000000000..a306a7760819 --- /dev/null +++ b/tools/gator/daemon/Child.h @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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__ + +class OlySocket; + +class Child { +public: + Child(); + Child(OlySocket* sock, int numConnections); + ~Child(); + void run(); + OlySocket *socket; + void endSession(); + int numExceptions; +private: + int mNumConnections; + + void initialization(); + + // Intentionally unimplemented + Child(const Child &); + Child &operator=(const Child &); +}; + +#endif //__CHILD_H__ diff --git a/tools/gator/daemon/Config.h b/tools/gator/daemon/Config.h new file mode 100644 index 000000000000..6f5e2aae50e1 --- /dev/null +++ b/tools/gator/daemon/Config.h @@ -0,0 +1,17 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 CONFIG_H +#define CONFIG_H + +#define ARRAY_LENGTH(A) static_cast(sizeof(A)/sizeof((A)[0])) + +#define MAX_PERFORMANCE_COUNTERS 50 +#define NR_CPUS 16 + +#endif // CONFIG_H diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp new file mode 100644 index 000000000000..6590dd389196 --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -0,0 +1,217 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "ConfigurationXML.h" + +#include +#include +#include + +#include "Driver.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +static const char* ATTR_COUNTER = "counter"; +static const char* ATTR_REVISION = "revision"; +static const char* ATTR_EVENT = "event"; +static const char* ATTR_COUNT = "count"; +static const char* ATTR_CORES = "cores"; + +ConfigurationXML::ConfigurationXML() { + const char * configuration_xml; + unsigned int configuration_xml_len; + getDefaultConfigurationXml(configuration_xml, configuration_xml_len); + + char path[PATH_MAX]; + + getPath(path); + mConfigurationXML = util->readFromDisk(path); + + for (int retryCount = 0; retryCount < 2; ++retryCount) { + 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; + } + + int ret = parse(mConfigurationXML); + if (ret == 1) { + remove(); + + // Free the current configuration and reload + free((void*)mConfigurationXML); + mConfigurationXML = NULL; + continue; + } + + break; + } + + validate(); +} + +ConfigurationXML::~ConfigurationXML() { + if (mConfigurationXML) { + free((void*)mConfigurationXML); + } +} + +int ConfigurationXML::parse(const char* configurationXML) { + mxml_node_t *tree, *node; + int ret; + + // clear counter overflow + gSessionData->mCounterOverflow = 0; + gSessionData->mIsEBS = false; + mIndex = 0; + + // disable all counters prior to parsing the configuration xml + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + gSessionData->mCounters[i].setEnabled(false); + } + + 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++) { + const Counter & counter = gSessionData->mCounters[i]; + if (counter.isEnabled()) { + if (strcmp(counter.getType(), "") == 0) { + logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n event=%d\n", counter.getType(), counter.getEvent()); + handleException(); + } + + // iterate through the remaining enabled performance counters + for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { + const Counter & counter2 = gSessionData->mCounters[j]; + if (counter2.isEnabled()) { + // check if the types are the same + if (strcmp(counter.getType(), counter2.getType()) == 0) { + logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType()); + handleException(); + } + } + } + } + } +} + +#define CONFIGURATION_REVISION 3 +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 + } + + // A revision >= CONFIGURATION_REVISION is okay + // Greater than can occur when Streamline is newer than gator + + return 0; +} + +void ConfigurationXML::configurationTag(mxml_node_t *node) { + // handle all other performance counters + if (mIndex >= MAX_PERFORMANCE_COUNTERS) { + mIndex++; + gSessionData->mCounterOverflow = mIndex; + return; + } + + // read attributes + Counter & counter = gSessionData->mCounters[mIndex]; + counter.clear(); + if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); + if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); + if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); + if (mxmlElementGetAttr(node, ATTR_CORES)) counter.setCores(strtol(mxmlElementGetAttr(node, ATTR_CORES), NULL, 10)); + if (counter.getCount() > 0) { + gSessionData->mIsEBS = true; + } + counter.setEnabled(true); + + // Associate a driver with each counter + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + if (driver->claimCounter(counter)) { + if (counter.getDriver() != NULL) { + logg->logError(__FILE__, __LINE__, "More than one driver has claimed %s:%i", counter.getType(), counter.getEvent()); + handleException(); + } + counter.setDriver(driver); + } + } + + // If no driver is associated with the counter, disable it + if (counter.getDriver() == NULL) { + logg->logMessage("No driver has claimed %s:%i", counter.getType(), counter.getEvent()); + counter.setEnabled(false); + } + + if (counter.isEnabled()) { + // update counter index + mIndex++; + } +} + +void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { +#include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len + xml = (const char *)defaults_xml; + len = defaults_xml_len; +} + +void ConfigurationXML::getPath(char* path) { + 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); + } +} + +void ConfigurationXML::remove() { + char path[PATH_MAX]; + getPath(path); + + 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"); +} diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h new file mode 100644 index 000000000000..efa415e508b6 --- /dev/null +++ b/tools/gator/daemon/ConfigurationXML.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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: + static void getDefaultConfigurationXml(const char * & xml, unsigned int & len); + static void getPath(char* path); + static void remove(); + + 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); + + // Intentionally unimplemented + ConfigurationXML(const ConfigurationXML &); + ConfigurationXML &operator=(const ConfigurationXML &); +}; + +#endif // COUNTERS_H diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h new file mode 100644 index 000000000000..5202aa046362 --- /dev/null +++ b/tools/gator/daemon/Counter.h @@ -0,0 +1,65 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 COUNTER_H +#define COUNTER_H + +#include + +class Driver; + +class Counter { +public: + static const size_t MAX_STRING_LEN = 80; + static const size_t MAX_DESCRIPTION_LEN = 400; + + Counter () { + clear(); + } + + void clear () { + mType[0] = '\0'; + mEnabled = false; + mEvent = -1; + mCount = 0; + mCores = -1; + mKey = 0; + mDriver = NULL; + } + + void setType(const char *const type) { strncpy(mType, type, sizeof(mType)); mType[sizeof(mType) - 1] = '\0'; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + void setEvent(const int event) { mEvent = event; } + void setCount(const int count) { mCount = count; } + void setCores(const int cores) { mCores = cores; } + void setKey(const int key) { mKey = key; } + void setDriver(Driver *const driver) { mDriver = driver; } + + const char *getType() const { return mType;} + bool isEnabled() const { return mEnabled; } + int getEvent() const { return mEvent; } + int getCount() const { return mCount; } + int getCores() const { return mCores; } + int getKey() const { return mKey; } + Driver *getDriver() const { return mDriver; } + +private: + // Intentionally unimplemented + Counter(const Counter &); + Counter & operator=(const Counter &); + + char mType[MAX_STRING_LEN]; + bool mEnabled; + int mEvent; + int mCount; + int mCores; + int mKey; + Driver *mDriver; +}; + +#endif // COUNTER_H diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp new file mode 100644 index 000000000000..09e040162912 --- /dev/null +++ b/tools/gator/daemon/Driver.cpp @@ -0,0 +1,15 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "Driver.h" + +Driver *Driver::head = NULL; + +Driver::Driver() : next(head) { + head = this; +} diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h new file mode 100644 index 000000000000..e5ed7b6c1295 --- /dev/null +++ b/tools/gator/daemon/Driver.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 DRIVER_H +#define DRIVER_H + +#include "mxml/mxml.h" + +class Counter; + +class Driver { +public: + static Driver *getHead() { return head; } + + virtual ~Driver() {} + + // Returns true if this driver can manage the counter + virtual bool claimCounter(const Counter &counter) const = 0; + // Clears and disables all counters + virtual void resetCounters() = 0; + // Enables and prepares the counter for capture + virtual void setupCounter(Counter &counter) = 0; + + // Emits available counters + virtual int writeCounters(mxml_node_t *root) const = 0; + // Emits possible dynamically generated events/counters + virtual void writeEvents(mxml_node_t *) const {} + + Driver *getNext() const { return next; } + +protected: + Driver (); + +private: + static Driver *head; + Driver *next; + + // Intentionally unimplemented + Driver(const Driver &); + Driver &operator=(const Driver &); +}; + +#endif // DRIVER_H diff --git a/tools/gator/daemon/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp new file mode 100644 index 000000000000..11d3095ef6d2 --- /dev/null +++ b/tools/gator/daemon/DriverSource.cpp @@ -0,0 +1,321 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 __STDC_FORMAT_MACROS + +#include "DriverSource.h" + +#include +#include +#include +#include + +#include "Buffer.h" +#include "Child.h" +#include "DynBuf.h" +#include "Fifo.h" +#include "Logging.h" +#include "Proc.h" +#include "Sender.h" +#include "SessionData.h" + +extern Child *child; + +DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mBuffer(NULL), mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { + int driver_version = 0; + + mBuffer = new Buffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem); + 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(); + } + } + + 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; + } + + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + handleException(); + } +} + +DriverSource::~DriverSource() { + delete mFifo; + + // 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); + } +} + +bool DriverSource::prepare() { + // 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, mBufferSize); + mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); + + return true; +} + +void DriverSource::bootstrapThread() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-bootstrap", 0, 0, 0); + + DynBuf printb; + DynBuf b1; + DynBuf b2; + DynBuf b3; + + if (!readProc(mBuffer, false, &printb, &b1, &b2, &b3)) { + logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); + handleException(); + } + + mBuffer->commit(1); + mBuffer->setDone(); +} + +void *DriverSource::bootstrapThreadStatic(void *arg) { + static_cast(arg)->bootstrapThread(); + return NULL; +} + +void DriverSource::run() { + // Get the initial pointer to the collect buffer + char *collectBuffer = mFifo->start(); + int bytesCollected = 0; + + logg->logMessage("********** Profiling started **********"); + + // 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(); + } + + // Set the live rate + if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate"); + 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); + + sem_post(mStartProfile); + + pthread_t bootstrapThreadID; + if (pthread_create(&bootstrapThreadID, NULL, bootstrapThreadStatic, this) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to start the gator_bootstrap thread"); + handleException(); + } + + // Collect Data + do { + // This command will stall until data is received from the driver + // Calls event_buffer_read in the driver + errno = 0; + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + + // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data + if (bytesCollected == -1 && errno == EINTR) { + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + } + + // return the total bytes written + logg->logMessage("Driver read of %d bytes", bytesCollected); + + // In one shot mode, stop collection once all the buffers are filled + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { + logg->logMessage("One shot"); + child->endSession(); + } + } + collectBuffer = mFifo->write(bytesCollected); + } while (bytesCollected > 0); + + logg->logMessage("Exit collect data loop"); + + pthread_join(bootstrapThreadID, NULL); +} + +void DriverSource::interrupt() { + // This command should cause the read() function in collect() to return and stop the driver from profiling + if (writeDriver("/dev/gator/enable", "0") != 0) { + logg->logMessage("Stopping kernel failed"); + } +} + +bool DriverSource::isDone() { + return mLength <= 0 && (mBuffer == NULL || mBuffer->isDone()); +} + +void DriverSource::write(Sender *sender) { + char *data = mFifo->read(&mLength); + if (data != NULL) { + sender->writeData(data, mLength, RESPONSE_APC_DATA); + mFifo->release(); + // Assume the summary packet is in the first block received from the driver + gSessionData->mSentSummary = true; + } + if (mBuffer != NULL && !mBuffer->isDone()) { + mBuffer->write(sender); + if (mBuffer->isDone()) { + Buffer *buf = mBuffer; + mBuffer = NULL; + delete buf; + } + } +} + +int DriverSource::readIntDriver(const char *fullpath, int *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { + return -1; + } + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { + return -1; + } + data[bytes] = '\0'; + + char *endptr; + errno = 0; + *value = strtol(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + + return 0; +} + +int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { + return -1; + } + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { + return -1; + } + data[bytes] = '\0'; + + char *endptr; + errno = 0; + *value = strtoll(data, &endptr, 10); + if (errno != 0 || (*endptr != '\n' && *endptr != '\0')) { + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + + return 0; +} + +int DriverSource::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 DriverSource::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 DriverSource::writeDriver(const char *path, int64_t value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%" PRIi64, value); + return writeDriver(path, data); +} + +int DriverSource::writeReadDriver(const char *path, int *value) { + if (writeDriver(path, *value) || readIntDriver(path, value)) { + return -1; + } + return 0; +} + +int DriverSource::writeReadDriver(const char *path, int64_t *value) { + if (writeDriver(path, *value) || readInt64Driver(path, value)) { + return -1; + } + return 0; +} diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h new file mode 100644 index 000000000000..ec27b0815bbf --- /dev/null +++ b/tools/gator/daemon/DriverSource.h @@ -0,0 +1,57 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 DRIVERSOURCE_H +#define DRIVERSOURCE_H + +#include +#include + +#include "Source.h" + +class Buffer; +class Fifo; + +class DriverSource : public Source { +public: + DriverSource(sem_t *senderSem, sem_t *startProfile); + ~DriverSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + + static int readIntDriver(const char *fullpath, int *value); + static int readInt64Driver(const char *fullpath, int64_t *value); + static int writeDriver(const char *fullpath, const char *data); + static int writeDriver(const char *path, int value); + static int writeDriver(const char *path, int64_t value); + static int writeReadDriver(const char *path, int *value); + static int writeReadDriver(const char *path, int64_t *value); + +private: + static void *bootstrapThreadStatic(void *arg); + void bootstrapThread(); + + Buffer *mBuffer; + Fifo *mFifo; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mBufferSize; + int mBufferFD; + int mLength; + + // Intentionally unimplemented + DriverSource(const DriverSource &); + DriverSource &operator=(const DriverSource &); +}; + +#endif // DRIVERSOURCE_H diff --git a/tools/gator/daemon/DynBuf.cpp b/tools/gator/daemon/DynBuf.cpp new file mode 100644 index 000000000000..6f92b336ae19 --- /dev/null +++ b/tools/gator/daemon/DynBuf.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "DynBuf.h" + +#include +#include +#include +#include +#include + +#include "Logging.h" + +// Pick an aggressive size as buffer is primarily used for disk IO +#define MIN_BUFFER_FREE (1 << 12) + +int DynBuf::resize(const size_t minCapacity) { + size_t scaledCapacity = 2 * capacity; + if (scaledCapacity < minCapacity) { + scaledCapacity = minCapacity; + } + if (scaledCapacity < 2 * MIN_BUFFER_FREE) { + scaledCapacity = 2 * MIN_BUFFER_FREE; + } + capacity = scaledCapacity; + + buf = static_cast(realloc(buf, capacity)); + if (buf == NULL) { + return -errno; + } + + return 0; +} + +bool DynBuf::read(const char *const path) { + int result = false; + + const int fd = open(path, O_RDONLY); + if (fd < 0) { + logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + length = 0; + + for (;;) { + const size_t minCapacity = length + MIN_BUFFER_FREE + 1; + if (capacity < minCapacity) { + if (resize(minCapacity) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } else if (bytes == 0) { + break; + } + length += bytes; + } + + buf[length] = '\0'; + result = true; + + fail: + close(fd); + + return result; +} + +int DynBuf::readlink(const char *const path) { + ssize_t bytes = MIN_BUFFER_FREE; + + for (;;) { + if (static_cast(bytes) >= capacity) { + const int err = resize(2 * bytes); + if (err != 0) { + return err; + } + } + bytes = ::readlink(path, buf, capacity); + if (bytes < 0) { + return -errno; + } else if (static_cast(bytes) < capacity) { + break; + } + } + + length = bytes; + buf[bytes] = '\0'; + + return 0; +} + +bool DynBuf::printf(const char *format, ...) { + va_list ap; + + if (capacity <= 0) { + if (resize(2 * MIN_BUFFER_FREE) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + va_start(ap, format); + int bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (static_cast(bytes) > capacity) { + if (resize(bytes + 1) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + va_start(ap, format); + bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + length = bytes; + + return true; +} diff --git a/tools/gator/daemon/DynBuf.h b/tools/gator/daemon/DynBuf.h new file mode 100644 index 000000000000..2f4554ab2e49 --- /dev/null +++ b/tools/gator/daemon/DynBuf.h @@ -0,0 +1,52 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 DYNBUF_H +#define DYNBUF_H + +#include + +class DynBuf { +public: + DynBuf() : capacity(0), length(0), buf(NULL) {} + ~DynBuf() { + reset(); + } + + inline void reset() { + capacity = 0; + length = 0; + if (buf != NULL) { + free(buf); + buf = NULL; + } + } + + bool read(const char *const path); + // On error instead of printing the error and returning false, this returns -errno + int readlink(const char *const path); + __attribute__ ((format(printf, 2, 3))) + bool printf(const char *format, ...); + + size_t getLength() const { return length; } + const char *getBuf() const { return buf; } + char *getBuf() { return buf; } + +private: + int resize(const size_t minCapacity); + + size_t capacity; + size_t length; + char *buf; + + // Intentionally undefined + DynBuf(const DynBuf &); + DynBuf &operator=(const DynBuf &); +}; + +#endif // DYNBUF_H diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp new file mode 100644 index 000000000000..cf0192ef671f --- /dev/null +++ b/tools/gator/daemon/EventsXML.cpp @@ -0,0 +1,76 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "EventsXML.h" + +#include "CapturedXML.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +mxml_node_t *EventsXML::getTree() { +#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len + char path[PATH_MAX]; + mxml_node_t *xml; + FILE *fl; + + // Avoid unused variable warning + (void)events_xml_len; + + // Load the provided or default events xml + if (gSessionData->mEventsXMLPath) { + strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + } + fl = fopen(path, "r"); + if (fl) { + xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); + fclose(fl); + } else { + logg->logMessage("Unable to locate events.xml, using default"); + xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); + } + + return xml; +} + +char *EventsXML::getXML() { + mxml_node_t *xml = getTree(); + + // Add dynamic events from the drivers + mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); + if (!events) { + logg->logMessage("Unable to find node in the events.xml"); + handleException(); + } + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->writeEvents(events); + } + + char *string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + + return string; +} + +void EventsXML::write(const char *path) { + char file[PATH_MAX]; + + // Set full path + snprintf(file, PATH_MAX, "%s/events.xml", path); + + char *buf = getXML(); + if (util->writeToDisk(file, buf) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(buf); +} diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h new file mode 100644 index 000000000000..ff7a02fd3c78 --- /dev/null +++ b/tools/gator/daemon/EventsXML.h @@ -0,0 +1,21 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 EVENTS_XML +#define EVENTS_XML + +#include "mxml/mxml.h" + +class EventsXML { +public: + mxml_node_t *getTree(); + char *getXML(); + void write(const char* path); +}; + +#endif // EVENTS_XML diff --git a/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp new file mode 100644 index 000000000000..b6ec301d0c08 --- /dev/null +++ b/tools/gator/daemon/ExternalSource.cpp @@ -0,0 +1,215 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "ExternalSource.h" + +#include +#include +#include + +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +static const char MALI_VIDEO[] = "\0mali-video"; +static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup"; +static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n"; + +static bool setNonblock(const int fd) { + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags < 0) { + logg->logMessage("fcntl getfl failed"); + return false; + } + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) { + logg->logMessage("fcntl setfl failed"); + return false; + } + + return true; +} + +ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mInterruptFd(-1), mMveUds(-1) { + sem_init(&mBufferSem, 0, 0); +} + +ExternalSource::~ExternalSource() { +} + +void ExternalSource::waitFor(const uint64_t currTime, const int bytes) { + while (mBuffer.bytesAvailable() <= bytes) { + mBuffer.check(currTime); + sem_wait(&mBufferSem); + } +} + +void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) { + if (!setNonblock(fd)) { + logg->logError(__FILE__, __LINE__, "Unable to set nonblock on fh"); + handleException(); + } + + if (!mMonitor.add(fd)) { + logg->logError(__FILE__, __LINE__, "Unable to add fh to monitor"); + handleException(); + } + + // Write the handshake to the circular buffer + waitFor(1, Buffer::MAXSIZE_PACK32 + 4 + size - 1); + mBuffer.packInt(fd); + mBuffer.writeLEInt((unsigned char *)mBuffer.getWritePos(), size - 1); + mBuffer.advanceWrite(4); + mBuffer.writeBytes(handshake, size - 1); +} + +bool ExternalSource::connectMve() { + if (!gSessionData->maliVideo.countersEnabled()) { + return true; + } + + mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO)); + if (mMveUds < 0) { + return false; + } + + if (!gSessionData->maliVideo.start(mMveUds)) { + return false; + } + + configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1)); + + return true; +} + +bool ExternalSource::prepare() { + if (!mMonitor.init() || !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd())) { + return false; + } + + connectMve(); + + return true; +} + +void ExternalSource::run() { + int pipefd[2]; + + prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0); + + if (pipe(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + handleException(); + } + + while (gSessionData->mSessionIsActive) { + struct epoll_event events[16]; + // Clear any pending sem posts + while (sem_trywait(&mBufferSem) == 0); + int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); + } + + const uint64_t currTime = getTime(); + + for (int i = 0; i < ready; ++i) { + const int fd = events[i].data.fd; + if (fd == mMveStartupUds.getFd()) { + // Mali Video Engine says it's alive + int client = mMveStartupUds.acceptConnection(); + // Don't read from this connection, establish a new connection to Mali-V500 + close(client); + if (!connectMve()) { + logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection"); + handleException(); + } + } else if (fd == pipefd[0]) { + // Means interrupt has been called and mSessionIsActive should be reread + } else { + while (true) { + waitFor(currTime, Buffer::MAXSIZE_PACK32 + 4); + + mBuffer.packInt(fd); + char *const bytesPos = mBuffer.getWritePos(); + mBuffer.advanceWrite(4); + const int contiguous = mBuffer.contiguousSpaceAvailable(); + const int bytes = read(fd, mBuffer.getWritePos(), contiguous); + if (bytes < 0) { + if (errno == EAGAIN) { + // Nothing left to read, and Buffer convention dictates that writePos can't go backwards + mBuffer.writeLEInt((unsigned char *)bytesPos, 0); + break; + } + // Something else failed, close the socket + mBuffer.writeLEInt((unsigned char *)bytesPos, -1); + close(fd); + break; + } else if (bytes == 0) { + // The other side is closed + mBuffer.writeLEInt((unsigned char *)bytesPos, -1); + close(fd); + break; + } + + mBuffer.writeLEInt((unsigned char *)bytesPos, bytes); + mBuffer.advanceWrite(bytes); + + // Short reads also mean nothing is left to read + if (bytes < contiguous) { + break; + } + } + } + } + + // Only call mBufferCheck once per iteration + mBuffer.check(currTime); + } + + mBuffer.setDone(); + + mInterruptFd = -1; + close(pipefd[0]); + close(pipefd[1]); +} + +void ExternalSource::interrupt() { + if (mInterruptFd >= 0) { + int8_t c = 0; + // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread + if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { + logg->logError(__FILE__, __LINE__, "write failed"); + handleException(); + } + } +} + +bool ExternalSource::isDone() { + return mBuffer.isDone(); +} + +void ExternalSource::write(Sender *sender) { + // Don't send external data until the summary packet is sent so that monotonic delta is available + if (!gSessionData->mSentSummary) { + return; + } + if (!mBuffer.isDone()) { + mBuffer.write(sender); + sem_post(&mBufferSem); + } +} diff --git a/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h new file mode 100644 index 000000000000..2e7ed27df255 --- /dev/null +++ b/tools/gator/daemon/ExternalSource.h @@ -0,0 +1,49 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 EXTERNALSOURCE_H +#define EXTERNALSOURCE_H + +#include + +#include "Buffer.h" +#include "Monitor.h" +#include "OlySocket.h" +#include "Source.h" + +// Unix domain socket counters from external sources like graphics drivers +class ExternalSource : public Source { +public: + ExternalSource(sem_t *senderSem); + ~ExternalSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + void waitFor(const uint64_t currTime, const int bytes); + void configureConnection(const int fd, const char *const handshake, size_t size); + bool connectMve(); + + sem_t mBufferSem; + Buffer mBuffer; + Monitor mMonitor; + OlyServerSocket mMveStartupUds; + int mInterruptFd; + int mMveUds; + + // Intentionally unimplemented + ExternalSource(const ExternalSource &); + ExternalSource &operator=(const ExternalSource &); +}; + +#endif // EXTERNALSOURCE_H diff --git a/tools/gator/daemon/FSDriver.cpp b/tools/gator/daemon/FSDriver.cpp new file mode 100644 index 000000000000..40c8df1af222 --- /dev/null +++ b/tools/gator/daemon/FSDriver.cpp @@ -0,0 +1,212 @@ +/** + * Copyright (C) ARM Limited 2014. 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 "FSDriver.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "Counter.h" +#include "DriverSource.h" +#include "Logging.h" +#include "SessionData.h" + +class FSCounter { +public: + FSCounter(FSCounter *next, char *name, const char *regex); + ~FSCounter(); + + FSCounter *getNext() const { return next; } + int getKey() const { return key; } + bool isEnabled() const { return enabled; } + void setEnabled(const bool enabled) { this->enabled = enabled; } + const char *getName() const { return name; } + int64_t read(); + +private: + FSCounter *const next; + regex_t reg; + char *name; + const int key; + int enabled : 1, + useRegex : 1; + + // Intentionally unimplemented + FSCounter(const FSCounter &); + FSCounter &operator=(const FSCounter &); +}; + +FSCounter::FSCounter(FSCounter *next, char *name, const char *regex) : next(next), name(name), key(getEventKey()), enabled(false), useRegex(regex != NULL) { + if (useRegex) { + int result = regcomp(®, regex, REG_EXTENDED); + if (result != 0) { + char buf[128]; + regerror(result, ®, buf, sizeof(buf)); + logg->logError(__FILE__, __LINE__, "Invalid regex '%s': %s", regex, buf); + handleException(); + } + } +} + +FSCounter::~FSCounter() { + free(name); + if (useRegex) { + regfree(®); + } +} + +int64_t FSCounter::read() { + int64_t value; + if (useRegex) { + char buf[4096]; + size_t pos = 0; + const int fd = open(name, O_RDONLY); + if (fd < 0) { + goto fail; + } + while (pos < sizeof(buf) - 1) { + const ssize_t bytes = ::read(fd, buf + pos, sizeof(buf) - pos - 1); + if (bytes < 0) { + goto fail; + } else if (bytes == 0) { + break; + } + pos += bytes; + } + close(fd); + buf[pos] = '\0'; + + regmatch_t match[2]; + int result = regexec(®, buf, 2, match, 0); + if (result != 0) { + regerror(result, ®, buf, sizeof(buf)); + logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, buf); + handleException(); + } + + if (match[1].rm_so < 0) { + logg->logError(__FILE__, __LINE__, "Parsing %s failed", name); + handleException(); + } + char *endptr; + errno = 0; + value = strtoll(buf + match[1].rm_so, &endptr, 0); + if (errno != 0) { + logg->logError(__FILE__, __LINE__, "Parsing %s failed: %s", name, strerror(errno)); + handleException(); + } + } else { + if (DriverSource::readInt64Driver(name, &value) != 0) { + goto fail; + } + } + return value; + + fail: + logg->logError(__FILE__, __LINE__, "Unable to read %s", name); + handleException(); +} + +FSDriver::FSDriver() : counters(NULL) { +} + +FSDriver::~FSDriver() { + while (counters != NULL) { + FSCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } +} + +void FSDriver::setup(mxml_node_t *const xml) { + // fs driver does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if ((counter != NULL) && (counter[0] == '/')) { + const char *regex = mxmlElementGetAttr(node, "regex"); + counters = new FSCounter(counters, strdup(counter), regex); + } + } +} + +FSCounter *FSDriver::findCounter(const Counter &counter) const { + for (FSCounter * fsCounter = counters; fsCounter != NULL; fsCounter = fsCounter->getNext()) { + if (strcmp(fsCounter->getName(), counter.getType()) == 0) { + return fsCounter; + } + } + + return NULL; +} + +bool FSDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool FSDriver::countersEnabled() const { + for (FSCounter *counter = counters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void FSDriver::resetCounters() { + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void FSDriver::setupCounter(Counter &counter) { + FSCounter *const fsCounter = findCounter(counter); + if (fsCounter == NULL) { + counter.setEnabled(false); + return; + } + fsCounter->setEnabled(true); + counter.setKey(fsCounter->getKey()); +} + +int FSDriver::writeCounters(mxml_node_t *root) const { + int count = 0; + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (access(counter->getName(), R_OK) == 0) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + } + + return count; +} + +void FSDriver::start() { +} + +void FSDriver::read(Buffer * const buffer) { + for (FSCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + buffer->event(counter->getKey(), counter->read()); + } +} diff --git a/tools/gator/daemon/FSDriver.h b/tools/gator/daemon/FSDriver.h new file mode 100644 index 000000000000..ef3955362331 --- /dev/null +++ b/tools/gator/daemon/FSDriver.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2014. 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 FSDRIVER_H +#define FSDRIVER_H + +#include "Driver.h" + +class Buffer; +class FSCounter; + +class FSDriver : public Driver { +public: + FSDriver(); + ~FSDriver(); + + void setup(mxml_node_t *const xml); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + void start(); + void read(Buffer * buffer); + +private: + FSCounter *findCounter(const Counter &counter) const; + + FSCounter *counters; + + // Intentionally unimplemented + FSDriver(const FSDriver &); + FSDriver &operator=(const FSDriver &); +}; + +#endif // FSDRIVER_H diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp new file mode 100644 index 000000000000..f672e92a6807 --- /dev/null +++ b/tools/gator/daemon/Fifo.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "Fifo.h" + +#include +#ifdef WIN32 +#define valloc malloc +#endif + +#include "Logging.h" + +// 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, sem_t* readerSem) { + mWrite = mRead = mReadCommit = mRaggedEnd = 0; + mWrapThreshold = bufferSize; + mSingleBufferSize = singleBufferSize; + mReaderSem = readerSem; + 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)) { + logg->logError(__FILE__, __LINE__, "sem_init() failed"); + handleException(); + } +} + +Fifo::~Fifo() { + free(mBuffer); + sem_destroy(&mWaitForSpaceSem); +} + +int Fifo::numBytesFilled() const { + return mWrite - mRead + mRaggedEnd; +} + +char* Fifo::start() const { + return mBuffer; +} + +bool Fifo::isEmpty() const { + return mRead == mWrite && mRaggedEnd == 0; +} + +bool Fifo::isFull() const { + return willFill(0); +} + +// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer +// 'full' means there is less than singleBufferSize bytes available contiguously; it does not mean there are zero bytes available +bool Fifo::willFill(int additional) const { + 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(mReaderSem); + + // wait for space + while (isFull()) { + sem_wait(&mWaitForSpaceSem); + } + + return &mBuffer[mWrite]; +} + +void Fifo::release() { + // 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); +} + +// This function will return null if no data is available +char* Fifo::read(int *const length) { + // wait for data + if (isEmpty() && !mEnd) { + return NULL; + } + + // 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 000000000000..bdda3f549b50 --- /dev/null +++ b/tools/gator/daemon/Fifo.h @@ -0,0 +1,48 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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__ + +#ifdef WIN32 +#include +#define sem_t HANDLE +#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL) +#define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) +#define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) +#define sem_destroy(sem) CloseHandle(*(sem)) +#else +#include +#endif + +class Fifo { +public: + Fifo(int singleBufferSize, int totalBufferSize, sem_t* readerSem); + ~Fifo(); + int numBytesFilled() const; + bool isEmpty() const; + bool isFull() const; + bool willFill(int additional) const; + char* start() const; + char* write(int length); + void release(); + char* read(int *const length); + +private: + int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold; + sem_t mWaitForSpaceSem; + sem_t* mReaderSem; + char* mBuffer; + bool mEnd; + + // Intentionally unimplemented + Fifo(const Fifo &); + Fifo &operator=(const Fifo &); +}; + +#endif //__FIFO_H__ diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp new file mode 100644 index 000000000000..e44424743ef0 --- /dev/null +++ b/tools/gator/daemon/Hwmon.cpp @@ -0,0 +1,352 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "Hwmon.h" + +#include "libsensors/sensors.h" + +#include "Buffer.h" +#include "Counter.h" +#include "Logging.h" +#include "SessionData.h" + +class HwmonCounter { +public: + HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature); + ~HwmonCounter(); + + HwmonCounter *getNext() const { return next; } + int getKey() const { return key; } + bool isEnabled() const { return enabled; } + const char *getName() const { return name; } + const char *getLabel() const { return label; } + const char *getTitle() const { return title; } + bool isDuplicate() const { return duplicate; } + const char *getDisplay() const { return display; } + const char *getCounterClass() const { return counter_class; } + const char *getUnit() const { return unit; } + int getModifier() const { return modifier; } + + void setEnabled(const bool enabled) { + this->enabled = enabled; + // canRead will clear enabled if the counter is not readable + canRead(); + } + + double read(); + bool canRead(); + +private: + void init(const sensors_chip_name *chip, const sensors_feature *feature); + + HwmonCounter *const next; + const int key; + int polled : 1, + readable : 1, + enabled : 1, + monotonic: 1, + duplicate : 1; + + const sensors_chip_name *chip; + const sensors_feature *feature; + + char *name; + char *label; + const char *title; + const char *display; + const char *counter_class; + const char *unit; + int modifier; + double previous_value; + + sensors_subfeature_type input; + + // Intentionally unimplemented + HwmonCounter(const HwmonCounter &); + HwmonCounter &operator=(const HwmonCounter &); +}; + +HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { + + int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; + char *chip_name = new char[len]; + sensors_snprintf_chip_name(chip_name, len, chip); + + len = snprintf(NULL, 0, "hwmon_%s_%d", chip_name, feature->number) + 1; + name = new char[len]; + snprintf(name, len, "hwmon_%s_%d", chip_name, feature->number); + + delete [] chip_name; + + label = sensors_get_label(chip, feature); + + switch (feature->type) { + case SENSORS_FEATURE_IN: + title = "Voltage"; + input = SENSORS_SUBFEATURE_IN_INPUT; + display = "maximum"; + counter_class = "absolute"; + unit = "V"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_FAN: + title = "Fan"; + input = SENSORS_SUBFEATURE_FAN_INPUT; + display = "average"; + counter_class = "absolute"; + unit = "RPM"; + modifier = 1; + monotonic = false; + break; + case SENSORS_FEATURE_TEMP: + title = "Temperature"; + input = SENSORS_SUBFEATURE_TEMP_INPUT; + display = "maximum"; + counter_class = "absolute"; + unit = "°C"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_POWER: + title = "Power"; + input = SENSORS_SUBFEATURE_POWER_INPUT; + display = "maximum"; + counter_class = "absolute"; + unit = "W"; + modifier = 1000000; + monotonic = false; + break; + case SENSORS_FEATURE_ENERGY: + title = "Energy"; + input = SENSORS_SUBFEATURE_ENERGY_INPUT; + display = "accumulate"; + counter_class = "delta"; + unit = "J"; + modifier = 1000000; + monotonic = true; + break; + case SENSORS_FEATURE_CURR: + title = "Current"; + input = SENSORS_SUBFEATURE_CURR_INPUT; + display = "maximum"; + counter_class = "absolute"; + unit = "A"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_HUMIDITY: + title = "Humidity"; + input = SENSORS_SUBFEATURE_HUMIDITY_INPUT; + display = "average"; + counter_class = "absolute"; + unit = "%"; + modifier = 1000; + monotonic = false; + break; + default: + logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type); + handleException(); + } + + for (HwmonCounter * counter = next; counter != NULL; counter = counter->getNext()) { + if (strcmp(label, counter->getLabel()) == 0 && strcmp(title, counter->getTitle()) == 0) { + duplicate = true; + counter->duplicate = true; + break; + } + } +} + +HwmonCounter::~HwmonCounter() { + free((void *)label); + delete [] name; +} + +double HwmonCounter::read() { + double value; + double result; + const sensors_subfeature *subfeature; + + // Keep in sync with canRead + subfeature = sensors_get_subfeature(chip, feature, input); + if (!subfeature) { + logg->logError(__FILE__, __LINE__, "No input value for hwmon sensor %s", label); + handleException(); + } + + if (sensors_get_value(chip, subfeature->number, &value) != 0) { + logg->logError(__FILE__, __LINE__, "Can't get input value for hwmon sensor %s", label); + handleException(); + } + + result = (monotonic ? value - previous_value : value); + previous_value = value; + + return result; +} + +bool HwmonCounter::canRead() { + if (!polled) { + double value; + const sensors_subfeature *subfeature; + bool result = true; + + subfeature = sensors_get_subfeature(chip, feature, input); + if (!subfeature) { + result = false; + } else { + result = sensors_get_value(chip, subfeature->number, &value) == 0; + } + + polled = true; + readable = result; + } + + enabled &= readable; + + return readable; +} + +Hwmon::Hwmon() : counters(NULL) { +} + +Hwmon::~Hwmon() { + while (counters != NULL) { + HwmonCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } + sensors_cleanup(); +} + +void Hwmon::setup() { + // hwmon does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + + int err = sensors_init(NULL); + if (err) { + logg->logMessage("Failed to initialize libsensors! (%d)", err); + return; + } + sensors_sysfs_no_scaling = 1; + + int chip_nr = 0; + const sensors_chip_name *chip; + while ((chip = sensors_get_detected_chips(NULL, &chip_nr))) { + int feature_nr = 0; + const sensors_feature *feature; + while ((feature = sensors_get_features(chip, &feature_nr))) { + counters = new HwmonCounter(counters, chip, feature); + } + } +} + +HwmonCounter *Hwmon::findCounter(const Counter &counter) const { + for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { + if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { + return hwmonCounter; + } + } + + return NULL; +} + +bool Hwmon::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool Hwmon::countersEnabled() const { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void Hwmon::resetCounters() { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void Hwmon::setupCounter(Counter &counter) { + HwmonCounter *const hwmonCounter = findCounter(counter); + if (hwmonCounter == NULL) { + counter.setEnabled(false); + return; + } + hwmonCounter->setEnabled(true); + counter.setKey(hwmonCounter->getKey()); +} + +int Hwmon::writeCounters(mxml_node_t *root) const { + int count = 0; + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->canRead()) { + continue; + } + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +void Hwmon::writeEvents(mxml_node_t *root) const { + root = mxmlNewElement(root, "category"); + mxmlElementSetAttr(root, "name", "hwmon"); + + char buf[1024]; + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->canRead()) { + continue; + } + mxml_node_t *node = mxmlNewElement(root, "event"); + mxmlElementSetAttr(node, "counter", counter->getName()); + mxmlElementSetAttr(node, "title", counter->getTitle()); + if (counter->isDuplicate()) { + mxmlElementSetAttrf(node, "name", "%s (0x%x)", counter->getLabel(), counter->getKey()); + } else { + mxmlElementSetAttr(node, "name", counter->getLabel()); + } + mxmlElementSetAttr(node, "display", counter->getDisplay()); + mxmlElementSetAttr(node, "class", counter->getCounterClass()); + mxmlElementSetAttr(node, "units", counter->getUnit()); + if (counter->getModifier() != 1) { + mxmlElementSetAttrf(node, "modifier", "%d", counter->getModifier()); + } + if (strcmp(counter->getDisplay(), "average") == 0 || strcmp(counter->getDisplay(), "maximum") == 0) { + mxmlElementSetAttr(node, "average_selection", "yes"); + } + snprintf(buf, sizeof(buf), "libsensors %s sensor %s (%s)", counter->getTitle(), counter->getLabel(), counter->getName()); + mxmlElementSetAttr(node, "description", buf); + } +} + +void Hwmon::start() { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + counter->read(); + } +} + +void Hwmon::read(Buffer * const buffer) { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + if (!counter->isEnabled()) { + continue; + } + buffer->event(counter->getKey(), counter->read()); + } +} diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h new file mode 100644 index 000000000000..a22a3609f99f --- /dev/null +++ b/tools/gator/daemon/Hwmon.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 HWMON_H +#define HWMON_H + +#include "Driver.h" + +class Buffer; +class HwmonCounter; + +class Hwmon : public Driver { +public: + Hwmon(); + ~Hwmon(); + + void setup(); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + void writeEvents(mxml_node_t *root) const; + + void start(); + void read(Buffer * buffer); + +private: + HwmonCounter *findCounter(const Counter &counter) const; + + HwmonCounter *counters; + + // Intentionally unimplemented + Hwmon(const Hwmon &); + Hwmon &operator=(const Hwmon &); +}; + +#endif // HWMON_H diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp new file mode 100644 index 000000000000..73e123d2f14e --- /dev/null +++ b/tools/gator/daemon/KMod.cpp @@ -0,0 +1,110 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "KMod.h" + +#include +#include +#include + +#include "ConfigurationXML.h" +#include "Counter.h" +#include "DriverSource.h" +#include "Logging.h" + +// Claim all the counters in /dev/gator/events +bool KMod::claimCounter(const Counter &counter) const { + char text[128]; + snprintf(text, sizeof(text), "/dev/gator/events/%s", counter.getType()); + return access(text, F_OK) == 0; +} + +void KMod::resetCounters() { + char base[128]; + char text[128]; + + // 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(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); + snprintf(text, sizeof(text), "%s/enabled", base); + DriverSource::writeDriver(text, 0); + snprintf(text, sizeof(text), "%s/count", base); + DriverSource::writeDriver(text, 0); + } + closedir(dir); + } +} + +void KMod::setupCounter(Counter &counter) { + char base[128]; + char text[128]; + snprintf(base, sizeof(base), "/dev/gator/events/%s", counter.getType()); + + snprintf(text, sizeof(text), "%s/enabled", base); + int enabled = true; + if (DriverSource::writeReadDriver(text, &enabled) || !enabled) { + counter.setEnabled(false); + return; + } + + int value = 0; + snprintf(text, sizeof(text), "%s/key", base); + DriverSource::readIntDriver(text, &value); + counter.setKey(value); + + snprintf(text, sizeof(text), "%s/cores", base); + if (DriverSource::readIntDriver(text, &value) == 0) { + counter.setCores(value); + } + + snprintf(text, sizeof(text), "%s/event", base); + DriverSource::writeDriver(text, counter.getEvent()); + snprintf(text, sizeof(text), "%s/count", base); + if (access(text, F_OK) == 0) { + int count = counter.getCount(); + if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); + handleException(); + } + counter.setCount(count); + } else if (counter.getCount() > 0) { + ConfigurationXML::remove(); + 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. The invalid configuration.xml has been removed.\n"); + handleException(); + } +} + +int KMod::writeCounters(mxml_node_t *root) const { + struct dirent *ent; + mxml_node_t *counter; + + // counters.xml is simply a file listing of /dev/gator/events + DIR* dir = opendir("/dev/gator/events"); + if (dir == NULL) { + return 0; + } + + int count = 0; + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + counter = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(counter, "name", ent->d_name); + ++count; + } + closedir(dir); + + return count; +} diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h new file mode 100644 index 000000000000..fb7fc8a8f9c6 --- /dev/null +++ b/tools/gator/daemon/KMod.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 KMOD_H +#define KMOD_H + +#include "Driver.h" + +// Driver for the gator kernel module +class KMod : public Driver { +public: + KMod() {} + ~KMod() {} + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; +}; + +#endif // KMOD_H diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp new file mode 100644 index 000000000000..d2a4b799d7ac --- /dev/null +++ b/tools/gator/daemon/LocalCapture.cpp @@ -0,0 +1,131 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "LocalCapture.h" + +#include +#include +#include +#include +#include +#include + +#include "SessionData.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "EventsXML.h" + +LocalCapture::LocalCapture() {} + +LocalCapture::~LocalCapture() {} + +void LocalCapture::createAPCDirectory(char* target_path) { + gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc"); + 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[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(); + } + + // Write events XML + EventsXML eventsXML; + eventsXML.write(gSessionData->mAPCDir); +} + +char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) { + char* output; + char path[PATH_MAX]; + + // Ensure the path is an absolute path, i.e. starts with a slash + if (initialPath == 0 || strlen(initialPath) == 0) { + logg->logError(__FILE__, __LINE__, "Missing -o command line option required for a local capture."); + handleException(); + } else if (initialPath[0] != '/') { + if (getcwd(path, PATH_MAX) == 0) { + logg->logMessage("Unable to retrieve 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 + } + + // 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); + } + + output = strdup(path); + + return output; +} + +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[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; + } +} diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h new file mode 100644 index 000000000000..b1e7219795cf --- /dev/null +++ b/tools/gator/daemon/LocalCapture.h @@ -0,0 +1,26 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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__ + +struct ImageLinkList; + +class LocalCapture { +public: + LocalCapture(); + ~LocalCapture(); + void write(char* string); + void copyImages(ImageLinkList* ptr); + void createAPCDirectory(char* target_path); +private: + char* createUniqueDirectory(const char* path, const char* ending); + 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 100644 index 000000000000..b8d3178950d6 --- /dev/null +++ b/tools/gator/daemon/Logging.cpp @@ -0,0 +1,78 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "Logging.h" + +#include +#include +#include +#include + +#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 + +// 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 100644 index 000000000000..4934bb079754 --- /dev/null +++ b/tools/gator/daemon/Logging.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 + +#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; + pthread_mutex_t mLoggingMutex; +}; + +extern Logging* logg; + +extern void handleException() __attribute__ ((noreturn)); + +#endif //__LOGGING_H__ diff --git a/tools/gator/daemon/Makefile b/tools/gator/daemon/Makefile new file mode 100644 index 000000000000..2ed49fdb688b --- /dev/null +++ b/tools/gator/daemon/Makefile @@ -0,0 +1,25 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/arm-linux-gnueabihf- +# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat +# targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see +# README_Streamline.txt for more details + +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ + +# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc +CPPFLAGS += -mthumb-interwork + +ifeq ($(SOFTFLOAT),1) + CPPFLAGS += -marm -march=armv4t -mfloat-abi=soft + LDFLAGS += -marm -march=armv4t -mfloat-abi=soft +endif +ifneq ($(SYSROOT),) + LDFLAGS += --sysroot=$(SYSROOT) +endif + +include common.mk diff --git a/tools/gator/daemon/Makefile_aarch64 b/tools/gator/daemon/Makefile_aarch64 new file mode 100644 index 000000000000..efd1fa002182 --- /dev/null +++ b/tools/gator/daemon/Makefile_aarch64 @@ -0,0 +1,12 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# make -f Makefile_aarch64 +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/aarch64-linux-gnu- + +CC = $(CROSS_COMPILE)gcc +CXX = $(CROSS_COMPILE)g++ + +include common.mk diff --git a/tools/gator/daemon/MaliVideoDriver.cpp b/tools/gator/daemon/MaliVideoDriver.cpp new file mode 100644 index 000000000000..18b413b01a37 --- /dev/null +++ b/tools/gator/daemon/MaliVideoDriver.cpp @@ -0,0 +1,253 @@ +/** + * Copyright (C) ARM Limited 2014. 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 "MaliVideoDriver.h" + +#include + +#include "Buffer.h" +#include "Counter.h" +#include "Logging.h" +#include "SessionData.h" + +// From instr/src/mve_instr_comm_protocol.h +typedef enum mve_instr_configuration_type { + MVE_INSTR_RAW = 1 << 0, + MVE_INSTR_COUNTERS = 1 << 1, + MVE_INSTR_EVENTS = 1 << 2, + MVE_INSTR_ACTIVITIES = 1 << 3, + + // Raw always pushed regardless + MVE_INSTR_PULL = 1 << 12, + // Raw always unpacked regardless + MVE_INSTR_PACKED_COMM = 1 << 13, + // Don’t send ACKt response + MVE_INSTR_NO_AUTO_ACK = 1 << 14, +} mve_instr_configuration_type_t; + +static const char COUNTER[] = "ARM_Mali-V500_cnt"; +static const char EVENT[] = "ARM_Mali-V500_evn"; +static const char ACTIVITY[] = "ARM_Mali-V500_act"; + +class MaliVideoCounter { +public: + MaliVideoCounter(MaliVideoCounter *next, const char *name, const MaliVideoCounterType type, const int id) : mNext(next), mName(name), mType(type), mId(id), mKey(getEventKey()), mEnabled(false) { + } + + ~MaliVideoCounter() { + delete mName; + } + + MaliVideoCounter *getNext() const { return mNext; } + const char *getName() const { return mName; } + MaliVideoCounterType getType() const { return mType; } + int getId() const { return mId; } + int getKey() const { return mKey; } + bool isEnabled() const { return mEnabled; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + +private: + MaliVideoCounter *const mNext; + const char *const mName; + const MaliVideoCounterType mType; + // Mali Video id + const int mId; + // Streamline key + const int mKey; + bool mEnabled; +}; + +MaliVideoDriver::MaliVideoDriver() : mCounters(NULL), mActivityCount(0) { +} + +MaliVideoDriver::~MaliVideoDriver() { + while (mCounters != NULL) { + MaliVideoCounter *counter = mCounters; + mCounters = counter->getNext(); + delete counter; + } +} + +void MaliVideoDriver::setup(mxml_node_t *const xml) { + // hwmon does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + + mxml_node_t *node = xml; + while (true) { + node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); + if (node == NULL) { + break; + } + const char *counter = mxmlElementGetAttr(node, "counter"); + if (counter == NULL) { + // Ignore + } else if (strncmp(counter, COUNTER, sizeof(COUNTER) - 1) == 0) { + const int i = strtol(counter + sizeof(COUNTER) - 1, NULL, 10); + mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_COUNTER, i); + } else if (strncmp(counter, EVENT, sizeof(EVENT) - 1) == 0) { + const int i = strtol(counter + sizeof(EVENT) - 1, NULL, 10); + mCounters = new MaliVideoCounter(mCounters, strdup(counter), MVCT_EVENT, i); + } else if (strcmp(counter, ACTIVITY) == 0) { + mCounters = new MaliVideoCounter(mCounters, strdup(ACTIVITY), MVCT_ACTIVITY, 0); + mActivityCount = 0; + while (true) { + char buf[32]; + snprintf(buf, sizeof(buf), "activity%i", mActivityCount + 1); + if (mxmlElementGetAttr(node, buf) == NULL) { + break; + } + ++mActivityCount; + } + } + } +} + +MaliVideoCounter *MaliVideoDriver::findCounter(const Counter &counter) const { + for (MaliVideoCounter *maliVideoCounter = mCounters; maliVideoCounter != NULL; maliVideoCounter = maliVideoCounter->getNext()) { + if (strcmp(maliVideoCounter->getName(), counter.getType()) == 0) { + return maliVideoCounter; + } + } + + return NULL; +} + +bool MaliVideoDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +bool MaliVideoDriver::countersEnabled() const { + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled()) { + return true; + } + } + return false; +} + +void MaliVideoDriver::resetCounters() { + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void MaliVideoDriver::setupCounter(Counter &counter) { + MaliVideoCounter *const maliVideoCounter = findCounter(counter); + if (maliVideoCounter == NULL) { + counter.setEnabled(false); + return; + } + maliVideoCounter->setEnabled(true); + counter.setKey(maliVideoCounter->getKey()); +} + +int MaliVideoDriver::writeCounters(mxml_node_t *root) const { + if (access("/dev/mv500", F_OK) != 0) { + return 0; + } + + int count = 0; + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos) { + // size + int numEnabled = 0; + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() == type)) { + ++numEnabled; + } + } + Buffer::packInt(buf, bufsize, pos, numEnabled*sizeof(uint32_t)); + for (MaliVideoCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() == type)) { + Buffer::packInt(buf, bufsize, pos, counter->getId()); + } + } +} + +bool MaliVideoDriver::start(const int mveUds) { + char buf[256]; + int pos = 0; + + // code - MVE_INSTR_STARTUP + buf[pos++] = 'C'; + buf[pos++] = 'L'; + buf[pos++] = 'N'; + buf[pos++] = 'T'; + // size + Buffer::packInt(buf, sizeof(buf), pos, sizeof(uint32_t)); + // client_version_number + Buffer::packInt(buf, sizeof(buf), pos, 1); + + // code - MVE_INSTR_CONFIGURE + buf[pos++] = 'C'; + buf[pos++] = 'N'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + // size + Buffer::packInt(buf, sizeof(buf), pos, 5*sizeof(uint32_t)); + // configuration + Buffer::packInt(buf, sizeof(buf), pos, MVE_INSTR_COUNTERS | MVE_INSTR_EVENTS | MVE_INSTR_ACTIVITIES | MVE_INSTR_PACKED_COMM); + // communication_protocol_version + Buffer::packInt(buf, sizeof(buf), pos, 1); + // data_protocol_version + Buffer::packInt(buf, sizeof(buf), pos, 1); + // sample_rate - convert samples/second to ms/sample + Buffer::packInt(buf, sizeof(buf), pos, 1000/gSessionData->mSampleRate); + // live_rate - convert ns/flush to ms/flush + Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate/1000000); + + // code - MVE_INSTR_ENABLE_COUNTERS + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'c'; + marshalEnable(MVCT_COUNTER, buf, sizeof(buf), pos); + + // code - MVE_INSTR_ENABLE_EVENTS + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'e'; + marshalEnable(MVCT_EVENT, buf, sizeof(buf), pos); + + /* + // code - MVE_INSTR_ENABLE_ACTIVITIES + buf[pos++] = 'C'; + buf[pos++] = 'F'; + buf[pos++] = 'G'; + buf[pos++] = 'a'; + // size + Buffer::packInt(buf, sizeof(buf), pos, mActivityCount*sizeof(uint32_t)); + for (int i = 0; i < mActivityCount; ++i) { + // activity_id + Buffer::packInt(buf, sizeof(buf), pos, i); + } + */ + + int written = 0; + while (written < pos) { + size_t bytes = ::write(mveUds, buf + written, pos - written); + if (bytes <= 0) { + logg->logMessage("%s(%s:%i): write failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + written += bytes; + } + + return true; +} diff --git a/tools/gator/daemon/MaliVideoDriver.h b/tools/gator/daemon/MaliVideoDriver.h new file mode 100644 index 000000000000..00cb80889a74 --- /dev/null +++ b/tools/gator/daemon/MaliVideoDriver.h @@ -0,0 +1,50 @@ +/** + * Copyright (C) ARM Limited 2014. 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 MALIVIDEODRIVER_H +#define MALIVIDEODRIVER_H + +#include "Driver.h" + +class MaliVideoCounter; + +enum MaliVideoCounterType { + MVCT_COUNTER, + MVCT_EVENT, + MVCT_ACTIVITY, +}; + +class MaliVideoDriver : public Driver { +public: + MaliVideoDriver(); + ~MaliVideoDriver(); + + void setup(mxml_node_t *const xml); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + bool start(const int mveUds); + +private: + MaliVideoCounter *findCounter(const Counter &counter) const; + void marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos); + + MaliVideoCounter *mCounters; + int mActivityCount; + + // Intentionally unimplemented + MaliVideoDriver(const MaliVideoDriver &); + MaliVideoDriver &operator=(const MaliVideoDriver &); +}; + +#endif // MALIVIDEODRIVER_H diff --git a/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp new file mode 100644 index 000000000000..b34a15f0eb0c --- /dev/null +++ b/tools/gator/daemon/Monitor.cpp @@ -0,0 +1,68 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "Monitor.h" + +#include +#include +#include + +#include "Logging.h" + +Monitor::Monitor() : mFd(-1) { +} + +Monitor::~Monitor() { + if (mFd >= 0) { + ::close(mFd); + } +} + +void Monitor::close() { + if (mFd >= 0) { + ::close(mFd); + mFd = -1; + } +} + +bool Monitor::init() { + mFd = epoll_create(16); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool Monitor::add(const int fd) { + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.data.fd = fd; + event.events = EPOLLIN; + if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { + logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) { + int result = epoll_wait(mFd, events, maxevents, timeout); + if (result < 0) { + // Ignore if the call was interrupted as this will happen when SIGINT is received + if (errno == EINTR) { + result = 0; + } else { + logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); + } + } + + return result; +} diff --git a/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h new file mode 100644 index 000000000000..7194e0e4ca50 --- /dev/null +++ b/tools/gator/daemon/Monitor.h @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 MONITOR_H +#define MONITOR_H + +#include + +class Monitor { +public: + Monitor(); + ~Monitor(); + + void close(); + bool init(); + bool add(const int fd); + int wait(struct epoll_event *const events, int maxevents, int timeout); + +private: + + int mFd; + + // Intentionally unimplemented + Monitor(const Monitor &); + Monitor &operator=(const Monitor &); +}; + +#endif // MONITOR_H diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp new file mode 100644 index 000000000000..28774e36e510 --- /dev/null +++ b/tools/gator/daemon/OlySocket.cpp @@ -0,0 +1,282 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "OlySocket.h" + +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#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 + +OlyServerSocket::OlyServerSocket(int port) { +#ifdef WIN32 + WSADATA wsaData; + if (WSAStartup(0x0202, &wsaData) != 0) { + logg->logError(__FILE__, __LINE__, "Windows socket initialization failed"); + handleException(); + } +#endif + + createServerSocket(port); +} + +OlySocket::OlySocket(int socketID) : mSocketID(socketID) { +} + +#ifndef WIN32 + +#define MIN(A, B) ({ \ + const __typeof__(A) __a = A; \ + const __typeof__(B) __b = B; \ + __a > __b ? __b : __a; \ +}) + +OlyServerSocket::OlyServerSocket(const char* path, const size_t pathSize) { + // Create socket + mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path))); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + // 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."); + handleException(); + } + + // Listen for connections on this socket + if (listen(mFDServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); + } +} + +int OlySocket::connect(const char* path, const size_t pathSize) { + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return -1; + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + memcpy(sockaddr.sun_path, path, MIN(pathSize, sizeof(sockaddr.sun_path))); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + if (::connect(fd, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + close(fd); + return -1; + } + + return fd; +} + +#endif + +OlySocket::~OlySocket() { + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + } +} + +OlyServerSocket::~OlyServerSocket() { + if (mFDServer > 0) { + CLOSE_SOCKET(mFDServer); + } +} + +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 OlyServerSocket::closeServerSocket() { + if (CLOSE_SOCKET(mFDServer) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to close server socket."); + handleException(); + } + mFDServer = 0; +} + +void OlyServerSocket::createServerSocket(int port) { + int family = AF_INET6; + + // Create socket + mFDServer = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (mFDServer < 0) { + family = AF_INET; + 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_in6 sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = family; + sockaddr.sin6_port = htons(port); + sockaddr.sin6_addr = in6addr_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 OlyServerSocket::acceptConnection() { + int socketID; + 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 + socketID = accept(mFDServer, NULL, NULL); + if (socketID < 0) { + logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); + handleException(); + } + return socketID; +} + +void OlySocket::send(const 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 100644 index 000000000000..20c67cc695e1 --- /dev/null +++ b/tools/gator/daemon/OlySocket.h @@ -0,0 +1,55 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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: +#ifndef WIN32 + static int connect(const char* path, const size_t pathSize); +#endif + + OlySocket(int socketID); + ~OlySocket(); + + void closeSocket(); + void shutdownConnection(); + void send(const char* buffer, int size); + int receive(char* buffer, int size); + int receiveNBytes(char* buffer, int size); + int receiveString(char* buffer, int size); + + bool isValid() const { return mSocketID >= 0; } + +private: + int mSocketID; +}; + +class OlyServerSocket { +public: + OlyServerSocket(int port); +#ifndef WIN32 + OlyServerSocket(const char* path, const size_t pathSize); +#endif + ~OlyServerSocket(); + + int acceptConnection(); + void closeServerSocket(); + + int getFd() { return mFDServer; } + +private: + int mFDServer; + + 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 100644 index 000000000000..45340a27d9fa --- /dev/null +++ b/tools/gator/daemon/OlyUtility.cpp @@ -0,0 +1,227 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "OlyUtility.h" + +#include +#include +#include +#include + +#if defined(WIN32) +#include +#elif defined(__linux__) +#include +#elif defined(DARWIN) +#include +#endif + +OlyUtility* util = NULL; + +bool OlyUtility::stringToBool(const char* string, bool defValue) { + char value[32]; + + if (string == NULL) { + return defValue; + } + + 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); +#if defined(WIN32) + int length = GetModuleFileName(NULL, fullpath, sizeOfPath); +#elif defined(__linux__) + int length = readlink("/proc/self/exe", fullpath, sizeOfPath); +#elif defined(DARWIN) + uint32_t length_u = (uint32_t)sizeOfPath; + int length = sizeOfPath; + if (_NSGetExecutablePath(fullpath, &length_u) == 0) { + length = strlen(fullpath); + } +#endif + + if (length == sizeOfPath) { + return -1; + } + + fullpath[length] = 0; + 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[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); + 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 last_sep++; +} + +// 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; + } + last_sep++; + *last_sep = 0; + + return (path); +} diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h new file mode 100644 index 000000000000..1d26beb596fa --- /dev/null +++ b/tools/gator/daemon/OlyUtility.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 + +#include + +#ifdef WIN32 +#define PATH_SEPARATOR '\\' +#define CAIMAN_PATH_MAX MAX_PATH +#define snprintf _snprintf +#else +#include +#define PATH_SEPARATOR '/' +#define CAIMAN_PATH_MAX PATH_MAX +#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: +}; + +extern OlyUtility* util; + +#endif // OLY_UTILITY_H diff --git a/tools/gator/daemon/PerfBuffer.cpp b/tools/gator/daemon/PerfBuffer.cpp new file mode 100644 index 000000000000..5fad583f7bd0 --- /dev/null +++ b/tools/gator/daemon/PerfBuffer.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "PerfBuffer.h" + +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +PerfBuffer::PerfBuffer() { + for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) { + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + } +} + +PerfBuffer::~PerfBuffer() { + for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { + if (mBuf[cpu] != MAP_FAILED) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + } + } +} + +bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) { + if (fd == groupFd) { + if (mBuf[cpu] != MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); + return false; + } + + // The buffer isn't mapped yet + mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Check the version + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + if (pemp->compat_version != 0) { + logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } else { + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +void PerfBuffer::discard(const int cpu) { + if (mBuf[cpu] != MAP_FAILED) { + mDiscard[cpu] = true; + } +} + +bool PerfBuffer::isEmpty() { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] != MAP_FAILED) { + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head != tail) { + return false; + } + } + } + + return true; +} + +bool PerfBuffer::send(Sender *const sender) { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] == MAP_FAILED) { + continue; + } + + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head > tail) { + const uint8_t *const b = static_cast(mBuf[cpu]) + gSessionData->mPageSize; + const int offset = gSessionData->mLocalCapture ? 1 : 0; + unsigned char header[7]; + header[0] = RESPONSE_APC_DATA; + Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5); + // Should use real packing functions + header[5] = FRAME_PERF; + header[6] = cpu; + + // Write header + sender->writeData(reinterpret_cast(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA); + + // Write data + if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) { + // Not wrapped + sender->writeData(reinterpret_cast(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA); + } else { + // Wrapped + sender->writeData(reinterpret_cast(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA); + sender->writeData(reinterpret_cast(b), head & BUF_MASK, RESPONSE_APC_DATA); + } + + // Update tail with the data read + pemp->data_tail = head; + } + + if (mDiscard[cpu]) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + } + } + + return true; +} diff --git a/tools/gator/daemon/PerfBuffer.h b/tools/gator/daemon/PerfBuffer.h new file mode 100644 index 000000000000..278a3b9d6db7 --- /dev/null +++ b/tools/gator/daemon/PerfBuffer.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 PERF_BUFFER +#define PERF_BUFFER + +#include "Config.h" + +#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) +#define BUF_MASK (BUF_SIZE - 1) + +class Sender; + +class PerfBuffer { +public: + PerfBuffer(); + ~PerfBuffer(); + + bool useFd(const int cpu, const int fd, const int groupFd); + void discard(const int cpu); + bool isEmpty(); + bool send(Sender *const sender); + +private: + void *mBuf[NR_CPUS]; + // After the buffer is flushed it should be unmaped + bool mDiscard[NR_CPUS]; + + // Intentionally undefined + PerfBuffer(const PerfBuffer &); + PerfBuffer &operator=(const PerfBuffer &); +}; + +#endif // PERF_BUFFER diff --git a/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp new file mode 100644 index 000000000000..ac97a077d266 --- /dev/null +++ b/tools/gator/daemon/PerfDriver.cpp @@ -0,0 +1,409 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "PerfDriver.h" + +#include +#include +#include +#include + +#include "Buffer.h" +#include "Config.h" +#include "ConfigurationXML.h" +#include "Counter.h" +#include "DriverSource.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfGroup.h" +#include "SessionData.h" + +#define PERF_DEVICES "/sys/bus/event_source/devices" + +#define TYPE_DERIVED ~0U + +// From gator.h +struct gator_cpu { + const int cpuid; + // Human readable name + const char *const core_name; + // gatorfs event and Perf PMU name + const char *const pmnc_name; + const int pmnc_counters; +}; + +// From gator_main.c +static const struct gator_cpu gator_cpus[] = { + { 0xb36, "ARM1136", "ARM_ARM11", 3 }, + { 0xb56, "ARM1156", "ARM_ARM11", 3 }, + { 0xb76, "ARM1176", "ARM_ARM11", 3 }, + { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, + { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, + { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, + { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, + { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, + { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 }, + { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, + { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, + { 0x00f, "Scorpion", "Scorpion", 4 }, + { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, + { 0x049, "KraitSIM", "Krait", 4 }, + { 0x04d, "Krait", "Krait", 4 }, + { 0x06f, "Krait S4 Pro", "Krait", 4 }, + { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, + { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, + { 0xd0f, "AArch64", "ARM_AArch64", 6 }, +}; + +static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; +static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; + +struct uncore_counter { + // gatorfs event and Perf PMU name + const char *const name; + const int count; +}; + +static const struct uncore_counter uncore_counters[] = { + { "CCI_400", 4 }, + { "CCI_400-r1", 4 }, +}; + +class PerfCounter { +public: + PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config, bool perCpu) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false), mPerCpu(perCpu) {} + ~PerfCounter() { + delete [] mName; + } + + PerfCounter *getNext() const { return mNext; } + const char *getName() const { return mName; } + uint32_t getType() const { return mType; } + int getCount() const { return mCount; } + void setCount(const int count) { mCount = count; } + int getKey() const { return mKey; } + uint64_t getConfig() const { return mConfig; } + void setConfig(const uint64_t config) { mConfig = config; } + bool isEnabled() const { return mEnabled; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + bool isPerCpu() const { return mPerCpu; } + +private: + PerfCounter *const mNext; + const char *const mName; + const uint32_t mType; + int mCount; + const int mKey; + uint64_t mConfig; + int mEnabled : 1, + mPerCpu : 1; +}; + +PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false), mLegacySupport(false) { +} + +PerfDriver::~PerfDriver() { + while (mCounters != NULL) { + PerfCounter *counter = mCounters; + mCounters = counter->getNext(); + delete counter; + } +} + +void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { + int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + char *name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + mCounters = new PerfCounter(mCounters, name, type, -1, true); + + for (int j = 0; j < numCounters; ++j) { + len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; + name = new char[len]; + snprintf(name, len, "%s_cnt%d", counterName, j); + mCounters = new PerfCounter(mCounters, name, type, -1, true); + } +} + +void PerfDriver::addUncoreCounters(const char *const counterName, const int type, const int numCounters) { + int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + char *name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + mCounters = new PerfCounter(mCounters, name, type, -1, false); + + for (int j = 0; j < numCounters; ++j) { + len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; + name = new char[len]; + snprintf(name, len, "%s_cnt%d", counterName, j); + mCounters = new PerfCounter(mCounters, name, type, -1, false); + } +} + +// From include/generated/uapi/linux/version.h +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +bool PerfDriver::setup() { + // Check the kernel version + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + int release[3] = { 0, 0, 0 }; + int part = 0; + char *ch = utsname.release; + while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) { + release[part] = 10*release[part] + *ch - '0'; + + ++ch; + if (*ch == '.') { + ++part; + ++ch; + } + } + + if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 4, 0)) { + logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + mLegacySupport = KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0); + + if (access(EVENTS_PATH, R_OK) != 0) { + logg->logMessage("%s(%s:%i): " EVENTS_PATH " does not exist, is CONFIG_TRACING enabled?", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Add supported PMUs + bool foundCpu = false; + DIR *dir = opendir(PERF_DEVICES); + if (dir == NULL) { + logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + // Do the names match exactly? + if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 && + // Do these names match but have the old vs new prefix? + (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || + strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || + strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) { + continue; + } + + int type; + char buf[256]; + snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); + if (DriverSource::readIntDriver(buf, &type) != 0) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); + } + + for (int i = 0; i < ARRAY_LENGTH(uncore_counters); ++i) { + if (strcmp(dirent->d_name, uncore_counters[i].name) != 0) { + continue; + } + + int type; + char buf[256]; + snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); + if (DriverSource::readIntDriver(buf, &type) != 0) { + continue; + } + + addUncoreCounters(uncore_counters[i].name, type, uncore_counters[i].count); + } + } + closedir(dir); + + if (!foundCpu) { + // If no cpu was found based on pmu names, try by cpuid + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); + } + } + + /* + if (!foundCpu) { + // If all else fails, use the perf architected counters + // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once + addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); + } + */ + + // Add supported software counters + long long id; + DynBuf printb; + + id = getTracepointId("irq/softirq_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, true); + } + + id = getTracepointId("irq/irq_handler_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, true); + } + + //Linux_block_rq_wr + //Linux_block_rq_rd + //Linux_net_rx + //Linux_net_tx + + id = getTracepointId(SCHED_SWITCH, &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, true); + } + + //Linux_meminfo_memused + //Linux_meminfo_memfree + //Linux_meminfo_bufferram + //Linux_power_cpu_freq + //Linux_power_cpu_idle + + mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1, false); + + //Linux_cpu_wait_io + + mIsSetup = true; + return true; +} + +bool PerfDriver::summary(Buffer *const buffer) { + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + char buf[512]; + snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); + + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + + const int64_t uptime = getTime(); + + buffer->summary(timestamp, uptime, 0, buf); + + for (int i = 0; i < gSessionData->mCores; ++i) { + // Don't send information on a cpu we know nothing about + if (gSessionData->mCpuIds[i] == -1) { + continue; + } + + int j; + for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + break; + } + } + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); + } else { + if (gSessionData->mCpuIds[i] == -1) { + snprintf(buf, sizeof(buf), "Unknown"); + } else { + snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); + } + buffer->coreName(i, gSessionData->mCpuIds[i], buf); + } + } + buffer->commit(1); + + return true; +} + +PerfCounter *PerfDriver::findCounter(const Counter &counter) const { + for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) { + if (strcmp(perfCounter->getName(), counter.getType()) == 0) { + return perfCounter; + } + } + + return NULL; +} + +bool PerfDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +void PerfDriver::resetCounters() { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void PerfDriver::setupCounter(Counter &counter) { + PerfCounter *const perfCounter = findCounter(counter); + if (perfCounter == NULL) { + counter.setEnabled(false); + return; + } + + // Don't use the config from counters XML if it's not set, ex: software counters + if (counter.getEvent() != -1) { + perfCounter->setConfig(counter.getEvent()); + } + perfCounter->setCount(counter.getCount()); + perfCounter->setEnabled(true); + counter.setKey(perfCounter->getKey()); +} + +int PerfDriver::writeCounters(mxml_node_t *root) const { + int count = 0; + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +bool PerfDriver::enable(PerfGroup *const group, Buffer *const buffer) const { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { + if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0, counter->isPerCpu() ? PERF_GROUP_PER_CPU : 0)) { + logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return true; +} + +long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { + if (!printb->printf(EVENTS_PATH "/%s/id", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + int64_t result; + if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { + logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + return result; +} diff --git a/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h new file mode 100644 index 000000000000..2cae575a7059 --- /dev/null +++ b/tools/gator/daemon/PerfDriver.h @@ -0,0 +1,60 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 PERFDRIVER_H +#define PERFDRIVER_H + +#include "Driver.h" + +// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH +#define DEBUGFS_PATH "/sys/kernel/debug" +#define EVENTS_PATH DEBUGFS_PATH "/tracing/events" + +#define SCHED_SWITCH "sched/sched_switch" + +class Buffer; +class DynBuf; +class PerfCounter; +class PerfGroup; + +class PerfDriver : public Driver { +public: + PerfDriver(); + ~PerfDriver(); + + bool getLegacySupport() const { return mLegacySupport; } + + bool setup(); + bool summary(Buffer *const buffer); + bool isSetup() const { return mIsSetup; } + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + bool enable(PerfGroup *const group, Buffer *const buffer) const; + + static long long getTracepointId(const char *const name, DynBuf *const printb); + +private: + PerfCounter *findCounter(const Counter &counter) const; + void addCpuCounters(const char *const counterName, const int type, const int numCounters); + void addUncoreCounters(const char *const counterName, const int type, const int numCounters); + + PerfCounter *mCounters; + bool mIsSetup; + bool mLegacySupport; + + // Intentionally undefined + PerfDriver(const PerfDriver &); + PerfDriver &operator=(const PerfDriver &); +}; + +#endif // PERFDRIVER_H diff --git a/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp new file mode 100644 index 000000000000..2a0239f7c348 --- /dev/null +++ b/tools/gator/daemon/PerfGroup.cpp @@ -0,0 +1,226 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "PerfGroup.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "SessionData.h" + +#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ + pea.size = sizeof(pea); \ + /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ + pea.sample_type = (gSessionData->perf.getLegacySupport() \ + ? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_ID \ + : PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER ) | additionalSampleType; \ + /* Emit emit value in group format */ \ + pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ + /* start out disabled */ \ + pea.disabled = 1; \ + /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \ + pea.watermark = 1; \ + /* Be conservative in flush size as only one buffer set is monitored */ \ + pea.wakeup_watermark = 3 * BUF_SIZE / 4 + +static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) { + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { + memset(&mAttrs, 0, sizeof(mAttrs)); + memset(&mPerCpu, 0, sizeof(mPerCpu)); + memset(&mKeys, -1, sizeof(mKeys)); + memset(&mFds, -1, sizeof(mFds)); +} + +PerfGroup::~PerfGroup() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + close(mFds[pos]); + } + } +} + +bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { + int i; + for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + break; + } + } + + if (i >= ARRAY_LENGTH(mKeys)) { + logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + DEFAULT_PEA_ARGS(mAttrs[i], sampleType); + mAttrs[i].type = type; + mAttrs[i].config = config; + mAttrs[i].sample_period = sample; + // always be on the CPU but only a group leader can be pinned + mAttrs[i].pinned = (i == 0 ? 1 : 0); + mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0); + mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0); + mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); + mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); + mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); + mPerCpu[i] = (flags & PERF_GROUP_PER_CPU); + + mKeys[i] = key; + + buffer->pea(&mAttrs[i], key); + + return true; +} + +bool PerfGroup::prepareCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + if ((cpu != 0) && !mPerCpu[i]) { + continue; + } + + const int offset = i * gSessionData->mCores; + if (mFds[cpu + offset] >= 0) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: 0x%llx pinned: %i mmap: %i comm: %i freq: %i task: %i sample_id_all: %i", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type, mAttrs[i].pinned, mAttrs[i].mmap, mAttrs[i].comm, mAttrs[i].freq, mAttrs[i].task, mAttrs[i].sample_id_all); + mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); + if (mFds[cpu + offset] < 0) { + logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); + continue; + } + + if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) { + logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) { + __u64 ids[ARRAY_LENGTH(mKeys)]; + int coreKeys[ARRAY_LENGTH(mKeys)]; + int idCount = 0; + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + const int fd = mFds[cpu + i * gSessionData->mCores]; + if (fd < 0) { + continue; + } + + coreKeys[idCount] = mKeys[i]; + if (!gSessionData->perf.getLegacySupport() && ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0 && + // Workaround for running 32-bit gatord on 64-bit systems, kernel patch in the works + ioctl(fd, (PERF_EVENT_IOC_ID & ~IOCSIZE_MASK) | (8 << _IOC_SIZESHIFT), &ids[idCount]) != 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++idCount; + } + + if (!monitor->add(mFds[cpu])) { + logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!gSessionData->perf.getLegacySupport()) { + buffer->keys(idCount, ids, coreKeys); + } else { + char buf[1024]; + ssize_t bytes = read(mFds[cpu], buf, sizeof(buf)); + if (bytes < 0) { + logg->logMessage("read failed"); + return false; + } + buffer->keysOld(idCount, coreKeys, bytes, buf); + } + + if (start) { + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return idCount; +} + +bool PerfGroup::offlineCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + // Mark the buffer so that it will be released next time it's read + mPb->discard(cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0) { + close(mFds[offset]); + mFds[offset] = -1; + } + } + + return true; +} + +bool PerfGroup::start() { + for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) { + if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + return true; + + fail: + stop(); + + return false; +} + +void PerfGroup::stop() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE); + } + } +} diff --git a/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h new file mode 100644 index 000000000000..3f1e2bb4d1c8 --- /dev/null +++ b/tools/gator/daemon/PerfGroup.h @@ -0,0 +1,57 @@ + /** + * Copyright (C) ARM Limited 2013-2014. 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 PERF_GROUP +#define PERF_GROUP + +// Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways +#include "k/perf_event.h" + +#include "Config.h" + +class Buffer; +class Monitor; +class PerfBuffer; + +enum PerfGroupFlags { + PERF_GROUP_MMAP = 1 << 0, + PERF_GROUP_COMM = 1 << 1, + PERF_GROUP_FREQ = 1 << 2, + PERF_GROUP_TASK = 1 << 3, + PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, + PERF_GROUP_PER_CPU = 1 << 5, +}; + +class PerfGroup { +public: + PerfGroup(PerfBuffer *const pb); + ~PerfGroup(); + + bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); + // Safe to call concurrently + bool prepareCPU(const int cpu); + // Not safe to call concurrently. Returns the number of events enabled + int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor); + bool offlineCPU(int cpu); + bool start(); + void stop(); + +private: + // +1 for the group leader + struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; + bool mPerCpu[MAX_PERFORMANCE_COUNTERS + 1]; + int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; + int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; + PerfBuffer *const mPb; + + // Intentionally undefined + PerfGroup(const PerfGroup &); + PerfGroup &operator=(const PerfGroup &); +}; + +#endif // PERF_GROUP diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp new file mode 100644 index 000000000000..ecfaa66832bd --- /dev/null +++ b/tools/gator/daemon/PerfSource.cpp @@ -0,0 +1,275 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "PerfSource.h" + +#include +#include +#include + +#include "Child.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfDriver.h" +#include "Proc.h" +#include "SessionData.h" + +#define MS_PER_US 1000000 + +extern Child *child; + +static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) { + if (!printb->printf(EVENTS_PATH "/%s/format", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (!b->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + buffer->format(b->getLength(), b->getBuf()); + + return true; +} + +PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { + long l = sysconf(_SC_PAGE_SIZE); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the page size"); + handleException(); + } + gSessionData->mPageSize = static_cast(l); + + l = sysconf(_SC_NPROCESSORS_CONF); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores"); + handleException(); + } + gSessionData->mCores = static_cast(l); +} + +PerfSource::~PerfSource() { +} + +struct PrepareParallelArgs { + PerfGroup *pg; + int cpu; +}; + +void *prepareParallel(void *arg) { + const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg; + args->pg->prepareCPU(args->cpu); + return NULL; +} + +bool PerfSource::prepare() { + DynBuf printb; + DynBuf b1; + DynBuf b2; + DynBuf b3; + long long schedSwitchId; + + // Reread cpuinfo since cores may have changed since startup + gSessionData->readCpuInfo(); + + if (0 + || !mMonitor.init() + || !mUEvent.init() + || !mMonitor.add(mUEvent.getFd()) + + || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0 + || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1) + + // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID + || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU) + + // Only want TID and IP but not RAW on timer + || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU)) + + || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) + || 0) { + logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!gSessionData->perf.summary(&mSummary)) { + logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + { + // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases + pthread_t threads[NR_CPUS]; + PrepareParallelArgs args[NR_CPUS]; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + args[cpu].pg = &mCountersGroup; + args[cpu].cpu = cpu; + if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) { + logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (pthread_join(threads[cpu], NULL) != 0) { + logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + int numEvents = 0; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor); + } + if (numEvents <= 0) { + logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Start events before reading proc to avoid race conditions + if (!mCountersGroup.start()) { + logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!readProc(&mBuffer, true, &printb, &b1, &b2, &b3)) { + logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + mBuffer.commit(1); + + return true; +} + +static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu"; + +void PerfSource::run() { + int pipefd[2]; + + if (pipe(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + handleException(); + } + + int timeout = -1; + if (gSessionData->mLiveRate > 0) { + timeout = gSessionData->mLiveRate/MS_PER_US; + } + + sem_post(mStartProfile); + + while (gSessionData->mSessionIsActive) { + // +1 for uevents, +1 for pipe + struct epoll_event events[NR_CPUS + 2]; + int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); + } + + for (int i = 0; i < ready; ++i) { + if (events[i].data.fd == mUEvent.getFd()) { + if (!handleUEvent()) { + logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); + handleException(); + } + break; + } + } + + // send a notification that data is ready + sem_post(mSenderSem); + + // In one shot mode, stop collection once all the buffers are filled + // Assume timeout == 0 in this case + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); + child->endSession(); + } + } + + mCountersGroup.stop(); + mBuffer.setDone(); + mIsDone = true; + + // send a notification that data is ready + sem_post(mSenderSem); + + mInterruptFd = -1; + close(pipefd[0]); + close(pipefd[1]); +} + +bool PerfSource::handleUEvent() { + UEventResult result; + if (!mUEvent.read(&result)) { + logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (strcmp(result.mSubsystem, "cpu") == 0) { + if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) { + logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__); + return false; + } + char *endptr; + errno = 0; + int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10); + if (errno != 0 || *endptr != '\0') { + logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (strcmp(result.mAction, "online") == 0) { + // Only call onlineCPU if prepareCPU succeeded + const bool result = mCountersGroup.prepareCPU(cpu) && + mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor); + mBuffer.commit(1); + return result; + } else if (strcmp(result.mAction, "offline") == 0) { + return mCountersGroup.offlineCPU(cpu); + } + } + + return true; +} + +void PerfSource::interrupt() { + if (mInterruptFd >= 0) { + int8_t c = 0; + // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread + if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { + logg->logError(__FILE__, __LINE__, "write failed"); + handleException(); + } + } +} + +bool PerfSource::isDone () { + return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty(); +} + +void PerfSource::write (Sender *sender) { + if (!mSummary.isDone()) { + mSummary.write(sender); + gSessionData->mSentSummary = true; + } + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } + if (!mCountersBuf.send(sender)) { + logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); + handleException(); + } +} diff --git a/tools/gator/daemon/PerfSource.h b/tools/gator/daemon/PerfSource.h new file mode 100644 index 000000000000..3f471c8de414 --- /dev/null +++ b/tools/gator/daemon/PerfSource.h @@ -0,0 +1,54 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 PERFSOURCE_H +#define PERFSOURCE_H + +#include + +#include "Buffer.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "PerfGroup.h" +#include "Source.h" +#include "UEvent.h" + +class Sender; + +class PerfSource : public Source { +public: + PerfSource(sem_t *senderSem, sem_t *startProfile); + ~PerfSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + bool handleUEvent(); + + Buffer mSummary; + Buffer mBuffer; + PerfBuffer mCountersBuf; + PerfGroup mCountersGroup; + Monitor mMonitor; + UEvent mUEvent; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mInterruptFd; + bool mIsDone; + + // Intentionally undefined + PerfSource(const PerfSource &); + PerfSource &operator=(const PerfSource &); +}; + +#endif // PERFSOURCE_H diff --git a/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp new file mode 100644 index 000000000000..9f01770d6609 --- /dev/null +++ b/tools/gator/daemon/Proc.cpp @@ -0,0 +1,215 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "Proc.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "DynBuf.h" +#include "Logging.h" + +struct ProcStat { + // From linux-dev/include/linux/sched.h +#define TASK_COMM_LEN 16 + // TASK_COMM_LEN may grow, so be ready for it to get larger + char comm[2*TASK_COMM_LEN]; + long numThreads; +}; + +static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) { + if (!b->read(pathname)) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the thread just doesn't exist any more + return true; + } + + char *comm = strchr(b->getBuf(), '('); + if (comm == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++comm; + char *const str = strrchr(comm, ')'); + if (str == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + *str = '\0'; + strncpy(ps->comm, comm, sizeof(ps->comm) - 1); + ps->comm[sizeof(ps->comm) - 1] = '\0'; + + const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads); + if (count != 1) { + logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +static const char *readProcExe(DynBuf *const printb, const int pid, const int tid, DynBuf *const b) { + if (tid == -1 ? !printb->printf("/proc/%i/exe", pid) + : !printb->printf("/proc/%i/task/%i/exe", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + const int err = b->readlink(printb->getBuf()); + const char *image; + if (err == 0) { + image = strrchr(b->getBuf(), '/'); + if (image == NULL) { + image = b->getBuf(); + } else { + ++image; + } + } else if (err == -ENOENT) { + // readlink /proc/[pid]/exe returns ENOENT for kernel threads + image = "\0"; + } else { + logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + // Android apps are run by app_process but the cmdline is changed to reference the actual app name + if (strcmp(image, "app_process") != 0) { + return image; + } + + if (tid == -1 ? !printb->printf("/proc/%i/cmdline", pid) + : !printb->printf("/proc/%i/task/%i/cmdline", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + if (!b->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + return NULL; + } + + return b->getBuf(); +} + +static bool readProcTask(Buffer *const buffer, const int pid, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2) { + bool result = false; + + if (!b1->printf("/proc/%i/task", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + DIR *task = opendir(b1->getBuf()); + if (task == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(task)) != NULL) { + char *endptr; + const int tid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore task items that are not integers like ., etc... + continue; + } + + if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b1)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + const char *const image = readProcExe(printb, pid, tid, b2); + if (image == NULL) { + logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + buffer->comm(pid, tid, image, ps.comm); + } + + result = true; + + fail: + closedir(task); + + return result; +} + +bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { + bool result = false; + + DIR *proc = opendir("/proc"); + if (proc == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(proc)) != NULL) { + char *endptr; + const int pid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore proc items that are not integers like ., cpuinfo, etc... + continue; + } + + if (!printb->printf("/proc/%i/stat", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b1)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + if (sendMaps) { + if (!printb->printf("/proc/%i/maps", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + if (!b2->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the process just doesn't exist any more + continue; + } + + buffer->maps(pid, pid, b2->getBuf()); + } + if (ps.numThreads <= 1) { + const char *const image = readProcExe(printb, pid, -1, b1); + if (image == NULL) { + logg->logMessage("%s(%s:%i): readImage failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + buffer->comm(pid, pid, image, ps.comm); + } else { + if (!readProcTask(buffer, pid, printb, b1, b3)) { + logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + } + + result = true; + + fail: + closedir(proc); + + return result; +} diff --git a/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h new file mode 100644 index 000000000000..31c2eecb7aeb --- /dev/null +++ b/tools/gator/daemon/Proc.h @@ -0,0 +1,17 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 PROC_H +#define PROC_H + +class Buffer; +class DynBuf; + +bool readProc(Buffer *const buffer, bool sendMaps, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); + +#endif // PROC_H diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp new file mode 100644 index 000000000000..3a981a6427be --- /dev/null +++ b/tools/gator/daemon/Sender.cpp @@ -0,0 +1,134 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "Sender.h" + +#include +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +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, after which error messages can be sent + char magic[32]; + snprintf(magic, 32, "GATOR %i\n", PROTOCOL_VERSION); + mDataSocket->send(magic, strlen(magic)); + + gSessionData->mWaitingOnCommand = true; + logg->logMessage("Completed magic sequence"); + } + + pthread_mutex_init(&mSendMutex, NULL); +} + +Sender::~Sender() { + // Just close it as the client socket is on the stack + if (mDataSocket != NULL) { + mDataSocket->closeSocket(); + mDataSocket = NULL; + } + if (mDataFile != NULL) { + 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(); + } +} + +template +inline T min(const T a, const T b) { + return (a < b ? a : b); +} + +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 + const int alarmDuration = 8; + alarm(alarmDuration); + + // 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 + unsigned char header[5]; + header[0] = type; + Buffer::writeLEInt(header + 1, length); + mDataSocket->send((char*)&header, sizeof(header)); + } + + // 100Kbits/sec * alarmDuration sec / 8 bits/byte + const int chunkSize = 100*1000 * alarmDuration / 8; + int pos = 0; + while (true) { + mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize)); + pos += chunkSize; + if (pos >= length) { + break; + } + + // Reset the alarm + alarm(alarmDuration); + logg->logMessage("Resetting the alarm"); + } + + // 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 000000000000..33b6cc3c5d8d --- /dev/null +++ b/tools/gator/daemon/Sender.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 + +class OlySocket; + +enum { + 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; + + // Intentionally unimplemented + Sender(const Sender &); + Sender &operator=(const Sender &); +}; + +#endif //__SENDER_H__ diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp new file mode 100644 index 000000000000..14d995fc39fa --- /dev/null +++ b/tools/gator/daemon/SessionData.cpp @@ -0,0 +1,194 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "SessionData.h" + +#include +#include + +#include "SessionXML.h" +#include "Logging.h" + +SessionData* gSessionData = NULL; + +SessionData::SessionData() { + initialize(); +} + +SessionData::~SessionData() { +} + +void SessionData::initialize() { + mWaitingOnCommand = false; + mSessionIsActive = false; + mLocalCapture = false; + mOneShot = false; + mSentSummary = false; + const size_t cpuIdSize = sizeof(int)*NR_CPUS; + // Share mCpuIds across all instances of gatord + mCpuIds = (int *)mmap(NULL, cpuIdSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mCpuIds == MAP_FAILED) { + logg->logError(__FILE__, __LINE__, "Unable to mmap shared memory for cpuids"); + handleException(); + } + memset(mCpuIds, -1, cpuIdSize); + readCpuInfo(); + mConfigurationXMLPath = NULL; + mSessionXMLPath = NULL; + mEventsXMLPath = NULL; + mTargetPath = NULL; + mAPCDir = NULL; + mSampleRate = 0; + mLiveRate = 0; + mDuration = 0; + mBacktraceDepth = 0; + mTotalBufferSize = 0; + // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module + mCores = 1; + mPageSize = 0; +} + +void SessionData::parseSessionXML(char* xmlString) { + SessionXML session(xmlString); + session.parse(); + + // Set session data values - use prime numbers just below the desired value to reduce the chance of events firing at the same time + if (strcmp(session.parameters.sample_rate, "high") == 0) { + mSampleRate = 9973; // 10000 + } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { + mSampleRate = 997; // 1000 + } else if (strcmp(session.parameters.sample_rate, "low") == 0) { + mSampleRate = 97; // 100 + } else if (strcmp(session.parameters.sample_rate, "none") == 0) { + mSampleRate = 0; + } else { + logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate); + handleException(); + } + mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; + mDuration = session.parameters.duration; + + // Determine buffer size (in MB) based on buffer mode + mOneShot = true; + if (strcmp(session.parameters.buffer_mode, "streaming") == 0) { + mOneShot = false; + mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "small") == 0) { + mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) { + mTotalBufferSize = 4; + } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { + mTotalBufferSize = 16; + } else { + logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); + handleException(); + } + + mImages = session.parameters.images; + // Convert milli- to nanoseconds + mLiveRate = session.parameters.live_rate * (int64_t)1000000; + if (mLiveRate > 0 && mLocalCapture) { + logg->logMessage("Local capture is not compatable with live, disabling live"); + mLiveRate = 0; + } +} + +void SessionData::readCpuInfo() { + char temp[256]; // arbitrarily large amount + strcpy(mCoreName, "unknown"); + mMaxCpuId = -1; + + 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; + } + + bool foundCoreName = false; + int processor = -1; + while (fgets(temp, sizeof(temp), f)) { + const size_t len = strlen(temp); + + if (len == 1) { + // New section, clear the processor. Streamline will not know the cpus if the pre Linux 3.8 format of cpuinfo is encountered but also that no incorrect information will be transmitted. + processor = -1; + continue; + } + + if (len > 0) { + temp[len - 1] = '\0'; // Replace the line feed with a null + } + + const bool foundHardware = strstr(temp, "Hardware") != 0; + const bool foundCPUPart = strstr(temp, "CPU part") != 0; + const bool foundProcessor = strstr(temp, "processor") != 0; + if (foundHardware || foundCPUPart || foundProcessor) { + 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; + } + position += 2; + + if (foundHardware) { + strncpy(mCoreName, position, sizeof(mCoreName)); + mCoreName[sizeof(mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string + foundCoreName = true; + } + + if (foundCPUPart) { + const int cpuId = strtol(position, NULL, 0); + // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId + if (cpuId > mMaxCpuId) { + mMaxCpuId = cpuId; + } + if (processor >= NR_CPUS) { + logg->logMessage("Too many processors, please increase NR_CPUS"); + } else if (processor >= 0) { + mCpuIds[processor] = cpuId; + } + } + + if (foundProcessor) { + processor = strtol(position, NULL, 0); + } + } + } + + if (!foundCoreName) { + logg->logMessage("Could not determine core name from /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + } + fclose(f); +} + +uint64_t getTime() { + struct timespec ts; +#ifndef CLOCK_MONOTONIC_RAW + // Android doesn't have this defined but it was added in Linux 2.6.28 +#define CLOCK_MONOTONIC_RAW 4 +#endif + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + handleException(); + } + return (NS_PER_S*ts.tv_sec + ts.tv_nsec); +} + +int getEventKey() { + // key 0 is reserved as a timestamp + // key 1 is reserved as the marker for thread specific counters + // Odd keys are assigned by the driver, even keys by the daemon + static int key = 2; + + const int ret = key; + key += 2; + return ret; +} diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h new file mode 100644 index 000000000000..835082d86c4b --- /dev/null +++ b/tools/gator/daemon/SessionData.h @@ -0,0 +1,86 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 + +#include + +#include "Config.h" +#include "Counter.h" +#include "FSDriver.h" +#include "Hwmon.h" +#include "MaliVideoDriver.h" +#include "PerfDriver.h" + +#define PROTOCOL_VERSION 19 +#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions + +#define NS_PER_S ((uint64_t)1000000000) + +struct ImageLinkList { + char* path; + struct ImageLinkList *next; +}; + +class SessionData { +public: + static const size_t MAX_STRING_LEN = 80; + + SessionData(); + ~SessionData(); + void initialize(); + void parseSessionXML(char* xmlString); + void readCpuInfo(); + + Hwmon hwmon; + FSDriver fsDriver; + PerfDriver perf; + MaliVideoDriver maliVideo; + + char mCoreName[MAX_STRING_LEN]; + struct ImageLinkList *mImages; + char* mConfigurationXMLPath; + char* mSessionXMLPath; + char* mEventsXMLPath; + char* mTargetPath; + char* mAPCDir; + + bool mWaitingOnCommand; + bool mSessionIsActive; + bool mLocalCapture; + bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + bool mIsEBS; + bool mSentSummary; + + int mBacktraceDepth; + int mTotalBufferSize; // number of MB to use for the entire collection buffer + int mSampleRate; + int64_t mLiveRate; + int mDuration; + int mCores; + int mPageSize; + int *mCpuIds; + int mMaxCpuId; + + // PMU Counters + int mCounterOverflow; + Counter mCounters[MAX_PERFORMANCE_COUNTERS]; + +private: + // Intentionally unimplemented + SessionData(const SessionData &); + SessionData &operator=(const SessionData &); +}; + +extern SessionData* gSessionData; + +uint64_t getTime(); +int getEventKey(); + +#endif // SESSION_DATA_H diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp new file mode 100644 index 000000000000..8cdc9409ca21 --- /dev/null +++ b/tools/gator/daemon/SessionXML.cpp @@ -0,0 +1,111 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "SessionXML.h" + +#include +#include +#include + +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +static const char* TAG_SESSION = "session"; +static const char* TAG_IMAGE = "image"; + +static const char* ATTR_VERSION = "version"; +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_DURATION = "duration"; +static const char* ATTR_PATH = "path"; +static const char* ATTR_LIVE_RATE = "live_rate"; + +SessionXML::SessionXML(const char *str) { + parameters.buffer_mode[0] = 0; + parameters.sample_rate[0] = 0; + parameters.duration = 0; + parameters.call_stack_unwinding = false; + parameters.live_rate = 0; + parameters.images = NULL; + mPath = 0; + mSessionXML = (const char *)str; + logg->logMessage(mSessionXML); +} + +SessionXML::~SessionXML() { + if (mPath != 0) { + free((char *)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(); + } + + // copy to pre-allocated strings + 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); + if (mxmlElementGetAttr(node, ATTR_LIVE_RATE)) parameters.live_rate = strtol(mxmlElementGetAttr(node, ATTR_LIVE_RATE), 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 000000000000..e146094a4d17 --- /dev/null +++ b/tools/gator/daemon/SessionXML.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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" + +struct ImageLinkList; + +struct ConfigParameters { + 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 + int live_rate; + struct ImageLinkList *images; // linked list of image strings +}; + +class SessionXML { +public: + SessionXML(const char *str); + ~SessionXML(); + void parse(); + ConfigParameters parameters; +private: + const char *mSessionXML; + const char *mPath; + void sessionTag(mxml_node_t *tree, mxml_node_t *node); + void sessionImage(mxml_node_t *node); + + // Intentionally unimplemented + SessionXML(const SessionXML &); + SessionXML &operator=(const SessionXML &); +}; + +#endif // SESSION_XML_H diff --git a/tools/gator/daemon/Source.cpp b/tools/gator/daemon/Source.cpp new file mode 100644 index 000000000000..60cf704e599b --- /dev/null +++ b/tools/gator/daemon/Source.cpp @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "Source.h" + +#include "Logging.h" + +Source::Source() : mThreadID() { +} + +Source::~Source() { +} + +void Source::start() { + if (pthread_create(&mThreadID, NULL, runStatic, this)) { + logg->logError(__FILE__, __LINE__, "Failed to create source thread"); + handleException(); + } +} + +void Source::join() { + pthread_join(mThreadID, NULL); +} + +void *Source::runStatic(void *arg) { + static_cast(arg)->run(); + return NULL; +} diff --git a/tools/gator/daemon/Source.h b/tools/gator/daemon/Source.h new file mode 100644 index 000000000000..56ac3d6e94f3 --- /dev/null +++ b/tools/gator/daemon/Source.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 SOURCE_H +#define SOURCE_H + +#include + +class Sender; + +class Source { +public: + Source(); + virtual ~Source(); + + virtual bool prepare() = 0; + void start(); + virtual void run() = 0; + virtual void interrupt() = 0; + void join(); + + virtual bool isDone() = 0; + virtual void write(Sender *sender) = 0; + +private: + static void *runStatic(void *arg); + + pthread_t mThreadID; + + // Intentionally undefined + Source(const Source &); + Source &operator=(const Source &); +}; + +#endif // SOURCE_H diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp new file mode 100644 index 000000000000..2b61eaeb290d --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -0,0 +1,272 @@ +/** + * Copyright (C) ARM Limited 2011-2014. 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 "StreamlineSetup.h" + +#include "Buffer.h" +#include "CapturedXML.h" +#include "ConfigurationXML.h" +#include "Driver.h" +#include "EventsXML.h" +#include "Logging.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "Sender.h" +#include "SessionData.h" + +static const char* TAG_SESSION = "session"; +static const char* TAG_REQUEST = "request"; +static const char* TAG_CONFIGURATIONS = "configurations"; + +static const char* ATTR_TYPE = "type"; +static const char* VALUE_EVENTS = "events"; +static const char* VALUE_CONFIGURATION = "configuration"; +static const char* VALUE_COUNTERS = "counters"; +static const char* VALUE_CAPTURED = "captured"; +static const char* VALUE_DEFAULTS = "defaults"; + +StreamlineSetup::StreamlineSetup(OlySocket* s) { + bool ready = false; + char* data = NULL; + int type; + + mSocket = s; + + // 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); + } + + if (gSessionData->mCounterOverflow > 0) { + logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + handleException(); + } +} + +StreamlineSetup::~StreamlineSetup() { +} + +char* StreamlineSetup::readCommand(int* command) { + unsigned char header[5]; + char* data; + int response; + + // receive type and length + response = mSocket->receiveNBytes((char*)&header, sizeof(header)); + + // 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(); + } + + const char type = header[0]; + const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); + + // 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; + const char * attr = NULL; + + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST); + if (node) { + attr = mxmlElementGetAttr(node, ATTR_TYPE); + } + if (attr && strcmp(attr, VALUE_EVENTS) == 0) { + sendEvents(); + logg->logMessage("Sent events xml response"); + } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) { + sendConfiguration(); + logg->logMessage("Sent configuration xml response"); + } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) { + sendCounters(); + logg->logMessage("Sent counters xml response"); + } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) { + CapturedXML capturedXML; + char* capturedText = capturedXML.getXML(false); + sendData(capturedText, strlen(capturedText), RESPONSE_XML); + free(capturedText); + logg->logMessage("Sent captured xml response"); + } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) { + 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); + 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, uint32_t length, char type) { + unsigned char header[5]; + header[0] = type; + Buffer::writeLEInt(header + 1, length); + mSocket->send((char*)&header, sizeof(header)); + mSocket->send((const char*)data, length); +} + +void StreamlineSetup::sendEvents() { + EventsXML eventsXML; + char* string = eventsXML.getXML(); + sendString(string, RESPONSE_XML); + free(string); +} + +void StreamlineSetup::sendConfiguration() { + ConfigurationXML xml; + + const char* string = xml.getConfigurationXML(); + sendData(string, strlen(string), RESPONSE_XML); +} + +void StreamlineSetup::sendDefaults() { + // Send the config built into the binary + const char* xml; + unsigned int size; + ConfigurationXML::getDefaultConfigurationXml(xml, size); + + // Artificial size restriction + if (size > 1024*1024) { + logg->logError(__FILE__, __LINE__, "Corrupt default configuration file"); + handleException(); + } + + sendData(xml, size, RESPONSE_XML); +} + +void StreamlineSetup::sendCounters() { + mxml_node_t *xml; + mxml_node_t *counters; + + xml = mxmlNewXML("1.0"); + counters = mxmlNewElement(xml, "counters"); + int count = 0; + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + count += driver->writeCounters(counters); + } + + if (count == 0) { + logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); + handleException(); + } + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); +} + +void StreamlineSetup::writeConfiguration(char* xml) { + char path[PATH_MAX]; + + ConfigurationXML::getPath(path); + + 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 + { ConfigurationXML configuration; } + + if (gSessionData->mCounterOverflow > 0) { + logg->logError(__FILE__, __LINE__, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow); + handleException(); + } +} diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h new file mode 100644 index 000000000000..b380f46b98f0 --- /dev/null +++ b/tools/gator/daemon/StreamlineSetup.h @@ -0,0 +1,50 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 +#include + +class OlySocket; + +// 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: + OlySocket* mSocket; + + char* readCommand(int*); + void handleRequest(char* xml); + void handleDeliver(char* xml); + void sendData(const char* data, uint32_t length, char type); + void sendString(const char* string, int type) {sendData(string, strlen(string), type);} + void sendEvents(); + void sendConfiguration(); + void sendDefaults(); + void sendCounters(); + void writeConfiguration(char* xml); + + // Intentionally unimplemented + StreamlineSetup(const StreamlineSetup &); + StreamlineSetup &operator=(const StreamlineSetup &); +}; + +#endif //__STREAMLINE_SETUP_H__ diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp new file mode 100644 index 000000000000..54d45751e3c9 --- /dev/null +++ b/tools/gator/daemon/UEvent.cpp @@ -0,0 +1,76 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 "UEvent.h" + +#include +#include +#include + +#include + +#include "Logging.h" + +static const char EMPTY[] = ""; +static const char ACTION[] = "ACTION="; +static const char DEVPATH[] = "DEVPATH="; +static const char SUBSYSTEM[] = "SUBSYSTEM="; + +UEvent::UEvent() : mFd(-1) { +} + +UEvent::~UEvent() { + if (mFd >= 0) { + close(mFd); + } +} + +bool UEvent::init() { + mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct sockaddr_nl sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.nl_family = AF_NETLINK; + sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events + sockaddr.nl_pid = 0; + if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) { + logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool UEvent::read(UEventResult *const result) { + ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0); + if (bytes <= 0) { + logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + result->mAction = EMPTY; + result->mDevPath = EMPTY; + result->mSubsystem = EMPTY; + + for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) { + char *const str = result->mBuf + pos; + if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) { + result->mAction = str + sizeof(ACTION) - 1; + } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) { + result->mDevPath = str + sizeof(DEVPATH) - 1; + } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) { + result->mSubsystem = str + sizeof(SUBSYSTEM) - 1; + } + } + + return true; +} diff --git a/tools/gator/daemon/UEvent.h b/tools/gator/daemon/UEvent.h new file mode 100644 index 000000000000..2f7ef2c93f5d --- /dev/null +++ b/tools/gator/daemon/UEvent.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) ARM Limited 2013-2014. 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 UEVENT_H +#define UEVENT_H + +struct UEventResult { + const char *mAction; + const char *mDevPath; + const char *mSubsystem; + char mBuf[1<<13]; +}; + +class UEvent { +public: + UEvent(); + ~UEvent(); + + bool init(); + bool read(UEventResult *const result); + int getFd() const { return mFd; } + +private: + int mFd; + + // Intentionally undefined + UEvent(const UEvent &); + UEvent &operator=(const UEvent &); +}; + +#endif // UEVENT_H diff --git a/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp new file mode 100644 index 000000000000..8c328e0e0fb5 --- /dev/null +++ b/tools/gator/daemon/UserSpaceSource.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 "UserSpaceSource.h" + +#include +#include + +#include "Child.h" +#include "DriverSource.h" +#include "Logging.h" +#include "SessionData.h" + +#define NS_PER_US 1000 + +extern Child *child; + +UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) { +} + +UserSpaceSource::~UserSpaceSource() { +} + +bool UserSpaceSource::prepare() { + return true; +} + +void UserSpaceSource::run() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); + + gSessionData->hwmon.start(); + gSessionData->fsDriver.start(); + + int64_t monotonic_started = 0; + while (monotonic_started <= 0) { + usleep(10); + + if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); + handleException(); + } + } + + uint64_t next_time = 0; + while (gSessionData->mSessionIsActive) { + const uint64_t curr_time = getTime() - monotonic_started; + // Sample ten times a second ignoring gSessionData->mSampleRate + next_time += NS_PER_S/10;//gSessionData->mSampleRate; + if (next_time < curr_time) { + logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); + next_time = curr_time; + } + + if (mBuffer.eventHeader(curr_time)) { + gSessionData->hwmon.read(&mBuffer); + gSessionData->fsDriver.read(&mBuffer); + // Only check after writing all counters so that time and corresponding counters appear in the same frame + mBuffer.check(curr_time); + } + + if (mBuffer.bytesAvailable() <= 0) { + logg->logMessage("One shot (counters)"); + child->endSession(); + } + + usleep((next_time - curr_time)/NS_PER_US); + } + + mBuffer.setDone(); +} + +void UserSpaceSource::interrupt() { + // Do nothing +} + +bool UserSpaceSource::isDone() { + return mBuffer.isDone(); +} + +void UserSpaceSource::write(Sender *sender) { + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } +} diff --git a/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h new file mode 100644 index 000000000000..9b3666016dc5 --- /dev/null +++ b/tools/gator/daemon/UserSpaceSource.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 USERSPACESOURCE_H +#define USERSPACESOURCE_H + +#include + +#include "Buffer.h" +#include "Source.h" + +// User space counters +class UserSpaceSource : public Source { +public: + UserSpaceSource(sem_t *senderSem); + ~UserSpaceSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + Buffer mBuffer; + + // Intentionally unimplemented + UserSpaceSource(const UserSpaceSource &); + UserSpaceSource &operator=(const UserSpaceSource &); +}; + +#endif // USERSPACESOURCE_H diff --git a/tools/gator/daemon/c++.cpp b/tools/gator/daemon/c++.cpp new file mode 100644 index 000000000000..6041e5e96469 --- /dev/null +++ b/tools/gator/daemon/c++.cpp @@ -0,0 +1,40 @@ +/** + * Minimal set of C++ functions so that libstdc++ is not required + * + * Copyright (C) ARM Limited 2010-2014. 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 + +void operator delete(void *ptr) { + if (ptr != NULL) { + free(ptr); + } +} + +void operator delete[](void *ptr) { + operator delete(ptr); +} + +void *operator new(size_t size) { + void *ptr = malloc(size == 0 ? 1 : size); + if (ptr == NULL) { + abort(); + } + return ptr; +} + +void *operator new[](size_t size) { + return operator new(size); +} + +extern "C" +void __cxa_pure_virtual() { + printf("pure virtual method called\n"); + abort(); +} diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk new file mode 100644 index 000000000000..769a92e51a35 --- /dev/null +++ b/tools/gator/daemon/common.mk @@ -0,0 +1,52 @@ +# -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 +CPPFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors +CXXFLAGS += -fno-rtti -Wextra # -Weffc++ +ifeq ($(WERROR),1) + CPPFLAGS += -Werror +endif +# -s strips the binary of debug info +LDFLAGS += -s +LDLIBS += -lrt -lm -pthread +TARGET = gatord +C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c) +CXX_SRC = $(wildcard *.cpp) + +all: $(TARGET) + +events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml + cat $^ > $@ + +include $(wildcard *.d) +include $(wildcard mxml/*.d) + +EventsXML.cpp: events_xml.h +ConfigurationXML.cpp: defaults_xml.h + +# Don't regenerate conf-lex.c or conf-parse.c +libsensors/conf-lex.c: ; +libsensors/conf-parse.c: ; + +%_xml.h: %.xml escape + ./escape $< > $@ + +%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +$(TARGET): $(CXX_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +# Intentionally ignore CC as a native binary is required +escape: escape.c + gcc $^ -o $@ + +clean: + rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml new file mode 100644 index 000000000000..39a0f656f7e6 --- /dev/null +++ b/tools/gator/daemon/defaults.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c new file mode 100644 index 000000000000..2b0863aaf425 --- /dev/null +++ b/tools/gator/daemon/escape.c @@ -0,0 +1,75 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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. + */ + +/* + * The Makefile in the daemon folder builds and executes 'escape' + * 'escape' creates configuration_xml.h from configuration.xml and events_xml.h from events-*.xml + * these genereated xml files are then #included and built as part of the gatord binary + */ + +#include +#include +#include +#include +#include + +static void print_escaped_path(char *path) { + if (isdigit(*path)) { + printf("__"); + } + for (; *path != '\0'; ++path) { + printf("%c", isalnum(*path) ? *path : '_'); + } +} + +int main(int argc, char *argv[]) { + int i; + char *path; + FILE *in = NULL; + int ch; + unsigned int len = 0; + + for (i = 1; i < argc && argv[i][0] == '-'; ++i) ; + if (i == argc) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + path = argv[i]; + + errno = 0; + if ((in = fopen(path, "r")) == NULL) { + fprintf(stderr, "Unable to open '%s': %s\n", path, strerror(errno)); + return EXIT_FAILURE; + } + + printf("static const unsigned char "); + print_escaped_path(path); + printf("[] = {"); + for (;;) { + ch = fgetc(in); + if (len != 0) { + printf(","); + } + if (len % 12 == 0) { + printf("\n "); + } + // Write out a null character after the contents of the file but do not increment len + printf(" 0x%.2x", (ch == EOF ? 0 : ch)); + if (ch == EOF) { + break; + } + ++len; + } + printf("\n};\nstatic const unsigned int "); + print_escaped_path(path); + printf("_len = %i;\n", len); + + fclose(in); + + return EXIT_SUCCESS; +} diff --git a/tools/gator/daemon/events-ARM11.xml b/tools/gator/daemon/events-ARM11.xml new file mode 100644 index 000000000000..57e323546314 --- /dev/null +++ b/tools/gator/daemon/events-ARM11.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ARM11MPCore.xml b/tools/gator/daemon/events-ARM11MPCore.xml new file mode 100644 index 000000000000..2d5c5e199e66 --- /dev/null +++ b/tools/gator/daemon/events-ARM11MPCore.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml new file mode 100644 index 000000000000..20002efd1543 --- /dev/null +++ b/tools/gator/daemon/events-CCI-400.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-CCN-504.xml b/tools/gator/daemon/events-CCN-504.xml new file mode 100644 index 000000000000..6ef3e6483717 --- /dev/null +++ b/tools/gator/daemon/events-CCN-504.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml new file mode 100644 index 000000000000..9c04354ad137 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A12.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml new file mode 100644 index 000000000000..f50e55d66195 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A15.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml new file mode 100644 index 000000000000..d67581d77c08 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A5.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A53.xml b/tools/gator/daemon/events-Cortex-A53.xml new file mode 100644 index 000000000000..5ba17907d5ab --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A53.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A57.xml b/tools/gator/daemon/events-Cortex-A57.xml new file mode 100644 index 000000000000..fbe96c2d4eb2 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A57.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml new file mode 100644 index 000000000000..6e078b3cffa3 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A7.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml new file mode 100644 index 000000000000..a69e25ab2c34 --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A8.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml new file mode 100644 index 000000000000..3e7f8289062e --- /dev/null +++ b/tools/gator/daemon/events-Cortex-A9.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Filesystem.xml b/tools/gator/daemon/events-Filesystem.xml new file mode 100644 index 000000000000..5feeb9014a63 --- /dev/null +++ b/tools/gator/daemon/events-Filesystem.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/tools/gator/daemon/events-Krait-architected.xml b/tools/gator/daemon/events-Krait-architected.xml new file mode 100644 index 000000000000..b8d3bcb48de7 --- /dev/null +++ b/tools/gator/daemon/events-Krait-architected.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-L2C-310.xml b/tools/gator/daemon/events-L2C-310.xml new file mode 100644 index 000000000000..923fb90334d0 --- /dev/null +++ b/tools/gator/daemon/events-L2C-310.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml new file mode 100644 index 000000000000..c306dd62208e --- /dev/null +++ b/tools/gator/daemon/events-Linux.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml new file mode 100644 index 000000000000..0a95dfeb6485 --- /dev/null +++ b/tools/gator/daemon/events-Mali-4xx.xml @@ -0,0 +1,245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml new file mode 100644 index 000000000000..5e8979704870 --- /dev/null +++ b/tools/gator/daemon/events-Mali-T6xx.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml new file mode 100644 index 000000000000..df2796262473 --- /dev/null +++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Mali-V500.xml b/tools/gator/daemon/events-Mali-V500.xml new file mode 100644 index 000000000000..d2751e7239b5 --- /dev/null +++ b/tools/gator/daemon/events-Mali-V500.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Perf-Hardware.xml b/tools/gator/daemon/events-Perf-Hardware.xml new file mode 100644 index 000000000000..423696f82420 --- /dev/null +++ b/tools/gator/daemon/events-Perf-Hardware.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Scorpion.xml b/tools/gator/daemon/events-Scorpion.xml new file mode 100644 index 000000000000..fa716fdc4840 --- /dev/null +++ b/tools/gator/daemon/events-Scorpion.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-ScorpionMP.xml b/tools/gator/daemon/events-ScorpionMP.xml new file mode 100644 index 000000000000..c648ccefb287 --- /dev/null +++ b/tools/gator/daemon/events-ScorpionMP.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events_footer.xml b/tools/gator/daemon/events_footer.xml new file mode 100644 index 000000000000..cd2b44665bab --- /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 000000000000..38ec4c03246e --- /dev/null +++ b/tools/gator/daemon/events_header.xml @@ -0,0 +1,2 @@ + + diff --git a/tools/gator/daemon/k/perf_event.3.12.h b/tools/gator/daemon/k/perf_event.3.12.h new file mode 100644 index 000000000000..e886c48cadf6 --- /dev/null +++ b/tools/gator/daemon/k/perf_event.3.12.h @@ -0,0 +1,792 @@ +/* + * Performance events: + * + * Copyright (C) 2008-2009, Thomas Gleixner + * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra + * + * Data type definitions, declarations, prototypes. + * + * Started by: Thomas Gleixner and Ingo Molnar + * + * For licencing details see kernel-base/COPYING + */ +#ifndef _LINUX_PERF_EVENT_H +#define _LINUX_PERF_EVENT_H + +#include +#include +#include + +/* + * User-space ABI bits: + */ + +/* + * attr.type + */ +enum perf_type_id { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, + PERF_TYPE_BREAKPOINT = 5, + + PERF_TYPE_MAX, /* non-ABI */ +}; + +/* + * Generalized performance event event_id types, used by the + * attr.event_id parameter of the sys_perf_event_open() + * syscall: + */ +enum perf_hw_id { + /* + * Common hardware events, generalized by the kernel: + */ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, + PERF_COUNT_HW_REF_CPU_CYCLES = 9, + + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + PERF_COUNT_HW_CACHE_NODE = 6, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/* + * Special "software" events provided by the kernel, even if the hardware + * does not support performance events. These events measure various + * physical and sw events of the kernel (and allow the profiling of them as + * well): + */ +enum perf_sw_ids { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, + PERF_COUNT_SW_DUMMY = 9, + + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +/* + * Bits that can be set in attr.sample_type to request information + * in the overflow packets. + */ +enum perf_event_sample_format { + PERF_SAMPLE_IP = 1U << 0, + PERF_SAMPLE_TID = 1U << 1, + PERF_SAMPLE_TIME = 1U << 2, + PERF_SAMPLE_ADDR = 1U << 3, + PERF_SAMPLE_READ = 1U << 4, + PERF_SAMPLE_CALLCHAIN = 1U << 5, + PERF_SAMPLE_ID = 1U << 6, + PERF_SAMPLE_CPU = 1U << 7, + PERF_SAMPLE_PERIOD = 1U << 8, + PERF_SAMPLE_STREAM_ID = 1U << 9, + PERF_SAMPLE_RAW = 1U << 10, + PERF_SAMPLE_BRANCH_STACK = 1U << 11, + PERF_SAMPLE_REGS_USER = 1U << 12, + PERF_SAMPLE_STACK_USER = 1U << 13, + PERF_SAMPLE_WEIGHT = 1U << 14, + PERF_SAMPLE_DATA_SRC = 1U << 15, + PERF_SAMPLE_IDENTIFIER = 1U << 16, + + PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */ +}; + +/* + * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set + * + * If the user does not pass priv level information via branch_sample_type, + * the kernel uses the event's priv level. Branch and event priv levels do + * not have to match. Branch priv level is checked for permissions. + * + * The branch types can be combined, however BRANCH_ANY covers all types + * of branches and therefore it supersedes all the other types. + */ +enum perf_branch_sample_type { + PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ + PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ + PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ + + PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ + PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ + PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ + PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ + PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */ + PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */ + PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */ + + PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */ +}; + +#define PERF_SAMPLE_BRANCH_PLM_ALL \ + (PERF_SAMPLE_BRANCH_USER|\ + PERF_SAMPLE_BRANCH_KERNEL|\ + PERF_SAMPLE_BRANCH_HV) + +/* + * Values to determine ABI of the registers dump. + */ +enum perf_sample_regs_abi { + PERF_SAMPLE_REGS_ABI_NONE = 0, + PERF_SAMPLE_REGS_ABI_32 = 1, + PERF_SAMPLE_REGS_ABI_64 = 2, +}; + +/* + * The format of the data returned by read() on a perf event fd, + * as specified by attr.read_format: + * + * struct read_format { + * { u64 value; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 id; } && PERF_FORMAT_ID + * } && !PERF_FORMAT_GROUP + * + * { u64 nr; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 value; + * { u64 id; } && PERF_FORMAT_ID + * } cntr[nr]; + * } && PERF_FORMAT_GROUP + * }; + */ +enum perf_event_read_format { + PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, + PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, + PERF_FORMAT_ID = 1U << 2, + PERF_FORMAT_GROUP = 1U << 3, + + PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ +}; + +#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ +#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ +#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ +#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ + /* add: sample_stack_user */ + +/* + * Hardware event_id to monitor via a performance monitoring event: + */ +struct perf_event_attr { + + /* + * Major type: hardware/software/tracepoint/etc. + */ + __u32 type; + + /* + * Size of the attr structure, for fwd/bwd compat. + */ + __u32 size; + + /* + * Type specific configuration information. + */ + __u64 config; + + union { + __u64 sample_period; + __u64 sample_freq; + }; + + __u64 sample_type; + __u64 read_format; + + __u64 disabled : 1, /* off by default */ + inherit : 1, /* children inherit it */ + pinned : 1, /* must always be on PMU */ + exclusive : 1, /* only group on PMU */ + exclude_user : 1, /* don't count user */ + exclude_kernel : 1, /* ditto kernel */ + exclude_hv : 1, /* ditto hypervisor */ + exclude_idle : 1, /* don't count when idle */ + mmap : 1, /* include mmap data */ + comm : 1, /* include comm data */ + freq : 1, /* use freq, not period */ + inherit_stat : 1, /* per task counts */ + enable_on_exec : 1, /* next exec enables */ + task : 1, /* trace fork/exit */ + watermark : 1, /* wakeup_watermark */ + /* + * precise_ip: + * + * 0 - SAMPLE_IP can have arbitrary skid + * 1 - SAMPLE_IP must have constant skid + * 2 - SAMPLE_IP requested to have 0 skid + * 3 - SAMPLE_IP must have 0 skid + * + * See also PERF_RECORD_MISC_EXACT_IP + */ + precise_ip : 2, /* skid constraint */ + mmap_data : 1, /* non-exec mmap data */ + sample_id_all : 1, /* sample_type all events */ + + exclude_host : 1, /* don't count in host */ + exclude_guest : 1, /* don't count in guest */ + + exclude_callchain_kernel : 1, /* exclude kernel callchains */ + exclude_callchain_user : 1, /* exclude user callchains */ + mmap2 : 1, /* include mmap with inode data */ + + __reserved_1 : 40; + + union { + __u32 wakeup_events; /* wakeup every n events */ + __u32 wakeup_watermark; /* bytes before wakeup */ + }; + + __u32 bp_type; + union { + __u64 bp_addr; + __u64 config1; /* extension of config */ + }; + union { + __u64 bp_len; + __u64 config2; /* extension of config1 */ + }; + __u64 branch_sample_type; /* enum perf_branch_sample_type */ + + /* + * Defines set of user regs to dump on samples. + * See asm/perf_regs.h for details. + */ + __u64 sample_regs_user; + + /* + * Defines size of the user stack to dump on samples. + */ + __u32 sample_stack_user; + + /* Align to u64. */ + __u32 __reserved_2; +}; + +#define perf_flags(attr) (*(&(attr)->read_format + 1)) + +/* + * Ioctls that can be done on a perf event fd: + */ +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) +#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) + +enum perf_event_ioc_flags { + PERF_IOC_FLAG_GROUP = 1U << 0, +}; + +/* + * Structure of the page that can be mapped via mmap + */ +struct perf_event_mmap_page { + __u32 version; /* version number of this structure */ + __u32 compat_version; /* lowest version this is compat with */ + + /* + * Bits needed to read the hw events in user-space. + * + * u32 seq, time_mult, time_shift, idx, width; + * u64 count, enabled, running; + * u64 cyc, time_offset; + * s64 pmc = 0; + * + * do { + * seq = pc->lock; + * barrier() + * + * enabled = pc->time_enabled; + * running = pc->time_running; + * + * if (pc->cap_usr_time && enabled != running) { + * cyc = rdtsc(); + * time_offset = pc->time_offset; + * time_mult = pc->time_mult; + * time_shift = pc->time_shift; + * } + * + * idx = pc->index; + * count = pc->offset; + * if (pc->cap_usr_rdpmc && idx) { + * width = pc->pmc_width; + * pmc = rdpmc(idx - 1); + * } + * + * barrier(); + * } while (pc->lock != seq); + * + * NOTE: for obvious reason this only works on self-monitoring + * processes. + */ + __u32 lock; /* seqlock for synchronization */ + __u32 index; /* hardware event identifier */ + __s64 offset; /* add to hardware event value */ + __u64 time_enabled; /* time event active */ + __u64 time_running; /* time event on cpu */ + union { + __u64 capabilities; + struct { + __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */ + cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */ + + cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */ + cap_user_time : 1, /* The time_* fields are used */ + cap_user_time_zero : 1, /* The time_zero field is used */ + cap_____res : 59; + }; + }; + + /* + * If cap_usr_rdpmc this field provides the bit-width of the value + * read using the rdpmc() or equivalent instruction. This can be used + * to sign extend the result like: + * + * pmc <<= 64 - width; + * pmc >>= 64 - width; // signed shift right + * count += pmc; + */ + __u16 pmc_width; + + /* + * If cap_usr_time the below fields can be used to compute the time + * delta since time_enabled (in ns) using rdtsc or similar. + * + * u64 quot, rem; + * u64 delta; + * + * quot = (cyc >> time_shift); + * rem = cyc & ((1 << time_shift) - 1); + * delta = time_offset + quot * time_mult + + * ((rem * time_mult) >> time_shift); + * + * Where time_offset,time_mult,time_shift and cyc are read in the + * seqcount loop described above. This delta can then be added to + * enabled and possible running (if idx), improving the scaling: + * + * enabled += delta; + * if (idx) + * running += delta; + * + * quot = count / running; + * rem = count % running; + * count = quot * enabled + (rem * enabled) / running; + */ + __u16 time_shift; + __u32 time_mult; + __u64 time_offset; + /* + * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated + * from sample timestamps. + * + * time = timestamp - time_zero; + * quot = time / time_mult; + * rem = time % time_mult; + * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; + * + * And vice versa: + * + * quot = cyc >> time_shift; + * rem = cyc & ((1 << time_shift) - 1); + * timestamp = time_zero + quot * time_mult + + * ((rem * time_mult) >> time_shift); + */ + __u64 time_zero; + __u32 size; /* Header size up to __reserved[] fields. */ + + /* + * Hole for extension of the self monitor capabilities + */ + + __u8 __reserved[118*8+4]; /* align to 1k. */ + + /* + * Control data for the mmap() data buffer. + * + * User-space reading the @data_head value should issue an smp_rmb(), + * after reading this value. + * + * When the mapping is PROT_WRITE the @data_tail value should be + * written by userspace to reflect the last read data, after issueing + * an smp_mb() to separate the data read from the ->data_tail store. + * In this case the kernel will not over-write unread data. + * + * See perf_output_put_handle() for the data ordering. + */ + __u64 data_head; /* head in the data section */ + __u64 data_tail; /* user-space written tail */ +}; + +#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) +#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (2 << 0) +#define PERF_RECORD_MISC_HYPERVISOR (3 << 0) +#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) +#define PERF_RECORD_MISC_GUEST_USER (5 << 0) + +#define PERF_RECORD_MISC_MMAP_DATA (1 << 13) +/* + * Indicates that the content of PERF_SAMPLE_IP points to + * the actual instruction that triggered the event. See also + * perf_event_attr::precise_ip. + */ +#define PERF_RECORD_MISC_EXACT_IP (1 << 14) +/* + * Reserve the last bit to indicate some extended misc field + */ +#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) + +struct perf_event_header { + __u32 type; + __u16 misc; + __u16 size; +}; + +enum perf_event_type { + + /* + * If perf_event_attr.sample_id_all is set then all event types will + * have the sample_type selected fields related to where/when + * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, + * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed + * just after the perf_event_header and the fields already present for + * the existing fields, i.e. at the end of the payload. That way a newer + * perf.data file will be supported by older perf tools, with these new + * optional fields being ignored. + * + * struct sample_id { + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * } && perf_event_attr::sample_id_all + * + * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The + * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed + * relative to header.size. + */ + + /* + * The MMAP events record the PROT_EXEC mappings so that we can + * correlate userspace IPs to code. They have the following structure: + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP = 1, + + /* + * struct { + * struct perf_event_header header; + * u64 id; + * u64 lost; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_LOST = 2, + + /* + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * char comm[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_COMM = 3, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_EXIT = 4, + + /* + * struct { + * struct perf_event_header header; + * u64 time; + * u64 id; + * u64 stream_id; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_THROTTLE = 5, + PERF_RECORD_UNTHROTTLE = 6, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_FORK = 7, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, tid; + * + * struct read_format values; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_READ = 8, + + /* + * struct { + * struct perf_event_header header; + * + * # + * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. + * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position + * # is fixed relative to header. + * # + * + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * { u64 ip; } && PERF_SAMPLE_IP + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 addr; } && PERF_SAMPLE_ADDR + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 period; } && PERF_SAMPLE_PERIOD + * + * { struct read_format values; } && PERF_SAMPLE_READ + * + * { u64 nr, + * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN + * + * # + * # The RAW record below is opaque data wrt the ABI + * # + * # That is, the ABI doesn't make any promises wrt to + * # the stability of its content, it may vary depending + * # on event, hardware, kernel version and phase of + * # the moon. + * # + * # In other words, PERF_SAMPLE_RAW contents are not an ABI. + * # + * + * { u32 size; + * char data[size];}&& PERF_SAMPLE_RAW + * + * { u64 nr; + * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * + * { u64 abi; # enum perf_sample_regs_abi + * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER + * + * { u64 size; + * char data[size]; + * u64 dyn_size; } && PERF_SAMPLE_STACK_USER + * + * { u64 weight; } && PERF_SAMPLE_WEIGHT + * { u64 data_src; } && PERF_SAMPLE_DATA_SRC + * }; + */ + PERF_RECORD_SAMPLE = 9, + + /* + * The MMAP2 records are an augmented version of MMAP, they add + * maj, min, ino numbers to be used to uniquely identify each mapping + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * u32 maj; + * u32 min; + * u64 ino; + * u64 ino_generation; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP2 = 10, + + PERF_RECORD_MAX, /* non-ABI */ +}; + +#define PERF_MAX_STACK_DEPTH 127 + +enum perf_callchain_context { + PERF_CONTEXT_HV = (__u64)-32, + PERF_CONTEXT_KERNEL = (__u64)-128, + PERF_CONTEXT_USER = (__u64)-512, + + PERF_CONTEXT_GUEST = (__u64)-2048, + PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, + PERF_CONTEXT_GUEST_USER = (__u64)-2560, + + PERF_CONTEXT_MAX = (__u64)-4095, +}; + +#define PERF_FLAG_FD_NO_GROUP (1U << 0) +#define PERF_FLAG_FD_OUTPUT (1U << 1) +#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ + +union perf_mem_data_src { + __u64 val; + struct { + __u64 mem_op:5, /* type of opcode */ + mem_lvl:14, /* memory hierarchy level */ + mem_snoop:5, /* snoop mode */ + mem_lock:2, /* lock instr */ + mem_dtlb:7, /* tlb access */ + mem_rsvd:31; + }; +}; + +/* type of opcode (load/store/prefetch,code) */ +#define PERF_MEM_OP_NA 0x01 /* not available */ +#define PERF_MEM_OP_LOAD 0x02 /* load instruction */ +#define PERF_MEM_OP_STORE 0x04 /* store instruction */ +#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ +#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ +#define PERF_MEM_OP_SHIFT 0 + +/* memory hierarchy (memory level, hit or miss) */ +#define PERF_MEM_LVL_NA 0x01 /* not available */ +#define PERF_MEM_LVL_HIT 0x02 /* hit level */ +#define PERF_MEM_LVL_MISS 0x04 /* miss level */ +#define PERF_MEM_LVL_L1 0x08 /* L1 */ +#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ +#define PERF_MEM_LVL_L2 0x20 /* L2 */ +#define PERF_MEM_LVL_L3 0x40 /* L3 */ +#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ +#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ +#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ +#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ +#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ +#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ +#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ +#define PERF_MEM_LVL_SHIFT 5 + +/* snoop mode */ +#define PERF_MEM_SNOOP_NA 0x01 /* not available */ +#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ +#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ +#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ +#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ +#define PERF_MEM_SNOOP_SHIFT 19 + +/* locked instruction */ +#define PERF_MEM_LOCK_NA 0x01 /* not available */ +#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ +#define PERF_MEM_LOCK_SHIFT 24 + +/* TLB access */ +#define PERF_MEM_TLB_NA 0x01 /* not available */ +#define PERF_MEM_TLB_HIT 0x02 /* hit level */ +#define PERF_MEM_TLB_MISS 0x04 /* miss level */ +#define PERF_MEM_TLB_L1 0x08 /* L1 */ +#define PERF_MEM_TLB_L2 0x10 /* L2 */ +#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ +#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ +#define PERF_MEM_TLB_SHIFT 26 + +#define PERF_MEM_S(a, s) \ + (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) + +/* + * single taken branch record layout: + * + * from: source instruction (may not always be a branch insn) + * to: branch target + * mispred: branch target was mispredicted + * predicted: branch target was predicted + * + * support for mispred, predicted is optional. In case it + * is not supported mispred = predicted = 0. + * + * in_tx: running in a hardware transaction + * abort: aborting a hardware transaction + */ +struct perf_branch_entry { + __u64 from; + __u64 to; + __u64 mispred:1, /* target mispredicted */ + predicted:1,/* target predicted */ + in_tx:1, /* in transaction */ + abort:1, /* transaction abort */ + reserved:60; +}; + +#endif /* _LINUX_PERF_EVENT_H */ diff --git a/tools/gator/daemon/k/perf_event.h b/tools/gator/daemon/k/perf_event.h new file mode 120000 index 000000000000..e5dff8c21ef4 --- /dev/null +++ b/tools/gator/daemon/k/perf_event.h @@ -0,0 +1 @@ +perf_event.3.12.h \ No newline at end of file diff --git a/tools/gator/daemon/libsensors/COPYING.LGPL b/tools/gator/daemon/libsensors/COPYING.LGPL new file mode 100644 index 000000000000..4362b49151d7 --- /dev/null +++ b/tools/gator/daemon/libsensors/COPYING.LGPL @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/tools/gator/daemon/libsensors/access.c b/tools/gator/daemon/libsensors/access.c new file mode 100644 index 000000000000..8e227e2550db --- /dev/null +++ b/tools/gator/daemon/libsensors/access.c @@ -0,0 +1,561 @@ +/* + access.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include +#include +#include +#include "access.h" +#include "sensors.h" +#include "data.h" +#include "error.h" +#include "sysfs.h" + +/* We watch the recursion depth for variables only, as an easy way to + detect cycles. */ +#define DEPTH_MAX 8 + +static int sensors_eval_expr(const sensors_chip_features *chip_features, + const sensors_expr *expr, + double val, int depth, double *result); + +/* Compare two chips name descriptions, to see whether they could match. + Return 0 if it does not match, return 1 if it does match. */ +static int sensors_match_chip(const sensors_chip_name *chip1, + const sensors_chip_name *chip2) +{ + if ((chip1->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) && + (chip2->prefix != SENSORS_CHIP_NAME_PREFIX_ANY) && + strcmp(chip1->prefix, chip2->prefix)) + return 0; + + if ((chip1->bus.type != SENSORS_BUS_TYPE_ANY) && + (chip2->bus.type != SENSORS_BUS_TYPE_ANY) && + (chip1->bus.type != chip2->bus.type)) + return 0; + + if ((chip1->bus.nr != SENSORS_BUS_NR_ANY) && + (chip2->bus.nr != SENSORS_BUS_NR_ANY) && + (chip1->bus.nr != chip2->bus.nr)) + return 0; + + if ((chip1->addr != chip2->addr) && + (chip1->addr != SENSORS_CHIP_NAME_ADDR_ANY) && + (chip2->addr != SENSORS_CHIP_NAME_ADDR_ANY)) + return 0; + + return 1; +} + +/* Returns, one by one, a pointer to all sensor_chip structs of the + config file which match with the given chip name. Last should be + the value returned by the last call, or NULL if this is the first + call. Returns NULL if no more matches are found. Do not modify + the struct the return value points to! + Note that this visits the list of chips from last to first. Usually, + you want the match that was latest in the config file. */ +static sensors_chip * +sensors_for_all_config_chips(const sensors_chip_name *name, + const sensors_chip *last) +{ + int nr, i; + sensors_chip_name_list chips; + + for (nr = last ? last - sensors_config_chips - 1 : + sensors_config_chips_count - 1; nr >= 0; nr--) { + + chips = sensors_config_chips[nr].chips; + for (i = 0; i < chips.fits_count; i++) { + if (sensors_match_chip(&chips.fits[i], name)) + return sensors_config_chips + nr; + } + } + return NULL; +} + +/* Look up a chip in the intern chip list, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_chip_features * +sensors_lookup_chip(const sensors_chip_name *name) +{ + int i; + + for (i = 0; i < sensors_proc_chips_count; i++) + if (sensors_match_chip(&sensors_proc_chips[i].chip, name)) + return &sensors_proc_chips[i]; + + return NULL; +} + +/* Look up a subfeature of the given chip, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_subfeature * +sensors_lookup_subfeature_nr(const sensors_chip_features *chip, + int subfeat_nr) +{ + if (subfeat_nr < 0 || + subfeat_nr >= chip->subfeature_count) + return NULL; + return chip->subfeature + subfeat_nr; +} + +/* Look up a feature of the given chip, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_feature * +sensors_lookup_feature_nr(const sensors_chip_features *chip, int feat_nr) +{ + if (feat_nr < 0 || + feat_nr >= chip->feature_count) + return NULL; + return chip->feature + feat_nr; +} + +/* Look up a subfeature by name, and return a pointer to it. + Do not modify the struct the return value points to! Returns NULL if + not found.*/ +static const sensors_subfeature * +sensors_lookup_subfeature_name(const sensors_chip_features *chip, + const char *name) +{ + int j; + + for (j = 0; j < chip->subfeature_count; j++) + if (!strcmp(chip->subfeature[j].name, name)) + return chip->subfeature + j; + return NULL; +} + +/* Check whether the chip name is an 'absolute' name, which can only match + one chip, or whether it has wildcards. Returns 0 if it is absolute, 1 + if there are wildcards. */ +int sensors_chip_name_has_wildcards(const sensors_chip_name *chip) +{ + if ((chip->prefix == SENSORS_CHIP_NAME_PREFIX_ANY) || + (chip->bus.type == SENSORS_BUS_TYPE_ANY) || + (chip->bus.nr == SENSORS_BUS_NR_ANY) || + (chip->addr == SENSORS_CHIP_NAME_ADDR_ANY)) + return 1; + else + return 0; +} + +/* Look up the label for a given feature. Note that chip should not + contain wildcard values! The returned string is newly allocated (free it + yourself). On failure, NULL is returned. + If no label exists for this feature, its name is returned itself. */ +char *sensors_get_label(const sensors_chip_name *name, + const sensors_feature *feature) +{ + char *label; + const sensors_chip *chip; + char buf[PATH_MAX]; + FILE *f; + int i; + + if (sensors_chip_name_has_wildcards(name)) + return NULL; + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->labels_count; i++) + if (!strcmp(feature->name, chip->labels[i].name)) { + label = chip->labels[i].value; + goto sensors_get_label_exit; + } + + /* No user specified label, check for a _label sysfs file */ + snprintf(buf, PATH_MAX, "%s/%s_label", name->path, feature->name); + + if ((f = fopen(buf, "r"))) { + i = fread(buf, 1, sizeof(buf), f); + fclose(f); + if (i > 0) { + /* i - 1 to strip the '\n' at the end */ + buf[i - 1] = 0; + label = buf; + goto sensors_get_label_exit; + } + } + + /* No label, return the feature name instead */ + label = feature->name; + +sensors_get_label_exit: + label = strdup(label); + if (!label) + sensors_fatal_error(__func__, "Allocating label text"); + return label; +} + +/* Looks up whether a feature should be ignored. Returns + 1 if it should be ignored, 0 if not. */ +static int sensors_get_ignored(const sensors_chip_name *name, + const sensors_feature *feature) +{ + const sensors_chip *chip; + int i; + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->ignores_count; i++) + if (!strcmp(feature->name, chip->ignores[i].name)) + return 1; + return 0; +} + +/* Read the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +static int __sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + int depth, double *result) +{ + const sensors_chip_features *chip_features; + const sensors_subfeature *subfeature; + const sensors_expr *expr = NULL; + double val; + int res, i; + + if (depth >= DEPTH_MAX) + return -SENSORS_ERR_RECURSION; + if (sensors_chip_name_has_wildcards(name)) + return -SENSORS_ERR_WILDCARDS; + if (!(chip_features = sensors_lookup_chip(name))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature = sensors_lookup_subfeature_nr(chip_features, + subfeat_nr))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature->flags & SENSORS_MODE_R)) + return -SENSORS_ERR_ACCESS_R; + + /* Apply compute statement if it exists */ + if (subfeature->flags & SENSORS_COMPUTE_MAPPING) { + const sensors_feature *feature; + const sensors_chip *chip; + + feature = sensors_lookup_feature_nr(chip_features, + subfeature->mapping); + + chip = NULL; + while (!expr && + (chip = sensors_for_all_config_chips(name, chip))) + for (i = 0; i < chip->computes_count; i++) { + if (!strcmp(feature->name, + chip->computes[i].name)) { + expr = chip->computes[i].from_proc; + break; + } + } + } + + res = sensors_read_sysfs_attr(name, subfeature, &val); + if (res) + return res; + if (!expr) + *result = val; + else if ((res = sensors_eval_expr(chip_features, expr, val, depth, + result))) + return res; + return 0; +} + +int sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + double *result) +{ + return __sensors_get_value(name, subfeat_nr, 0, result); +} + +/* Set the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_set_value(const sensors_chip_name *name, int subfeat_nr, + double value) +{ + const sensors_chip_features *chip_features; + const sensors_subfeature *subfeature; + const sensors_expr *expr = NULL; + int i, res; + double to_write; + + if (sensors_chip_name_has_wildcards(name)) + return -SENSORS_ERR_WILDCARDS; + if (!(chip_features = sensors_lookup_chip(name))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature = sensors_lookup_subfeature_nr(chip_features, + subfeat_nr))) + return -SENSORS_ERR_NO_ENTRY; + if (!(subfeature->flags & SENSORS_MODE_W)) + return -SENSORS_ERR_ACCESS_W; + + /* Apply compute statement if it exists */ + if (subfeature->flags & SENSORS_COMPUTE_MAPPING) { + const sensors_feature *feature; + const sensors_chip *chip; + + feature = sensors_lookup_feature_nr(chip_features, + subfeature->mapping); + + chip = NULL; + while (!expr && + (chip = sensors_for_all_config_chips(name, chip))) + for (i = 0; i < chip->computes_count; i++) { + if (!strcmp(feature->name, + chip->computes[i].name)) { + expr = chip->computes[i].to_proc; + break; + } + } + } + + to_write = value; + if (expr) + if ((res = sensors_eval_expr(chip_features, expr, + value, 0, &to_write))) + return res; + return sensors_write_sysfs_attr(name, subfeature, to_write); +} + +const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name + *match, int *nr) +{ + const sensors_chip_name *res; + + while (*nr < sensors_proc_chips_count) { + res = &sensors_proc_chips[(*nr)++].chip; + if (!match || sensors_match_chip(res, match)) + return res; + } + return NULL; +} + +const char *sensors_get_adapter_name(const sensors_bus_id *bus) +{ + int i; + + /* bus types with a single instance */ + switch (bus->type) { + case SENSORS_BUS_TYPE_ISA: + return "ISA adapter"; + case SENSORS_BUS_TYPE_PCI: + return "PCI adapter"; + /* SPI should not be here, but for now SPI adapters have no name + so we don't have any custom string to return. */ + case SENSORS_BUS_TYPE_SPI: + return "SPI adapter"; + case SENSORS_BUS_TYPE_VIRTUAL: + return "Virtual device"; + case SENSORS_BUS_TYPE_ACPI: + return "ACPI interface"; + /* HID should probably not be there either, but I don't know if + HID buses have a name nor where to find it. */ + case SENSORS_BUS_TYPE_HID: + return "HID adapter"; + } + + /* bus types with several instances */ + for (i = 0; i < sensors_proc_bus_count; i++) + if (sensors_proc_bus[i].bus.type == bus->type && + sensors_proc_bus[i].bus.nr == bus->nr) + return sensors_proc_bus[i].adapter; + return NULL; +} + +const sensors_feature * +sensors_get_features(const sensors_chip_name *name, int *nr) +{ + const sensors_chip_features *chip; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + while (*nr < chip->feature_count + && sensors_get_ignored(name, &chip->feature[*nr])) + (*nr)++; + if (*nr >= chip->feature_count) + return NULL; + return &chip->feature[(*nr)++]; +} + +const sensors_subfeature * +sensors_get_all_subfeatures(const sensors_chip_name *name, + const sensors_feature *feature, int *nr) +{ + const sensors_chip_features *chip; + const sensors_subfeature *subfeature; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + /* Seek directly to the first subfeature */ + if (*nr < feature->first_subfeature) + *nr = feature->first_subfeature; + + if (*nr >= chip->subfeature_count) + return NULL; /* end of list */ + subfeature = &chip->subfeature[(*nr)++]; + if (subfeature->mapping == feature->number) + return subfeature; + return NULL; /* end of subfeature list */ +} + +const sensors_subfeature * +sensors_get_subfeature(const sensors_chip_name *name, + const sensors_feature *feature, + sensors_subfeature_type type) +{ + const sensors_chip_features *chip; + int i; + + if (!(chip = sensors_lookup_chip(name))) + return NULL; /* No such chip */ + + for (i = feature->first_subfeature; i < chip->subfeature_count && + chip->subfeature[i].mapping == feature->number; i++) { + if (chip->subfeature[i].type == type) + return &chip->subfeature[i]; + } + return NULL; /* No such subfeature */ +} + +/* Evaluate an expression */ +int sensors_eval_expr(const sensors_chip_features *chip_features, + const sensors_expr *expr, + double val, int depth, double *result) +{ + double res1, res2; + int res; + const sensors_subfeature *subfeature; + + if (expr->kind == sensors_kind_val) { + *result = expr->data.val; + return 0; + } + if (expr->kind == sensors_kind_source) { + *result = val; + return 0; + } + if (expr->kind == sensors_kind_var) { + if (!(subfeature = sensors_lookup_subfeature_name(chip_features, + expr->data.var))) + return -SENSORS_ERR_NO_ENTRY; + return __sensors_get_value(&chip_features->chip, + subfeature->number, depth + 1, + result); + } + if ((res = sensors_eval_expr(chip_features, expr->data.subexpr.sub1, + val, depth, &res1))) + return res; + if (expr->data.subexpr.sub2 && + (res = sensors_eval_expr(chip_features, expr->data.subexpr.sub2, + val, depth, &res2))) + return res; + switch (expr->data.subexpr.op) { + case sensors_add: + *result = res1 + res2; + return 0; + case sensors_sub: + *result = res1 - res2; + return 0; + case sensors_multiply: + *result = res1 * res2; + return 0; + case sensors_divide: + if (res2 == 0.0) + return -SENSORS_ERR_DIV_ZERO; + *result = res1 / res2; + return 0; + case sensors_negate: + *result = -res1; + return 0; + case sensors_exp: + *result = exp(res1); + return 0; + case sensors_log: + if (res1 < 0.0) + return -SENSORS_ERR_DIV_ZERO; + *result = log(res1); + return 0; + } + return 0; +} + +/* Execute all set statements for this particular chip. The chip may not + contain wildcards! This function will return 0 on success, and <0 on + failure. */ +static int sensors_do_this_chip_sets(const sensors_chip_name *name) +{ + const sensors_chip_features *chip_features; + sensors_chip *chip; + double value; + int i; + int err = 0, res; + const sensors_subfeature *subfeature; + + chip_features = sensors_lookup_chip(name); /* Can't fail */ + + for (chip = NULL; (chip = sensors_for_all_config_chips(name, chip));) + for (i = 0; i < chip->sets_count; i++) { + subfeature = sensors_lookup_subfeature_name(chip_features, + chip->sets[i].name); + if (!subfeature) { + sensors_parse_error_wfn("Unknown feature name", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = -SENSORS_ERR_NO_ENTRY; + continue; + } + + res = sensors_eval_expr(chip_features, + chip->sets[i].value, 0, + 0, &value); + if (res) { + sensors_parse_error_wfn("Error parsing expression", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = res; + continue; + } + if ((res = sensors_set_value(name, subfeature->number, + value))) { + sensors_parse_error_wfn("Failed to set value", + chip->sets[i].line.filename, + chip->sets[i].line.lineno); + err = res; + continue; + } + } + return err; +} + +/* Execute all set statements for this particular chip. The chip may contain + wildcards! This function will return 0 on success, and <0 on failure. */ +int sensors_do_chip_sets(const sensors_chip_name *name) +{ + int nr, this_res; + const sensors_chip_name *found_name; + int res = 0; + + for (nr = 0; (found_name = sensors_get_detected_chips(name, &nr));) { + this_res = sensors_do_this_chip_sets(found_name); + if (this_res) + res = this_res; + } + return res; +} diff --git a/tools/gator/daemon/libsensors/access.h b/tools/gator/daemon/libsensors/access.h new file mode 100644 index 000000000000..1d3784340757 --- /dev/null +++ b/tools/gator/daemon/libsensors/access.h @@ -0,0 +1,33 @@ +/* + access.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_ACCESS_H +#define LIB_SENSORS_ACCESS_H + +#include "sensors.h" +#include "data.h" + +/* Check whether the chip name is an 'absolute' name, which can only match + one chip, or whether it has wildcards. Returns 0 if it is absolute, 1 + if there are wildcards. */ +int sensors_chip_name_has_wildcards(const sensors_chip_name *chip); + +#endif /* def LIB_SENSORS_ACCESS_H */ diff --git a/tools/gator/daemon/libsensors/conf-lex.c b/tools/gator/daemon/libsensors/conf-lex.c new file mode 100644 index 000000000000..a54664b3d77b --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-lex.c @@ -0,0 +1,2881 @@ + +#line 3 "" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer sensors_yy_create_buffer +#define yy_delete_buffer sensors_yy_delete_buffer +#define yy_flex_debug sensors_yy_flex_debug +#define yy_init_buffer sensors_yy_init_buffer +#define yy_flush_buffer sensors_yy_flush_buffer +#define yy_load_buffer_state sensors_yy_load_buffer_state +#define yy_switch_to_buffer sensors_yy_switch_to_buffer +#define yyin sensors_yyin +#define yyleng sensors_yyleng +#define yylex sensors_yylex +#define yylineno sensors_yylineno +#define yyout sensors_yyout +#define yyrestart sensors_yyrestart +#define yytext sensors_yytext +#define yywrap sensors_yywrap +#define yyalloc sensors_yyalloc +#define yyrealloc sensors_yyrealloc +#define yyfree sensors_yyfree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE sensors_yyrestart(sensors_yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int sensors_yyleng; + +extern FILE *sensors_yyin, *sensors_yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up sensors_yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up sensors_yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via sensors_yyrestart()), so that the user can continue scanning by + * just pointing sensors_yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when sensors_yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int sensors_yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow sensors_yywrap()'s to do buffer switches + * instead of setting up a fresh sensors_yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void sensors_yyrestart (FILE *input_file ); +void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE sensors_yy_create_buffer (FILE *file,int size ); +void sensors_yy_delete_buffer (YY_BUFFER_STATE b ); +void sensors_yy_flush_buffer (YY_BUFFER_STATE b ); +void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer ); +void sensors_yypop_buffer_state (void ); + +static void sensors_yyensure_buffer_stack (void ); +static void sensors_yy_load_buffer_state (void ); +static void sensors_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER sensors_yy_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE sensors_yy_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE sensors_yy_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char *bytes,int len ); + +void *sensors_yyalloc (yy_size_t ); +void *sensors_yyrealloc (void *,yy_size_t ); +void sensors_yyfree (void * ); + +#define yy_new_buffer sensors_yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + sensors_yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + sensors_yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define sensors_yywrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *sensors_yyin = (FILE *) 0, *sensors_yyout = (FILE *) 0; + +typedef int yy_state_type; + +extern int sensors_yylineno; + +int sensors_yylineno = 1; + +extern char *sensors_yytext; +#define yytext_ptr sensors_yytext +static yyconst flex_int16_t yy_nxt[][39] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + + { + 9, 10, 11, 12, 10, 13, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 14, 15, 16, 14, 14, 14, 14, 14, 17, 18, + 14, 14, 14, 14, 14, 19, 14, 14, 14 + }, + + { + 9, 10, 11, 12, 10, 13, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + + 14, 15, 16, 14, 14, 14, 14, 14, 17, 18, + 14, 14, 14, 14, 14, 19, 14, 14, 14 + }, + + { + 9, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35 + }, + + { + 9, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35 + + }, + + { + 9, 39, 39, 40, 41, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39 + }, + + { + 9, 39, 39, 40, 41, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 42, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39 + }, + + { + 9, 43, 43, 44, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43 + }, + + { + 9, 43, 43, 44, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43 + }, + + { + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, + -9, -9, -9, -9, -9, -9, -9, -9, -9 + + }, + + { + 9, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -10, -10, -10, -10, -10, -10, -10 + }, + + { + 9, -11, 45, 46, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11, -11 + }, + + { + 9, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12 + }, + + { + 9, 47, 47, 48, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47 + }, + + { + 9, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14, -14, -14, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -15, -15, -15, -15, -15, -15, -15, -15, -15, + -15, -15, -15, -15, -15, -15, -15, -15, -15, -15, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 50, 49 + }, + + { + 9, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16, -16, -16, + 49, 49, 49, 49, 49, 49, 49, 51, 49, 49, + 49, 49, 52, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17, -17, -17, + + 49, 49, 49, 49, 49, 49, 53, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, -18, -18, -18, -18, -18, -18, -18, -18, -18, + 54, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -19, -19, -19, -19, -19, -19, -19, -19, -19, + -19, -19, -19, -19, -19, -19, -19, -19, -19, -19, + 49, 49, 49, 49, 55, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20, -20 + }, + + { + 9, -21, 56, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21, -21, + -21, -21, -21, -21, -21, -21, -21, -21, -21 + }, + + { + 9, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + + -22, -22, -22, -22, -22, -22, -22, -22, -22, -22, + -22, -22, -22, -22, -22, -22, -22, -22, -22 + }, + + { + 9, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23, -23 + }, + + { + 9, 57, 57, 58, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57 + + }, + + { + 9, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25, -25, + -25, -25, -25, -25, -25, -25, -25, -25, -25 + }, + + { + 9, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26, -26 + }, + + { + 9, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, -27 + }, + + { + 9, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28, -28 + }, + + { + 9, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, -29, -29, -29, -29, -29 + + }, + + { + 9, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30, -30 + }, + + { + 9, -31, -31, -31, -31, -31, -31, -31, -31, -31, + -31, -31, -31, -31, 59, -31, -31, -31, -31, -31, + -31, -31, -31, -31, -31, -31, -31, -31, -31, -31, + -31, -31, -31, -31, -31, -31, -31, -31, -31 + }, + + { + 9, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32 + }, + + { + 9, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, 60, -33, 61, -33, 62, -33, -33, -33, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34, -34 + + }, + + { + 9, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, 62, -35, 62, -35, -35, -35, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -36, 63, 64, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36 + }, + + { + 9, -37, -37, -37, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, + + -37, -37, -37, -37, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -37, -37 + }, + + { + 9, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, -38, -38, -38 + }, + + { + 9, 65, 65, -39, -39, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, -39, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65 + + }, + + { + 9, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, -40, -40 + }, + + { + 9, -41, -41, -41, 66, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41, -41 + }, + + { + 9, 67, 67, 68, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + + 69, 70, 67, 67, 67, 71, 67, 67, 67, 67, + 67, 72, 67, 67, 73, 67, 74, 67, 75 + }, + + { + 9, 76, 76, -43, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76 + }, + + { + 9, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44, -44, + -44, -44, -44, -44, -44, -44, -44, -44, -44 + + }, + + { + 9, -45, 45, 46, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45, -45 + }, + + { + 9, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -46, -46, -46, -46, -46, -46, -46 + }, + + { + 9, 47, 47, 48, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47 + }, + + { + 9, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, -48 + }, + + { + 9, -49, -49, -49, -49, -49, -49, -49, -49, -49, + -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 77, 49, 49, 49 + }, + + { + 9, -51, -51, -51, -51, -51, -51, -51, -51, -51, + -51, -51, -51, -51, -51, -51, -51, -51, -51, -51, + 49, 49, 49, 49, 49, 49, 49, 49, 78, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 79, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -53, -53, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, -53, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 80, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54, -54, -54, + 49, 81, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -55, -55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55, -55, -55, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 82, 49, 49 + }, + + { + 9, -56, 56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56, -56 + }, + + { + 9, 57, 57, 58, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57 + }, + + { + 9, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58 + }, + + { + 9, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, 59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -59, -59, -59, -59, -59, -59 + + }, + + { + 9, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, 59, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60, -60 + }, + + { + 9, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, 60, -61, 61, -61, 62, -61, -61, -61, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, 62, -62, 62, -62, -62, -62, + + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62 + }, + + { + 9, -63, 63, 64, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63, -63 + }, + + { + 9, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, -64 + + }, + + { + 9, 65, 65, -65, -65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, -65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65 + }, + + { + 9, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -66, -66, -66, -66 + }, + + { + 9, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + + -67, -67, -67, -67, -67, -67, -67, -67, -67, -67, + -67, -67, -67, -67, -67, -67, -67, -67, -67 + }, + + { + 9, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68, -68, + -68, -68, -68, -68, -68, -68, -68, -68, -68 + }, + + { + 9, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69, -69, + -69, -69, -69, -69, -69, -69, -69, -69, -69 + + }, + + { + 9, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70, -70, + -70, -70, -70, -70, -70, -70, -70, -70, -70 + }, + + { + 9, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71, -71, + -71, -71, -71, -71, -71, -71, -71, -71, -71 + }, + + { + 9, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + + -72, -72, -72, -72, -72, -72, -72, -72, -72, -72, + -72, -72, -72, -72, -72, -72, -72, -72, -72 + }, + + { + 9, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73, -73, + -73, -73, -73, -73, -73, -73, -73, -73, -73 + }, + + { + 9, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, + -74, -74, -74, -74, -74, -74, -74, -74, -74 + + }, + + { + 9, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75, -75, + -75, -75, -75, -75, -75, -75, -75, -75, -75 + }, + + { + 9, 76, 76, -76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76 + }, + + { + 9, -77, 83, -77, -77, -77, -77, -77, -77, -77, + -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -78, -78, -78, -78, -78, -78, -78, -78, -78, + -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 84, 49, 49, 49, 49, 49 + }, + + { + 9, -79, -79, -79, -79, -79, -79, -79, -79, -79, + -79, -79, -79, -79, -79, -79, -79, -79, -79, -79, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 85, 49, 49, 49, 49, 49 + + }, + + { + 9, -80, -80, -80, -80, -80, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, -80, -80, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 86, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -81, -81, -81, -81, -81, -81, -81, -81, -81, + -81, -81, -81, -81, -81, -81, -81, -81, -81, -81, + 49, 49, 49, 49, 87, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -82, 88, -82, -82, -82, -82, -82, -82, -82, + -82, -82, -82, -82, -82, -82, -82, -82, -82, -82, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -83, 83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83, -83, + -83, -83, -83, -83, -83, -83, -83, -83, -83 + }, + + { + 9, -84, 89, -84, -84, -84, -84, -84, -84, -84, + -84, -84, -84, -84, -84, -84, -84, -84, -84, -84, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -85, -85, -85, -85, -85, -85, -85, -85, -85, + -85, -85, -85, -85, -85, -85, -85, -85, -85, -85, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 90, 49 + }, + + { + 9, -86, -86, -86, -86, -86, -86, -86, -86, -86, + -86, -86, -86, -86, -86, -86, -86, -86, -86, -86, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 91, 49, 49, 49, 49 + }, + + { + 9, -87, -87, -87, -87, -87, -87, -87, -87, -87, + -87, -87, -87, -87, -87, -87, -87, -87, -87, -87, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 92, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -88, 88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, -88 + }, + + { + 9, -89, 89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89, -89, + -89, -89, -89, -89, -89, -89, -89, -89, -89 + + }, + + { + 9, -90, -90, -90, -90, -90, -90, -90, -90, -90, + -90, -90, -90, -90, -90, -90, -90, -90, -90, -90, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 93, 49, 49 + }, + + { + 9, -91, -91, -91, -91, -91, -91, -91, -91, -91, + -91, -91, -91, -91, -91, -91, -91, -91, -91, -91, + 49, 49, 49, 49, 94, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -92, 95, -92, -92, -92, -92, -92, -92, -92, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -93, -93, -93, -93, -93, -93, -93, -93, -93, + -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, + 49, 49, 49, 49, 96, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -94, 97, -94, -94, -94, -94, -94, -94, -94, + -94, -94, -94, -94, -94, -94, -94, -94, -94, -94, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + + }, + + { + 9, -95, 95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95, -95, + -95, -95, -95, -95, -95, -95, -95, -95, -95 + }, + + { + 9, -96, 98, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -96, -96, -96, -96, -96, -96, -96, -96, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49 + }, + + { + 9, -97, 97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + + -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, -97, -97, -97, -97, -97 + }, + + { + 9, -98, 98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98, -98, + -98, -98, -98, -98, -98, -98, -98, -98, -98 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up sensors_yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + sensors_yyleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 50 +#define YY_END_OF_BUFFER 51 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[99] = + { 0, + 0, 0, 0, 0, 0, 0, 13, 13, 51, 12, + 1, 2, 3, 11, 11, 11, 11, 11, 11, 33, + 15, 16, 31, 18, 25, 26, 23, 21, 27, 22, + 33, 24, 20, 28, 32, 33, 29, 30, 49, 36, + 39, 48, 13, 14, 1, 2, 3, 4, 11, 11, + 11, 11, 11, 11, 11, 15, 18, 19, 20, 34, + 20, 32, 35, 17, 49, 38, 47, 37, 40, 41, + 42, 43, 44, 45, 46, 13, 8, 11, 11, 11, + 11, 6, 8, 9, 11, 11, 11, 6, 9, 11, + 11, 5, 11, 10, 5, 7, 10, 7 + + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 4, 5, 1, 1, 1, 1, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 1, 1, 1, + 1, 1, 1, 15, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 1, 17, 1, 18, 16, 19, 20, 21, 22, 23, + + 24, 25, 26, 27, 28, 23, 23, 29, 30, 31, + 32, 33, 23, 34, 35, 36, 37, 38, 23, 23, + 23, 23, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int sensors_yy_flex_debug; +int sensors_yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *sensors_yytext; +#line 1 "lib/conf-lex.l" +#line 2 "lib/conf-lex.l" +/* + conf-lex.l - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include +#include + +#include "general.h" +#include "data.h" +#include "conf-parse.h" +#include "error.h" +#include "scanner.h" + +static int buffer_count; +static int buffer_max; +static char *buffer; + +char sensors_lex_error[100]; + +const char *sensors_yyfilename; +int sensors_yylineno; + +#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\ + &buffer_max,1) +#define buffer_free() sensors_free_array(&buffer,&buffer_count,\ + &buffer_max) +#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\ + &buffer_count,\ + &buffer_max,1) +#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\ + &buffer, \ + &buffer_count,&buffer_max,1) + +/* Scanner for configuration files */ +/* All states are exclusive */ + + + +/* Any whitespace-like character */ +/* Note: `10', `10.4' and `.4' are valid, `10.' is not */ +/* Only positive whole numbers are recognized here */ +#line 1255 "" + +#define INITIAL 0 +#define MIDDLE 1 +#define STRING 2 +#define ERR 3 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int sensors_yylex_destroy (void ); + +int sensors_yyget_debug (void ); + +void sensors_yyset_debug (int debug_flag ); + +YY_EXTRA_TYPE sensors_yyget_extra (void ); + +void sensors_yyset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *sensors_yyget_in (void ); + +void sensors_yyset_in (FILE * in_str ); + +FILE *sensors_yyget_out (void ); + +void sensors_yyset_out (FILE * out_str ); + +int sensors_yyget_leng (void ); + +char *sensors_yyget_text (void ); + +int sensors_yyget_lineno (void ); + +void sensors_yyset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int sensors_yywrap (void ); +#else +extern int sensors_yywrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( sensors_yytext, sensors_yyleng, 1, sensors_yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( sensors_yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( sensors_yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, sensors_yyin))==0 && ferror(sensors_yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(sensors_yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int sensors_yylex (void); + +#define YY_DECL int sensors_yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after sensors_yytext and sensors_yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 80 "lib/conf-lex.l" + + + /* + * STATE: INITIAL + */ + +#line 1450 "" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! sensors_yyin ) + sensors_yyin = stdin; + + if ( ! sensors_yyout ) + sensors_yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + sensors_yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); + } + + sensors_yy_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of sensors_yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + +case YY_STATE_EOF(INITIAL): +#line 88 "lib/conf-lex.l" +{ /* EOF from this state terminates */ + return 0; + } + YY_BREAK +case 1: +YY_RULE_SETUP +#line 92 "lib/conf-lex.l" +; /* eat as many blanks as possible at once */ + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 94 "lib/conf-lex.l" +{ /* eat a bare newline (possibly preceded by blanks) */ + sensors_yylineno++; + } + YY_BREAK +/* comments */ +case 3: +YY_RULE_SETUP +#line 100 "lib/conf-lex.l" +; /* eat the rest of the line after comment char */ + YY_BREAK +case 4: +/* rule 4 can match eol */ +YY_RULE_SETUP +#line 102 "lib/conf-lex.l" +{ /* eat the rest of the line after comment char */ + sensors_yylineno++; + } + YY_BREAK +/* + * Keywords must be followed by whitespace - eat that too. + * If there isn't trailing whitespace, we still need to + * accept it as lexically correct (even though the parser + * will reject it anyway.) + */ +case 5: +YY_RULE_SETUP +#line 113 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return LABEL; + } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 120 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return SET; + } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 127 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return COMPUTE; + } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 134 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return BUS; + } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 141 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return CHIP; + } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 148 "lib/conf-lex.l" +{ + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return IGNORE; + } + YY_BREAK +/* Anything else at the beginning of a line is an error */ +case 11: +#line 158 "lib/conf-lex.l" +case 12: +YY_RULE_SETUP +#line 158 "lib/conf-lex.l" +{ + BEGIN(ERR); + strcpy(sensors_lex_error,"Invalid keyword"); + return ERROR; + } + YY_BREAK + +/* + * STATE: ERROR + */ + +case 13: +YY_RULE_SETUP +#line 171 "lib/conf-lex.l" +; /* eat whatever is left on this line */ + YY_BREAK +case 14: +/* rule 14 can match eol */ +YY_RULE_SETUP +#line 173 "lib/conf-lex.l" +{ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK + +/* + * STATE: MIDDLE + */ + +case 15: +YY_RULE_SETUP +#line 186 "lib/conf-lex.l" +; /* eat as many blanks as possible at once */ + YY_BREAK +case 16: +/* rule 16 can match eol */ +YY_RULE_SETUP +#line 188 "lib/conf-lex.l" +{ /* newline here sends EOL token to parser */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK +case YY_STATE_EOF(MIDDLE): +#line 194 "lib/conf-lex.l" +{ /* EOF here sends EOL token to parser also */ + BEGIN(INITIAL); + return EOL; + } + YY_BREAK +case 17: +/* rule 17 can match eol */ +YY_RULE_SETUP +#line 199 "lib/conf-lex.l" +{ /* eat an escaped newline with no state change */ + sensors_yylineno++; + } + YY_BREAK +/* comments */ +case 18: +YY_RULE_SETUP +#line 205 "lib/conf-lex.l" +; /* eat the rest of the line after comment char */ + YY_BREAK +case 19: +/* rule 19 can match eol */ +YY_RULE_SETUP +#line 207 "lib/conf-lex.l" +{ /* eat the rest of the line after comment char */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + YY_BREAK +/* A number */ +case 20: +YY_RULE_SETUP +#line 215 "lib/conf-lex.l" +{ + sensors_yylval.value = atof(sensors_yytext); + return FLOAT; + } + YY_BREAK +/* Some operators */ +case 21: +YY_RULE_SETUP +#line 222 "lib/conf-lex.l" +return '+'; + YY_BREAK +case 22: +YY_RULE_SETUP +#line 223 "lib/conf-lex.l" +return '-'; + YY_BREAK +case 23: +YY_RULE_SETUP +#line 224 "lib/conf-lex.l" +return '*'; + YY_BREAK +case 24: +YY_RULE_SETUP +#line 225 "lib/conf-lex.l" +return '/'; + YY_BREAK +case 25: +YY_RULE_SETUP +#line 226 "lib/conf-lex.l" +return '('; + YY_BREAK +case 26: +YY_RULE_SETUP +#line 227 "lib/conf-lex.l" +return ')'; + YY_BREAK +case 27: +YY_RULE_SETUP +#line 228 "lib/conf-lex.l" +return ','; + YY_BREAK +case 28: +YY_RULE_SETUP +#line 229 "lib/conf-lex.l" +return '@'; + YY_BREAK +case 29: +YY_RULE_SETUP +#line 230 "lib/conf-lex.l" +return '^'; + YY_BREAK +case 30: +YY_RULE_SETUP +#line 231 "lib/conf-lex.l" +return '`'; + YY_BREAK +/* Quoted string */ +case 31: +YY_RULE_SETUP +#line 235 "lib/conf-lex.l" +{ + buffer_malloc(); + BEGIN(STRING); + } + YY_BREAK +/* A normal, unquoted identifier */ +case 32: +YY_RULE_SETUP +#line 242 "lib/conf-lex.l" +{ + sensors_yylval.name = strdup(sensors_yytext); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + + return NAME; + } + YY_BREAK +/* anything else is bogus */ +case 33: +#line 254 "lib/conf-lex.l" +case 34: +#line 255 "lib/conf-lex.l" +case 35: +YY_RULE_SETUP +#line 255 "lib/conf-lex.l" +{ + BEGIN(ERR); + return ERROR; + } + YY_BREAK + +/* + * STATE: STRING + */ + +/* Oops, newline or EOF while in a string is not good */ +case 36: +/* rule 36 can match eol */ +#line 270 "lib/conf-lex.l" +case 37: +/* rule 37 can match eol */ +YY_RULE_SETUP +#line 270 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "No matching double quote."); + buffer_free(); + yyless(0); + BEGIN(ERR); + return ERROR; + } + YY_BREAK +case YY_STATE_EOF(STRING): +#line 280 "lib/conf-lex.l" +{ + strcpy(sensors_lex_error, + "Reached end-of-file without a matching double quote."); + buffer_free(); + BEGIN(MIDDLE); + return ERROR; + } + YY_BREAK +/* At the end */ +case 38: +YY_RULE_SETUP +#line 290 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "Quoted strings must be separated by whitespace."); + buffer_free(); + BEGIN(ERR); + return ERROR; + } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 299 "lib/conf-lex.l" +{ + buffer_add_char("\0"); + sensors_yylval.name = strdup(buffer); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + buffer_free(); + BEGIN(MIDDLE); + return NAME; + } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 310 "lib/conf-lex.l" +buffer_add_char("\a"); + YY_BREAK +case 41: +YY_RULE_SETUP +#line 311 "lib/conf-lex.l" +buffer_add_char("\b"); + YY_BREAK +case 42: +YY_RULE_SETUP +#line 312 "lib/conf-lex.l" +buffer_add_char("\f"); + YY_BREAK +case 43: +YY_RULE_SETUP +#line 313 "lib/conf-lex.l" +buffer_add_char("\n"); + YY_BREAK +case 44: +YY_RULE_SETUP +#line 314 "lib/conf-lex.l" +buffer_add_char("\r"); + YY_BREAK +case 45: +YY_RULE_SETUP +#line 315 "lib/conf-lex.l" +buffer_add_char("\t"); + YY_BREAK +case 46: +YY_RULE_SETUP +#line 316 "lib/conf-lex.l" +buffer_add_char("\v"); + YY_BREAK +/* Other escapes: just copy the character behind the slash */ +case 47: +YY_RULE_SETUP +#line 320 "lib/conf-lex.l" +{ + buffer_add_char(&sensors_yytext[1]); + } + YY_BREAK +/* Anything else (including a bare '\' which may be followed by EOF) */ +case 48: +#line 327 "lib/conf-lex.l" +case 49: +YY_RULE_SETUP +#line 327 "lib/conf-lex.l" +{ + buffer_add_string(sensors_yytext); + } + YY_BREAK + +case 50: +YY_RULE_SETUP +#line 332 "lib/conf-lex.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1903 "" + case YY_STATE_EOF(ERR): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed sensors_yyin at a new source and called + * sensors_yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = sensors_yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( sensors_yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * sensors_yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of sensors_yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + sensors_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + sensors_yyrestart(sensors_yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) sensors_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + sensors_yyrestart(sensors_yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( sensors_yywrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve sensors_yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void sensors_yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + sensors_yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + sensors_yy_create_buffer(sensors_yyin,YY_BUF_SIZE ); + } + + sensors_yy_init_buffer(YY_CURRENT_BUFFER,input_file ); + sensors_yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void sensors_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * sensors_yypop_buffer_state(); + * sensors_yypush_buffer_state(new_buffer); + */ + sensors_yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + sensors_yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (sensors_yywrap()) processing, but the only time this flag + * is looked at is after sensors_yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void sensors_yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + sensors_yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE sensors_yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) sensors_yyalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + sensors_yy_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with sensors_yy_create_buffer() + * + */ + void sensors_yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + sensors_yyfree((void *) b->yy_ch_buf ); + + sensors_yyfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a sensors_yyrestart() or at EOF. + */ + static void sensors_yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + sensors_yy_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then sensors_yy_init_buffer was _probably_ + * called from sensors_yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void sensors_yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + sensors_yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void sensors_yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + sensors_yyensure_buffer_stack(); + + /* This block is copied from sensors_yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from sensors_yy_switch_to_buffer. */ + sensors_yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void sensors_yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + sensors_yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + sensors_yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void sensors_yyensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)sensors_yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE sensors_yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) sensors_yyalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + sensors_yy_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to sensors_yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * sensors_yy_scan_bytes() instead. + */ +YY_BUFFER_STATE sensors_yy_scan_string (yyconst char * yystr ) +{ + + return sensors_yy_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to sensors_yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE sensors_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) sensors_yyalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in sensors_yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = sensors_yy_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in sensors_yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up sensors_yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + sensors_yytext[sensors_yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = sensors_yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + sensors_yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int sensors_yyget_lineno (void) +{ + + return sensors_yylineno; +} + +/** Get the input stream. + * + */ +FILE *sensors_yyget_in (void) +{ + return sensors_yyin; +} + +/** Get the output stream. + * + */ +FILE *sensors_yyget_out (void) +{ + return sensors_yyout; +} + +/** Get the length of the current token. + * + */ +int sensors_yyget_leng (void) +{ + return sensors_yyleng; +} + +/** Get the current token. + * + */ + +char *sensors_yyget_text (void) +{ + return sensors_yytext; +} + +/** Set the current line number. + * @param line_number + * + */ +void sensors_yyset_lineno (int line_number ) +{ + + sensors_yylineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see sensors_yy_switch_to_buffer + */ +void sensors_yyset_in (FILE * in_str ) +{ + sensors_yyin = in_str ; +} + +void sensors_yyset_out (FILE * out_str ) +{ + sensors_yyout = out_str ; +} + +int sensors_yyget_debug (void) +{ + return sensors_yy_flex_debug; +} + +void sensors_yyset_debug (int bdebug ) +{ + sensors_yy_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from sensors_yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + sensors_yyin = stdin; + sensors_yyout = stdout; +#else + sensors_yyin = (FILE *) 0; + sensors_yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * sensors_yylex_init() + */ + return 0; +} + +/* sensors_yylex_destroy is for both reentrant and non-reentrant scanners. */ +int sensors_yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + sensors_yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + sensors_yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + sensors_yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * sensors_yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *sensors_yyalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *sensors_yyrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void sensors_yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see sensors_yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 332 "lib/conf-lex.l" + + + +/* + Do the buffer handling manually. This allows us to scan as many + config files as we need to, while cleaning up properly after each + one. The "BEGIN(0)" line ensures that we start in the default state, + even if e.g. the previous config file was syntactically broken. + + Returns 0 if successful, !0 otherwise. +*/ + +static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0; + +int sensors_scanner_init(FILE *input, const char *filename) +{ + BEGIN(0); + if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE))) + return -1; + + sensors_yy_switch_to_buffer(scan_buf); + sensors_yyfilename = filename; + sensors_yylineno = 1; + return 0; +} + +void sensors_scanner_exit(void) +{ + sensors_yy_delete_buffer(scan_buf); + scan_buf = (YY_BUFFER_STATE)0; + +/* As of flex 2.5.9, sensors_yylex_destroy() must be called when done with the + scaller, otherwise we'll leak memory. */ +#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION) +#if YY_FLEX_MAJOR_VERSION > 2 || \ + (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \ + (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9))) + sensors_yylex_destroy(); +#endif +#endif +} + + diff --git a/tools/gator/daemon/libsensors/conf-lex.l b/tools/gator/daemon/libsensors/conf-lex.l new file mode 100644 index 000000000000..43ddbd8f5bd5 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-lex.l @@ -0,0 +1,372 @@ +%{ +/* + conf-lex.l - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include +#include + +#include "general.h" +#include "data.h" +#include "conf-parse.h" +#include "error.h" +#include "scanner.h" + +static int buffer_count; +static int buffer_max; +static char *buffer; + +char sensors_lex_error[100]; + +const char *sensors_yyfilename; +int sensors_yylineno; + +#define buffer_malloc() sensors_malloc_array(&buffer,&buffer_count,\ + &buffer_max,1) +#define buffer_free() sensors_free_array(&buffer,&buffer_count,\ + &buffer_max) +#define buffer_add_char(c) sensors_add_array_el(c,&buffer,\ + &buffer_count,\ + &buffer_max,1) +#define buffer_add_string(s) sensors_add_array_els(s,strlen(s),\ + &buffer, \ + &buffer_count,&buffer_max,1) + +%} + + /* Scanner for configuration files */ + +%option nodefault +%option noyywrap +%option nounput + + /* All states are exclusive */ + +%x MIDDLE +%x STRING +%x ERR + + /* Any whitespace-like character */ + +BLANK [ \f\r\t\v] + +IDCHAR [[:alnum:]_] + + /* Note: `10', `10.4' and `.4' are valid, `10.' is not */ + +FLOAT [[:digit:]]*\.?[[:digit:]]+ + + /* Only positive whole numbers are recognized here */ + +NUM 0|([1-9][[:digit:]]*) + + +%% + + /* + * STATE: INITIAL + */ + +{ + +<> { /* EOF from this state terminates */ + return 0; + } + +{BLANK}+ ; /* eat as many blanks as possible at once */ + +{BLANK}*\n { /* eat a bare newline (possibly preceded by blanks) */ + sensors_yylineno++; + } + + /* comments */ + +#.* ; /* eat the rest of the line after comment char */ + +#.*\n { /* eat the rest of the line after comment char */ + sensors_yylineno++; + } + + /* + * Keywords must be followed by whitespace - eat that too. + * If there isn't trailing whitespace, we still need to + * accept it as lexically correct (even though the parser + * will reject it anyway.) + */ + +label{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return LABEL; + } + +set{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return SET; + } + +compute{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return COMPUTE; + } + +bus{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return BUS; + } + +chip{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return CHIP; + } + +ignore{BLANK}* { + sensors_yylval.line.filename = sensors_yyfilename; + sensors_yylval.line.lineno = sensors_yylineno; + BEGIN(MIDDLE); + return IGNORE; + } + + /* Anything else at the beginning of a line is an error */ + +[a-z]+ | +. { + BEGIN(ERR); + strcpy(sensors_lex_error,"Invalid keyword"); + return ERROR; + } +} + + /* + * STATE: ERROR + */ + +{ + +.* ; /* eat whatever is left on this line */ + +\n { + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } +} + + /* + * STATE: MIDDLE + */ + +{ + +{BLANK}+ ; /* eat as many blanks as possible at once */ + +\n { /* newline here sends EOL token to parser */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + +<> { /* EOF here sends EOL token to parser also */ + BEGIN(INITIAL); + return EOL; + } + +\\{BLANK}*\n { /* eat an escaped newline with no state change */ + sensors_yylineno++; + } + + /* comments */ + +#.* ; /* eat the rest of the line after comment char */ + +#.*\n { /* eat the rest of the line after comment char */ + BEGIN(INITIAL); + sensors_yylineno++; + return EOL; + } + + /* A number */ + +{FLOAT} { + sensors_yylval.value = atof(sensors_yytext); + return FLOAT; + } + + /* Some operators */ + +"+" return '+'; +"-" return '-'; +"*" return '*'; +"/" return '/'; +"(" return '('; +")" return ')'; +"," return ','; +"@" return '@'; +"^" return '^'; +"`" return '`'; + + /* Quoted string */ + +\" { + buffer_malloc(); + BEGIN(STRING); + } + + /* A normal, unquoted identifier */ + +{IDCHAR}+ { + sensors_yylval.name = strdup(sensors_yytext); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + + return NAME; + } + + /* anything else is bogus */ + +. | +[[:digit:]]*\. | +\\{BLANK}* { + BEGIN(ERR); + return ERROR; + } +} + + /* + * STATE: STRING + */ + +{ + + /* Oops, newline or EOF while in a string is not good */ + +\n | +\\\n { + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "No matching double quote."); + buffer_free(); + yyless(0); + BEGIN(ERR); + return ERROR; + } + +<> { + strcpy(sensors_lex_error, + "Reached end-of-file without a matching double quote."); + buffer_free(); + BEGIN(MIDDLE); + return ERROR; + } + + /* At the end */ + +\"\" { + buffer_add_char("\0"); + strcpy(sensors_lex_error, + "Quoted strings must be separated by whitespace."); + buffer_free(); + BEGIN(ERR); + return ERROR; + } + +\" { + buffer_add_char("\0"); + sensors_yylval.name = strdup(buffer); + if (! sensors_yylval.name) + sensors_fatal_error("conf-lex.l", + "Allocating a new string"); + buffer_free(); + BEGIN(MIDDLE); + return NAME; + } + +\\a buffer_add_char("\a"); +\\b buffer_add_char("\b"); +\\f buffer_add_char("\f"); +\\n buffer_add_char("\n"); +\\r buffer_add_char("\r"); +\\t buffer_add_char("\t"); +\\v buffer_add_char("\v"); + + /* Other escapes: just copy the character behind the slash */ + +\\. { + buffer_add_char(&sensors_yytext[1]); + } + + /* Anything else (including a bare '\' which may be followed by EOF) */ + +\\ | +[^\\\n\"]+ { + buffer_add_string(sensors_yytext); + } +} + +%% + +/* + Do the buffer handling manually. This allows us to scan as many + config files as we need to, while cleaning up properly after each + one. The "BEGIN(0)" line ensures that we start in the default state, + even if e.g. the previous config file was syntactically broken. + + Returns 0 if successful, !0 otherwise. +*/ + +static YY_BUFFER_STATE scan_buf = (YY_BUFFER_STATE)0; + +int sensors_scanner_init(FILE *input, const char *filename) +{ + BEGIN(0); + if (!(scan_buf = sensors_yy_create_buffer(input, YY_BUF_SIZE))) + return -1; + + sensors_yy_switch_to_buffer(scan_buf); + sensors_yyfilename = filename; + sensors_yylineno = 1; + return 0; +} + +void sensors_scanner_exit(void) +{ + sensors_yy_delete_buffer(scan_buf); + scan_buf = (YY_BUFFER_STATE)0; + +/* As of flex 2.5.9, yylex_destroy() must be called when done with the + scaller, otherwise we'll leak memory. */ +#if defined(YY_FLEX_MAJOR_VERSION) && defined(YY_FLEX_MINOR_VERSION) && defined(YY_FLEX_SUBMINOR_VERSION) +#if YY_FLEX_MAJOR_VERSION > 2 || \ + (YY_FLEX_MAJOR_VERSION == 2 && (YY_FLEX_MINOR_VERSION > 5 || \ + (YY_FLEX_MINOR_VERSION == 5 && YY_FLEX_SUBMINOR_VERSION >= 9))) + sensors_yylex_destroy(); +#endif +#endif +} + diff --git a/tools/gator/daemon/libsensors/conf-parse.c b/tools/gator/daemon/libsensors/conf-parse.c new file mode 100644 index 000000000000..fb775460a1c3 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.c @@ -0,0 +1,2042 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse sensors_yyparse +#define yylex sensors_yylex +#define yyerror sensors_yyerror +#define yylval sensors_yylval +#define yychar sensors_yychar +#define yydebug sensors_yydebug +#define yynerrs sensors_yynerrs + + +/* Copy the first part of user declarations. */ + +/* Line 268 of yacc.c */ +#line 1 "lib/conf-parse.y" + +/* + conf-parse.y - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +#define YYERROR_VERBOSE + +#include +#include +#include + +#include "data.h" +#include "general.h" +#include "error.h" +#include "conf.h" +#include "access.h" +#include "init.h" + +static void sensors_yyerror(const char *err); +static sensors_expr *malloc_expr(void); + +static sensors_chip *current_chip = NULL; + +#define bus_add_el(el) sensors_add_array_el(el,\ + &sensors_config_busses,\ + &sensors_config_busses_count,\ + &sensors_config_busses_max,\ + sizeof(sensors_bus)) +#define label_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->labels,\ + ¤t_chip->labels_count,\ + ¤t_chip->labels_max,\ + sizeof(sensors_label)); +#define set_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->sets,\ + ¤t_chip->sets_count,\ + ¤t_chip->sets_max,\ + sizeof(sensors_set)); +#define compute_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->computes,\ + ¤t_chip->computes_count,\ + ¤t_chip->computes_max,\ + sizeof(sensors_compute)); +#define ignore_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->ignores,\ + ¤t_chip->ignores_count,\ + ¤t_chip->ignores_max,\ + sizeof(sensors_ignore)); +#define chip_add_el(el) sensors_add_array_el(el,\ + &sensors_config_chips,\ + &sensors_config_chips_count,\ + &sensors_config_chips_max,\ + sizeof(sensors_chip)); + +#define fits_add_el(el,list) sensors_add_array_el(el,\ + &(list).fits,\ + &(list).fits_count,\ + &(list).fits_max, \ + sizeof(sensors_chip_name)); + + + +/* Line 268 of yacc.c */ +#line 158 "lib/conf-parse.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + NEG = 258, + EOL = 259, + BUS = 260, + LABEL = 261, + SET = 262, + CHIP = 263, + COMPUTE = 264, + IGNORE = 265, + FLOAT = 266, + NAME = 267, + ERROR = 268 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 293 of yacc.c */ +#line 79 "lib/conf-parse.y" + + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; + + + +/* Line 293 of yacc.c */ +#line 220 "lib/conf-parse.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 343 of yacc.c */ +#line 232 "lib/conf-parse.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 58 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 24 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 16 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 34 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 63 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 268 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 22, 23, 5, 4, 10, 3, 2, 6, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 21, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 8, 2, 9, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 7, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 4, 7, 10, 13, 16, 19, 22, + 25, 28, 32, 36, 40, 46, 49, 52, 54, 57, + 59, 61, 63, 67, 71, 75, 79, 82, 86, 89, + 92, 94, 96, 98, 100 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 25, 0, -1, -1, 25, 26, -1, 27, 11, -1, + 28, 11, -1, 29, 11, -1, 32, 11, -1, 30, + 11, -1, 31, 11, -1, 1, 11, -1, 12, 35, + 36, -1, 13, 37, 38, -1, 14, 37, 34, -1, + 16, 37, 34, 10, 34, -1, 17, 37, -1, 15, + 33, -1, 39, -1, 33, 39, -1, 18, -1, 19, + -1, 21, -1, 34, 4, 34, -1, 34, 3, 34, + -1, 34, 5, 34, -1, 34, 6, 34, -1, 3, + 34, -1, 22, 34, 23, -1, 8, 34, -1, 9, + 34, -1, 19, -1, 19, -1, 19, -1, 19, -1, + 19, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 119, 119, 120, 123, 124, 125, 126, 127, 128, + 129, 132, 141, 156, 171, 188, 201, 219, 225, 231, + 236, 241, 245, 252, 259, 266, 273, 280, 282, 289, + 298, 308, 312, 316, 320 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "'-'", "'+'", "'*'", "'/'", "NEG", "'^'", + "'`'", "','", "EOL", "BUS", "LABEL", "SET", "CHIP", "COMPUTE", "IGNORE", + "FLOAT", "NAME", "ERROR", "'@'", "'('", "')'", "$accept", "input", + "line", "bus_statement", "label_statement", "set_statement", + "compute_statement", "ignore_statement", "chip_statement", + "chip_name_list", "expression", "bus_id", "adapter_name", + "function_name", "string", "chip_name", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 45, 43, 42, 47, 258, 94, 96, + 44, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 64, 40, 41 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 24, 25, 25, 26, 26, 26, 26, 26, 26, + 26, 27, 28, 29, 30, 31, 32, 33, 33, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 35, 36, 37, 38, 39 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 5, 2, 2, 1, 2, 1, + 1, 1, 3, 3, 3, 3, 2, 3, 2, 2, + 1, 1, 1, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 10, 30, 0, + 32, 0, 0, 34, 16, 17, 0, 15, 4, 5, + 6, 8, 9, 7, 31, 11, 33, 12, 0, 0, + 0, 19, 20, 21, 0, 13, 18, 0, 26, 28, + 29, 0, 0, 0, 0, 0, 0, 27, 23, 22, + 24, 25, 14 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 1, 10, 11, 12, 13, 14, 15, 16, 24, + 45, 19, 35, 21, 37, 25 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -27 +static const yytype_int8 yypact[] = +{ + -27, 37, -27, -4, -3, 1, 1, 6, 1, 1, + -27, 8, 13, 20, 23, 32, 34, -27, -27, 29, + -27, 39, 14, -27, 6, -27, 14, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27, 14, 14, + 14, -27, -27, -27, 14, 36, -27, 5, -27, -27, + -27, -2, 14, 14, 14, 14, 14, -27, 0, 0, + -27, -27, 36 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -27, -27, -27, -27, -27, -27, -27, -27, -27, -27, + -26, -27, -27, 38, -27, 31 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 47, 52, 53, 54, 55, 54, 55, 17, 52, 53, + 54, 55, 48, 49, 50, 56, 18, 38, 51, 28, + 20, 57, 39, 40, 29, 23, 58, 59, 60, 61, + 62, 30, 41, 42, 31, 43, 44, 2, 3, 52, + 53, 54, 55, 32, 22, 33, 26, 27, 34, 4, + 5, 6, 7, 8, 9, 46, 0, 0, 36 +}; + +#define yypact_value_is_default(yystate) \ + ((yystate) == (-27)) + +#define yytable_value_is_error(yytable_value) \ + YYID (0) + +static const yytype_int8 yycheck[] = +{ + 26, 3, 4, 5, 6, 5, 6, 11, 3, 4, + 5, 6, 38, 39, 40, 10, 19, 3, 44, 11, + 19, 23, 8, 9, 11, 19, 52, 53, 54, 55, + 56, 11, 18, 19, 11, 21, 22, 0, 1, 3, + 4, 5, 6, 11, 6, 11, 8, 9, 19, 12, + 13, 14, 15, 16, 17, 24, -1, -1, 19 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 25, 0, 1, 12, 13, 14, 15, 16, 17, + 26, 27, 28, 29, 30, 31, 32, 11, 19, 35, + 19, 37, 37, 19, 33, 39, 37, 37, 11, 11, + 11, 11, 11, 11, 19, 36, 19, 38, 3, 8, + 9, 18, 19, 21, 22, 34, 39, 34, 34, 34, + 34, 34, 3, 4, 5, 6, 10, 23, 34, 34, + 34, 34, 34 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ + +#define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* This macro is provided for backward compatibility. */ + +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = 0; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 11: + +/* Line 1806 of yacc.c */ +#line 133 "lib/conf-parse.y" + { sensors_bus new_el; + new_el.line = (yyvsp[(1) - (3)].line); + new_el.bus = (yyvsp[(2) - (3)].bus); + new_el.adapter = (yyvsp[(3) - (3)].name); + bus_add_el(&new_el); + } + break; + + case 12: + +/* Line 1806 of yacc.c */ +#line 142 "lib/conf-parse.y" + { sensors_label new_el; + if (!current_chip) { + sensors_yyerror("Label statement before first chip statement"); + free((yyvsp[(2) - (3)].name)); + free((yyvsp[(3) - (3)].name)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (3)].line); + new_el.name = (yyvsp[(2) - (3)].name); + new_el.value = (yyvsp[(3) - (3)].name); + label_add_el(&new_el); + } + break; + + case 13: + +/* Line 1806 of yacc.c */ +#line 157 "lib/conf-parse.y" + { sensors_set new_el; + if (!current_chip) { + sensors_yyerror("Set statement before first chip statement"); + free((yyvsp[(2) - (3)].name)); + sensors_free_expr((yyvsp[(3) - (3)].expr)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (3)].line); + new_el.name = (yyvsp[(2) - (3)].name); + new_el.value = (yyvsp[(3) - (3)].expr); + set_add_el(&new_el); + } + break; + + case 14: + +/* Line 1806 of yacc.c */ +#line 172 "lib/conf-parse.y" + { sensors_compute new_el; + if (!current_chip) { + sensors_yyerror("Compute statement before first chip statement"); + free((yyvsp[(2) - (5)].name)); + sensors_free_expr((yyvsp[(3) - (5)].expr)); + sensors_free_expr((yyvsp[(5) - (5)].expr)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (5)].line); + new_el.name = (yyvsp[(2) - (5)].name); + new_el.from_proc = (yyvsp[(3) - (5)].expr); + new_el.to_proc = (yyvsp[(5) - (5)].expr); + compute_add_el(&new_el); + } + break; + + case 15: + +/* Line 1806 of yacc.c */ +#line 189 "lib/conf-parse.y" + { sensors_ignore new_el; + if (!current_chip) { + sensors_yyerror("Ignore statement before first chip statement"); + free((yyvsp[(2) - (2)].name)); + YYERROR; + } + new_el.line = (yyvsp[(1) - (2)].line); + new_el.name = (yyvsp[(2) - (2)].name); + ignore_add_el(&new_el); + } + break; + + case 16: + +/* Line 1806 of yacc.c */ +#line 202 "lib/conf-parse.y" + { sensors_chip new_el; + new_el.line = (yyvsp[(1) - (2)].line); + new_el.labels = NULL; + new_el.sets = NULL; + new_el.computes = NULL; + new_el.ignores = NULL; + new_el.labels_count = new_el.labels_max = 0; + new_el.sets_count = new_el.sets_max = 0; + new_el.computes_count = new_el.computes_max = 0; + new_el.ignores_count = new_el.ignores_max = 0; + new_el.chips = (yyvsp[(2) - (2)].chips); + chip_add_el(&new_el); + current_chip = sensors_config_chips + + sensors_config_chips_count - 1; + } + break; + + case 17: + +/* Line 1806 of yacc.c */ +#line 220 "lib/conf-parse.y" + { + (yyval.chips).fits = NULL; + (yyval.chips).fits_count = (yyval.chips).fits_max = 0; + fits_add_el(&(yyvsp[(1) - (1)].chip),(yyval.chips)); + } + break; + + case 18: + +/* Line 1806 of yacc.c */ +#line 226 "lib/conf-parse.y" + { (yyval.chips) = (yyvsp[(1) - (2)].chips); + fits_add_el(&(yyvsp[(2) - (2)].chip),(yyval.chips)); + } + break; + + case 19: + +/* Line 1806 of yacc.c */ +#line 232 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->data.val = (yyvsp[(1) - (1)].value); + (yyval.expr)->kind = sensors_kind_val; + } + break; + + case 20: + +/* Line 1806 of yacc.c */ +#line 237 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->data.var = (yyvsp[(1) - (1)].name); + (yyval.expr)->kind = sensors_kind_var; + } + break; + + case 21: + +/* Line 1806 of yacc.c */ +#line 242 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_source; + } + break; + + case 22: + +/* Line 1806 of yacc.c */ +#line 246 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_add; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 23: + +/* Line 1806 of yacc.c */ +#line 253 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_sub; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 24: + +/* Line 1806 of yacc.c */ +#line 260 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_multiply; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 25: + +/* Line 1806 of yacc.c */ +#line 267 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_divide; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(1) - (3)].expr); + (yyval.expr)->data.subexpr.sub2 = (yyvsp[(3) - (3)].expr); + } + break; + + case 26: + +/* Line 1806 of yacc.c */ +#line 274 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_negate; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 27: + +/* Line 1806 of yacc.c */ +#line 281 "lib/conf-parse.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); } + break; + + case 28: + +/* Line 1806 of yacc.c */ +#line 283 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_exp; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 29: + +/* Line 1806 of yacc.c */ +#line 290 "lib/conf-parse.y" + { (yyval.expr) = malloc_expr(); + (yyval.expr)->kind = sensors_kind_sub; + (yyval.expr)->data.subexpr.op = sensors_log; + (yyval.expr)->data.subexpr.sub1 = (yyvsp[(2) - (2)].expr); + (yyval.expr)->data.subexpr.sub2 = NULL; + } + break; + + case 30: + +/* Line 1806 of yacc.c */ +#line 299 "lib/conf-parse.y" + { int res = sensors_parse_bus_id((yyvsp[(1) - (1)].name),&(yyval.bus)); + free((yyvsp[(1) - (1)].name)); + if (res) { + sensors_yyerror("Parse error in bus id"); + YYERROR; + } + } + break; + + case 31: + +/* Line 1806 of yacc.c */ +#line 309 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 32: + +/* Line 1806 of yacc.c */ +#line 313 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 33: + +/* Line 1806 of yacc.c */ +#line 317 "lib/conf-parse.y" + { (yyval.name) = (yyvsp[(1) - (1)].name); } + break; + + case 34: + +/* Line 1806 of yacc.c */ +#line 321 "lib/conf-parse.y" + { int res = sensors_parse_chip_name((yyvsp[(1) - (1)].name),&(yyval.chip)); + free((yyvsp[(1) - (1)].name)); + if (res) { + sensors_yyerror("Parse error in chip name"); + YYERROR; + } + } + break; + + + +/* Line 1806 of yacc.c */ +#line 1793 "lib/conf-parse.c" + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + +/* Line 2067 of yacc.c */ +#line 330 "lib/conf-parse.y" + + +void sensors_yyerror(const char *err) +{ + if (sensors_lex_error[0]) { + sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno); + sensors_lex_error[0] = '\0'; + } else + sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno); +} + +sensors_expr *malloc_expr(void) +{ + sensors_expr *res = malloc(sizeof(sensors_expr)); + if (! res) + sensors_fatal_error(__func__, "Allocating a new expression"); + return res; +} + diff --git a/tools/gator/daemon/libsensors/conf-parse.h b/tools/gator/daemon/libsensors/conf-parse.h new file mode 100644 index 000000000000..89c9c1a0d59f --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.h @@ -0,0 +1,84 @@ +/* A Bison parser, made by GNU Bison 2.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. + + 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 3 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, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + NEG = 258, + EOL = 259, + BUS = 260, + LABEL = 261, + SET = 262, + CHIP = 263, + COMPUTE = 264, + IGNORE = 265, + FLOAT = 266, + NAME = 267, + ERROR = 268 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 2068 of yacc.c */ +#line 79 "lib/conf-parse.y" + + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; + + + +/* Line 2068 of yacc.c */ +#line 76 "lib/conf-parse.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + +extern YYSTYPE sensors_yylval; + + diff --git a/tools/gator/daemon/libsensors/conf-parse.y b/tools/gator/daemon/libsensors/conf-parse.y new file mode 100644 index 000000000000..1937f544dc66 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf-parse.y @@ -0,0 +1,347 @@ +%{ +/* + conf-parse.y - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +#define YYERROR_VERBOSE + +#include +#include +#include + +#include "data.h" +#include "general.h" +#include "error.h" +#include "conf.h" +#include "access.h" +#include "init.h" + +static void sensors_yyerror(const char *err); +static sensors_expr *malloc_expr(void); + +static sensors_chip *current_chip = NULL; + +#define bus_add_el(el) sensors_add_array_el(el,\ + &sensors_config_busses,\ + &sensors_config_busses_count,\ + &sensors_config_busses_max,\ + sizeof(sensors_bus)) +#define label_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->labels,\ + ¤t_chip->labels_count,\ + ¤t_chip->labels_max,\ + sizeof(sensors_label)); +#define set_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->sets,\ + ¤t_chip->sets_count,\ + ¤t_chip->sets_max,\ + sizeof(sensors_set)); +#define compute_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->computes,\ + ¤t_chip->computes_count,\ + ¤t_chip->computes_max,\ + sizeof(sensors_compute)); +#define ignore_add_el(el) sensors_add_array_el(el,\ + ¤t_chip->ignores,\ + ¤t_chip->ignores_count,\ + ¤t_chip->ignores_max,\ + sizeof(sensors_ignore)); +#define chip_add_el(el) sensors_add_array_el(el,\ + &sensors_config_chips,\ + &sensors_config_chips_count,\ + &sensors_config_chips_max,\ + sizeof(sensors_chip)); + +#define fits_add_el(el,list) sensors_add_array_el(el,\ + &(list).fits,\ + &(list).fits_count,\ + &(list).fits_max, \ + sizeof(sensors_chip_name)); + +%} + +%union { + double value; + char *name; + void *nothing; + sensors_chip_name_list chips; + sensors_expr *expr; + sensors_bus_id bus; + sensors_chip_name chip; + sensors_config_line line; +} + +%left '-' '+' +%left '*' '/' +%left NEG +%right '^' '`' + +%token ',' +%token EOL +%token BUS +%token LABEL +%token SET +%token CHIP +%token COMPUTE +%token IGNORE +%token FLOAT +%token NAME +%token ERROR + +%type chip_name_list +%type expression +%type bus_id +%type adapter_name +%type function_name +%type string +%type chip_name + +%start input + +%% + +input: /* empty */ + | input line +; + +line: bus_statement EOL + | label_statement EOL + | set_statement EOL + | chip_statement EOL + | compute_statement EOL + | ignore_statement EOL + | error EOL +; + +bus_statement: BUS bus_id adapter_name + { sensors_bus new_el; + new_el.line = $1; + new_el.bus = $2; + new_el.adapter = $3; + bus_add_el(&new_el); + } +; + +label_statement: LABEL function_name string + { sensors_label new_el; + if (!current_chip) { + sensors_yyerror("Label statement before first chip statement"); + free($2); + free($3); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.value = $3; + label_add_el(&new_el); + } +; + +set_statement: SET function_name expression + { sensors_set new_el; + if (!current_chip) { + sensors_yyerror("Set statement before first chip statement"); + free($2); + sensors_free_expr($3); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.value = $3; + set_add_el(&new_el); + } +; + +compute_statement: COMPUTE function_name expression ',' expression + { sensors_compute new_el; + if (!current_chip) { + sensors_yyerror("Compute statement before first chip statement"); + free($2); + sensors_free_expr($3); + sensors_free_expr($5); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + new_el.from_proc = $3; + new_el.to_proc = $5; + compute_add_el(&new_el); + } +; + +ignore_statement: IGNORE function_name + { sensors_ignore new_el; + if (!current_chip) { + sensors_yyerror("Ignore statement before first chip statement"); + free($2); + YYERROR; + } + new_el.line = $1; + new_el.name = $2; + ignore_add_el(&new_el); + } +; + +chip_statement: CHIP chip_name_list + { sensors_chip new_el; + new_el.line = $1; + new_el.labels = NULL; + new_el.sets = NULL; + new_el.computes = NULL; + new_el.ignores = NULL; + new_el.labels_count = new_el.labels_max = 0; + new_el.sets_count = new_el.sets_max = 0; + new_el.computes_count = new_el.computes_max = 0; + new_el.ignores_count = new_el.ignores_max = 0; + new_el.chips = $2; + chip_add_el(&new_el); + current_chip = sensors_config_chips + + sensors_config_chips_count - 1; + } +; + +chip_name_list: chip_name + { + $$.fits = NULL; + $$.fits_count = $$.fits_max = 0; + fits_add_el(&$1,$$); + } + | chip_name_list chip_name + { $$ = $1; + fits_add_el(&$2,$$); + } +; + +expression: FLOAT + { $$ = malloc_expr(); + $$->data.val = $1; + $$->kind = sensors_kind_val; + } + | NAME + { $$ = malloc_expr(); + $$->data.var = $1; + $$->kind = sensors_kind_var; + } + | '@' + { $$ = malloc_expr(); + $$->kind = sensors_kind_source; + } + | expression '+' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_add; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '-' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_sub; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '*' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_multiply; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | expression '/' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_divide; + $$->data.subexpr.sub1 = $1; + $$->data.subexpr.sub2 = $3; + } + | '-' expression %prec NEG + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_negate; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } + | '(' expression ')' + { $$ = $2; } + | '^' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_exp; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } + | '`' expression + { $$ = malloc_expr(); + $$->kind = sensors_kind_sub; + $$->data.subexpr.op = sensors_log; + $$->data.subexpr.sub1 = $2; + $$->data.subexpr.sub2 = NULL; + } +; + +bus_id: NAME + { int res = sensors_parse_bus_id($1,&$$); + free($1); + if (res) { + sensors_yyerror("Parse error in bus id"); + YYERROR; + } + } +; + +adapter_name: NAME + { $$ = $1; } +; + +function_name: NAME + { $$ = $1; } +; + +string: NAME + { $$ = $1; } +; + +chip_name: NAME + { int res = sensors_parse_chip_name($1,&$$); + free($1); + if (res) { + sensors_yyerror("Parse error in chip name"); + YYERROR; + } + } +; + +%% + +void sensors_yyerror(const char *err) +{ + if (sensors_lex_error[0]) { + sensors_parse_error_wfn(sensors_lex_error, sensors_yyfilename, sensors_yylineno); + sensors_lex_error[0] = '\0'; + } else + sensors_parse_error_wfn(err, sensors_yyfilename, sensors_yylineno); +} + +sensors_expr *malloc_expr(void) +{ + sensors_expr *res = malloc(sizeof(sensors_expr)); + if (! res) + sensors_fatal_error(__func__, "Allocating a new expression"); + return res; +} diff --git a/tools/gator/daemon/libsensors/conf.h b/tools/gator/daemon/libsensors/conf.h new file mode 100644 index 000000000000..b7ce4f7ca833 --- /dev/null +++ b/tools/gator/daemon/libsensors/conf.h @@ -0,0 +1,34 @@ +/* + conf.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_CONF_H +#define LIB_SENSORS_CONF_H + +/* This is defined in conf-lex.l */ +int sensors_yylex(void); +extern char sensors_lex_error[]; +extern const char *sensors_yyfilename; +extern int sensors_yylineno; +extern FILE *sensors_yyin; + +/* This is defined in conf-parse.y */ +int sensors_yyparse(void); + +#endif /* LIB_SENSORS_CONF_H */ diff --git a/tools/gator/daemon/libsensors/data.c b/tools/gator/daemon/libsensors/data.c new file mode 100644 index 000000000000..cac9c8dc6eb1 --- /dev/null +++ b/tools/gator/daemon/libsensors/data.c @@ -0,0 +1,278 @@ +/* + data.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to move version.h to the current directory. ***/ + +/* this define needed for strndup() */ +#define _GNU_SOURCE + +#include +#include + +#include "access.h" +#include "error.h" +#include "data.h" +#include "sensors.h" +#include "version.h" + +const char *libsensors_version = LM_VERSION; + +char **sensors_config_files = NULL; +int sensors_config_files_count = 0; +int sensors_config_files_max = 0; + +sensors_chip *sensors_config_chips = NULL; +int sensors_config_chips_count = 0; +int sensors_config_chips_subst = 0; +int sensors_config_chips_max = 0; + +sensors_bus *sensors_config_busses = NULL; +int sensors_config_busses_count = 0; +int sensors_config_busses_max = 0; + +sensors_chip_features *sensors_proc_chips = NULL; +int sensors_proc_chips_count = 0; +int sensors_proc_chips_max = 0; + +sensors_bus *sensors_proc_bus = NULL; +int sensors_proc_bus_count = 0; +int sensors_proc_bus_max = 0; + +void sensors_free_chip_name(sensors_chip_name *chip) +{ + free(chip->prefix); +} + +/* + Parse a chip name to the internal representation. These are valid names: + + lm78-i2c-10-5e *-i2c-10-5e + lm78-i2c-10-* *-i2c-10-* + lm78-i2c-*-5e *-i2c-*-5e + lm78-i2c-*-* *-i2c-*-* + lm78-isa-10dd *-isa-10dd + lm78-isa-* *-isa-* + lm78-* *-* + + Here 'lm78' can be any prefix. 'i2c' and 'isa' are + literal strings, just like all dashes '-' and wildcards '*'. '10' can + be any decimal i2c bus number. '5e' can be any hexadecimal i2c device + address, and '10dd' any hexadecimal isa address. + + The 'prefix' part in the result is freshly allocated. All old contents + of res is overwritten. res itself is not allocated. In case of an error + return (ie. != 0), res is undefined, but all allocations are undone. +*/ + +int sensors_parse_chip_name(const char *name, sensors_chip_name *res) +{ + char *dash; + + /* First, the prefix. It's either "*" or a real chip name. */ + if (!strncmp(name, "*-", 2)) { + res->prefix = SENSORS_CHIP_NAME_PREFIX_ANY; + name += 2; + } else { + if (!(dash = strchr(name, '-'))) + return -SENSORS_ERR_CHIP_NAME; + res->prefix = strndup(name, dash - name); + if (!res->prefix) + sensors_fatal_error(__func__, + "Allocating name prefix"); + name = dash + 1; + } + + /* Then we have either a sole "*" (all chips with this name) or a bus + type and an address. */ + if (!strcmp(name, "*")) { + res->bus.type = SENSORS_BUS_TYPE_ANY; + res->bus.nr = SENSORS_BUS_NR_ANY; + res->addr = SENSORS_CHIP_NAME_ADDR_ANY; + return 0; + } + + if (!(dash = strchr(name, '-'))) + goto ERROR; + if (!strncmp(name, "i2c", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_I2C; + else if (!strncmp(name, "isa", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_ISA; + else if (!strncmp(name, "pci", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_PCI; + else if (!strncmp(name, "spi", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_SPI; + else if (!strncmp(name, "virtual", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_VIRTUAL; + else if (!strncmp(name, "acpi", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_ACPI; + else if (!strncmp(name, "hid", dash - name)) + res->bus.type = SENSORS_BUS_TYPE_HID; + else + goto ERROR; + name = dash + 1; + + /* Some bus types (i2c, spi) have an additional bus number. + For these, the next part is either a "*" (any bus of that type) + or a decimal number. */ + switch (res->bus.type) { + case SENSORS_BUS_TYPE_I2C: + case SENSORS_BUS_TYPE_SPI: + case SENSORS_BUS_TYPE_HID: + if (!strncmp(name, "*-", 2)) { + res->bus.nr = SENSORS_BUS_NR_ANY; + name += 2; + break; + } + + res->bus.nr = strtoul(name, &dash, 10); + if (*name == '\0' || *dash != '-' || res->bus.nr < 0) + goto ERROR; + name = dash + 1; + break; + default: + res->bus.nr = SENSORS_BUS_NR_ANY; + } + + /* Last part is the chip address, or "*" for any address. */ + if (!strcmp(name, "*")) { + res->addr = SENSORS_CHIP_NAME_ADDR_ANY; + } else { + res->addr = strtoul(name, &dash, 16); + if (*name == '\0' || *dash != '\0' || res->addr < 0) + goto ERROR; + } + + return 0; + +ERROR: + free(res->prefix); + return -SENSORS_ERR_CHIP_NAME; +} + +int sensors_snprintf_chip_name(char *str, size_t size, + const sensors_chip_name *chip) +{ + if (sensors_chip_name_has_wildcards(chip)) + return -SENSORS_ERR_WILDCARDS; + + switch (chip->bus.type) { + case SENSORS_BUS_TYPE_ISA: + return snprintf(str, size, "%s-isa-%04x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_PCI: + return snprintf(str, size, "%s-pci-%04x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_I2C: + return snprintf(str, size, "%s-i2c-%hd-%02x", chip->prefix, + chip->bus.nr, chip->addr); + case SENSORS_BUS_TYPE_SPI: + return snprintf(str, size, "%s-spi-%hd-%x", chip->prefix, + chip->bus.nr, chip->addr); + case SENSORS_BUS_TYPE_VIRTUAL: + return snprintf(str, size, "%s-virtual-%x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_ACPI: + return snprintf(str, size, "%s-acpi-%x", chip->prefix, + chip->addr); + case SENSORS_BUS_TYPE_HID: + return snprintf(str, size, "%s-hid-%hd-%x", chip->prefix, + chip->bus.nr, chip->addr); + } + + return -SENSORS_ERR_CHIP_NAME; +} + +int sensors_parse_bus_id(const char *name, sensors_bus_id *bus) +{ + char *endptr; + + if (strncmp(name, "i2c-", 4)) { + return -SENSORS_ERR_BUS_NAME; + } + name += 4; + bus->type = SENSORS_BUS_TYPE_I2C; + bus->nr = strtoul(name, &endptr, 10); + if (*name == '\0' || *endptr != '\0' || bus->nr < 0) + return -SENSORS_ERR_BUS_NAME; + return 0; +} + +static int sensors_substitute_chip(sensors_chip_name *name, + const char *filename, int lineno) +{ + int i, j; + for (i = 0; i < sensors_config_busses_count; i++) + if (sensors_config_busses[i].bus.type == name->bus.type && + sensors_config_busses[i].bus.nr == name->bus.nr) + break; + + if (i == sensors_config_busses_count) { + sensors_parse_error_wfn("Undeclared bus id referenced", + filename, lineno); + name->bus.nr = SENSORS_BUS_NR_IGNORE; + return -SENSORS_ERR_BUS_NAME; + } + + /* Compare the adapter names */ + for (j = 0; j < sensors_proc_bus_count; j++) { + if (!strcmp(sensors_config_busses[i].adapter, + sensors_proc_bus[j].adapter)) { + name->bus.nr = sensors_proc_bus[j].bus.nr; + return 0; + } + } + + /* We did not find a matching bus name, simply ignore this chip + config entry. */ + name->bus.nr = SENSORS_BUS_NR_IGNORE; + return 0; +} + +/* Bus substitution is on a per-configuration file basis, so we keep + memory (in sensors_config_chips_subst) of which chip entries have been + already substituted. */ +int sensors_substitute_busses(void) +{ + int err, i, j, lineno; + sensors_chip_name_list *chips; + const char *filename; + int res = 0; + + for (i = sensors_config_chips_subst; + i < sensors_config_chips_count; i++) { + filename = sensors_config_chips[i].line.filename; + lineno = sensors_config_chips[i].line.lineno; + chips = &sensors_config_chips[i].chips; + for (j = 0; j < chips->fits_count; j++) { + /* We can only substitute if a specific bus number + is given. */ + if (chips->fits[j].bus.nr == SENSORS_BUS_NR_ANY) + continue; + + err = sensors_substitute_chip(&chips->fits[j], + filename, lineno); + if (err) + res = err; + } + } + sensors_config_chips_subst = sensors_config_chips_count; + return res; +} diff --git a/tools/gator/daemon/libsensors/data.h b/tools/gator/daemon/libsensors/data.h new file mode 100644 index 000000000000..4a23eabe1414 --- /dev/null +++ b/tools/gator/daemon/libsensors/data.h @@ -0,0 +1,184 @@ +/* + data.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_DATA_H +#define LIB_SENSORS_DATA_H + +#include "sensors.h" +#include "general.h" + +/* This header file contains all kinds of data structures which are used + for the representation of the config file data and the sensors + data. */ + +/* Kinds of expression operators recognized */ +typedef enum sensors_operation { + sensors_add, sensors_sub, sensors_multiply, sensors_divide, + sensors_negate, sensors_exp, sensors_log, +} sensors_operation; + +/* An expression can have several forms */ +typedef enum sensors_expr_kind { + sensors_kind_val, sensors_kind_source, sensors_kind_var, + sensors_kind_sub +} sensors_expr_kind; + +/* An expression. It is either a floating point value, a variable name, + an operation on subexpressions, or the special value 'sub' } */ +struct sensors_expr; + +typedef struct sensors_subexpr { + sensors_operation op; + struct sensors_expr *sub1; + struct sensors_expr *sub2; +} sensors_subexpr; + +typedef struct sensors_expr { + sensors_expr_kind kind; + union { + double val; + char *var; + sensors_subexpr subexpr; + } data; +} sensors_expr; + +/* Config file line reference */ +typedef struct sensors_config_line { + const char *filename; + int lineno; +} sensors_config_line; + +/* Config file label declaration: a feature name, combined with the label + value */ +typedef struct sensors_label { + char *name; + char *value; + sensors_config_line line; +} sensors_label; + +/* Config file set declaration: a subfeature name, combined with an + expression */ +typedef struct sensors_set { + char *name; + sensors_expr *value; + sensors_config_line line; +} sensors_set; + +/* Config file compute declaration: a feature name, combined with two + expressions */ +typedef struct sensors_compute { + char *name; + sensors_expr *from_proc; + sensors_expr *to_proc; + sensors_config_line line; +} sensors_compute; + +/* Config file ignore declaration: a feature name */ +typedef struct sensors_ignore { + char *name; + sensors_config_line line; +} sensors_ignore; + +/* A list of chip names, used to represent a config file chips declaration */ +typedef struct sensors_chip_name_list { + sensors_chip_name *fits; + int fits_count; + int fits_max; +} sensors_chip_name_list; + +/* A config file chip block */ +typedef struct sensors_chip { + sensors_chip_name_list chips; + sensors_label *labels; + int labels_count; + int labels_max; + sensors_set *sets; + int sets_count; + int sets_max; + sensors_compute *computes; + int computes_count; + int computes_max; + sensors_ignore *ignores; + int ignores_count; + int ignores_max; + sensors_config_line line; +} sensors_chip; + +/* Config file bus declaration: the bus type and number, combined with adapter + name */ +typedef struct sensors_bus { + char *adapter; + sensors_bus_id bus; + sensors_config_line line; +} sensors_bus; + +/* Internal data about all features and subfeatures of a chip */ +typedef struct sensors_chip_features { + struct sensors_chip_name chip; + struct sensors_feature *feature; + struct sensors_subfeature *subfeature; + int feature_count; + int subfeature_count; +} sensors_chip_features; + +extern char **sensors_config_files; +extern int sensors_config_files_count; +extern int sensors_config_files_max; + +#define sensors_add_config_files(el) sensors_add_array_el( \ + (el), &sensors_config_files, &sensors_config_files_count, \ + &sensors_config_files_max, sizeof(char *)) + +extern sensors_chip *sensors_config_chips; +extern int sensors_config_chips_count; +extern int sensors_config_chips_subst; +extern int sensors_config_chips_max; + +extern sensors_bus *sensors_config_busses; +extern int sensors_config_busses_count; +extern int sensors_config_busses_max; + +extern sensors_chip_features *sensors_proc_chips; +extern int sensors_proc_chips_count; +extern int sensors_proc_chips_max; + +#define sensors_add_proc_chips(el) sensors_add_array_el( \ + (el), &sensors_proc_chips, &sensors_proc_chips_count,\ + &sensors_proc_chips_max, sizeof(struct sensors_chip_features)) + +extern sensors_bus *sensors_proc_bus; +extern int sensors_proc_bus_count; +extern int sensors_proc_bus_max; + +#define sensors_add_proc_bus(el) sensors_add_array_el( \ + (el), &sensors_proc_bus, &sensors_proc_bus_count,\ + &sensors_proc_bus_max, sizeof(struct sensors_bus)) + +/* Substitute configuration bus numbers with real-world bus numbers + in the chips lists */ +int sensors_substitute_busses(void); + + +/* Parse a bus id into its components. Returns 0 on success, a value from + error.h on failure. */ +int sensors_parse_bus_id(const char *name, sensors_bus_id *bus); + +#endif /* def LIB_SENSORS_DATA_H */ diff --git a/tools/gator/daemon/libsensors/error.c b/tools/gator/daemon/libsensors/error.c new file mode 100644 index 000000000000..55bde81295fb --- /dev/null +++ b/tools/gator/daemon/libsensors/error.c @@ -0,0 +1,92 @@ +/* + error.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include +#include +#include "error.h" +#include "general.h" + +static void sensors_default_parse_error(const char *err, int lineno); +static void sensors_default_parse_error_wfn(const char *err, + const char *filename, int lineno); +static void sensors_default_fatal_error(const char *proc, const char *err); + +void (*sensors_parse_error) (const char *err, int lineno) = + sensors_default_parse_error; +void (*sensors_parse_error_wfn) (const char *err, const char *filename, + int lineno) = sensors_default_parse_error_wfn; +void (*sensors_fatal_error) (const char *proc, const char *err) = + sensors_default_fatal_error; + +static const char *errorlist[] = { + /* Invalid error code */ "Unknown error", + /* SENSORS_ERR_WILDCARDS */ "Wildcard found in chip name", + /* SENSORS_ERR_NO_ENTRY */ "No such subfeature known", + /* SENSORS_ERR_ACCESS_R */ "Can't read", + /* SENSORS_ERR_KERNEL */ "Kernel interface error", + /* SENSORS_ERR_DIV_ZERO */ "Divide by zero", + /* SENSORS_ERR_CHIP_NAME */ "Can't parse chip name", + /* SENSORS_ERR_BUS_NAME */ "Can't parse bus name", + /* SENSORS_ERR_PARSE */ "General parse error", + /* SENSORS_ERR_ACCESS_W */ "Can't write", + /* SENSORS_ERR_IO */ "I/O error", + /* SENSORS_ERR_RECURSION */ "Evaluation recurses too deep", +}; + +const char *sensors_strerror(int errnum) +{ + if (errnum < 0) + errnum = -errnum; + if (errnum >= ARRAY_SIZE(errorlist)) + errnum = 0; + return errorlist[errnum]; +} + +void sensors_default_parse_error(const char *err, int lineno) +{ + if (lineno) + fprintf(stderr, "Error: Line %d: %s\n", lineno, err); + else + fprintf(stderr, "Error: %s\n", err); +} + +void sensors_default_parse_error_wfn(const char *err, + const char *filename, int lineno) +{ + /* If application provided a custom parse error reporting function + but not the variant with the filename, fall back to the original + variant without the filename, for backwards compatibility. */ + if (sensors_parse_error != sensors_default_parse_error || + !filename) + return sensors_parse_error(err, lineno); + + if (lineno) + fprintf(stderr, "Error: File %s, line %d: %s\n", filename, + lineno, err); + else + fprintf(stderr, "Error: File %s: %s\n", filename, err); +} + +void sensors_default_fatal_error(const char *proc, const char *err) +{ + fprintf(stderr, "Fatal error in `%s': %s\n", proc, err); + exit(1); +} diff --git a/tools/gator/daemon/libsensors/error.h b/tools/gator/daemon/libsensors/error.h new file mode 100644 index 000000000000..37cdc956151d --- /dev/null +++ b/tools/gator/daemon/libsensors/error.h @@ -0,0 +1,74 @@ +/* + error.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007-2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_ERROR_H +#define LIB_SENSORS_ERROR_H + +#define SENSORS_ERR_WILDCARDS 1 /* Wildcard found in chip name */ +#define SENSORS_ERR_NO_ENTRY 2 /* No such subfeature known */ +#define SENSORS_ERR_ACCESS_R 3 /* Can't read */ +#define SENSORS_ERR_KERNEL 4 /* Kernel interface error */ +#define SENSORS_ERR_DIV_ZERO 5 /* Divide by zero */ +#define SENSORS_ERR_CHIP_NAME 6 /* Can't parse chip name */ +#define SENSORS_ERR_BUS_NAME 7 /* Can't parse bus name */ +#define SENSORS_ERR_PARSE 8 /* General parse error */ +#define SENSORS_ERR_ACCESS_W 9 /* Can't write */ +#define SENSORS_ERR_IO 10 /* I/O error */ +#define SENSORS_ERR_RECURSION 11 /* Evaluation recurses too deep */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* This function returns a pointer to a string which describes the error. + errnum may be negative (the corresponding positive error is returned). + You may not modify the result! */ +const char *sensors_strerror(int errnum); + +/* These functions are called when a parse error is detected. Give them new + values, and your own functions are called instead of the default (which + print to stderr). These functions may terminate the program, but they + usually output an error and return. The first function is the original + one, the second one was added later when support for multiple + configuration files was added. + The library code now only calls the second function. However, for + backwards compatibility, if an application provides a custom handling + function for the first function but not the second, then all parse + errors will be reported using the first function (that is, the filename + is never reported.) + Note that filename can be NULL (if filename isn't known) and lineno + can be 0 (if the error occurs before the actual parsing starts.) */ +extern void (*sensors_parse_error) (const char *err, int lineno); +extern void (*sensors_parse_error_wfn) (const char *err, + const char *filename, int lineno); + +/* This function is called when an immediately fatal error (like no + memory left) is detected. Give it a new value, and your own function + is called instead of the default (which prints to stderr and ends + the program). Never let it return! */ +extern void (*sensors_fatal_error) (const char *proc, const char *err); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* def LIB_SENSORS_ERROR_H */ diff --git a/tools/gator/daemon/libsensors/general.c b/tools/gator/daemon/libsensors/general.c new file mode 100644 index 000000000000..f237e3b59c22 --- /dev/null +++ b/tools/gator/daemon/libsensors/general.c @@ -0,0 +1,85 @@ +/* + general.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#include "error.h" +#include "general.h" +#include +#include +#include +#include + + +#define A_BUNCH 16 + +void sensors_malloc_array(void *list, int *num_el, int *max_el, int el_size) +{ + void **my_list = (void **)list; + + *my_list = malloc(el_size*A_BUNCH); + if (! *my_list) + sensors_fatal_error(__func__, "Allocating new elements"); + *max_el = A_BUNCH; + *num_el = 0; +} + +void sensors_free_array(void *list, int *num_el, int *max_el) +{ + void **my_list = (void **)list; + + free(*my_list); + *my_list = NULL; + *num_el = 0; + *max_el = 0; +} + +void sensors_add_array_el(const void *el, void *list, int *num_el, + int *max_el, int el_size) +{ + int new_max_el; + void **my_list = (void *)list; + if (*num_el + 1 > *max_el) { + new_max_el = *max_el + A_BUNCH; + *my_list = realloc(*my_list, new_max_el * el_size); + if (! *my_list) + sensors_fatal_error(__func__, + "Allocating new elements"); + *max_el = new_max_el; + } + memcpy(((char *) *my_list) + *num_el * el_size, el, el_size); + (*num_el) ++; +} + +void sensors_add_array_els(const void *els, int nr_els, void *list, + int *num_el, int *max_el, int el_size) +{ + int new_max_el; + void **my_list = (void *)list; + if (*num_el + nr_els > *max_el) { + new_max_el = (*max_el + nr_els + A_BUNCH); + new_max_el -= new_max_el % A_BUNCH; + *my_list = realloc(*my_list, new_max_el * el_size); + if (! *my_list) + sensors_fatal_error(__func__, + "Allocating new elements"); + *max_el = new_max_el; + } + memcpy(((char *)*my_list) + *num_el * el_size, els, el_size * nr_els); + *num_el += nr_els; +} diff --git a/tools/gator/daemon/libsensors/general.h b/tools/gator/daemon/libsensors/general.h new file mode 100644 index 000000000000..a3971e02e1b7 --- /dev/null +++ b/tools/gator/daemon/libsensors/general.h @@ -0,0 +1,39 @@ +/* + general.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_GENERAL +#define LIB_SENSORS_GENERAL + +/* These are general purpose functions. They allow you to use variable- + length arrays, which are extended automatically. A distinction is + made between the current number of elements and the maximum number. + You can only add elements at the end. Primitive, but very useful + for internal use. */ +void sensors_malloc_array(void *list, int *num_el, int *max_el, + int el_size); +void sensors_free_array(void *list, int *num_el, int *max_el); +void sensors_add_array_el(const void *el, void *list, int *num_el, + int *max_el, int el_size); +void sensors_add_array_els(const void *els, int nr_els, void *list, + int *num_el, int *max_el, int el_size); + +#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0])) + +#endif /* LIB_SENSORS_GENERAL */ diff --git a/tools/gator/daemon/libsensors/init.c b/tools/gator/daemon/libsensors/init.c new file mode 100644 index 000000000000..558046e76c24 --- /dev/null +++ b/tools/gator/daemon/libsensors/init.c @@ -0,0 +1,341 @@ +/* + init.c - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2009 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to cast alphasort to supress a warning as it's prototype is different on android. ***/ + +/* Needed for scandir() and alphasort() */ +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sensors.h" +#include "data.h" +#include "error.h" +#include "access.h" +#include "conf.h" +#include "sysfs.h" +#include "scanner.h" +#include "init.h" + +#define DEFAULT_CONFIG_FILE ETCDIR "/sensors3.conf" +#define ALT_CONFIG_FILE ETCDIR "/sensors.conf" +#define DEFAULT_CONFIG_DIR ETCDIR "/sensors.d" + +/* Wrapper around sensors_yyparse(), which clears the locale so that + the decimal numbers are always parsed properly. */ +static int sensors_parse(void) +{ + int res; + char *locale; + + /* Remember the current locale and clear it */ + locale = setlocale(LC_ALL, NULL); + if (locale) { + locale = strdup(locale); + if (!locale) + sensors_fatal_error(__func__, "Out of memory"); + + setlocale(LC_ALL, "C"); + } + + res = sensors_yyparse(); + + /* Restore the old locale */ + if (locale) { + setlocale(LC_ALL, locale); + free(locale); + } + + return res; +} + +static void free_bus(sensors_bus *bus) +{ + free(bus->adapter); +} + +static void free_config_busses(void) +{ + int i; + + for (i = 0; i < sensors_config_busses_count; i++) + free_bus(&sensors_config_busses[i]); + free(sensors_config_busses); + sensors_config_busses = NULL; + sensors_config_busses_count = sensors_config_busses_max = 0; +} + +static int parse_config(FILE *input, const char *name) +{ + int err; + char *name_copy; + + if (name) { + /* Record configuration file name for error reporting */ + name_copy = strdup(name); + if (!name_copy) + sensors_fatal_error(__func__, "Out of memory"); + sensors_add_config_files(&name_copy); + } else + name_copy = NULL; + + if (sensors_scanner_init(input, name_copy)) { + err = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + err = sensors_parse(); + sensors_scanner_exit(); + if (err) { + err = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + + err = sensors_substitute_busses(); + +exit_cleanup: + free_config_busses(); + return err; +} + +static int config_file_filter(const struct dirent *entry) +{ + return entry->d_name[0] != '.'; /* Skip hidden files */ +} + +static int add_config_from_dir(const char *dir) +{ + int count, res, i; + struct dirent **namelist; + + count = scandir(dir, &namelist, config_file_filter, (int (*)(const struct dirent **, const struct dirent **))alphasort); + if (count < 0) { + /* Do not return an error if directory does not exist */ + if (errno == ENOENT) + return 0; + + sensors_parse_error_wfn(strerror(errno), NULL, 0); + return -SENSORS_ERR_PARSE; + } + + for (res = 0, i = 0; !res && i < count; i++) { + int len; + char path[PATH_MAX]; + FILE *input; + struct stat st; + + len = snprintf(path, sizeof(path), "%s/%s", dir, + namelist[i]->d_name); + if (len < 0 || len >= (int)sizeof(path)) { + res = -SENSORS_ERR_PARSE; + continue; + } + + /* Only accept regular files */ + if (stat(path, &st) < 0 || !S_ISREG(st.st_mode)) + continue; + + input = fopen(path, "r"); + if (input) { + res = parse_config(input, path); + fclose(input); + } else { + res = -SENSORS_ERR_PARSE; + sensors_parse_error_wfn(strerror(errno), path, 0); + } + } + + /* Free memory allocated by scandir() */ + for (i = 0; i < count; i++) + free(namelist[i]); + free(namelist); + + return res; +} + +int sensors_init(FILE *input) +{ + int res; + + if (!sensors_init_sysfs()) + return -SENSORS_ERR_KERNEL; + if ((res = sensors_read_sysfs_bus()) || + (res = sensors_read_sysfs_chips())) + goto exit_cleanup; + + if (input) { + res = parse_config(input, NULL); + if (res) + goto exit_cleanup; + } else { + const char* name; + + /* No configuration provided, use default */ + input = fopen(name = DEFAULT_CONFIG_FILE, "r"); + if (!input && errno == ENOENT) + input = fopen(name = ALT_CONFIG_FILE, "r"); + if (input) { + res = parse_config(input, name); + fclose(input); + if (res) + goto exit_cleanup; + + } else if (errno != ENOENT) { + sensors_parse_error_wfn(strerror(errno), name, 0); + res = -SENSORS_ERR_PARSE; + goto exit_cleanup; + } + + /* Also check for files in default directory */ + res = add_config_from_dir(DEFAULT_CONFIG_DIR); + if (res) + goto exit_cleanup; + } + + return 0; + +exit_cleanup: + sensors_cleanup(); + return res; +} + +static void free_chip_name(sensors_chip_name *name) +{ + free(name->prefix); + free(name->path); +} + +static void free_chip_features(sensors_chip_features *features) +{ + int i; + + for (i = 0; i < features->subfeature_count; i++) + free(features->subfeature[i].name); + free(features->subfeature); + for (i = 0; i < features->feature_count; i++) + free(features->feature[i].name); + free(features->feature); +} + +static void free_label(sensors_label *label) +{ + free(label->name); + free(label->value); +} + +void sensors_free_expr(sensors_expr *expr) +{ + if (expr->kind == sensors_kind_var) + free(expr->data.var); + else if (expr->kind == sensors_kind_sub) { + if (expr->data.subexpr.sub1) + sensors_free_expr(expr->data.subexpr.sub1); + if (expr->data.subexpr.sub2) + sensors_free_expr(expr->data.subexpr.sub2); + } + free(expr); +} + +static void free_set(sensors_set *set) +{ + free(set->name); + sensors_free_expr(set->value); +} + +static void free_compute(sensors_compute *compute) +{ + free(compute->name); + sensors_free_expr(compute->from_proc); + sensors_free_expr(compute->to_proc); +} + +static void free_ignore(sensors_ignore *ignore) +{ + free(ignore->name); +} + +static void free_chip(sensors_chip *chip) +{ + int i; + + for (i = 0; i < chip->chips.fits_count; i++) + free_chip_name(&chip->chips.fits[i]); + free(chip->chips.fits); + chip->chips.fits_count = chip->chips.fits_max = 0; + + for (i = 0; i < chip->labels_count; i++) + free_label(&chip->labels[i]); + free(chip->labels); + chip->labels_count = chip->labels_max = 0; + + for (i = 0; i < chip->sets_count; i++) + free_set(&chip->sets[i]); + free(chip->sets); + chip->sets_count = chip->sets_max = 0; + + for (i = 0; i < chip->computes_count; i++) + free_compute(&chip->computes[i]); + free(chip->computes); + chip->computes_count = chip->computes_max = 0; + + for (i = 0; i < chip->ignores_count; i++) + free_ignore(&chip->ignores[i]); + free(chip->ignores); + chip->ignores_count = chip->ignores_max = 0; +} + +void sensors_cleanup(void) +{ + int i; + + for (i = 0; i < sensors_proc_chips_count; i++) { + free_chip_name(&sensors_proc_chips[i].chip); + free_chip_features(&sensors_proc_chips[i]); + } + free(sensors_proc_chips); + sensors_proc_chips = NULL; + sensors_proc_chips_count = sensors_proc_chips_max = 0; + + for (i = 0; i < sensors_config_chips_count; i++) + free_chip(&sensors_config_chips[i]); + free(sensors_config_chips); + sensors_config_chips = NULL; + sensors_config_chips_count = sensors_config_chips_max = 0; + sensors_config_chips_subst = 0; + + for (i = 0; i < sensors_proc_bus_count; i++) + free_bus(&sensors_proc_bus[i]); + free(sensors_proc_bus); + sensors_proc_bus = NULL; + sensors_proc_bus_count = sensors_proc_bus_max = 0; + + for (i = 0; i < sensors_config_files_count; i++) + free(sensors_config_files[i]); + free(sensors_config_files); + sensors_config_files = NULL; + sensors_config_files_count = sensors_config_files_max = 0; +} diff --git a/tools/gator/daemon/libsensors/init.h b/tools/gator/daemon/libsensors/init.h new file mode 100644 index 000000000000..47006a62db63 --- /dev/null +++ b/tools/gator/daemon/libsensors/init.h @@ -0,0 +1,28 @@ +/* + init.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (C) 2007 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef LIB_SENSORS_INIT_H +#define LIB_SENSORS_INIT_H + +#include "data.h" + +void sensors_free_expr(sensors_expr *expr); + +#endif /* def LIB_SENSORS_INIT_H */ diff --git a/tools/gator/daemon/libsensors/scanner.h b/tools/gator/daemon/libsensors/scanner.h new file mode 100644 index 000000000000..4c415167de5c --- /dev/null +++ b/tools/gator/daemon/libsensors/scanner.h @@ -0,0 +1,32 @@ +/* + scanner.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 2006 Mark M. Hoffman + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to fix input defined but not used warning from conf-lex.c. ***/ + +#ifndef LIB_SENSORS_SCANNER_H +#define LIB_SENSORS_SCANNER_H + +int sensors_scanner_init(FILE *input, const char *filename); +void sensors_scanner_exit(void); + +#define YY_NO_INPUT + +#endif + diff --git a/tools/gator/daemon/libsensors/sensors.h b/tools/gator/daemon/libsensors/sensors.h new file mode 100644 index 000000000000..7874d028dc82 --- /dev/null +++ b/tools/gator/daemon/libsensors/sensors.h @@ -0,0 +1,311 @@ +/* + sensors.h - Part of libsensors, a Linux library for reading sensor data. + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (C) 2007, 2010 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to read non-scaled values. ***/ + +#ifndef LIB_SENSORS_SENSORS_H +#define LIB_SENSORS_SENSORS_H + +#include +#include + +/* Publicly accessible library functions */ + +/* libsensors API version define, first digit is the major version (changed + when the API + ABI breaks), the third digit is incremented to track small + API additions like new flags / enum values. The second digit is for tracking + larger additions like new methods. */ +#define SENSORS_API_VERSION 0x432 + +#define SENSORS_CHIP_NAME_PREFIX_ANY NULL +#define SENSORS_CHIP_NAME_ADDR_ANY (-1) + +#define SENSORS_BUS_TYPE_ANY (-1) +#define SENSORS_BUS_TYPE_I2C 0 +#define SENSORS_BUS_TYPE_ISA 1 +#define SENSORS_BUS_TYPE_PCI 2 +#define SENSORS_BUS_TYPE_SPI 3 +#define SENSORS_BUS_TYPE_VIRTUAL 4 +#define SENSORS_BUS_TYPE_ACPI 5 +#define SENSORS_BUS_TYPE_HID 6 +#define SENSORS_BUS_NR_ANY (-1) +#define SENSORS_BUS_NR_IGNORE (-2) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern const char *libsensors_version; + +extern int sensors_sysfs_no_scaling; + +typedef struct sensors_bus_id { + short type; + short nr; +} sensors_bus_id; + +/* A chip name is encoded in this structure */ +typedef struct sensors_chip_name { + char *prefix; + sensors_bus_id bus; + int addr; + char *path; +} sensors_chip_name; + +/* Load the configuration file and the detected chips list. If this + returns a value unequal to zero, you are in trouble; you can not + assume anything will be initialized properly. If you want to + reload the configuration file, call sensors_cleanup() below before + calling sensors_init() again. */ +int sensors_init(FILE *input); + +/* Clean-up function: You can't access anything after + this, until the next sensors_init() call! */ +void sensors_cleanup(void); + +/* Parse a chip name to the internal representation. Return 0 on success, <0 + on error. */ +int sensors_parse_chip_name(const char *orig_name, sensors_chip_name *res); + +/* Free memory allocated for the internal representation of a chip name. */ +void sensors_free_chip_name(sensors_chip_name *chip); + +/* Print a chip name from its internal representation. Note that chip should + not contain wildcard values! Return the number of characters printed on + success (same as snprintf), <0 on error. */ +int sensors_snprintf_chip_name(char *str, size_t size, + const sensors_chip_name *chip); + +/* This function returns the adapter name of a bus, + as used within the sensors_chip_name structure. If it could not be found, + it returns NULL */ +const char *sensors_get_adapter_name(const sensors_bus_id *bus); + +typedef struct sensors_feature sensors_feature; + +/* Look up the label for a given feature. Note that chip should not + contain wildcard values! The returned string is newly allocated (free it + yourself). On failure, NULL is returned. + If no label exists for this feature, its name is returned itself. */ +char *sensors_get_label(const sensors_chip_name *name, + const sensors_feature *feature); + +/* Read the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_get_value(const sensors_chip_name *name, int subfeat_nr, + double *value); + +/* Set the value of a subfeature of a certain chip. Note that chip should not + contain wildcard values! This function will return 0 on success, and <0 + on failure. */ +int sensors_set_value(const sensors_chip_name *name, int subfeat_nr, + double value); + +/* Execute all set statements for this particular chip. The chip may contain + wildcards! This function will return 0 on success, and <0 on failure. */ +int sensors_do_chip_sets(const sensors_chip_name *name); + +/* This function returns all detected chips that match a given chip name, + one by one. If no chip name is provided, all detected chips are returned. + To start at the beginning of the list, use 0 for nr; NULL is returned if + we are at the end of the list. Do not try to change these chip names, as + they point to internal structures! */ +const sensors_chip_name *sensors_get_detected_chips(const sensors_chip_name + *match, int *nr); + +/* These defines are used in the flags field of sensors_subfeature */ +#define SENSORS_MODE_R 1 +#define SENSORS_MODE_W 2 +#define SENSORS_COMPUTE_MAPPING 4 + +typedef enum sensors_feature_type { + SENSORS_FEATURE_IN = 0x00, + SENSORS_FEATURE_FAN = 0x01, + SENSORS_FEATURE_TEMP = 0x02, + SENSORS_FEATURE_POWER = 0x03, + SENSORS_FEATURE_ENERGY = 0x04, + SENSORS_FEATURE_CURR = 0x05, + SENSORS_FEATURE_HUMIDITY = 0x06, + SENSORS_FEATURE_MAX_MAIN, + SENSORS_FEATURE_VID = 0x10, + SENSORS_FEATURE_INTRUSION = 0x11, + SENSORS_FEATURE_MAX_OTHER, + SENSORS_FEATURE_BEEP_ENABLE = 0x18, + SENSORS_FEATURE_UNKNOWN = INT_MAX, +} sensors_feature_type; + +/* All the sensor types (in, fan, temp, vid) are a multiple of 0x100 apart, + and sensor subfeatures which have no compute mapping have bit 7 set. */ +typedef enum sensors_subfeature_type { + SENSORS_SUBFEATURE_IN_INPUT = SENSORS_FEATURE_IN << 8, + SENSORS_SUBFEATURE_IN_MIN, + SENSORS_SUBFEATURE_IN_MAX, + SENSORS_SUBFEATURE_IN_LCRIT, + SENSORS_SUBFEATURE_IN_CRIT, + SENSORS_SUBFEATURE_IN_AVERAGE, + SENSORS_SUBFEATURE_IN_LOWEST, + SENSORS_SUBFEATURE_IN_HIGHEST, + SENSORS_SUBFEATURE_IN_ALARM = (SENSORS_FEATURE_IN << 8) | 0x80, + SENSORS_SUBFEATURE_IN_MIN_ALARM, + SENSORS_SUBFEATURE_IN_MAX_ALARM, + SENSORS_SUBFEATURE_IN_BEEP, + SENSORS_SUBFEATURE_IN_LCRIT_ALARM, + SENSORS_SUBFEATURE_IN_CRIT_ALARM, + + SENSORS_SUBFEATURE_FAN_INPUT = SENSORS_FEATURE_FAN << 8, + SENSORS_SUBFEATURE_FAN_MIN, + SENSORS_SUBFEATURE_FAN_MAX, + SENSORS_SUBFEATURE_FAN_ALARM = (SENSORS_FEATURE_FAN << 8) | 0x80, + SENSORS_SUBFEATURE_FAN_FAULT, + SENSORS_SUBFEATURE_FAN_DIV, + SENSORS_SUBFEATURE_FAN_BEEP, + SENSORS_SUBFEATURE_FAN_PULSES, + SENSORS_SUBFEATURE_FAN_MIN_ALARM, + SENSORS_SUBFEATURE_FAN_MAX_ALARM, + + SENSORS_SUBFEATURE_TEMP_INPUT = SENSORS_FEATURE_TEMP << 8, + SENSORS_SUBFEATURE_TEMP_MAX, + SENSORS_SUBFEATURE_TEMP_MAX_HYST, + SENSORS_SUBFEATURE_TEMP_MIN, + SENSORS_SUBFEATURE_TEMP_CRIT, + SENSORS_SUBFEATURE_TEMP_CRIT_HYST, + SENSORS_SUBFEATURE_TEMP_LCRIT, + SENSORS_SUBFEATURE_TEMP_EMERGENCY, + SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST, + SENSORS_SUBFEATURE_TEMP_LOWEST, + SENSORS_SUBFEATURE_TEMP_HIGHEST, + SENSORS_SUBFEATURE_TEMP_ALARM = (SENSORS_FEATURE_TEMP << 8) | 0x80, + SENSORS_SUBFEATURE_TEMP_MAX_ALARM, + SENSORS_SUBFEATURE_TEMP_MIN_ALARM, + SENSORS_SUBFEATURE_TEMP_CRIT_ALARM, + SENSORS_SUBFEATURE_TEMP_FAULT, + SENSORS_SUBFEATURE_TEMP_TYPE, + SENSORS_SUBFEATURE_TEMP_OFFSET, + SENSORS_SUBFEATURE_TEMP_BEEP, + SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM, + SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM, + + SENSORS_SUBFEATURE_POWER_AVERAGE = SENSORS_FEATURE_POWER << 8, + SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST, + SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST, + SENSORS_SUBFEATURE_POWER_INPUT, + SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST, + SENSORS_SUBFEATURE_POWER_INPUT_LOWEST, + SENSORS_SUBFEATURE_POWER_CAP, + SENSORS_SUBFEATURE_POWER_CAP_HYST, + SENSORS_SUBFEATURE_POWER_MAX, + SENSORS_SUBFEATURE_POWER_CRIT, + SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL = (SENSORS_FEATURE_POWER << 8) | 0x80, + SENSORS_SUBFEATURE_POWER_ALARM, + SENSORS_SUBFEATURE_POWER_CAP_ALARM, + SENSORS_SUBFEATURE_POWER_MAX_ALARM, + SENSORS_SUBFEATURE_POWER_CRIT_ALARM, + + SENSORS_SUBFEATURE_ENERGY_INPUT = SENSORS_FEATURE_ENERGY << 8, + + SENSORS_SUBFEATURE_CURR_INPUT = SENSORS_FEATURE_CURR << 8, + SENSORS_SUBFEATURE_CURR_MIN, + SENSORS_SUBFEATURE_CURR_MAX, + SENSORS_SUBFEATURE_CURR_LCRIT, + SENSORS_SUBFEATURE_CURR_CRIT, + SENSORS_SUBFEATURE_CURR_AVERAGE, + SENSORS_SUBFEATURE_CURR_LOWEST, + SENSORS_SUBFEATURE_CURR_HIGHEST, + SENSORS_SUBFEATURE_CURR_ALARM = (SENSORS_FEATURE_CURR << 8) | 0x80, + SENSORS_SUBFEATURE_CURR_MIN_ALARM, + SENSORS_SUBFEATURE_CURR_MAX_ALARM, + SENSORS_SUBFEATURE_CURR_BEEP, + SENSORS_SUBFEATURE_CURR_LCRIT_ALARM, + SENSORS_SUBFEATURE_CURR_CRIT_ALARM, + + SENSORS_SUBFEATURE_HUMIDITY_INPUT = SENSORS_FEATURE_HUMIDITY << 8, + + SENSORS_SUBFEATURE_VID = SENSORS_FEATURE_VID << 8, + + SENSORS_SUBFEATURE_INTRUSION_ALARM = SENSORS_FEATURE_INTRUSION << 8, + SENSORS_SUBFEATURE_INTRUSION_BEEP, + + SENSORS_SUBFEATURE_BEEP_ENABLE = SENSORS_FEATURE_BEEP_ENABLE << 8, + + SENSORS_SUBFEATURE_UNKNOWN = INT_MAX, +} sensors_subfeature_type; + +/* Data about a single chip feature (or category leader) */ +struct sensors_feature { + char *name; + int number; + sensors_feature_type type; + /* Members below are for libsensors internal use only */ + int first_subfeature; + int padding1; +}; + +/* Data about a single chip subfeature: + name is the string name used to refer to this subfeature (in config files) + number is the internal subfeature number, used in many functions to refer + to this subfeature + type is the subfeature type + mapping is the number of a main feature this subfeature belongs to + (for example subfeatures fan1_input, fan1_min, fan1_div and fan1_alarm + are mapped to main feature fan1) + flags is a bitfield, its value is a combination of SENSORS_MODE_R (readable), + SENSORS_MODE_W (writable) and SENSORS_COMPUTE_MAPPING (affected by the + computation rules of the main feature) */ +typedef struct sensors_subfeature { + char *name; + int number; + sensors_subfeature_type type; + int mapping; + unsigned int flags; +} sensors_subfeature; + +/* This returns all main features of a specific chip. nr is an internally + used variable. Set it to zero to start at the begin of the list. If no + more features are found NULL is returned. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_feature * +sensors_get_features(const sensors_chip_name *name, int *nr); + +/* This returns all subfeatures of a given main feature. nr is an internally + used variable. Set it to zero to start at the begin of the list. If no + more features are found NULL is returned. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_subfeature * +sensors_get_all_subfeatures(const sensors_chip_name *name, + const sensors_feature *feature, int *nr); + +/* This returns the subfeature of the given type for a given main feature, + if it exists, NULL otherwise. + Do not try to change the returned structure; you will corrupt internal + data structures. */ +const sensors_subfeature * +sensors_get_subfeature(const sensors_chip_name *name, + const sensors_feature *feature, + sensors_subfeature_type type); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* def LIB_SENSORS_ERROR_H */ diff --git a/tools/gator/daemon/libsensors/sysfs.c b/tools/gator/daemon/libsensors/sysfs.c new file mode 100644 index 000000000000..2b494c9975b4 --- /dev/null +++ b/tools/gator/daemon/libsensors/sysfs.c @@ -0,0 +1,926 @@ +/* + sysfs.c - Part of libsensors, a library for reading Linux sensor data + Copyright (c) 2005 Mark M. Hoffman + Copyright (C) 2007-2010 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU 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. +*/ + +/*** This file modified by ARM on Jan 23, 2013 to improve performance by substituting calls to fread() with calls to read() and to read non-scaled values. ***/ + +/* this define needed for strndup() */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "data.h" +#include "error.h" +#include "access.h" +#include "general.h" +#include "sysfs.h" + + +/****************************************************************************/ + +#define ATTR_MAX 128 +#define SYSFS_MAGIC 0x62656572 + +int sensors_sysfs_no_scaling; + +/* + * Read an attribute from sysfs + * Returns a pointer to a freshly allocated string; free it yourself. + * If the file doesn't exist or can't be read, NULL is returned. + */ +static char *sysfs_read_attr(const char *device, const char *attr) +{ + char path[NAME_MAX]; + char buf[ATTR_MAX], *p; + FILE *f; + + snprintf(path, NAME_MAX, "%s/%s", device, attr); + + if (!(f = fopen(path, "r"))) + return NULL; + p = fgets(buf, ATTR_MAX, f); + fclose(f); + if (!p) + return NULL; + + /* Last byte is a '\n'; chop that off */ + p = strndup(buf, strlen(buf) - 1); + if (!p) + sensors_fatal_error(__func__, "Out of memory"); + return p; +} + +/* + * Call an arbitrary function for each class device of the given class + * Returns 0 on success (all calls returned 0), a positive errno for + * local errors, or a negative error value if any call fails. + */ +static int sysfs_foreach_classdev(const char *class_name, + int (*func)(const char *, const char *)) +{ + char path[NAME_MAX]; + int path_off, ret; + DIR *dir; + struct dirent *ent; + + path_off = snprintf(path, NAME_MAX, "%s/class/%s", + sensors_sysfs_mount, class_name); + if (!(dir = opendir(path))) + return errno; + + ret = 0; + while (!ret && (ent = readdir(dir))) { + if (ent->d_name[0] == '.') /* skip hidden entries */ + continue; + + snprintf(path + path_off, NAME_MAX - path_off, "/%s", + ent->d_name); + ret = func(path, ent->d_name); + } + + closedir(dir); + return ret; +} + +/* + * Call an arbitrary function for each device of the given bus type + * Returns 0 on success (all calls returned 0), a positive errno for + * local errors, or a negative error value if any call fails. + */ +static int sysfs_foreach_busdev(const char *bus_type, + int (*func)(const char *, const char *)) +{ + char path[NAME_MAX]; + int path_off, ret; + DIR *dir; + struct dirent *ent; + + path_off = snprintf(path, NAME_MAX, "%s/bus/%s/devices", + sensors_sysfs_mount, bus_type); + if (!(dir = opendir(path))) + return errno; + + ret = 0; + while (!ret && (ent = readdir(dir))) { + if (ent->d_name[0] == '.') /* skip hidden entries */ + continue; + + snprintf(path + path_off, NAME_MAX - path_off, "/%s", + ent->d_name); + ret = func(path, ent->d_name); + } + + closedir(dir); + return ret; +} + +/****************************************************************************/ + +char sensors_sysfs_mount[NAME_MAX]; + +#define MAX_MAIN_SENSOR_TYPES (SENSORS_FEATURE_MAX_MAIN - SENSORS_FEATURE_IN) +#define MAX_OTHER_SENSOR_TYPES (SENSORS_FEATURE_MAX_OTHER - SENSORS_FEATURE_VID) +#define MAX_SENSORS_PER_TYPE 24 +/* max_subfeatures is now computed dynamically */ +#define FEATURE_SIZE (max_subfeatures * 2) +#define FEATURE_TYPE_SIZE (MAX_SENSORS_PER_TYPE * FEATURE_SIZE) + +/* + * Room for all 7 main types (in, fan, temp, power, energy, current, humidity) + * and 2 other types (VID, intrusion) with all their subfeatures + misc features + */ +#define SUB_OFFSET_OTHER (MAX_MAIN_SENSOR_TYPES * FEATURE_TYPE_SIZE) +#define SUB_OFFSET_MISC (SUB_OFFSET_OTHER + \ + MAX_OTHER_SENSOR_TYPES * FEATURE_TYPE_SIZE) +#define ALL_POSSIBLE_SUBFEATURES (SUB_OFFSET_MISC + 1) + +static +int get_type_scaling(sensors_subfeature_type type) +{ + /* Multipliers for subfeatures */ + switch (type & 0xFF80) { + case SENSORS_SUBFEATURE_IN_INPUT: + case SENSORS_SUBFEATURE_TEMP_INPUT: + case SENSORS_SUBFEATURE_CURR_INPUT: + case SENSORS_SUBFEATURE_HUMIDITY_INPUT: + return 1000; + case SENSORS_SUBFEATURE_FAN_INPUT: + return 1; + case SENSORS_SUBFEATURE_POWER_AVERAGE: + case SENSORS_SUBFEATURE_ENERGY_INPUT: + return 1000000; + } + + /* Multipliers for second class subfeatures + that need their own multiplier */ + switch (type) { + case SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL: + case SENSORS_SUBFEATURE_VID: + case SENSORS_SUBFEATURE_TEMP_OFFSET: + return 1000; + default: + return 1; + } +} + +static +char *get_feature_name(sensors_feature_type ftype, char *sfname) +{ + char *name, *underscore; + + switch (ftype) { + case SENSORS_FEATURE_IN: + case SENSORS_FEATURE_FAN: + case SENSORS_FEATURE_TEMP: + case SENSORS_FEATURE_POWER: + case SENSORS_FEATURE_ENERGY: + case SENSORS_FEATURE_CURR: + case SENSORS_FEATURE_HUMIDITY: + case SENSORS_FEATURE_INTRUSION: + underscore = strchr(sfname, '_'); + name = strndup(sfname, underscore - sfname); + if (!name) + sensors_fatal_error(__func__, "Out of memory"); + + break; + default: + name = strdup(sfname); + if (!name) + sensors_fatal_error(__func__, "Out of memory"); + } + + return name; +} + +/* Static mappings for use by sensors_subfeature_get_type() */ +struct subfeature_type_match +{ + const char *name; + sensors_subfeature_type type; +}; + +struct feature_type_match +{ + const char *name; + const struct subfeature_type_match *submatches; +}; + +static const struct subfeature_type_match temp_matches[] = { + { "input", SENSORS_SUBFEATURE_TEMP_INPUT }, + { "max", SENSORS_SUBFEATURE_TEMP_MAX }, + { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST }, + { "min", SENSORS_SUBFEATURE_TEMP_MIN }, + { "crit", SENSORS_SUBFEATURE_TEMP_CRIT }, + { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST }, + { "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT }, + { "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY }, + { "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST }, + { "lowest", SENSORS_SUBFEATURE_TEMP_LOWEST }, + { "highest", SENSORS_SUBFEATURE_TEMP_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_TEMP_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_TEMP_MAX_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM }, + { "emergency_alarm", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM }, + { "fault", SENSORS_SUBFEATURE_TEMP_FAULT }, + { "type", SENSORS_SUBFEATURE_TEMP_TYPE }, + { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET }, + { "beep", SENSORS_SUBFEATURE_TEMP_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match in_matches[] = { + { "input", SENSORS_SUBFEATURE_IN_INPUT }, + { "min", SENSORS_SUBFEATURE_IN_MIN }, + { "max", SENSORS_SUBFEATURE_IN_MAX }, + { "lcrit", SENSORS_SUBFEATURE_IN_LCRIT }, + { "crit", SENSORS_SUBFEATURE_IN_CRIT }, + { "average", SENSORS_SUBFEATURE_IN_AVERAGE }, + { "lowest", SENSORS_SUBFEATURE_IN_LOWEST }, + { "highest", SENSORS_SUBFEATURE_IN_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_IN_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_IN_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_IN_MAX_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_IN_LCRIT_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_IN_CRIT_ALARM }, + { "beep", SENSORS_SUBFEATURE_IN_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match fan_matches[] = { + { "input", SENSORS_SUBFEATURE_FAN_INPUT }, + { "min", SENSORS_SUBFEATURE_FAN_MIN }, + { "max", SENSORS_SUBFEATURE_FAN_MAX }, + { "div", SENSORS_SUBFEATURE_FAN_DIV }, + { "pulses", SENSORS_SUBFEATURE_FAN_PULSES }, + { "alarm", SENSORS_SUBFEATURE_FAN_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_FAN_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_FAN_MAX_ALARM }, + { "fault", SENSORS_SUBFEATURE_FAN_FAULT }, + { "beep", SENSORS_SUBFEATURE_FAN_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match power_matches[] = { + { "average", SENSORS_SUBFEATURE_POWER_AVERAGE }, + { "average_highest", SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST }, + { "average_lowest", SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST }, + { "input", SENSORS_SUBFEATURE_POWER_INPUT }, + { "input_highest", SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST }, + { "input_lowest", SENSORS_SUBFEATURE_POWER_INPUT_LOWEST }, + { "cap", SENSORS_SUBFEATURE_POWER_CAP }, + { "cap_hyst", SENSORS_SUBFEATURE_POWER_CAP_HYST }, + { "cap_alarm", SENSORS_SUBFEATURE_POWER_CAP_ALARM }, + { "alarm", SENSORS_SUBFEATURE_POWER_ALARM }, + { "max", SENSORS_SUBFEATURE_POWER_MAX }, + { "max_alarm", SENSORS_SUBFEATURE_POWER_MAX_ALARM }, + { "crit", SENSORS_SUBFEATURE_POWER_CRIT }, + { "crit_alarm", SENSORS_SUBFEATURE_POWER_CRIT_ALARM }, + { "average_interval", SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL }, + { NULL, 0 } +}; + +static const struct subfeature_type_match energy_matches[] = { + { "input", SENSORS_SUBFEATURE_ENERGY_INPUT }, + { NULL, 0 } +}; + +static const struct subfeature_type_match curr_matches[] = { + { "input", SENSORS_SUBFEATURE_CURR_INPUT }, + { "min", SENSORS_SUBFEATURE_CURR_MIN }, + { "max", SENSORS_SUBFEATURE_CURR_MAX }, + { "lcrit", SENSORS_SUBFEATURE_CURR_LCRIT }, + { "crit", SENSORS_SUBFEATURE_CURR_CRIT }, + { "average", SENSORS_SUBFEATURE_CURR_AVERAGE }, + { "lowest", SENSORS_SUBFEATURE_CURR_LOWEST }, + { "highest", SENSORS_SUBFEATURE_CURR_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_CURR_ALARM }, + { "min_alarm", SENSORS_SUBFEATURE_CURR_MIN_ALARM }, + { "max_alarm", SENSORS_SUBFEATURE_CURR_MAX_ALARM }, + { "lcrit_alarm", SENSORS_SUBFEATURE_CURR_LCRIT_ALARM }, + { "crit_alarm", SENSORS_SUBFEATURE_CURR_CRIT_ALARM }, + { "beep", SENSORS_SUBFEATURE_CURR_BEEP }, + { NULL, 0 } +}; + +static const struct subfeature_type_match humidity_matches[] = { + { "input", SENSORS_SUBFEATURE_HUMIDITY_INPUT }, + { NULL, 0 } +}; + +static const struct subfeature_type_match cpu_matches[] = { + { "vid", SENSORS_SUBFEATURE_VID }, + { NULL, 0 } +}; + +static const struct subfeature_type_match intrusion_matches[] = { + { "alarm", SENSORS_SUBFEATURE_INTRUSION_ALARM }, + { "beep", SENSORS_SUBFEATURE_INTRUSION_BEEP }, + { NULL, 0 } +}; +static struct feature_type_match matches[] = { + { "temp%d%c", temp_matches }, + { "in%d%c", in_matches }, + { "fan%d%c", fan_matches }, + { "cpu%d%c", cpu_matches }, + { "power%d%c", power_matches }, + { "curr%d%c", curr_matches }, + { "energy%d%c", energy_matches }, + { "intrusion%d%c", intrusion_matches }, + { "humidity%d%c", humidity_matches }, +}; + +/* Return the subfeature type and channel number based on the subfeature + name */ +static +sensors_subfeature_type sensors_subfeature_get_type(const char *name, int *nr) +{ + char c; + int i, count; + const struct subfeature_type_match *submatches; + + /* Special case */ + if (!strcmp(name, "beep_enable")) { + *nr = 0; + return SENSORS_SUBFEATURE_BEEP_ENABLE; + } + + for (i = 0; i < ARRAY_SIZE(matches); i++) + if ((count = sscanf(name, matches[i].name, nr, &c))) + break; + + if (i == ARRAY_SIZE(matches) || count != 2 || c != '_') + return SENSORS_SUBFEATURE_UNKNOWN; /* no match */ + + submatches = matches[i].submatches; + name = strchr(name + 3, '_') + 1; + for (i = 0; submatches[i].name != NULL; i++) + if (!strcmp(name, submatches[i].name)) + return submatches[i].type; + + return SENSORS_SUBFEATURE_UNKNOWN; +} + +static int sensors_compute_max(void) +{ + int i, j, max, offset; + const struct subfeature_type_match *submatches; + sensors_feature_type ftype; + + max = 0; + for (i = 0; i < ARRAY_SIZE(matches); i++) { + submatches = matches[i].submatches; + for (j = 0; submatches[j].name != NULL; j++) { + ftype = submatches[j].type >> 8; + + if (ftype < SENSORS_FEATURE_VID) { + offset = submatches[j].type & 0x7F; + if (offset >= max) + max = offset + 1; + } else { + offset = submatches[j].type & 0xFF; + if (offset >= max * 2) + max = ((offset + 1) + 1) / 2; + } + } + } + + return max; +} + +static int sensors_get_attr_mode(const char *device, const char *attr) +{ + char path[NAME_MAX]; + struct stat st; + int mode = 0; + + snprintf(path, NAME_MAX, "%s/%s", device, attr); + if (!stat(path, &st)) { + if (st.st_mode & S_IRUSR) + mode |= SENSORS_MODE_R; + if (st.st_mode & S_IWUSR) + mode |= SENSORS_MODE_W; + } + return mode; +} + +static int sensors_read_dynamic_chip(sensors_chip_features *chip, + const char *dev_path) +{ + int i, fnum = 0, sfnum = 0, prev_slot; + static int max_subfeatures; + DIR *dir; + struct dirent *ent; + sensors_subfeature *all_subfeatures; + sensors_subfeature *dyn_subfeatures; + sensors_feature *dyn_features; + sensors_feature_type ftype; + sensors_subfeature_type sftype; + + if (!(dir = opendir(dev_path))) + return -errno; + + /* Dynamically figure out the max number of subfeatures */ + if (!max_subfeatures) + max_subfeatures = sensors_compute_max(); + + /* We use a large sparse table at first to store all found + subfeatures, so that we can store them sorted at type and index + and then later create a dense sorted table. */ + all_subfeatures = calloc(ALL_POSSIBLE_SUBFEATURES, + sizeof(sensors_subfeature)); + if (!all_subfeatures) + sensors_fatal_error(__func__, "Out of memory"); + + while ((ent = readdir(dir))) { + char *name; + int nr; + + /* Skip directories and symlinks */ + if (ent->d_type != DT_REG) + continue; + + name = ent->d_name; + + sftype = sensors_subfeature_get_type(name, &nr); + if (sftype == SENSORS_SUBFEATURE_UNKNOWN) + continue; + ftype = sftype >> 8; + + /* Adjust the channel number */ + switch (ftype) { + case SENSORS_FEATURE_FAN: + case SENSORS_FEATURE_TEMP: + case SENSORS_FEATURE_POWER: + case SENSORS_FEATURE_ENERGY: + case SENSORS_FEATURE_CURR: + case SENSORS_FEATURE_HUMIDITY: + nr--; + break; + default: + break; + } + + if (nr < 0 || nr >= MAX_SENSORS_PER_TYPE) { + /* More sensors of one type than MAX_SENSORS_PER_TYPE, + we have to ignore it */ +#ifdef DEBUG + sensors_fatal_error(__func__, + "Increase MAX_SENSORS_PER_TYPE!"); +#endif + continue; + } + + /* "calculate" a place to store the subfeature in our sparse, + sorted table */ + switch (ftype) { + case SENSORS_FEATURE_VID: + case SENSORS_FEATURE_INTRUSION: + i = SUB_OFFSET_OTHER + + (ftype - SENSORS_FEATURE_VID) * FEATURE_TYPE_SIZE + + nr * FEATURE_SIZE + (sftype & 0xFF); + break; + case SENSORS_FEATURE_BEEP_ENABLE: + i = SUB_OFFSET_MISC + + (ftype - SENSORS_FEATURE_BEEP_ENABLE); + break; + default: + i = ftype * FEATURE_TYPE_SIZE + + nr * FEATURE_SIZE + + ((sftype & 0x80) >> 7) * max_subfeatures + + (sftype & 0x7F); + } + + if (all_subfeatures[i].name) { +#ifdef DEBUG + sensors_fatal_error(__func__, "Duplicate subfeature"); +#endif + continue; + } + + /* fill in the subfeature members */ + all_subfeatures[i].type = sftype; + all_subfeatures[i].name = strdup(name); + if (!all_subfeatures[i].name) + sensors_fatal_error(__func__, "Out of memory"); + + /* Other and misc subfeatures are never scaled */ + if (sftype < SENSORS_SUBFEATURE_VID && !(sftype & 0x80)) + all_subfeatures[i].flags |= SENSORS_COMPUTE_MAPPING; + all_subfeatures[i].flags |= sensors_get_attr_mode(dev_path, name); + + sfnum++; + } + closedir(dir); + + if (!sfnum) { /* No subfeature */ + chip->subfeature = NULL; + goto exit_free; + } + + /* How many main features? */ + prev_slot = -1; + for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) { + if (!all_subfeatures[i].name) + continue; + + if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) { + fnum++; + prev_slot = i / FEATURE_SIZE; + } + } + + dyn_subfeatures = calloc(sfnum, sizeof(sensors_subfeature)); + dyn_features = calloc(fnum, sizeof(sensors_feature)); + if (!dyn_subfeatures || !dyn_features) + sensors_fatal_error(__func__, "Out of memory"); + + /* Copy from the sparse array to the compact array */ + sfnum = 0; + fnum = -1; + prev_slot = -1; + for (i = 0; i < ALL_POSSIBLE_SUBFEATURES; i++) { + if (!all_subfeatures[i].name) + continue; + + /* New main feature? */ + if (i >= SUB_OFFSET_MISC || i / FEATURE_SIZE != prev_slot) { + ftype = all_subfeatures[i].type >> 8; + fnum++; + prev_slot = i / FEATURE_SIZE; + + dyn_features[fnum].name = get_feature_name(ftype, + all_subfeatures[i].name); + dyn_features[fnum].number = fnum; + dyn_features[fnum].first_subfeature = sfnum; + dyn_features[fnum].type = ftype; + } + + dyn_subfeatures[sfnum] = all_subfeatures[i]; + dyn_subfeatures[sfnum].number = sfnum; + /* Back to the feature */ + dyn_subfeatures[sfnum].mapping = fnum; + + sfnum++; + } + + chip->subfeature = dyn_subfeatures; + chip->subfeature_count = sfnum; + chip->feature = dyn_features; + chip->feature_count = ++fnum; + +exit_free: + free(all_subfeatures); + return 0; +} + +/* returns !0 if sysfs filesystem was found, 0 otherwise */ +int sensors_init_sysfs(void) +{ + struct statfs statfsbuf; + + snprintf(sensors_sysfs_mount, NAME_MAX, "%s", "/sys"); + if (statfs(sensors_sysfs_mount, &statfsbuf) < 0 + || statfsbuf.f_type != SYSFS_MAGIC) + return 0; + + return 1; +} + +/* returns: number of devices added (0 or 1) if successful, <0 otherwise */ +static int sensors_read_one_sysfs_chip(const char *dev_path, + const char *dev_name, + const char *hwmon_path) +{ + int domain, bus, slot, fn, vendor, product, id; + int err = -SENSORS_ERR_KERNEL; + char *bus_attr; + char bus_path[NAME_MAX]; + char linkpath[NAME_MAX]; + char subsys_path[NAME_MAX], *subsys; + int sub_len; + sensors_chip_features entry; + + /* ignore any device without name attribute */ + if (!(entry.chip.prefix = sysfs_read_attr(hwmon_path, "name"))) + return 0; + + entry.chip.path = strdup(hwmon_path); + if (!entry.chip.path) + sensors_fatal_error(__func__, "Out of memory"); + + if (dev_path == NULL) { + /* Virtual device */ + entry.chip.bus.type = SENSORS_BUS_TYPE_VIRTUAL; + entry.chip.bus.nr = 0; + /* For now we assume that virtual devices are unique */ + entry.chip.addr = 0; + goto done; + } + + /* Find bus type */ + snprintf(linkpath, NAME_MAX, "%s/subsystem", dev_path); + sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1); + if (sub_len < 0 && errno == ENOENT) { + /* Fallback to "bus" link for kernels <= 2.6.17 */ + snprintf(linkpath, NAME_MAX, "%s/bus", dev_path); + sub_len = readlink(linkpath, subsys_path, NAME_MAX - 1); + } + if (sub_len < 0) { + /* Older kernels (<= 2.6.11) have neither the subsystem + symlink nor the bus symlink */ + if (errno == ENOENT) + subsys = NULL; + else + goto exit_free; + } else { + subsys_path[sub_len] = '\0'; + subsys = strrchr(subsys_path, '/') + 1; + } + + if ((!subsys || !strcmp(subsys, "i2c")) && + sscanf(dev_name, "%hd-%x", &entry.chip.bus.nr, + &entry.chip.addr) == 2) { + /* find out if legacy ISA or not */ + if (entry.chip.bus.nr == 9191) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } else { + entry.chip.bus.type = SENSORS_BUS_TYPE_I2C; + snprintf(bus_path, sizeof(bus_path), + "%s/class/i2c-adapter/i2c-%d/device", + sensors_sysfs_mount, entry.chip.bus.nr); + + if ((bus_attr = sysfs_read_attr(bus_path, "name"))) { + if (!strncmp(bus_attr, "ISA ", 4)) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } + + free(bus_attr); + } + } + } else + if ((!subsys || !strcmp(subsys, "spi")) && + sscanf(dev_name, "spi%hd.%d", &entry.chip.bus.nr, + &entry.chip.addr) == 2) { + /* SPI */ + entry.chip.bus.type = SENSORS_BUS_TYPE_SPI; + } else + if ((!subsys || !strcmp(subsys, "pci")) && + sscanf(dev_name, "%x:%x:%x.%x", &domain, &bus, &slot, &fn) == 4) { + /* PCI */ + entry.chip.addr = (domain << 16) + (bus << 8) + (slot << 3) + fn; + entry.chip.bus.type = SENSORS_BUS_TYPE_PCI; + entry.chip.bus.nr = 0; + } else + if ((!subsys || !strcmp(subsys, "platform") || + !strcmp(subsys, "of_platform"))) { + /* must be new ISA (platform driver) */ + if (sscanf(dev_name, "%*[a-z0-9_].%d", &entry.chip.addr) != 1) + entry.chip.addr = 0; + entry.chip.bus.type = SENSORS_BUS_TYPE_ISA; + entry.chip.bus.nr = 0; + } else if (subsys && !strcmp(subsys, "acpi")) { + entry.chip.bus.type = SENSORS_BUS_TYPE_ACPI; + /* For now we assume that acpi devices are unique */ + entry.chip.bus.nr = 0; + entry.chip.addr = 0; + } else + if (subsys && !strcmp(subsys, "hid") && + sscanf(dev_name, "%x:%x:%x.%x", &bus, &vendor, &product, &id) == 4) { + entry.chip.bus.type = SENSORS_BUS_TYPE_HID; + /* As of kernel 2.6.32, the hid device names don't look good */ + entry.chip.bus.nr = bus; + entry.chip.addr = id; + } else { + /* Ignore unknown device */ + err = 0; + goto exit_free; + } + +done: + if (sensors_read_dynamic_chip(&entry, hwmon_path) < 0) + goto exit_free; + if (!entry.subfeature) { /* No subfeature, discard chip */ + err = 0; + goto exit_free; + } + sensors_add_proc_chips(&entry); + + return 1; + +exit_free: + free(entry.chip.prefix); + free(entry.chip.path); + return err; +} + +static int sensors_add_hwmon_device_compat(const char *path, + const char *dev_name) +{ + int err; + + err = sensors_read_one_sysfs_chip(path, dev_name, path); + if (err < 0) + return err; + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +static int sensors_read_sysfs_chips_compat(void) +{ + int ret; + + ret = sysfs_foreach_busdev("i2c", sensors_add_hwmon_device_compat); + if (ret && ret != ENOENT) + return -SENSORS_ERR_KERNEL; + + return 0; +} + +static int sensors_add_hwmon_device(const char *path, const char *classdev) +{ + char linkpath[NAME_MAX]; + char device[NAME_MAX], *device_p; + int dev_len, err; + (void)classdev; /* hide warning */ + + snprintf(linkpath, NAME_MAX, "%s/device", path); + dev_len = readlink(linkpath, device, NAME_MAX - 1); + if (dev_len < 0) { + /* No device link? Treat as virtual */ + err = sensors_read_one_sysfs_chip(NULL, NULL, path); + } else { + device[dev_len] = '\0'; + device_p = strrchr(device, '/') + 1; + + /* The attributes we want might be those of the hwmon class + device, or those of the device itself. */ + err = sensors_read_one_sysfs_chip(linkpath, device_p, path); + if (err == 0) + err = sensors_read_one_sysfs_chip(linkpath, device_p, + linkpath); + } + if (err < 0) + return err; + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +int sensors_read_sysfs_chips(void) +{ + int ret; + + ret = sysfs_foreach_classdev("hwmon", sensors_add_hwmon_device); + if (ret == ENOENT) { + /* compatibility function for kernel 2.6.n where n <= 13 */ + return sensors_read_sysfs_chips_compat(); + } + + if (ret > 0) + ret = -SENSORS_ERR_KERNEL; + return ret; +} + +/* returns 0 if successful, !0 otherwise */ +static int sensors_add_i2c_bus(const char *path, const char *classdev) +{ + sensors_bus entry; + + if (sscanf(classdev, "i2c-%hd", &entry.bus.nr) != 1 || + entry.bus.nr == 9191) /* legacy ISA */ + return 0; + entry.bus.type = SENSORS_BUS_TYPE_I2C; + + /* Get the adapter name from the classdev "name" attribute + * (Linux 2.6.20 and later). If it fails, fall back to + * the device "name" attribute (for older kernels). */ + entry.adapter = sysfs_read_attr(path, "name"); + if (!entry.adapter) + entry.adapter = sysfs_read_attr(path, "device/name"); + if (entry.adapter) + sensors_add_proc_bus(&entry); + + return 0; +} + +/* returns 0 if successful, !0 otherwise */ +int sensors_read_sysfs_bus(void) +{ + int ret; + + ret = sysfs_foreach_classdev("i2c-adapter", sensors_add_i2c_bus); + if (ret == ENOENT) + ret = sysfs_foreach_busdev("i2c", sensors_add_i2c_bus); + if (ret && ret != ENOENT) + return -SENSORS_ERR_KERNEL; + + return 0; +} + +int sensors_read_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double *value) +{ + char n[NAME_MAX]; + int f; + + snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name); + if ((f = open(n, O_RDONLY)) != -1) { + int res, err = 0; + char buf[512]; + int count; + + errno = 0; + if ((count = read(f, buf, sizeof(buf) - 1)) == -1) { + if (errno == EIO) + err = -SENSORS_ERR_IO; + else + err = -SENSORS_ERR_ACCESS_R; + } else { + buf[count] = '\0'; + errno = 0; + res = sscanf(buf, "%lf", value); + if (res == EOF && errno == EIO) + err = -SENSORS_ERR_IO; + else if (res != 1) + err = -SENSORS_ERR_ACCESS_R; + } + res = close(f); + if (err) + return err; + + if (res != 0) { + if (errno == EIO) + return -SENSORS_ERR_IO; + else + return -SENSORS_ERR_ACCESS_R; + } + if (!sensors_sysfs_no_scaling) + *value /= get_type_scaling(subfeature->type); + } else + return -SENSORS_ERR_KERNEL; + + return 0; +} + +int sensors_write_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double value) +{ + char n[NAME_MAX]; + FILE *f; + + snprintf(n, NAME_MAX, "%s/%s", name->path, subfeature->name); + if ((f = fopen(n, "w"))) { + int res, err = 0; + + if (!sensors_sysfs_no_scaling) + value *= get_type_scaling(subfeature->type); + res = fprintf(f, "%d", (int) value); + if (res == -EIO) + err = -SENSORS_ERR_IO; + else if (res < 0) + err = -SENSORS_ERR_ACCESS_W; + res = fclose(f); + if (err) + return err; + + if (res == EOF) { + if (errno == EIO) + return -SENSORS_ERR_IO; + else + return -SENSORS_ERR_ACCESS_W; + } + } else + return -SENSORS_ERR_KERNEL; + + return 0; +} diff --git a/tools/gator/daemon/libsensors/sysfs.h b/tools/gator/daemon/libsensors/sysfs.h new file mode 100644 index 000000000000..38584afd028e --- /dev/null +++ b/tools/gator/daemon/libsensors/sysfs.h @@ -0,0 +1,43 @@ +/* + sysfs.h - part of libsensors, a library for reading Linux sensor data + Copyright (C) Mark M. Hoffman + Copyright (C) 2007 Jean Delvare + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA. +*/ + +#ifndef SENSORS_LIB_SYSFS_H +#define SENSORS_LIB_SYSFS_H + +extern char sensors_sysfs_mount[]; + +int sensors_init_sysfs(void); + +int sensors_read_sysfs_chips(void); + +int sensors_read_sysfs_bus(void); + +/* Read a value out of a sysfs attribute file */ +int sensors_read_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double *value); + +/* Write a value to a sysfs attribute file */ +int sensors_write_sysfs_attr(const sensors_chip_name *name, + const sensors_subfeature *subfeature, + double value); + +#endif /* !SENSORS_LIB_SYSFS_H */ diff --git a/tools/gator/daemon/libsensors/version.h b/tools/gator/daemon/libsensors/version.h new file mode 100644 index 000000000000..76ceb08c29d0 --- /dev/null +++ b/tools/gator/daemon/libsensors/version.h @@ -0,0 +1 @@ +#define LM_VERSION "3.3.2" diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp new file mode 100644 index 000000000000..2998c7012221 --- /dev/null +++ b/tools/gator/daemon/main.cpp @@ -0,0 +1,547 @@ +/** + * Copyright (C) ARM Limited 2010-2014. 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 + +#include "Child.h" +#include "EventsXML.h" +#include "KMod.h" +#include "Logging.h" +#include "Monitor.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "SessionData.h" + +#define DEBUG false + +extern Child* child; +static int shutdownFilesystem(); +static pthread_mutex_t numSessions_mutex; +static OlyServerSocket* sock = NULL; +static Monitor monitor; +static int numSessions = 0; +static bool driverRunningAtStart = false; +static bool driverMountedAtStart = false; + +struct cmdline_t { + int port; + char* module; +}; + +#define DEFAULT_PORT 8080 + +void cleanUp() { + if (shutdownFilesystem() == -1) { + logg->logMessage("Error shutting down gator filesystem"); + } + delete sock; + delete util; + delete logg; +} + +// CTRL C Signal Handler +static 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 +static void child_exit(int) { + 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); + } +} + +static const int UDP_ANS_PORT = 30000; +static const int UDP_REQ_PORT = 30001; + +typedef struct { + char rviHeader[8]; + uint32_t messageID; + uint8_t ethernetAddress[8]; + uint32_t ethernetType; + uint32_t dhcp; + char dhcpName[40]; + uint32_t ipAddress; + uint32_t defaultGateway; + uint32_t subnetMask; + uint32_t activeConnections; +} RVIConfigureInfo; + +static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; + +class UdpListener { +public: + UdpListener() : mDstAns(), mReq(-1), mAns(-1) {} + + void setup(int port) { + mReq = udpPort(UDP_REQ_PORT); + mAns = udpPort(UDP_ANS_PORT); + + // Format the answer buffer + memset(&mDstAns, 0, sizeof(mDstAns)); + memcpy(mDstAns.rviHeader, "STR_ANS ", sizeof(mDstAns.rviHeader)); + if (gethostname(mDstAns.dhcpName, sizeof(mDstAns.dhcpName) - 1) != 0) { + logg->logError(__FILE__, __LINE__, "gethostname failed"); + handleException(); + } + // Subvert the defaultGateway field for the port number + if (port != DEFAULT_PORT) { + mDstAns.defaultGateway = port; + } + // Subvert the subnetMask field for the protocol version + mDstAns.subnetMask = PROTOCOL_VERSION; + } + + int getReq() const { + return mReq; + } + + void handle() { + char buf[128]; + struct sockaddr_in6 sockaddr; + socklen_t addrlen; + int read; + addrlen = sizeof(sockaddr); + read = recvfrom(mReq, &buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr, &addrlen); + if (read < 0) { + logg->logError(__FILE__, __LINE__, "recvfrom failed"); + handleException(); + } else if ((read == 12) && (memcmp(buf, DST_REQ, sizeof(DST_REQ)) == 0)) { + if (sendto(mAns, &mDstAns, sizeof(mDstAns), 0, (struct sockaddr *)&sockaddr, addrlen) != sizeof(mDstAns)) { + logg->logError(__FILE__, __LINE__, "sendto failed"); + handleException(); + } + } + } + + void close() { + ::close(mReq); + ::close(mAns); + } + +private: + int udpPort(int port) { + int s; + struct sockaddr_in6 sockaddr; + int on; + int family = AF_INET6; + + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + family = AF_INET; + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + logg->logError(__FILE__, __LINE__, "socket failed"); + handleException(); + } + } + + on = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { + logg->logError(__FILE__, __LINE__, "setsockopt failed"); + handleException(); + } + + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = family; + sockaddr.sin6_port = htons(port); + sockaddr.sin6_addr = in6addr_any; + if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "socket failed"); + handleException(); + } + + return s; + } + + RVIConfigureInfo mDstAns; + int mReq; + int mAns; +}; + +static UdpListener udpListener; + +// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted +static 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; + } +} + +static bool init_module (const char * const location) { + bool ret(false); + const int fd = open(location, O_RDONLY); + if (fd >= 0) { + struct stat st; + if (fstat(fd, &st) == 0) { + void * const p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (p != MAP_FAILED) { + if (syscall(__NR_init_module, p, (unsigned long)st.st_size, "") == 0) { + ret = true; + } + munmap(p, st.st_size); + } + } + close(fd); + } + + return ret; +} + +static bool setupFilesystem(char* module) { + 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(); + } + } + + const int 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) { + if (module == NULL) { + // The gator kernel is not already loaded and unable to locate gator.ko in the default location + return false; + } else { + // gator location specified on the command line but it was not found + logg->logError(__FILE__, __LINE__, "gator module not found at %s", location); + handleException(); + } + } + + // Load driver + bool success = init_module(location); + if (!success) { + logg->logMessage("init_module failed, trying insmod"); + 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 true; +} + +static int shutdownFilesystem() { + if (driverMountedAtStart == false) { + umount("/dev/gator"); + } + if (driverRunningAtStart == false) { + if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) { + logg->logMessage("delete_module failed, trying rmmod"); + if (system("rmmod gator >/dev/null 2>&1") != 0) { + return -1; + } + } + } + + return 0; // success +} + +static struct cmdline_t parseCommandLine(int argc, char** argv) { + struct cmdline_t cmdline; + cmdline.port = DEFAULT_PORT; + cmdline.module = NULL; + char version_string[256]; // arbitrary length to hold the version information + int c; + + // build the version string + if (PROTOCOL_VERSION < PROTOCOL_DEV) { + snprintf(version_string, sizeof(version_string), "Streamline gatord version %d (DS-5 v5.%d)", PROTOCOL_VERSION, PROTOCOL_VERSION); + } else { + snprintf(version_string, sizeof(version_string), "Streamline gatord development version %d", PROTOCOL_VERSION); + } + + while ((c = getopt(argc, argv, "hvp:s:c:e:m:o:")) != -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 'o': + gSessionData->mTargetPath = optarg; + break; + case 'h': + case '?': + logg->logError(__FILE__, __LINE__, + "%s. All parameters are optional:\n" + "-c config_xml path and filename of the configuration.xml to use\n" + "-e events_xml path and filename of the events.xml to use\n" + "-h this help page\n" + "-m module path and filename of gator.ko\n" + "-p port_number port upon which the server listens; default is 8080\n" + "-s session_xml path and filename of a session xml used for local capture\n" + "-o apc_dir path and name of the output for a local capture\n" + "-v version information\n" + , version_string); + handleException(); + break; + case 'v': + logg->logError(__FILE__, __LINE__, version_string); + handleException(); + break; + } + } + + // Error checking + if (cmdline.port != DEFAULT_PORT && gSessionData->mSessionXMLPath != NULL) { + logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); + handleException(); + } + + if (gSessionData->mTargetPath != NULL && gSessionData->mSessionXMLPath == NULL) { + logg->logError(__FILE__, __LINE__, "Missing -s command line option required for a local capture."); + handleException(); + } + + if (optind < argc) { + logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]); + handleException(); + } + + return cmdline; +} + +void handleClient() { + OlySocket client(sock->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 + sock->closeServerSocket(); + udpListener.close(); + monitor.close(); + child = new Child(&client, numSessions + 1); + child->run(); + delete child; + exit(0); + } else { + // Parent + client.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); + } + } +} + +// Gator data flow: collector -> collector fifo -> sender +int main(int argc, char** argv) { + // Ensure proper signal handling by making gatord the process group leader + // e.g. it may not be the group leader when launched as 'sudo gatord' + setsid(); + + logg = new Logging(DEBUG); // Set up global thread-safe logging + gSessionData = new SessionData(); // Global data class + util = new OlyUtility(); // Set up global utility class + + // Initialize drivers + new KMod(); + + prctl(PR_SET_NAME, (unsigned long)&"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"); + } + + // Parse the command line parameters + struct cmdline_t cmdline = parseCommandLine(argc, argv); + + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + + // Call before setting up the SIGCHLD handler, as system() spawns child processes + if (!setupFilesystem(cmdline.module)) { + logg->logMessage("Unable to setup gatorfs, trying perf"); + if (!gSessionData->perf.setup()) { + 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\n" + " >>> OR run Linux 3.4 or later with perf (CONFIG_PERF_EVENTS and CONFIG_HW_PERF_EVENTS) and tracing (CONFIG_TRACING) support to collect data via userspace only"); + handleException(); + } + } + + gSessionData->hwmon.setup(); + { + EventsXML eventsXML; + mxml_node_t *xml = eventsXML.getTree(); + gSessionData->fsDriver.setup(xml); + gSessionData->maliVideo.setup(xml); + mxmlDelete(xml); + } + + // 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 { + sock = new OlyServerSocket(cmdline.port); + udpListener.setup(cmdline.port); + if (!monitor.init() || !monitor.add(sock->getFd()) || !monitor.add(udpListener.getReq())) { + logg->logError(__FILE__, __LINE__, "Monitor setup failed"); + handleException(); + } + // Forever loop, can be exited via a signal or exception + while (1) { + struct epoll_event events[2]; + logg->logMessage("Waiting on connection..."); + int ready = monitor.wait(events, ARRAY_LENGTH(events), -1); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); + } + for (int i = 0; i < ready; ++i) { + if (events[i].data.fd == sock->getFd()) { + handleClient(); + } else if (events[i].data.fd == udpListener.getReq()) { + udpListener.handle(); + } + } + } + } + + cleanUp(); + return 0; +} diff --git a/tools/gator/daemon/mxml/COPYING b/tools/gator/daemon/mxml/COPYING new file mode 100644 index 000000000000..4d0aa78af224 --- /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 000000000000..1f59ba34a474 --- /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 000000000000..c9950f5fb732 --- /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 000000000000..c5c9f61f73c2 --- /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 000000000000..7860ee5f8370 --- /dev/null +++ b/tools/gator/daemon/mxml/mxml-file.c @@ -0,0 +1,3082 @@ +/* + * "$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); + lch |= 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); + lch |= (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 000000000000..a5356d57e186 --- /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 000000000000..b6efc66f055c --- /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 000000000000..44af759f9de3 --- /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 000000000000..72f3e2320c7c --- /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 000000000000..8789e6c52cbd --- /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 000000000000..f975af1543ca --- /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 000000000000..b0bd52790b2e --- /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 000000000000..6be42523f95c --- /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 000000000000..79c711f4c80f --- /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 43bc6d5b076e30d55b59daedeb205063cb6e54a2 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 Signed-off-by: Mark Brown Conflicts: drivers/Kconfig drivers/Makefile --- drivers/Kconfig | 2 ++ drivers/Makefile | 2 ++ drivers/gator/Kconfig | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/Kconfig b/drivers/Kconfig index b3138fbb46a4..c1e4021f4362 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -170,4 +170,6 @@ source "drivers/phy/Kconfig" source "drivers/powercap/Kconfig" +source "drivers/gator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 8e3b8b06c0b2..7edd884f2b5a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -155,3 +155,5 @@ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ obj-$(CONFIG_FMC) += fmc/ obj-$(CONFIG_POWERCAP) += powercap/ + +obj-$(CONFIG_GATOR) += gator/ diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig index e46ccb9b8064..31f0c6fb8bae 100644 --- a/drivers/gator/Kconfig +++ b/drivers/gator/Kconfig @@ -3,7 +3,6 @@ config GATOR default m if (ARM || ARM64) depends on PROFILING depends on HIGH_RES_TIMERS - depends on LOCAL_TIMERS || !(ARM && SMP) depends on PERF_EVENTS depends on HW_PERF_EVENTS || !(ARM || ARM64) select TRACING -- cgit v1.2.3