diff options
author | Jon Medhurst | 2014-04-09 06:39:14 -0500 |
---|---|---|
committer | Jon Medhurst | 2014-04-09 06:40:23 -0500 |
commit | 0f66ada81c8f90a7b2e4f7570032e33aaedfb32f (patch) | |
tree | c686b9fe25f272834766eba38b51cc7a97c69786 | |
parent | 33bef9ed7feca41e7cd6de8bf5d80052669278d3 (diff) | |
download | arm-ds5-gator-DS-5.18.tar.gz arm-ds5-gator-DS-5.18.tar.xz arm-ds5-gator-DS-5.18.zip |
gator: Version 5.18DS-5.18
Signed-off-by: Jon Medhurst <tixy@linaro.org>
119 files changed, 4350 insertions, 1124 deletions
diff --git a/README_Streamline.txt b/README_Streamline.txt index 744c33f..df3f923 100755 --- a/README_Streamline.txt +++ b/README_Streamline.txt | |||
@@ -2,18 +2,19 @@ | |||
2 | *** Purpose *** | 2 | *** Purpose *** |
3 | 3 | ||
4 | Instructions on setting up ARM Streamline on the target. | 4 | Instructions on setting up ARM Streamline on the target. |
5 | The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate. | 5 | The gator driver and gator daemon are required to run on the ARM Linux target in order for ARM Streamline to operate. A new early access feature allows the gator daemon can run without the gator driver by using userspace APIs with reduced functionality when using Linux 3.12 or later. |
6 | The driver should be built as a module and the daemon must run with root permissions on the target. | 6 | The driver should be built as a module and the daemon must run with root permissions on the target. |
7 | 7 | ||
8 | *** Introduction *** | 8 | *** Introduction *** |
9 | 9 | ||
10 | A linux development environment with cross compiling tools is most likely required, depending on what is already created and provided. | 10 | A Linux development environment with cross compiling tools is most likely required, depending on what is already created and provided. |
11 | -For users, the ideal environment is to be given a BSP with gatord and gator.ko already running on a properly configured kernel. In such a scenario, a development environment is not needed, root permission may or may not be needed (gatord must be executed with root permissions but can be automatically started, see below), and the user can run Streamline and profile the system without any setup. | 11 | -For users, the ideal environment is to be given a BSP with gatord and gator.ko already running on a properly configured kernel. In such a scenario, a development environment is not needed, root permission may or may not be needed (gatord must be executed with root permissions but can be automatically started, see below), and the user can run Streamline and profile the system without any setup. |
12 | -The ideal development environment has the kernel source code available to be rebuilt, usually by cross-compiling on a host machine. This environment allows the greatest flexibility in configuring the kernel and building the gator driver module. | 12 | -The ideal development environment has the kernel source code available to be rebuilt, usually by cross-compiling on a host machine. This environment allows the greatest flexibility in configuring the kernel and building the gator driver module. |
13 | -However, it is possible that a user/developer has a kernel but does not have the source code. In this scenario it may or may not be possible to obtain a valid profile. | 13 | -However, it is possible that a user/developer has a kernel but does not have the source code. In this scenario it may or may not be possible to obtain a valid profile. |
14 | -First, check if the kernel has the proper configuration options (see below). Profiling cannot occur using a kernel that is not configured properly, a new kernel must be created. See if /proc/config.gz exists on the target. | 14 | -First, check if the kernel has the proper configuration options (see below). Profiling cannot occur using a kernel that is not configured properly, a new kernel must be created. See if /proc/config.gz exists on the target. |
15 | -Second, given a properly configured kernel, check if the filesystem contains the kernel source/headers, which can be used to re-create the gator driver. These files may be located in different areas, but common locations are /lib/modules/ and /usr/src. | 15 | -Second, given a properly configured kernel, check if the filesystem contains the kernel source/headers, which can be used to re-create the gator driver. These files may be located in different areas, but common locations are /lib/modules/ and /usr/src. |
16 | -If the kernel is not properly configured or sources/headers are not available, the developer is on their own and kernel creation is beyond the scope of this document. Note: It is possible for a module to work when compiled against a similar kernel source code, though this is not guaranteed to work due to differences in kernel structures, exported symbols and incompatible configuration parameters. | 16 | -If the kernel is not properly configured or sources/headers are not available, the developer is on their own and kernel creation is beyond the scope of this document. Note: It is possible for a module to work when compiled against a similar kernel source code, though this is not guaranteed to work due to differences in kernel structures, exported symbols and incompatible configuration parameters. |
17 | -If the target is running Linux 3.12 or later the kernel driver is not required and userspace APIs will be used instead. | ||
17 | 18 | ||
18 | *** Kernel configuration *** | 19 | *** Kernel configuration *** |
19 | 20 | ||
@@ -24,7 +25,7 @@ menuconfig options (depending on the kernel version, the location of these confi | |||
24 | - [*] Profiling Support (enables CONFIG_PROFILING) | 25 | - [*] Profiling Support (enables CONFIG_PROFILING) |
25 | - Kernel Features | 26 | - Kernel Features |
26 | - [*] High Resolution Timer Support (enables CONFIG_HIGH_RES_TIMERS) | 27 | - [*] High Resolution Timer Support (enables CONFIG_HIGH_RES_TIMERS) |
27 | - [*] Use local timer interrupts (only required for SMP, enables CONFIG_LOCAL_TIMERS) | 28 | - [*] Use local timer interrupts (only required for SMP and for version before Linux 3.12, enables CONFIG_LOCAL_TIMERS) |
28 | - [*] Enable hardware performance counter support for perf events (enables CONFIG_HW_PERF_EVENTS) | 29 | - [*] Enable hardware performance counter support for perf events (enables CONFIG_HW_PERF_EVENTS) |
29 | - CPU Power Management | 30 | - CPU Power Management |
30 | - CPU Frequency scaling | 31 | - CPU Frequency scaling |
@@ -46,8 +47,10 @@ CONFIG_DEBUG_INFO (optional, used for analyzing the kernel) | |||
46 | CONFIG_CPU_FREQ (optional, provides frequency setting of the CPU) | 47 | CONFIG_CPU_FREQ (optional, provides frequency setting of the CPU) |
47 | 48 | ||
48 | These may be verified on a running system using /proc/config.gz (if this file exists) by running 'zcat /proc/config.gz | grep <option>'. For example, confirming that CONFIG_PROFILING is enabled | 49 | These may be verified on a running system using /proc/config.gz (if this file exists) by running 'zcat /proc/config.gz | grep <option>'. For example, confirming that CONFIG_PROFILING is enabled |
49 | > zcat /proc/config.gz | grep CONFIG_PROFILING | 50 | > zcat /proc/config.gz | grep CONFIG_PROFILING |
50 | CONFIG_PROFILING=y | 51 | CONFIG_PROFILING=y |
52 | |||
53 | If a device tree is used it must include the pmu bindings, see Documentation/devicetree/bindings/arm/pmu.txt for details. | ||
51 | 54 | ||
52 | *** Checking the gator requirements *** | 55 | *** Checking the gator requirements *** |
53 | 56 | ||
@@ -64,6 +67,17 @@ for example when using the linaro-toolchain-binaries | |||
64 | 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 | 67 | 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 |
65 | If successful, a gator.ko module should be generated | 68 | If successful, a gator.ko module should be generated |
66 | 69 | ||
70 | It is also possible to integrate the gator.ko module into the kernel build system | ||
71 | cd /path/to/kernel/build/dir | ||
72 | cd drivers | ||
73 | mkdir gator | ||
74 | cp -r /path/to/gator/driver-src/* gator | ||
75 | Edit Makefile in the kernel drivers folder and add this to the end | ||
76 | obj-$(CONFIG_GATOR) += gator/ | ||
77 | Edit Kconfig in the kernel drivers folder and add this before the last endmenu | ||
78 | source "drivers/gator/Kconfig" | ||
79 | You can now select gator when using menuconfig while configuring the kernel and rebuild as directed | ||
80 | |||
67 | *** Building the gator daemon *** | 81 | *** Building the gator daemon *** |
68 | 82 | ||
69 | cd /path/to/gator/daemon-src | 83 | cd /path/to/gator/daemon-src |
@@ -78,6 +92,9 @@ For Android targets (install the android ndk, see developer.android.com) | |||
78 | ndk-build | 92 | ndk-build |
79 | or execute /path/to/ndk/ndk-build if the ndk is not on your path | 93 | or execute /path/to/ndk/ndk-build if the ndk is not on your path |
80 | gatord should now be created and located in libs/armeabi | 94 | gatord should now be created and located in libs/armeabi |
95 | If you get an error like the following, upgrade to a more recent version of the android ndk | ||
96 | jni/PerfGroup.cpp: In function 'int sys_perf_event_open(perf_event_attr*, pid_t, int, int, long unsigned int)': | ||
97 | jni/PerfGroup.cpp:36:17: error: '__NR_perf_event_open' was not declared in this scope | ||
81 | 98 | ||
82 | *** Running gator *** | 99 | *** Running gator *** |
83 | 100 | ||
@@ -88,6 +105,7 @@ gator.ko must be located in the same directory as gatord on the target or the lo | |||
88 | With root privileges, run the daemon | 105 | With root privileges, run the daemon |
89 | sudo ./gatord & | 106 | sudo ./gatord & |
90 | 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. | 107 | 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. |
108 | If gator.ko is not loaded and is not in the same directory as gatord when using Linux 3.12 or later, gatord can run without gator.ko by using userspace APIs. Not all features are supported by userspace gator. If /dev/gator/version does not exist after starting gatord it is running userspace gator. | ||
91 | 109 | ||
92 | *** Customizing the l2c-310 Counter *** | 110 | *** Customizing the l2c-310 Counter *** |
93 | 111 | ||
@@ -96,6 +114,10 @@ The l2c-310 counter in gator_events_l2c-310.c contains hard coded offsets where | |||
96 | Further, the l2c-310 counter can be disabled by providing an offset of zero, ex: | 114 | Further, the l2c-310 counter can be disabled by providing an offset of zero, ex: |
97 | insmod gator.ko l2c310_addr=0 | 115 | insmod gator.ko l2c310_addr=0 |
98 | 116 | ||
117 | *** CCN-504 *** | ||
118 | |||
119 | CCN-504 is disabled by default. To enable CCN-504, insmod gator module with the ccn504_addr=<addr> parameter where addr is the base address of the CCN-504 configuration register space (PERIPHBASE), ex: insmod gator.ko ccn504_addr=0x2E000000. | ||
120 | |||
99 | *** Compiling an application or shared library *** | 121 | *** Compiling an application or shared library *** |
100 | 122 | ||
101 | Recommended compiler settings: | 123 | Recommended compiler settings: |
@@ -113,18 +135,31 @@ Attempting to run an incompatible binary often results in the confusing error me | |||
113 | *** Bugs *** | 135 | *** Bugs *** |
114 | 136 | ||
115 | 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. | 137 | 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. |
116 | 138 | # ls /sys/bus/event_source/devices/ | |
117 | # ls /sys/bus/event_source/devices/ | 139 | ARMv7_Cortex_A9 breakpoint software tracepoint |
118 | ARMv7_Cortex_A9 breakpoint software tracepoint | ||
119 | |||
120 | To work around the issue try upgrading to a later kernel or comment out the gator_events_perf_pmu_cpu_init(gator_cpu, type); call in gator_events_perf_pmu.c | 140 | To work around the issue try upgrading to a later kernel or comment out the gator_events_perf_pmu_cpu_init(gator_cpu, type); call in gator_events_perf_pmu.c |
121 | 141 | ||
142 | There is a bug in some Linux kernels where an Oops may occurs when using userspace gator and a core is offlined. The fix was merged into mainline in 3.14-rc5, see http://git.kernel.org/tip/e3703f8cdfcf39c25c4338c3ad8e68891cca3731, and as been backported to older kernels. | ||
143 | |||
144 | If you see this error when using SELinux, ex: Android 4.4 or later | ||
145 | # ./gatord | ||
146 | Unable to load (insmod) gator.ko driver: | ||
147 | >>> gator.ko must be built against the current kernel version & configuration | ||
148 | >>> See dmesg for more details | ||
149 | # dmesg | ||
150 | ... | ||
151 | <7>[ 6745.475110] SELinux: initialized (dev gatorfs, type gatorfs), not configured for labeling | ||
152 | <5>[ 6745.477434] type=1400 audit(1393005053.336:10): avc: denied { mount } for pid=1996 comm="gatord-main" name="/" dev="gatorfs" ino=8733 scontext=u:r:shell:s0 tcontext=u:object_r:unlabeled:s0 tclass=filesystem | ||
153 | disable SELinux so that gatorfs can be mounted by running | ||
154 | # setenforce 0 | ||
155 | Once gator is started, SELinux can be reenabled | ||
156 | |||
122 | *** Profiling the kernel (optional) *** | 157 | *** Profiling the kernel (optional) *** |
123 | 158 | ||
124 | CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above. | 159 | CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above. |
125 | Use vmlinux as the image for debug symbols in Streamline. | 160 | Use vmlinux as the image for debug symbols in Streamline. |
126 | Drivers may be profiled using this method by statically linking the driver into the kernel image or adding the driver as an image to Streamline. | 161 | Drivers may be profiled using this method by statically linking the driver into the kernel image or adding the driver as an image to Streamline. |
127 | To perform kernel stack unwinding and module unwinding, edit the Makefile to enable GATOR_KERNEL_STACK_UNWINDING and rebuild gator.ko. | 162 | To perform kernel stack unwinding and module unwinding, edit the Makefile to enable GATOR_KERNEL_STACK_UNWINDING and rebuild gator.ko or run "echo 1 > /sys/module/gator/parameters/kernel_stack_unwinding" as root on the target after gatord is started. |
128 | 163 | ||
129 | *** Automatically start gator on boot (optional) *** | 164 | *** Automatically start gator on boot (optional) *** |
130 | 165 | ||
@@ -137,4 +172,3 @@ update-rc.d rungator.sh defaults | |||
137 | *** GPL License *** | 172 | *** GPL License *** |
138 | 173 | ||
139 | For license information, please see the file LICENSE after unzipping driver-src/gator-driver.tar.gz. | 174 | For license information, please see the file LICENSE after unzipping driver-src/gator-driver.tar.gz. |
140 | |||
diff --git a/daemon/Android.mk b/daemon/Android.mk index a042971..045d028 100644 --- a/daemon/Android.mk +++ b/daemon/Android.mk | |||
@@ -1,7 +1,7 @@ | |||
1 | LOCAL_PATH := $(call my-dir) | 1 | LOCAL_PATH := $(call my-dir) |
2 | include $(CLEAR_VARS) | 2 | include $(CLEAR_VARS) |
3 | 3 | ||
4 | XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) | 4 | XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) |
5 | 5 | ||
6 | LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors | 6 | LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors |
7 | 7 | ||
@@ -9,22 +9,33 @@ LOCAL_SRC_FILES := \ | |||
9 | Buffer.cpp \ | 9 | Buffer.cpp \ |
10 | CapturedXML.cpp \ | 10 | CapturedXML.cpp \ |
11 | Child.cpp \ | 11 | Child.cpp \ |
12 | Collector.cpp \ | ||
13 | ConfigurationXML.cpp \ | 12 | ConfigurationXML.cpp \ |
14 | Driver.cpp \ | 13 | Driver.cpp \ |
14 | DriverSource.cpp \ | ||
15 | DynBuf.cpp \ | ||
15 | EventsXML.cpp \ | 16 | EventsXML.cpp \ |
17 | ExternalSource.cpp \ | ||
16 | Fifo.cpp \ | 18 | Fifo.cpp \ |
17 | Hwmon.cpp \ | 19 | Hwmon.cpp \ |
18 | KMod.cpp \ | 20 | KMod.cpp \ |
19 | LocalCapture.cpp \ | 21 | LocalCapture.cpp \ |
20 | Logging.cpp \ | 22 | Logging.cpp \ |
21 | main.cpp \ | 23 | main.cpp \ |
24 | Monitor.cpp \ | ||
22 | OlySocket.cpp \ | 25 | OlySocket.cpp \ |
23 | OlyUtility.cpp \ | 26 | OlyUtility.cpp \ |
27 | PerfBuffer.cpp \ | ||
28 | PerfDriver.cpp \ | ||
29 | PerfGroup.cpp \ | ||
30 | PerfSource.cpp \ | ||
31 | Proc.cpp \ | ||
24 | Sender.cpp \ | 32 | Sender.cpp \ |
25 | SessionData.cpp \ | 33 | SessionData.cpp \ |
26 | SessionXML.cpp \ | 34 | SessionXML.cpp \ |
35 | Source.cpp \ | ||
27 | StreamlineSetup.cpp \ | 36 | StreamlineSetup.cpp \ |
37 | UEvent.cpp \ | ||
38 | UserSpaceSource.cpp \ | ||
28 | libsensors/access.c \ | 39 | libsensors/access.c \ |
29 | libsensors/conf-lex.c \ | 40 | libsensors/conf-lex.c \ |
30 | libsensors/conf-parse.c \ | 41 | libsensors/conf-parse.c \ |
diff --git a/daemon/Buffer.cpp b/daemon/Buffer.cpp index 090a715..93557da 100644 --- a/daemon/Buffer.cpp +++ b/daemon/Buffer.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,33 +12,60 @@ | |||
12 | #include "Sender.h" | 12 | #include "Sender.h" |
13 | #include "SessionData.h" | 13 | #include "SessionData.h" |
14 | 14 | ||
15 | #define mask (size - 1) | 15 | #define mask (mSize - 1) |
16 | 16 | ||
17 | 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]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) { | 17 | enum { |
18 | if ((size & mask) != 0) { | 18 | CODE_PEA = 1, |
19 | CODE_KEYS = 2, | ||
20 | CODE_FORMAT = 3, | ||
21 | CODE_MAPS = 4, | ||
22 | CODE_COMM = 5, | ||
23 | }; | ||
24 | |||
25 | // Summary Frame Messages | ||
26 | enum { | ||
27 | MESSAGE_SUMMARY = 1, | ||
28 | MESSAGE_CORE_NAME = 3, | ||
29 | }; | ||
30 | |||
31 | // From gator_marshaling.c | ||
32 | #define NEWLINE_CANARY \ | ||
33 | /* Unix */ \ | ||
34 | "1\n" \ | ||
35 | /* Windows */ \ | ||
36 | "2\r\n" \ | ||
37 | /* Mac OS */ \ | ||
38 | "3\r" \ | ||
39 | /* RISC OS */ \ | ||
40 | "4\n\r" \ | ||
41 | /* Add another character so the length isn't 0x0a bytes */ \ | ||
42 | "5" | ||
43 | |||
44 | Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) { | ||
45 | if ((mSize & mask) != 0) { | ||
19 | logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); | 46 | logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); |
20 | handleException(); | 47 | handleException(); |
21 | } | 48 | } |
22 | frame(); | 49 | frame(); |
23 | } | 50 | } |
24 | 51 | ||
25 | Buffer::~Buffer () { | 52 | Buffer::~Buffer() { |
26 | delete [] buf; | 53 | delete [] mBuf; |
27 | } | 54 | } |
28 | 55 | ||
29 | void Buffer::write (Sender * const sender) { | 56 | void Buffer::write(Sender *const sender) { |
30 | if (!commitReady()) { | 57 | if (!commitReady()) { |
31 | return; | 58 | return; |
32 | } | 59 | } |
33 | 60 | ||
34 | // determine the size of two halves | 61 | // determine the size of two halves |
35 | int length1 = commitPos - readPos; | 62 | int length1 = mCommitPos - mReadPos; |
36 | char * buffer1 = buf + readPos; | 63 | char *buffer1 = mBuf + mReadPos; |
37 | int length2 = 0; | 64 | int length2 = 0; |
38 | char * buffer2 = buf; | 65 | char *buffer2 = mBuf; |
39 | if (length1 < 0) { | 66 | if (length1 < 0) { |
40 | length1 = size - readPos; | 67 | length1 = mSize - mReadPos; |
41 | length2 = commitPos; | 68 | length2 = mCommitPos; |
42 | } | 69 | } |
43 | 70 | ||
44 | logg->logMessage("Sending data length1: %i length2: %i", length1, length2); | 71 | logg->logMessage("Sending data length1: %i length2: %i", length1, length2); |
@@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) { | |||
53 | sender->writeData(buffer2, length2, RESPONSE_APC_DATA); | 80 | sender->writeData(buffer2, length2, RESPONSE_APC_DATA); |
54 | } | 81 | } |
55 | 82 | ||
56 | readPos = commitPos; | 83 | mReadPos = mCommitPos; |
57 | } | 84 | } |
58 | 85 | ||
59 | bool Buffer::commitReady () const { | 86 | bool Buffer::commitReady() const { |
60 | return commitPos != readPos; | 87 | return mCommitPos != mReadPos; |
61 | } | 88 | } |
62 | 89 | ||
63 | int Buffer::bytesAvailable () const { | 90 | int Buffer::bytesAvailable() const { |
64 | int filled = writePos - readPos; | 91 | int filled = mWritePos - mReadPos; |
65 | if (filled < 0) { | 92 | if (filled < 0) { |
66 | filled += size; | 93 | filled += mSize; |
67 | } | 94 | } |
68 | 95 | ||
69 | int remaining = size - filled; | 96 | int remaining = mSize - filled; |
70 | 97 | ||
71 | if (available) { | 98 | if (mAvailable) { |
72 | // Give some extra room; also allows space to insert the overflow error packet | 99 | // Give some extra room; also allows space to insert the overflow error packet |
73 | remaining -= 200; | 100 | remaining -= 200; |
74 | } else { | 101 | } else { |
@@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const { | |||
79 | return remaining; | 106 | return remaining; |
80 | } | 107 | } |
81 | 108 | ||
82 | bool Buffer::checkSpace (const int bytes) { | 109 | bool Buffer::checkSpace(const int bytes) { |
83 | const int remaining = bytesAvailable(); | 110 | const int remaining = bytesAvailable(); |
84 | 111 | ||
85 | if (remaining < bytes) { | 112 | if (remaining < bytes) { |
86 | available = false; | 113 | mAvailable = false; |
87 | } else { | 114 | } else { |
88 | available = true; | 115 | mAvailable = true; |
89 | } | 116 | } |
90 | 117 | ||
91 | return available; | 118 | return mAvailable; |
119 | } | ||
120 | |||
121 | int Buffer::contiguousSpaceAvailable() const { | ||
122 | int remaining = bytesAvailable(); | ||
123 | int contiguous = mSize - mWritePos; | ||
124 | if (remaining < contiguous) { | ||
125 | return remaining; | ||
126 | } else { | ||
127 | return contiguous; | ||
128 | } | ||
92 | } | 129 | } |
93 | 130 | ||
94 | void Buffer::commit (const uint64_t time) { | 131 | void Buffer::commit(const uint64_t time) { |
95 | // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload | 132 | // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload |
96 | const int typeLength = gSessionData->mLocalCapture ? 0 : 1; | 133 | const int typeLength = gSessionData->mLocalCapture ? 0 : 1; |
97 | int length = writePos - commitPos; | 134 | int length = mWritePos - mCommitPos; |
98 | if (length < 0) { | 135 | if (length < 0) { |
99 | length += size; | 136 | length += mSize; |
100 | } | 137 | } |
101 | length = length - typeLength - sizeof(int32_t); | 138 | length = length - typeLength - sizeof(int32_t); |
102 | for (size_t byte = 0; byte < sizeof(int32_t); byte++) { | 139 | for (size_t byte = 0; byte < sizeof(int32_t); byte++) { |
103 | buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; | 140 | mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; |
104 | } | 141 | } |
105 | 142 | ||
106 | logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); | 143 | logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); |
107 | commitPos = writePos; | 144 | mCommitPos = mWritePos; |
108 | 145 | ||
109 | if (gSessionData->mLiveRate > 0) { | 146 | if (gSessionData->mLiveRate > 0) { |
110 | while (time > commitTime) { | 147 | while (time > mCommitTime) { |
111 | commitTime += gSessionData->mLiveRate; | 148 | mCommitTime += gSessionData->mLiveRate; |
112 | } | 149 | } |
113 | } | 150 | } |
114 | 151 | ||
115 | if (!done) { | 152 | if (!mIsDone) { |
116 | frame(); | 153 | frame(); |
117 | } | 154 | } |
118 | 155 | ||
119 | // send a notification that data is ready | 156 | // send a notification that data is ready |
120 | sem_post(readerSem); | 157 | sem_post(mReaderSem); |
121 | } | 158 | } |
122 | 159 | ||
123 | void Buffer::check (const uint64_t time) { | 160 | void Buffer::check(const uint64_t time) { |
124 | int filled = writePos - commitPos; | 161 | int filled = mWritePos - mCommitPos; |
125 | if (filled < 0) { | 162 | if (filled < 0) { |
126 | filled += size; | 163 | filled += mSize; |
127 | } | 164 | } |
128 | if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) { | 165 | if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { |
129 | commit(time); | 166 | commit(time); |
130 | } | 167 | } |
131 | } | 168 | } |
132 | 169 | ||
133 | void Buffer::packInt (int32_t x) { | 170 | void Buffer::packInt(int32_t x) { |
134 | int packedBytes = 0; | 171 | int packedBytes = 0; |
135 | int more = true; | 172 | int more = true; |
136 | while (more) { | 173 | while (more) { |
@@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) { | |||
144 | b |= 0x80; | 181 | b |= 0x80; |
145 | } | 182 | } |
146 | 183 | ||
147 | buf[(writePos + packedBytes) & mask] = b; | 184 | mBuf[(mWritePos + packedBytes) & mask] = b; |
148 | packedBytes++; | 185 | packedBytes++; |
149 | } | 186 | } |
150 | 187 | ||
151 | writePos = (writePos + packedBytes) & mask; | 188 | mWritePos = (mWritePos + packedBytes) & mask; |
152 | } | 189 | } |
153 | 190 | ||
154 | void Buffer::packInt64 (int64_t x) { | 191 | void Buffer::packInt64(int64_t x) { |
155 | int packedBytes = 0; | 192 | int packedBytes = 0; |
156 | int more = true; | 193 | int more = true; |
157 | while (more) { | 194 | while (more) { |
@@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) { | |||
165 | b |= 0x80; | 202 | b |= 0x80; |
166 | } | 203 | } |
167 | 204 | ||
168 | buf[(writePos + packedBytes) & mask] = b; | 205 | mBuf[(mWritePos + packedBytes) & mask] = b; |
169 | packedBytes++; | 206 | packedBytes++; |
170 | } | 207 | } |
171 | 208 | ||
172 | writePos = (writePos + packedBytes) & mask; | 209 | mWritePos = (mWritePos + packedBytes) & mask; |
210 | } | ||
211 | |||
212 | void Buffer::writeBytes(const void *const data, size_t count) { | ||
213 | size_t i; | ||
214 | for (i = 0; i < count; ++i) { | ||
215 | mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i]; | ||
216 | } | ||
217 | |||
218 | mWritePos = (mWritePos + i) & mask; | ||
173 | } | 219 | } |
174 | 220 | ||
175 | void Buffer::frame () { | 221 | void Buffer::writeString(const char *const str) { |
222 | const int len = strlen(str); | ||
223 | packInt(len); | ||
224 | writeBytes(str, len); | ||
225 | } | ||
226 | |||
227 | void Buffer::frame() { | ||
176 | if (!gSessionData->mLocalCapture) { | 228 | if (!gSessionData->mLocalCapture) { |
177 | packInt(RESPONSE_APC_DATA); | 229 | packInt(RESPONSE_APC_DATA); |
178 | } | 230 | } |
179 | // Reserve space for the length | 231 | // Reserve space for the length |
180 | writePos += sizeof(int32_t); | 232 | mWritePos += sizeof(int32_t); |
181 | packInt(buftype); | 233 | packInt(mBufType); |
182 | packInt(core); | 234 | packInt(mCore); |
183 | } | 235 | } |
184 | 236 | ||
185 | bool Buffer::eventHeader (const uint64_t curr_time) { | 237 | void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { |
238 | packInt(MESSAGE_SUMMARY); | ||
239 | writeString(NEWLINE_CANARY); | ||
240 | packInt64(timestamp); | ||
241 | packInt64(uptime); | ||
242 | packInt64(monotonicDelta); | ||
243 | writeString("uname"); | ||
244 | writeString(uname); | ||
245 | writeString(""); | ||
246 | check(1); | ||
247 | } | ||
248 | |||
249 | void Buffer::coreName(const int core, const int cpuid, const char *const name) { | ||
250 | if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) { | ||
251 | packInt(MESSAGE_CORE_NAME); | ||
252 | packInt(core); | ||
253 | packInt(cpuid); | ||
254 | writeString(name); | ||
255 | } | ||
256 | check(1); | ||
257 | } | ||
258 | |||
259 | bool Buffer::eventHeader(const uint64_t curr_time) { | ||
186 | bool retval = false; | 260 | bool retval = false; |
187 | if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { | 261 | if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { |
188 | packInt(0); // key of zero indicates a timestamp | 262 | packInt(0); // key of zero indicates a timestamp |
@@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) { | |||
193 | return retval; | 267 | return retval; |
194 | } | 268 | } |
195 | 269 | ||
196 | bool Buffer::eventTid (const int tid) { | 270 | bool Buffer::eventTid(const int tid) { |
197 | bool retval = false; | 271 | bool retval = false; |
198 | if (checkSpace(2*MAXSIZE_PACK32)) { | 272 | if (checkSpace(2 * MAXSIZE_PACK32)) { |
199 | packInt(1); // key of 1 indicates a tid | 273 | packInt(1); // key of 1 indicates a tid |
200 | packInt(tid); | 274 | packInt(tid); |
201 | retval = true; | 275 | retval = true; |
@@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) { | |||
204 | return retval; | 278 | return retval; |
205 | } | 279 | } |
206 | 280 | ||
207 | void Buffer::event (const int32_t key, const int32_t value) { | 281 | void Buffer::event(const int32_t key, const int32_t value) { |
208 | if (checkSpace(2 * MAXSIZE_PACK32)) { | 282 | if (checkSpace(2 * MAXSIZE_PACK32)) { |
209 | packInt(key); | 283 | packInt(key); |
210 | packInt(value); | 284 | packInt(value); |
211 | } | 285 | } |
212 | } | 286 | } |
213 | 287 | ||
214 | void Buffer::event64 (const int64_t key, const int64_t value) { | 288 | void Buffer::event64(const int64_t key, const int64_t value) { |
215 | if (checkSpace(2 * MAXSIZE_PACK64)) { | 289 | if (checkSpace(2 * MAXSIZE_PACK64)) { |
216 | packInt64(key); | 290 | packInt64(key); |
217 | packInt64(value); | 291 | packInt64(value); |
218 | } | 292 | } |
219 | } | 293 | } |
220 | 294 | ||
221 | void Buffer::setDone () { | 295 | void Buffer::pea(const struct perf_event_attr *const pea, int key) { |
222 | done = true; | 296 | if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { |
297 | packInt(CODE_PEA); | ||
298 | writeBytes(pea, pea->size); | ||
299 | packInt(key); | ||
300 | } else { | ||
301 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
302 | handleException(); | ||
303 | } | ||
304 | // Don't know the real perf time so use 1 as it will work for now | ||
305 | check(1); | ||
306 | } | ||
307 | |||
308 | void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) { | ||
309 | if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { | ||
310 | packInt(CODE_KEYS); | ||
311 | packInt(count); | ||
312 | for (int i = 0; i < count; ++i) { | ||
313 | packInt64(ids[i]); | ||
314 | packInt(keys[i]); | ||
315 | } | ||
316 | } else { | ||
317 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
318 | handleException(); | ||
319 | } | ||
320 | check(1); | ||
321 | } | ||
322 | |||
323 | void Buffer::format(const int length, const char *const format) { | ||
324 | if (checkSpace(MAXSIZE_PACK32 + length + 1)) { | ||
325 | packInt(CODE_FORMAT); | ||
326 | writeBytes(format, length + 1); | ||
327 | } else { | ||
328 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
329 | handleException(); | ||
330 | } | ||
331 | check(1); | ||
332 | } | ||
333 | |||
334 | void Buffer::maps(const int pid, const int tid, const char *const maps) { | ||
335 | const int mapsLen = strlen(maps) + 1; | ||
336 | if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { | ||
337 | packInt(CODE_MAPS); | ||
338 | packInt(pid); | ||
339 | packInt(tid); | ||
340 | writeBytes(maps, mapsLen); | ||
341 | } else { | ||
342 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
343 | handleException(); | ||
344 | } | ||
345 | check(1); | ||
346 | } | ||
347 | |||
348 | void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) { | ||
349 | const int imageLen = strlen(image) + 1; | ||
350 | const int commLen = strlen(comm) + 1; | ||
351 | if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { | ||
352 | packInt(CODE_COMM); | ||
353 | packInt(pid); | ||
354 | packInt(tid); | ||
355 | writeBytes(image, imageLen); | ||
356 | writeBytes(comm, commLen); | ||
357 | } else { | ||
358 | logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); | ||
359 | handleException(); | ||
360 | } | ||
361 | check(1); | ||
362 | } | ||
363 | |||
364 | void Buffer::setDone() { | ||
365 | mIsDone = true; | ||
223 | commit(0); | 366 | commit(0); |
224 | } | 367 | } |
225 | 368 | ||
226 | bool Buffer::isDone () const { | 369 | bool Buffer::isDone() const { |
227 | return done && readPos == commitPos && commitPos == writePos; | 370 | return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; |
228 | } | 371 | } |
diff --git a/daemon/Buffer.h b/daemon/Buffer.h index b3c8d78..5023777 100644 --- a/daemon/Buffer.h +++ b/daemon/Buffer.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,54 +9,89 @@ | |||
9 | #ifndef BUFFER_H | 9 | #ifndef BUFFER_H |
10 | #define BUFFER_H | 10 | #define BUFFER_H |
11 | 11 | ||
12 | #include <stddef.h> | ||
13 | #include <stdint.h> | 12 | #include <stdint.h> |
14 | #include <semaphore.h> | 13 | #include <semaphore.h> |
15 | 14 | ||
15 | #include "k/perf_event.h" | ||
16 | |||
16 | class Sender; | 17 | class Sender; |
17 | 18 | ||
19 | enum { | ||
20 | FRAME_SUMMARY = 1, | ||
21 | FRAME_BLOCK_COUNTER = 5, | ||
22 | FRAME_EXTERNAL = 10, | ||
23 | FRAME_PERF_ATTRS = 11, | ||
24 | FRAME_PERF = 12, | ||
25 | }; | ||
26 | |||
18 | class Buffer { | 27 | class Buffer { |
19 | public: | 28 | public: |
20 | static const size_t MAXSIZE_PACK32 = 5; | 29 | static const size_t MAXSIZE_PACK32 = 5; |
21 | static const size_t MAXSIZE_PACK64 = 10; | 30 | static const size_t MAXSIZE_PACK64 = 10; |
22 | 31 | ||
23 | Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem); | 32 | Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem); |
24 | ~Buffer (); | 33 | ~Buffer(); |
34 | |||
35 | void write(Sender *sender); | ||
36 | |||
37 | int bytesAvailable() const; | ||
38 | int contiguousSpaceAvailable() const; | ||
39 | void commit(const uint64_t time); | ||
40 | void check(const uint64_t time); | ||
25 | 41 | ||
26 | void write (Sender * sender); | 42 | void frame(); |
27 | 43 | ||
28 | int bytesAvailable () const; | 44 | // Summary messages |
29 | void commit (const uint64_t time); | 45 | void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); |
30 | void check (const uint64_t time); | 46 | void coreName(const int core, const int cpuid, const char *const name); |
31 | 47 | ||
32 | void frame (); | 48 | // Block Counter messages |
49 | bool eventHeader(uint64_t curr_time); | ||
50 | bool eventTid(int tid); | ||
51 | void event(int32_t key, int32_t value); | ||
52 | void event64(int64_t key, int64_t value); | ||
33 | 53 | ||
34 | bool eventHeader (uint64_t curr_time); | 54 | // Perf Attrs messages |
35 | bool eventTid (int tid); | 55 | void pea(const struct perf_event_attr *const pea, int key); |
36 | void event (int32_t key, int32_t value); | 56 | void keys(const int count, const __u64 *const ids, const int *const keys); |
37 | void event64 (int64_t key, int64_t value); | 57 | void format(const int length, const char *const format); |
58 | void maps(const int pid, const int tid, const char *const maps); | ||
59 | void comm(const int pid, const int tid, const char *const image, const char *const comm); | ||
38 | 60 | ||
39 | void setDone (); | 61 | void setDone(); |
40 | bool isDone () const; | 62 | bool isDone() const; |
63 | |||
64 | // Prefer a new member to using these functions if possible | ||
65 | char *getWritePos() { return mBuf + mWritePos; } | ||
66 | void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } | ||
67 | |||
68 | static void writeLEInt(unsigned char *buf, int v) { | ||
69 | buf[0] = (v >> 0) & 0xFF; | ||
70 | buf[1] = (v >> 8) & 0xFF; | ||
71 | buf[2] = (v >> 16) & 0xFF; | ||
72 | buf[3] = (v >> 24) & 0xFF; | ||
73 | } | ||
41 | 74 | ||
42 | private: | 75 | private: |
43 | bool commitReady () const; | 76 | bool commitReady() const; |
44 | bool checkSpace (int bytes); | 77 | bool checkSpace(int bytes); |
45 | 78 | ||
46 | void packInt (int32_t x); | 79 | void packInt(int32_t x); |
47 | void packInt64 (int64_t x); | 80 | void packInt64(int64_t x); |
48 | 81 | void writeBytes(const void *const data, size_t count); | |
49 | const int32_t core; | 82 | void writeString(const char *const str); |
50 | const int32_t buftype; | 83 | |
51 | const int size; | 84 | const int32_t mCore; |
52 | int readPos; | 85 | const int32_t mBufType; |
53 | int writePos; | 86 | const int mSize; |
54 | int commitPos; | 87 | int mReadPos; |
55 | bool available; | 88 | int mWritePos; |
56 | bool done; | 89 | int mCommitPos; |
57 | char *const buf; | 90 | bool mAvailable; |
58 | uint64_t commitTime; | 91 | bool mIsDone; |
59 | sem_t *const readerSem; | 92 | char *const mBuf; |
93 | uint64_t mCommitTime; | ||
94 | sem_t *const mReaderSem; | ||
60 | 95 | ||
61 | // Intentionally unimplemented | 96 | // Intentionally unimplemented |
62 | Buffer(const Buffer &); | 97 | Buffer(const Buffer &); |
diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp index 30c4c44..cf79b72 100644 --- a/daemon/CapturedXML.cpp +++ b/daemon/CapturedXML.cpp | |||
@@ -1,16 +1,18 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "CapturedXML.h" | ||
10 | |||
9 | #include <stdlib.h> | 11 | #include <stdlib.h> |
10 | #include <string.h> | 12 | #include <string.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
14 | |||
12 | #include "SessionData.h" | 15 | #include "SessionData.h" |
13 | #include "CapturedXML.h" | ||
14 | #include "Logging.h" | 16 | #include "Logging.h" |
15 | #include "OlyUtility.h" | 17 | #include "OlyUtility.h" |
16 | 18 | ||
@@ -30,6 +32,9 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { | |||
30 | 32 | ||
31 | captured = mxmlNewElement(xml, "captured"); | 33 | captured = mxmlNewElement(xml, "captured"); |
32 | mxmlElementSetAttr(captured, "version", "1"); | 34 | mxmlElementSetAttr(captured, "version", "1"); |
35 | if (gSessionData->perf.isSetup()) { | ||
36 | mxmlElementSetAttr(captured, "type", "Perf"); | ||
37 | } | ||
33 | mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); | 38 | mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); |
34 | if (includeTime) { // Send the following only after the capture is complete | 39 | if (includeTime) { // Send the following only after the capture is complete |
35 | if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) | 40 | if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) |
@@ -41,7 +46,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { | |||
41 | mxmlElementSetAttr(target, "name", gSessionData->mCoreName); | 46 | mxmlElementSetAttr(target, "name", gSessionData->mCoreName); |
42 | mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); | 47 | mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); |
43 | mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); | 48 | mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); |
44 | mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); | 49 | mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); |
45 | 50 | ||
46 | if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { | 51 | if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { |
47 | mxmlElementSetAttr(target, "supports_live", "yes"); | 52 | mxmlElementSetAttr(target, "supports_live", "yes"); |
diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h index b0482f5..efc1e52 100644 --- a/daemon/CapturedXML.h +++ b/daemon/CapturedXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Child.cpp b/daemon/Child.cpp index 9ee2ef8..ca33561 100644 --- a/daemon/Child.cpp +++ b/daemon/Child.cpp | |||
@@ -1,38 +1,39 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "Child.h" | ||
10 | |||
9 | #include <stdlib.h> | 11 | #include <stdlib.h> |
10 | #include <string.h> | 12 | #include <string.h> |
11 | #include <signal.h> | 13 | #include <signal.h> |
12 | #include <unistd.h> | 14 | #include <unistd.h> |
13 | #include <sys/prctl.h> | 15 | #include <sys/prctl.h> |
16 | |||
14 | #include "Logging.h" | 17 | #include "Logging.h" |
15 | #include "CapturedXML.h" | 18 | #include "CapturedXML.h" |
16 | #include "SessionData.h" | 19 | #include "SessionData.h" |
17 | #include "Child.h" | ||
18 | #include "LocalCapture.h" | 20 | #include "LocalCapture.h" |
19 | #include "Collector.h" | ||
20 | #include "Sender.h" | 21 | #include "Sender.h" |
21 | #include "OlyUtility.h" | 22 | #include "OlyUtility.h" |
23 | #include "OlySocket.h" | ||
22 | #include "StreamlineSetup.h" | 24 | #include "StreamlineSetup.h" |
23 | #include "ConfigurationXML.h" | 25 | #include "ConfigurationXML.h" |
24 | #include "Driver.h" | 26 | #include "Driver.h" |
25 | #include "Fifo.h" | 27 | #include "PerfSource.h" |
26 | #include "Buffer.h" | 28 | #include "DriverSource.h" |
27 | 29 | #include "UserSpaceSource.h" | |
28 | #define NS_PER_S ((uint64_t)1000000000) | 30 | #include "ExternalSource.h" |
29 | #define NS_PER_US 1000 | ||
30 | 31 | ||
31 | static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads | 32 | static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads |
32 | static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads | 33 | static Source *primarySource = NULL; |
33 | static Buffer* buffer = NULL; | 34 | static Source *userSpaceSource = NULL; |
35 | static Source *externalSource = NULL; | ||
34 | static Sender* sender = NULL; // Shared by Child.cpp and spawned threads | 36 | static Sender* sender = NULL; // Shared by Child.cpp and spawned threads |
35 | static Collector* collector = NULL; | ||
36 | Child* child = NULL; // shared by Child.cpp and main.cpp | 37 | Child* child = NULL; // shared by Child.cpp and main.cpp |
37 | 38 | ||
38 | extern void cleanUp(); | 39 | extern void cleanUp(); |
@@ -78,7 +79,7 @@ static void child_handler(int signum) { | |||
78 | } | 79 | } |
79 | beenHere = true; | 80 | beenHere = true; |
80 | logg->logMessage("Gator is shutting down."); | 81 | logg->logMessage("Gator is shutting down."); |
81 | if (signum == SIGALRM || !collector) { | 82 | if (signum == SIGALRM || !primarySource) { |
82 | exit(1); | 83 | exit(1); |
83 | } else { | 84 | } else { |
84 | child->endSession(); | 85 | child->endSession(); |
@@ -139,77 +140,22 @@ static void *stopThread(void *) { | |||
139 | return 0; | 140 | return 0; |
140 | } | 141 | } |
141 | 142 | ||
142 | static void *countersThread(void *) { | ||
143 | prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); | ||
144 | |||
145 | gSessionData->hwmon.start(); | ||
146 | |||
147 | int64_t monotonic_started = 0; | ||
148 | while (monotonic_started <= 0) { | ||
149 | usleep(10); | ||
150 | |||
151 | if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { | ||
152 | logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); | ||
153 | handleException(); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | uint64_t next_time = 0; | ||
158 | while (gSessionData->mSessionIsActive) { | ||
159 | struct timespec ts; | ||
160 | #ifndef CLOCK_MONOTONIC_RAW | ||
161 | // Android doesn't have this defined but it was added in Linux 2.6.28 | ||
162 | #define CLOCK_MONOTONIC_RAW 4 | ||
163 | #endif | ||
164 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { | ||
165 | logg->logError(__FILE__, __LINE__, "Failed to get uptime"); | ||
166 | handleException(); | ||
167 | } | ||
168 | const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; | ||
169 | // Sample ten times a second ignoring gSessionData->mSampleRate | ||
170 | next_time += NS_PER_S/10;//gSessionData->mSampleRate; | ||
171 | if (next_time < curr_time) { | ||
172 | logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); | ||
173 | next_time = curr_time; | ||
174 | } | ||
175 | |||
176 | if (buffer->eventHeader(curr_time)) { | ||
177 | gSessionData->hwmon.read(buffer); | ||
178 | // Only check after writing all counters so that time and corresponding counters appear in the same frame | ||
179 | buffer->check(curr_time); | ||
180 | } | ||
181 | |||
182 | if (buffer->bytesAvailable() <= 0) { | ||
183 | logg->logMessage("One shot (counters)"); | ||
184 | child->endSession(); | ||
185 | } | ||
186 | |||
187 | usleep((next_time - curr_time)/NS_PER_US); | ||
188 | } | ||
189 | |||
190 | buffer->setDone(); | ||
191 | |||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | static void *senderThread(void *) { | 143 | static void *senderThread(void *) { |
196 | int length = 1; | ||
197 | char* data; | ||
198 | char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; | 144 | char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; |
199 | 145 | ||
200 | sem_post(&senderThreadStarted); | 146 | sem_post(&senderThreadStarted); |
201 | prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); | 147 | prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); |
202 | sem_wait(&haltPipeline); | 148 | sem_wait(&haltPipeline); |
203 | 149 | ||
204 | while (length > 0 || !buffer->isDone()) { | 150 | while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) { |
205 | sem_wait(&senderSem); | 151 | sem_wait(&senderSem); |
206 | data = collectorFifo->read(&length); | 152 | |
207 | if (data != NULL) { | 153 | primarySource->write(sender); |
208 | sender->writeData(data, length, RESPONSE_APC_DATA); | 154 | if (userSpaceSource != NULL) { |
209 | collectorFifo->release(); | 155 | userSpaceSource->write(sender); |
210 | } | 156 | } |
211 | if (!buffer->isDone()) { | 157 | if (externalSource != NULL) { |
212 | buffer->write(sender); | 158 | externalSource->write(sender); |
213 | } | 159 | } |
214 | } | 160 | } |
215 | 161 | ||
@@ -255,15 +201,13 @@ void Child::initialization() { | |||
255 | 201 | ||
256 | void Child::endSession() { | 202 | void Child::endSession() { |
257 | gSessionData->mSessionIsActive = false; | 203 | gSessionData->mSessionIsActive = false; |
258 | collector->stop(); | 204 | primarySource->interrupt(); |
259 | sem_post(&haltPipeline); | 205 | sem_post(&haltPipeline); |
260 | } | 206 | } |
261 | 207 | ||
262 | void Child::run() { | 208 | void Child::run() { |
263 | char* collectBuffer; | ||
264 | int bytesCollected = 0; | ||
265 | LocalCapture* localCapture = NULL; | 209 | LocalCapture* localCapture = NULL; |
266 | pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; | 210 | pthread_t durationThreadID, stopThreadID, senderThreadID; |
267 | 211 | ||
268 | prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); | 212 | prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); |
269 | 213 | ||
@@ -282,7 +226,11 @@ void Child::run() { | |||
282 | { ConfigurationXML configuration; } | 226 | { ConfigurationXML configuration; } |
283 | 227 | ||
284 | // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated | 228 | // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated |
285 | collector = new Collector(); | 229 | if (!gSessionData->perf.isSetup()) { |
230 | primarySource = new DriverSource(&senderSem, &startProfile); | ||
231 | } else { | ||
232 | primarySource = new PerfSource(&senderSem, &startProfile); | ||
233 | } | ||
286 | 234 | ||
287 | // Initialize all drivers | 235 | // Initialize all drivers |
288 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { | 236 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { |
@@ -317,15 +265,11 @@ void Child::run() { | |||
317 | free(xmlString); | 265 | free(xmlString); |
318 | } | 266 | } |
319 | 267 | ||
320 | // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length | 268 | // Must be after session XML is parsed |
321 | logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); | 269 | if (!primarySource->prepare()) { |
322 | collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); | 270 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); |
323 | 271 | handleException(); | |
324 | // Get the initial pointer to the collect buffer | 272 | } |
325 | collectBuffer = collectorFifo->start(); | ||
326 | |||
327 | // Create a new Block Counter Buffer | ||
328 | buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); | ||
329 | 273 | ||
330 | // Sender thread shall be halted until it is signaled for one shot mode | 274 | // Sender thread shall be halted until it is signaled for one shot mode |
331 | sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); | 275 | sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); |
@@ -340,14 +284,21 @@ void Child::run() { | |||
340 | thread_creation_success = false; | 284 | thread_creation_success = false; |
341 | } | 285 | } |
342 | 286 | ||
343 | bool startcountersThread = gSessionData->hwmon.countersEnabled(); | 287 | if (gSessionData->hwmon.countersEnabled()) { |
344 | if (startcountersThread) { | 288 | userSpaceSource = new UserSpaceSource(&senderSem); |
345 | if (pthread_create(&countersThreadID, NULL, countersThread, this)) { | 289 | if (!userSpaceSource->prepare()) { |
346 | thread_creation_success = false; | 290 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); |
291 | handleException(); | ||
347 | } | 292 | } |
348 | } else { | 293 | userSpaceSource->start(); |
349 | // Let senderThread know there is no buffer data to send | 294 | } |
350 | buffer->setDone(); | 295 | if (access("/tmp/gator", F_OK) == 0) { |
296 | externalSource = new ExternalSource(&senderSem); | ||
297 | if (!externalSource->prepare()) { | ||
298 | logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); | ||
299 | handleException(); | ||
300 | } | ||
301 | externalSource->start(); | ||
351 | } | 302 | } |
352 | 303 | ||
353 | if (!thread_creation_success) { | 304 | if (!thread_creation_success) { |
@@ -359,28 +310,13 @@ void Child::run() { | |||
359 | sem_wait(&senderThreadStarted); | 310 | sem_wait(&senderThreadStarted); |
360 | 311 | ||
361 | // Start profiling | 312 | // Start profiling |
362 | logg->logMessage("********** Profiling started **********"); | 313 | primarySource->run(); |
363 | collector->start(); | ||
364 | sem_post(&startProfile); | ||
365 | |||
366 | // Collect Data | ||
367 | do { | ||
368 | // This command will stall until data is received from the driver | ||
369 | bytesCollected = collector->collect(collectBuffer); | ||
370 | |||
371 | // In one shot mode, stop collection once all the buffers are filled | ||
372 | if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { | ||
373 | if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { | ||
374 | logg->logMessage("One shot"); | ||
375 | endSession(); | ||
376 | } | ||
377 | } | ||
378 | collectBuffer = collectorFifo->write(bytesCollected); | ||
379 | } while (bytesCollected > 0); | ||
380 | logg->logMessage("Exit collect data loop"); | ||
381 | 314 | ||
382 | if (startcountersThread) { | 315 | if (externalSource != NULL) { |
383 | pthread_join(countersThreadID, NULL); | 316 | externalSource->join(); |
317 | } | ||
318 | if (userSpaceSource != NULL) { | ||
319 | userSpaceSource->join(); | ||
384 | } | 320 | } |
385 | 321 | ||
386 | // Wait for the other threads to exit | 322 | // Wait for the other threads to exit |
@@ -401,9 +337,9 @@ void Child::run() { | |||
401 | 337 | ||
402 | logg->logMessage("Profiling ended."); | 338 | logg->logMessage("Profiling ended."); |
403 | 339 | ||
404 | delete buffer; | 340 | delete externalSource; |
405 | delete collectorFifo; | 341 | delete userSpaceSource; |
342 | delete primarySource; | ||
406 | delete sender; | 343 | delete sender; |
407 | delete collector; | ||
408 | delete localCapture; | 344 | delete localCapture; |
409 | } | 345 | } |
diff --git a/daemon/Child.h b/daemon/Child.h index 0330e9d..9e206d7 100644 --- a/daemon/Child.h +++ b/daemon/Child.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,8 +9,6 @@ | |||
9 | #ifndef __CHILD_H__ | 9 | #ifndef __CHILD_H__ |
10 | #define __CHILD_H__ | 10 | #define __CHILD_H__ |
11 | 11 | ||
12 | #include <pthread.h> | ||
13 | |||
14 | class OlySocket; | 12 | class OlySocket; |
15 | 13 | ||
16 | class Child { | 14 | class Child { |
diff --git a/daemon/Collector.h b/daemon/Collector.h deleted file mode 100644 index c5e9eac..0000000 --- a/daemon/Collector.h +++ /dev/null | |||
@@ -1,38 +0,0 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef __COLLECTOR_H__ | ||
10 | #define __COLLECTOR_H__ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | |||
14 | class Collector { | ||
15 | public: | ||
16 | Collector(); | ||
17 | ~Collector(); | ||
18 | void start(); | ||
19 | void stop(); | ||
20 | int collect(char* buffer); | ||
21 | int getBufferSize() {return mBufferSize;} | ||
22 | |||
23 | static int readIntDriver(const char* path, int* value); | ||
24 | static int readInt64Driver(const char* path, int64_t* value); | ||
25 | static int writeDriver(const char* path, int value); | ||
26 | static int writeDriver(const char* path, int64_t value); | ||
27 | static int writeDriver(const char* path, const char* data); | ||
28 | static int writeReadDriver(const char* path, int* value); | ||
29 | static int writeReadDriver(const char* path, int64_t* value); | ||
30 | |||
31 | private: | ||
32 | int mBufferSize; | ||
33 | int mBufferFD; | ||
34 | |||
35 | void checkVersion(); | ||
36 | }; | ||
37 | |||
38 | #endif //__COLLECTOR_H__ | ||
diff --git a/daemon/Config.h b/daemon/Config.h new file mode 100644 index 0000000..6f5e2aa --- /dev/null +++ b/daemon/Config.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef CONFIG_H | ||
10 | #define CONFIG_H | ||
11 | |||
12 | #define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0])) | ||
13 | |||
14 | #define MAX_PERFORMANCE_COUNTERS 50 | ||
15 | #define NR_CPUS 16 | ||
16 | |||
17 | #endif // CONFIG_H | ||
diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp index 2a5252a..fd479f2 100644 --- a/daemon/ConfigurationXML.cpp +++ b/daemon/ConfigurationXML.cpp | |||
@@ -1,15 +1,17 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "ConfigurationXML.h" | ||
10 | |||
9 | #include <string.h> | 11 | #include <string.h> |
10 | #include <stdlib.h> | 12 | #include <stdlib.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
12 | #include "ConfigurationXML.h" | 14 | |
13 | #include "Driver.h" | 15 | #include "Driver.h" |
14 | #include "Logging.h" | 16 | #include "Logging.h" |
15 | #include "OlyUtility.h" | 17 | #include "OlyUtility.h" |
@@ -67,6 +69,7 @@ int ConfigurationXML::parse(const char* configurationXML) { | |||
67 | 69 | ||
68 | // clear counter overflow | 70 | // clear counter overflow |
69 | gSessionData->mCounterOverflow = 0; | 71 | gSessionData->mCounterOverflow = 0; |
72 | gSessionData->mIsEBS = false; | ||
70 | mIndex = 0; | 73 | mIndex = 0; |
71 | 74 | ||
72 | // disable all counters prior to parsing the configuration xml | 75 | // disable all counters prior to parsing the configuration xml |
@@ -155,6 +158,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { | |||
155 | if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); | 158 | if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); |
156 | if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); | 159 | if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); |
157 | if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); | 160 | if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); |
161 | if (counter.getCount() > 0) { | ||
162 | gSessionData->mIsEBS = true; | ||
163 | } | ||
158 | counter.setEnabled(true); | 164 | counter.setEnabled(true); |
159 | 165 | ||
160 | // Associate a driver with each counter | 166 | // Associate a driver with each counter |
@@ -181,9 +187,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { | |||
181 | } | 187 | } |
182 | 188 | ||
183 | void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { | 189 | void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { |
184 | #include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len | 190 | #include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len |
185 | xml = (const char *)configuration_xml; | 191 | xml = (const char *)defaults_xml; |
186 | len = configuration_xml_len; | 192 | len = defaults_xml_len; |
187 | } | 193 | } |
188 | 194 | ||
189 | void ConfigurationXML::getPath(char* path) { | 195 | void ConfigurationXML::getPath(char* path) { |
diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h index 5650f48..efa415e 100644 --- a/daemon/ConfigurationXML.h +++ b/daemon/ConfigurationXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Counter.h b/daemon/Counter.h index 231a85d..6891745 100644 --- a/daemon/Counter.h +++ b/daemon/Counter.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -25,7 +25,7 @@ public: | |||
25 | void clear () { | 25 | void clear () { |
26 | mType[0] = '\0'; | 26 | mType[0] = '\0'; |
27 | mEnabled = false; | 27 | mEnabled = false; |
28 | mEvent = 0; | 28 | mEvent = -1; |
29 | mCount = 0; | 29 | mCount = 0; |
30 | mKey = 0; | 30 | mKey = 0; |
31 | mDriver = NULL; | 31 | mDriver = NULL; |
diff --git a/daemon/Driver.cpp b/daemon/Driver.cpp index c262467..09e0401 100644 --- a/daemon/Driver.cpp +++ b/daemon/Driver.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Driver.h b/daemon/Driver.h index f3a932f..e5ed7b6 100644 --- a/daemon/Driver.h +++ b/daemon/Driver.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -27,7 +27,7 @@ public: | |||
27 | virtual void setupCounter(Counter &counter) = 0; | 27 | virtual void setupCounter(Counter &counter) = 0; |
28 | 28 | ||
29 | // Emits available counters | 29 | // Emits available counters |
30 | virtual void writeCounters(mxml_node_t *root) const = 0; | 30 | virtual int writeCounters(mxml_node_t *root) const = 0; |
31 | // Emits possible dynamically generated events/counters | 31 | // Emits possible dynamically generated events/counters |
32 | virtual void writeEvents(mxml_node_t *) const {} | 32 | virtual void writeEvents(mxml_node_t *) const {} |
33 | 33 | ||
diff --git a/daemon/Collector.cpp b/daemon/DriverSource.cpp index bf73534..f78ec6b 100644 --- a/daemon/Collector.cpp +++ b/daemon/DriverSource.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -8,23 +8,47 @@ | |||
8 | 8 | ||
9 | #define __STDC_FORMAT_MACROS | 9 | #define __STDC_FORMAT_MACROS |
10 | 10 | ||
11 | #include "DriverSource.h" | ||
12 | |||
11 | #include <fcntl.h> | 13 | #include <fcntl.h> |
12 | #include <unistd.h> | ||
13 | #include <string.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <errno.h> | ||
16 | #include <sys/time.h> | ||
17 | #include <inttypes.h> | 14 | #include <inttypes.h> |
18 | #include "Collector.h" | 15 | #include <unistd.h> |
19 | #include "SessionData.h" | 16 | |
17 | #include "Child.h" | ||
18 | #include "Fifo.h" | ||
20 | #include "Logging.h" | 19 | #include "Logging.h" |
21 | #include "Sender.h" | 20 | #include "Sender.h" |
21 | #include "SessionData.h" | ||
22 | |||
23 | extern Child *child; | ||
22 | 24 | ||
23 | // Driver initialization independent of session settings | 25 | DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { |
24 | Collector::Collector() { | 26 | int driver_version = 0; |
25 | mBufferFD = 0; | ||
26 | 27 | ||
27 | checkVersion(); | 28 | if (readIntDriver("/dev/gator/version", &driver_version) == -1) { |
29 | logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); | ||
30 | handleException(); | ||
31 | } | ||
32 | |||
33 | // Verify the driver version matches the daemon version | ||
34 | if (driver_version != PROTOCOL_VERSION) { | ||
35 | if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { | ||
36 | // One of the mismatched versions is development version | ||
37 | logg->logError(__FILE__, __LINE__, | ||
38 | "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" | ||
39 | ">> The following must be synchronized from engineering repository:\n" | ||
40 | ">> * gator driver\n" | ||
41 | ">> * gator daemon\n" | ||
42 | ">> * Streamline", driver_version, PROTOCOL_VERSION); | ||
43 | handleException(); | ||
44 | } else { | ||
45 | // Release version mismatch | ||
46 | logg->logError(__FILE__, __LINE__, | ||
47 | "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" | ||
48 | ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); | ||
49 | handleException(); | ||
50 | } | ||
51 | } | ||
28 | 52 | ||
29 | int enable = -1; | 53 | int enable = -1; |
30 | if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { | 54 | if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { |
@@ -37,14 +61,15 @@ Collector::Collector() { | |||
37 | gSessionData->mCores = 1; | 61 | gSessionData->mCores = 1; |
38 | } | 62 | } |
39 | 63 | ||
40 | mBufferSize = 0; | ||
41 | if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { | 64 | if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { |
42 | logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); | 65 | logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); |
43 | handleException(); | 66 | handleException(); |
44 | } | 67 | } |
45 | } | 68 | } |
46 | 69 | ||
47 | Collector::~Collector() { | 70 | DriverSource::~DriverSource() { |
71 | delete mFifo; | ||
72 | |||
48 | // Write zero for safety, as a zero should have already been written | 73 | // Write zero for safety, as a zero should have already been written |
49 | writeDriver("/dev/gator/enable", "0"); | 74 | writeDriver("/dev/gator/enable", "0"); |
50 | 75 | ||
@@ -54,36 +79,21 @@ Collector::~Collector() { | |||
54 | } | 79 | } |
55 | } | 80 | } |
56 | 81 | ||
57 | void Collector::checkVersion() { | 82 | bool DriverSource::prepare() { |
58 | int driver_version = 0; | 83 | // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length |
59 | 84 | logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); | |
60 | if (readIntDriver("/dev/gator/version", &driver_version) == -1) { | 85 | mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); |
61 | logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); | ||
62 | handleException(); | ||
63 | } | ||
64 | 86 | ||
65 | // Verify the driver version matches the daemon version | 87 | return true; |
66 | if (driver_version != PROTOCOL_VERSION) { | ||
67 | if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { | ||
68 | // One of the mismatched versions is development version | ||
69 | logg->logError(__FILE__, __LINE__, | ||
70 | "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" | ||
71 | ">> The following must be synchronized from engineering repository:\n" | ||
72 | ">> * gator driver\n" | ||
73 | ">> * gator daemon\n" | ||
74 | ">> * Streamline", driver_version, PROTOCOL_VERSION); | ||
75 | handleException(); | ||
76 | } else { | ||
77 | // Release version mismatch | ||
78 | logg->logError(__FILE__, __LINE__, | ||
79 | "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" | ||
80 | ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); | ||
81 | handleException(); | ||
82 | } | ||
83 | } | ||
84 | } | 88 | } |
85 | 89 | ||
86 | void Collector::start() { | 90 | void DriverSource::run() { |
91 | // Get the initial pointer to the collect buffer | ||
92 | char *collectBuffer = mFifo->start(); | ||
93 | int bytesCollected = 0; | ||
94 | |||
95 | logg->logMessage("********** Profiling started **********"); | ||
96 | |||
87 | // Set the maximum backtrace depth | 97 | // Set the maximum backtrace depth |
88 | if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { | 98 | if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { |
89 | logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); | 99 | logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); |
@@ -125,79 +135,112 @@ void Collector::start() { | |||
125 | } | 135 | } |
126 | 136 | ||
127 | lseek(mBufferFD, 0, SEEK_SET); | 137 | lseek(mBufferFD, 0, SEEK_SET); |
138 | |||
139 | sem_post(mStartProfile); | ||
140 | |||
141 | // Collect Data | ||
142 | do { | ||
143 | // This command will stall until data is received from the driver | ||
144 | // Calls event_buffer_read in the driver | ||
145 | errno = 0; | ||
146 | bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); | ||
147 | |||
148 | // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data | ||
149 | if (bytesCollected == -1 && errno == EINTR) { | ||
150 | bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); | ||
151 | } | ||
152 | |||
153 | // return the total bytes written | ||
154 | logg->logMessage("Driver read of %d bytes", bytesCollected); | ||
155 | |||
156 | // In one shot mode, stop collection once all the buffers are filled | ||
157 | if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { | ||
158 | if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { | ||
159 | logg->logMessage("One shot"); | ||
160 | child->endSession(); | ||
161 | } | ||
162 | } | ||
163 | collectBuffer = mFifo->write(bytesCollected); | ||
164 | } while (bytesCollected > 0); | ||
165 | |||
166 | logg->logMessage("Exit collect data loop"); | ||
128 | } | 167 | } |
129 | 168 | ||
130 | // These commands should cause the read() function in collect() to return | 169 | void DriverSource::interrupt() { |
131 | void Collector::stop() { | 170 | // This command should cause the read() function in collect() to return and stop the driver from profiling |
132 | // This will stop the driver from profiling | ||
133 | if (writeDriver("/dev/gator/enable", "0") != 0) { | 171 | if (writeDriver("/dev/gator/enable", "0") != 0) { |
134 | logg->logMessage("Stopping kernel failed"); | 172 | logg->logMessage("Stopping kernel failed"); |
135 | } | 173 | } |
136 | } | 174 | } |
137 | 175 | ||
138 | int Collector::collect(char* buffer) { | 176 | bool DriverSource::isDone() { |
139 | // Calls event_buffer_read in the driver | 177 | return mLength <= 0; |
140 | int bytesRead; | 178 | } |
141 | |||
142 | errno = 0; | ||
143 | bytesRead = read(mBufferFD, buffer, mBufferSize); | ||
144 | 179 | ||
145 | // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data | 180 | void DriverSource::write(Sender *sender) { |
146 | if (bytesRead == -1 && errno == EINTR) { | 181 | char *data = mFifo->read(&mLength); |
147 | bytesRead = read(mBufferFD, buffer, mBufferSize); | 182 | if (data != NULL) { |
183 | sender->writeData(data, mLength, RESPONSE_APC_DATA); | ||
184 | mFifo->release(); | ||
148 | } | 185 | } |
149 | |||
150 | // return the total bytes written | ||
151 | logg->logMessage("Driver read of %d bytes", bytesRead); | ||
152 | return bytesRead; | ||
153 | } | 186 | } |
154 | 187 | ||
155 | int Collector::readIntDriver(const char* fullpath, int* value) { | 188 | int DriverSource::readIntDriver(const char *fullpath, int *value) { |
156 | FILE* file = fopen(fullpath, "r"); | 189 | char data[40]; // Sufficiently large to hold any integer |
157 | if (file == NULL) { | 190 | const int fd = open(fullpath, O_RDONLY); |
191 | if (fd < 0) { | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | const ssize_t bytes = read(fd, data, sizeof(data) - 1); | ||
196 | close(fd); | ||
197 | if (bytes < 0) { | ||
158 | return -1; | 198 | return -1; |
159 | } | 199 | } |
160 | if (fscanf(file, "%u", value) != 1) { | 200 | data[bytes] = '\0'; |
161 | fclose(file); | 201 | |
202 | char *endptr; | ||
203 | errno = 0; | ||
204 | *value = strtol(data, &endptr, 10); | ||
205 | if (errno != 0 || *endptr != '\n') { | ||
162 | logg->logMessage("Invalid value in file %s", fullpath); | 206 | logg->logMessage("Invalid value in file %s", fullpath); |
163 | return -1; | 207 | return -1; |
164 | } | 208 | } |
165 | fclose(file); | 209 | |
166 | return 0; | 210 | return 0; |
167 | } | 211 | } |
168 | 212 | ||
169 | int Collector::readInt64Driver(const char* fullpath, int64_t* value) { | 213 | int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { |
170 | FILE* file = fopen(fullpath, "r"); | 214 | char data[40]; // Sufficiently large to hold any integer |
171 | if (file == NULL) { | 215 | const int fd = open(fullpath, O_RDONLY); |
216 | if (fd < 0) { | ||
172 | return -1; | 217 | return -1; |
173 | } | 218 | } |
174 | if (fscanf(file, "%" SCNi64, value) != 1) { | 219 | |
175 | fclose(file); | 220 | const ssize_t bytes = read(fd, data, sizeof(data) - 1); |
176 | logg->logMessage("Invalid value in file %s", fullpath); | 221 | close(fd); |
222 | if (bytes < 0) { | ||
177 | return -1; | 223 | return -1; |
178 | } | 224 | } |
179 | fclose(file); | 225 | data[bytes] = '\0'; |
180 | return 0; | ||
181 | } | ||
182 | 226 | ||
183 | int Collector::writeDriver(const char* path, int value) { | 227 | char *endptr; |
184 | char data[40]; // Sufficiently large to hold any integer | 228 | errno = 0; |
185 | snprintf(data, sizeof(data), "%d", value); | 229 | *value = strtoll(data, &endptr, 10); |
186 | return writeDriver(path, data); | 230 | if (errno != 0 || *endptr != '\n') { |
187 | } | 231 | logg->logMessage("Invalid value in file %s", fullpath); |
232 | return -1; | ||
233 | } | ||
188 | 234 | ||
189 | int Collector::writeDriver(const char* path, int64_t value) { | 235 | return 0; |
190 | char data[40]; // Sufficiently large to hold any integer | ||
191 | snprintf(data, sizeof(data), "%" PRIi64, value); | ||
192 | return writeDriver(path, data); | ||
193 | } | 236 | } |
194 | 237 | ||
195 | int Collector::writeDriver(const char* fullpath, const char* data) { | 238 | int DriverSource::writeDriver(const char *fullpath, const char *data) { |
196 | int fd = open(fullpath, O_WRONLY); | 239 | int fd = open(fullpath, O_WRONLY); |
197 | if (fd < 0) { | 240 | if (fd < 0) { |
198 | return -1; | 241 | return -1; |
199 | } | 242 | } |
200 | if (write(fd, data, strlen(data)) < 0) { | 243 | if (::write(fd, data, strlen(data)) < 0) { |
201 | close(fd); | 244 | close(fd); |
202 | logg->logMessage("Opened but could not write to %s", fullpath); | 245 | logg->logMessage("Opened but could not write to %s", fullpath); |
203 | return -1; | 246 | return -1; |
@@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) { | |||
206 | return 0; | 249 | return 0; |
207 | } | 250 | } |
208 | 251 | ||
209 | int Collector::writeReadDriver(const char* path, int* value) { | 252 | int DriverSource::writeDriver(const char *path, int value) { |
253 | char data[40]; // Sufficiently large to hold any integer | ||
254 | snprintf(data, sizeof(data), "%d", value); | ||
255 | return writeDriver(path, data); | ||
256 | } | ||
257 | |||
258 | int DriverSource::writeDriver(const char *path, int64_t value) { | ||
259 | char data[40]; // Sufficiently large to hold any integer | ||
260 | snprintf(data, sizeof(data), "%" PRIi64, value); | ||
261 | return writeDriver(path, data); | ||
262 | } | ||
263 | |||
264 | int DriverSource::writeReadDriver(const char *path, int *value) { | ||
210 | if (writeDriver(path, *value) || readIntDriver(path, value)) { | 265 | if (writeDriver(path, *value) || readIntDriver(path, value)) { |
211 | return -1; | 266 | return -1; |
212 | } | 267 | } |
213 | return 0; | 268 | return 0; |
214 | } | 269 | } |
215 | 270 | ||
216 | int Collector::writeReadDriver(const char* path, int64_t* value) { | 271 | int DriverSource::writeReadDriver(const char *path, int64_t *value) { |
217 | if (writeDriver(path, *value) || readInt64Driver(path, value)) { | 272 | if (writeDriver(path, *value) || readInt64Driver(path, value)) { |
218 | return -1; | 273 | return -1; |
219 | } | 274 | } |
diff --git a/daemon/DriverSource.h b/daemon/DriverSource.h new file mode 100644 index 0000000..dcf1078 --- /dev/null +++ b/daemon/DriverSource.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef DRIVERSOURCE_H | ||
10 | #define DRIVERSOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | #include <stdint.h> | ||
14 | |||
15 | #include "Source.h" | ||
16 | |||
17 | class Fifo; | ||
18 | |||
19 | class DriverSource : public Source { | ||
20 | public: | ||
21 | DriverSource(sem_t *senderSem, sem_t *startProfile); | ||
22 | ~DriverSource(); | ||
23 | |||
24 | bool prepare(); | ||
25 | void run(); | ||
26 | void interrupt(); | ||
27 | |||
28 | bool isDone(); | ||
29 | void write(Sender *sender); | ||
30 | |||
31 | static int readIntDriver(const char *fullpath, int *value); | ||
32 | static int readInt64Driver(const char *fullpath, int64_t *value); | ||
33 | static int writeDriver(const char *fullpath, const char *data); | ||
34 | static int writeDriver(const char *path, int value); | ||
35 | static int writeDriver(const char *path, int64_t value); | ||
36 | static int writeReadDriver(const char *path, int *value); | ||
37 | static int writeReadDriver(const char *path, int64_t *value); | ||
38 | |||
39 | private: | ||
40 | Fifo *mFifo; | ||
41 | sem_t *const mSenderSem; | ||
42 | sem_t *const mStartProfile; | ||
43 | int mBufferSize; | ||
44 | int mBufferFD; | ||
45 | int mLength; | ||
46 | |||
47 | // Intentionally unimplemented | ||
48 | DriverSource(const DriverSource &); | ||
49 | DriverSource &operator=(const DriverSource &); | ||
50 | }; | ||
51 | |||
52 | #endif // DRIVERSOURCE_H | ||
diff --git a/daemon/DynBuf.cpp b/daemon/DynBuf.cpp new file mode 100644 index 0000000..6f92b33 --- /dev/null +++ b/daemon/DynBuf.cpp | |||
@@ -0,0 +1,139 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "DynBuf.h" | ||
10 | |||
11 | #include <errno.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <stdarg.h> | ||
14 | #include <stdio.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include "Logging.h" | ||
18 | |||
19 | // Pick an aggressive size as buffer is primarily used for disk IO | ||
20 | #define MIN_BUFFER_FREE (1 << 12) | ||
21 | |||
22 | int DynBuf::resize(const size_t minCapacity) { | ||
23 | size_t scaledCapacity = 2 * capacity; | ||
24 | if (scaledCapacity < minCapacity) { | ||
25 | scaledCapacity = minCapacity; | ||
26 | } | ||
27 | if (scaledCapacity < 2 * MIN_BUFFER_FREE) { | ||
28 | scaledCapacity = 2 * MIN_BUFFER_FREE; | ||
29 | } | ||
30 | capacity = scaledCapacity; | ||
31 | |||
32 | buf = static_cast<char *>(realloc(buf, capacity)); | ||
33 | if (buf == NULL) { | ||
34 | return -errno; | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | bool DynBuf::read(const char *const path) { | ||
41 | int result = false; | ||
42 | |||
43 | const int fd = open(path, O_RDONLY); | ||
44 | if (fd < 0) { | ||
45 | logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | length = 0; | ||
50 | |||
51 | for (;;) { | ||
52 | const size_t minCapacity = length + MIN_BUFFER_FREE + 1; | ||
53 | if (capacity < minCapacity) { | ||
54 | if (resize(minCapacity) != 0) { | ||
55 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
56 | goto fail; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); | ||
61 | if (bytes < 0) { | ||
62 | logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); | ||
63 | goto fail; | ||
64 | } else if (bytes == 0) { | ||
65 | break; | ||
66 | } | ||
67 | length += bytes; | ||
68 | } | ||
69 | |||
70 | buf[length] = '\0'; | ||
71 | result = true; | ||
72 | |||
73 | fail: | ||
74 | close(fd); | ||
75 | |||
76 | return result; | ||
77 | } | ||
78 | |||
79 | int DynBuf::readlink(const char *const path) { | ||
80 | ssize_t bytes = MIN_BUFFER_FREE; | ||
81 | |||
82 | for (;;) { | ||
83 | if (static_cast<size_t>(bytes) >= capacity) { | ||
84 | const int err = resize(2 * bytes); | ||
85 | if (err != 0) { | ||
86 | return err; | ||
87 | } | ||
88 | } | ||
89 | bytes = ::readlink(path, buf, capacity); | ||
90 | if (bytes < 0) { | ||
91 | return -errno; | ||
92 | } else if (static_cast<size_t>(bytes) < capacity) { | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | length = bytes; | ||
98 | buf[bytes] = '\0'; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | bool DynBuf::printf(const char *format, ...) { | ||
104 | va_list ap; | ||
105 | |||
106 | if (capacity <= 0) { | ||
107 | if (resize(2 * MIN_BUFFER_FREE) != 0) { | ||
108 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
109 | return false; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | va_start(ap, format); | ||
114 | int bytes = vsnprintf(buf, capacity, format, ap); | ||
115 | va_end(ap); | ||
116 | if (bytes < 0) { | ||
117 | logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | if (static_cast<size_t>(bytes) > capacity) { | ||
122 | if (resize(bytes + 1) != 0) { | ||
123 | logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); | ||
124 | return false; | ||
125 | } | ||
126 | |||
127 | va_start(ap, format); | ||
128 | bytes = vsnprintf(buf, capacity, format, ap); | ||
129 | va_end(ap); | ||
130 | if (bytes < 0) { | ||
131 | logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); | ||
132 | return false; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | length = bytes; | ||
137 | |||
138 | return true; | ||
139 | } | ||
diff --git a/daemon/DynBuf.h b/daemon/DynBuf.h new file mode 100644 index 0000000..2f4554a --- /dev/null +++ b/daemon/DynBuf.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef DYNBUF_H | ||
10 | #define DYNBUF_H | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | |||
14 | class DynBuf { | ||
15 | public: | ||
16 | DynBuf() : capacity(0), length(0), buf(NULL) {} | ||
17 | ~DynBuf() { | ||
18 | reset(); | ||
19 | } | ||
20 | |||
21 | inline void reset() { | ||
22 | capacity = 0; | ||
23 | length = 0; | ||
24 | if (buf != NULL) { | ||
25 | free(buf); | ||
26 | buf = NULL; | ||
27 | } | ||
28 | } | ||
29 | |||
30 | bool read(const char *const path); | ||
31 | // On error instead of printing the error and returning false, this returns -errno | ||
32 | int readlink(const char *const path); | ||
33 | __attribute__ ((format(printf, 2, 3))) | ||
34 | bool printf(const char *format, ...); | ||
35 | |||
36 | size_t getLength() const { return length; } | ||
37 | const char *getBuf() const { return buf; } | ||
38 | char *getBuf() { return buf; } | ||
39 | |||
40 | private: | ||
41 | int resize(const size_t minCapacity); | ||
42 | |||
43 | size_t capacity; | ||
44 | size_t length; | ||
45 | char *buf; | ||
46 | |||
47 | // Intentionally undefined | ||
48 | DynBuf(const DynBuf &); | ||
49 | DynBuf &operator=(const DynBuf &); | ||
50 | }; | ||
51 | |||
52 | #endif // DYNBUF_H | ||
diff --git a/daemon/EventsXML.cpp b/daemon/EventsXML.cpp index 2a80482..a07a046 100644 --- a/daemon/EventsXML.cpp +++ b/daemon/EventsXML.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -35,7 +35,7 @@ char* EventsXML::getXML() { | |||
35 | fclose(fl); | 35 | fclose(fl); |
36 | } else { | 36 | } else { |
37 | logg->logMessage("Unable to locate events.xml, using default"); | 37 | logg->logMessage("Unable to locate events.xml, using default"); |
38 | xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); | 38 | xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); |
39 | } | 39 | } |
40 | 40 | ||
41 | // Add dynamic events from the drivers | 41 | // Add dynamic events from the drivers |
diff --git a/daemon/EventsXML.h b/daemon/EventsXML.h index 8e693ef..6cd1560 100644 --- a/daemon/EventsXML.h +++ b/daemon/EventsXML.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/ExternalSource.cpp b/daemon/ExternalSource.cpp new file mode 100644 index 0000000..fe5824b --- /dev/null +++ b/daemon/ExternalSource.cpp | |||
@@ -0,0 +1,56 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "ExternalSource.h" | ||
10 | |||
11 | #include <sys/prctl.h> | ||
12 | |||
13 | #include "Logging.h" | ||
14 | #include "OlySocket.h" | ||
15 | #include "SessionData.h" | ||
16 | |||
17 | ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") { | ||
18 | } | ||
19 | |||
20 | ExternalSource::~ExternalSource() { | ||
21 | } | ||
22 | |||
23 | bool ExternalSource::prepare() { | ||
24 | return true; | ||
25 | } | ||
26 | |||
27 | void ExternalSource::run() { | ||
28 | prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0); | ||
29 | |||
30 | while (gSessionData->mSessionIsActive) { | ||
31 | // Will be aborted when the socket is closed at the end of the capture | ||
32 | int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable()); | ||
33 | if (length <= 0) { | ||
34 | break; | ||
35 | } | ||
36 | |||
37 | mBuffer.advanceWrite(length); | ||
38 | mBuffer.check(0); | ||
39 | } | ||
40 | |||
41 | mBuffer.setDone(); | ||
42 | } | ||
43 | |||
44 | void ExternalSource::interrupt() { | ||
45 | // Do nothing | ||
46 | } | ||
47 | |||
48 | bool ExternalSource::isDone() { | ||
49 | return mBuffer.isDone(); | ||
50 | } | ||
51 | |||
52 | void ExternalSource::write(Sender *sender) { | ||
53 | if (!mBuffer.isDone()) { | ||
54 | mBuffer.write(sender); | ||
55 | } | ||
56 | } | ||
diff --git a/daemon/ExternalSource.h b/daemon/ExternalSource.h new file mode 100644 index 0000000..2052bdf --- /dev/null +++ b/daemon/ExternalSource.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef EXTERNALSOURCE_H | ||
10 | #define EXTERNALSOURCE_H | ||
11 | |||
12 | #include <semaphore.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "OlySocket.h" | ||
16 | #include "Source.h" | ||
17 | |||
18 | // Unix domain socket counters from external sources like graphics drivers | ||
19 | class ExternalSource : public Source { | ||
20 | public: | ||
21 | ExternalSource(sem_t *senderSem); | ||
22 | ~ExternalSource(); | ||
23 | |||
24 | bool prepare(); | ||
25 | void run(); | ||
26 | void interrupt(); | ||
27 | |||
28 | bool isDone(); | ||
29 | void write(Sender *sender); | ||
30 | |||
31 | private: | ||
32 | Buffer mBuffer; | ||
33 | OlySocket mSock; | ||
34 | |||
35 | // Intentionally unimplemented | ||
36 | ExternalSource(const ExternalSource &); | ||
37 | ExternalSource &operator=(const ExternalSource &); | ||
38 | }; | ||
39 | |||
40 | #endif // EXTERNALSOURCE_H | ||
diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp index 250a4d0..f672e92 100644 --- a/daemon/Fifo.cpp +++ b/daemon/Fifo.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Fifo.h b/daemon/Fifo.h index d25cd68..7dd7426 100644 --- a/daemon/Fifo.h +++ b/daemon/Fifo.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,7 +12,7 @@ | |||
12 | #ifdef WIN32 | 12 | #ifdef WIN32 |
13 | #include <windows.h> | 13 | #include <windows.h> |
14 | #define sem_t HANDLE | 14 | #define sem_t HANDLE |
15 | #define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL) | 15 | #define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL) |
16 | #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) | 16 | #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) |
17 | #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) | 17 | #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) |
18 | #define sem_destroy(sem) CloseHandle(*(sem)) | 18 | #define sem_destroy(sem) CloseHandle(*(sem)) |
diff --git a/daemon/Hwmon.cpp b/daemon/Hwmon.cpp index 1d7c0da..778f307 100644 --- a/daemon/Hwmon.cpp +++ b/daemon/Hwmon.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -17,7 +17,7 @@ | |||
17 | 17 | ||
18 | class HwmonCounter { | 18 | class HwmonCounter { |
19 | public: | 19 | public: |
20 | HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature); | 20 | HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature); |
21 | ~HwmonCounter(); | 21 | ~HwmonCounter(); |
22 | 22 | ||
23 | HwmonCounter *getNext() const { return next; } | 23 | HwmonCounter *getNext() const { return next; } |
@@ -69,7 +69,7 @@ private: | |||
69 | HwmonCounter &operator=(const HwmonCounter &); | 69 | HwmonCounter &operator=(const HwmonCounter &); |
70 | }; | 70 | }; |
71 | 71 | ||
72 | HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { | 72 | HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { |
73 | 73 | ||
74 | int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; | 74 | int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; |
75 | char *chip_name = new char[len]; | 75 | char *chip_name = new char[len]; |
@@ -205,6 +205,23 @@ bool HwmonCounter::canRead() { | |||
205 | } | 205 | } |
206 | 206 | ||
207 | Hwmon::Hwmon() : counters(NULL) { | 207 | Hwmon::Hwmon() : counters(NULL) { |
208 | } | ||
209 | |||
210 | Hwmon::~Hwmon() { | ||
211 | while (counters != NULL) { | ||
212 | HwmonCounter * counter = counters; | ||
213 | counters = counter->getNext(); | ||
214 | delete counter; | ||
215 | } | ||
216 | sensors_cleanup(); | ||
217 | } | ||
218 | |||
219 | void Hwmon::setup() { | ||
220 | // hwmon does not currently work with perf | ||
221 | if (gSessionData->perf.isSetup()) { | ||
222 | return; | ||
223 | } | ||
224 | |||
208 | int err = sensors_init(NULL); | 225 | int err = sensors_init(NULL); |
209 | if (err) { | 226 | if (err) { |
210 | logg->logMessage("Failed to initialize libsensors! (%d)", err); | 227 | logg->logMessage("Failed to initialize libsensors! (%d)", err); |
@@ -218,20 +235,11 @@ Hwmon::Hwmon() : counters(NULL) { | |||
218 | int feature_nr = 0; | 235 | int feature_nr = 0; |
219 | const sensors_feature *feature; | 236 | const sensors_feature *feature; |
220 | while ((feature = sensors_get_features(chip, &feature_nr))) { | 237 | while ((feature = sensors_get_features(chip, &feature_nr))) { |
221 | counters = new HwmonCounter(counters, getEventKey(), chip, feature); | 238 | counters = new HwmonCounter(counters, chip, feature); |
222 | } | 239 | } |
223 | } | 240 | } |
224 | } | 241 | } |
225 | 242 | ||
226 | Hwmon::~Hwmon() { | ||
227 | while (counters != NULL) { | ||
228 | HwmonCounter * counter = counters; | ||
229 | counters = counter->getNext(); | ||
230 | delete counter; | ||
231 | } | ||
232 | sensors_cleanup(); | ||
233 | } | ||
234 | |||
235 | HwmonCounter *Hwmon::findCounter(const Counter &counter) const { | 243 | HwmonCounter *Hwmon::findCounter(const Counter &counter) const { |
236 | for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { | 244 | for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { |
237 | if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { | 245 | if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { |
@@ -271,14 +279,18 @@ void Hwmon::setupCounter(Counter &counter) { | |||
271 | counter.setKey(hwmonCounter->getKey()); | 279 | counter.setKey(hwmonCounter->getKey()); |
272 | } | 280 | } |
273 | 281 | ||
274 | void Hwmon::writeCounters(mxml_node_t *root) const { | 282 | int Hwmon::writeCounters(mxml_node_t *root) const { |
283 | int count = 0; | ||
275 | for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { | 284 | for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { |
276 | if (!counter->canRead()) { | 285 | if (!counter->canRead()) { |
277 | continue; | 286 | continue; |
278 | } | 287 | } |
279 | mxml_node_t *node = mxmlNewElement(root, "counter"); | 288 | mxml_node_t *node = mxmlNewElement(root, "counter"); |
280 | mxmlElementSetAttr(node, "name", counter->getName()); | 289 | mxmlElementSetAttr(node, "name", counter->getName()); |
290 | ++count; | ||
281 | } | 291 | } |
292 | |||
293 | return count; | ||
282 | } | 294 | } |
283 | 295 | ||
284 | void Hwmon::writeEvents(mxml_node_t *root) const { | 296 | void Hwmon::writeEvents(mxml_node_t *root) const { |
diff --git a/daemon/Hwmon.h b/daemon/Hwmon.h index 46bb42e..a22a360 100644 --- a/daemon/Hwmon.h +++ b/daemon/Hwmon.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -19,12 +19,14 @@ public: | |||
19 | Hwmon(); | 19 | Hwmon(); |
20 | ~Hwmon(); | 20 | ~Hwmon(); |
21 | 21 | ||
22 | void setup(); | ||
23 | |||
22 | bool claimCounter(const Counter &counter) const; | 24 | bool claimCounter(const Counter &counter) const; |
23 | bool countersEnabled() const; | 25 | bool countersEnabled() const; |
24 | void resetCounters(); | 26 | void resetCounters(); |
25 | void setupCounter(Counter &counter); | 27 | void setupCounter(Counter &counter); |
26 | 28 | ||
27 | void writeCounters(mxml_node_t *root) const; | 29 | int writeCounters(mxml_node_t *root) const; |
28 | void writeEvents(mxml_node_t *root) const; | 30 | void writeEvents(mxml_node_t *root) const; |
29 | 31 | ||
30 | void start(); | 32 | void start(); |
diff --git a/daemon/KMod.cpp b/daemon/KMod.cpp index 559297f..9300002 100644 --- a/daemon/KMod.cpp +++ b/daemon/KMod.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -12,9 +12,9 @@ | |||
12 | #include <dirent.h> | 12 | #include <dirent.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | 14 | ||
15 | #include "Collector.h" | ||
16 | #include "ConfigurationXML.h" | 15 | #include "ConfigurationXML.h" |
17 | #include "Counter.h" | 16 | #include "Counter.h" |
17 | #include "DriverSource.h" | ||
18 | #include "Logging.h" | 18 | #include "Logging.h" |
19 | 19 | ||
20 | // Claim all the counters in /dev/gator/events | 20 | // Claim all the counters in /dev/gator/events |
@@ -38,9 +38,9 @@ void KMod::resetCounters() { | |||
38 | continue; | 38 | continue; |
39 | snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); | 39 | snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); |
40 | snprintf(text, sizeof(text), "%s/enabled", base); | 40 | snprintf(text, sizeof(text), "%s/enabled", base); |
41 | Collector::writeDriver(text, 0); | 41 | DriverSource::writeDriver(text, 0); |
42 | snprintf(text, sizeof(text), "%s/count", base); | 42 | snprintf(text, sizeof(text), "%s/count", base); |
43 | Collector::writeDriver(text, 0); | 43 | DriverSource::writeDriver(text, 0); |
44 | } | 44 | } |
45 | closedir(dir); | 45 | closedir(dir); |
46 | } | 46 | } |
@@ -53,22 +53,22 @@ void KMod::setupCounter(Counter &counter) { | |||
53 | 53 | ||
54 | snprintf(text, sizeof(text), "%s/enabled", base); | 54 | snprintf(text, sizeof(text), "%s/enabled", base); |
55 | int enabled = true; | 55 | int enabled = true; |
56 | if (Collector::writeReadDriver(text, &enabled) || !enabled) { | 56 | if (DriverSource::writeReadDriver(text, &enabled) || !enabled) { |
57 | counter.setEnabled(false); | 57 | counter.setEnabled(false); |
58 | return; | 58 | return; |
59 | } | 59 | } |
60 | 60 | ||
61 | snprintf(text, sizeof(text), "%s/key", base); | 61 | snprintf(text, sizeof(text), "%s/key", base); |
62 | int key = 0; | 62 | int key = 0; |
63 | Collector::readIntDriver(text, &key); | 63 | DriverSource::readIntDriver(text, &key); |
64 | counter.setKey(key); | 64 | counter.setKey(key); |
65 | 65 | ||
66 | snprintf(text, sizeof(text), "%s/event", base); | 66 | snprintf(text, sizeof(text), "%s/event", base); |
67 | Collector::writeDriver(text, counter.getEvent()); | 67 | DriverSource::writeDriver(text, counter.getEvent()); |
68 | snprintf(text, sizeof(text), "%s/count", base); | 68 | snprintf(text, sizeof(text), "%s/count", base); |
69 | if (access(text, F_OK) == 0) { | 69 | if (access(text, F_OK) == 0) { |
70 | int count = counter.getCount(); | 70 | int count = counter.getCount(); |
71 | if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { | 71 | if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { |
72 | logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); | 72 | logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); |
73 | handleException(); | 73 | handleException(); |
74 | } | 74 | } |
@@ -80,23 +80,26 @@ void KMod::setupCounter(Counter &counter) { | |||
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | void KMod::writeCounters(mxml_node_t *root) const { | 83 | int KMod::writeCounters(mxml_node_t *root) const { |
84 | struct dirent *ent; | 84 | struct dirent *ent; |
85 | mxml_node_t *counter; | 85 | mxml_node_t *counter; |
86 | 86 | ||
87 | // counters.xml is simply a file listing of /dev/gator/events | 87 | // counters.xml is simply a file listing of /dev/gator/events |
88 | DIR* dir = opendir("/dev/gator/events"); | 88 | DIR* dir = opendir("/dev/gator/events"); |
89 | if (dir == NULL) { | 89 | if (dir == NULL) { |
90 | logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); | 90 | return 0; |
91 | handleException(); | ||
92 | } | 91 | } |
93 | 92 | ||
93 | int count = 0; | ||
94 | while ((ent = readdir(dir)) != NULL) { | 94 | while ((ent = readdir(dir)) != NULL) { |
95 | // skip hidden files, current dir, and parent dir | 95 | // skip hidden files, current dir, and parent dir |
96 | if (ent->d_name[0] == '.') | 96 | if (ent->d_name[0] == '.') |
97 | continue; | 97 | continue; |
98 | counter = mxmlNewElement(root, "counter"); | 98 | counter = mxmlNewElement(root, "counter"); |
99 | mxmlElementSetAttr(counter, "name", ent->d_name); | 99 | mxmlElementSetAttr(counter, "name", ent->d_name); |
100 | ++count; | ||
100 | } | 101 | } |
101 | closedir(dir); | 102 | closedir(dir); |
103 | |||
104 | return count; | ||
102 | } | 105 | } |
diff --git a/daemon/KMod.h b/daemon/KMod.h index 7974262..fb7fc8a 100644 --- a/daemon/KMod.h +++ b/daemon/KMod.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -21,7 +21,7 @@ public: | |||
21 | void resetCounters(); | 21 | void resetCounters(); |
22 | void setupCounter(Counter &counter); | 22 | void setupCounter(Counter &counter); |
23 | 23 | ||
24 | void writeCounters(mxml_node_t *root) const; | 24 | int writeCounters(mxml_node_t *root) const; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #endif // KMOD_H | 27 | #endif // KMOD_H |
diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp index 3235a34..d2a4b79 100644 --- a/daemon/LocalCapture.cpp +++ b/daemon/LocalCapture.cpp | |||
@@ -1,18 +1,20 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "LocalCapture.h" | ||
10 | |||
9 | #include <sys/stat.h> | 11 | #include <sys/stat.h> |
10 | #include <sys/types.h> | 12 | #include <sys/types.h> |
11 | #include <dirent.h> | 13 | #include <dirent.h> |
12 | #include <string.h> | 14 | #include <string.h> |
13 | #include <stdlib.h> | 15 | #include <stdlib.h> |
14 | #include <unistd.h> | 16 | #include <unistd.h> |
15 | #include "LocalCapture.h" | 17 | |
16 | #include "SessionData.h" | 18 | #include "SessionData.h" |
17 | #include "Logging.h" | 19 | #include "Logging.h" |
18 | #include "OlyUtility.h" | 20 | #include "OlyUtility.h" |
diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h index 8042d6a..aadecce 100644 --- a/daemon/LocalCapture.h +++ b/daemon/LocalCapture.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/Logging.cpp b/daemon/Logging.cpp index 5fd45b5..b8d3178 100644 --- a/daemon/Logging.cpp +++ b/daemon/Logging.cpp | |||
@@ -1,11 +1,13 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "Logging.h" | ||
10 | |||
9 | #include <stdio.h> | 11 | #include <stdio.h> |
10 | #include <stdlib.h> | 12 | #include <stdlib.h> |
11 | #include <stdarg.h> | 13 | #include <stdarg.h> |
@@ -23,8 +25,6 @@ | |||
23 | #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) | 25 | #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) |
24 | #endif | 26 | #endif |
25 | 27 | ||
26 | #include "Logging.h" | ||
27 | |||
28 | // Global thread-safe logging | 28 | // Global thread-safe logging |
29 | Logging* logg = NULL; | 29 | Logging* logg = NULL; |
30 | 30 | ||
diff --git a/daemon/Logging.h b/daemon/Logging.h index 8f960de..6ae3280 100644 --- a/daemon/Logging.h +++ b/daemon/Logging.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,14 +9,7 @@ | |||
9 | #ifndef __LOGGING_H__ | 9 | #ifndef __LOGGING_H__ |
10 | #define __LOGGING_H__ | 10 | #define __LOGGING_H__ |
11 | 11 | ||
12 | #include <stdio.h> | ||
13 | #include <string.h> | ||
14 | #include <limits.h> | ||
15 | #ifdef WIN32 | ||
16 | #include <windows.h> | ||
17 | #else | ||
18 | #include <pthread.h> | 12 | #include <pthread.h> |
19 | #endif | ||
20 | 13 | ||
21 | #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" | 14 | #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" |
22 | 15 | ||
@@ -33,11 +26,7 @@ private: | |||
33 | char mErrBuf[4096]; // Arbitrarily large buffer to hold a string | 26 | char mErrBuf[4096]; // Arbitrarily large buffer to hold a string |
34 | char mLogBuf[4096]; // Arbitrarily large buffer to hold a string | 27 | char mLogBuf[4096]; // Arbitrarily large buffer to hold a string |
35 | bool mDebug; | 28 | bool mDebug; |
36 | #ifdef WIN32 | ||
37 | HANDLE mLoggingMutex; | ||
38 | #else | ||
39 | pthread_mutex_t mLoggingMutex; | 29 | pthread_mutex_t mLoggingMutex; |
40 | #endif | ||
41 | }; | 30 | }; |
42 | 31 | ||
43 | extern Logging* logg; | 32 | extern Logging* logg; |
diff --git a/daemon/Monitor.cpp b/daemon/Monitor.cpp new file mode 100644 index 0000000..90d5c47 --- /dev/null +++ b/daemon/Monitor.cpp | |||
@@ -0,0 +1,61 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "Monitor.h" | ||
10 | |||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <unistd.h> | ||
14 | |||
15 | #include "Logging.h" | ||
16 | |||
17 | Monitor::Monitor() : mFd(-1) { | ||
18 | } | ||
19 | |||
20 | Monitor::~Monitor() { | ||
21 | if (mFd >= -1) { | ||
22 | close(mFd); | ||
23 | } | ||
24 | } | ||
25 | |||
26 | bool Monitor::init() { | ||
27 | mFd = epoll_create(16); | ||
28 | if (mFd < 0) { | ||
29 | logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); | ||
30 | return false; | ||
31 | } | ||
32 | |||
33 | return true; | ||
34 | } | ||
35 | |||
36 | bool Monitor::add(const int fd) { | ||
37 | struct epoll_event event; | ||
38 | memset(&event, 0, sizeof(event)); | ||
39 | event.data.fd = fd; | ||
40 | event.events = EPOLLIN; | ||
41 | if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { | ||
42 | logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
43 | return false; | ||
44 | } | ||
45 | |||
46 | return true; | ||
47 | } | ||
48 | |||
49 | int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) { | ||
50 | int result = epoll_wait(mFd, events, maxevents, timeout); | ||
51 | if (result < 0) { | ||
52 | // Ignore if the call was interrupted as this will happen when SIGINT is received | ||
53 | if (errno == EINTR) { | ||
54 | result = 0; | ||
55 | } else { | ||
56 | logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | return result; | ||
61 | } | ||
diff --git a/daemon/Monitor.h b/daemon/Monitor.h new file mode 100644 index 0000000..6e268b6 --- /dev/null +++ b/daemon/Monitor.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef MONITOR_H | ||
10 | #define MONITOR_H | ||
11 | |||
12 | #include <sys/epoll.h> | ||
13 | |||
14 | class Monitor { | ||
15 | public: | ||
16 | Monitor(); | ||
17 | ~Monitor(); | ||
18 | |||
19 | bool init(); | ||
20 | bool add(const int fd); | ||
21 | int wait(struct epoll_event *const events, int maxevents, int timeout); | ||
22 | |||
23 | private: | ||
24 | |||
25 | int mFd; | ||
26 | |||
27 | // Intentionally unimplemented | ||
28 | Monitor(const Monitor &); | ||
29 | Monitor &operator=(const Monitor &); | ||
30 | }; | ||
31 | |||
32 | #endif // MONITOR_H | ||
diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp index ab5c3c2..26e4768 100644 --- a/daemon/OlySocket.cpp +++ b/daemon/OlySocket.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -15,6 +15,7 @@ | |||
15 | #else | 15 | #else |
16 | #include <netinet/in.h> | 16 | #include <netinet/in.h> |
17 | #include <sys/socket.h> | 17 | #include <sys/socket.h> |
18 | #include <sys/un.h> | ||
18 | #include <unistd.h> | 19 | #include <unistd.h> |
19 | #include <netdb.h> | 20 | #include <netdb.h> |
20 | #endif | 21 | #endif |
@@ -30,7 +31,7 @@ | |||
30 | #define SHUTDOWN_RX_TX SHUT_RDWR | 31 | #define SHUTDOWN_RX_TX SHUT_RDWR |
31 | #endif | 32 | #endif |
32 | 33 | ||
33 | OlySocket::OlySocket(int port, bool multiple) { | 34 | OlyServerSocket::OlyServerSocket(int port) { |
34 | #ifdef WIN32 | 35 | #ifdef WIN32 |
35 | WSADATA wsaData; | 36 | WSADATA wsaData; |
36 | if (WSAStartup(0x0202, &wsaData) != 0) { | 37 | if (WSAStartup(0x0202, &wsaData) != 0) { |
@@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) { | |||
39 | } | 40 | } |
40 | #endif | 41 | #endif |
41 | 42 | ||
42 | if (multiple) { | 43 | createServerSocket(port); |
43 | createServerSocket(port); | ||
44 | } else { | ||
45 | createSingleServerConnection(port); | ||
46 | } | ||
47 | } | 44 | } |
48 | 45 | ||
49 | OlySocket::OlySocket(int port, char* host) { | 46 | OlySocket::OlySocket(int port, const char* host) { |
50 | mFDServer = 0; | ||
51 | createClientSocket(host, port); | 47 | createClientSocket(host, port); |
52 | } | 48 | } |
53 | 49 | ||
50 | OlySocket::OlySocket(int socketID) : mSocketID(socketID) { | ||
51 | } | ||
52 | |||
53 | #ifndef WIN32 | ||
54 | |||
55 | OlyServerSocket::OlyServerSocket(const char* path) { | ||
56 | // Create socket | ||
57 | mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); | ||
58 | if (mFDServer < 0) { | ||
59 | logg->logError(__FILE__, __LINE__, "Error creating server socket"); | ||
60 | handleException(); | ||
61 | } | ||
62 | |||
63 | unlink(path); | ||
64 | |||
65 | // Create sockaddr_in structure, ensuring non-populated fields are zero | ||
66 | struct sockaddr_un sockaddr; | ||
67 | memset((void*)&sockaddr, 0, sizeof(sockaddr)); | ||
68 | sockaddr.sun_family = AF_UNIX; | ||
69 | strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); | ||
70 | sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; | ||
71 | |||
72 | // Bind the socket to an address | ||
73 | if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { | ||
74 | logg->logError(__FILE__, __LINE__, "Binding of server socket failed."); | ||
75 | handleException(); | ||
76 | } | ||
77 | |||
78 | // Listen for connections on this socket | ||
79 | if (listen(mFDServer, 1) < 0) { | ||
80 | logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); | ||
81 | handleException(); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | OlySocket::OlySocket(const char* path) { | ||
86 | mSocketID = socket(PF_UNIX, SOCK_STREAM, 0); | ||
87 | if (mSocketID < 0) { | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | // Create sockaddr_in structure, ensuring non-populated fields are zero | ||
92 | struct sockaddr_un sockaddr; | ||
93 | memset((void*)&sockaddr, 0, sizeof(sockaddr)); | ||
94 | sockaddr.sun_family = AF_UNIX; | ||
95 | strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); | ||
96 | sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; | ||
97 | |||
98 | if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { | ||
99 | close(mSocketID); | ||
100 | mSocketID = -1; | ||
101 | return; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | #endif | ||
106 | |||
54 | OlySocket::~OlySocket() { | 107 | OlySocket::~OlySocket() { |
55 | if (mSocketID > 0) { | 108 | if (mSocketID > 0) { |
56 | CLOSE_SOCKET(mSocketID); | 109 | CLOSE_SOCKET(mSocketID); |
57 | } | 110 | } |
58 | } | 111 | } |
59 | 112 | ||
113 | OlyServerSocket::~OlyServerSocket() { | ||
114 | if (mFDServer > 0) { | ||
115 | CLOSE_SOCKET(mFDServer); | ||
116 | } | ||
117 | } | ||
118 | |||
60 | void OlySocket::shutdownConnection() { | 119 | void OlySocket::shutdownConnection() { |
61 | // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions | 120 | // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions |
62 | shutdown(mSocketID, SHUTDOWN_RX_TX); | 121 | shutdown(mSocketID, SHUTDOWN_RX_TX); |
@@ -70,7 +129,7 @@ void OlySocket::closeSocket() { | |||
70 | } | 129 | } |
71 | } | 130 | } |
72 | 131 | ||
73 | void OlySocket::closeServerSocket() { | 132 | void OlyServerSocket::closeServerSocket() { |
74 | if (CLOSE_SOCKET(mFDServer) != 0) { | 133 | if (CLOSE_SOCKET(mFDServer) != 0) { |
75 | logg->logError(__FILE__, __LINE__, "Failed to close server socket."); | 134 | logg->logError(__FILE__, __LINE__, "Failed to close server socket."); |
76 | handleException(); | 135 | handleException(); |
@@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() { | |||
78 | mFDServer = 0; | 137 | mFDServer = 0; |
79 | } | 138 | } |
80 | 139 | ||
81 | void OlySocket::createClientSocket(char* hostname, int portno) { | 140 | void OlySocket::createClientSocket(const char* hostname, int portno) { |
82 | #ifdef WIN32 | 141 | #ifdef WIN32 |
83 | // TODO: Implement for Windows | 142 | // TODO: Implement for Windows |
84 | #else | 143 | #else |
@@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) { | |||
119 | #endif | 178 | #endif |
120 | } | 179 | } |
121 | 180 | ||
122 | void OlySocket::createSingleServerConnection(int port) { | 181 | void OlyServerSocket::createServerSocket(int port) { |
123 | createServerSocket(port); | ||
124 | |||
125 | mSocketID = acceptConnection(); | ||
126 | closeServerSocket(); | ||
127 | } | ||
128 | |||
129 | void OlySocket::createServerSocket(int port) { | ||
130 | int family = AF_INET6; | 182 | int family = AF_INET6; |
131 | 183 | ||
132 | // Create socket | 184 | // Create socket |
@@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) { | |||
169 | 221 | ||
170 | // mSocketID is always set to the most recently accepted connection | 222 | // mSocketID is always set to the most recently accepted connection |
171 | // The user of this class should maintain the different socket connections, e.g. by forking the process | 223 | // The user of this class should maintain the different socket connections, e.g. by forking the process |
172 | int OlySocket::acceptConnection() { | 224 | int OlyServerSocket::acceptConnection() { |
225 | int socketID; | ||
173 | if (mFDServer <= 0) { | 226 | if (mFDServer <= 0) { |
174 | logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); | 227 | logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); |
175 | handleException(); | 228 | handleException(); |
176 | } | 229 | } |
177 | 230 | ||
178 | // Accept a connection, note that this call blocks until a client connects | 231 | // Accept a connection, note that this call blocks until a client connects |
179 | mSocketID = accept(mFDServer, NULL, NULL); | 232 | socketID = accept(mFDServer, NULL, NULL); |
180 | if (mSocketID < 0) { | 233 | if (socketID < 0) { |
181 | logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); | 234 | logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); |
182 | handleException(); | 235 | handleException(); |
183 | } | 236 | } |
184 | return mSocketID; | 237 | return socketID; |
185 | } | 238 | } |
186 | 239 | ||
187 | void OlySocket::send(char* buffer, int size) { | 240 | void OlySocket::send(const char* buffer, int size) { |
188 | if (size <= 0 || buffer == NULL) { | 241 | if (size <= 0 || buffer == NULL) { |
189 | return; | 242 | return; |
190 | } | 243 | } |
diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h index 5bab7d1..eab786b 100644 --- a/daemon/OlySocket.h +++ b/daemon/OlySocket.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,27 +9,44 @@ | |||
9 | #ifndef __OLY_SOCKET_H__ | 9 | #ifndef __OLY_SOCKET_H__ |
10 | #define __OLY_SOCKET_H__ | 10 | #define __OLY_SOCKET_H__ |
11 | 11 | ||
12 | #include <string.h> | ||
13 | |||
14 | class OlySocket { | 12 | class OlySocket { |
15 | public: | 13 | public: |
16 | OlySocket(int port, bool multipleConnections = false); | 14 | OlySocket(int port, const char* hostname); |
17 | OlySocket(int port, char* hostname); | 15 | OlySocket(int socketID); |
16 | #ifndef WIN32 | ||
17 | OlySocket(const char* path); | ||
18 | #endif | ||
18 | ~OlySocket(); | 19 | ~OlySocket(); |
19 | int acceptConnection(); | 20 | |
20 | void closeSocket(); | 21 | void closeSocket(); |
21 | void closeServerSocket(); | ||
22 | void shutdownConnection(); | 22 | void shutdownConnection(); |
23 | void send(char* buffer, int size); | 23 | void send(const char* buffer, int size); |
24 | void sendString(const char* string) {send((char*)string, strlen(string));} | ||
25 | int receive(char* buffer, int size); | 24 | int receive(char* buffer, int size); |
26 | int receiveNBytes(char* buffer, int size); | 25 | int receiveNBytes(char* buffer, int size); |
27 | int receiveString(char* buffer, int size); | 26 | int receiveString(char* buffer, int size); |
28 | int getSocketID() {return mSocketID;} | 27 | |
28 | bool isValid() const { return mSocketID >= 0; } | ||
29 | |||
30 | private: | ||
31 | int mSocketID; | ||
32 | |||
33 | void createClientSocket(const char* hostname, int port); | ||
34 | }; | ||
35 | |||
36 | class OlyServerSocket { | ||
37 | public: | ||
38 | OlyServerSocket(int port); | ||
39 | #ifndef WIN32 | ||
40 | OlyServerSocket(const char* path); | ||
41 | #endif | ||
42 | ~OlyServerSocket(); | ||
43 | |||
44 | int acceptConnection(); | ||
45 | void closeServerSocket(); | ||
46 | |||
29 | private: | 47 | private: |
30 | int mSocketID, mFDServer; | 48 | int mFDServer; |
31 | void createClientSocket(char* hostname, int port); | 49 | |
32 | void createSingleServerConnection(int port); | ||
33 | void createServerSocket(int port); | 50 | void createServerSocket(int port); |
34 | }; | 51 | }; |
35 | 52 | ||
diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp index 0b22d6e..45340a2 100644 --- a/daemon/OlyUtility.cpp +++ b/daemon/OlyUtility.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h index abab0a5..1d26beb 100644 --- a/daemon/OlyUtility.h +++ b/daemon/OlyUtility.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * Copyright (C) ARM Limited 2010-2013. All rights reserved. | 2 | * Copyright (C) ARM Limited 2010-2014. All rights reserved. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
diff --git a/daemon/PerfBuffer.cpp b/daemon/PerfBuffer.cpp new file mode 100644 index 0000000..5fad583 --- /dev/null +++ b/daemon/PerfBuffer.cpp | |||
@@ -0,0 +1,139 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "PerfBuffer.h" | ||
10 | |||
11 | #include <sys/ioctl.h> | ||
12 | #include <sys/mman.h> | ||
13 | |||
14 | #include "Buffer.h" | ||
15 | #include "Logging.h" | ||
16 | #include "Sender.h" | ||
17 | #include "SessionData.h" | ||
18 | |||
19 | PerfBuffer::PerfBuffer() { | ||
20 | for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) { | ||
21 | mBuf[cpu] = MAP_FAILED; | ||
22 | mDiscard[cpu] = false; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | PerfBuffer::~PerfBuffer() { | ||
27 | for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { | ||
28 | if (mBuf[cpu] != MAP_FAILED) { | ||
29 | munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) { | ||
35 | if (fd == groupFd) { | ||
36 | if (mBuf[cpu] != MAP_FAILED) { | ||
37 | logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
38 | return false; | ||
39 | } | ||
40 | |||
41 | // The buffer isn't mapped yet | ||
42 | mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||
43 | if (mBuf[cpu] == MAP_FAILED) { | ||
44 | logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); | ||
45 | return false; | ||
46 | } | ||
47 | |||
48 | // Check the version | ||
49 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
50 | if (pemp->compat_version != 0) { | ||
51 | logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); | ||
52 | return false; | ||
53 | } | ||
54 | } else { | ||
55 | if (mBuf[cpu] == MAP_FAILED) { | ||
56 | logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) { | ||
61 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
62 | return false; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | return true; | ||
67 | } | ||
68 | |||
69 | void PerfBuffer::discard(const int cpu) { | ||
70 | if (mBuf[cpu] != MAP_FAILED) { | ||
71 | mDiscard[cpu] = true; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | bool PerfBuffer::isEmpty() { | ||
76 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
77 | if (mBuf[cpu] != MAP_FAILED) { | ||
78 | // Take a snapshot of the positions | ||
79 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
80 | const __u64 head = pemp->data_head; | ||
81 | const __u64 tail = pemp->data_tail; | ||
82 | |||
83 | if (head != tail) { | ||
84 | return false; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return true; | ||
90 | } | ||
91 | |||
92 | bool PerfBuffer::send(Sender *const sender) { | ||
93 | for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { | ||
94 | if (mBuf[cpu] == MAP_FAILED) { | ||
95 | continue; | ||
96 | } | ||
97 | |||
98 | // Take a snapshot of the positions | ||
99 | struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); | ||
100 | const __u64 head = pemp->data_head; | ||
101 | const __u64 tail = pemp->data_tail; | ||
102 | |||
103 | if (head > tail) { | ||
104 | const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize; | ||
105 | const int offset = gSessionData->mLocalCapture ? 1 : 0; | ||
106 | unsigned char header[7]; | ||
107 | header[0] = RESPONSE_APC_DATA; | ||
108 | Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5); | ||
109 | // Should use real packing functions | ||
110 | header[5] = FRAME_PERF; | ||
111 | header[6] = cpu; | ||
112 | |||
113 | // Write header | ||
114 | sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA); | ||
115 | |||
116 | // Write data | ||
117 | if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) { | ||
118 | // Not wrapped | ||
119 | sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA); | ||
120 | } else { | ||
121 | // Wrapped | ||
122 | sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA); | ||
123 | sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA); | ||
124 | } | ||
125 | |||
126 | // Update tail with the data read | ||
127 | pemp->data_tail = head; | ||
128 | } | ||
129 | |||
130 | if (mDiscard[cpu]) { | ||
131 | munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); | ||
132 | mBuf[cpu] = MAP_FAILED; | ||
133 | mDiscard[cpu] = false; | ||
134 | logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | return true; | ||
139 | } | ||
diff --git a/daemon/PerfBuffer.h b/daemon/PerfBuffer.h new file mode 100644 index 0000000..278a3b9 --- /dev/null +++ b/daemon/PerfBuffer.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef PERF_BUFFER | ||
10 | #define PERF_BUFFER | ||
11 | |||
12 | #include "Config.h" | ||
13 | |||
14 | #define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) | ||
15 | #define BUF_MASK (BUF_SIZE - 1) | ||
16 | |||
17 | class Sender; | ||
18 | |||
19 | class PerfBuffer { | ||
20 | public: | ||
21 | PerfBuffer(); | ||
22 | ~PerfBuffer(); | ||
23 | |||
24 | bool useFd(const int cpu, const int fd, const int groupFd); | ||
25 | void discard(const int cpu); | ||
26 | bool isEmpty(); | ||
27 | bool send(Sender *const sender); | ||
28 | |||
29 | private: | ||
30 | void *mBuf[NR_CPUS]; | ||
31 | // After the buffer is flushed it should be unmaped | ||
32 | bool mDiscard[NR_CPUS]; | ||
33 | |||
34 | // Intentionally undefined | ||
35 | PerfBuffer(const PerfBuffer &); | ||
36 | PerfBuffer &operator=(const PerfBuffer &); | ||
37 | }; | ||
38 | |||
39 | #endif // PERF_BUFFER | ||
diff --git a/daemon/PerfDriver.cpp b/daemon/PerfDriver.cpp new file mode 100644 index 0000000..8e25c22 --- /dev/null +++ b/daemon/PerfDriver.cpp | |||
@@ -0,0 +1,355 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "PerfDriver.h" | ||
10 | |||
11 | #include <dirent.h> | ||
12 | #include <sys/utsname.h> | ||
13 | #include <time.h> | ||
14 | |||
15 | #include "Buffer.h" | ||
16 | #include "Config.h" | ||
17 | #include "ConfigurationXML.h" | ||
18 | #include "Counter.h" | ||
19 | #include "DriverSource.h" | ||
20 | #include "DynBuf.h" | ||
21 | #include "Logging.h" | ||
22 | #include "PerfGroup.h" | ||
23 | #include "SessionData.h" | ||
24 | |||
25 | #define PERF_DEVICES "/sys/bus/event_source/devices" | ||
26 | |||
27 | #define TYPE_DERIVED ~0U | ||
28 | |||
29 | // From gator.h | ||
30 | struct gator_cpu { | ||
31 | const int cpuid; | ||
32 | // Human readable name | ||
33 | const char core_name[32]; | ||
34 | // gatorfs event and Perf PMU name | ||
35 | const char *const pmnc_name; | ||
36 | const int pmnc_counters; | ||
37 | }; | ||
38 | |||
39 | // From gator_main.c | ||
40 | static const struct gator_cpu gator_cpus[] = { | ||
41 | { 0xb36, "ARM1136", "ARM_ARM11", 3 }, | ||
42 | { 0xb56, "ARM1156", "ARM_ARM11", 3 }, | ||
43 | { 0xb76, "ARM1176", "ARM_ARM11", 3 }, | ||
44 | { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, | ||
45 | { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, | ||
46 | { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, | ||
47 | { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, | ||
48 | { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, | ||
49 | { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 }, | ||
50 | { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, | ||
51 | { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, | ||
52 | { 0x00f, "Scorpion", "Scorpion", 4 }, | ||
53 | { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, | ||
54 | { 0x049, "KraitSIM", "Krait", 4 }, | ||
55 | { 0x04d, "Krait", "Krait", 4 }, | ||
56 | { 0x06f, "Krait S4 Pro", "Krait", 4 }, | ||
57 | { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, | ||
58 | { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, | ||
59 | { 0xd0f, "AArch64", "ARM_AArch64", 6 }, | ||
60 | }; | ||
61 | |||
62 | static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; | ||
63 | static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; | ||
64 | |||
65 | class PerfCounter { | ||
66 | public: | ||
67 | PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false) {} | ||
68 | ~PerfCounter() { | ||
69 | delete [] mName; | ||
70 | } | ||
71 | |||
72 | PerfCounter *getNext() const { return mNext; } | ||
73 | const char *getName() const { return mName; } | ||
74 | uint32_t getType() const { return mType; } | ||
75 | int getCount() const { return mCount; } | ||
76 | void setCount(const int count) { mCount = count; } | ||
77 | int getKey() const { return mKey; } | ||
78 | uint64_t getConfig() const { return mConfig; } | ||
79 | void setConfig(const uint64_t config) { mConfig = config; } | ||
80 | bool isEnabled() const { return mEnabled; } | ||
81 | void setEnabled(const bool enabled) { mEnabled = enabled; } | ||
82 | |||
83 | private: | ||
84 | PerfCounter *const mNext; | ||
85 | const char *const mName; | ||
86 | const uint32_t mType; | ||
87 | int mCount; | ||
88 | const int mKey; | ||
89 | uint64_t mConfig; | ||
90 | bool mEnabled; | ||
91 | }; | ||
92 | |||
93 | PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) { | ||
94 | } | ||
95 | |||
96 | PerfDriver::~PerfDriver() { | ||
97 | while (mCounters != NULL) { | ||
98 | PerfCounter *counter = mCounters; | ||
99 | mCounters = counter->getNext(); | ||
100 | delete counter; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { | ||
105 | int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; | ||
106 | char *name = new char[len]; | ||
107 | snprintf(name, len, "%s_ccnt", counterName); | ||
108 | mCounters = new PerfCounter(mCounters, name, type, -1); | ||
109 | |||
110 | for (int j = 0; j < numCounters; ++j) { | ||
111 | len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; | ||
112 | name = new char[len]; | ||
113 | snprintf(name, len, "%s_cnt%d", counterName, j); | ||
114 | mCounters = new PerfCounter(mCounters, name, type, -1); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | // From include/generated/uapi/linux/version.h | ||
119 | #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) | ||
120 | |||
121 | bool PerfDriver::setup() { | ||
122 | // Check the kernel version | ||
123 | struct utsname utsname; | ||
124 | if (uname(&utsname) != 0) { | ||
125 | logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | int release[3] = { 0, 0, 0 }; | ||
130 | int part = 0; | ||
131 | char *ch = utsname.release; | ||
132 | while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) { | ||
133 | release[part] = 10*release[part] + *ch - '0'; | ||
134 | |||
135 | ++ch; | ||
136 | if (*ch == '.') { | ||
137 | ++part; | ||
138 | ++ch; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) { | ||
143 | logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); | ||
144 | return false; | ||
145 | } | ||
146 | |||
147 | // Add supported PMUs | ||
148 | bool foundCpu = false; | ||
149 | DIR *dir = opendir(PERF_DEVICES); | ||
150 | if (dir == NULL) { | ||
151 | logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); | ||
152 | return false; | ||
153 | } | ||
154 | |||
155 | struct dirent *dirent; | ||
156 | while ((dirent = readdir(dir)) != NULL) { | ||
157 | for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { | ||
158 | // Do the names match exactly? | ||
159 | if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 && | ||
160 | // Do these names match but have the old vs new prefix? | ||
161 | (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || | ||
162 | strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || | ||
163 | strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) { | ||
164 | continue; | ||
165 | } | ||
166 | |||
167 | int type; | ||
168 | char buf[256]; | ||
169 | snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); | ||
170 | if (DriverSource::readIntDriver(buf, &type) != 0) { | ||
171 | continue; | ||
172 | } | ||
173 | |||
174 | foundCpu = true; | ||
175 | addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); | ||
176 | } | ||
177 | } | ||
178 | closedir(dir); | ||
179 | |||
180 | if (!foundCpu) { | ||
181 | // If no cpu was found based on pmu names, try by cpuid | ||
182 | for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { | ||
183 | if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { | ||
184 | continue; | ||
185 | } | ||
186 | |||
187 | foundCpu = true; | ||
188 | addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | if (!foundCpu) { | ||
194 | // If all else fails, use the perf architected counters | ||
195 | // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once | ||
196 | addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); | ||
197 | } | ||
198 | */ | ||
199 | |||
200 | // Add supported software counters | ||
201 | long long id; | ||
202 | DynBuf printb; | ||
203 | |||
204 | id = getTracepointId("irq/softirq_exit", &printb); | ||
205 | if (id >= 0) { | ||
206 | mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id); | ||
207 | } | ||
208 | |||
209 | id = getTracepointId("irq/irq_handler_exit", &printb); | ||
210 | if (id >= 0) { | ||
211 | mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id); | ||
212 | } | ||
213 | |||
214 | //Linux_block_rq_wr | ||
215 | //Linux_block_rq_rd | ||
216 | //Linux_net_rx | ||
217 | //Linux_net_tx | ||
218 | |||
219 | id = getTracepointId(SCHED_SWITCH, &printb); | ||
220 | if (id >= 0) { | ||
221 | mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id); | ||
222 | } | ||
223 | |||
224 | //Linux_meminfo_memused | ||
225 | //Linux_meminfo_memfree | ||
226 | //Linux_meminfo_bufferram | ||
227 | //Linux_power_cpu_freq | ||
228 | //Linux_power_cpu_idle | ||
229 | |||
230 | mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1); | ||
231 | |||
232 | //Linux_cpu_wait_io | ||
233 | |||
234 | mIsSetup = true; | ||
235 | return true; | ||
236 | } | ||
237 | |||
238 | bool PerfDriver::summary(Buffer *const buffer) { | ||
239 | struct utsname utsname; | ||
240 | if (uname(&utsname) != 0) { | ||
241 | logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); | ||
242 | return false; | ||
243 | } | ||
244 | |||
245 | char buf[512]; | ||
246 | snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); | ||
247 | |||
248 | struct timespec ts; | ||
249 | if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { | ||
250 | logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); | ||
251 | return false; | ||
252 | } | ||
253 | const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; | ||
254 | |||
255 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { | ||
256 | logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); | ||
257 | return false; | ||
258 | } | ||
259 | const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; | ||
260 | |||
261 | buffer->summary(timestamp, uptime, 0, buf); | ||
262 | |||
263 | for (int i = 0; i < gSessionData->mCores; ++i) { | ||
264 | int j; | ||
265 | for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { | ||
266 | if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { | ||
267 | break; | ||
268 | } | ||
269 | } | ||
270 | if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { | ||
271 | buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); | ||
272 | } else { | ||
273 | snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); | ||
274 | buffer->coreName(i, gSessionData->mCpuIds[i], buf); | ||
275 | } | ||
276 | } | ||
277 | buffer->commit(1); | ||
278 | |||
279 | return true; | ||
280 | } | ||
281 | |||
282 | PerfCounter *PerfDriver::findCounter(const Counter &counter) const { | ||
283 | for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) { | ||
284 | if (strcmp(perfCounter->getName(), counter.getType()) == 0) { | ||
285 | return perfCounter; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | return NULL; | ||
290 | } | ||
291 | |||
292 | bool PerfDriver::claimCounter(const Counter &counter) const { | ||
293 | return findCounter(counter) != NULL; | ||
294 | } | ||
295 | |||
296 | void PerfDriver::resetCounters() { | ||
297 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
298 | counter->setEnabled(false); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | void PerfDriver::setupCounter(Counter &counter) { | ||
303 | PerfCounter *const perfCounter = findCounter(counter); | ||
304 | if (perfCounter == NULL) { | ||
305 | counter.setEnabled(false); | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | // Don't use the config from counters XML if it's not set, ex: software counters | ||
310 | if (counter.getEvent() != -1) { | ||
311 | perfCounter->setConfig(counter.getEvent()); | ||
312 | } | ||
313 | perfCounter->setCount(counter.getCount()); | ||
314 | perfCounter->setEnabled(true); | ||
315 | counter.setKey(perfCounter->getKey()); | ||
316 | } | ||
317 | |||
318 | int PerfDriver::writeCounters(mxml_node_t *root) const { | ||
319 | int count = 0; | ||
320 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
321 | mxml_node_t *node = mxmlNewElement(root, "counter"); | ||
322 | mxmlElementSetAttr(node, "name", counter->getName()); | ||
323 | ++count; | ||
324 | } | ||
325 | |||
326 | return count; | ||
327 | } | ||
328 | |||
329 | bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const { | ||
330 | for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { | ||
331 | if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { | ||
332 | if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) { | ||
333 | logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); | ||
334 | return false; | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | return true; | ||
340 | } | ||
341 | |||
342 | long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { | ||
343 | if (!printb->printf(EVENTS_PATH "/%s/id", name)) { | ||
344 | logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); | ||
345 | return -1; | ||
346 | } | ||
347 | |||
348 | int64_t result; | ||
349 | if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { | ||
350 | logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); | ||
351 | return -1; | ||
352 | } | ||
353 | |||
354 | return result; | ||
355 | } | ||
diff --git a/daemon/PerfDriver.h b/daemon/PerfDriver.h new file mode 100644 index 0000000..3181b74 --- /dev/null +++ b/daemon/PerfDriver.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef PERFDRIVER_H | ||
10 | #define PERFDRIVER_H | ||
11 | |||
12 | #include "Driver.h" | ||
13 | |||
14 | // If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH | ||
15 | #define DEBUGFS_PATH "/sys/kernel/debug" | ||
16 | #define EVENTS_PATH DEBUGFS_PATH "/tracing/events" | ||
17 | |||
18 | #define SCHED_SWITCH "sched/sched_switch" | ||
19 | |||
20 | class Buffer; | ||
21 | class DynBuf; | ||
22 | class PerfCounter; | ||
23 | class PerfGroup; | ||
24 | |||
25 | class PerfDriver : public Driver { | ||
26 | public: | ||
27 | PerfDriver(); | ||
28 | ~PerfDriver(); | ||
29 | |||
30 | bool setup(); | ||
31 | bool summary(Buffer *const buffer); | ||
32 | bool isSetup() const { return mIsSetup; } | ||
33 | |||
34 | bool claimCounter(const Counter &counter) const; | ||
35 | void resetCounters(); | ||
36 | void setupCounter(Counter &counter); | ||
37 | |||
38 | int writeCounters(mxml_node_t *root) const; | ||
39 | |||
40 | bool enable(PerfGroup *group, Buffer *const buffer) const; | ||
41 | |||
42 | static long long getTracepointId(const char *const name, DynBuf *const printb); | ||
43 | |||
44 | private: | ||
45 | PerfCounter *findCounter(const Counter &counter) const; | ||
46 | void addCpuCounters(const char *const counterName, const int type, const int numCounters); | ||
47 | |||
48 | PerfCounter *mCounters; | ||
49 | bool mIsSetup; | ||
50 | |||
51 | // Intentionally undefined | ||
52 | PerfDriver(const PerfDriver &); | ||
53 | PerfDriver &operator=(const PerfDriver &); | ||
54 | }; | ||
55 | |||
56 | #endif // PERFDRIVER_H | ||
diff --git a/daemon/PerfGroup.cpp b/daemon/PerfGroup.cpp new file mode 100644 index 0000000..faf5fca --- /dev/null +++ b/daemon/PerfGroup.cpp | |||
@@ -0,0 +1,206 @@ | |||
1 | /** | ||
2 | * Copyright (C) ARM Limited 2013-2014. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include "PerfGroup.h" | ||
10 | |||
11 | #include <errno.h> | ||
12 | #include <string.h> | ||
13 | #include <sys/ioctl.h> | ||
14 | #include <sys/syscall.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include "Buffer.h" | ||
18 | #include "Logging.h" | ||
19 | #include "Monitor.h" | ||
20 | #include "PerfBuffer.h" | ||
21 | #include "SessionData.h" | ||
22 | |||
23 | #define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ | ||
24 | pea.size = sizeof(pea); \ | ||
25 | /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ | ||
26 | pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \ | ||
27 | /* Emit emit value in group format */ \ | ||
28 | pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ | ||
29 | /* start out disabled */ \ | ||
30 | pea.disabled = 1; \ | ||
31 | /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \ | ||
32 | pea.watermark = 1; \ | ||
33 | /* Be conservative in flush size as only one buffer set is monitored */ \ | ||
34 | pea.wakeup_watermark = 3 * BUF_SIZE / 4 | ||
35 | |||
36 | static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) { | ||
37 | return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); | ||
38 | } | ||
39 | |||
40 | PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { | ||
41 | memset(&mAttrs, 0, sizeof(mAttrs)); | ||
42 | memset(&mKeys, -1, sizeof(mKeys)); | ||
43 | memset(&mFds, -1, sizeof(mFds)); | ||
44 | } | ||
45 | |||
46 | PerfGroup::~PerfGroup() { | ||
47 | for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { | ||
48 | if (mFds[pos] >= 0) { | ||
49 | close(mFds[pos]); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { | ||
55 | int i; | ||
56 | for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
57 | if (mKeys[i] < 0) { | ||
58 | break; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | if (i >= ARRAY_LENGTH(mKeys)) { | ||
63 | logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__); | ||
64 | return false; | ||
65 | } | ||
66 | |||
67 | DEFAULT_PEA_ARGS(mAttrs[i], sampleType); | ||
68 | mAttrs[i].type = type; | ||
69 | mAttrs[i].config = config; | ||
70 | mAttrs[i].sample_period = sample; | ||
71 | // always be on the CPU but only a group leader can be pinned | ||
72 | mAttrs[i].pinned = (i == 0 ? 1 : 0); | ||
73 | mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0); | ||
74 | mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0); | ||
75 | mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); | ||
76 | mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); | ||
77 | mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); | ||
78 | |||
79 | mKeys[i] = key; | ||
80 | |||
81 | buffer->pea(&mAttrs[i], key); | ||
82 | |||
83 | return true; | ||
84 | } | ||
85 | |||
86 | bool PerfGroup::prepareCPU(const int cpu) { | ||
87 | logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
88 | |||
89 | for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
90 | if (mKeys[i] < 0) { | ||
91 | continue; | ||
92 | } | ||
93 | |||
94 | const int offset = i * gSessionData->mCores; | ||
95 | if (mFds[cpu + offset] >= 0) { | ||
96 | logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %lli", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type); | ||
101 | mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); | ||
102 | if (mFds[cpu + offset] < 0) { | ||
103 | logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); | ||
104 | continue; | ||
105 | } | ||
106 | |||
107 | if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) { | ||
108 | logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__); | ||
109 | return false; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return true; | ||
114 | } | ||
115 | |||
116 | int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) { | ||
117 | __u64 ids[ARRAY_LENGTH(mKeys)]; | ||
118 | int coreKeys[ARRAY_LENGTH(mKeys)]; | ||
119 | int idCount = 0; | ||
120 | |||
121 | for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
122 | const int fd = mFds[cpu + i * gSessionData->mCores]; | ||
123 | if (fd < 0) { | ||
124 | continue; | ||
125 | } | ||
126 | |||
127 | coreKeys[idCount] = mKeys[i]; | ||
128 | if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) { | ||
129 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
130 | return false; | ||
131 | } | ||
132 | ++idCount; | ||
133 | } | ||
134 | |||
135 | if (!monitor->add(mFds[cpu])) { | ||
136 | logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__); | ||
137 | return false; | ||
138 | } | ||
139 | |||
140 | buffer->keys(idCount, ids, coreKeys); | ||
141 | |||
142 | if (start) { | ||
143 | for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
144 | int offset = i * gSessionData->mCores + cpu; | ||
145 | if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) { | ||
146 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
147 | return false; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | return idCount; | ||
153 | } | ||
154 | |||
155 | bool PerfGroup::offlineCPU(const int cpu) { | ||
156 | logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); | ||
157 | |||
158 | for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
159 | int offset = i * gSessionData->mCores + cpu; | ||
160 | if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) { | ||
161 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
162 | return false; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // Mark the buffer so that it will be released next time it's read | ||
167 | mPb->discard(cpu); | ||
168 | |||
169 | for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { | ||
170 | if (mKeys[i] < 0) { | ||
171 | continue; | ||
172 | } | ||
173 | |||
174 | int offset = i * gSessionData->mCores + cpu; | ||
175 | if (mFds[offset] >= 0) { | ||
176 | close(mFds[offset]); | ||
177 | mFds[offset] = -1; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | return true; | ||
182 | } | ||
183 | |||
184 | bool PerfGroup::start() { | ||
185 | for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) { | ||
186 | if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) { | ||
187 | logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); | ||
188 | goto fail; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | return true; | ||
193 | |||
194 | fail: | ||
195 | stop(); | ||
196 | |||
197 | return false; | ||
198 | } | ||
199 | |||
200 | void PerfGroup::stop() { | ||
201 | for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { | ||
202 | if (mFds[pos] >= 0) { | ||
203 | ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE); | ||