From 1b5637426bfc10a64571c81e24019032206d651b Mon Sep 17 00:00:00 2001 From: Drew Richardson Date: Tue, 26 Mar 2013 12:00:00 -0700 Subject: gator: Version 5.14 Signed-off-by: Drew Richardson --- .gitignore | 5 + README_Streamline.txt | 28 +- daemon/Android.mk | 14 +- daemon/Buffer.cpp | 354 ++++ daemon/Buffer.h | 66 + daemon/CapturedXML.cpp | 76 +- daemon/CapturedXML.h | 3 +- daemon/Child.cpp | 131 +- daemon/Child.h | 6 +- daemon/Collector.cpp | 141 +- daemon/Collector.h | 19 +- daemon/ConfigurationXML.cpp | 153 +- daemon/ConfigurationXML.h | 2 +- daemon/Counter.h | 97 ++ daemon/Driver.cpp | 15 + daemon/Driver.h | 44 + daemon/Fifo.cpp | 20 +- daemon/Fifo.h | 8 +- daemon/Hwmon.cpp | 277 +++ daemon/Hwmon.h | 39 + daemon/KMod.cpp | 102 ++ daemon/KMod.h | 27 + daemon/LocalCapture.cpp | 12 +- daemon/LocalCapture.h | 4 +- daemon/Logging.cpp | 3 +- daemon/Logging.h | 4 +- daemon/Makefile | 55 +- daemon/Makefile_aarch64 | 15 + daemon/OlySocket.cpp | 2 +- daemon/OlySocket.h | 2 +- daemon/OlyUtility.cpp | 2 +- daemon/OlyUtility.h | 2 +- daemon/Sender.cpp | 27 +- daemon/Sender.h | 5 +- daemon/SessionData.cpp | 122 +- daemon/SessionData.h | 38 +- daemon/SessionXML.cpp | 6 +- daemon/SessionXML.h | 6 +- daemon/StreamlineSetup.cpp | 81 +- daemon/StreamlineSetup.h | 3 +- daemon/common.mk | 50 + daemon/configuration.xml | 24 +- daemon/escape.c | 8 +- daemon/events-ARM11.xml | 2 +- daemon/events-ARM11MPCore.xml | 2 +- daemon/events-CCI-400.xml | 47 + daemon/events-Cortex-A15.xml | 12 +- daemon/events-Cortex-A5.xml | 6 +- daemon/events-Cortex-A53.xml | 2 +- daemon/events-Cortex-A57.xml | 2 +- daemon/events-Cortex-A7.xml | 11 +- daemon/events-Cortex-A8.xml | 4 +- daemon/events-Cortex-A9.xml | 2 +- daemon/events-Krait-architected.xml | 2 +- daemon/events-Mali-400.xml | 404 ++--- daemon/events-Mali-T6xx_hw.xml | 12 +- daemon/events-Scorpion.xml | 2 +- daemon/events-ScorpionMP.xml | 2 +- daemon/libsensors/COPYING.LGPL | 502 ++++++ daemon/libsensors/access.c | 561 ++++++ daemon/libsensors/access.h | 33 + daemon/libsensors/conf-lex.c | 2881 +++++++++++++++++++++++++++++++ daemon/libsensors/conf-lex.l | 372 ++++ daemon/libsensors/conf-parse.c | 2042 ++++++++++++++++++++++ daemon/libsensors/conf-parse.h | 84 + daemon/libsensors/conf-parse.y | 347 ++++ daemon/libsensors/conf.h | 34 + daemon/libsensors/data.c | 278 +++ daemon/libsensors/data.h | 184 ++ daemon/libsensors/error.c | 92 + daemon/libsensors/error.h | 74 + daemon/libsensors/general.c | 85 + daemon/libsensors/general.h | 39 + daemon/libsensors/init.c | 341 ++++ daemon/libsensors/init.h | 28 + daemon/libsensors/scanner.h | 32 + daemon/libsensors/sensors.h | 311 ++++ daemon/libsensors/sysfs.c | 926 ++++++++++ daemon/libsensors/sysfs.h | 43 + daemon/libsensors/version.h | 1 + daemon/main.cpp | 26 +- driver/Makefile | 1 + driver/gator.h | 43 +- driver/gator_annotate.c | 23 +- driver/gator_annotate_kernel.c | 2 +- driver/gator_backtrace.c | 39 +- driver/gator_cookies.c | 8 +- driver/gator_events_armv6.c | 6 +- driver/gator_events_armv7.c | 6 +- driver/gator_events_block.c | 9 +- driver/gator_events_irq.c | 48 +- driver/gator_events_l2c-310.c | 74 +- driver/gator_events_mali_400.c | 4 +- driver/gator_events_mali_400.h | 2 +- driver/gator_events_mali_common.c | 2 +- driver/gator_events_mali_common.h | 2 +- driver/gator_events_mali_t6xx.c | 4 +- driver/gator_events_mali_t6xx_hw.c | 4 +- driver/gator_events_mali_t6xx_hw_test.c | 2 +- driver/gator_events_meminfo.c | 4 +- driver/gator_events_mmaped.c | 6 +- driver/gator_events_net.c | 4 +- driver/gator_events_perf_pmu.c | 520 ++++-- driver/gator_events_sched.c | 6 +- driver/gator_events_scorpion.c | 6 +- driver/gator_fs.c | 87 + driver/gator_hrtimer_gator.c | 35 +- driver/gator_hrtimer_perf.c | 2 +- driver/gator_iks.c | 144 ++ driver/gator_main.c | 331 +++- driver/gator_marshaling.c | 119 +- driver/gator_pack.c | 2 +- driver/gator_trace_gpu.c | 86 +- driver/gator_trace_gpu.h | 2 +- driver/gator_trace_power.c | 43 +- driver/gator_trace_sched.c | 28 +- 116 files changed, 12336 insertions(+), 1294 deletions(-) create mode 100644 daemon/Buffer.cpp create mode 100644 daemon/Buffer.h create mode 100644 daemon/Counter.h create mode 100644 daemon/Driver.cpp create mode 100644 daemon/Driver.h create mode 100644 daemon/Hwmon.cpp create mode 100644 daemon/Hwmon.h create mode 100644 daemon/KMod.cpp create mode 100644 daemon/KMod.h create mode 100644 daemon/Makefile_aarch64 create mode 100644 daemon/common.mk create mode 100644 daemon/events-CCI-400.xml create mode 100644 daemon/libsensors/COPYING.LGPL create mode 100644 daemon/libsensors/access.c create mode 100644 daemon/libsensors/access.h create mode 100644 daemon/libsensors/conf-lex.c create mode 100644 daemon/libsensors/conf-lex.l create mode 100644 daemon/libsensors/conf-parse.c create mode 100644 daemon/libsensors/conf-parse.h create mode 100644 daemon/libsensors/conf-parse.y create mode 100644 daemon/libsensors/conf.h create mode 100644 daemon/libsensors/data.c create mode 100644 daemon/libsensors/data.h create mode 100644 daemon/libsensors/error.c create mode 100644 daemon/libsensors/error.h create mode 100644 daemon/libsensors/general.c create mode 100644 daemon/libsensors/general.h create mode 100644 daemon/libsensors/init.c create mode 100644 daemon/libsensors/init.h create mode 100644 daemon/libsensors/scanner.h create mode 100644 daemon/libsensors/sensors.h create mode 100644 daemon/libsensors/sysfs.c create mode 100644 daemon/libsensors/sysfs.h create mode 100644 daemon/libsensors/version.h create mode 100644 driver/gator_iks.c diff --git a/.gitignore b/.gitignore index d13fee9..a4f82d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,12 @@ +daemon/*.d daemon/*.o +daemon/*_xml.h daemon/escape daemon/events.xml daemon/gatord +daemon/libsensors/*.d +daemon/libsensors/*.o +daemon/mxml/*.d daemon/mxml/*.o driver/*.cmd driver/*.mod.c diff --git a/README_Streamline.txt b/README_Streamline.txt index f8309e1..5472c07 100644 --- a/README_Streamline.txt +++ b/README_Streamline.txt @@ -62,8 +62,8 @@ To create the gator.ko module, tar xzf gator-driver.tar.gz cd gator-driver make -C M=`pwd` ARCH=arm CROSS_COMPILE=<...> modules -for example - make -C /home/username/kernel_2.6.32/ M=`pwd` ARCH=arm CROSS_COMPILE=/usr/local/DS-5/bin/arm-linux-gnueabihf- modules +for example when using the linaro-toolchain-binaries + make -C /home/username/kernel_2.6.32/ M=`pwd` ARCH=arm CROSS_COMPILE=/home/username/gcc-linaro-arm-linux-gnueabihf-4.7-2013.01-20130125_linux/bin/arm-linux-gnueabihf- modules If successful, a gator.ko module should be generated *** Building the gator daemon *** @@ -72,7 +72,8 @@ cd /path/to/gator/daemon-src tar -xzf gator-daemon.tar.gz (may need to issue with 'sudo') For Linux targets, cd gator-daemon - make + make CROSS_COMPILE=<...> # For ARMv7 targets + make -f Makefile_aarch64 CROSS_COMPILE=<...> # For ARMv8 targets gatord should now be created For Android targets (install the android ndk, see developer.android.com) mv gator-daemon jni @@ -90,6 +91,13 @@ With root privileges, run the daemon sudo ./gatord & Note: gatord requires libstdc++.so.6 which is usually supplied by the Linux distribution on the target. A copy of libstdc++.so.6 is available in the DS-5 Linux example distribution. +*** Customizing the l2c-310 Counter *** + +The l2c-310 counter in gator_events_l2c-310.c contains hard coded offsets where the L2 cache counter registers are located. This offset can also be configured via a module parameter specified when gator.ko is loaded, ex: + insmod gator.ko l2c310_addr= +Further, the l2c-310 counter can be disabled by providing an offset of zero, ex: + insmod gator.ko l2c310_addr=0 + *** Compiling an application or shared library *** Recommended compiler settings: @@ -99,10 +107,20 @@ Recommended compiler settings: "-marm": This option is required if your compiler is configured with --with-mode=thumb, otherwise call stack unwinding will not work. *** Hardfloat EABI *** -Binary applications built for the soft or softfp ABI are not compatible on a hardfloat system. All soft/softfp applications need to be rebuilt for hardfloat. The included compiler with DS-5 supports hardfloat. -To compile for non-hardfloat targets it is necessary to add options '-marm -march=armv4t -mfloat-abi=soft'. +Binary applications built for the soft or softfp ABI are not compatible on a hardfloat system. All soft/softfp applications need to be rebuilt for hardfloat. To see if your ARM compiler supports hardfloat, run "gcc -v" and look for --with-float=hard. +To compile for non-hardfloat targets it is necessary to add options '-marm -march=armv4t -mfloat-abi=soft'. It may also be necessary to provide a softfloat filesystem by adding the option --sysroot, ex: '--sysroot=../DS-5Examples/distribution/filesystem/armv5t_mtx'. The gatord makefile will do this when run as 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot' +The armv5t_mtx filesystem is provided as part of the "DS-5 Linux Example Distribution" package which can be downloaded from the DS-5 Downloads page. Attempting to run an incompatible binary often results in the confusing error message "No such file or directory" when clearly the file exists. +*** Bugs *** + +There is a bug in some Linux kernels where perf misidentifies the CPU type. To see if you are affected by this, run ls /sys/bus/event_source/devices/ and verify the listed processor type matches what is expected. For example, an A9 should show the following. + +# ls /sys/bus/event_source/devices/ +ARMv7_Cortex_A9 breakpoint software tracepoint + +To workaround the issue try upgrading to a later kernel or comment out the gator_events_perf_pmu_cpu_init(gator_cpu, type); cal in gator_events_perf_pmu.c + *** Profiling the kernel (optional) *** CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above. diff --git a/daemon/Android.mk b/daemon/Android.mk index cc1d542..719dbbd 100644 --- a/daemon/Android.mk +++ b/daemon/Android.mk @@ -3,14 +3,18 @@ include $(CLEAR_VARS) XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) -LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions +LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors LOCAL_SRC_FILES := \ + Buffer.cpp \ CapturedXML.cpp \ Child.cpp \ Collector.cpp \ ConfigurationXML.cpp \ + Driver.cpp \ Fifo.cpp \ + Hwmon.cpp \ + KMod.cpp \ LocalCapture.cpp \ Logging.cpp \ main.cpp \ @@ -20,6 +24,14 @@ LOCAL_SRC_FILES := \ SessionData.cpp \ SessionXML.cpp \ StreamlineSetup.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 \ diff --git a/daemon/Buffer.cpp b/daemon/Buffer.cpp new file mode 100644 index 0000000..ee391bc --- /dev/null +++ b/daemon/Buffer.cpp @@ -0,0 +1,354 @@ +/** + * Copyright (C) ARM Limited 2013. 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 (size - 1) + +Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), +#ifdef GATOR_LIVE + commitTime(gSessionData->mLiveRate), +#endif + readerSem(readerSem) { + if ((size & mask) != 0) { + logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); + handleException(); + } + frame(); +} + +Buffer::~Buffer () { + delete [] buf; +} + +void Buffer::write (Sender * const sender) { + if (!commitReady()) { + return; + } + + // determine the size of two halves + int length1 = commitPos - readPos; + char * buffer1 = buf + readPos; + int length2 = 0; + char * buffer2 = buf; + if (length1 < 0) { + length1 = size - readPos; + length2 = commitPos; + } + + 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); + } + + readPos = commitPos; +} + +bool Buffer::commitReady () const { + return commitPos != readPos; +} + +int Buffer::bytesAvailable () const { + int filled = writePos - readPos; + if (filled < 0) { + filled += size; + } + + int remaining = size - filled; + + if (available) { + // 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) { + available = false; + } else { + available = true; + } + + return available; +} + +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 = writePos - commitPos; + if (length < 0) { + length += size; + } + length = length - typeLength - sizeof(int32_t); + for (size_t byte = 0; byte < sizeof(int32_t); byte++) { + buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + } + + logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); + commitPos = writePos; + +#ifdef GATOR_LIVE + if (gSessionData->mLiveRate > 0) { + while (time > commitTime) { + commitTime += gSessionData->mLiveRate; + } + } +#endif + + if (!done) { + frame(); + } + + // send a notification that data is ready + sem_post(readerSem); +} + +void Buffer::check (const uint64_t time) { + int filled = writePos - commitPos; + if (filled < 0) { + filled += size; + } + if (filled >= ((size * 3) / 4) +#ifdef GATOR_LIVE + || (gSessionData->mLiveRate > 0 && time >= commitTime) +#endif + ) { + commit(time); + } +} + +void Buffer::packInt (const int32_t x) { + const int write0 = (writePos + 0) & mask; + const int write1 = (writePos + 1) & mask; + + if ((x & 0xffffff80) == 0) { + buf[write0] = x & 0x7f; + writePos = write1; + } else if ((x & 0xffffc000) == 0) { + const int write2 = (writePos + 2) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) & 0x7f; + writePos = write2; + } else if ((x & 0xffe00000) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) & 0x7f; + writePos = write3; + } else if ((x & 0xf0000000) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) & 0x7f; + writePos = write4; + } else { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) & 0x0f; + writePos = write5; + } +} + +void Buffer::packInt64 (const int64_t x) { + const int write0 = (writePos + 0) & mask; + const int write1 = (writePos + 1) & mask; + + if ((x & 0xffffffffffffff80LL) == 0) { + buf[write0] = x & 0x7f; + writePos = write1; + } else if ((x & 0xffffffffffffc000LL) == 0) { + const int write2 = (writePos + 2) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) & 0x7f; + writePos = write2; + } else if ((x & 0xffffffffffe00000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) & 0x7f; + writePos = write3; + } else if ((x & 0xfffffffff0000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) & 0x7f; + writePos = write4; + } else if ((x & 0xfffffff800000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) & 0x7f; + writePos = write5; + } else if ((x & 0xfffffc0000000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + const int write6 = (writePos + 6) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) | 0x80; + buf[write5] = (x >> 35) & 0x7f; + writePos = write6; + } else if ((x & 0xfffe000000000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + const int write6 = (writePos + 6) & mask; + const int write7 = (writePos + 7) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) | 0x80; + buf[write5] = (x >> 35) | 0x80; + buf[write6] = (x >> 42) & 0x7f; + writePos = write7; + } else if ((x & 0xff00000000000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + const int write6 = (writePos + 6) & mask; + const int write7 = (writePos + 7) & mask; + const int write8 = (writePos + 8) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) | 0x80; + buf[write5] = (x >> 35) | 0x80; + buf[write6] = (x >> 42) | 0x80; + buf[write7] = (x >> 49) & 0x7f; + writePos = write8; + } else if ((x & 0x8000000000000000LL) == 0) { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + const int write6 = (writePos + 6) & mask; + const int write7 = (writePos + 7) & mask; + const int write8 = (writePos + 8) & mask; + const int write9 = (writePos + 9) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) | 0x80; + buf[write5] = (x >> 35) | 0x80; + buf[write6] = (x >> 42) | 0x80; + buf[write7] = (x >> 49) | 0x80; + buf[write8] = (x >> 56) & 0x7f; + writePos = write9; + } else { + const int write2 = (writePos + 2) & mask; + const int write3 = (writePos + 3) & mask; + const int write4 = (writePos + 4) & mask; + const int write5 = (writePos + 5) & mask; + const int write6 = (writePos + 6) & mask; + const int write7 = (writePos + 7) & mask; + const int write8 = (writePos + 8) & mask; + const int write9 = (writePos + 9) & mask; + const int write10 = (writePos + 10) & mask; + buf[write0] = x | 0x80; + buf[write1] = (x >> 7) | 0x80; + buf[write2] = (x >> 14) | 0x80; + buf[write3] = (x >> 21) | 0x80; + buf[write4] = (x >> 28) | 0x80; + buf[write5] = (x >> 35) | 0x80; + buf[write6] = (x >> 42) | 0x80; + buf[write7] = (x >> 49) | 0x80; + buf[write8] = (x >> 56) | 0x80; + buf[write9] = (x >> 63) & 0x7f; + writePos = write10; + } +} + +void Buffer::frame () { + if (!gSessionData->mLocalCapture) { + packInt(RESPONSE_APC_DATA); + } + // Reserve space for the length + writePos += sizeof(int32_t); + packInt(buftype); + packInt(core); +} + +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; +} + +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::setDone () { + done = true; + commit(0); +} + +bool Buffer::isDone () const { + return done && readPos == commitPos && commitPos == writePos; +} diff --git a/daemon/Buffer.h b/daemon/Buffer.h new file mode 100644 index 0000000..c460fb7 --- /dev/null +++ b/daemon/Buffer.h @@ -0,0 +1,66 @@ +/** + * Copyright (C) ARM Limited 2013. 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 + +#define GATOR_LIVE + +class Sender; + +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; + void commit (const uint64_t time); + void check (const uint64_t time); + + void frame (); + + bool eventHeader (uint64_t curr_time); + void event (int32_t key, int32_t value); + void event64 (int64_t key, int64_t value); + + void setDone (); + bool isDone () const; + +private: + bool commitReady () const; + bool checkSpace (int bytes); + + void packInt (int32_t x); + void packInt64 (int64_t x); + + const int32_t core; + const int32_t buftype; + const int size; + int readPos; + int writePos; + int commitPos; + bool available; + bool done; + char *const buf; +#ifdef GATOR_LIVE + uint64_t commitTime; +#endif + + sem_t *const readerSem; +}; + +#endif // BUFFER_H diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp index cc218d7..8b037cb 100644 --- a/daemon/CapturedXML.cpp +++ b/daemon/CapturedXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -21,21 +21,11 @@ CapturedXML::~CapturedXML() { } mxml_node_t* CapturedXML::getTree(bool includeTime) { - bool perfCounters = false; mxml_node_t *xml; mxml_node_t *captured; mxml_node_t *target; - mxml_node_t *counters; - mxml_node_t *counter; int x; - for (x=0; xmPerfCounterEnabled[x]) { - perfCounters = true; - break; - } - } - xml = mxmlNewXML("1.0"); captured = mxmlNewElement(xml, "captured"); @@ -51,35 +41,40 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { mxmlElementSetAttr(target, "name", gSessionData->mCoreName); mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); - - if (perfCounters) { - counters = mxmlNewElement(captured, "counters"); - for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { - if (gSessionData->mPerfCounterEnabled[x]) { - counter = mxmlNewElement(counters, "counter"); - mxmlElementSetAttr(counter, "title", gSessionData->mPerfCounterTitle[x]); - mxmlElementSetAttr(counter, "name", gSessionData->mPerfCounterName[x]); - mxmlElementSetAttrf(counter, "color", "0x%08x", gSessionData->mPerfCounterColor[x]); - mxmlElementSetAttrf(counter, "key", "0x%08x", gSessionData->mPerfCounterKey[x]); - mxmlElementSetAttr(counter, "type", gSessionData->mPerfCounterType[x]); - mxmlElementSetAttrf(counter, "event", "0x%08x", gSessionData->mPerfCounterEvent[x]); - if (gSessionData->mPerfCounterPerCPU[x]) { - mxmlElementSetAttr(counter, "per_cpu", "yes"); - } - if (gSessionData->mPerfCounterCount[x] > 0) { - mxmlElementSetAttrf(counter, "count", "%d", gSessionData->mPerfCounterCount[x]); - } - if (strlen(gSessionData->mPerfCounterDisplay[x]) > 0) { - mxmlElementSetAttr(counter, "display", gSessionData->mPerfCounterDisplay[x]); - } - if (strlen(gSessionData->mPerfCounterUnits[x]) > 0) { - mxmlElementSetAttr(counter, "units", gSessionData->mPerfCounterUnits[x]); - } - if (gSessionData->mPerfCounterAverageSelection[x]) { - mxmlElementSetAttr(counter, "average_selection", "yes"); - } - mxmlElementSetAttr(counter, "description", gSessionData->mPerfCounterDescription[x]); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); + + 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"); + mxmlElementSetAttr(node, "title", counter.getTitle()); + mxmlElementSetAttr(node, "name", counter.getName()); + mxmlElementSetAttrf(node, "key", "0x%08x", counter.getKey()); + mxmlElementSetAttr(node, "type", counter.getType()); + mxmlElementSetAttrf(node, "event", "0x%08x", counter.getEvent()); + if (counter.isPerCPU()) { + mxmlElementSetAttr(node, "per_cpu", "yes"); + } + if (counter.getCount() > 0) { + mxmlElementSetAttrf(node, "count", "%d", counter.getCount()); + } + if (strlen(counter.getDisplay()) > 0) { + mxmlElementSetAttr(node, "display", counter.getDisplay()); + } + if (strlen(counter.getUnits()) > 0) { + mxmlElementSetAttr(node, "units", counter.getUnits()); + } + if (counter.getModifier() != 1) { + mxmlElementSetAttrf(node, "modifier", "%d", counter.getModifier()); + } + if (counter.isAverageSelection()) { + mxmlElementSetAttr(node, "average_selection", "yes"); } + mxmlElementSetAttr(node, "description", counter.getDescription()); } } @@ -95,7 +90,7 @@ char* CapturedXML::getXML(bool includeTime) { } void CapturedXML::write(char* path) { - char *file = (char*)malloc(PATH_MAX); + char file[PATH_MAX]; // Set full path snprintf(file, PATH_MAX, "%s/captured.xml", path); @@ -107,7 +102,6 @@ void CapturedXML::write(char* path) { } free(xml); - free(file); } // whitespace callback utility function used with mini-xml diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h index 3cac576..b0482f5 100644 --- a/daemon/CapturedXML.h +++ b/daemon/CapturedXML.h @@ -1,6 +1,5 @@ /** - - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/Child.cpp b/daemon/Child.cpp index b97e0db..c784847 100644 --- a/daemon/Child.cpp +++ b/daemon/Child.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include "Logging.h" @@ -23,11 +21,18 @@ #include "OlyUtility.h" #include "StreamlineSetup.h" #include "ConfigurationXML.h" +#include "Driver.h" +#include "Fifo.h" +#include "Buffer.h" -static sem_t haltPipeline, senderThreadStarted, startProfile; // Shared by Child and spawned threads +#define NS_PER_S ((uint64_t)1000000000) +#define NS_PER_US 1000 + +static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads +static Buffer* buffer = NULL; static Sender* sender = NULL; // Shared by Child.cpp and spawned threads -Collector* collector = NULL; +static Collector* collector = NULL; Child* child = NULL; // shared by Child.cpp and main.cpp extern void cleanUp(); @@ -62,7 +67,7 @@ void handleException() { } // CTRL C Signal Handler for child process -void child_handler(int signum) { +static void child_handler(int signum) { static bool beenHere = false; if (beenHere == true) { logg->logMessage("Gator is being forced to shut down."); @@ -78,7 +83,7 @@ void child_handler(int signum) { } } -void* durationThread(void* pVoid) { +static void* durationThread(void* pVoid) { prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0); sem_wait(&startProfile); if (gSessionData->mSessionIsActive) { @@ -94,7 +99,7 @@ void* durationThread(void* pVoid) { return 0; } -void* stopThread(void* pVoid) { +static void* stopThread(void* pVoid) { int length; char type; OlySocket* socket = child->socket; @@ -134,8 +139,61 @@ void* stopThread(void* pVoid) { return 0; } -void* senderThread(void* pVoid) { - int length; +void* countersThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); + + gSessionData->hwmon.start(); + + int64_t monotonic_started = 0; + while (monotonic_started <= 0) { + usleep(10); + + if (Collector::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) { + 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(); + } + const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - 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 (buffer->eventHeader(curr_time)) { + gSessionData->hwmon.read(buffer); + // Only check after writing all counters so that time and corresponding counters appear in the same frame + buffer->check(curr_time); + } + + if (buffer->bytesAvailable() <= 0) { + logg->logMessage("One shot (counters)"); + child->endSession(); + } + + usleep((next_time - curr_time)/NS_PER_US); + } + + buffer->setDone(); + + return NULL; +} + +static void* senderThread(void* pVoid) { + int length = 1; char* data; char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; @@ -143,10 +201,17 @@ void* senderThread(void* pVoid) { prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); sem_wait(&haltPipeline); - do { + while (length > 0 || !buffer->isDone()) { + sem_wait(&senderSem); data = collectorFifo->read(&length); - sender->writeData(data, length, RESPONSE_APC_DATA); - } while (length > 0); + if (data != NULL) { + sender->writeData(data, length, RESPONSE_APC_DATA); + collectorFifo->release(); + } + if (!buffer->isDone()) { + buffer->write(sender); + } + } // write end-of-capture sequence if (!gSessionData->mLocalCapture) { @@ -185,6 +250,7 @@ void Child::initialization() { // Initialize semaphores sem_init(&senderThreadStarted, 0, 0); sem_init(&startProfile, 0, 0); + sem_init(&senderSem, 0, 0); } void Child::endSession() { @@ -197,7 +263,7 @@ void Child::run() { char* collectBuffer; int bytesCollected = 0; LocalCapture* localCapture = NULL; - pthread_t durationThreadID, stopThreadID, senderThreadID; + pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); @@ -213,11 +279,24 @@ void Child::run() { } // Populate gSessionData with the configuration - new ConfigurationXML(); + { ConfigurationXML configuration; } // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated collector = new Collector(); + // 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 @@ -238,16 +317,16 @@ void Child::run() { free(xmlString); } - // Write configuration into the driver - collector->setupPerfCounters(); - // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); - collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize* 1024 * 1024); + collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); // Get the initial pointer to the collect buffer collectBuffer = collectorFifo->start(); + // Create a new Block Counter Buffer + buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); + // Sender thread shall be halted until it is signaled for one shot mode sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); @@ -261,6 +340,15 @@ void Child::run() { thread_creation_success = false; } + if (gSessionData->hwmon.countersEnabled()) { + if (pthread_create(&countersThreadID, NULL, countersThread, this)) { + thread_creation_success = false; + } + } else { + // Let senderThread know there is no buffer data to send + buffer->setDone(); + } + if (!thread_creation_success) { logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); handleException(); @@ -290,6 +378,10 @@ void Child::run() { } while (bytesCollected > 0); logg->logMessage("Exit collect data loop"); + if (gSessionData->hwmon.countersEnabled()) { + pthread_join(countersThreadID, NULL); + } + // Wait for the other threads to exit pthread_join(senderThreadID, NULL); @@ -308,6 +400,7 @@ void Child::run() { logg->logMessage("Profiling ended."); + delete buffer; delete collectorFifo; delete sender; delete collector; diff --git a/daemon/Child.h b/daemon/Child.h index 612ca7c..e39d182 100644 --- a/daemon/Child.h +++ b/daemon/Child.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -10,8 +10,8 @@ #define __CHILD_H__ #include -#include "Fifo.h" -#include "OlySocket.h" + +class OlySocket; class Child { public: diff --git a/daemon/Collector.cpp b/daemon/Collector.cpp index 2e4af0c..bf73534 100644 --- a/daemon/Collector.cpp +++ b/daemon/Collector.cpp @@ -1,18 +1,20 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 -#include #include #include #include #include #include +#include #include "Collector.h" #include "SessionData.h" #include "Logging.h" @@ -20,8 +22,6 @@ // Driver initialization independent of session settings Collector::Collector() { - char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// - mBufferFD = 0; checkVersion(); @@ -42,18 +42,6 @@ Collector::Collector() { logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); handleException(); } - - getCoreName(); - - enablePerfCounters(); - - // Read unchanging keys from driver which are created at insmod'ing of gator.ko - for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - if (gSessionData->mPerfCounterEnabled[i]) { - snprintf(text, sizeof(text), "/dev/gator/events/%s/key", gSessionData->mPerfCounterType[i]); - readIntDriver(text, &gSessionData->mPerfCounterKey[i]); - } - } } Collector::~Collector() { @@ -66,63 +54,6 @@ Collector::~Collector() { } } -#include -void Collector::enablePerfCounters() { - char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events//enabled - - // Initialize all perf counters in the driver, i.e. set enabled to zero - struct dirent *ent; - DIR* dir = opendir("/dev/gator/events"); - if (dir) { - while ((ent = readdir(dir)) != NULL) { - // skip hidden files, current dir, and parent dir - if (ent->d_name[0] == '.') - continue; - snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", ent->d_name); - writeDriver(text, 0); - } - closedir (dir); - } - - for (int i=0; imPerfCounterEnabled[i]) { - continue; - } - snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", gSessionData->mPerfCounterType[i]); - if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { - // Disable those events that don't exist on this hardware platform even though they exist in configuration.xml - gSessionData->mPerfCounterEnabled[i] = 0; - continue; - } - } -} - -void Collector::setupPerfCounters() { - char base[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all /dev/gator/events/ - char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// - - for (int i=0; imPerfCounterEnabled[i]) { - continue; - } - snprintf(base, sizeof(base), "/dev/gator/events/%s", gSessionData->mPerfCounterType[i]); - snprintf(text, sizeof(text), "%s/event", base); - writeDriver(text, gSessionData->mPerfCounterEvent[i]); - if (gSessionData->mPerfCounterEBSCapable[i]) { - snprintf(text, sizeof(text), "%s/count", base); - if (access(text, F_OK) == 0) { - if (writeReadDriver(text, &gSessionData->mPerfCounterCount[i]) && gSessionData->mPerfCounterCount[i] > 0) { - logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%s with a count of %d\n", gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]); - handleException(); - } - } else if (gSessionData->mPerfCounterCount[i] > 0) { - logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); - handleException(); - } - } - } -} - void Collector::checkVersion() { int driver_version = 0; @@ -179,6 +110,12 @@ void Collector::start() { 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 @@ -215,47 +152,26 @@ int Collector::collect(char* buffer) { return bytesRead; } -void Collector::getCoreName() { - char temp[256]; // arbitrarily large amount - strcpy(gSessionData->mCoreName, "unknown"); - - FILE* f = fopen("/proc/cpuinfo", "r"); - if (f == NULL) { - logg->logMessage("Error opening /proc/cpuinfo\n" - "The core name in the captured xml file will be 'unknown'."); - return; +int Collector::readIntDriver(const char* fullpath, int* value) { + FILE* file = fopen(fullpath, "r"); + if (file == NULL) { + return -1; } - - while (fgets(temp, sizeof(temp), f)) { - if (strlen(temp) > 0) { - temp[strlen(temp) - 1] = 0; // Replace the line feed with a null - } - - if (strstr(temp, "Hardware") != 0) { - char* position = strchr(temp, ':'); - if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { - logg->logMessage("Unknown format of /proc/cpuinfo\n" - "The core name in the captured xml file will be 'unknown'."); - return; - } - strncpy(gSessionData->mCoreName, (char*)((long)position + 2), sizeof(gSessionData->mCoreName)); - gSessionData->mCoreName[sizeof(gSessionData->mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string - fclose(f); - return; - } + if (fscanf(file, "%u", value) != 1) { + fclose(file); + logg->logMessage("Invalid value in file %s", fullpath); + return -1; } - - logg->logMessage("Could not determine core name from /proc/cpuinfo\n" - "The core name in the captured xml file will be 'unknown'."); - fclose(f); + fclose(file); + return 0; } -int Collector::readIntDriver(const char* fullpath, int* value) { +int Collector::readInt64Driver(const char* fullpath, int64_t* value) { FILE* file = fopen(fullpath, "r"); if (file == NULL) { return -1; } - if (fscanf(file, "%u", value) != 1) { + if (fscanf(file, "%" SCNi64, value) != 1) { fclose(file); logg->logMessage("Invalid value in file %s", fullpath); return -1; @@ -270,6 +186,12 @@ int Collector::writeDriver(const char* path, int value) { return writeDriver(path, data); } +int Collector::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 Collector::writeDriver(const char* fullpath, const char* data) { int fd = open(fullpath, O_WRONLY); if (fd < 0) { @@ -290,3 +212,10 @@ int Collector::writeReadDriver(const char* path, int* value) { } return 0; } + +int Collector::writeReadDriver(const char* path, int64_t* value) { + if (writeDriver(path, *value) || readInt64Driver(path, value)) { + return -1; + } + return 0; +} diff --git a/daemon/Collector.h b/daemon/Collector.h index 5977a72..c5e9eac 100644 --- a/daemon/Collector.h +++ b/daemon/Collector.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -18,20 +18,21 @@ public: void start(); void stop(); int collect(char* buffer); - void enablePerfCounters(); - void setupPerfCounters(); int getBufferSize() {return mBufferSize;} + + static int readIntDriver(const char* path, int* value); + static int readInt64Driver(const char* path, int64_t* value); + static int writeDriver(const char* path, int value); + static int writeDriver(const char* path, int64_t value); + static int writeDriver(const char* path, const char* data); + static int writeReadDriver(const char* path, int* value); + static int writeReadDriver(const char* path, int64_t* value); + private: int mBufferSize; int mBufferFD; void checkVersion(); - void getCoreName(); - - int readIntDriver(const char* path, int* value); - int writeDriver(const char* path, int value); - int writeDriver(const char* path, const char* data); - int writeReadDriver(const char* path, int* value); }; #endif //__COLLECTOR_H__ diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp index 14c2809..9d51f26 100644 --- a/daemon/ConfigurationXML.cpp +++ b/daemon/ConfigurationXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -10,30 +10,31 @@ #include #include #include "ConfigurationXML.h" +#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_TITLE = "title"; -static const char* ATTR_NAME = "name"; -static const char* ATTR_EVENT = "event"; -static const char* ATTR_COUNT = "count"; -static const char* ATTR_PER_CPU = "per_cpu"; -static const char* ATTR_DESCRIPTION = "description"; -static const char* ATTR_EBS = "supports_event_based_sampling"; -static const char* ATTR_DISPLAY = "display"; -static const char* ATTR_UNITS = "units"; -static const char* ATTR_AVERAGE_SELECTION = "average_selection"; +static const char* ATTR_COUNTER = "counter"; +static const char* ATTR_REVISION = "revision"; +static const char* ATTR_TITLE = "title"; +static const char* ATTR_NAME = "name"; +static const char* ATTR_EVENT = "event"; +static const char* ATTR_COUNT = "count"; +static const char* ATTR_PER_CPU = "per_cpu"; +static const char* ATTR_DESCRIPTION = "description"; +static const char* ATTR_EBS = "supports_event_based_sampling"; +static const char* ATTR_DISPLAY = "display"; +static const char* ATTR_UNITS = "units"; +static const char* ATTR_MODIFIER = "modifier"; +static const char* ATTR_AVERAGE_SELECTION = "average_selection"; ConfigurationXML::ConfigurationXML() { const char * configuration_xml; unsigned int configuration_xml_len; getDefaultConfigurationXml(configuration_xml, configuration_xml_len); - mIndex = 0; - char* path = (char*)malloc(PATH_MAX); + char path[PATH_MAX]; if (gSessionData->mConfigurationXMLPath) { strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); @@ -45,35 +46,34 @@ ConfigurationXML::ConfigurationXML() { } mConfigurationXML = util->readFromDisk(path); - if (mConfigurationXML == NULL) { - logg->logMessage("Unable to locate configuration.xml, using default in binary"); - // null-terminate configuration_xml - mConfigurationXML = (char*)malloc(configuration_xml_len + 1); - memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len); - mConfigurationXML[configuration_xml_len] = 0; - } - - // disable all counters prior to parsing the configuration xml - for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - gSessionData->mPerfCounterEnabled[i] = 0; - } + 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; + } - // clear counter overflow - gSessionData->mCounterOverflow = false; + int ret = parse(mConfigurationXML); + if (ret == 1) { + // remove configuration.xml on disk to use the default + if (remove(path) != 0) { + logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + handleException(); + } + logg->logMessage("Invalid configuration.xml file detected and removed"); - int ret = parse(mConfigurationXML); - if (ret == 1) { - // remove configuration.xml on disk to use the default - if (remove(path) != 0) { - logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); - handleException(); + // Free the current configuration and reload + free((void*)mConfigurationXML); + mConfigurationXML = NULL; + continue; } - logg->logMessage("Invalid configuration.xml file detected and removed"); + + break; } validate(); - - free(path); } ConfigurationXML::~ConfigurationXML() { @@ -86,6 +86,15 @@ int ConfigurationXML::parse(const char* configurationXML) { mxml_node_t *tree, *node; int ret; + // clear counter overflow + gSessionData->mCounterOverflow = 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); @@ -111,18 +120,20 @@ int ConfigurationXML::parse(const char* configurationXML) { void ConfigurationXML::validate(void) { for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - if (gSessionData->mPerfCounterEnabled[i]) { - if (strcmp(gSessionData->mPerfCounterType[i], "") == 0) { - logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n title=\"%s\"\n name=\"%s\"\n event=%d\n", gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterEvent[i]); + 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 title=\"%s\"\n name=\"%s\"\n event=%d\n", counter.getType(), counter.getTitle(), counter.getName(), counter.getEvent()); handleException(); } // iterate through the remaining enabled performance counters for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { - if (gSessionData->mPerfCounterEnabled[j]) { + const Counter & counter2 = gSessionData->mCounters[j]; + if (counter2.isEnabled()) { // check if the types are the same - if (strcmp(gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterType[j]) == 0) { - logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", gSessionData->mPerfCounterType[i]); + if (strcmp(counter.getType(), counter2.getType()) == 0) { + logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", counter.getType()); handleException(); } } @@ -131,7 +142,7 @@ void ConfigurationXML::validate(void) { } } -#define CONFIGURATION_REVISION 1 +#define CONFIGURATION_REVISION 2 int ConfigurationXML::configurationsTag(mxml_node_t *node) { const char* revision_string; @@ -156,26 +167,38 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { } // read attributes - if (mxmlElementGetAttr(node, ATTR_COUNTER)) strncpy(gSessionData->mPerfCounterType[mIndex], mxmlElementGetAttr(node, ATTR_COUNTER), sizeof(gSessionData->mPerfCounterType[mIndex])); - if (mxmlElementGetAttr(node, ATTR_TITLE)) strncpy(gSessionData->mPerfCounterTitle[mIndex], mxmlElementGetAttr(node, ATTR_TITLE), sizeof(gSessionData->mPerfCounterTitle[mIndex])); - if (mxmlElementGetAttr(node, ATTR_NAME)) strncpy(gSessionData->mPerfCounterName[mIndex], mxmlElementGetAttr(node, ATTR_NAME), sizeof(gSessionData->mPerfCounterName[mIndex])); - if (mxmlElementGetAttr(node, ATTR_DESCRIPTION)) strncpy(gSessionData->mPerfCounterDescription[mIndex], mxmlElementGetAttr(node, ATTR_DESCRIPTION), sizeof(gSessionData->mPerfCounterDescription[mIndex])); - if (mxmlElementGetAttr(node, ATTR_EVENT)) gSessionData->mPerfCounterEvent[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16); - if (mxmlElementGetAttr(node, ATTR_COUNT)) gSessionData->mPerfCounterCount[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10); - if (mxmlElementGetAttr(node, ATTR_PER_CPU)) gSessionData->mPerfCounterPerCPU[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_PER_CPU), false); - if (mxmlElementGetAttr(node, ATTR_EBS)) gSessionData->mPerfCounterEBSCapable[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_EBS), false); - if (mxmlElementGetAttr(node, ATTR_DISPLAY)) strncpy(gSessionData->mPerfCounterDisplay[mIndex], mxmlElementGetAttr(node, ATTR_DISPLAY), sizeof(gSessionData->mPerfCounterDisplay[mIndex])); - if (mxmlElementGetAttr(node, ATTR_UNITS)) strncpy(gSessionData->mPerfCounterUnits[mIndex], mxmlElementGetAttr(node, ATTR_UNITS), sizeof(gSessionData->mPerfCounterUnits[mIndex])); - if (mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION)) gSessionData->mPerfCounterAverageSelection[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION), false); - gSessionData->mPerfCounterEnabled[mIndex] = true; - - // strncpy does not guarantee a null-terminated string - gSessionData->mPerfCounterType[mIndex][sizeof(gSessionData->mPerfCounterType[mIndex]) - 1] = 0; - gSessionData->mPerfCounterTitle[mIndex][sizeof(gSessionData->mPerfCounterTitle[mIndex]) - 1] = 0; - gSessionData->mPerfCounterName[mIndex][sizeof(gSessionData->mPerfCounterName[mIndex]) - 1] = 0; - gSessionData->mPerfCounterDescription[mIndex][sizeof(gSessionData->mPerfCounterDescription[mIndex]) - 1] = 0; - gSessionData->mPerfCounterDisplay[mIndex][sizeof(gSessionData->mPerfCounterDisplay[mIndex]) - 1] = 0; - gSessionData->mPerfCounterUnits[mIndex][sizeof(gSessionData->mPerfCounterUnits[mIndex]) - 1] = 0; + Counter & counter = gSessionData->mCounters[mIndex]; + counter.clear(); + if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); + if (mxmlElementGetAttr(node, ATTR_TITLE)) counter.setTitle(mxmlElementGetAttr(node, ATTR_TITLE)); + if (mxmlElementGetAttr(node, ATTR_NAME)) counter.setName(mxmlElementGetAttr(node, ATTR_NAME)); + if (mxmlElementGetAttr(node, ATTR_DESCRIPTION)) counter.setDescription(mxmlElementGetAttr(node, ATTR_DESCRIPTION)); + 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_PER_CPU)) counter.setPerCPU(util->stringToBool(mxmlElementGetAttr(node, ATTR_PER_CPU), false)); + if (mxmlElementGetAttr(node, ATTR_EBS)) counter.setEBSCapable(util->stringToBool(mxmlElementGetAttr(node, ATTR_EBS), false)); + if (mxmlElementGetAttr(node, ATTR_DISPLAY)) counter.setDisplay(mxmlElementGetAttr(node, ATTR_DISPLAY)); + if (mxmlElementGetAttr(node, ATTR_UNITS)) counter.setUnits(mxmlElementGetAttr(node, ATTR_UNITS)); + if (mxmlElementGetAttr(node, ATTR_MODIFIER)) counter.setModifier(strtol(mxmlElementGetAttr(node, ATTR_MODIFIER), NULL, 10)); + if (mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION)) counter.setAverageSelection(util->stringToBool(mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION), false)); + 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: %s", counter.getTitle(), counter.getName()); + 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 (%s: %s)", counter.getType(), counter.getTitle(), counter.getName()); + counter.setEnabled(false); + } // update counter index mIndex++; diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h index 66ad587..f709ad1 100644 --- a/daemon/ConfigurationXML.h +++ b/daemon/ConfigurationXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/Counter.h b/daemon/Counter.h new file mode 100644 index 0000000..041020b --- /dev/null +++ b/daemon/Counter.h @@ -0,0 +1,97 @@ +/** + * Copyright (C) ARM Limited 2013. 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'; + mTitle[0] = '\0'; + mName[0] = '\0'; + mDescription[0] = '\0'; + mDisplay[0] = '\0'; + mUnits[0] = '\0'; + mModifier = 1; + mEnabled = false; + mEvent = 0; + mCount = 0; + mKey = 0; + mPerCPU = false; + mEBSCapable = false; + mAverageSelection = false; + mDriver = NULL; + } + + void setType(const char *const type) { strncpy(mType, type, sizeof(mType)); mType[sizeof(mType) - 1] = '\0'; } + void setTitle(const char *const title) { strncpy(mTitle, title, sizeof(mTitle)); mTitle[sizeof(mTitle) - 1] = '\0'; } + void setName(const char *const name) { strncpy(mName, name, sizeof(mName)); mName[sizeof(mName) - 1] = '\0'; } + void setDescription(const char *const description) { strncpy(mDescription, description, sizeof(mDescription)); mDescription[sizeof(mDescription) - 1] = '\0'; } + void setDisplay(const char *const display) { strncpy(mDisplay, display, sizeof(mDisplay)); mDisplay[sizeof(mDisplay) - 1] = '\0'; } + void setUnits(const char *const units) { strncpy(mUnits, units, sizeof(mUnits)); mUnits[sizeof(mUnits) - 1] = '\0'; } + void setModifier(const int modifier) { mModifier = modifier; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + void setEvent(const int event) { mEvent = event; } + void setCount(const int count) { mCount = count; } + void setKey(const int key) { mKey = key; } + void setPerCPU(const bool perCPU) { mPerCPU = perCPU; } + void setEBSCapable(const bool ebsCapable) { mEBSCapable = ebsCapable; } + void setAverageSelection(const bool averageSelection) { mAverageSelection = averageSelection; } + void setDriver(Driver *const driver) { mDriver = driver; } + + const char *getType() const { return mType;} + const char *getTitle() const { return mTitle; } + const char *getName() const { return mName; } + const char *getDescription() const { return mDescription; } + const char *getDisplay() const { return mDisplay; } + const char *getUnits() const { return mUnits; } + int getModifier() const { return mModifier; } + bool isEnabled() const { return mEnabled; } + int getEvent() const { return mEvent; } + int getCount() const { return mCount; } + int getKey() const { return mKey; } + bool isPerCPU() const { return mPerCPU; } + bool isEBSCapable() const { return mEBSCapable; } + bool isAverageSelection() const { return mAverageSelection; } + Driver *getDriver() const { return mDriver; } + +private: + // Intentionally unimplemented + Counter(const Counter &); + Counter & operator=(const Counter &); + + char mType[MAX_STRING_LEN]; + char mTitle[MAX_STRING_LEN]; + char mName[MAX_STRING_LEN]; + char mDescription[MAX_DESCRIPTION_LEN]; + char mDisplay[MAX_STRING_LEN]; + char mUnits[MAX_STRING_LEN]; + int mModifier; + bool mEnabled; + int mEvent; + int mCount; + int mKey; + bool mPerCPU; + bool mEBSCapable; + bool mAverageSelection; + Driver *mDriver; +}; + +#endif // COUNTER_H diff --git a/daemon/Driver.cpp b/daemon/Driver.cpp new file mode 100644 index 0000000..c262467 --- /dev/null +++ b/daemon/Driver.cpp @@ -0,0 +1,15 @@ +/** + * Copyright (C) ARM Limited 2013. 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/daemon/Driver.h b/daemon/Driver.h new file mode 100644 index 0000000..dd1dc27 --- /dev/null +++ b/daemon/Driver.h @@ -0,0 +1,44 @@ +/** + * Copyright (C) ARM Limited 2013. 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 void writeCounters(mxml_node_t *root) const = 0; + // Emits possible dynamically generated events/counters + virtual void writeEvents(mxml_node_t *root) const {} + + Driver *getNext() const { return next; } + +protected: + Driver (); + +private: + static Driver *head; + Driver *next; +}; + +#endif // DRIVER_H diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp index 4a27452..f0b0178 100644 --- a/daemon/Fifo.cpp +++ b/daemon/Fifo.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -15,10 +15,11 @@ // 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) { +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; @@ -27,7 +28,7 @@ Fifo::Fifo(int singleBufferSize, int bufferSize) { handleException(); } - if (sem_init(&mWaitForSpaceSem, 0, 0) || sem_init(&mWaitForDataSem, 0, 0)) { + if (sem_init(&mWaitForSpaceSem, 0, 0)) { logg->logError(__FILE__, __LINE__, "sem_init() failed"); handleException(); } @@ -36,7 +37,6 @@ Fifo::Fifo(int singleBufferSize, int bufferSize) { Fifo::~Fifo() { free(mBuffer); sem_destroy(&mWaitForSpaceSem); - sem_destroy(&mWaitForDataSem); } int Fifo::numBytesFilled() const { @@ -87,7 +87,7 @@ char* Fifo::write(int length) { } // send a notification that data is ready - sem_post(&mWaitForDataSem); + sem_post(mReaderSem); // wait for space while (isFull()) { @@ -97,8 +97,7 @@ char* Fifo::write(int length) { return &mBuffer[mWrite]; } -// This function will stall until data is available -char* Fifo::read(int *const length) { +void Fifo::release() { // update the read pointer now that the data has been handled mRead = mReadCommit; @@ -109,10 +108,13 @@ char* Fifo::read(int *const length) { // 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 - while (isEmpty() && !mEnd) { - sem_wait(&mWaitForDataSem); + if (isEmpty() && !mEnd) { + return NULL; } // obtain the length diff --git a/daemon/Fifo.h b/daemon/Fifo.h index e2a5d94..d46e1af 100644 --- a/daemon/Fifo.h +++ b/daemon/Fifo.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -13,7 +13,7 @@ class Fifo { public: - Fifo(int singleBufferSize, int totalBufferSize); + Fifo(int singleBufferSize, int totalBufferSize, sem_t* readerSem); ~Fifo(); int numBytesFilled() const; bool isEmpty() const; @@ -21,11 +21,13 @@ public: 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, mWaitForDataSem; + sem_t mWaitForSpaceSem; + sem_t* mReaderSem; char* mBuffer; bool mEnd; }; diff --git a/daemon/Hwmon.cpp b/daemon/Hwmon.cpp new file mode 100644 index 0000000..9603411 --- /dev/null +++ b/daemon/Hwmon.cpp @@ -0,0 +1,277 @@ +/** + * Copyright (C) ARM Limited 2013. 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, int key, 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; } + const char *getDisplay() const { return display; } + const char *getUnit() const { return unit; } + int getModifier() const { return modifier; } + + void setEnabled(const bool enabled) { this->enabled = enabled; } + + double read(); + +private: + void init(const sensors_chip_name *chip, const sensors_feature *feature); + + HwmonCounter *const next; + const int key; + bool enabled; + + const sensors_chip_name *chip; + const sensors_feature *feature; + + char *name; + char *label; + const char *title; + const char *display; + const char *unit; + int modifier; + bool monotonic; + double previous_value; + + sensors_subfeature_type input; +}; + +HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), enabled(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]; + 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 = "average"; + unit = "V"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_FAN: + title = "Fan"; + input = SENSORS_SUBFEATURE_FAN_INPUT; + display = "average"; + unit = "RPM"; + modifier = 1; + monotonic = false; + break; + case SENSORS_FEATURE_TEMP: + title = "Temperature"; + input = SENSORS_SUBFEATURE_TEMP_INPUT; + display = "maximum"; + unit = "°C"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_POWER: + title = "Power"; + input = SENSORS_SUBFEATURE_POWER_INPUT; + display = "average"; + unit = "W"; + modifier = 1000000; + monotonic = false; + break; + case SENSORS_FEATURE_ENERGY: + title = "Energy"; + input = SENSORS_SUBFEATURE_ENERGY_INPUT; + display = "accumulate"; + unit = "J"; + modifier = 1000000; + monotonic = true; + break; + case SENSORS_FEATURE_CURR: + title = "Current"; + input = SENSORS_SUBFEATURE_CURR_INPUT; + display = "average"; + unit = "A"; + modifier = 1000; + monotonic = false; + break; + case SENSORS_FEATURE_HUMIDITY: + title = "Humidity"; + input = SENSORS_SUBFEATURE_HUMIDITY_INPUT; + display = "average"; + unit = "%"; + modifier = 1000; + monotonic = false; + break; + default: + logg->logError(__FILE__, __LINE__, "Unsupported hwmon feature %i", feature->type); + handleException(); + } +} + +HwmonCounter::~HwmonCounter() { + free((void *)label); + delete [] name; +} + +double HwmonCounter::read() { + double value; + double result; + const sensors_subfeature *subfeature; + + 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; +} + + +Hwmon::Hwmon() : counters(NULL) { + 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, getEventKey(), chip, feature); + } + } +} + +Hwmon::~Hwmon() { + while (counters != NULL) { + HwmonCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } + sensors_cleanup(); +} + +HwmonCounter *Hwmon::findCounter(const Counter &counter) const { + for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { + if (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()); +} + +void Hwmon::writeCounters(mxml_node_t *root) const { + for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + } +} + +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()) { + mxml_node_t *node = mxmlNewElement(root, "event"); + mxmlElementSetAttr(node, "counter", counter->getName()); + mxmlElementSetAttr(node, "title", counter->getTitle()); + mxmlElementSetAttr(node, "name", counter->getLabel()); + mxmlElementSetAttr(node, "display", counter->getDisplay()); + 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/daemon/Hwmon.h b/daemon/Hwmon.h new file mode 100644 index 0000000..35981dc --- /dev/null +++ b/daemon/Hwmon.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) ARM Limited 2013. 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(); + + bool claimCounter(const Counter &counter) const; + bool countersEnabled() const; + void resetCounters(); + void setupCounter(Counter &counter); + + void 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; +}; + +#endif // HWMON_H diff --git a/daemon/KMod.cpp b/daemon/KMod.cpp new file mode 100644 index 0000000..5f12046 --- /dev/null +++ b/daemon/KMod.cpp @@ -0,0 +1,102 @@ +/** + * Copyright (C) ARM Limited 2013. 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 "Collector.h" +#include "Counter.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); + Collector::writeDriver(text, 0); + snprintf(text, sizeof(text), "%s/count", base); + Collector::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 (Collector::writeReadDriver(text, &enabled) || !enabled) { + counter.setEnabled(false); + return; + } + + snprintf(text, sizeof(text), "%s/key", base); + int key; + Collector::readIntDriver(text, &key); + counter.setKey(key); + + snprintf(text, sizeof(text), "%s/event", base); + Collector::writeDriver(text, counter.getEvent()); + if (counter.isEBSCapable()) { + snprintf(text, sizeof(text), "%s/count", base); + if (access(text, F_OK) == 0) { + int count = counter.getCount(); + if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%s with a count of %d\n", counter.getTitle(), counter.getName(), counter.getCount()); + handleException(); + } + counter.setCount(count); + } else if (counter.getCount() > 0) { + logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); + handleException(); + } + } +} + +void 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) { + logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); + handleException(); + } + + 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); + } + closedir(dir); +} diff --git a/daemon/KMod.h b/daemon/KMod.h new file mode 100644 index 0000000..7974262 --- /dev/null +++ b/daemon/KMod.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2013. 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); + + void writeCounters(mxml_node_t *root) const; +}; + +#endif // KMOD_H diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp index 2dd3aff..baa3dd9 100644 --- a/daemon/LocalCapture.cpp +++ b/daemon/LocalCapture.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -30,7 +30,7 @@ void LocalCapture::createAPCDirectory(char* target_path) { } void LocalCapture::write(char* string) { - char* file = (char*)malloc(PATH_MAX); + char file[PATH_MAX]; // Set full path snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir); @@ -40,13 +40,11 @@ void LocalCapture::write(char* string) { logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); handleException(); } - - free(file); } char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending) { char* output; - char* path = (char*)malloc(PATH_MAX); + char path[PATH_MAX]; // Ensure the path is an absolute path, i.e. starts with a slash if (initialPath == 0 || strlen(initialPath) == 0) { @@ -70,7 +68,6 @@ char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* e output = strdup(path); - free(path); return output; } @@ -107,7 +104,7 @@ int LocalCapture::removeDirAndAllContents(char* path) { } void LocalCapture::copyImages(ImageLinkList* ptr) { - char* dstfilename = (char*)malloc(PATH_MAX); + char dstfilename[PATH_MAX]; while (ptr) { strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX); @@ -124,5 +121,4 @@ void LocalCapture::copyImages(ImageLinkList* ptr) { ptr = ptr->next; } - free(dstfilename); } diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h index 40f7883..8042d6a 100644 --- a/daemon/LocalCapture.h +++ b/daemon/LocalCapture.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -9,7 +9,7 @@ #ifndef __LOCAL_CAPTURE_H__ #define __LOCAL_CAPTURE_H__ -#include "SessionXML.h" +struct ImageLinkList; class LocalCapture { public: diff --git a/daemon/Logging.cpp b/daemon/Logging.cpp index 3e6f8a3..5fd45b5 100644 --- a/daemon/Logging.cpp +++ b/daemon/Logging.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -10,7 +10,6 @@ #include #include #include -#include "OlyUtility.h" #ifdef WIN32 #define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL); diff --git a/daemon/Logging.h b/daemon/Logging.h index 37f7dd3..8f960de 100644 --- a/daemon/Logging.h +++ b/daemon/Logging.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -42,6 +42,6 @@ private: extern Logging* logg; -extern void handleException(); +extern void handleException() __attribute__ ((noreturn)); #endif //__LOGGING_H__ diff --git a/daemon/Makefile b/daemon/Makefile index 95d1809..24ee940 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -5,54 +5,21 @@ # 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 it is necessary to add options -# '-marm -march=armv4t -mfloat-abi=soft'. +# targets run 'make SOFTFLOAT=1 SYSROOT=/path/to/sysroot', see +# README_Streamline.txt for more details CPP = $(CROSS_COMPILE)g++ GCC = $(CROSS_COMPILE)gcc -# -g produces debugging information -# -O3 maximum optimization -# -O0 no optimization, used for debugging -# -Wall enables most warnings -# -Werror treats warnings as errors -# -std=c++0x is the planned new c++ standard -# -std=c++98 is the 1998 c++ standard # -mthumb-interwork is required for interworking to ARM or Thumb stdlibc -CFLAGS = -O3 -Wall -mthumb-interwork -fno-exceptions -CXXFLAGS = -fno-rtti -ifeq ($(WERROR),1) - CFLAGS += -Werror -endif -# -s strips the binary of debug info -LDFLAGS = -s -TARGET = gatord -C_SRC = $(wildcard mxml/*.c) -CPP_SRC = $(wildcard *.cpp) - -all: $(TARGET) - -events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml - cat $^ > $@ - -StreamlineSetup.cpp: events_xml.h -ConfigurationXML.cpp: configuration_xml.h - -%_xml.h: %.xml escape - ./escape $< > $@ +CFLAGS += -mthumb-interwork -%.o: %.c *.h - $(GCC) -c $(CFLAGS) -o $@ $< - -%.o: %.cpp *.h - $(CPP) -c $(CFLAGS) $(CXXFLAGS) -o $@ $< - -$(TARGET): $(CPP_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) - $(CPP) $(LDFLAGS) -o $@ $^ -lc -lrt -lpthread - rm -f events_xml.h configuration_xml.h - -escape: escape.c - gcc $^ -o $@ +ifeq ($(SOFTFLOAT),1) + CFLAGS += -marm -march=armv4t -mfloat-abi=soft + LDFLAGS += -marm -march=armv4t -mfloat-abi=soft +endif +ifneq ($(SYSROOT),) + LDFLAGS += --sysroot=$(SYSROOT) +endif -clean: - rm -f *.o mxml/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h +include common.mk diff --git a/daemon/Makefile_aarch64 b/daemon/Makefile_aarch64 new file mode 100644 index 0000000..10b4b4a --- /dev/null +++ b/daemon/Makefile_aarch64 @@ -0,0 +1,15 @@ +# +# 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/arm-linux-gnueabihf- +# NOTE: This toolchain uses the hardfloat abi by default. For non-hardfloat +# targets it is necessary to add options +# '-marm -march=armv4t -mfloat-abi=soft'. + +CPP = $(CROSS_COMPILE)g++ +GCC = $(CROSS_COMPILE)gcc + +include common.mk diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp index 6128dc0..499f68c 100644 --- a/daemon/OlySocket.cpp +++ b/daemon/OlySocket.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h index b306908..5bab7d1 100644 --- a/daemon/OlySocket.h +++ b/daemon/OlySocket.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp index df4fe22..2f85131 100644 --- a/daemon/OlyUtility.cpp +++ b/daemon/OlyUtility.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h index 424c583..5c68a58 100644 --- a/daemon/OlyUtility.h +++ b/daemon/OlyUtility.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/Sender.cpp b/daemon/Sender.cpp index 54f2207..159503f 100644 --- a/daemon/Sender.cpp +++ b/daemon/Sender.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -15,6 +15,7 @@ #include #include "Sender.h" #include "Logging.h" +#include "OlySocket.h" #include "SessionData.h" Sender::Sender(OlySocket* socket) { @@ -69,6 +70,11 @@ void Sender::createDataFile(char* apcDir) { } } +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; @@ -80,7 +86,8 @@ void Sender::writeData(const char* data, int length, int type) { // Send data over the socket connection if (mDataSocket) { // Start alarm - alarm(8); + 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); @@ -89,7 +96,21 @@ void Sender::writeData(const char* data, int length, int type) { mDataSocket->send((char*)&type, 1); mDataSocket->send((char*)&length, sizeof(length)); } - mDataSocket->send((char*)data, length); + + // 100Kbits/sec * alarmDuration sec / 8 bits/byte + const int chunkSize = 100*1000 * alarmDuration / 8; + int pos = 0; + while (true) { + mDataSocket->send((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); diff --git a/daemon/Sender.h b/daemon/Sender.h index ceab343..8f23361 100644 --- a/daemon/Sender.h +++ b/daemon/Sender.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -11,7 +11,8 @@ #include #include -#include "OlySocket.h" + +class OlySocket; enum { RESPONSE_XML = 1, diff --git a/daemon/SessionData.cpp b/daemon/SessionData.cpp index 53a3ea6..4068d4e 100644 --- a/daemon/SessionData.cpp +++ b/daemon/SessionData.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -25,39 +25,19 @@ void SessionData::initialize() { mSessionIsActive = false; mLocalCapture = false; mOneShot = false; - strcpy(mCoreName, "unknown"); + 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; - - initializeCounters(); -} - -void SessionData::initializeCounters() { - // PMU Counters - for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - mPerfCounterType[i][0] = 0; - mPerfCounterTitle[i][0] = 0; - mPerfCounterName[i][0] = 0; - mPerfCounterDescription[i][0] = 0; - mPerfCounterDisplay[i][0] = 0; - mPerfCounterUnits[i][0] = 0; - mPerfCounterEnabled[i] = 0; - mPerfCounterEvent[i] = 0; - mPerfCounterColor[i] = 0; - mPerfCounterKey[i] = 0; - mPerfCounterCount[i] = 0; - mPerfCounterPerCPU[i] = false; - mPerfCounterEBSCapable[i] = false; - mPerfCounterAverageSelection[i] = false; - } } void SessionData::parseSessionXML(char* xmlString) { @@ -66,35 +46,103 @@ void SessionData::parseSessionXML(char* xmlString) { // Set session data values if (strcmp(session.parameters.sample_rate, "high") == 0) { - gSessionData->mSampleRate = 10000; + mSampleRate = 10000; } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { - gSessionData->mSampleRate = 1000; + mSampleRate = 1000; } else if (strcmp(session.parameters.sample_rate, "low") == 0) { - gSessionData->mSampleRate = 100; + mSampleRate = 100; } else if (strcmp(session.parameters.sample_rate, "none") == 0) { - gSessionData->mSampleRate = 0; + mSampleRate = 0; } else { logg->logError(__FILE__, __LINE__, "Invalid sample rate (%s) in session xml.", session.parameters.sample_rate); handleException(); } - gSessionData->mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; - gSessionData->mDuration = session.parameters.duration; + mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; + mDuration = session.parameters.duration; // Determine buffer size (in MB) based on buffer mode - gSessionData->mOneShot = true; + mOneShot = true; if (strcmp(session.parameters.buffer_mode, "streaming") == 0) { - gSessionData->mOneShot = false; - gSessionData->mTotalBufferSize = 1; + mOneShot = false; + mTotalBufferSize = 1; } else if (strcmp(session.parameters.buffer_mode, "small") == 0) { - gSessionData->mTotalBufferSize = 1; + mTotalBufferSize = 1; } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) { - gSessionData->mTotalBufferSize = 4; + mTotalBufferSize = 4; } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { - gSessionData->mTotalBufferSize = 16; + mTotalBufferSize = 16; } else { logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); handleException(); } - gSessionData->mImages = session.parameters.images; + 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"); + mCpuId = -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; + bool foundCpuId = false; + while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) { + if (strlen(temp) > 0) { + temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + } + + const bool foundHardware = strstr(temp, "Hardware") != 0; + const bool foundCPUPart = strstr(temp, "CPU part") != 0; + if (foundHardware || foundCPUPart) { + 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) { + int cpuId = strtol(position, NULL, 16); + if (cpuId > mCpuId) { + mCpuId = cpuId; + } + foundCpuId = true; + } + } + } + + 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); + } + +int getEventKey() { + // Start one after the gator.ko's value of 1 + static int key = 2; + + const int ret = key; + key += 2; + return ret; } diff --git a/daemon/SessionData.h b/daemon/SessionData.h index e0e0b7a..5b6899b 100644 --- a/daemon/SessionData.h +++ b/daemon/SessionData.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -9,11 +9,14 @@ #ifndef SESSION_DATA_H #define SESSION_DATA_H +#include + +#include "Counter.h" +#include "Hwmon.h" + #define MAX_PERFORMANCE_COUNTERS 50 -#define MAX_STRING_LEN 80 -#define MAX_DESCRIPTION_LEN 400 -#define PROTOCOL_VERSION 12 +#define PROTOCOL_VERSION 13 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { @@ -23,12 +26,15 @@ struct ImageLinkList { class SessionData { public: + static const size_t MAX_STRING_LEN = 80; + SessionData(); ~SessionData(); void initialize(); - void initializeCounters(); void parseSessionXML(char* xmlString); + Hwmon hwmon; + char mCoreName[MAX_STRING_LEN]; struct ImageLinkList *mImages; char* mConfigurationXMLPath; @@ -45,27 +51,21 @@ public: int mBacktraceDepth; int mTotalBufferSize; // number of MB to use for the entire collection buffer int mSampleRate; + int64_t mLiveRate; int mDuration; int mCores; + int mCpuId; // PMU Counters bool mCounterOverflow; - char mPerfCounterType[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; - char mPerfCounterTitle[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; - char mPerfCounterName[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; - char mPerfCounterDescription[MAX_PERFORMANCE_COUNTERS][MAX_DESCRIPTION_LEN]; - char mPerfCounterDisplay[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; - char mPerfCounterUnits[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; - int mPerfCounterEnabled[MAX_PERFORMANCE_COUNTERS]; - int mPerfCounterEvent[MAX_PERFORMANCE_COUNTERS]; - int mPerfCounterColor[MAX_PERFORMANCE_COUNTERS]; - int mPerfCounterCount[MAX_PERFORMANCE_COUNTERS]; - int mPerfCounterKey[MAX_PERFORMANCE_COUNTERS]; - bool mPerfCounterPerCPU[MAX_PERFORMANCE_COUNTERS]; - bool mPerfCounterEBSCapable[MAX_PERFORMANCE_COUNTERS]; - bool mPerfCounterAverageSelection[MAX_PERFORMANCE_COUNTERS]; + Counter mCounters[MAX_PERFORMANCE_COUNTERS]; + +private: + void readCpuInfo(); }; extern SessionData* gSessionData; +int getEventKey(); + #endif // SESSION_DATA_H diff --git a/daemon/SessionXML.cpp b/daemon/SessionXML.cpp index b2b6c30..0a0a027 100644 --- a/daemon/SessionXML.cpp +++ b/daemon/SessionXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -12,6 +12,7 @@ #include "SessionXML.h" #include "Logging.h" #include "OlyUtility.h" +#include "SessionData.h" static const char* TAG_SESSION = "session"; static const char* TAG_IMAGE = "image"; @@ -22,12 +23,14 @@ 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 = (char*)str; @@ -78,6 +81,7 @@ void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { // 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); diff --git a/daemon/SessionXML.h b/daemon/SessionXML.h index f7a5641..c7e3798 100644 --- a/daemon/SessionXML.h +++ b/daemon/SessionXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -10,13 +10,15 @@ #define SESSION_XML_H #include "mxml/mxml.h" -#include "SessionData.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 }; diff --git a/daemon/StreamlineSetup.cpp b/daemon/StreamlineSetup.cpp index d13cf1d..88c07a6 100644 --- a/daemon/StreamlineSetup.cpp +++ b/daemon/StreamlineSetup.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -21,6 +19,7 @@ #include "CapturedXML.h" #include "StreamlineSetup.h" #include "ConfigurationXML.h" +#include "Driver.h" static const char* TAG_SESSION = "session"; static const char* TAG_REQUEST = "request"; @@ -30,7 +29,6 @@ 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_SESSION = "session"; static const char* VALUE_CAPTURED = "captured"; static const char* VALUE_DEFAULTS = "defaults"; @@ -40,7 +38,6 @@ StreamlineSetup::StreamlineSetup(OlySocket* s) { int type; mSocket = s; - mSessionXML = NULL; // Receive commands from Streamline (master) while (!ready) { @@ -87,9 +84,6 @@ StreamlineSetup::StreamlineSetup(OlySocket* s) { } StreamlineSetup::~StreamlineSetup() { - if (mSessionXML) { - free(mSessionXML); - } } char* StreamlineSetup::readCommand(int* command) { @@ -162,9 +156,6 @@ void StreamlineSetup::handleRequest(char* xml) { } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) { sendCounters(); logg->logMessage("Sent counters xml response"); - } else if (attr && strcmp(attr, VALUE_SESSION) == 0) { - sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML); - logg->logMessage("Sent session xml response"); } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) { CapturedXML capturedXML; char* capturedText = capturedXML.getXML(false); @@ -191,13 +182,6 @@ void StreamlineSetup::handleDeliver(char* xml) { if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { // Session XML gSessionData->parseSessionXML(xml); - - // Save xml - mSessionXML = strdup(xml); - if (mSessionXML == NULL) { - logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1); - handleException(); - } sendData(NULL, 0, RESPONSE_ACK); logg->logMessage("Received session xml"); } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) { @@ -222,28 +206,43 @@ void StreamlineSetup::sendData(const char* data, int length, int type) { void StreamlineSetup::sendEvents() { #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len - char* path = (char*)malloc(PATH_MAX);; - char* buffer; - unsigned int size = 0; + 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); } - buffer = util->readFromDisk(path, &size); - if (buffer == NULL) { + 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"); - buffer = (char*)events_xml; - size = events_xml_len; + xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); } - sendData(buffer, size, RESPONSE_XML); - if (buffer != (char*)events_xml) { - free(buffer); + // 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); } - free(path); + + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + free(string); + mxmlDelete(xml); } void StreamlineSetup::sendConfiguration() { @@ -268,30 +267,15 @@ void StreamlineSetup::sendDefaults() { sendData(xml, size, RESPONSE_XML); } -#include void StreamlineSetup::sendCounters() { - struct dirent *ent; mxml_node_t *xml; mxml_node_t *counters; - mxml_node_t *counter; - - // counters.xml is simply a file listing of /dev/gator/events - DIR* dir = opendir("/dev/gator/events"); - if (dir == NULL) { - logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); - handleException(); - } xml = mxmlNewXML("1.0"); counters = mxmlNewElement(xml, "counters"); - while ((ent = readdir(dir)) != NULL) { - // skip hidden files, current dir, and parent dir - if (ent->d_name[0] == '.') - continue; - counter = mxmlNewElement(counters, "counter"); - mxmlElementSetAttr(counter, "name", ent->d_name); + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->writeCounters(counters); } - closedir (dir); char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); sendString(string, RESPONSE_XML); @@ -301,7 +285,7 @@ void StreamlineSetup::sendCounters() { } void StreamlineSetup::writeConfiguration(char* xml) { - char* path = (char*)malloc(PATH_MAX); + char path[PATH_MAX]; if (gSessionData->mConfigurationXMLPath) { strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); @@ -316,8 +300,7 @@ void StreamlineSetup::writeConfiguration(char* xml) { } // Re-populate gSessionData with the configuration, as it has now changed - new ConfigurationXML(); - free(path); + { ConfigurationXML configuration; } if (gSessionData->mCounterOverflow) { logg->logError(__FILE__, __LINE__, "Exceeded maximum number of %d performance counters", MAX_PERFORMANCE_COUNTERS); diff --git a/daemon/StreamlineSetup.h b/daemon/StreamlineSetup.h index 8086fe2..841735d 100644 --- a/daemon/StreamlineSetup.h +++ b/daemon/StreamlineSetup.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -28,7 +28,6 @@ public: private: int mNumConnections; OlySocket* mSocket; - char* mSessionXML; char* readCommand(int*); void handleRequest(char* xml); diff --git a/daemon/common.mk b/daemon/common.mk new file mode 100644 index 0000000..112b990 --- /dev/null +++ b/daemon/common.mk @@ -0,0 +1,50 @@ +# -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 +CFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors +CXXFLAGS += -fno-rtti +ifeq ($(WERROR),1) + CFLAGS += -Werror +endif +# -s strips the binary of debug info +LDFLAGS += -s +TARGET = gatord +C_SRC = $(wildcard mxml/*.c) $(wildcard libsensors/*.c) +CPP_SRC = $(wildcard *.cpp) + +all: $(TARGET) + +events.xml: events_header.xml $(wildcard events-*.xml) events_footer.xml + cat $^ > $@ + +include $(wildcard *.d) +include $(wildcard mxml/*.d) + +StreamlineSetup.cpp: events_xml.h +ConfigurationXML.cpp: configuration_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 + $(GCC) -c $(CFLAGS) -o $@ $< + +%.o: %.cpp + $(CPP) -c $(CFLAGS) $(CXXFLAGS) -o $@ $< + +$(TARGET): $(CPP_SRC:%.cpp=%.o) $(C_SRC:%.c=%.o) + $(CPP) $(LDFLAGS) -o $@ $^ -lrt -pthread + +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 configuration_xml.h diff --git a/daemon/configuration.xml b/daemon/configuration.xml index fbdef31..62ccd08 100644 --- a/daemon/configuration.xml +++ b/daemon/configuration.xml @@ -1,41 +1,41 @@ - - + + - + - + - + - + - + - + - - + + - + - + diff --git a/daemon/escape.c b/daemon/escape.c index c0f47f1..a154515 100644 --- a/daemon/escape.c +++ b/daemon/escape.c @@ -1,11 +1,17 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/daemon/events-ARM11.xml b/daemon/events-ARM11.xml index 0a5ee66..9f31313 100644 --- a/daemon/events-ARM11.xml +++ b/daemon/events-ARM11.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/events-ARM11MPCore.xml b/daemon/events-ARM11MPCore.xml index 1a9ca3f..68ca9a5 100644 --- a/daemon/events-ARM11MPCore.xml +++ b/daemon/events-ARM11MPCore.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/events-CCI-400.xml b/daemon/events-CCI-400.xml new file mode 100644 index 0000000..2be0df3 --- /dev/null +++ b/daemon/events-CCI-400.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/events-Cortex-A15.xml b/daemon/events-Cortex-A15.xml index e0bd201..e3de814 100644 --- a/daemon/events-Cortex-A15.xml +++ b/daemon/events-Cortex-A15.xml @@ -1,6 +1,6 @@ - + @@ -19,13 +19,13 @@ - + - + - + @@ -34,7 +34,7 @@ - + @@ -42,7 +42,7 @@ - + diff --git a/daemon/events-Cortex-A5.xml b/daemon/events-Cortex-A5.xml index 4a894d3..77dd838 100644 --- a/daemon/events-Cortex-A5.xml +++ b/daemon/events-Cortex-A5.xml @@ -1,6 +1,6 @@ - + @@ -30,7 +30,7 @@ - - + + diff --git a/daemon/events-Cortex-A53.xml b/daemon/events-Cortex-A53.xml index 3fa9c66..0ffa412 100644 --- a/daemon/events-Cortex-A53.xml +++ b/daemon/events-Cortex-A53.xml @@ -1,7 +1,7 @@ - + diff --git a/daemon/events-Cortex-A57.xml b/daemon/events-Cortex-A57.xml index 5db6dc2..e8c0a61 100644 --- a/daemon/events-Cortex-A57.xml +++ b/daemon/events-Cortex-A57.xml @@ -1,7 +1,7 @@ - + diff --git a/daemon/events-Cortex-A7.xml b/daemon/events-Cortex-A7.xml index 50bba7f..bbd7a26 100644 --- a/daemon/events-Cortex-A7.xml +++ b/daemon/events-Cortex-A7.xml @@ -1,6 +1,6 @@ - + @@ -17,7 +17,6 @@ - @@ -25,8 +24,8 @@ - - + + @@ -37,8 +36,8 @@ - - + + diff --git a/daemon/events-Cortex-A8.xml b/daemon/events-Cortex-A8.xml index fe4c69d..a301f1f 100644 --- a/daemon/events-Cortex-A8.xml +++ b/daemon/events-Cortex-A8.xml @@ -1,6 +1,6 @@ - + @@ -27,7 +27,7 @@ - + diff --git a/daemon/events-Cortex-A9.xml b/daemon/events-Cortex-A9.xml index 89d6a19..9ebb308 100644 --- a/daemon/events-Cortex-A9.xml +++ b/daemon/events-Cortex-A9.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/events-Krait-architected.xml b/daemon/events-Krait-architected.xml index 06c1901..4254666 100644 --- a/daemon/events-Krait-architected.xml +++ b/daemon/events-Krait-architected.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/events-Mali-400.xml b/daemon/events-Mali-400.xml index cb0b9d5..dceccfb 100644 --- a/daemon/events-Mali-400.xml +++ b/daemon/events-Mali-400.xml @@ -1,8 +1,4 @@ - - - - @@ -33,308 +29,110 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + + + + diff --git a/daemon/events-Mali-T6xx_hw.xml b/daemon/events-Mali-T6xx_hw.xml index 825b668..7c7b1ea 100644 --- a/daemon/events-Mali-T6xx_hw.xml +++ b/daemon/events-Mali-T6xx_hw.xml @@ -109,7 +109,7 @@ - + @@ -128,7 +128,7 @@ - + @@ -170,11 +170,11 @@ - + - - - + + + diff --git a/daemon/events-Scorpion.xml b/daemon/events-Scorpion.xml index 1642e85..d08a5a2 100644 --- a/daemon/events-Scorpion.xml +++ b/daemon/events-Scorpion.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/events-ScorpionMP.xml b/daemon/events-ScorpionMP.xml index 309f103..dad4c70 100644 --- a/daemon/events-ScorpionMP.xml +++ b/daemon/events-ScorpionMP.xml @@ -1,6 +1,6 @@ - + diff --git a/daemon/libsensors/COPYING.LGPL b/daemon/libsensors/COPYING.LGPL new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/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/daemon/libsensors/access.c b/daemon/libsensors/access.c new file mode 100644 index 0000000..8e227e2 --- /dev/null +++ b/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/daemon/libsensors/access.h b/daemon/libsensors/access.h new file mode 100644 index 0000000..1d37843 --- /dev/null +++ b/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/daemon/libsensors/conf-lex.c b/daemon/libsensors/conf-lex.c new file mode 100644 index 0000000..a54664b --- /dev/null +++ b/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/daemon/libsensors/conf-lex.l b/daemon/libsensors/conf-lex.l new file mode 100644 index 0000000..43ddbd8 --- /dev/null +++ b/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/daemon/libsensors/conf-parse.c b/daemon/libsensors/conf-parse.c new file mode 100644 index 0000000..fb77546 --- /dev/null +++ b/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/daemon/libsensors/conf-parse.h b/daemon/libsensors/conf-parse.h new file mode 100644 index 0000000..89c9c1a --- /dev/null +++ b/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/daemon/libsensors/conf-parse.y b/daemon/libsensors/conf-parse.y new file mode 100644 index 0000000..1937f54 --- /dev/null +++ b/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/daemon/libsensors/conf.h b/daemon/libsensors/conf.h new file mode 100644 index 0000000..b7ce4f7 --- /dev/null +++ b/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/daemon/libsensors/data.c b/daemon/libsensors/data.c new file mode 100644 index 0000000..cac9c8d --- /dev/null +++ b/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/daemon/libsensors/data.h b/daemon/libsensors/data.h new file mode 100644 index 0000000..4a23eab --- /dev/null +++ b/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/daemon/libsensors/error.c b/daemon/libsensors/error.c new file mode 100644 index 0000000..55bde81 --- /dev/null +++ b/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/daemon/libsensors/error.h b/daemon/libsensors/error.h new file mode 100644 index 0000000..37cdc95 --- /dev/null +++ b/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/daemon/libsensors/general.c b/daemon/libsensors/general.c new file mode 100644 index 0000000..f237e3b --- /dev/null +++ b/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/daemon/libsensors/general.h b/daemon/libsensors/general.h new file mode 100644 index 0000000..a3971e0 --- /dev/null +++ b/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/daemon/libsensors/init.c b/daemon/libsensors/init.c new file mode 100644 index 0000000..558046e --- /dev/null +++ b/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/daemon/libsensors/init.h b/daemon/libsensors/init.h new file mode 100644 index 0000000..47006a6 --- /dev/null +++ b/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/daemon/libsensors/scanner.h b/daemon/libsensors/scanner.h new file mode 100644 index 0000000..4c41516 --- /dev/null +++ b/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/daemon/libsensors/sensors.h b/daemon/libsensors/sensors.h new file mode 100644 index 0000000..7874d02 --- /dev/null +++ b/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/daemon/libsensors/sysfs.c b/daemon/libsensors/sysfs.c new file mode 100644 index 0000000..2b494c9 --- /dev/null +++ b/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/daemon/libsensors/sysfs.h b/daemon/libsensors/sysfs.h new file mode 100644 index 0000000..38584af --- /dev/null +++ b/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/daemon/libsensors/version.h b/daemon/libsensors/version.h new file mode 100644 index 0000000..76ceb08 --- /dev/null +++ b/daemon/libsensors/version.h @@ -0,0 +1 @@ +#define LM_VERSION "3.3.2" diff --git a/daemon/main.cpp b/daemon/main.cpp index 894bad7..a6ddfe2 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -1,15 +1,13 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -26,11 +24,12 @@ #include "OlySocket.h" #include "Logging.h" #include "OlyUtility.h" +#include "KMod.h" #define DEBUG false extern Child* child; -int shutdownFilesystem(); +static int shutdownFilesystem(); static pthread_mutex_t numSessions_mutex; static int numSessions = 0; static OlySocket* socket = NULL; @@ -52,7 +51,7 @@ void cleanUp() { } // CTRL C Signal Handler -void handler(int signum) { +static void handler(int signum) { logg->logMessage("Received signal %d, gator daemon exiting", signum); // Case 1: both child and parent receive the signal @@ -90,7 +89,7 @@ void handler(int signum) { } // Child exit Signal Handler -void child_exit(int signum) { +static void child_exit(int signum) { int status; int pid = wait(&status); if (pid != -1) { @@ -102,7 +101,7 @@ void child_exit(int signum) { } // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted -int mountGatorFS() { +static int mountGatorFS() { // If already mounted, if (access("/dev/gator/buffer", F_OK) == 0) { return 0; @@ -117,7 +116,7 @@ int mountGatorFS() { } } -bool init_module (const char * const location) { +static bool init_module (const char * const location) { bool ret(false); const int fd = open(location, O_RDONLY); if (fd >= 0) { @@ -137,7 +136,7 @@ bool init_module (const char * const location) { return ret; } -int setupFilesystem(char* module) { +static int setupFilesystem(char* module) { int retval; // Verify root permissions @@ -205,7 +204,7 @@ int setupFilesystem(char* module) { return 0; } -int shutdownFilesystem() { +static int shutdownFilesystem() { if (driverMountedAtStart == false) { umount("/dev/gator"); } @@ -221,7 +220,7 @@ int shutdownFilesystem() { return 0; // success } -struct cmdline_t parseCommandLine(int argc, char** argv) { +static struct cmdline_t parseCommandLine(int argc, char** argv) { struct cmdline_t cmdline; cmdline.port = 8080; cmdline.module = NULL; @@ -302,10 +301,13 @@ int main(int argc, char** argv, char* envp[]) { // e.g. it may not be the group leader when launched as 'sudo gatord' setsid(); - gSessionData = new SessionData(); // Global data class 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); diff --git a/driver/Makefile b/driver/Makefile index d22d29d..3af8b8d 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -58,5 +58,6 @@ all: clean: rm -f *.o .*.cmd gator_events.h modules.order Module.symvers gator.ko gator.mod.c + rm -rf .tmp_versions endif diff --git a/driver/gator.h b/driver/gator.h index 9a4617b..205cbcd 100644 --- a/driver/gator.h +++ b/driver/gator.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -18,6 +18,9 @@ #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) + +#define GATOR_LIVE 1 // cpu ids #define ARM1136 0xb36 @@ -42,14 +45,15 @@ #define MAXSIZE_CORE_NAME 32 struct gator_cpu { - const int cpuid; - const char core_name[MAXSIZE_CORE_NAME]; - const char * const pmnc_name; - const int pmnc_counters; - const int ccnt; + const int cpuid; + const char core_name[MAXSIZE_CORE_NAME]; + const char * const pmu_name; + const char * const pmnc_name; + const int pmnc_counters; }; -extern struct gator_cpu gator_cpus[]; +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 @@ -98,10 +102,10 @@ struct gator_interface { int (*create_files)(struct super_block *sb, struct dentry *root); int (*start)(void); void (*stop)(void); // Complementary function to start - int (*online)(int **buffer); - int (*offline)(int **buffer); - void (*online_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' - void (*offline_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' + int (*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); struct list_head list; @@ -118,4 +122,21 @@ u32 gator_cpuid(void); void gator_backtrace_handler(struct pt_regs *const regs); +#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/driver/gator_annotate.c b/driver/gator_annotate.c index 42f9951..ad9f309 100644 --- a/driver/gator_annotate.c +++ b/driver/gator_annotate.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -50,6 +50,7 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t return -EINVAL; } + retry: // synchronize between cores and with collect_annotations spin_lock(&annotate_lock); @@ -74,17 +75,18 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t size = count < available ? count : available; if (size <= 0) { - // Buffer is full but don't return an error. Instead return 0 so the - // caller knows nothing was written and they can try again. - size = 0; - goto annotate_write_out; + // Buffer is full, wait until space is available + spin_unlock(&annotate_lock); + wait_event_interruptible(gator_annotate_wait, buffer_bytes_available(cpu, ANNOTATE_BUF) > header_size || !collect_annotations); + goto retry; } // synchronize shared variables annotateBuf and annotatePos if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { - gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, smp_processor_id()); + 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, gator_get_time()); + 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 @@ -108,7 +110,7 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, ANNOTATE_BUF); + buffer_check(cpu, ANNOTATE_BUF, time); } annotate_write_out: @@ -129,14 +131,14 @@ static int annotate_release(struct inode *inode, struct file *file) 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, smp_processor_id()); + 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); + buffer_check(cpu, ANNOTATE_BUF, gator_get_time()); spin_unlock(&annotate_lock); @@ -164,5 +166,6 @@ 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/driver/gator_annotate_kernel.c b/driver/gator_annotate_kernel.c index 67d2d6c..4715f64 100644 --- a/driver/gator_annotate_kernel.c +++ b/driver/gator_annotate_kernel.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c index e6125b3..94f01e6 100644 --- a/driver/gator_backtrace.c +++ b/driver/gator_backtrace.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -11,8 +11,17 @@ * EABI backtrace stores {fp,lr} on the stack. */ struct frame_tail_eabi { - unsigned long fp; // points to prev_lr - unsigned long lr; + union { + struct { + unsigned long fp; // points to prev_lr + unsigned long lr; + }; + // Used to read 32 bit fp/lr from a 64 bit kernel + struct { + u32 fp_32; + u32 lr_32; + }; + }; }; static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) @@ -20,18 +29,20 @@ static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int #if defined(__arm__) || defined(__aarch64__) struct frame_tail_eabi *tail; struct frame_tail_eabi *next; - struct frame_tail_eabi *ptrtail; struct frame_tail_eabi buftail; #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 frame_offset = 4; #else - unsigned long fp = regs->regs[29]; - unsigned long sp = regs->sp; - unsigned long lr = regs->regs[30]; - const int frame_offset = 0; + // 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 frame_offset = (is_compat ? 4 : 0); #endif int is_user_mode = user_mode(regs); @@ -55,15 +66,14 @@ static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int return; if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi))) return; - ptrtail = &buftail; - lr = ptrtail[0].lr; + lr = (is_compat ? buftail.lr_32 : buftail.lr); gator_add_trace(cpu, lr); /* frame pointers should progress back up the stack, towards higher addresses */ next = (struct frame_tail_eabi *)(lr - frame_offset); if (tail >= next || lr == 0) { - fp = ptrtail[0].fp; + fp = (is_compat ? buftail.fp_32 : buftail.fp); next = (struct frame_tail_eabi *)(fp - frame_offset); /* check tail is valid */ if (tail >= next || fp == 0) { @@ -79,16 +89,17 @@ static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int #if defined(__arm__) || defined(__aarch64__) static int report_trace(struct stackframe *frame, void *d) { - struct module *mod; - unsigned int *depth = d, cookie = NO_COOKIE, cpu = smp_processor_id(); + unsigned int *depth = d, cookie = NO_COOKIE, cpu = get_physical_cpu(); unsigned long addr = frame->pc; if (*depth) { - mod = __module_address(addr); +#if defined(MODULE) + 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); (*depth)--; } diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c index bb401bb..c332187 100644 --- a/driver/gator_cookies.c +++ b/driver/gator_cookies.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -61,7 +61,7 @@ static uint32_t gator_chksum_crc32(const char *data) static uint32_t cookiemap_exists(uint64_t key) { unsigned long x, flags, retval = 0; - int cpu = smp_processor_id(); + 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]); @@ -93,7 +93,7 @@ static uint32_t cookiemap_exists(uint64_t key) */ static void cookiemap_add(uint64_t key, uint32_t value) { - int cpu = smp_processor_id(); + 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]); @@ -124,7 +124,7 @@ static void wq_cookie_handler(struct work_struct *unused) { struct task_struct *task; char *text; - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); unsigned int commit; mutex_lock(&start_mutex); diff --git a/driver/gator_events_armv6.c b/driver/gator_events_armv6.c index ee36dd0..4f1bca6 100644 --- a/driver/gator_events_armv6.c +++ b/driver/gator_events_armv6.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -93,7 +93,7 @@ int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) return 0; } -static int gator_events_armv6_online(int **buffer) +static int gator_events_armv6_online(int **buffer, bool migrate) { unsigned int cnt, len = 0, cpu = smp_processor_id(); u32 pmnc; @@ -141,7 +141,7 @@ static int gator_events_armv6_online(int **buffer) return len; } -static int gator_events_armv6_offline(int **buffer) +static int gator_events_armv6_offline(int **buffer, bool migrate) { unsigned int cnt; diff --git a/driver/gator_events_armv7.c b/driver/gator_events_armv7.c index 212b17b..58f2956 100644 --- a/driver/gator_events_armv7.c +++ b/driver/gator_events_armv7.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -159,7 +159,7 @@ static int gator_events_armv7_create_files(struct super_block *sb, struct dentry return 0; } -static int gator_events_armv7_online(int **buffer) +static int gator_events_armv7_online(int **buffer, bool migrate) { unsigned int cnt, len = 0, cpu = smp_processor_id(); @@ -214,7 +214,7 @@ static int gator_events_armv7_online(int **buffer) return len; } -static int gator_events_armv7_offline(int **buffer) +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); diff --git a/driver/gator_events_block.c b/driver/gator_events_block.c index f512b13..56c6a67 100644 --- a/driver/gator_events_block.c +++ b/driver/gator_events_block.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -30,7 +30,6 @@ static int blockGet[BLOCK_TOTAL * 4]; GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) { - unsigned long flags; int write, size; if (!rq) @@ -42,9 +41,6 @@ GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct r if (!size) return; - // disable interrupts to synchronize with gator_events_block_read() - // spinlocks not needed since percpu buffers are used - local_irq_save(flags); if (write) { if (block_rq_wr_enabled) { atomic_add(size, &blockCnt[BLOCK_RQ_WR]); @@ -54,7 +50,6 @@ GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct r atomic_add(size, &blockCnt[BLOCK_RQ_RD]); } } - local_irq_restore(flags); } static int gator_events_block_create_files(struct super_block *sb, struct dentry *root) @@ -111,7 +106,7 @@ static int gator_events_block_read(int **buffer) { int len, value, data = 0; - if (smp_processor_id() != 0) { + if (!on_primary_core()) { return 0; } diff --git a/driver/gator_events_irq.c b/driver/gator_events_irq.c index 1221372..b4df7fa 100644 --- a/driver/gator_events_irq.c +++ b/driver/gator_events_irq.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -18,19 +18,13 @@ static ulong hardirq_enabled; static ulong softirq_enabled; static ulong hardirq_key; static ulong softirq_key; -static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); +static DEFINE_PER_CPU(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)) { - unsigned long flags; - - // disable interrupts to synchronize with gator_events_irq_read() - // spinlocks not needed since percpu buffers are used - local_irq_save(flags); - per_cpu(irqCnt, smp_processor_id())[HARDIRQ]++; - local_irq_restore(flags); + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[HARDIRQ]); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) @@ -39,13 +33,7 @@ GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softi GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) #endif { - unsigned long flags; - - // disable interrupts to synchronize with gator_events_irq_read() - // spinlocks not needed since percpu buffers are used - local_irq_save(flags); - per_cpu(irqCnt, smp_processor_id())[SOFTIRQ]++; - local_irq_restore(flags); + atomic_inc(&per_cpu(irqCnt, get_physical_cpu())[SOFTIRQ]); } static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root) @@ -71,24 +59,19 @@ static int gator_events_irq_create_files(struct super_block *sb, struct dentry * return 0; } -static int gator_events_irq_online(int **buffer) +static int gator_events_irq_online(int **buffer, bool migrate) { - int len = 0, cpu = smp_processor_id(); - unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt + 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) { - local_irq_save(flags); - per_cpu(irqCnt, cpu)[HARDIRQ] = 0; - local_irq_restore(flags); + atomic_set(&per_cpu(irqCnt, cpu)[HARDIRQ], 0); per_cpu(irqGet, cpu)[len++] = hardirq_key; per_cpu(irqGet, cpu)[len++] = 0; } if (softirq_enabled) { - local_irq_save(flags); - per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; - local_irq_restore(flags); + atomic_set(&per_cpu(irqCnt, cpu)[SOFTIRQ], 0); per_cpu(irqGet, cpu)[len++] = softirq_key; per_cpu(irqGet, cpu)[len++] = 0; } @@ -136,26 +119,21 @@ static void gator_events_irq_stop(void) static int gator_events_irq_read(int **buffer) { - unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt int len, value; - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); len = 0; if (hardirq_enabled) { - local_irq_save(flags); - value = per_cpu(irqCnt, cpu)[HARDIRQ]; - per_cpu(irqCnt, cpu)[HARDIRQ] = 0; - local_irq_restore(flags); + 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) { - local_irq_save(flags); - value = per_cpu(irqCnt, cpu)[SOFTIRQ]; - per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; - local_irq_restore(flags); + 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; diff --git a/driver/gator_events_l2c-310.c b/driver/gator_events_l2c-310.c index 197af04..52472c7 100644 --- a/driver/gator_events_l2c-310.c +++ b/driver/gator_events_l2c-310.c @@ -1,7 +1,7 @@ /** * l2c310 (L2 Cache Controller) event counters for gator * - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -10,6 +10,7 @@ #include #include +#include #include #include "gator.h" @@ -95,7 +96,7 @@ static int gator_events_l2c310_read(int **buffer) int i; int len = 0; - if (smp_processor_id()) + if (!on_primary_core()) return 0; for (i = 0; i < L2C310_COUNTERS_NUM; i++) { @@ -122,20 +123,48 @@ static struct gator_interface gator_events_l2c310_interface = { .read = gator_events_l2c310_read, }; -static void __maybe_unused gator_events_l2c310_probe(unsigned long phys) +#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) { - if (l2c310_base) - return; + 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; - l2c310_base = ioremap(phys, SZ_4K); - if (l2c310_base) { - u32 cache_id = readl(l2c310_base + L2X0_CACHE_ID); + for (i = 0; i < ARRAY_SIZE(variants); i++) { + void __iomem *base = ioremap(variants[i], SZ_4K); - if ((cache_id & 0xff0003c0) != 0x410000c0) { - iounmap(l2c310_base); - l2c310_base = NULL; + 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) @@ -145,24 +174,11 @@ int gator_events_l2c310_init(void) if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9) return -1; -#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310) - gator_events_l2c310_probe(0x10502000); -#endif -#if defined(CONFIG_ARCH_OMAP4) - gator_events_l2c310_probe(0x48242000); -#endif -#if defined(CONFIG_ARCH_TEGRA) - gator_events_l2c310_probe(0x50043000); -#endif -#if defined(CONFIG_ARCH_U8500) - gator_events_l2c310_probe(0xa0412000); -#endif -#if defined(CONFIG_ARCH_VEXPRESS) - // A9x4 core tile (HBI-0191) - gator_events_l2c310_probe(0x1e00a000); - // New memory map tiles - gator_events_l2c310_probe(0x2c0f0000); -#endif + if (l2c310_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; diff --git a/driver/gator_events_mali_400.c b/driver/gator_events_mali_400.c index 34a73c8..38c97d1 100644 --- a/driver/gator_events_mali_400.c +++ b/driver/gator_events_mali_400.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -630,7 +630,7 @@ static int read(int **buffer) { int cnt, len = 0; - if (smp_processor_id()) + if (!on_primary_core()) return 0; // Read the L2 C0 and C1 here. diff --git a/driver/gator_events_mali_400.h b/driver/gator_events_mali_400.h index a09757e..43aec49 100644 --- a/driver/gator_events_mali_400.h +++ b/driver/gator_events_mali_400.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 diff --git a/driver/gator_events_mali_common.c b/driver/gator_events_mali_common.c index 2186eee..22a517d 100644 --- a/driver/gator_events_mali_common.c +++ b/driver/gator_events_mali_common.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 diff --git a/driver/gator_events_mali_common.h b/driver/gator_events_mali_common.h index 8e33edf..27eaacc 100644 --- a/driver/gator_events_mali_common.h +++ b/driver/gator_events_mali_common.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 diff --git a/driver/gator_events_mali_t6xx.c b/driver/gator_events_mali_t6xx.c index 1b3a53d..2576a99 100644 --- a/driver/gator_events_mali_t6xx.c +++ b/driver/gator_events_mali_t6xx.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 @@ -411,7 +411,7 @@ static int read(int **buffer) long sample_interval_us = 0; struct timespec read_timestamp; - if (smp_processor_id() != 0) { + if (!on_primary_core()) { return 0; } diff --git a/driver/gator_events_mali_t6xx_hw.c b/driver/gator_events_mali_t6xx_hw.c index 72498c8..fb2e15c 100644 --- a/driver/gator_events_mali_t6xx_hw.c +++ b/driver/gator_events_mali_t6xx_hw.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 @@ -608,7 +608,7 @@ static int read(int **buffer) static u32 prev_time_s = 0; static s32 next_read_time_ns = 0; - if (smp_processor_id() != 0) { + if (!on_primary_core()) { return 0; } diff --git a/driver/gator_events_mali_t6xx_hw_test.c b/driver/gator_events_mali_t6xx_hw_test.c index eb77110..efb32dd 100644 --- a/driver/gator_events_mali_t6xx_hw_test.c +++ b/driver/gator_events_mali_t6xx_hw_test.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 diff --git a/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c index fd063b2..c1e360d 100644 --- a/driver/gator_events_meminfo.c +++ b/driver/gator_events_meminfo.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -198,7 +198,7 @@ static int gator_events_meminfo_read(long long **buffer) { static unsigned int last_mem_event = 0; - if (smp_processor_id() || !meminfo_global_enabled) + if (!on_primary_core() || !meminfo_global_enabled) return 0; if (last_mem_event != mem_event) { diff --git a/driver/gator_events_mmaped.c b/driver/gator_events_mmaped.c index c4cb44f..0027564 100644 --- a/driver/gator_events_mmaped.c +++ b/driver/gator_events_mmaped.c @@ -1,7 +1,7 @@ /* * Example events provider * - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -141,7 +141,7 @@ static int mmaped_simulate(int counter, int delta_in_us) break; case 2: /* PWM signal */ { - static int t, dc, x; + static int dc, x, t = 0; t += delta_in_us; if (t > 1000000) @@ -170,7 +170,7 @@ static int gator_events_mmaped_read(int **buffer) #endif /* System wide counters - read from one core only */ - if (smp_processor_id()) + if (!on_primary_core()) return 0; #ifndef TODO diff --git a/driver/gator_events_net.c b/driver/gator_events_net.c index b6ce06a..80cdee4 100644 --- a/driver/gator_events_net.c +++ b/driver/gator_events_net.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -117,7 +117,7 @@ static int gator_events_net_read(int **buffer) int len, rx_delta, tx_delta; static int last_rx_delta = 0, last_tx_delta = 0; - if (smp_processor_id() != 0) + if (!on_primary_core()) return 0; if (!netrx_enabled && !nettx_enabled) diff --git a/driver/gator_events_perf_pmu.c b/driver/gator_events_perf_pmu.c index ce3a40f..34a6bc7 100644 --- a/driver/gator_events_perf_pmu.c +++ b/driver/gator_events_perf_pmu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -13,49 +13,74 @@ // gator_events_armvX.c is used for Linux 2.6.x #if GATOR_PERF_PMU_SUPPORT -static const char *pmnc_name; -int pmnc_counters; -int ccnt = 0; +extern bool event_based_sampling; -#define CNTMAX (6+1) +#define CNTMAX 16 +#define CCI_400 4 +// + 1 for the cci-400 cycles counter +#define UCCNT (CCI_400 + 1) -static DEFINE_MUTEX(perf_mutex); +struct gator_attr { + char name[40]; + unsigned long enabled; + unsigned long type; + unsigned long event; + unsigned long count; + unsigned long key; +}; -unsigned long pmnc_enabled[CNTMAX]; -unsigned long pmnc_event[CNTMAX]; -unsigned long pmnc_count[CNTMAX]; -unsigned long pmnc_key[CNTMAX]; +static struct gator_attr attrs[CNTMAX]; +static int attr_count; +static struct gator_attr uc_attrs[UCCNT]; +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(int[CNTMAX], perfCurr); -static DEFINE_PER_CPU(int[CNTMAX], perfPrev); -static DEFINE_PER_CPU(int[CNTMAX], perfPrevDelta); -static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); -static DEFINE_PER_CPU(struct perf_event *[CNTMAX], pevent); -static DEFINE_PER_CPU(struct perf_event_attr *[CNTMAX], pevent_attr); +static 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 gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +static int __create_files(struct super_block *sb, struct dentry *root, struct gator_attr *const attr) { 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) { + 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; } - gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); - gatorfs_create_ulong(sb, dir, "count", &pmnc_count[i]); - gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); - if (i > 0) { - gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + + for (cnt = 0; cnt < uc_attr_count; cnt++) { + if (__create_files(sb, root, &uc_attrs[cnt]) != 0) { + return -1; } } @@ -80,177 +105,268 @@ static void dummy_handler(struct perf_event *event, struct perf_sample_data *dat // Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll } -static int gator_events_perf_pmu_online(int **buffer) +static int gator_events_perf_pmu_read(int **buffer); + +static int gator_events_perf_pmu_online(int **buffer, bool migrate) { - int cnt, len = 0, cpu = smp_processor_id(); + return gator_events_perf_pmu_read(buffer); +} - // read the counters and toss the invalid data, return zero instead - for (cnt = 0; cnt < pmnc_counters; cnt++) { - struct perf_event *ev = per_cpu(pevent, cpu)[cnt]; - if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { - ev->pmu->read(ev); - per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); - per_cpu(perfPrevDelta, cpu)[cnt] = 0; - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = 0; - } +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 (buffer) - *buffer = per_cpu(perfCnt, cpu); + if (attr->count > 0) { + handler = ebs_overflow_handler; + } else { + handler = dummy_handler; + } - return len; +#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) +static void gator_events_perf_pmu_online_dispatch(int cpu, bool migrate) { int cnt; - perf_overflow_handler_t handler; - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (per_cpu(pevent, cpu)[cnt] != NULL || per_cpu(pevent_attr, cpu)[cnt] == 0) - continue; + cpu = pcpu_to_lcpu(cpu); - if (pmnc_count[cnt] > 0) { - handler = ebs_overflow_handler; - } else { - handler = dummy_handler; - } + for (cnt = 0; cnt < attr_count; cnt++) { + __online_dispatch(cpu, migrate, &attrs[cnt], &per_cpu(events, cpu)[cnt]); + } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, handler); -#else - per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, handler, 0); -#endif - if (IS_ERR(per_cpu(pevent, cpu)[cnt])) { - pr_debug("gator: unable to online a counter on cpu %d\n", cpu); - per_cpu(pevent, cpu)[cnt] = NULL; - continue; + if (cpu == 0) { + for (cnt = 0; cnt < uc_attr_count; cnt++) { + __online_dispatch(cpu, migrate, &uc_attrs[cnt], &uc_events[cnt]); } + } +} - if (per_cpu(pevent, cpu)[cnt]->state != PERF_EVENT_STATE_ACTIVE) { - pr_debug("gator: inactive counter on cpu %d\n", cpu); - perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]); - per_cpu(pevent, cpu)[cnt] = NULL; - continue; - } +static void __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) +static void gator_events_perf_pmu_offline_dispatch(int cpu, bool migrate) { int cnt; - struct perf_event *pe; - for (cnt = 0; cnt < pmnc_counters; cnt++) { - pe = NULL; - mutex_lock(&perf_mutex); - if (per_cpu(pevent, cpu)[cnt]) { - pe = per_cpu(pevent, cpu)[cnt]; - per_cpu(pevent, cpu)[cnt] = NULL; + 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]); } - mutex_unlock(&perf_mutex); + } +} - if (pe) { - perf_event_release_kernel(pe); +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; - u32 size = sizeof(struct perf_event_attr); - int found_ebs = false; - - for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (pmnc_count[cnt] > 0) { - if (!found_ebs) { - found_ebs = true; - } else { - // Only one ebs counter is allowed - return -1; - } + + 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 < pmnc_counters; cnt++) { - per_cpu(pevent, cpu)[cnt] = NULL; - if (!pmnc_enabled[cnt]) // Skip disabled counters - continue; - - per_cpu(perfPrev, cpu)[cnt] = 0; - per_cpu(perfCurr, cpu)[cnt] = 0; - per_cpu(perfPrevDelta, cpu)[cnt] = 0; - per_cpu(pevent_attr, cpu)[cnt] = kmalloc(size, GFP_KERNEL); - if (!per_cpu(pevent_attr, cpu)[cnt]) { - gator_events_perf_pmu_stop(); + for (cnt = 0; cnt < attr_count; cnt++) { + if (__start(&attrs[cnt], &per_cpu(events, cpu)[cnt]) != 0) { return -1; } + } + } - memset(per_cpu(pevent_attr, cpu)[cnt], 0, size); - per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_RAW; - per_cpu(pevent_attr, cpu)[cnt]->size = size; - per_cpu(pevent_attr, cpu)[cnt]->config = pmnc_event[cnt]; - per_cpu(pevent_attr, cpu)[cnt]->sample_period = pmnc_count[cnt]; - per_cpu(pevent_attr, cpu)[cnt]->pinned = 1; - - // handle special case for ccnt - if (cnt == ccnt) { - per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_HARDWARE; - per_cpu(pevent_attr, cpu)[cnt]->config = PERF_COUNT_HW_CPU_CYCLES; - } + 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 < pmnc_counters; cnt++) { - if (per_cpu(pevent_attr, cpu)[cnt]) { - kfree(per_cpu(pevent_attr, cpu)[cnt]); - per_cpu(pevent_attr, cpu)[cnt] = NULL; - } + for (cnt = 0; cnt < attr_count; cnt++) { + __event_stop(&per_cpu(events, cpu)[cnt]); } } - for (cnt = 0; cnt < pmnc_counters; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - pmnc_count[cnt] = 0; + 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 int gator_events_perf_pmu_read(int **buffer) +static void __read(int *const len, int cpu, struct gator_attr *const attr, struct gator_event *const event) { - int cnt, delta, len = 0; - int cpu = smp_processor_id(); - - for (cnt = 0; cnt < pmnc_counters; cnt++) { - struct perf_event *ev = per_cpu(pevent, cpu)[cnt]; - if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + 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); - per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); - delta = per_cpu(perfCurr, cpu)[cnt] - per_cpu(perfPrev, cpu)[cnt]; - if (delta != 0 || delta != per_cpu(perfPrevDelta, cpu)[cnt]) { - per_cpu(perfPrevDelta, cpu)[cnt] = delta; - per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt]; - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - if (delta < 0) + 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(perfCnt, cpu)[len++] = delta; + } + per_cpu(perf_cnt, cpu)[(*len)++] = delta; } } } +} - if (buffer) - *buffer = per_cpu(perfCnt, cpu); +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; } @@ -265,30 +381,116 @@ static struct gator_interface gator_events_perf_pmu_interface = { .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(); +} + +static void gator_events_perf_pmu_cci_init(const int type) +{ + int cnt; + + strncpy(uc_attrs[uc_attr_count].name, "cci-400_ccnt", sizeof(uc_attrs[uc_attr_count].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), "cci-400_cnt%d", 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) { - unsigned int cnt; - const u32 cpuid = gator_cpuid(); - - for (cnt = 0; gator_cpus[cnt].cpuid != 0; ++cnt) { - if (gator_cpus[cnt].cpuid == cpuid) { - pmnc_name = gator_cpus[cnt].pmnc_name; - pmnc_counters = gator_cpus[cnt].pmnc_counters; - ccnt = gator_cpus[cnt].ccnt; + 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 contigious + if (IS_ERR(pe)) { break; } + + if (pe->pmu != NULL && type == pe->pmu->type) { + if (strcmp("CCI", 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); + } + } + + perf_event_release_kernel(pe); } - if (gator_cpus[cnt].cpuid == 0) { - return -1; + + 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); } - pmnc_counters++; // CNT[n] + CCNT + if (attr_count > CNTMAX) { + printk(KERN_ERR "gator: Too many perf counters\n"); + return -1; + } - for (cnt = 0; cnt < CNTMAX; cnt++) { - pmnc_enabled[cnt] = 0; - pmnc_event[cnt] = 0; - pmnc_count[cnt] = 0; - pmnc_key[cnt] = gator_events_get_key(); + 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); diff --git a/driver/gator_events_sched.c b/driver/gator_events_sched.c index ba6744d..461a051 100644 --- a/driver/gator_events_sched.c +++ b/driver/gator_events_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -29,7 +29,7 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ // disable interrupts to synchronize with gator_events_sched_read() // spinlocks not needed since percpu buffers are used local_irq_save(flags); - per_cpu(schedCnt, smp_processor_id())[SCHED_SWITCH]++; + per_cpu(schedCnt, get_physical_cpu())[SCHED_SWITCH]++; local_irq_restore(flags); } @@ -78,7 +78,7 @@ static int gator_events_sched_read(int **buffer) { unsigned long flags; int len, value; - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); len = 0; if (sched_switch_enabled) { diff --git a/driver/gator_events_scorpion.c b/driver/gator_events_scorpion.c index 5ffc63a..aaf306a 100644 --- a/driver/gator_events_scorpion.c +++ b/driver/gator_events_scorpion.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 @@ -524,7 +524,7 @@ static int gator_events_scorpion_create_files(struct super_block *sb, struct den return 0; } -static int gator_events_scorpion_online(int **buffer) +static int gator_events_scorpion_online(int **buffer, bool migrate) { unsigned int cnt, len = 0, cpu = smp_processor_id(); @@ -581,7 +581,7 @@ static int gator_events_scorpion_online(int **buffer) return len; } -static int gator_events_scorpion_offline(int **buffer) +static int gator_events_scorpion_offline(int **buffer, bool migrate) { scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); return 0; diff --git a/driver/gator_fs.c b/driver/gator_fs.c index 9ff118b..fe6f83d 100644 --- a/driver/gator_fs.c +++ b/driver/gator_fs.c @@ -53,6 +53,15 @@ ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); } +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); +} + int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) { char tmpbuf[TMPBUFSIZE]; @@ -75,12 +84,40 @@ int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t c return 0; } +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; @@ -96,6 +133,21 @@ static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_ 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) @@ -109,11 +161,22 @@ static const struct file_operations ulong_fops = { .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, @@ -148,6 +211,18 @@ int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, return 0; } +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) { @@ -160,6 +235,18 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, return 0; } +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; diff --git a/driver/gator_hrtimer_gator.c b/driver/gator_hrtimer_gator.c index 846fba4..8c35d49 100644 --- a/driver/gator_hrtimer_gator.c +++ b/driver/gator_hrtimer_gator.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 @@ -15,8 +15,8 @@ void (*callback)(void); DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); DEFINE_PER_CPU(int, hrtimer_is_active); static ktime_t profiling_interval; -static void gator_hrtimer_online(int cpu); -static void gator_hrtimer_offline(int cpu); +static void gator_hrtimer_online(void); +static void gator_hrtimer_offline(void); static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) { @@ -25,43 +25,28 @@ static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) return HRTIMER_RESTART; } -static void gator_hrtimer_switch_cpus_online(void *unused) -{ - gator_hrtimer_online(smp_processor_id()); -} - -static void gator_hrtimer_online(int cpu) +static void gator_hrtimer_online(void) { + int cpu = get_logical_cpu(); struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); - if (cpu != smp_processor_id()) { - smp_call_function_single(cpu, gator_hrtimer_switch_cpus_online, NULL, 1); - return; - } - if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0) return; per_cpu(hrtimer_is_active, cpu) = 1; hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer->function = gator_hrtimer_notify; +#ifdef CONFIG_PREEMPT_RT_BASE + hrtimer->irqsafe = 1; +#endif hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED); } -static void gator_hrtimer_switch_cpus_offline(void *unused) -{ - gator_hrtimer_offline(smp_processor_id()); -} - -static void gator_hrtimer_offline(int cpu) +static void gator_hrtimer_offline(void) { + int cpu = get_logical_cpu(); struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); - if (cpu != smp_processor_id()) { - smp_call_function_single(cpu, gator_hrtimer_switch_cpus_offline, NULL, 1); - return; - } - if (!per_cpu(hrtimer_is_active, cpu)) return; diff --git a/driver/gator_hrtimer_perf.c b/driver/gator_hrtimer_perf.c index 7c0333f..7b95399 100644 --- a/driver/gator_hrtimer_perf.c +++ b/driver/gator_hrtimer_perf.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 diff --git a/driver/gator_iks.c b/driver/gator_iks.c new file mode 100644 index 0000000..932be26 --- /dev/null +++ b/driver/gator_iks.c @@ -0,0 +1,144 @@ +/** + * Copyright (C) ARM Limited 2013. 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 int mpidr_cpuids[NR_CPUS]; +static int __lcpu_to_pcpu[NR_CPUS]; + +static void calc_first_cluster_size(void) +{ + int len; + const u32 *val; + 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)); + + 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; + } + + mpidr_cpuids[mpidr_cpuids_count] = be32_to_cpup(val); + ++mpidr_cpuids_count; + } + + BUG_ON(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; + 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; + 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 int gator_migrate_start(void) +{ + int retval = 0; + 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) +{ + 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_migrate_start() 0 +#define gator_migrate_stop() + +#endif diff --git a/driver/gator_main.c b/driver/gator_main.c index 9e031c1..3e62b59 100644 --- a/driver/gator_main.c +++ b/driver/gator_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -7,7 +7,8 @@ * */ -static unsigned long gator_protocol_version = 12; +// This version must match the gator daemon version +static unsigned long gator_protocol_version = 13; #include #include @@ -20,6 +21,7 @@ static unsigned long gator_protocol_version = 12; #include #include #include +#include #include #include @@ -92,9 +94,14 @@ static unsigned long gator_protocol_version = 12; #define MESSAGE_SCHED_SWITCH 1 #define MESSAGE_SCHED_EXIT 2 +#define MESSAGE_IDLE_ENTER 1 +#define MESSAGE_IDLE_EXIT 2 + #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__) @@ -123,23 +130,35 @@ 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 uint64_t monotonic_started; +static u64 gator_monotonic_started; static unsigned long gator_buffer_opened; static unsigned long gator_timer_count; static unsigned long gator_response_type; static DEFINE_MUTEX(start_mutex); static DEFINE_MUTEX(gator_buffer_mutex); +bool event_based_sampling; + static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); +static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); static struct timer_list gator_buffer_wake_up_timer; static LIST_HEAD(gator_events); +static DEFINE_PER_CPU(u64, last_timestamp); + +static bool printed_monotonic_warning; + +static bool sent_core_name[NR_CPUS]; + /****************************************************************************** * Prototypes ******************************************************************************/ -static void buffer_check(int cpu, int buftype); +static void buffer_check(int cpu, int buftype, u64 time); +static void gator_commit_buffer(int cpu, int buftype, u64 time); static int buffer_bytes_available(int cpu, int buftype); static bool buffer_check_space(int cpu, int buftype, int bytes); static int contiguous_space_available(int cpu, int bufytpe); @@ -149,7 +168,7 @@ static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int le static void gator_buffer_write_string(int cpu, int buftype, const char *x); static void gator_add_trace(int cpu, unsigned long address); static void gator_add_sample(int cpu, struct pt_regs *const regs); -static uint64_t gator_get_time(void); +static u64 gator_get_time(void); // 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]; @@ -167,6 +186,11 @@ 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); +#if GATOR_LIVE +// The time after which the buffer should be committed for live display +static DEFINE_PER_CPU(u64, gator_buffer_commit_time); +#endif + /****************************************************************************** * Application Includes ******************************************************************************/ @@ -186,83 +210,85 @@ static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); * Misc ******************************************************************************/ -struct gator_cpu gator_cpus[] = { - { +const struct gator_cpu gator_cpus[] = { + { .cpuid = ARM1136, .core_name = "ARM1136", .pmnc_name = "ARM_ARM11", .pmnc_counters = 3, - .ccnt = 2, }, - { + { .cpuid = ARM1156, .core_name = "ARM1156", .pmnc_name = "ARM_ARM11", .pmnc_counters = 3, - .ccnt = 2, }, - { + { .cpuid = ARM1176, .core_name = "ARM1176", .pmnc_name = "ARM_ARM11", .pmnc_counters = 3, - .ccnt = 2, }, - { + { .cpuid = ARM11MPCORE, .core_name = "ARM11MPCore", .pmnc_name = "ARM_ARM11MPCore", .pmnc_counters = 3, }, - { + { .cpuid = CORTEX_A5, .core_name = "Cortex-A5", + .pmu_name = "ARMv7_Cortex_A5", .pmnc_name = "ARM_Cortex-A5", .pmnc_counters = 2, }, - { + { .cpuid = CORTEX_A7, .core_name = "Cortex-A7", + .pmu_name = "ARMv7_Cortex_A7", .pmnc_name = "ARM_Cortex-A7", .pmnc_counters = 4, }, - { + { .cpuid = CORTEX_A8, .core_name = "Cortex-A8", + .pmu_name = "ARMv7_Cortex_A8", .pmnc_name = "ARM_Cortex-A8", .pmnc_counters = 4, }, - { + { .cpuid = CORTEX_A9, .core_name = "Cortex-A9", + .pmu_name = "ARMv7_Cortex_A9", .pmnc_name = "ARM_Cortex-A9", .pmnc_counters = 6, }, - { + { .cpuid = CORTEX_A15, .core_name = "Cortex-A15", + .pmu_name = "ARMv7_Cortex_A15", .pmnc_name = "ARM_Cortex-A15", .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", @@ -274,19 +300,19 @@ struct gator_cpu gator_cpus[] = { .pmnc_name = "Krait", .pmnc_counters = 4, }, - { + { .cpuid = CORTEX_A53, .core_name = "Cortex-A53", .pmnc_name = "ARM_Cortex-A53", .pmnc_counters = 6, }, - { + { .cpuid = CORTEX_A57, .core_name = "Cortex-A57", .pmnc_name = "ARM_Cortex-A57", .pmnc_counters = 6, }, - { + { .cpuid = AARCH64, .core_name = "AArch64", .pmnc_name = "ARM_AArch64", @@ -298,9 +324,37 @@ struct gator_cpu gator_cpus[] = { .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->pmu_name != NULL && strcmp(gator_cpu->pmu_name, name) == 0) { + return gator_cpu; + } + } + + return NULL; +} + u32 gator_cpuid(void) { #if defined(__arm__) || defined(__aarch64__) @@ -408,7 +462,7 @@ static void gator_buffer_write_string(int cpu, int buftype, const char *x) gator_buffer_write_bytes(cpu, buftype, x, len); } -static void gator_commit_buffer(int cpu, int buftype) +static void gator_commit_buffer(int cpu, int buftype, u64 time) { int type_length, commit, length, byte; @@ -423,25 +477,40 @@ static void gator_commit_buffer(int cpu, int buftype) length += gator_buffer_size[buftype]; } length = length - type_length - sizeof(int); + + if (length <= FRAME_HEADER_SIZE) { + // Nothing to write, only the frame header is present + return; + } + for (byte = 0; byte < sizeof(int); 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 + if (gator_live_rate > 0) { + while (time > per_cpu(gator_buffer_commit_time, cpu)) { + per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate; + } + } +#endif + marshal_frame(cpu, buftype); // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); } -static void buffer_check(int cpu, int buftype) +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); + gator_commit_buffer(cpu, buftype, time); } } @@ -496,7 +565,7 @@ static void gator_timer_interrupt(void) void gator_backtrace_handler(struct pt_regs *const regs) { - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); // Output backtrace gator_add_sample(cpu, regs); @@ -510,40 +579,48 @@ void gator_backtrace_handler(struct pt_regs *const regs) static int gator_running; // This function runs in interrupt context and on the appropriate core -static void gator_timer_offline(void *unused) +static void gator_timer_offline(void *migrate) { struct gator_interface *gi; - int i, len, cpu = smp_processor_id(); + int i, len, cpu = get_physical_cpu(); int *buffer; + u64 time; gator_trace_sched_offline(); gator_trace_power_offline(); - gator_hrtimer_offline(cpu); + if (!migrate) { + gator_hrtimer_offline(); + } // Offline any events and output counters + time = gator_get_time(); if (marshal_event_header()) { list_for_each_entry(gi, &gator_events, list) { if (gi->offline) { - len = gi->offline(&buffer); + 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); + gator_commit_buffer(cpu, i, time); } -// This function runs in process context and may be running on a core other than core 'cpu' -static void gator_timer_offline_dispatch(int cpu) +// 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); + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline_dispatch) { + gi->offline_dispatch(cpu, migrate); + } + } } static void gator_timer_stop(void) @@ -553,7 +630,7 @@ static void gator_timer_stop(void) if (gator_running) { on_each_cpu(gator_timer_offline, NULL, 1); for_each_online_cpu(cpu) { - gator_timer_offline_dispatch(cpu); + gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false); } gator_running = 0; @@ -562,10 +639,10 @@ static void gator_timer_stop(void) } // This function runs in interrupt context and on the appropriate core -static void gator_timer_online(void *unused) +static void gator_timer_online(void *migrate) { struct gator_interface *gi; - int len, cpu = smp_processor_id(); + int len, cpu = get_physical_cpu(); int *buffer; gator_trace_power_online(); @@ -574,39 +651,48 @@ static void gator_timer_online(void *unused) if (marshal_event_header()) { list_for_each_entry(gi, &gator_events, list) { if (gi->online) { - len = gi->online(&buffer); + 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, gator_get_time()); + } + + if (!migrate) { + gator_hrtimer_online(); } - gator_hrtimer_online(cpu); #if defined(__arm__) || defined(__aarch64__) - { - const char *core_name = "Unknown"; + if (!sent_core_name[cpu]) { + const char *core_name = NULL; const u32 cpuid = gator_cpuid(); - int i; - - for (i = 0; gator_cpus[i].cpuid != 0; ++i) { - if (gator_cpus[i].cpuid == cpuid) { - core_name = gator_cpus[i].core_name; - break; - } + const struct gator_cpu *const gator_cpu = gator_find_cpu_by_cpuid(cpuid); + char core_name_buf[32]; + + if (gator_cpu != NULL) { + core_name = gator_cpu->core_name; + } else { + snprintf(core_name_buf, sizeof(core_name_buf), "Unknown (0x%.3x)", cpuid); + core_name = core_name_buf; } - marshal_core_name(core_name); + marshal_core_name(cpuid, core_name); + sent_core_name[cpu] = true; } #endif } // 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) +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); + list_for_each_entry(gi, &gator_events, list) { + if (gi->online_dispatch) { + gi->online_dispatch(cpu, migrate); + } + } } int gator_timer_start(unsigned long sample_rate) @@ -620,45 +706,72 @@ int gator_timer_start(unsigned long sample_rate) gator_running = 1; + // event based sampling trumps hr timer based sampling + if (event_based_sampling) { + sample_rate = 0; + } + if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1) return -1; for_each_online_cpu(cpu) { - gator_timer_online_dispatch(cpu); + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); } on_each_cpu(gator_timer_online, NULL, 1); return 0; } -static uint64_t gator_get_time(void) +static u64 gator_get_time(void) { struct timespec ts; - uint64_t timestamp; + u64 timestamp; + u64 prev_timestamp; + u64 delta; + int cpu = smp_processor_id(); - //getnstimeofday(&ts); - do_posix_clock_monotonic_gettime(&ts); - timestamp = timespec_to_ns(&ts) - monotonic_started; + // Match clock_gettime(CLOCK_MONOTONIC_RAW, &ts) from userspace + getrawmonotonic(&ts); + timestamp = timespec_to_ns(&ts); - return timestamp; + // 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. + 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; + } else { + pr_debug("%s: getrawmonotonic is not monotonic cpu: %i delta: %lli\n", __FUNCTION__, cpu, delta); + } + timestamp = prev_timestamp; + } + + return timestamp - gator_monotonic_started; } /****************************************************************************** * cpu hotplug and pm notifiers ******************************************************************************/ +#include "gator_iks.c" + static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { - long cpu = (long)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); + gator_timer_offline_dispatch(cpu, false); break; case CPU_ONLINE: case CPU_ONLINE_FROZEN: - gator_timer_online_dispatch(cpu); + gator_timer_online_dispatch(cpu, false); smp_call_function_single(cpu, gator_timer_online, NULL, 1); break; } @@ -683,13 +796,13 @@ static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void unregister_scheduler_tracepoints(); on_each_cpu(gator_timer_offline, NULL, 1); for_each_online_cpu(cpu) { - gator_timer_offline_dispatch(cpu); + gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false); } break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: for_each_online_cpu(cpu) { - gator_timer_online_dispatch(cpu); + gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); } on_each_cpu(gator_timer_online, NULL, 1); register_scheduler_tracepoints(); @@ -724,15 +837,33 @@ static void gator_notifier_stop(void) ******************************************************************************/ static void gator_summary(void) { - uint64_t timestamp; - struct timespec uptime_ts; + u64 timestamp, uptime; + struct timespec ts; + char uname_buf[512]; + void (*m2b)(struct timespec *ts); + unsigned long flags; - timestamp = gator_get_time(); + snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine); - do_posix_clock_monotonic_gettime(&uptime_ts); - monotonic_started = timespec_to_ns(&uptime_ts); + getnstimeofday(&ts); + timestamp = timespec_to_ns(&ts); - marshal_summary(timestamp, monotonic_started); + 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 interrupts as gator_get_time calls smp_processor_id to verify time is monotonic + local_irq_save(flags); + // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started + gator_monotonic_started = 0; + gator_monotonic_started = gator_get_time(); + local_irq_restore(flags); + + marshal_summary(timestamp, uptime, uname_buf); } int gator_events_install(struct gator_interface *interface) @@ -747,13 +878,17 @@ int gator_events_get_key(void) // key of zero is reserved as a timestamp static int key = 1; - return key++; + const int ret = key; + key += 2; + return ret; } static int gator_init(void) { int i; + calc_first_cluster_size(); + // events sources (gator_events.h, generated by gator_events.sh) for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) if (gator_events_list[i]) @@ -778,14 +913,19 @@ static int gator_start(void) unsigned long cpu, i; struct gator_interface *gi; + 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 + // Capture the start time gator_summary(); // start all events @@ -838,9 +978,11 @@ annotate_failure: cookies_failure: // stop all events list_for_each_entry(gi, &gator_events, list) - if (gi->stop) - gi->stop(); + if (gi->stop) + gi->stop(); events_failure: + gator_migrate_stop(); +migrate_failure: return -1; } @@ -860,8 +1002,10 @@ static void gator_stop(void) // stop all events list_for_each_entry(gi, &gator_events, list) - if (gi->stop) - gi->stop(); + if (gi->stop) + gi->stop(); + + gator_migrate_stop(); } /****************************************************************************** @@ -915,6 +1059,9 @@ static int gator_op_setup(void) per_cpu(gator_buffer_write, cpu)[i] = 0; per_cpu(gator_buffer_commit, cpu)[i] = 0; per_cpu(buffer_space_available, cpu)[i] = true; +#if GATOR_LIVE + per_cpu(gator_buffer_commit_time, cpu) = gator_live_rate; +#endif // Annotation is a special case that only uses a single buffer if (cpu > 0 && i == ANNOTATE_BUF) { @@ -963,6 +1110,7 @@ static void gator_op_stop(void) mutex_lock(&gator_buffer_mutex); gator_started = 0; + gator_monotonic_started = 0; cookies_release(); wake_up(&gator_buffer_wait); @@ -987,10 +1135,15 @@ static void gator_shutdown(void) per_cpu(gator_buffer_write, cpu)[i] = 0; per_cpu(gator_buffer_commit, cpu)[i] = 0; per_cpu(buffer_space_available, cpu)[i] = true; +#if GATOR_LIVE + per_cpu(gator_buffer_commit_time, cpu) = 0; +#endif } mutex_unlock(&gator_buffer_mutex); } + memset(&sent_core_name, 0, sizeof(sent_core_name)); + mutex_unlock(&start_mutex); } @@ -1143,6 +1296,11 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, /* kick just in case we've lost an SMP event */ wake_up(&gator_buffer_wait); + // Wake up annotate_write if more space is available + if (buftype == ANNOTATE_BUF) { + wake_up(&gator_annotate_wait); + } + out: mutex_unlock(&gator_buffer_mutex); return retval; @@ -1196,6 +1354,7 @@ void gator_op_create_files(struct super_block *sb, struct dentry *root) } 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); @@ -1205,6 +1364,8 @@ void gator_op_create_files(struct super_block *sb, struct dentry *root) 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); @@ -1212,8 +1373,8 @@ void gator_op_create_files(struct super_block *sb, struct dentry *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); + if (gi->create_files) + gi->create_files(sb, dir); // Power interface gator_trace_power_create_files(sb, dir); diff --git a/driver/gator_marshaling.c b/driver/gator_marshaling.c index b2efdd2..627b441 100644 --- a/driver/gator_marshaling.c +++ b/driver/gator_marshaling.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012. All rights reserved. + * Copyright (C) ARM Limited 2012-2013. 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 @@ -7,50 +7,72 @@ * */ -static void marshal_summary(long long timestamp, long long uptime) +#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" + +static void marshal_summary(long long timestamp, long long uptime, const char * uname) { + unsigned long flags; int cpu = 0; + + local_irq_save(flags); + 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); - buffer_check(cpu, SUMMARY_BUF); + gator_buffer_write_string(cpu, SUMMARY_BUF, uname); + // Commit the buffer now so it can be one of the first frames read by Streamline + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); + local_irq_restore(flags); } static bool marshal_cookie_header(const char *text) { - int cpu = smp_processor_id(); + 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 = smp_processor_id(); + 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); + 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 = smp_processor_id(); + 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, gator_get_time()); + 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); } - buffer_check(cpu, NAME_BUF); + buffer_check(cpu, NAME_BUF, time); local_irq_restore(flags); } static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel) { - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); + u64 time = gator_get_time(); if (buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, gator_get_time()); + 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); @@ -59,30 +81,30 @@ static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inK } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, BACKTRACE_BUF); + buffer_check(cpu, BACKTRACE_BUF, time); return false; } static void marshal_backtrace(unsigned long address, int cookie) { - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address); } static void marshal_backtrace_footer(void) { - int cpu = smp_processor_id(); + 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); + buffer_check(cpu, BACKTRACE_BUF, gator_get_time()); } static bool marshal_event_header(void) { - unsigned long flags, cpu = smp_processor_id(); + unsigned long flags, cpu = get_physical_cpu(); bool retval = false; local_irq_save(flags); @@ -91,8 +113,6 @@ static bool marshal_event_header(void) gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, gator_get_time()); retval = true; } - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, BLOCK_COUNTER_BUF); local_irq_restore(flags); return retval; @@ -100,7 +120,7 @@ static bool marshal_event_header(void) static void marshal_event(int len, int *buffer) { - unsigned long i, flags, cpu = smp_processor_id(); + unsigned long i, flags, cpu = get_physical_cpu(); if (len <= 0) return; @@ -120,14 +140,12 @@ static void marshal_event(int len, int *buffer) gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i]); gator_buffer_write_packed_int(cpu, BLOCK_COUNTER_BUF, buffer[i + 1]); } - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, BLOCK_COUNTER_BUF); local_irq_restore(flags); } static void marshal_event64(int len, long long *buffer64) { - unsigned long i, flags, cpu = smp_processor_id(); + unsigned long i, flags, cpu = get_physical_cpu(); if (len <= 0) return; @@ -147,8 +165,6 @@ static void marshal_event64(int len, long long *buffer64) gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i]); gator_buffer_write_packed_int64(cpu, BLOCK_COUNTER_BUF, buffer64[i + 1]); } - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, BLOCK_COUNTER_BUF); local_irq_restore(flags); } @@ -156,97 +172,107 @@ static void marshal_event64(int len, long long *buffer64) static void marshal_event_single(int core, int key, int value) { unsigned long flags, cpu; + u64 time; local_irq_save(flags); - cpu = smp_processor_id(); + 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, gator_get_time()); + 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); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, COUNTER_BUF); + buffer_check(cpu, COUNTER_BUF, time); local_irq_restore(flags); } #endif static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid) { - unsigned long cpu = smp_processor_id(), flags; + unsigned long cpu = get_physical_cpu(), flags; + u64 time; if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) return; local_irq_save(flags); + time = gator_get_time(); if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_START); - gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, GPU_TRACE_BUF); + buffer_check(cpu, GPU_TRACE_BUF, time); local_irq_restore(flags); } static void marshal_sched_gpu_stop(int unit, int core) { - unsigned long cpu = smp_processor_id(), flags; + unsigned long cpu = get_physical_cpu(), flags; + u64 time; if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) return; local_irq_save(flags); + time = gator_get_time(); if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, MESSAGE_GPU_STOP); - gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, time); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, GPU_TRACE_BUF); + buffer_check(cpu, GPU_TRACE_BUF, time); local_irq_restore(flags); } static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) { - unsigned long cpu = smp_processor_id(), flags; + 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, gator_get_time()); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, SCHED_TRACE_BUF); + buffer_check(cpu, SCHED_TRACE_BUF, time); local_irq_restore(flags); } static void marshal_sched_trace_exit(int tgid, int pid) { - unsigned long cpu = smp_processor_id(), flags; + 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, gator_get_time()); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, SCHED_TRACE_BUF); + buffer_check(cpu, SCHED_TRACE_BUF, time); local_irq_restore(flags); } @@ -254,16 +280,18 @@ static void marshal_sched_trace_exit(int tgid, int pid) static void marshal_idle(int core, int state) { unsigned long flags, cpu; + u64 time; local_irq_save(flags); - cpu = smp_processor_id(); + 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, gator_get_time()); + gator_buffer_write_packed_int64(cpu, IDLE_BUF, time); gator_buffer_write_packed_int(cpu, IDLE_BUF, core); } // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, IDLE_BUF); + buffer_check(cpu, IDLE_BUF, time); local_irq_restore(flags); } #endif @@ -323,16 +351,17 @@ static void marshal_frame(int cpu, int buftype) } #if defined(__arm__) || defined(__aarch64__) -static void marshal_core_name(const char *name) +static void marshal_core_name(const int cpuid, const char *name) { - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); unsigned long flags; local_irq_save(flags); if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) { gator_buffer_write_packed_int(cpu, NAME_BUF, HRTIMER_CORE_NAME); + gator_buffer_write_packed_int(cpu, NAME_BUF, cpuid); gator_buffer_write_string(cpu, NAME_BUF, name); } - buffer_check(cpu, NAME_BUF); + buffer_check(cpu, NAME_BUF, gator_get_time()); local_irq_restore(flags); } #endif diff --git a/driver/gator_pack.c b/driver/gator_pack.c index 119746b..2bddcbe 100644 --- a/driver/gator_pack.c +++ b/driver/gator_pack.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/driver/gator_trace_gpu.c b/driver/gator_trace_gpu.c index 9fc488b..c94f6a0 100644 --- a/driver/gator_trace_gpu.c +++ b/driver/gator_trace_gpu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -32,14 +32,72 @@ static int mali_timeline_trace_registered; static int mali_job_slots_trace_registered; static int gpu_trace_registered; -#define GPU_UNIT_NONE 0 -#define GPU_UNIT_VP 1 -#define GPU_UNIT_FP 2 -#define GPU_UNIT_CL 3 +enum { + GPU_UNIT_NONE = 0, + GPU_UNIT_VP, + GPU_UNIT_FP, + GPU_UNIT_CL, + NUMBER_OF_GPU_UNITS +}; #define MALI_400 (0x0b07) #define MALI_T6xx (0x0056) +struct mali_gpu_job { + int count; + int last_core; + int last_tgid; + int last_pid; +}; + +#define NUMBER_OF_GPU_CORES 16 +static struct mali_gpu_job mali_gpu_jobs[NUMBER_OF_GPU_UNITS][NUMBER_OF_GPU_CORES]; +static DEFINE_SPINLOCK(mali_gpu_jobs_lock); + +static void mali_gpu_enqueue(int unit, int core, int tgid, int pid) +{ + int count; + + spin_lock(&mali_gpu_jobs_lock); + count = mali_gpu_jobs[unit][core].count; + BUG_ON(count < 0); + ++mali_gpu_jobs[unit][core].count; + if (count) { + mali_gpu_jobs[unit][core].last_core = core; + mali_gpu_jobs[unit][core].last_tgid = tgid; + mali_gpu_jobs[unit][core].last_pid = pid; + } + spin_unlock(&mali_gpu_jobs_lock); + + if (!count) { + marshal_sched_gpu_start(unit, core, tgid, pid); + } +} + +static void mali_gpu_stop(int unit, int core) +{ + int count; + int last_core = 0; + int last_tgid = 0; + int last_pid = 0; + + spin_lock(&mali_gpu_jobs_lock); + --mali_gpu_jobs[unit][core].count; + count = mali_gpu_jobs[unit][core].count; + BUG_ON(count < 0); + if (count) { + last_core = mali_gpu_jobs[unit][core].last_core; + last_tgid = mali_gpu_jobs[unit][core].last_tgid; + last_pid = mali_gpu_jobs[unit][core].last_pid; + } + spin_unlock(&mali_gpu_jobs_lock); + + marshal_sched_gpu_stop(unit, core); + if (count) { + marshal_sched_gpu_start(unit, last_core, last_tgid, last_pid); + } +} + #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) #include "gator_events_mali_400.h" @@ -80,18 +138,18 @@ GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned case EVENT_TYPE_START: if (component == EVENT_CHANNEL_VP0) { /* tgid = d0; pid = d1; */ - marshal_sched_gpu_start(GPU_UNIT_VP, 0, d0, d1); + mali_gpu_enqueue(GPU_UNIT_VP, 0, d0, d1); } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { /* tgid = d0; pid = d1; */ - marshal_sched_gpu_start(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1); + mali_gpu_enqueue(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1); } break; case EVENT_TYPE_STOP: if (component == EVENT_CHANNEL_VP0) { - marshal_sched_gpu_stop(GPU_UNIT_VP, 0); + mali_gpu_stop(GPU_UNIT_VP, 0); } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { - marshal_sched_gpu_stop(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0); + mali_gpu_stop(GPU_UNIT_FP, component - EVENT_CHANNEL_FP0); } break; @@ -136,16 +194,16 @@ GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigne if (unit != GPU_UNIT_NONE) { switch (state) { case EVENT_TYPE_START: - marshal_sched_gpu_start(unit, 0, tgid, (pid != 0 ? pid : tgid)); + mali_gpu_enqueue(unit, 0, tgid, (pid != 0 ? pid : tgid)); break; case EVENT_TYPE_STOP: - marshal_sched_gpu_stop(unit, 0); + mali_gpu_stop(unit, 0); break; default: /* * Some jobs can be soft-stopped, so ensure that this terminates the activity trace. */ - marshal_sched_gpu_stop(unit, 0); + mali_gpu_stop(unit, 0); } } } @@ -153,12 +211,12 @@ GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigne GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) { - marshal_sched_gpu_start(gpu_unit, gpu_core, (int)p->tgid, (int)p->pid); + mali_gpu_enqueue(gpu_unit, gpu_core, (int)p->tgid, (int)p->pid); } GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) { - marshal_sched_gpu_stop(gpu_unit, gpu_core); + mali_gpu_stop(gpu_unit, gpu_core); } int gator_trace_gpu_start(void) diff --git a/driver/gator_trace_gpu.h b/driver/gator_trace_gpu.h index efb47c6..bb0f42d 100644 --- a/driver/gator_trace_gpu.h +++ b/driver/gator_trace_gpu.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 diff --git a/driver/gator_trace_power.c b/driver/gator_trace_power.c index 79fa13c..272e056 100644 --- a/driver/gator_trace_power.c +++ b/driver/gator_trace_power.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2012. All rights reserved. + * Copyright (C) ARM Limited 2011-2013. 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 @@ -38,14 +38,27 @@ 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; + } + } - // cpu_frequency - dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); - if (!dir) { - return -1; + 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]); } - 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"); @@ -61,13 +74,14 @@ static int gator_trace_power_create_files(struct super_block *sb, struct dentry // '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_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000); } -#define WFI_EXIT 2 -#define WFI_ENTER 1 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; } @@ -75,10 +89,10 @@ GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) if (implements_wfi()) { if (state == PWR_EVENT_EXIT) { // transition from wfi to non-wfi - marshal_idle(cpu, WFI_EXIT); + marshal_idle(cpu, MESSAGE_IDLE_EXIT); } else { // transition from non-wfi to wfi - marshal_idle(cpu, WFI_ENTER); + marshal_idle(cpu, MESSAGE_IDLE_ENTER); } } @@ -92,16 +106,17 @@ GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) static void gator_trace_power_online(void) { - int cpu = smp_processor_id(); + int pcpu = get_physical_cpu(); + int lcpu = get_logical_cpu(); if (power_cpu_enabled[POWER_CPU_FREQ]) { - marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000); + marshal_event_single(pcpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(lcpu) * 1000); } } static void gator_trace_power_offline(void) { // Set frequency to zero on an offline - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); if (power_cpu_enabled[POWER_CPU_FREQ]) { marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0); } diff --git a/driver/gator_trace_sched.c b/driver/gator_trace_sched.c index d0336f9..eb989b5 100644 --- a/driver/gator_trace_sched.c +++ b/driver/gator_trace_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * Copyright (C) ARM Limited 2010-2013. 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 @@ -23,14 +23,13 @@ enum { STATE_WAIT_ON_OTHER = 0, STATE_CONTENTION, STATE_WAIT_ON_IO, - STATE_WAIT_ON_MUTEX, }; void emit_pid_name(struct task_struct *task) { bool found = false; char taskcomm[TASK_COMM_LEN + 3]; - unsigned long x, cpu = smp_processor_id(); + unsigned long x, cpu = get_physical_cpu(); uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]); uint64_t value; @@ -66,9 +65,10 @@ void emit_pid_name(struct task_struct *task) static void collect_counters(void) { - int *buffer, len; + int *buffer, len, cpu = get_physical_cpu(); long long *buffer64; struct gator_interface *gi; + u64 time; if (marshal_event_header()) { list_for_each_entry(gi, &gator_events, list) { @@ -80,13 +80,27 @@ static void collect_counters(void) marshal_event64(len, buffer64); } } + // Only check after writing all counters so that time and corresponding counters appear in the same frame + time = gator_get_time(); + buffer_check(cpu, BLOCK_COUNTER_BUF, time); + +#if GATOR_LIVE + // Commit buffers on timeout + if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { + static const int buftypes[] = { COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; + int i; + for (i = 0; i < sizeof(buftypes)/sizeof(buftypes[0]); ++i) { + gator_commit_buffer(cpu, buftypes[i], time); + } + } +#endif } } static void probe_sched_write(int type, struct task_struct *task, struct task_struct *old_task) { int cookie = 0, state = 0; - int cpu = smp_processor_id(); + int cpu = get_physical_cpu(); int tgid = task->tgid; int pid = task->pid; @@ -98,10 +112,6 @@ static void probe_sched_write(int type, struct task_struct *task, struct task_st state = STATE_CONTENTION; } else if (old_task->in_iowait) { state = STATE_WAIT_ON_IO; -#ifdef CONFIG_DEBUG_MUTEXES - } else if (old_task->blocked_on) { - state = STATE_WAIT_ON_MUTEX; -#endif } else { state = STATE_WAIT_ON_OTHER; } -- cgit v1.2.3