summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Medhurst2014-04-09 06:39:14 -0500
committerJon Medhurst2014-04-09 06:40:23 -0500
commit0f66ada81c8f90a7b2e4f7570032e33aaedfb32f (patch)
treec686b9fe25f272834766eba38b51cc7a97c69786
parent33bef9ed7feca41e7cd6de8bf5d80052669278d3 (diff)
downloadarm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.tar.gz
arm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.tar.xz
arm-ds5-gator-0f66ada81c8f90a7b2e4f7570032e33aaedfb32f.zip
gator: Version 5.18DS-5.18
Signed-off-by: Jon Medhurst <tixy@linaro.org>
-rwxr-xr-xREADME_Streamline.txt56
-rw-r--r--daemon/Android.mk15
-rw-r--r--daemon/Buffer.cpp259
-rw-r--r--daemon/Buffer.h99
-rw-r--r--daemon/CapturedXML.cpp11
-rw-r--r--daemon/CapturedXML.h2
-rw-r--r--daemon/Child.cpp174
-rw-r--r--daemon/Child.h4
-rw-r--r--daemon/Collector.h38
-rw-r--r--daemon/Config.h17
-rw-r--r--daemon/ConfigurationXML.cpp16
-rw-r--r--daemon/ConfigurationXML.h2
-rw-r--r--daemon/Counter.h4
-rw-r--r--daemon/Driver.cpp2
-rw-r--r--daemon/Driver.h4
-rw-r--r--daemon/DriverSource.cpp (renamed from daemon/Collector.cpp)225
-rw-r--r--daemon/DriverSource.h52
-rw-r--r--daemon/DynBuf.cpp139
-rw-r--r--daemon/DynBuf.h52
-rw-r--r--daemon/EventsXML.cpp4
-rw-r--r--daemon/EventsXML.h2
-rw-r--r--daemon/ExternalSource.cpp56
-rw-r--r--daemon/ExternalSource.h40
-rw-r--r--daemon/Fifo.cpp2
-rw-r--r--daemon/Fifo.h4
-rw-r--r--daemon/Hwmon.cpp40
-rw-r--r--daemon/Hwmon.h6
-rw-r--r--daemon/KMod.cpp25
-rw-r--r--daemon/KMod.h4
-rw-r--r--daemon/LocalCapture.cpp6
-rw-r--r--daemon/LocalCapture.h2
-rw-r--r--daemon/Logging.cpp6
-rw-r--r--daemon/Logging.h13
-rw-r--r--daemon/Monitor.cpp61
-rw-r--r--daemon/Monitor.h32
-rw-r--r--daemon/OlySocket.cpp101
-rw-r--r--daemon/OlySocket.h43
-rw-r--r--daemon/OlyUtility.cpp2
-rw-r--r--daemon/OlyUtility.h2
-rw-r--r--daemon/PerfBuffer.cpp139
-rw-r--r--daemon/PerfBuffer.h39
-rw-r--r--daemon/PerfDriver.cpp355
-rw-r--r--daemon/PerfDriver.h56
-rw-r--r--daemon/PerfGroup.cpp206
-rw-r--r--daemon/PerfGroup.h55
-rw-r--r--daemon/PerfSource.cpp271
-rw-r--r--daemon/PerfSource.h54
-rw-r--r--daemon/Proc.cpp179
-rw-r--r--daemon/Proc.h17
-rw-r--r--daemon/Sender.cpp29
-rw-r--r--daemon/Sender.h2
-rw-r--r--daemon/SessionData.cpp29
-rw-r--r--daemon/SessionData.h14
-rw-r--r--daemon/SessionXML.cpp12
-rw-r--r--daemon/SessionXML.h8
-rw-r--r--daemon/Source.cpp33
-rw-r--r--daemon/Source.h40
-rw-r--r--daemon/StreamlineSetup.cpp36
-rw-r--r--daemon/StreamlineSetup.h7
-rw-r--r--daemon/UEvent.cpp75
-rw-r--r--daemon/UEvent.h36
-rw-r--r--daemon/UserSpaceSource.cpp97
-rw-r--r--daemon/UserSpaceSource.h38
-rw-r--r--daemon/common.mk4
-rw-r--r--daemon/defaults.xml (renamed from daemon/configuration.xml)51
-rw-r--r--daemon/escape.c2
-rw-r--r--daemon/events-Cortex-A12.xml6
-rw-r--r--daemon/events-Cortex-A15.xml6
-rw-r--r--daemon/events-Cortex-A5.xml6
-rw-r--r--daemon/events-Cortex-A7.xml6
-rw-r--r--daemon/events-Cortex-A8.xml6
-rw-r--r--daemon/events-Cortex-A9.xml6
-rw-r--r--daemon/events-Linux.xml14
-rw-r--r--daemon/events-Mali-4xx.xml2
-rw-r--r--daemon/events-Mali-T6xx.xml16
-rw-r--r--daemon/events-Perf-Hardware.xml12
-rw-r--r--daemon/k/perf_event.3.12.h792
l---------daemon/k/perf_event.h1
-rw-r--r--daemon/main.cpp80
-rw-r--r--driver/Kconfig39
-rw-r--r--driver/gator.h13
-rw-r--r--driver/gator_annotate.c2
-rw-r--r--driver/gator_annotate_kernel.c8
-rw-r--r--driver/gator_backtrace.c48
-rw-r--r--driver/gator_buffer.c168
-rw-r--r--driver/gator_buffer_write.c (renamed from driver/gator_pack.c)24
-rw-r--r--driver/gator_cookies.c8
-rw-r--r--driver/gator_events_armv6.c2
-rw-r--r--driver/gator_events_armv7.c18
-rw-r--r--driver/gator_events_block.c2
-rw-r--r--driver/gator_events_ccn-504.c2
-rw-r--r--driver/gator_events_irq.c2
-rw-r--r--driver/gator_events_l2c-310.c2
-rw-r--r--driver/gator_events_mali_4xx.c2
-rw-r--r--driver/gator_events_mali_4xx.h2
-rw-r--r--driver/gator_events_mali_common.c2
-rw-r--r--driver/gator_events_mali_common.h2
-rw-r--r--driver/gator_events_mali_t6xx.c8
-rw-r--r--driver/gator_events_mali_t6xx_hw.c10
-rw-r--r--driver/gator_events_mali_t6xx_hw_test.c2
-rw-r--r--driver/gator_events_meminfo.c28
-rw-r--r--driver/gator_events_mmapped.c2
-rw-r--r--driver/gator_events_net.c2
-rw-r--r--driver/gator_events_perf_pmu.c2
-rw-r--r--driver/gator_events_sched.c2
-rw-r--r--driver/gator_events_scorpion.c2
-rw-r--r--driver/gator_fs.c47
-rw-r--r--driver/gator_hrtimer_gator.c8
-rw-r--r--driver/gator_hrtimer_perf.c113
-rw-r--r--driver/gator_iks.c2
-rw-r--r--driver/gator_main.c290
-rw-r--r--driver/gator_marshaling.c97
-rw-r--r--driver/gator_trace_gpu.c13
-rw-r--r--driver/gator_trace_gpu.h2
-rw-r--r--driver/gator_trace_power.c6
-rw-r--r--driver/gator_trace_sched.c20
-rw-r--r--driver/mali/mali_mjollnir_profiling_gator_api.h2
-rw-r--r--driver/mali/mali_utgard_profiling_gator_api.h2
-rw-r--r--driver/mali_t6xx.mk9
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
4Instructions on setting up ARM Streamline on the target. 4Instructions on setting up ARM Streamline on the target.
5The gator driver and gator daemon are required to run on the ARM linux target in order for ARM Streamline to operate. 5The 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.
6The driver should be built as a module and the daemon must run with root permissions on the target. 6The 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
10A linux development environment with cross compiling tools is most likely required, depending on what is already created and provided. 10A 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)
46CONFIG_CPU_FREQ (optional, provides frequency setting of the CPU) 47CONFIG_CPU_FREQ (optional, provides frequency setting of the CPU)
47 48
48These 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 49These 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
53If 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
65If successful, a gator.ko module should be generated 68If successful, a gator.ko module should be generated
66 69
70It 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
75Edit Makefile in the kernel drivers folder and add this to the end
76 obj-$(CONFIG_GATOR) += gator/
77Edit Kconfig in the kernel drivers folder and add this before the last endmenu
78 source "drivers/gator/Kconfig"
79You 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
69cd /path/to/gator/daemon-src 83cd /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
88With root privileges, run the daemon 105With root privileges, run the daemon
89 sudo ./gatord & 106 sudo ./gatord &
90Note: 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. 107Note: 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.
108If 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
96Further, the l2c-310 counter can be disabled by providing an offset of zero, ex: 114Further, 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
119CCN-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
101Recommended compiler settings: 123Recommended 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
115There 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. 137There 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
118ARMv7_Cortex_A9 breakpoint software tracepoint
119
120To 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 140To 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
142There 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
144If 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
153disable SELinux so that gatorfs can be mounted by running
154 # setenforce 0
155Once gator is started, SELinux can be reenabled
156
122*** Profiling the kernel (optional) *** 157*** Profiling the kernel (optional) ***
123 158
124CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above. 159CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above.
125Use vmlinux as the image for debug symbols in Streamline. 160Use vmlinux as the image for debug symbols in Streamline.
126Drivers may be profiled using this method by statically linking the driver into the kernel image or adding the driver as an image to Streamline. 161Drivers may be profiled using this method by statically linking the driver into the kernel image or adding the driver as an image to Streamline.
127To perform kernel stack unwinding and module unwinding, edit the Makefile to enable GATOR_KERNEL_STACK_UNWINDING and rebuild gator.ko. 162To 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
139For license information, please see the file LICENSE after unzipping driver-src/gator-driver.tar.gz. 174For 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 @@
1LOCAL_PATH := $(call my-dir) 1LOCAL_PATH := $(call my-dir)
2include $(CLEAR_VARS) 2include $(CLEAR_VARS)
3 3
4XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) 4XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h)
5 5
6LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors 6LOCAL_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
17Buffer::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) { 17enum {
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
26enum {
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
44Buffer::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
25Buffer::~Buffer () { 52Buffer::~Buffer() {
26 delete [] buf; 53 delete [] mBuf;
27} 54}
28 55
29void Buffer::write (Sender * const sender) { 56void 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
59bool Buffer::commitReady () const { 86bool Buffer::commitReady() const {
60 return commitPos != readPos; 87 return mCommitPos != mReadPos;
61} 88}
62 89
63int Buffer::bytesAvailable () const { 90int 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
82bool Buffer::checkSpace (const int bytes) { 109bool 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
121int 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
94void Buffer::commit (const uint64_t time) { 131void 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
123void Buffer::check (const uint64_t time) { 160void 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
133void Buffer::packInt (int32_t x) { 170void 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
154void Buffer::packInt64 (int64_t x) { 191void 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
212void 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
175void Buffer::frame () { 221void Buffer::writeString(const char *const str) {
222 const int len = strlen(str);
223 packInt(len);
224 writeBytes(str, len);
225}
226
227void 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
185bool Buffer::eventHeader (const uint64_t curr_time) { 237void 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
249void 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
259bool 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
196bool Buffer::eventTid (const int tid) { 270bool 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
207void Buffer::event (const int32_t key, const int32_t value) { 281void 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
214void Buffer::event64 (const int64_t key, const int64_t value) { 288void 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
221void Buffer::setDone () { 295void 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
308void 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
323void 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
334void 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
348void 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
364void Buffer::setDone() {
365 mIsDone = true;
223 commit(0); 366 commit(0);
224} 367}
225 368
226bool Buffer::isDone () const { 369bool 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
16class Sender; 17class Sender;
17 18
19enum {
20 FRAME_SUMMARY = 1,
21 FRAME_BLOCK_COUNTER = 5,
22 FRAME_EXTERNAL = 10,
23 FRAME_PERF_ATTRS = 11,
24 FRAME_PERF = 12,
25};
26
18class Buffer { 27class Buffer {
19public: 28public:
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
42private: 75private:
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
31static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads 32static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
32static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads 33static Source *primarySource = NULL;
33static Buffer* buffer = NULL; 34static Source *userSpaceSource = NULL;
35static Source *externalSource = NULL;
34static Sender* sender = NULL; // Shared by Child.cpp and spawned threads 36static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
35static Collector* collector = NULL;
36Child* child = NULL; // shared by Child.cpp and main.cpp 37Child* child = NULL; // shared by Child.cpp and main.cpp
37 38
38extern void cleanUp(); 39extern 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
142static 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
195static void *senderThread(void *) { 143static 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
256void Child::endSession() { 202void 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
262void Child::run() { 208void 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
14class OlySocket; 12class OlySocket;
15 13
16class Child { 14class 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
14class Collector {
15public:
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
31private:
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
183void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { 189void 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
189void ConfigurationXML::getPath(char* path) { 195void 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
23extern Child *child;
22 24
23// Driver initialization independent of session settings 25DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
24Collector::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
47Collector::~Collector() { 70DriverSource::~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
57void Collector::checkVersion() { 82bool 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
86void Collector::start() { 90void 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 169void DriverSource::interrupt() {
131void 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
138int Collector::collect(char* buffer) { 176bool 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 180void 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
155int Collector::readIntDriver(const char* fullpath, int* value) { 188int 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
169int Collector::readInt64Driver(const char* fullpath, int64_t* value) { 213int 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
183int 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
189int 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
195int Collector::writeDriver(const char* fullpath, const char* data) { 238int 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
209int Collector::writeReadDriver(const char* path, int* value) { 252int 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
258int 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
264int 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
216int Collector::writeReadDriver(const char* path, int64_t* value) { 271int 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
17class Fifo;
18
19class DriverSource : public Source {
20public:
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
39private:
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
22int 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
40bool 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
79int 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
103bool 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
14class DynBuf {
15public:
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
40private:
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
17ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") {
18}
19
20ExternalSource::~ExternalSource() {
21}
22
23bool ExternalSource::prepare() {
24 return true;
25}
26
27void 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
44void ExternalSource::interrupt() {
45 // Do nothing
46}
47
48bool ExternalSource::isDone() {
49 return mBuffer.isDone();
50}
51
52void 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
19class ExternalSource : public Source {
20public:
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
31private:
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
18class HwmonCounter { 18class HwmonCounter {
19public: 19public:
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
72HwmonCounter::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) { 72HwmonCounter::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
207Hwmon::Hwmon() : counters(NULL) { 207Hwmon::Hwmon() : counters(NULL) {
208}
209
210Hwmon::~Hwmon() {
211 while (counters != NULL) {
212 HwmonCounter * counter = counters;
213 counters = counter->getNext();
214 delete counter;
215 }
216 sensors_cleanup();
217}
218
219void 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
226Hwmon::~Hwmon() {
227 while (counters != NULL) {
228 HwmonCounter * counter = counters;
229 counters = counter->getNext();
230 delete counter;
231 }
232 sensors_cleanup();
233}
234
235HwmonCounter *Hwmon::findCounter(const Counter &counter) const { 243HwmonCounter *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
274void Hwmon::writeCounters(mxml_node_t *root) const { 282int 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
284void Hwmon::writeEvents(mxml_node_t *root) const { 296void 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
83void KMod::writeCounters(mxml_node_t *root) const { 83int 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
29Logging* logg = NULL; 29Logging* 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
43extern Logging* logg; 32extern 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
17Monitor::Monitor() : mFd(-1) {
18}
19
20Monitor::~Monitor() {
21 if (mFd >= -1) {
22 close(mFd);
23 }
24}
25
26bool 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
36bool 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
49int 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
14class Monitor {
15public:
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
23private:
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
33OlySocket::OlySocket(int port, bool multiple) { 34OlyServerSocket::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
49OlySocket::OlySocket(int port, char* host) { 46OlySocket::OlySocket(int port, const char* host) {
50 mFDServer = 0;
51 createClientSocket(host, port); 47 createClientSocket(host, port);
52} 48}
53 49
50OlySocket::OlySocket(int socketID) : mSocketID(socketID) {
51}
52
53#ifndef WIN32
54
55OlyServerSocket::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
85OlySocket::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
54OlySocket::~OlySocket() { 107OlySocket::~OlySocket() {
55 if (mSocketID > 0) { 108 if (mSocketID > 0) {
56 CLOSE_SOCKET(mSocketID); 109 CLOSE_SOCKET(mSocketID);
57 } 110 }
58} 111}
59 112
113OlyServerSocket::~OlyServerSocket() {
114 if (mFDServer > 0) {
115 CLOSE_SOCKET(mFDServer);
116 }
117}
118
60void OlySocket::shutdownConnection() { 119void 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
73void OlySocket::closeServerSocket() { 132void 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
81void OlySocket::createClientSocket(char* hostname, int portno) { 140void 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
122void OlySocket::createSingleServerConnection(int port) { 181void OlyServerSocket::createServerSocket(int port) {
123 createServerSocket(port);
124
125 mSocketID = acceptConnection();
126 closeServerSocket();
127}
128
129void 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
172int OlySocket::acceptConnection() { 224int 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
187void OlySocket::send(char* buffer, int size) { 240void 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
14class OlySocket { 12class OlySocket {
15public: 13public:
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
30private:
31 int mSocketID;
32
33 void createClientSocket(const char* hostname, int port);
34};
35
36class OlyServerSocket {
37public:
38 OlyServerSocket(int port);
39#ifndef WIN32
40 OlyServerSocket(const char* path);
41#endif
42 ~OlyServerSocket();
43
44 int acceptConnection();
45 void closeServerSocket();
46
29private: 47private:
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
19PerfBuffer::PerfBuffer() {
20 for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) {
21 mBuf[cpu] = MAP_FAILED;
22 mDiscard[cpu] = false;
23 }
24}
25
26PerfBuffer::~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
34bool 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
69void PerfBuffer::discard(const int cpu) {
70 if (mBuf[cpu] != MAP_FAILED) {
71 mDiscard[cpu] = true;
72 }
73}
74
75bool 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
92bool 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
17class Sender;
18
19class PerfBuffer {
20public:
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
29private:
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
30struct 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
40static 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
62static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-";
63static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_";
64
65class PerfCounter {
66public:
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
83private:
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
93PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) {
94}
95
96PerfDriver::~PerfDriver() {
97 while (mCounters != NULL) {
98 PerfCounter *counter = mCounters;
99 mCounters = counter->getNext();
100 delete counter;
101 }
102}
103
104void 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
121bool 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
238bool 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
282PerfCounter *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
292bool PerfDriver::claimCounter(const Counter &counter) const {
293 return findCounter(counter) != NULL;
294}
295
296void PerfDriver::resetCounters() {
297 for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
298 counter->setEnabled(false);
299 }
300}
301
302void 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
318int 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
329bool 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
342long 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
20class Buffer;
21class DynBuf;
22class PerfCounter;
23class PerfGroup;
24
25class PerfDriver : public Driver {
26public:
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
44private:
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
36static 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
40PerfGroup::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
46PerfGroup::~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
54bool 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
86bool 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
116int 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
155bool 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 }