From 34d9769988397a1edf93ad1966c167591ab29e79 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 19 Dec 2013 09:23:06 +0000 Subject: [PATCH 1/7] gator: Version 5.17 Signed-off-by: Jon Medhurst --- drivers/gator/Makefile | 61 ++-- drivers/gator/gator.h | 6 +- drivers/gator/gator_annotate_kernel.c | 107 ++++-- drivers/gator/gator_backtrace.c | 10 +- drivers/gator/gator_cookies.c | 36 +- drivers/gator/gator_events.sh | 19 -- drivers/gator/gator_events_armv6.c | 7 - drivers/gator/gator_events_armv7.c | 7 - drivers/gator/gator_events_block.c | 2 - drivers/gator/gator_events_ccn-504.c | 192 ++++++----- drivers/gator/gator_events_irq.c | 2 - drivers/gator/gator_events_l2c-310.c | 2 - drivers/gator/gator_events_mali_4xx.c | 30 +- drivers/gator/gator_events_mali_common.c | 9 +- drivers/gator/gator_events_mali_common.h | 4 +- drivers/gator/gator_events_mali_t6xx.c | 60 +++- drivers/gator/gator_events_mali_t6xx_hw.c | 46 ++- drivers/gator/gator_events_meminfo.c | 307 +++++++++++++----- ...events_mmaped.c => gator_events_mmapped.c} | 122 +++---- drivers/gator/gator_events_net.c | 5 +- drivers/gator/gator_events_perf_pmu.c | 83 ++++- drivers/gator/gator_events_sched.c | 2 - drivers/gator/gator_events_scorpion.c | 7 - drivers/gator/gator_main.c | 108 +++++- drivers/gator/gator_marshaling.c | 19 ++ drivers/gator/gator_trace_gpu.c | 6 +- drivers/gator/gator_trace_sched.c | 20 +- .../mali/mali_mjollnir_profiling_gator_api.h | 15 +- .../mali/mali_utgard_profiling_gator_api.h | 15 +- drivers/gator/mali_t6xx.mk | 11 +- tools/gator/daemon/Buffer.cpp | 11 + tools/gator/daemon/Buffer.h | 5 + tools/gator/daemon/Child.cpp | 13 +- tools/gator/daemon/Child.h | 4 + tools/gator/daemon/ConfigurationXML.h | 4 + tools/gator/daemon/Driver.h | 6 +- tools/gator/daemon/Fifo.h | 4 + tools/gator/daemon/Hwmon.cpp | 4 + tools/gator/daemon/Hwmon.h | 4 + tools/gator/daemon/OlySocket.cpp | 23 +- tools/gator/daemon/Sender.h | 4 + tools/gator/daemon/SessionData.cpp | 12 +- tools/gator/daemon/SessionData.h | 6 +- tools/gator/daemon/SessionXML.h | 4 + tools/gator/daemon/StreamlineSetup.h | 4 + tools/gator/daemon/common.mk | 2 +- tools/gator/daemon/events-CCI-400.xml | 62 +++- tools/gator/daemon/events-Linux.xml | 10 +- tools/gator/daemon/events-Mali-T6xx.xml | 10 + tools/gator/daemon/events-Mali-T6xx_hw.xml | 5 +- tools/gator/daemon/main.cpp | 25 +- 51 files changed, 1058 insertions(+), 484 deletions(-) delete mode 100755 drivers/gator/gator_events.sh rename drivers/gator/{gator_events_mmaped.c => gator_events_mmapped.c} (56%) diff --git a/drivers/gator/Makefile b/drivers/gator/Makefile index 0d4ca68701e0..3dc9d059a4b4 100644 --- a/drivers/gator/Makefile +++ b/drivers/gator/Makefile @@ -3,7 +3,8 @@ ifneq ($(KERNELRELEASE),) # Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c # EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING -obj-m := gator.o +CONFIG_GATOR ?= m +obj-$(CONFIG_GATOR) := gator.o gator-y := gator_main.o \ gator_events_irq.o \ @@ -11,23 +12,40 @@ gator-y := gator_main.o \ gator_events_net.o \ gator_events_block.o \ gator_events_meminfo.o \ - gator_events_perf_pmu.o - -gator-y += gator_events_mmaped.o + gator_events_perf_pmu.o \ + gator_events_mmapped.o \ +# Convert the old GATOR_WITH_MALI_SUPPORT to the new kernel flags ifneq ($(GATOR_WITH_MALI_SUPPORT),) -ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) -gator-y += gator_events_mali_t6xx.o \ + CONFIG_GATOR_WITH_MALI_SUPPORT := y + ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) + CONFIG_GATOR_MALI_4XXMP := n + CONFIG_GATOR_MALI_T6XX := y + else + CONFIG_GATOR_MALI_4XXMP := y + CONFIG_GATOR_MALI_T6XX := n + endif + EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) + ifneq ($(GATOR_MALI_INTERFACE_STYLE),) + EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE) + endif +endif + +ifeq ($(CONFIG_GATOR_WITH_MALI_SUPPORT),y) + ifeq ($(CONFIG_GATOR_MALI_T6XX),y) + gator-y += gator_events_mali_t6xx.o \ gator_events_mali_t6xx_hw.o -include $(M)/mali_t6xx.mk -else -gator-y += gator_events_mali_4xx.o -endif -gator-y += gator_events_mali_common.o -EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) -ifneq ($(GATOR_MALI_INTERFACE_STYLE),) -EXTRA_CFLAGS += -DGATOR_MALI_INTERFACE_STYLE=$(GATOR_MALI_INTERFACE_STYLE) -endif + include $(src)/mali_t6xx.mk + else + gator-y += gator_events_mali_4xx.o + endif + gator-y += gator_events_mali_common.o + + ifneq ($(CONFIG_GATOR_MALI_PATH),) + ccflags-y += -I$(CONFIG_GATOR_MALI_PATH) + endif + ccflags-$(CONFIG_GATOR_MALI_4XXMP) += -DMALI_SUPPORT=MALI_4xx + ccflags-$(CONFIG_GATOR_MALI_T6XX) += -DMALI_SUPPORT=MALI_T6xx endif # GATOR_TEST controls whether to include (=1) or exclude (=0) test code. @@ -42,17 +60,6 @@ gator-$(CONFIG_ARM) += gator_events_armv6.o \ gator-$(CONFIG_ARM64) += gator_events_ccn-504.o -$(obj)/gator_main.o: gator_events.h - -clean-files := gator_events.h - - chk_events.h = : - quiet_chk_events.h = echo ' CHK $@' -silent_chk_events.h = : -gator_events.h: FORCE - @$($(quiet)chk_events.h) - $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@ - else all: @@ -63,7 +70,7 @@ all: $(error) clean: - rm -f *.o .*.cmd gator_events.h modules.order Module.symvers gator.ko gator.mod.c + rm -f *.o .*.cmd modules.order Module.symvers gator.ko gator.mod.c rm -rf .tmp_versions endif diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h index 2e122da767d8..d8981ed85a6a 100644 --- a/drivers/gator/gator.h +++ b/drivers/gator/gator.h @@ -112,14 +112,10 @@ struct gator_interface { void (*offline_dispatch)(int cpu, bool migrate); // called in process context but may not be running on core 'cpu' int (*read)(int **buffer); int (*read64)(long long **buffer); + int (*read_proc)(long long **buffer, struct task_struct *); struct list_head list; }; -// gator_events_init is used as a search term in gator_events.sh -#define gator_events_init(initfn) \ - static inline int __gator_events_init_test(void) \ - { return initfn(); } - int gator_events_install(struct gator_interface *interface); int gator_events_get_key(void); u32 gator_cpuid(void); diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c index 4715f64a1865..a406e4882974 100644 --- a/drivers/gator/gator_annotate_kernel.c +++ b/drivers/gator/gator_annotate_kernel.c @@ -29,11 +29,27 @@ static void kannotate_write(const char *ptr, unsigned int size) } } +static void marshal_u16(char *buf, u16 val) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; +} + +static void marshal_u32(char *buf, u32 val) { + buf[0] = val & 0xff; + buf[1] = (val >> 8) & 0xff; + buf[2] = (val >> 16) & 0xff; + buf[3] = (val >> 24) & 0xff; +} + void gator_annotate_channel(int channel, const char *str) { - int str_size = strlen(str) & 0xffff; - long long header = ESCAPE_CODE | (STRING_ANNOTATION << 8) | (channel << 16) | ((long long)str_size << 48); - kannotate_write((char *)&header, sizeof(header)); + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size); } @@ -48,14 +64,14 @@ EXPORT_SYMBOL(gator_annotate); void gator_annotate_channel_color(int channel, int color, const char *str) { - int str_size = (strlen(str) + 4) & 0xffff; + const u16 str_size = (strlen(str) + 4) & 0xffff; char header[12]; header[0] = ESCAPE_CODE; header[1] = STRING_ANNOTATION; - *(u32 *)(&header[2]) = channel; - *(u16 *)(&header[6]) = str_size; - *(u32 *)(&header[8]) = color; - kannotate_write((char *)&header, sizeof(header)); + marshal_u32(header + 2, channel); + marshal_u16(header + 6, str_size); + marshal_u32(header + 8, color); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size - 4); } @@ -70,8 +86,12 @@ EXPORT_SYMBOL(gator_annotate_color); void gator_annotate_channel_end(int channel) { - long long header = ESCAPE_CODE | (STRING_ANNOTATION << 8) | (channel << 16); - kannotate_write((char *)&header, sizeof(header)); + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = STRING_ANNOTATION; + marshal_u32(header + 2, channel); + marshal_u16(header + 6, 0); + kannotate_write(header, sizeof(header)); } EXPORT_SYMBOL(gator_annotate_channel_end); @@ -85,14 +105,14 @@ EXPORT_SYMBOL(gator_annotate_end); void gator_annotate_name_channel(int channel, int group, const char* str) { - int str_size = strlen(str) & 0xffff; + const u16 str_size = strlen(str) & 0xffff; char header[12]; header[0] = ESCAPE_CODE; header[1] = NAME_CHANNEL_ANNOTATION; - *(u32 *)(&header[2]) = channel; - *(u32 *)(&header[6]) = group; - *(u16 *)(&header[10]) = str_size; - kannotate_write((char *)&header, sizeof(header)); + marshal_u32(header + 2, channel); + marshal_u32(header + 6, group); + marshal_u16(header + 10, str_size); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size); } @@ -100,9 +120,13 @@ EXPORT_SYMBOL(gator_annotate_name_channel); void gator_annotate_name_group(int group, const char* str) { - int str_size = strlen(str) & 0xffff; - long long header = ESCAPE_CODE | (NAME_GROUP_ANNOTATION << 8) | (group << 16) | ((long long)str_size << 48); - kannotate_write((char *)&header, sizeof(header)); + const u16 str_size = strlen(str) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = NAME_GROUP_ANNOTATION; + marshal_u32(header + 2, group); + marshal_u16(header + 6, str_size); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size); } @@ -110,11 +134,16 @@ EXPORT_SYMBOL(gator_annotate_name_group); void gator_annotate_visual(const char *data, unsigned int length, const char *str) { - int str_size = strlen(str) & 0xffff; - int visual_annotation = ESCAPE_CODE | (VISUAL_ANNOTATION << 8) | (str_size << 16); - kannotate_write((char *)&visual_annotation, sizeof(visual_annotation)); + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + char header_length[4]; + header[0] = ESCAPE_CODE; + header[1] = VISUAL_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header_length, length); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size); - kannotate_write((char *)&length, sizeof(length)); + kannotate_write(header_length, sizeof(header_length)); kannotate_write(data, length); } @@ -122,17 +151,23 @@ EXPORT_SYMBOL(gator_annotate_visual); void gator_annotate_marker(void) { - int header = ESCAPE_CODE | (MARKER_ANNOTATION << 8); - kannotate_write((char *)&header, sizeof(header)); + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 0); + kannotate_write(header, sizeof(header)); } EXPORT_SYMBOL(gator_annotate_marker); void gator_annotate_marker_str(const char *str) { - int str_size = strlen(str) & 0xffff; - int header = ESCAPE_CODE | (MARKER_ANNOTATION << 8) | (str_size << 16); - kannotate_write((char *)&header, sizeof(header)); + const u16 str_size = strlen(str) & 0xffff; + char header[4]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size); } @@ -140,17 +175,25 @@ EXPORT_SYMBOL(gator_annotate_marker_str); void gator_annotate_marker_color(int color) { - long long header = (ESCAPE_CODE | (MARKER_ANNOTATION << 8) | 0x00040000 | ((long long)color << 32)); - kannotate_write((char *)&header, sizeof(header)); + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, 4); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); } EXPORT_SYMBOL(gator_annotate_marker_color); void gator_annotate_marker_color_str(int color, const char *str) { - int str_size = (strlen(str) + 4) & 0xffff; - long long header = ESCAPE_CODE | (MARKER_ANNOTATION << 8) | (str_size << 16) | ((long long)color << 32); - kannotate_write((char *)&header, sizeof(header)); + const u16 str_size = (strlen(str) + 4) & 0xffff; + char header[8]; + header[0] = ESCAPE_CODE; + header[1] = MARKER_ANNOTATION; + marshal_u16(header + 2, str_size); + marshal_u32(header + 4, color); + kannotate_write(header, sizeof(header)); kannotate_write(str, str_size - 4); } diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c index 0670d6cea9bd..ffacb490194c 100644 --- a/drivers/gator/gator_backtrace.c +++ b/drivers/gator/gator_backtrace.c @@ -132,13 +132,21 @@ static int report_trace(struct stackframe *frame, void *d) // Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile // #define GATOR_KERNEL_STACK_UNWINDING + +#if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING) +// Disabled by default +MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding."); +bool kernel_stack_unwinding = 0; +module_param(kernel_stack_unwinding, bool, 0644); +#endif + static void kernel_backtrace(int cpu, struct pt_regs *const regs) { #if defined(__arm__) || defined(__aarch64__) #ifdef GATOR_KERNEL_STACK_UNWINDING int depth = gator_backtrace_depth; #else - int depth = 1; + int depth = (kernel_stack_unwinding ? gator_backtrace_depth : 1); #endif struct stackframe frame; if (depth == 0) diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c index 5f98a1cc309c..91adfdde9be2 100644 --- a/drivers/gator/gator_cookies.c +++ b/drivers/gator/gator_cookies.c @@ -8,7 +8,8 @@ */ #define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ -#define TRANSLATE_SIZE 256 +#define TRANSLATE_BUFFER_SIZE 512 // must be a power of 2 - 512/4 = 128 entries +#define TRANSLATE_TEXT_SIZE 256 #define MAX_COLLISIONS 2 static uint32_t *gator_crc32_table; @@ -22,7 +23,7 @@ static DEFINE_PER_CPU(int, translate_buffer_read); static DEFINE_PER_CPU(int, translate_buffer_write); static DEFINE_PER_CPU(void **, translate_buffer); -static inline uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); +static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); static void wq_cookie_handler(struct work_struct *unused); DECLARE_WORK(cookie_work, wq_cookie_handler); static struct timer_list app_process_wake_up_timer; @@ -107,11 +108,13 @@ static void cookiemap_add(uint64_t key, uint32_t value) values[0] = value; } +#ifndef CONFIG_PREEMPT_RT_FULL static void translate_buffer_write_ptr(int cpu, void *x) { per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; } +#endif static void *translate_buffer_read_ptr(int cpu) { @@ -124,7 +127,7 @@ static void wq_cookie_handler(struct work_struct *unused) { struct task_struct *task; char *text; - int cpu = get_physical_cpu(); + int cpu = get_physical_cpu(), cookie; unsigned int commit; mutex_lock(&start_mutex); @@ -134,7 +137,8 @@ static void wq_cookie_handler(struct work_struct *unused) while (per_cpu(translate_buffer_read, cpu) != commit) { task = (struct task_struct *)translate_buffer_read_ptr(cpu); text = (char *)translate_buffer_read_ptr(cpu); - get_cookie(cpu, task, text, true); + cookie = get_cookie(cpu, task, text, true); + marshal_link(cookie, task->tgid, task->pid); } } @@ -156,15 +160,16 @@ static int translate_app_process(const char **text, int cpu, struct task_struct struct mm_struct *mm; struct page *page = NULL; struct vm_area_struct *page_vma; - int bytes, offset, retval = 0, ptr; + int bytes, offset, retval = 0; char *buf = per_cpu(translate_text, cpu); +#ifndef CONFIG_PREEMPT_RT_FULL // Push work into a work queue if in atomic context as the kernel functions below might sleep // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems // inconsistent during a context switch between android/linux versions if (!from_wq) { // Check if already in buffer - ptr = per_cpu(translate_buffer_read, cpu); + int ptr = per_cpu(translate_buffer_read, cpu); while (ptr != per_cpu(translate_buffer_write, cpu)) { if (per_cpu(translate_buffer, cpu)[ptr] == (void *)task) goto out; @@ -174,9 +179,11 @@ static int translate_app_process(const char **text, int cpu, struct task_struct translate_buffer_write_ptr(cpu, (void *)task); translate_buffer_write_ptr(cpu, (void *)*text); + // Not safe to call in RT-Preempt full in schedule switch context mod_timer(&app_process_wake_up_timer, jiffies + 1); goto out; } +#endif mm = get_task_mm(task); if (!mm) @@ -186,8 +193,8 @@ static int translate_app_process(const char **text, int cpu, struct task_struct addr = mm->arg_start; len = mm->arg_end - mm->arg_start; - if (len > TRANSLATE_SIZE) - len = TRANSLATE_SIZE; + if (len > TRANSLATE_TEXT_SIZE) + len = TRANSLATE_TEXT_SIZE; down_read(&mm->mmap_sem); while (len) { @@ -225,7 +232,7 @@ out: return retval; } -static inline uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq) +static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq) { unsigned long flags, cookie; uint64_t key; @@ -312,8 +319,7 @@ static int cookies_initialize(void) uint32_t crc, poly; int i, j, cpu, size, err = 0; - int translate_buffer_size = 512; // must be a power of 2 - translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; + translate_buffer_mask = TRANSLATE_BUFFER_SIZE / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; for_each_present_cpu(cpu) { per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; @@ -334,7 +340,7 @@ static int cookies_initialize(void) } memset(per_cpu(cookie_values, cpu), 0, size); - per_cpu(translate_buffer, cpu) = (void **)kmalloc(translate_buffer_size, GFP_KERNEL); + per_cpu(translate_buffer, cpu) = (void **)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); if (!per_cpu(translate_buffer, cpu)) { err = -ENOMEM; goto cookie_setup_error; @@ -343,7 +349,7 @@ static int cookies_initialize(void) per_cpu(translate_buffer_write, cpu) = 0; per_cpu(translate_buffer_read, cpu) = 0; - per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL); + per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_TEXT_SIZE, GFP_KERNEL); if (!per_cpu(translate_text, cpu)) { err = -ENOMEM; goto cookie_setup_error; @@ -353,6 +359,10 @@ static int cookies_initialize(void) // build CRC32 table poly = 0x04c11db7; gator_crc32_table = (uint32_t *)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); + if (!gator_crc32_table) { + err = -ENOMEM; + goto cookie_setup_error; + } for (i = 0; i < 256; i++) { crc = i; for (j = 8; j > 0; j--) { diff --git a/drivers/gator/gator_events.sh b/drivers/gator/gator_events.sh deleted file mode 100755 index 5467dd6d17d6..000000000000 --- a/drivers/gator/gator_events.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -EVENTS=`grep gator_events_init *.c | sed 's/.\+gator_events_init(\(.\+\)).\+/\1/'` - -( - echo /\* This file is auto generated \*/ - echo - for EVENT in $EVENTS; do - echo __weak int $EVENT\(void\)\; - done - echo - echo static int \(*gator_events_list[]\)\(void\) = { - for EVENT in $EVENTS; do - echo \ $EVENT, - done - echo }\; -) > $1.tmp - -cmp -s $1 $1.tmp && rm $1.tmp || mv $1.tmp $1 diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c index 4f1bca6e2dbe..dd7974090b82 100644 --- a/drivers/gator/gator_events_armv6.c +++ b/drivers/gator/gator_events_armv6.c @@ -234,11 +234,4 @@ int gator_events_armv6_init(void) return gator_events_install(&gator_events_armv6_interface); } -gator_events_init(gator_events_armv6_init); - -#else -int gator_events_armv6_init(void) -{ - return -1; -} #endif diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c index 58f29566eeab..30881c8fd3fd 100644 --- a/drivers/gator/gator_events_armv7.c +++ b/drivers/gator/gator_events_armv7.c @@ -309,11 +309,4 @@ int gator_events_armv7_init(void) return gator_events_install(&gator_events_armv7_interface); } -gator_events_init(gator_events_armv7_init); - -#else -int gator_events_armv7_init(void) -{ - return -1; -} #endif diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c index 56c6a6736529..691ef2574536 100644 --- a/drivers/gator/gator_events_block.c +++ b/drivers/gator/gator_events_block.c @@ -151,5 +151,3 @@ int gator_events_block_init(void) return gator_events_install(&gator_events_block_interface); } - -gator_events_init(gator_events_block_init); diff --git a/drivers/gator/gator_events_ccn-504.c b/drivers/gator/gator_events_ccn-504.c index b91a9a149b90..b89231967c75 100644 --- a/drivers/gator/gator_events_ccn-504.c +++ b/drivers/gator/gator_events_ccn-504.c @@ -6,23 +6,16 @@ * published by the Free Software Foundation. */ -/******************************************************************************* - * WARNING: This code is an experimental implementation of the CCN-504 hardware - * counters which has not been tested on the hardware. Commented debug - * statements are present and can be uncommented for diagnostic purposes. - ******************************************************************************/ - #include #include #include "gator.h" -#define PERIPHBASE 0x2E000000 - #define NUM_REGIONS 256 #define REGION_SIZE (64*1024) #define REGION_DEBUG 1 #define REGION_XP 64 +#define NUM_XPS 11 // DT (Debug) region #define PMEVCNTSR0 0x0150 @@ -34,27 +27,86 @@ // XP region #define DT_CONFIG 0x0300 +#define DT_CONTROL 0x0370 // Multiple #define PMU_EVENT_SEL 0x0600 #define OLY_ID 0xFF00 #define CCNT 4 -#define CNTMAX (4 + 1) +#define CNTMAX (CCNT + 1) #define get_pmu_event_id(event) (((event) >> 0) & 0xFF) #define get_node_type(event) (((event) >> 8) & 0xFF) #define get_region(event) (((event) >> 16) & 0xFF) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + if (!val) return -EINVAL; \ + ret = strtolfn(val, 0, &l); \ + if (ret == -EINVAL || ((type)l != l)) \ + return -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, struct kernel_param *kp) \ + { \ + return sprintf(buffer, format, *((type *)kp->arg)); \ + } + +#else + +// From kernel/params.c +#define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ + int param_set_##name(const char *val, const struct kernel_param *kp) \ + { \ + tmptype l; \ + int ret; \ + \ + ret = strtolfn(val, 0, &l); \ + if (ret < 0 || ((type)l != l)) \ + return ret < 0 ? ret : -EINVAL; \ + *((type *)kp->arg) = l; \ + return 0; \ + } \ + int param_get_##name(char *buffer, const struct kernel_param *kp) \ + { \ + return scnprintf(buffer, PAGE_SIZE, format, \ + *((type *)kp->arg)); \ + } \ + struct kernel_param_ops param_ops_##name = { \ + .set = param_set_##name, \ + .get = param_get_##name, \ + }; \ + EXPORT_SYMBOL(param_set_##name); \ + EXPORT_SYMBOL(param_get_##name); \ + EXPORT_SYMBOL(param_ops_##name) + +#endif + +STANDARD_PARAM_DEF(u64, u64, "%llu", u64, strict_strtoull); + +// From include/linux/moduleparam.h +#define param_check_u64(name, p) __param_check(name, p, u64) + MODULE_PARM_DESC(ccn504_addr, "CCN-504 physical base address"); -static unsigned long ccn504_addr = 0; -module_param(ccn504_addr, ulong, 0444); +static u64 ccn504_addr = 0; +module_param(ccn504_addr, u64, 0444); static void __iomem *gator_events_ccn504_base; +static bool gator_events_ccn504_global_enabled; static unsigned long gator_events_ccn504_enabled[CNTMAX]; static unsigned long gator_events_ccn504_event[CNTMAX]; static unsigned long gator_events_ccn504_key[CNTMAX]; static int gator_events_ccn504_buffer[2*CNTMAX]; +static int gator_events_ccn504_prev[CNTMAX]; static void gator_events_ccn504_create_shutdown(void) { @@ -96,7 +148,6 @@ static void gator_events_ccn504_set_dt_config(int xp_node_id, int event_num, int dt_config = readl(gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); dt_config |= (value + event_num) << (4*event_num); - //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, dt_config, (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); writel(dt_config, gator_events_ccn504_base + (REGION_XP + xp_node_id)*REGION_SIZE + DT_CONFIG); } @@ -104,6 +155,20 @@ static int gator_events_ccn504_start(void) { int i; + gator_events_ccn504_global_enabled = 0; + for (i = 0; i < CNTMAX; ++i) { + if (gator_events_ccn504_enabled[i]) { + gator_events_ccn504_global_enabled = 1; + break; + } + } + + if (!gator_events_ccn504_global_enabled) { + return 0; + } + + memset(&gator_events_ccn504_prev, 0x80, sizeof(gator_events_ccn504_prev)); + // Disable INTREQ on overflow // [6] ovfl_intr_en = 0 // perhaps set to 1? @@ -112,9 +177,22 @@ static int gator_events_ccn504_start(void) // [4:1] cntcfg = 0 // Enable PMU features // [0] pmu_en = 1 - //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0x1, REGION_DEBUG*REGION_SIZE + PMCR); writel(0x1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMCR); + // Configure the XPs + for (i = 0; i < NUM_XPS; ++i) { + int dt_control; + + // Pass on all events + writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + + // Enable PMU capability + // [0] dt_enable = 1 + dt_control = readl(gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + dt_control |= 0x1; + writel(dt_control, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONTROL); + } + // Assume no other pmu_event_sel registers are set // cycle counter does not need to be enabled @@ -134,15 +212,14 @@ static int gator_events_ccn504_start(void) pmu_event_id = get_pmu_event_id(gator_events_ccn504_event[i]); node_type = get_node_type(gator_events_ccn504_event[i]); region = get_region(gator_events_ccn504_event[i]); - //printk(KERN_ERR "%s(%s:%i) pmu_event_id: %x node_type: %x region: %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_id, node_type, region); // Verify the node_type oly_id_whole = readl(gator_events_ccn504_base + region*REGION_SIZE + OLY_ID); oly_id = oly_id_whole & 0x1F; node_id = (oly_id_whole >> 8) & 0x7F; if ((oly_id != node_type) || - ((node_type == 0x16) && ((oly_id == 0x14) || (oly_id == 0x15) || (oly_id == 0x16) || (oly_id == 0x18) || (oly_id == 0x19) || (oly_id == 0x1A)))) { - printk(KERN_ERR "%s(%s:%i) oly_id is %x expected %x\n", __FUNCTION__, __FILE__, __LINE__, oly_id, node_type); + ((node_type == 0x16) && ((oly_id != 0x14) && (oly_id != 0x15) && (oly_id != 0x16) && (oly_id != 0x18) && (oly_id != 0x19) && (oly_id != 0x1A)))) { + printk(KERN_ERR "gator: oly_id is 0x%x expected 0x%x\n", oly_id, node_type); return -1; } @@ -160,7 +237,6 @@ static int gator_events_ccn504_start(void) gator_events_ccn504_set_dt_config(node_id/2, i, (node_id & 1) == 0 ? 0x8 : 0xC); break; } - //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, pmu_event_sel, region*REGION_SIZE + PMU_EVENT_SEL); writel(pmu_event_sel, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); } @@ -171,21 +247,25 @@ static void gator_events_ccn504_stop(void) { int i; + if (!gator_events_ccn504_global_enabled) { + return; + } + // cycle counter does not need to be disabled for (i = 0; i < CCNT; ++i) { - int node_type; int region; - node_type = get_node_type(gator_events_ccn504_event[i]); + if (!gator_events_ccn504_enabled[i]) { + continue; + } + region = get_region(gator_events_ccn504_event[i]); - //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, region*REGION_SIZE + PMU_EVENT_SEL); writel(0, gator_events_ccn504_base + region*REGION_SIZE + PMU_EVENT_SEL); } // Clear dt_config - for (i = 0; i < 11; ++i) { - //printk(KERN_ERR "%s(%s:%i) writel %x %x\n", __FUNCTION__, __FILE__, __LINE__, 0, (REGION_XP + i)*REGION_SIZE + DT_CONFIG); + for (i = 0; i < NUM_XPS; ++i) { writel(0, gator_events_ccn504_base + (REGION_XP + i)*REGION_SIZE + DT_CONFIG); } } @@ -194,27 +274,20 @@ static int gator_events_ccn504_read(int **buffer) { int i; int len = 0; + int value; - if (!on_primary_core()) { + if (!on_primary_core() || !gator_events_ccn504_global_enabled) { return 0; } // Verify the pmsr register is zero - //i = 0; - while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0) { - //++i; - } - //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i); + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) != 0); // Request a PMU snapshot writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_REQ); // Wait for the snapshot - //i = 0; - while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0) { - //++i; - } - //printk(KERN_ERR "%s(%s:%i) %i\n", __FUNCTION__, __FILE__, __LINE__, i); + while (readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR) == 0); // Read the shadow registers for (i = 0; i < CNTMAX; ++i) { @@ -222,8 +295,12 @@ static int gator_events_ccn504_read(int **buffer) continue; } - gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i]; - gator_events_ccn504_buffer[len++] = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i)); + value = readl(gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + (i == CCNT ? PMCCNTRSR : PMEVCNTSR0 + 8*i)); + if (gator_events_ccn504_prev[i] != 0x80808080) { + gator_events_ccn504_buffer[len++] = gator_events_ccn504_key[i]; + gator_events_ccn504_buffer[len++] = value - gator_events_ccn504_prev[i]; + } + gator_events_ccn504_prev[i] = value; // Are the counters registers cleared when read? Is that what the cntr_rst bit on the pmcr register does? } @@ -231,20 +308,12 @@ static int gator_events_ccn504_read(int **buffer) // Clear the PMU snapshot status writel(1, gator_events_ccn504_base + REGION_DEBUG*REGION_SIZE + PMSR_CLR); + if (buffer) + *buffer = gator_events_ccn504_buffer; + return len; } -static void __maybe_unused gator_events_ccn504_enumerate(int pos, int size) -{ - int i; - u32 oly_id; - - for (i = pos; i < pos + size; ++i) { - oly_id = readl(gator_events_ccn504_base + i*REGION_SIZE + OLY_ID); - printk(KERN_ERR "%s(%s:%i) %i %08x\n", __FUNCTION__, __FILE__, __LINE__, i, oly_id); - } -} - static struct gator_interface gator_events_ccn504_interface = { .shutdown = gator_events_ccn504_create_shutdown, .create_files = gator_events_ccn504_create_files, @@ -263,36 +332,9 @@ int gator_events_ccn504_init(void) gator_events_ccn504_base = ioremap(ccn504_addr, NUM_REGIONS*REGION_SIZE); if (gator_events_ccn504_base == NULL) { - printk(KERN_ERR "%s(%s:%i) ioremap returned NULL\n", __FUNCTION__, __FILE__, __LINE__); + printk(KERN_ERR "gator: ioremap returned NULL\n"); return -1; } - //printk(KERN_ERR "%s(%s:%i)\n", __FUNCTION__, __FILE__, __LINE__); - - // Test - can memory be read - { - //gator_events_ccn504_enumerate(0, NUM_REGIONS); - -#if 0 - // DT - gator_events_ccn504_enumerate(1, 1); - // HN-F - gator_events_ccn504_enumerate(32, 8); - // XP - gator_events_ccn504_enumerate(64, 11); - // RN-I - gator_events_ccn504_enumerate(128, 1); - gator_events_ccn504_enumerate(130, 1); - gator_events_ccn504_enumerate(134, 1); - gator_events_ccn504_enumerate(140, 1); - gator_events_ccn504_enumerate(144, 1); - gator_events_ccn504_enumerate(148, 1); - // SBAS - gator_events_ccn504_enumerate(129, 1); - gator_events_ccn504_enumerate(137, 1); - gator_events_ccn504_enumerate(139, 1); - gator_events_ccn504_enumerate(147, 1); -#endif - } for (i = 0; i < CNTMAX; ++i) { gator_events_ccn504_enabled[i] = 0; @@ -302,5 +344,3 @@ int gator_events_ccn504_init(void) return gator_events_install(&gator_events_ccn504_interface); } - -gator_events_init(gator_events_ccn504_init); diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c index b4df7faefff8..b11879a248f8 100644 --- a/drivers/gator/gator_events_irq.c +++ b/drivers/gator/gator_events_irq.c @@ -163,5 +163,3 @@ int gator_events_irq_init(void) return gator_events_install(&gator_events_irq_interface); } - -gator_events_init(gator_events_irq_init); diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c index 21aa4a214d97..ee521af22517 100644 --- a/drivers/gator/gator_events_l2c-310.c +++ b/drivers/gator/gator_events_l2c-310.c @@ -206,5 +206,3 @@ int gator_events_l2c310_init(void) return gator_events_install(&gator_events_l2c310_interface); } - -gator_events_init(gator_events_l2c310_init); diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c index dd275f70b700..6719c1ec73a2 100644 --- a/drivers/gator/gator_events_mali_4xx.c +++ b/drivers/gator/gator_events_mali_4xx.c @@ -415,25 +415,12 @@ static void mali_counter_initialize(void) int i; int core_id; - mali_osk_fb_control_set_type *mali_set_fb_event; mali_profiling_control_type *mali_control; init_counters(COUNTER_L2_0_C0, COUNTER_L2_0_C0 + (2 * n_l2_cores) - 1); init_counters(COUNTER_VP_0_C0, COUNTER_VP_0_C0 + (2 * n_vp_cores) - 1); init_counters(COUNTER_FP_0_C0, COUNTER_FP_0_C0 + (2 * n_fp_cores) - 1); - mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); - - if (mali_set_fb_event) { - pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); - - mali_set_fb_event(0, (counter_enabled[COUNTER_FILMSTRIP] ? 1 : 0)); - - symbol_put(_mali_osk_fb_control_set); - } else { - printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); - } - /* Generic control interface for Mali DDK. */ mali_control = symbol_get(_mali_profiling_control); if (mali_control) { @@ -491,7 +478,6 @@ static void mali_counter_initialize(void) static void mali_counter_deinitialize(void) { mali_profiling_set_event_type *mali_set_hw_event; - mali_osk_fb_control_set_type *mali_set_fb_event; mali_profiling_control_type *mali_control; mali_set_hw_event = symbol_get(_mali_profiling_set_event); @@ -509,23 +495,11 @@ static void mali_counter_deinitialize(void) printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); } - mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); - - if (mali_set_fb_event) { - pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); - - mali_set_fb_event(0, 0); - - symbol_put(_mali_osk_fb_control_set); - } else { - printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); - } - /* Generic control interface for Mali DDK. */ mali_control = symbol_get(_mali_profiling_control); if (mali_control) { - pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_set_fb_event); + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control); /* Reset the DDK state - disable counter collection */ mali_control(SW_COUNTER_ENABLE, 0); @@ -747,5 +721,3 @@ int gator_events_mali_init(void) return gator_events_install(&gator_events_mali_interface); } - -gator_events_init(gator_events_mali_init); diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c index 5a98b3745fc2..466ca1683c7e 100644 --- a/drivers/gator/gator_events_mali_common.c +++ b/drivers/gator/gator_events_mali_common.c @@ -28,7 +28,7 @@ extern const char *gator_mali_get_mali_name(void) } } -extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter) +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event) { int err; char buf[255]; @@ -56,6 +56,13 @@ extern int gator_mali_create_file_system(const char *mali_name, const char *even pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); return -1; } + if (event != NULL) { + err = gatorfs_create_ulong(sb, dir, "event", event); + if (err != 0) { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + return -1; + } + } } return 0; diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h index d67ee2d245ad..509f9b61884a 100644 --- a/drivers/gator/gator_events_mali_common.h +++ b/drivers/gator/gator_events_mali_common.h @@ -43,7 +43,6 @@ typedef struct { * Mali-4xx */ typedef int mali_profiling_set_event_type(unsigned int, int); -typedef void mali_osk_fb_control_set_type(unsigned int, unsigned int); typedef void mali_profiling_control_type(unsigned int, unsigned int); typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, unsigned int *, unsigned int *); @@ -51,7 +50,6 @@ typedef void mali_profiling_get_counters_type(unsigned int *, unsigned int *, un * Driver entry points for functions called directly by gator. */ extern int _mali_profiling_set_event(unsigned int, int); -extern void _mali_osk_fb_control_set(unsigned int, unsigned int); extern void _mali_profiling_control(unsigned int, unsigned int); extern void _mali_profiling_get_counters(unsigned int *, unsigned int *, unsigned int *, unsigned int *); @@ -75,7 +73,7 @@ extern const char *gator_mali_get_mali_name(void); * * @return 0 if entry point was created, non-zero if not. */ -extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter); +extern int gator_mali_create_file_system(const char *mali_name, const char *event_name, struct super_block *sb, struct dentry *root, mali_counter *counter, unsigned long *event); /** * Initializes the counter array. diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c index 2576a99a126a..7bf7d6a6dbf9 100644 --- a/drivers/gator/gator_events_mali_t6xx.c +++ b/drivers/gator/gator_events_mali_t6xx.c @@ -109,12 +109,14 @@ enum { #define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0])) #define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS) #define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0])) -#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS) +#define FILMSTRIP (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS) +#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS + 1) /* * gatorfs variables for counter enable state */ static mali_counter counters[NUMBER_OF_EVENTS]; +static unsigned long filmstrip_event; /* An array used to return the data we recorded * as key,value pairs hence the *2 @@ -285,28 +287,37 @@ static int create_files(struct super_block *sb, struct dentry *root) */ int counter_index = 0; const char *mali_name = gator_mali_get_mali_name(); + mali_profiling_control_type *mali_control; for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) { - if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event]) != 0) { + if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event], NULL) != 0) { return -1; } counter_index++; } counter_index = 0; for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) { - if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event]) != 0) { + if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) { return -1; } counter_index++; } counter_index = 0; for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) { - if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event]) != 0) { + if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event], NULL) != 0) { return -1; } counter_index++; } + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + if (gator_mali_create_file_system(mali_name, "Filmstrip_cnt0", sb, root, &counters[FILMSTRIP], &filmstrip_event) != 0) { + return -1; + } + symbol_put(_mali_profiling_control); + } + return 0; } @@ -350,6 +361,7 @@ static int register_tracepoints(void) static int start(void) { unsigned int cnt; + mali_profiling_control_type *mali_control; /* Clean all data for the next capture */ for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) { @@ -370,6 +382,30 @@ static int start(void) return -1; } + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int enabled = counters[FILMSTRIP].enabled ? 1 : 0; + unsigned int rate = filmstrip_event & 0xff; + unsigned int resize_factor = (filmstrip_event >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + mali_control(FBDUMP_CONTROL_ENABLE, enabled); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d, resize_factor=%d\n", enabled, rate, resize_factor); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + /* * Set the first timestamp for calculating the sample interval. The first interval could be quite long, * since it will be the time between 'start' and the first 'read'. @@ -382,6 +418,8 @@ static int start(void) static void stop(void) { + mali_profiling_control_type *mali_control; + pr_debug("gator: Mali-T6xx: stop\n"); /* @@ -402,6 +440,18 @@ static void stop(void) GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change); pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n"); + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_control); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } } static int read(int **buffer) @@ -508,5 +558,3 @@ extern int gator_events_mali_t6xx_init(void) return gator_events_install(&gator_events_mali_t6xx_interface); } - -gator_events_init(gator_events_mali_t6xx_init); diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c index f557350eb9bc..e406991398d9 100644 --- a/drivers/gator/gator_events_mali_t6xx_hw.c +++ b/drivers/gator/gator_events_mali_t6xx_hw.c @@ -63,6 +63,8 @@ static kbase_instr_hwcnt_disable_type *kbase_instr_hwcnt_disable_symbol; static kbase_va_free_type *kbase_va_free_symbol; static kbase_destroy_context_type *kbase_destroy_context_symbol; +static long shader_present_low = 0; + /** The interval between reads, in ns. * * Earlier we introduced @@ -496,6 +498,7 @@ static int start(void) mali_error err; int cnt; u16 bitmask[] = { 0, 0, 0, 0 }; + unsigned long long shadersPresent = 0; /* Setup HW counters */ num_hardware_counters_enabled = 0; @@ -539,6 +542,11 @@ static int start(void) goto out; } + + /* See if we can get the number of shader cores */ + shadersPresent = kbdevice->shader_present_bitmap; + shader_present_low = (unsigned long)shadersPresent; + /* * The amount of memory needed to store the dump (bytes) * DUMP_SIZE = number of core groups @@ -679,21 +687,41 @@ static int read(int **buffer) kbase_device_busy = false; if (success == MALI_TRUE) { + /* Cycle through hardware counters and accumulate totals */ for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { const mali_counter *counter = &counters[cnt]; if (counter->enabled) { const int block = GET_HW_BLOCK(cnt); const int counter_offset = GET_COUNTER_OFFSET(cnt); - const u32 *counter_block = (u32 *) ((uintptr_t)kernel_dump_buffer + vithar_blocks[block]); - const u32 *counter_address = counter_block + counter_offset; - value = *counter_address; + const char* block_base_address = (char*)kernel_dump_buffer + vithar_blocks[block]; + /* If counter belongs to shader block need to take into account all cores */ if (block == SHADER_BLOCK) { - /* (counter_address + 0x000) has already been accounted-for above. */ - value += *(counter_address + 0x100); - value += *(counter_address + 0x200); - value += *(counter_address + 0x300); + int i = 0; + int shader_core_count = 0; + value = 0; + + for (i = 0; i < 4; i++) { + if ((shader_present_low >> i) & 1) { + value += *((u32*) (block_base_address + (0x100 * i)) + counter_offset); + shader_core_count++; + } + } + + for (i = 0; i < 4; i++) { + if((shader_present_low >> (i+4)) & 1) { + value += *((u32*)(block_base_address + (0x100 * i) + 0x800) + counter_offset); + shader_core_count++; + } + } + + /* Need to total by number of cores to produce an average */ + if (shader_core_count != 0) { + value /= shader_core_count; + } + } else { + value = *((u32*)block_base_address + counter_offset); } counter_dump[len++] = counter->key; @@ -727,7 +755,7 @@ static int create_files(struct super_block *sb, struct dentry *root) const char *mali_name = gator_mali_get_mali_name(); for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) { - if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event]) != 0) + if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event], NULL) != 0) return -1; counter_index++; } @@ -754,5 +782,3 @@ int gator_events_mali_t6xx_hw_init(void) return gator_events_install(&gator_events_mali_t6xx_interface); } - -gator_events_init(gator_events_mali_t6xx_hw_init); diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c index c1e360d12895..451290d9af17 100644 --- a/drivers/gator/gator_events_meminfo.c +++ b/drivers/gator/gator_events_meminfo.c @@ -8,27 +8,62 @@ */ #include "gator.h" + +#include +#include +#include +#include #include #include -#include -#define MEMINFO_MEMFREE 0 -#define MEMINFO_MEMUSED 1 -#define MEMINFO_BUFFERRAM 2 -#define MEMINFO_TOTAL 3 +enum { + MEMINFO_MEMFREE, + MEMINFO_MEMUSED, + MEMINFO_BUFFERRAM, + MEMINFO_TOTAL, +}; -static ulong meminfo_global_enabled; +enum { + PROC_SIZE, + PROC_SHARE, + PROC_TEXT, + PROC_DATA, + PROC_COUNT, +}; + +static const char * const meminfo_names[] = { + "Linux_meminfo_memfree", + "Linux_meminfo_memused", + "Linux_meminfo_bufferram", +}; + +static const char * const proc_names[] = { + "Linux_proc_statm_size", + "Linux_proc_statm_share", + "Linux_proc_statm_text", + "Linux_proc_statm_data", +}; + +static bool meminfo_global_enabled; static ulong meminfo_enabled[MEMINFO_TOTAL]; -static ulong meminfo_key[MEMINFO_TOTAL]; -static unsigned long long meminfo_buffer[MEMINFO_TOTAL * 2]; +static ulong meminfo_keys[MEMINFO_TOTAL]; +static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)]; static int meminfo_length = 0; -static unsigned int mem_event = 0; static bool new_data_avail; -static void wq_sched_handler(struct work_struct *wsptr); -DECLARE_WORK(work, wq_sched_handler); -static struct timer_list meminfo_wake_up_timer; -static void meminfo_wake_up_handler(unsigned long unused_data); +static bool proc_global_enabled; +static ulong proc_enabled[PROC_COUNT]; +static ulong proc_keys[PROC_COUNT]; +static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]); + +static int gator_meminfo_func(void *data); +static bool gator_meminfo_run; +// Initialize semaphore unlocked to initialize memory values +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static DECLARE_MUTEX(gator_meminfo_sem); +#else +static DEFINE_SEMAPHORE(gator_meminfo_sem); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) @@ -36,7 +71,7 @@ GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) #endif { - mem_event++; + up(&gator_meminfo_sem); } #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) @@ -45,12 +80,12 @@ GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) #endif { - mem_event++; + up(&gator_meminfo_sem); } GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) { - mem_event++; + up(&gator_meminfo_sem); } static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root) @@ -59,24 +94,21 @@ static int gator_events_meminfo_create_files(struct super_block *sb, struct dent int i; for (i = 0; i < MEMINFO_TOTAL; i++) { - switch (i) { - case MEMINFO_MEMFREE: - dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree"); - break; - case MEMINFO_MEMUSED: - dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused"); - break; - case MEMINFO_BUFFERRAM: - dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram"); - break; - default: - return -1; - } + dir = gatorfs_mkdir(sb, root, meminfo_names[i]); if (!dir) { return -1; } gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); - gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]); + } + + for (i = 0; i < PROC_COUNT; ++i) { + dir = gatorfs_mkdir(sb, root, proc_names[i]); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]); } return 0; @@ -86,13 +118,26 @@ static int gator_events_meminfo_start(void) { int i; - new_data_avail = true; + new_data_avail = false; + meminfo_global_enabled = 0; for (i = 0; i < MEMINFO_TOTAL; i++) { if (meminfo_enabled[i]) { meminfo_global_enabled = 1; + break; } } + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + proc_global_enabled = 1; + break; + } + } + if (meminfo_enabled[MEMINFO_MEMUSED]) { + proc_global_enabled = 1; + } + if (meminfo_global_enabled == 0) return 0; @@ -111,9 +156,16 @@ static int gator_events_meminfo_start(void) if (GATOR_REGISTER_TRACE(mm_page_alloc)) goto mm_page_alloc_exit; - setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0); + // Start worker thread + gator_meminfo_run = true; + // Since the mutex starts unlocked, memory values will be initialized + if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo"))) + goto kthread_run_exit; + return 0; +kthread_run_exit: + GATOR_UNREGISTER_TRACE(mm_page_alloc); mm_page_alloc_exit: #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_UNREGISTER_TRACE(mm_pagevec_free); @@ -132,8 +184,6 @@ mm_page_free_exit: static void gator_events_meminfo_stop(void) { - int i; - if (meminfo_global_enabled) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_UNREGISTER_TRACE(mm_page_free_direct); @@ -144,68 +194,75 @@ static void gator_events_meminfo_stop(void) #endif GATOR_UNREGISTER_TRACE(mm_page_alloc); - del_timer_sync(&meminfo_wake_up_timer); - } - - meminfo_global_enabled = 0; - for (i = 0; i < MEMINFO_TOTAL; i++) { - meminfo_enabled[i] = 0; + // Stop worker thread + gator_meminfo_run = false; + up(&gator_meminfo_sem); } } // Must be run in process context as the kernel function si_meminfo() can sleep -static void wq_sched_handler(struct work_struct *wsptr) +static int gator_meminfo_func(void *data) { struct sysinfo info; int i, len; unsigned long long value; - meminfo_length = len = 0; - - si_meminfo(&info); - for (i = 0; i < MEMINFO_TOTAL; i++) { - if (meminfo_enabled[i]) { - switch (i) { - case MEMINFO_MEMFREE: - value = info.freeram * PAGE_SIZE; - break; - case MEMINFO_MEMUSED: - value = (info.totalram - info.freeram) * PAGE_SIZE; - break; - case MEMINFO_BUFFERRAM: - value = info.bufferram * PAGE_SIZE; - break; - default: - value = 0; - break; - } - meminfo_buffer[len++] = (unsigned long long)meminfo_key[i]; - meminfo_buffer[len++] = value; + for (;;) { + if (down_killable(&gator_meminfo_sem)) { + break; } + + // Eat up any pending events + while (!down_trylock(&gator_meminfo_sem)); + + if (!gator_meminfo_run) { + break; + } + + meminfo_length = len = 0; + + si_meminfo(&info); + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + switch (i) { + case MEMINFO_MEMFREE: + value = info.freeram * PAGE_SIZE; + break; + case MEMINFO_MEMUSED: + // pid -1 means system wide + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = -1; + // Emit value + meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED]; + meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE; + // Clear pid + meminfo_buffer[len++] = 1; + meminfo_buffer[len++] = 0; + continue; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = meminfo_keys[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; } - meminfo_length = len; - new_data_avail = true; -} - -static void meminfo_wake_up_handler(unsigned long unused_data) -{ - // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater - schedule_work(&work); + return 0; } static int gator_events_meminfo_read(long long **buffer) { - static unsigned int last_mem_event = 0; - if (!on_primary_core() || !meminfo_global_enabled) return 0; - if (last_mem_event != mem_event) { - last_mem_event = mem_event; - mod_timer(&meminfo_wake_up_timer, jiffies + 1); - } - if (!new_data_avail) return 0; @@ -217,11 +274,97 @@ static int gator_events_meminfo_read(long long **buffer) return meminfo_length; } +static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task) +{ + struct mm_struct *mm; + u64 share = 0; + int i; + long long value; + int len = 0; + int cpu = get_physical_cpu(); + long long *buf = per_cpu(proc_buffer, cpu); + + if (!proc_global_enabled) { + return 0; + } + + // Collect the memory stats of the process instead of the thread + if (task->group_leader != NULL) { + task = task->group_leader; + } + + // get_task_mm/mmput is not needed in this context because the task and it's mm are required as part of the sched_switch + mm = task->mm; + if (mm == NULL) { + return 0; + } + + // Derived from task_statm in fs/proc/task_mmu.c + if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) { + share = get_mm_counter(mm, +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) + file_rss +#else + MM_FILEPAGES +#endif + ); + } + + // key of 1 indicates a pid + buf[len++] = 1; + buf[len++] = task->pid; + + for (i = 0; i < PROC_COUNT; ++i) { + if (proc_enabled[i]) { + switch (i) { + case PROC_SIZE: + value = mm->total_vm; + break; + case PROC_SHARE: + value = share; + break; + case PROC_TEXT: + value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT; + break; + case PROC_DATA: + value = mm->total_vm - mm->shared_vm; + break; + } + + buf[len++] = proc_keys[i]; + buf[len++] = value * PAGE_SIZE; + } + } + + if (meminfo_enabled[MEMINFO_MEMUSED]) { + value = share + get_mm_counter(mm, +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) + anon_rss +#else + MM_ANONPAGES +#endif + ); + // Send resident for this pid + buf[len++] = meminfo_keys[MEMINFO_MEMUSED]; + buf[len++] = value * PAGE_SIZE; + } + + // Clear pid + buf[len++] = 1; + buf[len++] = 0; + + if (buffer) + *buffer = buf; + + return len; +} + static struct gator_interface gator_events_meminfo_interface = { .create_files = gator_events_meminfo_create_files, .start = gator_events_meminfo_start, .stop = gator_events_meminfo_stop, .read64 = gator_events_meminfo_read, + .read_proc = gator_events_meminfo_read_proc, }; int gator_events_meminfo_init(void) @@ -231,10 +374,14 @@ int gator_events_meminfo_init(void) meminfo_global_enabled = 0; for (i = 0; i < MEMINFO_TOTAL; i++) { meminfo_enabled[i] = 0; - meminfo_key[i] = gator_events_get_key(); + meminfo_keys[i] = gator_events_get_key(); + } + + proc_global_enabled = 0; + for (i = 0; i < PROC_COUNT; ++i) { + proc_enabled[i] = 0; + proc_keys[i] = gator_events_get_key(); } return gator_events_install(&gator_events_meminfo_interface); } - -gator_events_init(gator_events_meminfo_init); diff --git a/drivers/gator/gator_events_mmaped.c b/drivers/gator/gator_events_mmapped.c similarity index 56% rename from drivers/gator/gator_events_mmaped.c rename to drivers/gator/gator_events_mmapped.c index f7670f62a258..f055e48d317a 100644 --- a/drivers/gator/gator_events_mmaped.c +++ b/drivers/gator/gator_events_mmapped.c @@ -11,12 +11,18 @@ * To add them to the events.xml, create an events-mmap.xml with the * following contents and rebuild gatord: * - * - * + * + * * * * * + * + * When adding custom events, be sure do the following + * - add any needed .c files to the gator driver Makefile + * - call gator_events_install in the events init function + * - add the init function to GATOR_EVENTS_LIST in gator_main.c + * - add a new events-*.xml file to the gator daemon and rebuild */ #include @@ -25,79 +31,71 @@ #include "gator.h" -#define MMAPED_COUNTERS_NUM 3 +#define MMAPPED_COUNTERS_NUM 3 + +static int mmapped_global_enabled; static struct { unsigned long enabled; unsigned long event; unsigned long key; -} mmaped_counters[MMAPED_COUNTERS_NUM]; +} mmapped_counters[MMAPPED_COUNTERS_NUM]; -static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2]; +static int mmapped_buffer[MMAPPED_COUNTERS_NUM * 2]; -#ifdef TODO -static void __iomem *mmaped_base; -#endif - -#ifndef TODO static s64 prev_time; -#endif -/* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/events */ -static int gator_events_mmaped_create_files(struct super_block *sb, +/* Adds mmapped_cntX directories and enabled, event, and key files to /dev/gator/events */ +static int gator_events_mmapped_create_files(struct super_block *sb, struct dentry *root) { int i; - for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { char buf[16]; struct dentry *dir; - snprintf(buf, sizeof(buf), "mmaped_cnt%d", i); + snprintf(buf, sizeof(buf), "mmapped_cnt%d", i); dir = gatorfs_mkdir(sb, root, buf); if (WARN_ON(!dir)) return -1; gatorfs_create_ulong(sb, dir, "enabled", - &mmaped_counters[i].enabled); + &mmapped_counters[i].enabled); gatorfs_create_ulong(sb, dir, "event", - &mmaped_counters[i].event); + &mmapped_counters[i].event); gatorfs_create_ro_ulong(sb, dir, "key", - &mmaped_counters[i].key); + &mmapped_counters[i].key); } return 0; } -static int gator_events_mmaped_start(void) +static int gator_events_mmapped_start(void) { -#ifdef TODO - for (i = 0; i < MMAPED_COUNTERS_NUM; i++) - writel(mmaped_counters[i].event, - mmaped_base + COUNTERS_CONFIG_OFFSET[i]); - - writel(ENABLED, COUNTERS_CONTROL_OFFSET); -#endif - -#ifndef TODO + int i; struct timespec ts; + getnstimeofday(&ts); prev_time = timespec_to_ns(&ts); -#endif + + mmapped_global_enabled = 0; + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_global_enabled = 1; + break; + } + } return 0; } -static void gator_events_mmaped_stop(void) +static void gator_events_mmapped_stop(void) { -#ifdef TODO - writel(DISABLED, COUNTERS_CONTROL_OFFSET); -#endif } -#ifndef TODO /* This function "simulates" counters, generating values of fancy * functions like sine or triangle... */ -static int mmaped_simulate(int counter, int delta_in_us) +static int mmapped_simulate(int counter, int delta_in_us) { int result = 0; @@ -157,73 +155,55 @@ static int mmaped_simulate(int counter, int delta_in_us) return result; } -#endif -static int gator_events_mmaped_read(int **buffer) +static int gator_events_mmapped_read(int **buffer) { int i; int len = 0; -#ifndef TODO int delta_in_us; struct timespec ts; s64 time; -#endif /* System wide counters - read from one core only */ - if (!on_primary_core()) + if (!on_primary_core() || !mmapped_global_enabled) return 0; -#ifndef TODO getnstimeofday(&ts); time = timespec_to_ns(&ts); delta_in_us = (int)(time - prev_time) / 1000; prev_time = time; -#endif - for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { - if (mmaped_counters[i].enabled) { - mmaped_buffer[len++] = mmaped_counters[i].key; -#ifdef TODO - mmaped_buffer[len++] = - readl(mmaped_base + COUNTERS_VALUE_OFFSET[i]); -#else - mmaped_buffer[len++] = - mmaped_simulate(mmaped_counters[i].event, + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + if (mmapped_counters[i].enabled) { + mmapped_buffer[len++] = mmapped_counters[i].key; + mmapped_buffer[len++] = + mmapped_simulate(mmapped_counters[i].event, delta_in_us); -#endif } } if (buffer) - *buffer = mmaped_buffer; + *buffer = mmapped_buffer; return len; } -static struct gator_interface gator_events_mmaped_interface = { - .create_files = gator_events_mmaped_create_files, - .start = gator_events_mmaped_start, - .stop = gator_events_mmaped_stop, - .read = gator_events_mmaped_read, +static struct gator_interface gator_events_mmapped_interface = { + .create_files = gator_events_mmapped_create_files, + .start = gator_events_mmapped_start, + .stop = gator_events_mmapped_stop, + .read = gator_events_mmapped_read, }; /* Must not be static! */ -int __init gator_events_mmaped_init(void) +int __init gator_events_mmapped_init(void) { int i; -#ifdef TODO - mmaped_base = ioremap(COUNTERS_PHYS_ADDR, SZ_4K); - if (!mmaped_base) - return -ENOMEM; -#endif - - for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { - mmaped_counters[i].enabled = 0; - mmaped_counters[i].key = gator_events_get_key(); + for (i = 0; i < MMAPPED_COUNTERS_NUM; i++) { + mmapped_counters[i].enabled = 0; + mmapped_counters[i].key = gator_events_get_key(); } - return gator_events_install(&gator_events_mmaped_interface); + return gator_events_install(&gator_events_mmapped_interface); } - -gator_events_init(gator_events_mmaped_init); diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c index 80cdee41ae3d..9c8d3a43eaeb 100644 --- a/drivers/gator/gator_events_net.c +++ b/drivers/gator/gator_events_net.c @@ -73,6 +73,8 @@ static void calculate_delta(int *rx, int *tx) static int gator_events_net_create_files(struct super_block *sb, struct dentry *root) { + // Network counters are not currently supported in RT-Preempt full because mod_timer is used +#ifndef CONFIG_PREEMPT_RT_FULL struct dentry *dir; dir = gatorfs_mkdir(sb, root, "Linux_net_rx"); @@ -88,6 +90,7 @@ static int gator_events_net_create_files(struct super_block *sb, struct dentry * } gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled); gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key); +#endif return 0; } @@ -167,5 +170,3 @@ int gator_events_net_init(void) return gator_events_install(&gator_events_net_interface); } - -gator_events_init(gator_events_net_init); diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c index 53b2d0a5afbf..d472df918ab0 100644 --- a/drivers/gator/gator_events_perf_pmu.c +++ b/drivers/gator/gator_events_perf_pmu.c @@ -6,13 +6,18 @@ * published by the Free Software Foundation. */ -#include -#include #include "gator.h" // gator_events_armvX.c is used for Linux 2.6.x #if GATOR_PERF_PMU_SUPPORT +#include +#ifdef CONFIG_OF +#include +#endif +#include +#include + extern bool event_based_sampling; // Maximum number of per-core counters - currently reserves enough space for two full hardware PMUs for big.LITTLE @@ -22,6 +27,9 @@ extern bool event_based_sampling; // + 1 for the cci-400 cycles counter #define UCCNT (CCI_400 + 1) +// Default to 0 if unable to probe the revision which was the previous behavior +#define DEFAULT_CCI_REVISION 0 + // A gator_attr is needed for every counter struct gator_attr { // Set once in gator_events_perf_pmu_*_init - the name of the event in the gatorfs @@ -404,17 +412,81 @@ static void __attr_init(struct gator_attr *const attr) attr->key = gator_events_get_key(); } +#ifdef CONFIG_OF + +static const struct of_device_id arm_cci_matches[] = { + {.compatible = "arm,cci-400" }, + {}, +}; + +static int probe_cci_revision(void) +{ + struct device_node *np; + struct resource res; + void __iomem *cci_ctrl_base; + int rev; + int ret = DEFAULT_CCI_REVISION; + + np = of_find_matching_node(NULL, arm_cci_matches); + if (!np) { + return ret; + } + + if (of_address_to_resource(np, 0, &res)) { + goto node_put; + } + + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + + rev = (readl_relaxed(cci_ctrl_base + 0xfe8) >> 4) & 0xf; + + if (rev <= 4) { + ret = 0; + } else if (rev <= 6) { + ret = 1; + } + + iounmap(cci_ctrl_base); + + node_put: + of_node_put(np); + + return ret; +} + +#else + +static int probe_cci_revision(void) +{ + return DEFAULT_CCI_REVISION; +} + +#endif + static void gator_events_perf_pmu_cci_init(const int type) { int cnt; + const char *cci_name; - strncpy(uc_attrs[uc_attr_count].name, "cci-400_ccnt", sizeof(uc_attrs[uc_attr_count].name)); + switch (probe_cci_revision()) { + case 0: + cci_name = "cci-400"; + break; + case 1: + cci_name = "cci-400-r1"; + break; + default: + pr_debug("gator: unrecognized cci-400 revision\n"); + return; + } + + snprintf(uc_attrs[uc_attr_count].name, sizeof(uc_attrs[uc_attr_count].name), "%s_ccnt", cci_name); uc_attrs[uc_attr_count].type = type; ++uc_attr_count; for (cnt = 0; cnt < CCI_400; ++cnt, ++uc_attr_count) { struct gator_attr *const attr = &uc_attrs[uc_attr_count]; - snprintf(attr->name, sizeof(attr->name), "cci-400_cnt%d", cnt); + snprintf(attr->name, sizeof(attr->name), "%s_cnt%d", cci_name, cnt); attr->type = type; } } @@ -477,7 +549,7 @@ int gator_events_perf_pmu_init(void) } if (pe->pmu != NULL && type == pe->pmu->type) { - if (strcmp("CCI", pe->pmu->name) == 0) { + if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0) { gator_events_perf_pmu_cci_init(type); } else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) { found_cpu = true; @@ -512,5 +584,4 @@ int gator_events_perf_pmu_init(void) return gator_events_install(&gator_events_perf_pmu_interface); } -gator_events_init(gator_events_perf_pmu_init); #endif diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c index 461a0511143d..29f4e39e261c 100644 --- a/drivers/gator/gator_events_sched.c +++ b/drivers/gator/gator_events_sched.c @@ -111,5 +111,3 @@ int gator_events_sched_init(void) return gator_events_install(&gator_events_sched_interface); } - -gator_events_init(gator_events_sched_init); diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c index aaf306a4b4c8..c91db1219d08 100644 --- a/drivers/gator/gator_events_scorpion.c +++ b/drivers/gator/gator_events_scorpion.c @@ -666,11 +666,4 @@ int gator_events_scorpion_init(void) return gator_events_install(&gator_events_scorpion_interface); } -gator_events_init(gator_events_scorpion_init); - -#else -int gator_events_scorpion_init(void) -{ - return -1; -} #endif diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index 7dd70d9eccf9..9773ae24d6f2 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -8,7 +8,8 @@ */ // This version must match the gator daemon version -static unsigned long gator_protocol_version = 16; +#define PROTOCOL_VERSION 17 +static unsigned long gator_protocol_version = PROTOCOL_VERSION; #include #include @@ -22,16 +23,20 @@ static unsigned long gator_protocol_version = 16; #include #include #include +#include #include #include #include "gator.h" -#include "gator_events.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) #error kernels prior to 2.6.32 are not supported #endif +#if defined(MODULE) && !defined(CONFIG_MODULES) +#error Cannot build a module against a kernel that does not support modules. To resolve, either rebuild the kernel to support modules or build gator as part of the kernel. +#endif + #if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING) #error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined #endif @@ -44,7 +49,7 @@ static unsigned long gator_protocol_version = 16; #error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling #endif -#if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) #error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems #endif @@ -87,6 +92,7 @@ static unsigned long gator_protocol_version = 16; #define MESSAGE_COOKIE 1 #define MESSAGE_THREAD_NAME 2 #define HRTIMER_CORE_NAME 3 +#define MESSAGE_LINK 4 #define MESSAGE_GPU_START 1 #define MESSAGE_GPU_STOP 2 @@ -136,6 +142,7 @@ static u64 gator_live_rate; static unsigned long gator_started; static u64 gator_monotonic_started; +static u64 gator_hibernate_time; static unsigned long gator_buffer_opened; static unsigned long gator_timer_count; static unsigned long gator_response_type; @@ -147,6 +154,8 @@ bool event_based_sampling; static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); static struct timer_list gator_buffer_wake_up_timer; +static bool gator_buffer_wake_stop; +static struct task_struct *gator_buffer_wake_thread; static LIST_HEAD(gator_events); static DEFINE_PER_CPU(u64, last_timestamp); @@ -189,6 +198,34 @@ static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); // The time after which the buffer should be committed for live display static DEFINE_PER_CPU(u64, gator_buffer_commit_time); +// List of all gator events - new events must be added to this list +#define GATOR_EVENTS_LIST \ + GATOR_EVENT(gator_events_armv6_init) \ + GATOR_EVENT(gator_events_armv7_init) \ + GATOR_EVENT(gator_events_block_init) \ + GATOR_EVENT(gator_events_ccn504_init) \ + GATOR_EVENT(gator_events_irq_init) \ + GATOR_EVENT(gator_events_l2c310_init) \ + GATOR_EVENT(gator_events_mali_init) \ + GATOR_EVENT(gator_events_mali_t6xx_hw_init) \ + GATOR_EVENT(gator_events_mali_t6xx_init) \ + GATOR_EVENT(gator_events_meminfo_init) \ + GATOR_EVENT(gator_events_mmapped_init) \ + GATOR_EVENT(gator_events_net_init) \ + GATOR_EVENT(gator_events_perf_pmu_init) \ + GATOR_EVENT(gator_events_sched_init) \ + GATOR_EVENT(gator_events_scorpion_init) \ + +#define GATOR_EVENT(EVENT_INIT) __weak int EVENT_INIT(void); +GATOR_EVENTS_LIST +#undef GATOR_EVENT + +static int (*gator_events_list[])(void) = { +#define GATOR_EVENT(EVENT_INIT) EVENT_INIT, +GATOR_EVENTS_LIST +#undef GATOR_EVENT +}; + /****************************************************************************** * Application Includes ******************************************************************************/ @@ -392,6 +429,21 @@ static void gator_buffer_wake_up(unsigned long data) wake_up(&gator_buffer_wait); } +static int gator_buffer_wake_func(void *data) +{ + while (!gator_buffer_wake_stop) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (gator_buffer_wake_stop) { + break; + } + + gator_buffer_wake_up(0); + } + + return 0; +} + /****************************************************************************** * Commit interface ******************************************************************************/ @@ -517,7 +569,14 @@ static void gator_commit_buffer(int cpu, int buftype, u64 time) marshal_frame(cpu, buftype); // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater - mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); + if (per_cpu(in_scheduler_context, cpu)) { +#ifndef CONFIG_PREEMPT_RT_FULL + // mod_timer can not be used in interrupt context in RT-Preempt full + mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); +#endif + } else { + wake_up_process(gator_buffer_wake_thread); + } } static void buffer_check(int cpu, int buftype, u64 time) @@ -590,8 +649,13 @@ void gator_backtrace_handler(struct pt_regs *const regs) // Collect counters if (!per_cpu(collecting, cpu)) { - collect_counters(time); + collect_counters(time, NULL); } + + // No buffer flushing occurs during sched switch for RT-Preempt full. The block counter frame will be flushed by collect_counters, but the sched buffer needs to be explicitly flushed +#ifdef CONFIG_PREEMPT_RT_FULL + buffer_check(cpu, SCHED_TRACE_BUF, time); +#endif } static int gator_running; @@ -815,6 +879,7 @@ static struct notifier_block __refdata gator_hotcpu_notifier = { static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { int cpu; + struct timespec ts; switch (event) { case PM_HIBERNATION_PREPARE: @@ -825,9 +890,20 @@ static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void for_each_online_cpu(cpu) { gator_timer_offline_dispatch(lcpu_to_pcpu(cpu), false); } + + // Record the wallclock hibernate time + getnstimeofday(&ts); + gator_hibernate_time = timespec_to_ns(&ts) - gator_get_time(); break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: + // Adjust gator_monotonic_started for the time spent sleeping, as gator_get_time does not account for it + if (gator_hibernate_time > 0) { + getnstimeofday(&ts); + gator_monotonic_started += gator_hibernate_time + gator_get_time() - timespec_to_ns(&ts); + gator_hibernate_time = 0; + } + for_each_online_cpu(cpu) { gator_timer_online_dispatch(lcpu_to_pcpu(cpu), false); } @@ -902,8 +978,10 @@ int gator_events_install(struct gator_interface *interface) int gator_events_get_key(void) { - // key of zero is reserved as a timestamp - static int key = 1; + // key 0 is reserved as a timestamp + // key 1 is reserved as the marker for thread specific counters + // Odd keys are assigned by the driver, even keys by the daemon + static int key = 3; const int ret = key; key += 2; @@ -916,7 +994,7 @@ static int gator_init(void) calc_first_cluster_size(); - // events sources (gator_events.h, generated by gator_events.sh) + // events sources for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) if (gator_events_list[i]) gator_events_list[i](); @@ -941,6 +1019,11 @@ static int gator_start(void) unsigned long cpu, i; struct gator_interface *gi; + gator_buffer_wake_stop = false; + if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) { + goto bwake_failure; + } + if (gator_migrate_start()) goto migrate_failure; @@ -1011,6 +1094,9 @@ cookies_failure: events_failure: gator_migrate_stop(); migrate_failure: + gator_buffer_wake_stop = true; + wake_up_process(gator_buffer_wake_thread); +bwake_failure: return -1; } @@ -1034,6 +1120,9 @@ static void gator_stop(void) gi->stop(); gator_migrate_stop(); + + gator_buffer_wake_stop = true; + wake_up_process(gator_buffer_wake_thread); } /****************************************************************************** @@ -1438,3 +1527,6 @@ module_exit(gator_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ARM Ltd"); MODULE_DESCRIPTION("Gator system profiler"); +#define STRIFY2(ARG) #ARG +#define STRIFY(ARG) STRIFY2(ARG) +MODULE_VERSION(STRIFY(PROTOCOL_VERSION)); diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c index 3282de843f71..af80ff62e712 100644 --- a/drivers/gator/gator_marshaling.c +++ b/drivers/gator/gator_marshaling.c @@ -89,6 +89,25 @@ static void marshal_thread_name(int pid, char *name) local_irq_restore(flags); } +static void marshal_link(int cookie, int tgid, int pid) +{ + unsigned long cpu = get_physical_cpu(), flags; + u64 time; + + local_irq_save(flags); + time = gator_get_time(); + if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, NAME_BUF, MESSAGE_LINK); + gator_buffer_write_packed_int64(cpu, NAME_BUF, time); + gator_buffer_write_packed_int(cpu, NAME_BUF, cookie); + gator_buffer_write_packed_int(cpu, NAME_BUF, tgid); + gator_buffer_write_packed_int(cpu, NAME_BUF, pid); + } + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, NAME_BUF, time); + local_irq_restore(flags); +} + static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel, u64 time) { int cpu = get_physical_cpu(); diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c index 12623c4036ee..be135b4aac56 100644 --- a/drivers/gator/gator_trace_gpu.c +++ b/drivers/gator/gator_trace_gpu.c @@ -85,7 +85,7 @@ static void mali_gpu_stop(int unit, int core) int count; int last_tgid = 0; int last_pid = 0; - int last_job_id = 0; + //int last_job_id = 0; spin_lock(&mali_gpu_jobs_lock); if (mali_gpu_jobs[unit][core].count == 0) { @@ -97,7 +97,7 @@ static void mali_gpu_stop(int unit, int core) if (count) { last_tgid = mali_gpu_jobs[unit][core].last_tgid; last_pid = mali_gpu_jobs[unit][core].last_pid; - last_job_id = mali_gpu_jobs[unit][core].last_job_id; + //last_job_id = mali_gpu_jobs[unit][core].last_job_id; } spin_unlock(&mali_gpu_jobs_lock); @@ -242,7 +242,7 @@ int gator_trace_gpu_start(void) * Absence of gpu trace points is not an error */ - memset(&mali_gpu_jobs, sizeof(mali_gpu_jobs), 0); + memset(&mali_gpu_jobs, 0, sizeof(mali_gpu_jobs)); gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c index e98815e25b9c..332b3f6ba965 100644 --- a/drivers/gator/gator_trace_sched.c +++ b/drivers/gator/gator_trace_sched.c @@ -22,6 +22,7 @@ enum { static DEFINE_PER_CPU(uint64_t *, taskname_keys); static DEFINE_PER_CPU(int, collecting); +static DEFINE_PER_CPU(bool, in_scheduler_context); // this array is never read as the cpu wait charts are derived counters // the files are needed, nonetheless, to show that these counters are available @@ -89,7 +90,7 @@ void emit_pid_name(struct task_struct *task) } } -static void collect_counters(u64 time) +static void collect_counters(u64 time, struct task_struct *task) { int *buffer, len, cpu = get_physical_cpu(); long long *buffer64; @@ -104,17 +105,26 @@ static void collect_counters(u64 time) len = gi->read64(&buffer64); marshal_event64(len, buffer64); } + if (gi->read_proc && task != NULL) { + len = gi->read_proc(&buffer64, task); + marshal_event64(len, buffer64); + } } // Only check after writing all counters so that time and corresponding counters appear in the same frame buffer_check(cpu, BLOCK_COUNTER_BUF, time); // Commit buffers on timeout if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { - static const int buftypes[] = { COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; + static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; + unsigned long flags; int i; + + local_irq_save(flags); for (i = 0; i < ARRAY_SIZE(buftypes); ++i) { gator_commit_buffer(cpu, buftypes[i], time); } + local_irq_restore(flags); + // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full if (on_primary_core() && spin_trylock(&annotate_lock)) { gator_commit_buffer(0, ANNOTATE_BUF, time); @@ -151,6 +161,8 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ int state; int cpu = get_physical_cpu(); + per_cpu(in_scheduler_context, cpu) = true; + // do as much work as possible before disabling interrupts cookie = get_exec_cookie(cpu, next); emit_pid_name(next); @@ -163,10 +175,12 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ } per_cpu(collecting, cpu) = 1; - collect_counters(gator_get_time()); + collect_counters(gator_get_time(), prev); per_cpu(collecting, cpu) = 0; marshal_sched_trace_switch(next->tgid, next->pid, cookie, state); + + per_cpu(in_scheduler_context, cpu) = false; } GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h index 3db454371d59..347a4fe404bc 100644 --- a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h +++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h @@ -1,11 +1,10 @@ -/* - * This confidential and proprietary software may be used only as - * authorised by a licensing agreement from ARM Limited - * (C) COPYRIGHT 2013 ARM Limited - * ALL RIGHTS RESERVED - * The entire notice above must be reproduced on all authorised - * copies and copies may only be made to the extent permitted - * by a licensing agreement from ARM Limited. +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * */ #ifndef __MALI_MJOLLNIR_PROFILING_GATOR_API_H__ diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h index c02a1a43efff..559647a76d29 100644 --- a/drivers/gator/mali/mali_utgard_profiling_gator_api.h +++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h @@ -1,11 +1,10 @@ -/* - * This confidential and proprietary software may be used only as - * authorised by a licensing agreement from ARM Limited - * (C) COPYRIGHT 2013 ARM Limited - * ALL RIGHTS RESERVED - * The entire notice above must be reproduced on all authorised - * copies and copies may only be made to the extent permitted - * by a licensing agreement from ARM Limited. +/** + * Copyright (C) ARM Limited 2013. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * */ #ifndef __MALI_UTGARD_PROFILING_GATOR_API_H__ diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk index 2cc64113ea2c..1a98c1c6a73f 100644 --- a/drivers/gator/mali_t6xx.mk +++ b/drivers/gator/mali_t6xx.mk @@ -9,16 +9,17 @@ EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ -DMALI_BACKEND_KERNEL=1 \ -DMALI_NO_MALI=0 -KBASE_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase -OSK_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase/osk -UMP_DIR = $(DDK_DIR)/kernel/include/linux +DDK_DIR ?= . +KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase +OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk +UMP_DIR = $(DDK_DIR)/include/linux # Include directories in the DDK -EXTRA_CFLAGS += -I$(DDK_DIR) \ +EXTRA_CFLAGS += -I$(KBASE_DIR)/ \ -I$(KBASE_DIR)/.. \ -I$(OSK_DIR)/.. \ -I$(UMP_DIR)/.. \ - -I$(DDK_DIR)/kernel/include \ + -I$(DDK_DIR)/include \ -I$(KBASE_DIR)/osk/src/linux/include \ -I$(KBASE_DIR)/platform_dummy \ -I$(KBASE_DIR)/src diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp index c7abbf3a1820..090a71553277 100644 --- a/tools/gator/daemon/Buffer.cpp +++ b/tools/gator/daemon/Buffer.cpp @@ -193,6 +193,17 @@ bool Buffer::eventHeader (const uint64_t curr_time) { return retval; } +bool Buffer::eventTid (const int tid) { + bool retval = false; + if (checkSpace(2*MAXSIZE_PACK32)) { + packInt(1); // key of 1 indicates a tid + packInt(tid); + retval = true; + } + + return retval; +} + void Buffer::event (const int32_t key, const int32_t value) { if (checkSpace(2 * MAXSIZE_PACK32)) { packInt(key); diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h index f820cfd851e3..b3c8d78cf758 100644 --- a/tools/gator/daemon/Buffer.h +++ b/tools/gator/daemon/Buffer.h @@ -32,6 +32,7 @@ public: void frame (); bool eventHeader (uint64_t curr_time); + bool eventTid (int tid); void event (int32_t key, int32_t value); void event64 (int64_t key, int64_t value); @@ -56,6 +57,10 @@ private: char *const buf; uint64_t commitTime; sem_t *const readerSem; + + // Intentionally unimplemented + Buffer(const Buffer &); + Buffer &operator=(const Buffer &); }; #endif // BUFFER_H diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp index c0540762698f..9ee2ef8afb9d 100644 --- a/tools/gator/daemon/Child.cpp +++ b/tools/gator/daemon/Child.cpp @@ -86,7 +86,7 @@ static void child_handler(int signum) { } } -static void* durationThread(void* pVoid) { +static void *durationThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0); sem_wait(&startProfile); if (gSessionData->mSessionIsActive) { @@ -102,7 +102,7 @@ static void* durationThread(void* pVoid) { return 0; } -static void* stopThread(void* pVoid) { +static void *stopThread(void *) { OlySocket* socket = child->socket; prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0); @@ -139,7 +139,7 @@ static void* stopThread(void* pVoid) { return 0; } -void* countersThread(void* pVoid) { +static void *countersThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); gSessionData->hwmon.start(); @@ -192,7 +192,7 @@ void* countersThread(void* pVoid) { return NULL; } -static void* senderThread(void* pVoid) { +static void *senderThread(void *) { int length = 1; char* data; char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; @@ -340,7 +340,8 @@ void Child::run() { thread_creation_success = false; } - if (gSessionData->hwmon.countersEnabled()) { + bool startcountersThread = gSessionData->hwmon.countersEnabled(); + if (startcountersThread) { if (pthread_create(&countersThreadID, NULL, countersThread, this)) { thread_creation_success = false; } @@ -378,7 +379,7 @@ void Child::run() { } while (bytesCollected > 0); logg->logMessage("Exit collect data loop"); - if (gSessionData->hwmon.countersEnabled()) { + if (startcountersThread) { pthread_join(countersThreadID, NULL); } diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h index e39d18276407..0330e9d78027 100644 --- a/tools/gator/daemon/Child.h +++ b/tools/gator/daemon/Child.h @@ -26,6 +26,10 @@ private: int mNumConnections; void initialization(); + + // Intentionally unimplemented + Child(const Child &); + Child &operator=(const Child &); }; #endif //__CHILD_H__ diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h index eba7dc4bac46..5650f487b990 100644 --- a/tools/gator/daemon/ConfigurationXML.h +++ b/tools/gator/daemon/ConfigurationXML.h @@ -29,6 +29,10 @@ private: int parse(const char* xmlFile); int configurationsTag(mxml_node_t *node); void configurationTag(mxml_node_t *node); + + // Intentionally unimplemented + ConfigurationXML(const ConfigurationXML &); + ConfigurationXML &operator=(const ConfigurationXML &); }; #endif // COUNTERS_H diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h index dd1dc27d1cdb..f3a932f852cb 100644 --- a/tools/gator/daemon/Driver.h +++ b/tools/gator/daemon/Driver.h @@ -29,7 +29,7 @@ public: // Emits available counters virtual void writeCounters(mxml_node_t *root) const = 0; // Emits possible dynamically generated events/counters - virtual void writeEvents(mxml_node_t *root) const {} + virtual void writeEvents(mxml_node_t *) const {} Driver *getNext() const { return next; } @@ -39,6 +39,10 @@ protected: private: static Driver *head; Driver *next; + + // Intentionally unimplemented + Driver(const Driver &); + Driver &operator=(const Driver &); }; #endif // DRIVER_H diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h index ada42b9fb584..d25cd6882561 100644 --- a/tools/gator/daemon/Fifo.h +++ b/tools/gator/daemon/Fifo.h @@ -39,6 +39,10 @@ private: sem_t* mReaderSem; char* mBuffer; bool mEnd; + + // Intentionally unimplemented + Fifo(const Fifo &); + Fifo &operator=(const Fifo &); }; #endif //__FIFO_H__ diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp index 07925680c1f6..1d7c0da9cc83 100644 --- a/tools/gator/daemon/Hwmon.cpp +++ b/tools/gator/daemon/Hwmon.cpp @@ -63,6 +63,10 @@ private: double previous_value; sensors_subfeature_type input; + + // Intentionally unimplemented + HwmonCounter(const HwmonCounter &); + HwmonCounter &operator=(const HwmonCounter &); }; HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h index 35981dc3d9ad..46bb42e898d7 100644 --- a/tools/gator/daemon/Hwmon.h +++ b/tools/gator/daemon/Hwmon.h @@ -34,6 +34,10 @@ private: HwmonCounter *findCounter(const Counter &counter) const; HwmonCounter *counters; + + // Intentionally unimplemented + Hwmon(const Hwmon &); + Hwmon &operator=(const Hwmon &); }; #endif // HWMON_H diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp index 132510df584a..ab5c3c2c8938 100644 --- a/tools/gator/daemon/OlySocket.cpp +++ b/tools/gator/daemon/OlySocket.cpp @@ -11,6 +11,7 @@ #include #ifdef WIN32 #include +#include #else #include #include @@ -126,11 +127,17 @@ void OlySocket::createSingleServerConnection(int port) { } void OlySocket::createServerSocket(int port) { + int family = AF_INET6; + // Create socket - mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + mFDServer = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); if (mFDServer < 0) { - logg->logError(__FILE__, __LINE__, "Error creating server socket"); - handleException(); + family = AF_INET; + mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } } // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits @@ -141,11 +148,11 @@ void OlySocket::createServerSocket(int port) { } // Create sockaddr_in structure, ensuring non-populated fields are zero - struct sockaddr_in sockaddr; - memset((void*)&sockaddr, 0, sizeof(struct sockaddr_in)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(port); - sockaddr.sin_addr.s_addr = INADDR_ANY; + struct sockaddr_in6 sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin6_family = family; + sockaddr.sin6_port = htons(port); + sockaddr.sin6_addr = in6addr_any; // Bind the socket to an address if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h index 8f23361a5def..b388f039bad7 100644 --- a/tools/gator/daemon/Sender.h +++ b/tools/gator/daemon/Sender.h @@ -33,6 +33,10 @@ private: FILE* mDataFile; char* mDataFileName; pthread_mutex_t mSendMutex; + + // Intentionally unimplemented + Sender(const Sender &); + Sender &operator=(const Sender &); }; #endif //__SENDER_H__ diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp index 4068d4e957f0..cf844075401f 100644 --- a/tools/gator/daemon/SessionData.cpp +++ b/tools/gator/daemon/SessionData.cpp @@ -44,13 +44,13 @@ void SessionData::parseSessionXML(char* xmlString) { SessionXML session(xmlString); session.parse(); - // Set session data values + // Set session data values - use prime numbers just below the desired value to reduce the chance of events firing at the same time if (strcmp(session.parameters.sample_rate, "high") == 0) { - mSampleRate = 10000; + mSampleRate = 9973; // 10000 } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { - mSampleRate = 1000; + mSampleRate = 997; // 1000 } else if (strcmp(session.parameters.sample_rate, "low") == 0) { - mSampleRate = 100; + mSampleRate = 97; // 100 } else if (strcmp(session.parameters.sample_rate, "none") == 0) { mSampleRate = 0; } else { @@ -139,7 +139,9 @@ void SessionData::readCpuInfo() { } int getEventKey() { - // Start one after the gator.ko's value of 1 + // key 0 is reserved as a timestamp + // key 1 is reserved as the marker for thread specific counters + // Odd keys are assigned by the driver, even keys by the daemon static int key = 2; const int ret = key; diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h index e72fa5d7c5ed..c834251527cf 100644 --- a/tools/gator/daemon/SessionData.h +++ b/tools/gator/daemon/SessionData.h @@ -16,7 +16,7 @@ #define MAX_PERFORMANCE_COUNTERS 50 -#define PROTOCOL_VERSION 16 +#define PROTOCOL_VERSION 17 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { @@ -62,6 +62,10 @@ public: private: void readCpuInfo(); + + // Intentionally unimplemented + SessionData(const SessionData &); + SessionData &operator=(const SessionData &); }; extern SessionData* gSessionData; diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h index c7e3798d6950..0fb03bd6627c 100644 --- a/tools/gator/daemon/SessionXML.h +++ b/tools/gator/daemon/SessionXML.h @@ -33,6 +33,10 @@ private: char* mPath; void sessionTag(mxml_node_t *tree, mxml_node_t *node); void sessionImage(mxml_node_t *node); + + // Intentionally unimplemented + SessionXML(const SessionXML &); + SessionXML &operator=(const SessionXML &); }; #endif // SESSION_XML_H diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h index 092d956ec99f..d6d9a6ea2991 100644 --- a/tools/gator/daemon/StreamlineSetup.h +++ b/tools/gator/daemon/StreamlineSetup.h @@ -38,6 +38,10 @@ private: void sendDefaults(); void sendCounters(); void writeConfiguration(char* xml); + + // Intentionally unimplemented + StreamlineSetup(const StreamlineSetup &); + StreamlineSetup &operator=(const StreamlineSetup &); }; #endif //__STREAMLINE_SETUP_H__ diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk index ee2415b8825c..031d16906881 100644 --- a/tools/gator/daemon/common.mk +++ b/tools/gator/daemon/common.mk @@ -6,7 +6,7 @@ # -std=c++0x is the planned new c++ standard # -std=c++98 is the 1998 c++ standard CFLAGS += -O3 -Wall -fno-exceptions -pthread -MMD -DETCDIR=\"/etc\" -Ilibsensors -CXXFLAGS += -fno-rtti +CXXFLAGS += -fno-rtti -Wextra # -Weffc++ ifeq ($(WERROR),1) CFLAGS += -Werror endif diff --git a/tools/gator/daemon/events-CCI-400.xml b/tools/gator/daemon/events-CCI-400.xml index 86db2087e1f5..4fa77117d2d8 100644 --- a/tools/gator/daemon/events-CCI-400.xml +++ b/tools/gator/daemon/events-CCI-400.xml @@ -17,7 +17,7 @@ - + @@ -45,3 +45,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml index 4a30ad6ec4e6..31a90a1d6335 100644 --- a/tools/gator/daemon/events-Linux.xml +++ b/tools/gator/daemon/events-Linux.xml @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml index 647e3d5b0fcf..2465238a8bda 100644 --- a/tools/gator/daemon/events-Mali-T6xx.xml +++ b/tools/gator/daemon/events-Mali-T6xx.xml @@ -36,3 +36,13 @@ + + + + + + + diff --git a/tools/gator/daemon/events-Mali-T6xx_hw.xml b/tools/gator/daemon/events-Mali-T6xx_hw.xml index 8cfe7c3084d5..03566cbb06ab 100644 --- a/tools/gator/daemon/events-Mali-T6xx_hw.xml +++ b/tools/gator/daemon/events-Mali-T6xx_hw.xml @@ -60,12 +60,15 @@ - + + + + diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp index d1b0913aa78f..bfd36b98766c 100644 --- a/tools/gator/daemon/main.cpp +++ b/tools/gator/daemon/main.cpp @@ -93,7 +93,7 @@ static void handler(int signum) { } // Child exit Signal Handler -static void child_exit(int signum) { +static void child_exit(int) { int status; int pid = wait(&status); if (pid != -1) { @@ -106,13 +106,18 @@ static void child_exit(int signum) { static int udpPort(int port) { int s; - struct sockaddr_in sockaddr; + struct sockaddr_in6 sockaddr; int on; + int family = AF_INET6; - s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (s == -1) { - logg->logError(__FILE__, __LINE__, "socket failed"); - handleException(); + family = AF_INET; + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s == -1) { + logg->logError(__FILE__, __LINE__, "socket failed"); + handleException(); + } } on = 1; @@ -122,9 +127,9 @@ static int udpPort(int port) { } memset((void*)&sockaddr, 0, sizeof(sockaddr)); - sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(port); - sockaddr.sin_addr.s_addr = INADDR_ANY; + sockaddr.sin6_family = family; + sockaddr.sin6_port = htons(port); + sockaddr.sin6_addr = in6addr_any; if (bind(s, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) { logg->logError(__FILE__, __LINE__, "socket failed"); handleException(); @@ -173,7 +178,7 @@ static void* answerThread(void* pVoid) { for (;;) { char buf[128]; - struct sockaddr_in sockaddr; + struct sockaddr_in6 sockaddr; socklen_t addrlen; int read; addrlen = sizeof(sockaddr); @@ -386,7 +391,7 @@ static struct cmdline_t parseCommandLine(int argc, char** argv) { } // Gator data flow: collector -> collector fifo -> sender -int main(int argc, char** argv, char* envp[]) { +int main(int argc, char** argv) { // Ensure proper signal handling by making gatord the process group leader // e.g. it may not be the group leader when launched as 'sudo gatord' setsid(); From a4e47900af0ce31d8c2807df94b2e182add8c0c4 Mon Sep 17 00:00:00 2001 From: Drew Richardson Date: Tue, 17 Dec 2013 23:40:56 +0000 Subject: [PATCH 2/7] gator-driver: Handle task struct correctly Use put_task_struct/get_task_struct to ensure the task_struct pointer is still valid. Change translate_buffer to ensure that both arguments are written atomically. Drop additional requests if the translate_buffer is full. Signed-off-by: Drew Richardson Signed-off-by: Pawel Moll --- drivers/gator/gator_cookies.c | 74 +++++++++++++++++++++++------------ drivers/gator/gator_iks.c | 2 + 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c index 91adfdde9be2..eb9b946170c1 100644 --- a/drivers/gator/gator_cookies.c +++ b/drivers/gator/gator_cookies.c @@ -15,13 +15,18 @@ static uint32_t *gator_crc32_table; static unsigned int translate_buffer_mask; +struct cookie_args { + struct task_struct *task; + const char *text; +}; + static DEFINE_PER_CPU(char *, translate_text); static DEFINE_PER_CPU(uint32_t, cookie_next_key); static DEFINE_PER_CPU(uint64_t *, cookie_keys); static DEFINE_PER_CPU(uint32_t *, cookie_values); static DEFINE_PER_CPU(int, translate_buffer_read); static DEFINE_PER_CPU(int, translate_buffer_write); -static DEFINE_PER_CPU(void **, translate_buffer); +static DEFINE_PER_CPU(struct cookie_args *, translate_buffer); static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); static void wq_cookie_handler(struct work_struct *unused); @@ -109,36 +114,58 @@ static void cookiemap_add(uint64_t key, uint32_t value) } #ifndef CONFIG_PREEMPT_RT_FULL -static void translate_buffer_write_ptr(int cpu, void *x) +static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text) { - per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; - per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; + unsigned long flags; + int write; + int next_write; + struct cookie_args *args; + + local_irq_save(flags); + + write = per_cpu(translate_buffer_write, cpu); + next_write = (write + 1) & translate_buffer_mask; + + // At least one entry must always remain available as when read == write, the queue is empty not full + if (next_write != per_cpu(translate_buffer_read, cpu)) { + args = &per_cpu(translate_buffer, cpu)[write]; + args->task = task; + args->text = text; + get_task_struct(task); + per_cpu(translate_buffer_write, cpu) = next_write; + } + + local_irq_restore(flags); } #endif -static void *translate_buffer_read_ptr(int cpu) +static void translate_buffer_read_args(int cpu, struct cookie_args *args) { - void *value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++]; - per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask; - return value; + unsigned long flags; + int read; + + local_irq_save(flags); + + read = per_cpu(translate_buffer_read, cpu); + *args = per_cpu(translate_buffer, cpu)[read]; + per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask; + + local_irq_restore(flags); } static void wq_cookie_handler(struct work_struct *unused) { - struct task_struct *task; - char *text; + struct cookie_args args; int cpu = get_physical_cpu(), cookie; - unsigned int commit; mutex_lock(&start_mutex); if (gator_started != 0) { - commit = per_cpu(translate_buffer_write, cpu); - while (per_cpu(translate_buffer_read, cpu) != commit) { - task = (struct task_struct *)translate_buffer_read_ptr(cpu); - text = (char *)translate_buffer_read_ptr(cpu); - cookie = get_cookie(cpu, task, text, true); - marshal_link(cookie, task->tgid, task->pid); + while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) { + translate_buffer_read_args(cpu, &args); + cookie = get_cookie(cpu, args.task, args.text, true); + marshal_link(cookie, args.task->tgid, args.task->pid); + put_task_struct(args.task); } } @@ -169,15 +196,14 @@ static int translate_app_process(const char **text, int cpu, struct task_struct // inconsistent during a context switch between android/linux versions if (!from_wq) { // Check if already in buffer - int ptr = per_cpu(translate_buffer_read, cpu); - while (ptr != per_cpu(translate_buffer_write, cpu)) { - if (per_cpu(translate_buffer, cpu)[ptr] == (void *)task) + int pos = per_cpu(translate_buffer_read, cpu); + while (pos != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[pos].task == task) goto out; - ptr = (ptr + 2) & translate_buffer_mask; + pos = (pos + 1) & translate_buffer_mask; } - translate_buffer_write_ptr(cpu, (void *)task); - translate_buffer_write_ptr(cpu, (void *)*text); + translate_buffer_write_args(cpu, task, *text); // Not safe to call in RT-Preempt full in schedule switch context mod_timer(&app_process_wake_up_timer, jiffies + 1); @@ -340,7 +366,7 @@ static int cookies_initialize(void) } memset(per_cpu(cookie_values, cpu), 0, size); - per_cpu(translate_buffer, cpu) = (void **)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); + per_cpu(translate_buffer, cpu) = (struct cookie_args *)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); if (!per_cpu(translate_buffer, cpu)) { err = -ENOMEM; goto cookie_setup_error; diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c index 24233d775581..0a90bdd1904e 100644 --- a/drivers/gator/gator_iks.c +++ b/drivers/gator/gator_iks.c @@ -147,11 +147,13 @@ static void gator_send_iks_core_names(void) { int cpu; // Send the cpu names + preempt_disable(); for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { if (mpidr_cpus[cpu] != NULL) { gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]); } } + preempt_enable(); } static int gator_migrate_start(void) From 45030a56ba386e4ea0597b8f167f494b2d4ee259 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 18 May 2012 09:06:15 +0100 Subject: [PATCH 3/7] gator: Revert #error about lack of CONFIG_PERF_EVENTS Make this a #warning as in version 5.9. This lets Gator build (with much reduced functionality) on platforms which require CONFIG_PERF_EVENTS to be disabled for whatever reason. Signed-off-by: Jon Medhurst --- drivers/gator/gator_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index 9773ae24d6f2..19f51c7cd8ee 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -55,9 +55,9 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) #ifndef CONFIG_PERF_EVENTS -#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters #elif !defined CONFIG_HW_PERF_EVENTS -#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters #endif #endif From 29d5b5842b26f7923c206265a0fb81004813aa78 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 17:35:03 +0100 Subject: [PATCH 4/7] gator: Add config for building the module in-tree Signed-off-by: Jon Medhurst --- drivers/Kconfig | 2 ++ drivers/Makefile | 2 ++ drivers/gator/Kconfig | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 drivers/gator/Kconfig diff --git a/drivers/Kconfig b/drivers/Kconfig index 9953a42809ec..d27feb5460f3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -166,4 +166,6 @@ source "drivers/ipack/Kconfig" source "drivers/reset/Kconfig" +source "drivers/gator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 130abc1dfd65..092a62e79688 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -152,3 +152,5 @@ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ + +obj-$(CONFIG_GATOR) += gator/ diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig new file mode 100644 index 000000000000..7ea0fcc3d01d --- /dev/null +++ b/drivers/gator/Kconfig @@ -0,0 +1,33 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m if (ARM || ARM64) + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) + select TRACING + +config GATOR_WITH_MALI_SUPPORT + bool + +choice + prompt "Enable Mali GPU support in Gator" + depends on GATOR + optional + +config GATOR_MALI_400MP + bool "Mali-400MP" + select GATOR_WITH_MALI_SUPPORT + +config GATOR_MALI_T6XX + bool "Mali-T604 or Mali-T658" + select GATOR_WITH_MALI_SUPPORT + +endchoice + +config GATOR_MALI_PATH + string "Path to Mali driver" + depends on GATOR_WITH_MALI_SUPPORT + default "drivers/gpu/arm/mali400mp" + help + The gator code adds this to its include path so it can get the Mali + trace headers with: #include "linux/mali_linux_trace.h" From 15ce78dafc08b1c5c3ec8f42070ae37160b5154c Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 Apr 2014 09:02:02 +0100 Subject: [PATCH 5/7] gator: Version 5.18 Signed-off-by: Jon Medhurst --- drivers/gator/Kconfig | 39 + drivers/gator/gator.h | 13 +- drivers/gator/gator_annotate.c | 2 +- drivers/gator/gator_annotate_kernel.c | 8 +- drivers/gator/gator_backtrace.c | 48 +- drivers/gator/gator_buffer.c | 168 ++++ .../{gator_pack.c => gator_buffer_write.c} | 24 +- drivers/gator/gator_cookies.c | 82 +- drivers/gator/gator_events_armv6.c | 2 +- drivers/gator/gator_events_armv7.c | 18 +- drivers/gator/gator_events_block.c | 2 +- drivers/gator/gator_events_ccn-504.c | 2 +- drivers/gator/gator_events_irq.c | 2 +- drivers/gator/gator_events_l2c-310.c | 2 +- drivers/gator/gator_events_mali_4xx.c | 2 +- drivers/gator/gator_events_mali_4xx.h | 2 +- drivers/gator/gator_events_mali_common.c | 2 +- drivers/gator/gator_events_mali_common.h | 2 +- drivers/gator/gator_events_mali_t6xx.c | 8 +- drivers/gator/gator_events_mali_t6xx_hw.c | 10 +- .../gator/gator_events_mali_t6xx_hw_test.c | 2 +- drivers/gator/gator_events_meminfo.c | 28 +- drivers/gator/gator_events_mmapped.c | 2 +- drivers/gator/gator_events_net.c | 2 +- drivers/gator/gator_events_perf_pmu.c | 2 +- drivers/gator/gator_events_sched.c | 2 +- drivers/gator/gator_events_scorpion.c | 2 +- drivers/gator/gator_fs.c | 47 +- drivers/gator/gator_hrtimer_gator.c | 8 +- drivers/gator/gator_hrtimer_perf.c | 113 --- drivers/gator/gator_iks.c | 4 +- drivers/gator/gator_main.c | 286 ++----- drivers/gator/gator_marshaling.c | 97 +-- drivers/gator/gator_trace_gpu.c | 13 +- drivers/gator/gator_trace_gpu.h | 2 +- drivers/gator/gator_trace_power.c | 6 +- drivers/gator/gator_trace_sched.c | 20 +- .../mali/mali_mjollnir_profiling_gator_api.h | 2 +- .../mali/mali_utgard_profiling_gator_api.h | 2 +- drivers/gator/mali_t6xx.mk | 9 + tools/gator/daemon/Android.mk | 15 +- tools/gator/daemon/Buffer.cpp | 257 ++++-- tools/gator/daemon/Buffer.h | 95 ++- tools/gator/daemon/CapturedXML.cpp | 11 +- tools/gator/daemon/CapturedXML.h | 2 +- tools/gator/daemon/Child.cpp | 174 ++-- tools/gator/daemon/Child.h | 4 +- tools/gator/daemon/Collector.h | 38 - tools/gator/daemon/Config.h | 17 + tools/gator/daemon/ConfigurationXML.cpp | 16 +- tools/gator/daemon/ConfigurationXML.h | 2 +- tools/gator/daemon/Counter.h | 4 +- tools/gator/daemon/Driver.cpp | 2 +- tools/gator/daemon/Driver.h | 4 +- .../{Collector.cpp => DriverSource.cpp} | 237 ++++-- tools/gator/daemon/DriverSource.h | 52 ++ tools/gator/daemon/DynBuf.cpp | 139 +++ tools/gator/daemon/DynBuf.h | 52 ++ tools/gator/daemon/EventsXML.cpp | 4 +- tools/gator/daemon/EventsXML.h | 2 +- tools/gator/daemon/ExternalSource.cpp | 56 ++ tools/gator/daemon/ExternalSource.h | 40 + tools/gator/daemon/Fifo.cpp | 2 +- tools/gator/daemon/Fifo.h | 4 +- tools/gator/daemon/Hwmon.cpp | 40 +- tools/gator/daemon/Hwmon.h | 6 +- tools/gator/daemon/KMod.cpp | 25 +- tools/gator/daemon/KMod.h | 4 +- tools/gator/daemon/LocalCapture.cpp | 6 +- tools/gator/daemon/LocalCapture.h | 2 +- tools/gator/daemon/Logging.cpp | 6 +- tools/gator/daemon/Logging.h | 13 +- tools/gator/daemon/Monitor.cpp | 61 ++ tools/gator/daemon/Monitor.h | 32 + tools/gator/daemon/OlySocket.cpp | 101 ++- tools/gator/daemon/OlySocket.h | 43 +- tools/gator/daemon/OlyUtility.cpp | 2 +- tools/gator/daemon/OlyUtility.h | 2 +- tools/gator/daemon/PerfBuffer.cpp | 139 +++ tools/gator/daemon/PerfBuffer.h | 39 + tools/gator/daemon/PerfDriver.cpp | 355 ++++++++ tools/gator/daemon/PerfDriver.h | 56 ++ tools/gator/daemon/PerfGroup.cpp | 206 +++++ tools/gator/daemon/PerfGroup.h | 55 ++ tools/gator/daemon/PerfSource.cpp | 271 ++++++ tools/gator/daemon/PerfSource.h | 54 ++ tools/gator/daemon/Proc.cpp | 179 ++++ tools/gator/daemon/Proc.h | 17 + tools/gator/daemon/Sender.cpp | 31 +- tools/gator/daemon/Sender.h | 2 +- tools/gator/daemon/SessionData.cpp | 29 +- tools/gator/daemon/SessionData.h | 14 +- tools/gator/daemon/SessionXML.cpp | 12 +- tools/gator/daemon/SessionXML.h | 8 +- tools/gator/daemon/Source.cpp | 33 + tools/gator/daemon/Source.h | 40 + tools/gator/daemon/StreamlineSetup.cpp | 36 +- tools/gator/daemon/StreamlineSetup.h | 7 +- tools/gator/daemon/UEvent.cpp | 75 ++ tools/gator/daemon/UEvent.h | 36 + tools/gator/daemon/UserSpaceSource.cpp | 97 +++ tools/gator/daemon/UserSpaceSource.h | 38 + tools/gator/daemon/common.mk | 4 +- .../{configuration.xml => defaults.xml} | 51 +- tools/gator/daemon/escape.c | 2 +- tools/gator/daemon/events-Cortex-A12.xml | 6 +- tools/gator/daemon/events-Cortex-A15.xml | 6 +- tools/gator/daemon/events-Cortex-A5.xml | 6 +- tools/gator/daemon/events-Cortex-A7.xml | 6 +- tools/gator/daemon/events-Cortex-A8.xml | 6 +- tools/gator/daemon/events-Cortex-A9.xml | 6 +- tools/gator/daemon/events-Linux.xml | 14 +- tools/gator/daemon/events-Mali-4xx.xml | 2 +- tools/gator/daemon/events-Mali-T6xx.xml | 16 +- tools/gator/daemon/events-Perf-Hardware.xml | 12 + tools/gator/daemon/k/perf_event.3.12.h | 792 ++++++++++++++++++ tools/gator/daemon/k/perf_event.h | 1 + tools/gator/daemon/main.cpp | 86 +- 118 files changed, 4362 insertions(+), 1142 deletions(-) create mode 100644 drivers/gator/Kconfig create mode 100644 drivers/gator/gator_buffer.c rename drivers/gator/{gator_pack.c => gator_buffer_write.c} (68%) delete mode 100644 drivers/gator/gator_hrtimer_perf.c delete mode 100644 tools/gator/daemon/Collector.h create mode 100644 tools/gator/daemon/Config.h rename tools/gator/daemon/{Collector.cpp => DriverSource.cpp} (58%) create mode 100644 tools/gator/daemon/DriverSource.h create mode 100644 tools/gator/daemon/DynBuf.cpp create mode 100644 tools/gator/daemon/DynBuf.h create mode 100644 tools/gator/daemon/ExternalSource.cpp create mode 100644 tools/gator/daemon/ExternalSource.h create mode 100644 tools/gator/daemon/Monitor.cpp create mode 100644 tools/gator/daemon/Monitor.h create mode 100644 tools/gator/daemon/PerfBuffer.cpp create mode 100644 tools/gator/daemon/PerfBuffer.h create mode 100644 tools/gator/daemon/PerfDriver.cpp create mode 100644 tools/gator/daemon/PerfDriver.h create mode 100644 tools/gator/daemon/PerfGroup.cpp create mode 100644 tools/gator/daemon/PerfGroup.h create mode 100644 tools/gator/daemon/PerfSource.cpp create mode 100644 tools/gator/daemon/PerfSource.h create mode 100644 tools/gator/daemon/Proc.cpp create mode 100644 tools/gator/daemon/Proc.h create mode 100644 tools/gator/daemon/Source.cpp create mode 100644 tools/gator/daemon/Source.h create mode 100644 tools/gator/daemon/UEvent.cpp create mode 100644 tools/gator/daemon/UEvent.h create mode 100644 tools/gator/daemon/UserSpaceSource.cpp create mode 100644 tools/gator/daemon/UserSpaceSource.h rename tools/gator/daemon/{configuration.xml => defaults.xml} (51%) create mode 100644 tools/gator/daemon/events-Perf-Hardware.xml create mode 100644 tools/gator/daemon/k/perf_event.3.12.h create mode 120000 tools/gator/daemon/k/perf_event.h diff --git a/drivers/gator/Kconfig b/drivers/gator/Kconfig new file mode 100644 index 000000000000..e46ccb9b8064 --- /dev/null +++ b/drivers/gator/Kconfig @@ -0,0 +1,39 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m if (ARM || ARM64) + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) + depends on PERF_EVENTS + depends on HW_PERF_EVENTS || !(ARM || ARM64) + select TRACING + help + Gator module for ARM's Streamline Performance Analyzer + +config GATOR_WITH_MALI_SUPPORT + bool + +choice + prompt "Enable Mali GPU support in Gator" + depends on GATOR + optional + help + Enable Mali GPU support in Gator + +config GATOR_MALI_4XXMP + bool "Mali-400MP or Mali-450MP" + select GATOR_WITH_MALI_SUPPORT + +config GATOR_MALI_T6XX + bool "Mali-T604 or Mali-T658" + select GATOR_WITH_MALI_SUPPORT + +endchoice + +config GATOR_MALI_PATH + string "Path to Mali driver" + depends on GATOR_WITH_MALI_SUPPORT + default "drivers/gpu/arm/mali400mp" + help + The gator code adds this to its include path so it can get the Mali + trace headers with: #include "linux/mali_linux_trace.h" diff --git a/drivers/gator/gator.h b/drivers/gator/gator.h index d8981ed85a6a..586cd9e742fb 100644 --- a/drivers/gator/gator.h +++ b/drivers/gator/gator.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -31,6 +31,7 @@ #define CORTEX_A9 0xc09 #define CORTEX_A12 0xc0d #define CORTEX_A15 0xc0f +#define CORTEX_A17 0xc0e #define SCORPION 0x00f #define SCORPIONMP 0x02d #define KRAITSIM 0x049 @@ -47,9 +48,7 @@ struct gator_cpu { const int cpuid; // Human readable name const char core_name[MAXSIZE_CORE_NAME]; - // Perf PMU name - const char * const pmu_name; - // gatorfs event name + // gatorfs event and Perf PMU name const char * const pmnc_name; // compatible from Documentation/devicetree/bindings/arm/cpus.txt const char * const dt_name; @@ -62,10 +61,6 @@ const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name); /****************************************************************************** * Filesystem ******************************************************************************/ -int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, - char const *name, - const struct file_operations *fops, int perm); - struct dentry *gatorfs_mkdir(struct super_block *sb, struct dentry *root, char const *name); @@ -75,8 +70,6 @@ int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, char const *name, unsigned long *val); -void gator_op_create_files(struct super_block *sb, struct dentry *root); - /****************************************************************************** * Tracepoints ******************************************************************************/ diff --git a/drivers/gator/gator_annotate.c b/drivers/gator/gator_annotate.c index 5b9399bea230..7e2c6e5d8715 100644 --- a/drivers/gator/gator_annotate.c +++ b/drivers/gator/gator_annotate.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_annotate_kernel.c b/drivers/gator/gator_annotate_kernel.c index a406e4882974..010806825529 100644 --- a/drivers/gator/gator_annotate_kernel.c +++ b/drivers/gator/gator_annotate_kernel.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -29,12 +29,14 @@ static void kannotate_write(const char *ptr, unsigned int size) } } -static void marshal_u16(char *buf, u16 val) { +static void marshal_u16(char *buf, u16 val) +{ buf[0] = val & 0xff; buf[1] = (val >> 8) & 0xff; } -static void marshal_u32(char *buf, u32 val) { +static void marshal_u32(char *buf, u32 val) +{ buf[0] = val & 0xff; buf[1] = (val >> 8) & 0xff; buf[2] = (val >> 16) & 0xff; diff --git a/drivers/gator/gator_backtrace.c b/drivers/gator/gator_backtrace.c index ffacb490194c..9f305cf7242c 100644 --- a/drivers/gator/gator_backtrace.c +++ b/drivers/gator/gator_backtrace.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -30,6 +30,18 @@ struct stack_frame_eabi { }; }; +static void gator_add_trace(int cpu, unsigned long address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) { + offset = address; + } + + marshal_backtrace(offset & ~1, cookie, 0); +} + static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth) { #if defined(__arm__) || defined(__aarch64__) @@ -122,7 +134,7 @@ static int report_trace(struct stackframe *frame, void *d) addr = addr - (unsigned long)mod->module_core; } #endif - marshal_backtrace(addr & ~1, cookie); + marshal_backtrace(addr & ~1, cookie, 1); (*depth)--; } @@ -136,7 +148,7 @@ static int report_trace(struct stackframe *frame, void *d) #if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING) // Disabled by default MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding."); -bool kernel_stack_unwinding = 0; +static bool kernel_stack_unwinding = 0; module_param(kernel_stack_unwinding, bool, 0644); #endif @@ -163,6 +175,34 @@ static void kernel_backtrace(int cpu, struct pt_regs *const regs) #endif walk_stackframe(&frame, report_trace, &depth); #else - marshal_backtrace(PC_REG & ~1, NO_COOKIE); + marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1); #endif } + +static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time) +{ + bool in_kernel; + unsigned long exec_cookie; + + if (!regs) + return; + + in_kernel = !user_mode(regs); + exec_cookie = get_exec_cookie(cpu, current); + + if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, time)) + return; + + if (in_kernel) { + kernel_backtrace(cpu, regs); + } else { + // Cookie+PC + gator_add_trace(cpu, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, regs, gator_backtrace_depth); + } + + marshal_backtrace_footer(time); +} diff --git a/drivers/gator/gator_buffer.c b/drivers/gator/gator_buffer.c new file mode 100644 index 000000000000..eba22dfe3bf2 --- /dev/null +++ b/drivers/gator/gator_buffer.c @@ -0,0 +1,168 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static void marshal_frame(int cpu, int buftype) +{ + int frame; + + if (!per_cpu(gator_buffer, cpu)[buftype]) { + return; + } + + switch (buftype) { + case SUMMARY_BUF: + frame = FRAME_SUMMARY; + break; + case BACKTRACE_BUF: + frame = FRAME_BACKTRACE; + break; + case NAME_BUF: + frame = FRAME_NAME; + break; + case COUNTER_BUF: + frame = FRAME_COUNTER; + break; + case BLOCK_COUNTER_BUF: + frame = FRAME_BLOCK_COUNTER; + break; + case ANNOTATE_BUF: + frame = FRAME_ANNOTATE; + break; + case SCHED_TRACE_BUF: + frame = FRAME_SCHED_TRACE; + break; + case GPU_TRACE_BUF: + frame = FRAME_GPU_TRACE; + break; + case IDLE_BUF: + frame = FRAME_IDLE; + break; + default: + frame = -1; + break; + } + + // add response type + if (gator_response_type > 0) { + gator_buffer_write_packed_int(cpu, buftype, gator_response_type); + } + + // leave space for 4-byte unpacked length + per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype]; + + // add frame type and core number + gator_buffer_write_packed_int(cpu, buftype, frame); + gator_buffer_write_packed_int(cpu, buftype, cpu); +} + +static int buffer_bytes_available(int cpu, int buftype) +{ + int remaining, filled; + + filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + + remaining = gator_buffer_size[buftype] - filled; + + if (per_cpu(buffer_space_available, cpu)[buftype]) { + // Give some extra room; also allows space to insert the overflow error packet + remaining -= 200; + } else { + // Hysteresis, prevents multiple overflow messages + remaining -= 2000; + } + + return remaining; +} + +static bool buffer_check_space(int cpu, int buftype, int bytes) +{ + int remaining = buffer_bytes_available(cpu, buftype); + + if (remaining < bytes) { + per_cpu(buffer_space_available, cpu)[buftype] = false; + } else { + per_cpu(buffer_space_available, cpu)[buftype] = true; + } + + return per_cpu(buffer_space_available, cpu)[buftype]; +} + +static int contiguous_space_available(int cpu, int buftype) +{ + int remaining = buffer_bytes_available(cpu, buftype); + int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype]; + if (remaining < contiguous) + return remaining; + else + return contiguous; +} + +static void gator_commit_buffer(int cpu, int buftype, u64 time) +{ + int type_length, commit, length, byte; + unsigned long flags; + + if (!per_cpu(gator_buffer, cpu)[buftype]) + return; + + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + local_irq_save(flags); + type_length = gator_response_type ? 1 : 0; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + length = per_cpu(gator_buffer_write, cpu)[buftype] - commit; + if (length < 0) { + length += gator_buffer_size[buftype]; + } + length = length - type_length - sizeof(s32); + + if (length <= FRAME_HEADER_SIZE) { + // Nothing to write, only the frame header is present + local_irq_restore(flags); + return; + } + + for (byte = 0; byte < sizeof(s32); byte++) { + per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; + } + + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; + + if (gator_live_rate > 0) { + while (time > per_cpu(gator_buffer_commit_time, cpu)) { + per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate; + } + } + + marshal_frame(cpu, buftype); + local_irq_restore(flags); + + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + if (per_cpu(in_scheduler_context, cpu)) { +#ifndef CONFIG_PREEMPT_RT_FULL + // mod_timer can not be used in interrupt context in RT-Preempt full + mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); +#endif + } else { + up(&gator_buffer_wake_sem); + } +} + +static void buffer_check(int cpu, int buftype, u64 time) +{ + int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype]; + if (filled < 0) { + filled += gator_buffer_size[buftype]; + } + if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) { + gator_commit_buffer(cpu, buftype, time); + } +} diff --git a/drivers/gator/gator_pack.c b/drivers/gator/gator_buffer_write.c similarity index 68% rename from drivers/gator/gator_pack.c rename to drivers/gator/gator_buffer_write.c index 2c082f283adc..b621ba93ee5e 100644 --- a/drivers/gator/gator_pack.c +++ b/drivers/gator/gator_buffer_write.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -56,3 +56,25 @@ static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x) per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; } + +static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len) +{ + int i; + u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; + u32 mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + + for (i = 0; i < len; i++) { + buffer[write] = x[i]; + write = (write + 1) & mask; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = write; +} + +static void gator_buffer_write_string(int cpu, int buftype, const char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, buftype, len); + gator_buffer_write_bytes(cpu, buftype, x, len); +} diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c index 91adfdde9be2..5c7d842070e0 100644 --- a/drivers/gator/gator_cookies.c +++ b/drivers/gator/gator_cookies.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,17 +15,22 @@ static uint32_t *gator_crc32_table; static unsigned int translate_buffer_mask; +struct cookie_args { + struct task_struct *task; + const char *text; +}; + static DEFINE_PER_CPU(char *, translate_text); static DEFINE_PER_CPU(uint32_t, cookie_next_key); static DEFINE_PER_CPU(uint64_t *, cookie_keys); static DEFINE_PER_CPU(uint32_t *, cookie_values); static DEFINE_PER_CPU(int, translate_buffer_read); static DEFINE_PER_CPU(int, translate_buffer_write); -static DEFINE_PER_CPU(void **, translate_buffer); +static DEFINE_PER_CPU(struct cookie_args *, translate_buffer); static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq); static void wq_cookie_handler(struct work_struct *unused); -DECLARE_WORK(cookie_work, wq_cookie_handler); +static DECLARE_WORK(cookie_work, wq_cookie_handler); static struct timer_list app_process_wake_up_timer; static void app_process_wake_up_handler(unsigned long unused_data); @@ -109,36 +114,62 @@ static void cookiemap_add(uint64_t key, uint32_t value) } #ifndef CONFIG_PREEMPT_RT_FULL -static void translate_buffer_write_ptr(int cpu, void *x) +static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text) { - per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; - per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; + unsigned long flags; + int write; + int next_write; + struct cookie_args *args; + + local_irq_save(flags); + + write = per_cpu(translate_buffer_write, cpu); + next_write = (write + 1) & translate_buffer_mask; + + // At least one entry must always remain available as when read == write, the queue is empty not full + if (next_write != per_cpu(translate_buffer_read, cpu)) { + args = &per_cpu(translate_buffer, cpu)[write]; + args->task = task; + args->text = text; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + get_task_struct(task); +#endif + per_cpu(translate_buffer_write, cpu) = next_write; + } + + local_irq_restore(flags); } #endif -static void *translate_buffer_read_ptr(int cpu) +static void translate_buffer_read_args(int cpu, struct cookie_args *args) { - void *value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++]; - per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask; - return value; + unsigned long flags; + int read; + + local_irq_save(flags); + + read = per_cpu(translate_buffer_read, cpu); + *args = per_cpu(translate_buffer, cpu)[read]; + per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask; + + local_irq_restore(flags); } static void wq_cookie_handler(struct work_struct *unused) { - struct task_struct *task; - char *text; + struct cookie_args args; int cpu = get_physical_cpu(), cookie; - unsigned int commit; mutex_lock(&start_mutex); if (gator_started != 0) { - commit = per_cpu(translate_buffer_write, cpu); - while (per_cpu(translate_buffer_read, cpu) != commit) { - task = (struct task_struct *)translate_buffer_read_ptr(cpu); - text = (char *)translate_buffer_read_ptr(cpu); - cookie = get_cookie(cpu, task, text, true); - marshal_link(cookie, task->tgid, task->pid); + while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) { + translate_buffer_read_args(cpu, &args); + cookie = get_cookie(cpu, args.task, args.text, true); + marshal_link(cookie, args.task->tgid, args.task->pid); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + put_task_struct(args.task); +#endif } } @@ -169,15 +200,14 @@ static int translate_app_process(const char **text, int cpu, struct task_struct // inconsistent during a context switch between android/linux versions if (!from_wq) { // Check if already in buffer - int ptr = per_cpu(translate_buffer_read, cpu); - while (ptr != per_cpu(translate_buffer_write, cpu)) { - if (per_cpu(translate_buffer, cpu)[ptr] == (void *)task) + int pos = per_cpu(translate_buffer_read, cpu); + while (pos != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[pos].task == task) goto out; - ptr = (ptr + 2) & translate_buffer_mask; + pos = (pos + 1) & translate_buffer_mask; } - translate_buffer_write_ptr(cpu, (void *)task); - translate_buffer_write_ptr(cpu, (void *)*text); + translate_buffer_write_args(cpu, task, *text); // Not safe to call in RT-Preempt full in schedule switch context mod_timer(&app_process_wake_up_timer, jiffies + 1); @@ -340,7 +370,7 @@ static int cookies_initialize(void) } memset(per_cpu(cookie_values, cpu), 0, size); - per_cpu(translate_buffer, cpu) = (void **)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); + per_cpu(translate_buffer, cpu) = (struct cookie_args *)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL); if (!per_cpu(translate_buffer, cpu)) { err = -ENOMEM; goto cookie_setup_error; diff --git a/drivers/gator/gator_events_armv6.c b/drivers/gator/gator_events_armv6.c index dd7974090b82..353645622306 100644 --- a/drivers/gator/gator_events_armv6.c +++ b/drivers/gator/gator_events_armv6.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_armv7.c b/drivers/gator/gator_events_armv7.c index 30881c8fd3fd..153119b463e6 100644 --- a/drivers/gator/gator_events_armv7.c +++ b/drivers/gator/gator_events_armv7.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -141,9 +141,9 @@ static int gator_events_armv7_create_files(struct super_block *sb, struct dentry for (i = 0; i < pmnc_counters; i++) { char buf[40]; if (i == 0) { - snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); } else { - snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i - 1); + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1); } dir = gatorfs_mkdir(sb, root, buf); if (!dir) { @@ -275,25 +275,27 @@ int gator_events_armv7_init(void) switch (gator_cpuid()) { case CORTEX_A5: - pmnc_name = "Cortex-A5"; + pmnc_name = "ARMv7_Cortex_A5"; pmnc_counters = 2; break; case CORTEX_A7: - pmnc_name = "Cortex-A7"; + pmnc_name = "ARMv7_Cortex_A7"; pmnc_counters = 4; break; case CORTEX_A8: - pmnc_name = "Cortex-A8"; + pmnc_name = "ARMv7_Cortex_A8"; pmnc_counters = 4; break; case CORTEX_A9: - pmnc_name = "Cortex-A9"; + pmnc_name = "ARMv7_Cortex_A9"; pmnc_counters = 6; break; + // ARM Cortex A12 is not supported by version of Linux before 3.0 case CORTEX_A15: - pmnc_name = "Cortex-A15"; + pmnc_name = "ARMv7_Cortex_A15"; pmnc_counters = 6; break; + // ARM Cortex A17 is not supported by version of Linux before 3.0 default: return -1; } diff --git a/drivers/gator/gator_events_block.c b/drivers/gator/gator_events_block.c index 691ef2574536..b2bc414e462e 100644 --- a/drivers/gator/gator_events_block.c +++ b/drivers/gator/gator_events_block.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_ccn-504.c b/drivers/gator/gator_events_ccn-504.c index b89231967c75..024ffc2856aa 100644 --- a/drivers/gator/gator_events_ccn-504.c +++ b/drivers/gator/gator_events_ccn-504.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_irq.c b/drivers/gator/gator_events_irq.c index b11879a248f8..facbdd62325e 100644 --- a/drivers/gator/gator_events_irq.c +++ b/drivers/gator/gator_events_irq.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_l2c-310.c b/drivers/gator/gator_events_l2c-310.c index ee521af22517..553f9707bdbf 100644 --- a/drivers/gator/gator_events_l2c-310.c +++ b/drivers/gator/gator_events_l2c-310.c @@ -1,7 +1,7 @@ /** * l2c310 (L2 Cache Controller) event counters for gator * - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_mali_4xx.c b/drivers/gator/gator_events_mali_4xx.c index 6719c1ec73a2..85d47645a9d9 100644 --- a/drivers/gator/gator_events_mali_4xx.c +++ b/drivers/gator/gator_events_mali_4xx.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_mali_4xx.h b/drivers/gator/gator_events_mali_4xx.h index 413ad0ffe794..976ca8c4cfa1 100644 --- a/drivers/gator/gator_events_mali_4xx.h +++ b/drivers/gator/gator_events_mali_4xx.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_mali_common.c b/drivers/gator/gator_events_mali_common.c index 466ca1683c7e..dc58dcf0c662 100644 --- a/drivers/gator/gator_events_mali_common.c +++ b/drivers/gator/gator_events_mali_common.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_mali_common.h b/drivers/gator/gator_events_mali_common.h index 509f9b61884a..41c2a3c13fae 100644 --- a/drivers/gator/gator_events_mali_common.h +++ b/drivers/gator/gator_events_mali_common.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_mali_t6xx.c b/drivers/gator/gator_events_mali_t6xx.c index 7bf7d6a6dbf9..76f14eee7676 100644 --- a/drivers/gator/gator_events_mali_t6xx.c +++ b/drivers/gator/gator_events_mali_t6xx.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,7 +15,13 @@ #include #include +#ifdef MALI_DIR_MIDGARD +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ #include "linux/mali_linux_trace.h" +#endif #include "gator_events_mali_common.h" diff --git a/drivers/gator/gator_events_mali_t6xx_hw.c b/drivers/gator/gator_events_mali_t6xx_hw.c index e406991398d9..dfbc91ffd765 100644 --- a/drivers/gator/gator_events_mali_t6xx_hw.c +++ b/drivers/gator/gator_events_mali_t6xx_hw.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,9 +16,17 @@ #include /* Mali T6xx DDK includes */ +#ifdef MALI_DIR_MIDGARD +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#include "mali_kbase.h" +#include "mali_kbase_mem_linux.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ #include "linux/mali_linux_trace.h" #include "kbase/src/common/mali_kbase.h" #include "kbase/src/linux/mali_kbase_mem_linux.h" +#endif #include "gator_events_mali_common.h" diff --git a/drivers/gator/gator_events_mali_t6xx_hw_test.c b/drivers/gator/gator_events_mali_t6xx_hw_test.c index efb32ddf5483..ba6553f3540f 100644 --- a/drivers/gator/gator_events_mali_t6xx_hw_test.c +++ b/drivers/gator/gator_events_mali_t6xx_hw_test.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_meminfo.c b/drivers/gator/gator_events_meminfo.c index 451290d9af17..c633dfdce306 100644 --- a/drivers/gator/gator_events_meminfo.c +++ b/drivers/gator/gator_events_meminfo.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -274,6 +274,28 @@ static int gator_events_meminfo_read(long long **buffer) return meminfo_length; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + +static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member) +{ +#ifdef SPLIT_RSS_COUNTING + long val = atomic_long_read(&mm->rss_stat.count[member]); + if (val < 0) + val = 0; + return (unsigned long)val; +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) + return mm->rss_stat.count[member]; +#else + return atomic_long_read(&mm->rss_stat.count[member]); +#endif +#endif +} + +#define get_mm_counter(mm, member) gator_get_mm_counter(mm, member) + +#endif + static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task) { struct mm_struct *mm; @@ -302,7 +324,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct // Derived from task_statm in fs/proc/task_mmu.c if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) { share = get_mm_counter(mm, -#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) file_rss #else MM_FILEPAGES @@ -338,7 +360,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct if (meminfo_enabled[MEMINFO_MEMUSED]) { value = share + get_mm_counter(mm, -#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) anon_rss #else MM_ANONPAGES diff --git a/drivers/gator/gator_events_mmapped.c b/drivers/gator/gator_events_mmapped.c index f055e48d317a..3b248ec24e6e 100644 --- a/drivers/gator/gator_events_mmapped.c +++ b/drivers/gator/gator_events_mmapped.c @@ -1,7 +1,7 @@ /* * Example events provider * - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_net.c b/drivers/gator/gator_events_net.c index 9c8d3a43eaeb..11c10e375511 100644 --- a/drivers/gator/gator_events_net.c +++ b/drivers/gator/gator_events_net.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_perf_pmu.c b/drivers/gator/gator_events_perf_pmu.c index d472df918ab0..8b2d67a058b3 100644 --- a/drivers/gator/gator_events_perf_pmu.c +++ b/drivers/gator/gator_events_perf_pmu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_sched.c b/drivers/gator/gator_events_sched.c index 29f4e39e261c..9e3915830182 100644 --- a/drivers/gator/gator_events_sched.c +++ b/drivers/gator/gator_events_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_events_scorpion.c b/drivers/gator/gator_events_scorpion.c index c91db1219d08..8ca251af0e26 100644 --- a/drivers/gator/gator_events_scorpion.c +++ b/drivers/gator/gator_events_scorpion.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_fs.c b/drivers/gator/gator_fs.c index fe6f83d547e9..166cfe7d681d 100644 --- a/drivers/gator/gator_fs.c +++ b/drivers/gator/gator_fs.c @@ -39,12 +39,7 @@ static const struct super_operations s_ops = { .drop_inode = generic_delete_inode, }; -ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset) -{ - return simple_read_from_buffer(buf, count, offset, str, strlen(str)); -} - -ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset) +static ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset) { char tmpbuf[TMPBUFSIZE]; size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val); @@ -53,7 +48,7 @@ ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); } -ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset) +static ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset) { char tmpbuf[TMPBUFSIZE]; size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val); @@ -62,7 +57,7 @@ ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *off return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen); } -int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) +static int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count) { char tmpbuf[TMPBUFSIZE]; unsigned long flags; @@ -84,7 +79,7 @@ int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t c return 0; } -int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count) +static int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count) { char tmpbuf[TMPBUFSIZE]; unsigned long flags; @@ -211,8 +206,8 @@ int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, return 0; } -int gatorfs_create_u64(struct super_block *sb, struct dentry *root, - char const *name, u64 *val) +static int gatorfs_create_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 *val) { struct dentry *d = __gatorfs_create_file(sb, root, name, &u64_fops, 0644); @@ -235,8 +230,8 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, return 0; } -int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root, - char const *name, u64 * val) +static int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root, + char const *name, u64 * val) { struct dentry *d = __gatorfs_create_file(sb, root, name, &u64_ro_fops, 0444); @@ -258,29 +253,17 @@ static const struct file_operations atomic_ro_fops = { .open = default_open, }; -int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root, - char const *name, atomic_t *val) -{ - struct dentry *d = __gatorfs_create_file(sb, root, name, - &atomic_ro_fops, 0444); - if (!d) - return -EFAULT; - - d->d_inode->i_private = val; - return 0; -} - -int gatorfs_create_file(struct super_block *sb, struct dentry *root, - char const *name, const struct file_operations *fops) +static int gatorfs_create_file(struct super_block *sb, struct dentry *root, + char const *name, const struct file_operations *fops) { if (!__gatorfs_create_file(sb, root, name, fops, 0644)) return -EFAULT; return 0; } -int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, - char const *name, - const struct file_operations *fops, int perm) +static int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root, + char const *name, + const struct file_operations *fops, int perm) { if (!__gatorfs_create_file(sb, root, name, fops, perm)) return -EFAULT; @@ -371,12 +354,12 @@ static struct file_system_type gatorfs_type = { .kill_sb = kill_litter_super, }; -int __init gatorfs_register(void) +static int __init gatorfs_register(void) { return register_filesystem(&gatorfs_type); } -void gatorfs_unregister(void) +static void gatorfs_unregister(void) { unregister_filesystem(&gatorfs_type); } diff --git a/drivers/gator/gator_hrtimer_gator.c b/drivers/gator/gator_hrtimer_gator.c index b0c947afe1e1..76584554b00f 100644 --- a/drivers/gator/gator_hrtimer_gator.c +++ b/drivers/gator/gator_hrtimer_gator.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -7,10 +7,6 @@ * */ -// gator_hrtimer_perf.c is used if perf is supported -// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers -#if 1 - void (*callback)(void); DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); DEFINE_PER_CPU(ktime_t, hrtimer_expire); @@ -82,5 +78,3 @@ static void gator_hrtimer_shutdown(void) { /* empty */ } - -#endif diff --git a/drivers/gator/gator_hrtimer_perf.c b/drivers/gator/gator_hrtimer_perf.c deleted file mode 100644 index 7b95399478e4..000000000000 --- a/drivers/gator/gator_hrtimer_perf.c +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -// gator_hrtimer_gator.c is used if perf is not supported -// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers -#if 0 - -// Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36 -// not relevant as this code is not active until 3.0.0, but wanted to document the issue - -void (*callback)(void); -static int profiling_interval; -static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer); -static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr); - -static void gator_hrtimer_shutdown(void); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) -static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) -#else -static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) -#endif -{ - (*callback)(); -} - -static int gator_online_single_hrtimer(int cpu) -{ - if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0) - return 0; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler); -#else - per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0); -#endif - if (IS_ERR(per_cpu(perf_hrtimer, cpu))) { - per_cpu(perf_hrtimer, cpu) = NULL; - return -1; - } - - if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) { - perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); - per_cpu(perf_hrtimer, cpu) = NULL; - return -1; - } - - return 0; -} - -static void gator_hrtimer_online(int cpu) -{ - if (gator_online_single_hrtimer(cpu) < 0) { - pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu); - } -} - -static void gator_hrtimer_offline(int cpu) -{ - if (per_cpu(perf_hrtimer, cpu)) { - perf_event_release_kernel(per_cpu(perf_hrtimer, cpu)); - per_cpu(perf_hrtimer, cpu) = NULL; - } -} - -static int gator_hrtimer_init(int interval, void (*func)(void)) -{ - u32 size = sizeof(struct perf_event_attr); - int cpu; - - callback = func; - - // calculate profiling interval - profiling_interval = 1000000000 / interval; - - for_each_present_cpu(cpu) { - per_cpu(perf_hrtimer, cpu) = 0; - per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL); - if (per_cpu(perf_hrtimer_attr, cpu) == 0) { - gator_hrtimer_shutdown(); - return -1; - } - - memset(per_cpu(perf_hrtimer_attr, cpu), 0, size); - per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE; - per_cpu(perf_hrtimer_attr, cpu)->size = size; - per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK; - per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval; - per_cpu(perf_hrtimer_attr, cpu)->pinned = 1; - } - - return 0; -} - -static void gator_hrtimer_shutdown(void) -{ - int cpu; - - for_each_present_cpu(cpu) { - if (per_cpu(perf_hrtimer_attr, cpu)) { - kfree(per_cpu(perf_hrtimer_attr, cpu)); - per_cpu(perf_hrtimer_attr, cpu) = NULL; - } - } -} - -#endif diff --git a/drivers/gator/gator_iks.c b/drivers/gator/gator_iks.c index 24233d775581..e90dfcce9381 100644 --- a/drivers/gator/gator_iks.c +++ b/drivers/gator/gator_iks.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -147,11 +147,13 @@ static void gator_send_iks_core_names(void) { int cpu; // Send the cpu names + preempt_disable(); for (cpu = 0; cpu < nr_cpu_ids; ++cpu) { if (mpidr_cpus[cpu] != NULL) { gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]); } } + preempt_enable(); } static int gator_migrate_start(void) diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c index 9773ae24d6f2..e67f7c5cc61d 100644 --- a/drivers/gator/gator_main.c +++ b/drivers/gator/gator_main.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -8,7 +8,7 @@ */ // This version must match the gator daemon version -#define PROTOCOL_VERSION 17 +#define PROTOCOL_VERSION 18 static unsigned long gator_protocol_version = PROTOCOL_VERSION; #include @@ -89,20 +89,27 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION; #define MESSAGE_END_BACKTRACE 1 +// Name Frame Messages #define MESSAGE_COOKIE 1 #define MESSAGE_THREAD_NAME 2 -#define HRTIMER_CORE_NAME 3 #define MESSAGE_LINK 4 +// GPU Trace Frame Messages #define MESSAGE_GPU_START 1 #define MESSAGE_GPU_STOP 2 +// Scheduler Trace Frame Messages #define MESSAGE_SCHED_SWITCH 1 #define MESSAGE_SCHED_EXIT 2 #define MESSAGE_SCHED_START 3 +// Idle Frame Messages #define MESSAGE_IDLE_ENTER 1 -#define MESSAGE_IDLE_EXIT 2 +#define MESSAGE_IDLE_EXIT 2 + +// Summary Frame Messages +#define MESSAGE_SUMMARY 1 +#define MESSAGE_CORE_NAME 3 #define MAXSIZE_PACK32 5 #define MAXSIZE_PACK64 10 @@ -154,7 +161,13 @@ bool event_based_sampling; static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait); static struct timer_list gator_buffer_wake_up_timer; -static bool gator_buffer_wake_stop; +static bool gator_buffer_wake_run; +// Initialize semaphore unlocked to initialize memory values +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +static DECLARE_MUTEX(gator_buffer_wake_sem); +#else +static DEFINE_SEMAPHORE(gator_buffer_wake_sem); +#endif static struct task_struct *gator_buffer_wake_thread; static LIST_HEAD(gator_events); @@ -164,21 +177,19 @@ static bool printed_monotonic_warning; static bool sent_core_name[NR_CPUS]; +static DEFINE_PER_CPU(bool, in_scheduler_context); + /****************************************************************************** * Prototypes ******************************************************************************/ -static void buffer_check(int cpu, int buftype, u64 time); -static void gator_commit_buffer(int cpu, int buftype, u64 time); -static int buffer_bytes_available(int cpu, int buftype); -static bool buffer_check_space(int cpu, int buftype, int bytes); -static int contiguous_space_available(int cpu, int bufytpe); -static void gator_buffer_write_packed_int(int cpu, int buftype, int x); -static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x); -static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len); -static void gator_buffer_write_string(int cpu, int buftype, const char *x); -static void gator_add_trace(int cpu, unsigned long address); -static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time); static u64 gator_get_time(void); +static void gator_op_create_files(struct super_block *sb, struct dentry *root); + +// gator_buffer is protected by being per_cpu and by having IRQs disabled when writing to it. +// Most marshal_* calls take care of this except for marshal_cookie*, marshal_backtrace* and marshal_frame where the caller is responsible for doing so. +// No synchronization is needed with the backtrace buffer as it is per cpu and is only used from the hrtimer. +// The annotate_lock must be held when using the annotation buffer as it is not per cpu. +// collect_counters which is the sole writer to the block counter frame is additionally protected by the per cpu collecting flag // Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup. static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; @@ -229,8 +240,10 @@ GATOR_EVENTS_LIST /****************************************************************************** * Application Includes ******************************************************************************/ +#include "gator_fs.c" +#include "gator_buffer_write.c" +#include "gator_buffer.c" #include "gator_marshaling.c" -#include "gator_hrtimer_perf.c" #include "gator_hrtimer_gator.c" #include "gator_cookies.c" #include "gator_annotate.c" @@ -238,14 +251,12 @@ GATOR_EVENTS_LIST #include "gator_trace_power.c" #include "gator_trace_gpu.c" #include "gator_backtrace.c" -#include "gator_fs.c" -#include "gator_pack.c" /****************************************************************************** * Misc ******************************************************************************/ -const struct gator_cpu gator_cpus[] = { +static const struct gator_cpu gator_cpus[] = { { .cpuid = ARM1136, .core_name = "ARM1136", @@ -277,51 +288,52 @@ const struct gator_cpu gator_cpus[] = { { .cpuid = CORTEX_A5, .core_name = "Cortex-A5", - .pmu_name = "ARMv7_Cortex_A5", - .pmnc_name = "ARM_Cortex-A5", + .pmnc_name = "ARMv7_Cortex_A5", .dt_name = "arm,cortex-a5", .pmnc_counters = 2, }, { .cpuid = CORTEX_A7, .core_name = "Cortex-A7", - .pmu_name = "ARMv7_Cortex_A7", - .pmnc_name = "ARM_Cortex-A7", + .pmnc_name = "ARMv7_Cortex_A7", .dt_name = "arm,cortex-a7", .pmnc_counters = 4, }, { .cpuid = CORTEX_A8, .core_name = "Cortex-A8", - .pmu_name = "ARMv7_Cortex_A8", - .pmnc_name = "ARM_Cortex-A8", + .pmnc_name = "ARMv7_Cortex_A8", .dt_name = "arm,cortex-a8", .pmnc_counters = 4, }, { .cpuid = CORTEX_A9, .core_name = "Cortex-A9", - .pmu_name = "ARMv7_Cortex_A9", - .pmnc_name = "ARM_Cortex-A9", + .pmnc_name = "ARMv7_Cortex_A9", .dt_name = "arm,cortex-a9", .pmnc_counters = 6, }, { .cpuid = CORTEX_A12, .core_name = "Cortex-A12", - .pmu_name = "ARMv7_Cortex_A12", - .pmnc_name = "ARM_Cortex-A12", + .pmnc_name = "ARMv7_Cortex_A12", .dt_name = "arm,cortex-a12", .pmnc_counters = 6, }, { .cpuid = CORTEX_A15, .core_name = "Cortex-A15", - .pmu_name = "ARMv7_Cortex_A15", - .pmnc_name = "ARM_Cortex-A15", + .pmnc_name = "ARMv7_Cortex_A15", .dt_name = "arm,cortex-a15", .pmnc_counters = 6, }, + { + .cpuid = CORTEX_A17, + .core_name = "Cortex-A17", + .pmnc_name = "ARMv7_Cortex_A17", + .dt_name = "arm,cortex-a17", + .pmnc_counters = 6, + }, { .cpuid = SCORPION, .core_name = "Scorpion", @@ -401,7 +413,7 @@ const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name) for (i = 0; gator_cpus[i].cpuid != 0; ++i) { const struct gator_cpu *const gator_cpu = &gator_cpus[i]; - if (gator_cpu->pmu_name != NULL && strcmp(gator_cpu->pmu_name, name) == 0) { + if (gator_cpu->pmnc_name != NULL && strcmp(gator_cpu->pmnc_name, name) == 0) { return gator_cpu; } } @@ -431,10 +443,15 @@ static void gator_buffer_wake_up(unsigned long data) static int gator_buffer_wake_func(void *data) { - while (!gator_buffer_wake_stop) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (gator_buffer_wake_stop) { + for (;;) { + if (down_killable(&gator_buffer_wake_sem)) { + break; + } + + // Eat up any pending events + while (!down_trylock(&gator_buffer_wake_sem)); + + if (!gator_buffer_wake_run) { break; } @@ -463,173 +480,6 @@ static bool buffer_commit_ready(int *cpu, int *buftype) return false; } -/****************************************************************************** - * Buffer management - ******************************************************************************/ -static int buffer_bytes_available(int cpu, int buftype) -{ - int remaining, filled; - - filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; - if (filled < 0) { - filled += gator_buffer_size[buftype]; - } - - remaining = gator_buffer_size[buftype] - filled; - - if (per_cpu(buffer_space_available, cpu)[buftype]) { - // Give some extra room; also allows space to insert the overflow error packet - remaining -= 200; - } else { - // Hysteresis, prevents multiple overflow messages - remaining -= 2000; - } - - return remaining; -} - -static int contiguous_space_available(int cpu, int buftype) -{ - int remaining = buffer_bytes_available(cpu, buftype); - int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype]; - if (remaining < contiguous) - return remaining; - else - return contiguous; -} - -static bool buffer_check_space(int cpu, int buftype, int bytes) -{ - int remaining = buffer_bytes_available(cpu, buftype); - - if (remaining < bytes) { - per_cpu(buffer_space_available, cpu)[buftype] = false; - } else { - per_cpu(buffer_space_available, cpu)[buftype] = true; - } - - return per_cpu(buffer_space_available, cpu)[buftype]; -} - -static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len) -{ - int i; - u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; - u32 mask = gator_buffer_mask[buftype]; - char *buffer = per_cpu(gator_buffer, cpu)[buftype]; - - for (i = 0; i < len; i++) { - buffer[write] = x[i]; - write = (write + 1) & mask; - } - - per_cpu(gator_buffer_write, cpu)[buftype] = write; -} - -static void gator_buffer_write_string(int cpu, int buftype, const char *x) -{ - int len = strlen(x); - gator_buffer_write_packed_int(cpu, buftype, len); - gator_buffer_write_bytes(cpu, buftype, x, len); -} - -static void gator_commit_buffer(int cpu, int buftype, u64 time) -{ - int type_length, commit, length, byte; - - if (!per_cpu(gator_buffer, cpu)[buftype]) - return; - - // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload - type_length = gator_response_type ? 1 : 0; - commit = per_cpu(gator_buffer_commit, cpu)[buftype]; - length = per_cpu(gator_buffer_write, cpu)[buftype] - commit; - if (length < 0) { - length += gator_buffer_size[buftype]; - } - length = length - type_length - sizeof(s32); - - if (length <= FRAME_HEADER_SIZE) { - // Nothing to write, only the frame header is present - return; - } - - for (byte = 0; byte < sizeof(s32); byte++) { - per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; - } - - per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; - - if (gator_live_rate > 0) { - while (time > per_cpu(gator_buffer_commit_time, cpu)) { - per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate; - } - } - - marshal_frame(cpu, buftype); - - // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater - if (per_cpu(in_scheduler_context, cpu)) { -#ifndef CONFIG_PREEMPT_RT_FULL - // mod_timer can not be used in interrupt context in RT-Preempt full - mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); -#endif - } else { - wake_up_process(gator_buffer_wake_thread); - } -} - -static void buffer_check(int cpu, int buftype, u64 time) -{ - int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype]; - if (filled < 0) { - filled += gator_buffer_size[buftype]; - } - if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) { - gator_commit_buffer(cpu, buftype, time); - } -} - -static void gator_add_trace(int cpu, unsigned long address) -{ - off_t offset = 0; - unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset); - - if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) { - offset = address; - } - - marshal_backtrace(offset & ~1, cookie); -} - -static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time) -{ - bool inKernel; - unsigned long exec_cookie; - - if (!regs) - return; - - inKernel = !user_mode(regs); - exec_cookie = get_exec_cookie(cpu, current); - - if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel, time)) - return; - - if (inKernel) { - kernel_backtrace(cpu, regs); - } else { - // Cookie+PC - gator_add_trace(cpu, PC_REG); - - // Backtrace - if (gator_backtrace_depth) - arm_backtrace_eabi(cpu, regs, gator_backtrace_depth); - } - - marshal_backtrace_footer(time); -} - /****************************************************************************** * hrtimer interrupt processing ******************************************************************************/ @@ -721,7 +571,8 @@ static void gator_timer_stop(void) } #if defined(__arm__) || defined(__aarch64__) -static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) { +static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) +{ const char *core_name = NULL; char core_name_buf[32]; @@ -788,7 +639,7 @@ static void gator_timer_online_dispatch(int cpu, bool migrate) #include "gator_iks.c" -int gator_timer_start(unsigned long sample_rate) +static int gator_timer_start(unsigned long sample_rate) { int cpu; @@ -944,7 +795,6 @@ static void gator_summary(void) struct timespec ts; char uname_buf[512]; void (*m2b)(struct timespec *ts); - unsigned long flags; snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine); @@ -959,14 +809,14 @@ static void gator_summary(void) } uptime = timespec_to_ns(&ts); - // Disable interrupts as gator_get_time calls smp_processor_id to verify time is monotonic - local_irq_save(flags); + // Disable preemption as gator_get_time calls smp_processor_id to verify time is monotonic + preempt_disable(); // Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started gator_monotonic_started = 0; gator_monotonic_started = gator_get_time(); - local_irq_restore(flags); marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf); + preempt_enable(); } int gator_events_install(struct gator_interface *interface) @@ -1019,7 +869,7 @@ static int gator_start(void) unsigned long cpu, i; struct gator_interface *gi; - gator_buffer_wake_stop = false; + gator_buffer_wake_run = true; if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) { goto bwake_failure; } @@ -1094,8 +944,9 @@ cookies_failure: events_failure: gator_migrate_stop(); migrate_failure: - gator_buffer_wake_stop = true; - wake_up_process(gator_buffer_wake_thread); + gator_buffer_wake_run = false; + up(&gator_buffer_wake_sem); + gator_buffer_wake_thread = NULL; bwake_failure: return -1; @@ -1121,8 +972,9 @@ static void gator_stop(void) gator_migrate_stop(); - gator_buffer_wake_stop = true; - wake_up_process(gator_buffer_wake_thread); + gator_buffer_wake_run = false; + up(&gator_buffer_wake_sem); + gator_buffer_wake_thread = NULL; } /****************************************************************************** @@ -1417,7 +1269,7 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t return written > 0 ? written : -EFAULT; } -const struct file_operations gator_event_buffer_fops = { +static const struct file_operations gator_event_buffer_fops = { .open = userspace_buffer_open, .release = userspace_buffer_release, .read = userspace_buffer_read, @@ -1452,7 +1304,7 @@ static const struct file_operations depth_fops = { .write = depth_write }; -void gator_op_create_files(struct super_block *sb, struct dentry *root) +static void gator_op_create_files(struct super_block *sb, struct dentry *root) { struct dentry *dir; struct gator_interface *gi; diff --git a/drivers/gator/gator_marshaling.c b/drivers/gator/gator_marshaling.c index af80ff62e712..fd413ad1331c 100644 --- a/drivers/gator/gator_marshaling.c +++ b/drivers/gator/gator_marshaling.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2012-2013. All rights reserved. + * Copyright (C) ARM Limited 2012-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -29,6 +29,7 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon int cpu = 0; local_irq_save(flags); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY); gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY); gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp); gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime); @@ -52,8 +53,8 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon #endif gator_buffer_write_string(cpu, SUMMARY_BUF, ""); // Commit the buffer now so it can be one of the first frames read by Streamline - gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); local_irq_restore(flags); + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); } static bool marshal_cookie_header(const char *text) @@ -85,8 +86,8 @@ static void marshal_thread_name(int pid, char *name) gator_buffer_write_packed_int(cpu, NAME_BUF, pid); gator_buffer_write_string(cpu, NAME_BUF, name); } - buffer_check(cpu, NAME_BUF, time); local_irq_restore(flags); + buffer_check(cpu, NAME_BUF, time); } static void marshal_link(int cookie, int tgid, int pid) @@ -103,12 +104,12 @@ static void marshal_link(int cookie, int tgid, int pid) gator_buffer_write_packed_int(cpu, NAME_BUF, tgid); gator_buffer_write_packed_int(cpu, NAME_BUF, pid); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, NAME_BUF, time); - local_irq_restore(flags); } -static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel, u64 time) +static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, u64 time) { int cpu = get_physical_cpu(); if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { @@ -122,14 +123,16 @@ static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inK gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie); gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid); gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); - gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel); return true; } -static void marshal_backtrace(unsigned long address, int cookie) +static void marshal_backtrace(unsigned long address, int cookie, int in_kernel) { int cpu = get_physical_cpu(); + if (cookie == 0 && !in_kernel) { + cookie = UNRESOLVED_COOKIE; + } gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address); } @@ -224,9 +227,9 @@ static void marshal_event_single(int core, int key, int value) gator_buffer_write_packed_int(cpu, COUNTER_BUF, key); gator_buffer_write_packed_int(cpu, COUNTER_BUF, value); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, COUNTER_BUF, time); - local_irq_restore(flags); } #endif @@ -248,9 +251,9 @@ static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid) gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, GPU_TRACE_BUF, time); - local_irq_restore(flags); } static void marshal_sched_gpu_stop(int unit, int core) @@ -269,9 +272,9 @@ static void marshal_sched_gpu_stop(int unit, int core) gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, GPU_TRACE_BUF, time); - local_irq_restore(flags); } static void marshal_sched_trace_start(int tgid, int pid, int cookie) @@ -291,9 +294,9 @@ static void marshal_sched_trace_start(int tgid, int pid, int cookie) gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, SCHED_TRACE_BUF, time); - local_irq_restore(flags); } static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) @@ -314,9 +317,9 @@ static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state) gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, SCHED_TRACE_BUF, time); - local_irq_restore(flags); } static void marshal_sched_trace_exit(int tgid, int pid) @@ -334,9 +337,9 @@ static void marshal_sched_trace_exit(int tgid, int pid) gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time); gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, SCHED_TRACE_BUF, time); - local_irq_restore(flags); } #if GATOR_CPU_FREQ_SUPPORT @@ -353,80 +356,26 @@ static void marshal_idle(int core, int state) gator_buffer_write_packed_int64(cpu, IDLE_BUF, time); gator_buffer_write_packed_int(cpu, IDLE_BUF, core); } + local_irq_restore(flags); // Check and commit; commit is set to occur once buffer is 3/4 full buffer_check(cpu, IDLE_BUF, time); - local_irq_restore(flags); } #endif -static void marshal_frame(int cpu, int buftype) -{ - int frame; - - if (!per_cpu(gator_buffer, cpu)[buftype]) { - return; - } - - switch (buftype) { - case SUMMARY_BUF: - frame = FRAME_SUMMARY; - break; - case BACKTRACE_BUF: - frame = FRAME_BACKTRACE; - break; - case NAME_BUF: - frame = FRAME_NAME; - break; - case COUNTER_BUF: - frame = FRAME_COUNTER; - break; - case BLOCK_COUNTER_BUF: - frame = FRAME_BLOCK_COUNTER; - break; - case ANNOTATE_BUF: - frame = FRAME_ANNOTATE; - break; - case SCHED_TRACE_BUF: - frame = FRAME_SCHED_TRACE; - break; - case GPU_TRACE_BUF: - frame = FRAME_GPU_TRACE; - break; - case IDLE_BUF: - frame = FRAME_IDLE; - break; - default: - frame = -1; - break; - } - - // add response type - if (gator_response_type > 0) { - gator_buffer_write_packed_int(cpu, buftype, gator_response_type); - } - - // leave space for 4-byte unpacked length - per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype]; - - // add frame type and core number - gator_buffer_write_packed_int(cpu, buftype, frame); - gator_buffer_write_packed_int(cpu, buftype, cpu); -} - #if defined(__arm__) || defined(__aarch64__) static void marshal_core_name(const int core, const int cpuid, const char *name) { int cpu = get_physical_cpu(); unsigned long flags; local_irq_save(flags); - if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) { - gator_buffer_write_packed_int(cpu, NAME_BUF, HRTIMER_CORE_NAME); - gator_buffer_write_packed_int(cpu, NAME_BUF, core); - gator_buffer_write_packed_int(cpu, NAME_BUF, cpuid); - gator_buffer_write_string(cpu, NAME_BUF, name); + if (buffer_check_space(cpu, SUMMARY_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) { + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_CORE_NAME); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, core); + gator_buffer_write_packed_int(cpu, SUMMARY_BUF, cpuid); + gator_buffer_write_string(cpu, SUMMARY_BUF, name); } // Commit core names now so that they can show up in live - gator_commit_buffer(cpu, NAME_BUF, gator_get_time()); local_irq_restore(flags); + gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time()); } #endif diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c index be135b4aac56..6332098e5958 100644 --- a/drivers/gator/gator_trace_gpu.c +++ b/drivers/gator/gator_trace_gpu.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,8 +14,15 @@ #include #ifdef MALI_SUPPORT +#ifdef MALI_DIR_MIDGARD +/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/ +#include "mali_linux_trace.h" +#else +/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/ #include "linux/mali_linux_trace.h" #endif +#endif + #include "gator_trace_gpu.h" /* @@ -235,7 +242,7 @@ GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) mali_gpu_stop(gpu_unit, gpu_core); } -int gator_trace_gpu_start(void) +static int gator_trace_gpu_start(void) { /* * Returns nonzero for installation failed @@ -271,7 +278,7 @@ int gator_trace_gpu_start(void) return 0; } -void gator_trace_gpu_stop(void) +static void gator_trace_gpu_stop(void) { #if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) if (mali_timeline_trace_registered) { diff --git a/drivers/gator/gator_trace_gpu.h b/drivers/gator/gator_trace_gpu.h index bb0f42d290da..5113d459e24c 100644 --- a/drivers/gator/gator_trace_gpu.h +++ b/drivers/gator/gator_trace_gpu.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/gator_trace_power.c b/drivers/gator/gator_trace_power.c index 272e05684ee8..1895bb988c9f 100644 --- a/drivers/gator/gator_trace_power.c +++ b/drivers/gator/gator_trace_power.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -166,7 +166,7 @@ static void gator_trace_power_stop(void) } } -void gator_trace_power_init(void) +static void gator_trace_power_init(void) { int i; for (i = 0; i < POWER_TOTAL; i++) { @@ -197,7 +197,7 @@ static void gator_trace_power_stop(void) { } -void gator_trace_power_init(void) +static void gator_trace_power_init(void) { } #endif diff --git a/drivers/gator/gator_trace_sched.c b/drivers/gator/gator_trace_sched.c index 332b3f6ba965..52990e9d4811 100644 --- a/drivers/gator/gator_trace_sched.c +++ b/drivers/gator/gator_trace_sched.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,7 +22,6 @@ enum { static DEFINE_PER_CPU(uint64_t *, taskname_keys); static DEFINE_PER_CPU(int, collecting); -static DEFINE_PER_CPU(bool, in_scheduler_context); // this array is never read as the cpu wait charts are derived counters // the files are needed, nonetheless, to show that these counters are available @@ -52,7 +51,7 @@ static int sched_trace_create_files(struct super_block *sb, struct dentry *root) return 0; } -void emit_pid_name(struct task_struct *task) +static void emit_pid_name(struct task_struct *task) { bool found = false; char taskcomm[TASK_COMM_LEN + 3]; @@ -116,20 +115,21 @@ static void collect_counters(u64 time, struct task_struct *task) // Commit buffers on timeout if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) { static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF }; - unsigned long flags; int i; - local_irq_save(flags); for (i = 0; i < ARRAY_SIZE(buftypes); ++i) { gator_commit_buffer(cpu, buftypes[i], time); } - local_irq_restore(flags); + // spinlocks are noops on uniprocessor machines and mutexes do not work in sched_switch context in + // RT-Preempt full, so disable proactive flushing of the annotate frame on uniprocessor machines. +#ifdef CONFIG_SMP // Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full if (on_primary_core() && spin_trylock(&annotate_lock)) { gator_commit_buffer(0, ANNOTATE_BUF, time); spin_unlock(&annotate_lock); } +#endif } } } @@ -222,7 +222,7 @@ fail_sched_process_fork: return -1; } -int gator_trace_sched_start(void) +static int gator_trace_sched_start(void) { int cpu, size; @@ -237,7 +237,7 @@ int gator_trace_sched_start(void) return register_scheduler_tracepoints(); } -void gator_trace_sched_offline(void) +static void gator_trace_sched_offline(void) { trace_sched_insert_idle(); } @@ -250,7 +250,7 @@ static void unregister_scheduler_tracepoints(void) pr_debug("gator: unregistered tracepoints\n"); } -void gator_trace_sched_stop(void) +static void gator_trace_sched_stop(void) { int cpu; unregister_scheduler_tracepoints(); @@ -260,7 +260,7 @@ void gator_trace_sched_stop(void) } } -void gator_trace_sched_init(void) +static void gator_trace_sched_init(void) { int i; for (i = 0; i < CPU_WAIT_TOTAL; i++) { diff --git a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h index 347a4fe404bc..ff00d90cee78 100644 --- a/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h +++ b/drivers/gator/mali/mali_mjollnir_profiling_gator_api.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/mali/mali_utgard_profiling_gator_api.h b/drivers/gator/mali/mali_utgard_profiling_gator_api.h index 559647a76d29..43c576042880 100644 --- a/drivers/gator/mali/mali_utgard_profiling_gator_api.h +++ b/drivers/gator/mali/mali_utgard_profiling_gator_api.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/gator/mali_t6xx.mk b/drivers/gator/mali_t6xx.mk index 1a98c1c6a73f..059d47aec910 100644 --- a/drivers/gator/mali_t6xx.mk +++ b/drivers/gator/mali_t6xx.mk @@ -10,8 +10,17 @@ EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ -DMALI_NO_MALI=0 DDK_DIR ?= . +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/t6xx),) KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk +endif + +ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard),) +KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard +OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk +EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1 +endif + UMP_DIR = $(DDK_DIR)/include/linux # Include directories in the DDK diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk index a0429712fa82..045d028fda5f 100644 --- a/tools/gator/daemon/Android.mk +++ b/tools/gator/daemon/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors @@ -9,22 +9,33 @@ LOCAL_SRC_FILES := \ Buffer.cpp \ CapturedXML.cpp \ Child.cpp \ - Collector.cpp \ ConfigurationXML.cpp \ Driver.cpp \ + DriverSource.cpp \ + DynBuf.cpp \ EventsXML.cpp \ + ExternalSource.cpp \ Fifo.cpp \ Hwmon.cpp \ KMod.cpp \ LocalCapture.cpp \ Logging.cpp \ main.cpp \ + Monitor.cpp \ OlySocket.cpp \ OlyUtility.cpp \ + PerfBuffer.cpp \ + PerfDriver.cpp \ + PerfGroup.cpp \ + PerfSource.cpp \ + Proc.cpp \ Sender.cpp \ SessionData.cpp \ SessionXML.cpp \ + Source.cpp \ StreamlineSetup.cpp \ + UEvent.cpp \ + UserSpaceSource.cpp \ libsensors/access.c \ libsensors/conf-lex.c \ libsensors/conf-parse.c \ diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp index 090a71553277..93557dabed9f 100644 --- a/tools/gator/daemon/Buffer.cpp +++ b/tools/gator/daemon/Buffer.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,33 +12,60 @@ #include "Sender.h" #include "SessionData.h" -#define mask (size - 1) +#define mask (mSize - 1) -Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) { - if ((size & mask) != 0) { +enum { + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, +}; + +// Summary Frame Messages +enum { + MESSAGE_SUMMARY = 1, + MESSAGE_CORE_NAME = 3, +}; + +// From gator_marshaling.c +#define NEWLINE_CANARY \ + /* Unix */ \ + "1\n" \ + /* Windows */ \ + "2\r\n" \ + /* Mac OS */ \ + "3\r" \ + /* RISC OS */ \ + "4\n\r" \ + /* Add another character so the length isn't 0x0a bytes */ \ + "5" + +Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) { + if ((mSize & mask) != 0) { logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); handleException(); } frame(); } -Buffer::~Buffer () { - delete [] buf; +Buffer::~Buffer() { + delete [] mBuf; } -void Buffer::write (Sender * const sender) { +void Buffer::write(Sender *const sender) { if (!commitReady()) { return; } // determine the size of two halves - int length1 = commitPos - readPos; - char * buffer1 = buf + readPos; + int length1 = mCommitPos - mReadPos; + char *buffer1 = mBuf + mReadPos; int length2 = 0; - char * buffer2 = buf; + char *buffer2 = mBuf; if (length1 < 0) { - length1 = size - readPos; - length2 = commitPos; + length1 = mSize - mReadPos; + length2 = mCommitPos; } logg->logMessage("Sending data length1: %i length2: %i", length1, length2); @@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) { sender->writeData(buffer2, length2, RESPONSE_APC_DATA); } - readPos = commitPos; + mReadPos = mCommitPos; } -bool Buffer::commitReady () const { - return commitPos != readPos; +bool Buffer::commitReady() const { + return mCommitPos != mReadPos; } -int Buffer::bytesAvailable () const { - int filled = writePos - readPos; +int Buffer::bytesAvailable() const { + int filled = mWritePos - mReadPos; if (filled < 0) { - filled += size; + filled += mSize; } - int remaining = size - filled; + int remaining = mSize - filled; - if (available) { + if (mAvailable) { // Give some extra room; also allows space to insert the overflow error packet remaining -= 200; } else { @@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const { return remaining; } -bool Buffer::checkSpace (const int bytes) { +bool Buffer::checkSpace(const int bytes) { const int remaining = bytesAvailable(); if (remaining < bytes) { - available = false; + mAvailable = false; } else { - available = true; + mAvailable = true; } - return available; + return mAvailable; } -void Buffer::commit (const uint64_t time) { +int Buffer::contiguousSpaceAvailable() const { + int remaining = bytesAvailable(); + int contiguous = mSize - mWritePos; + if (remaining < contiguous) { + return remaining; + } else { + return contiguous; + } +} + +void Buffer::commit(const uint64_t time) { // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload const int typeLength = gSessionData->mLocalCapture ? 0 : 1; - int length = writePos - commitPos; + int length = mWritePos - mCommitPos; if (length < 0) { - length += size; + length += mSize; } length = length - typeLength - sizeof(int32_t); for (size_t byte = 0; byte < sizeof(int32_t); byte++) { - buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; } - logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); - commitPos = writePos; + logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); + mCommitPos = mWritePos; if (gSessionData->mLiveRate > 0) { - while (time > commitTime) { - commitTime += gSessionData->mLiveRate; + while (time > mCommitTime) { + mCommitTime += gSessionData->mLiveRate; } } - if (!done) { + if (!mIsDone) { frame(); } // send a notification that data is ready - sem_post(readerSem); + sem_post(mReaderSem); } -void Buffer::check (const uint64_t time) { - int filled = writePos - commitPos; +void Buffer::check(const uint64_t time) { + int filled = mWritePos - mCommitPos; if (filled < 0) { - filled += size; + filled += mSize; } - if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) { + if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { commit(time); } } -void Buffer::packInt (int32_t x) { +void Buffer::packInt(int32_t x) { int packedBytes = 0; int more = true; while (more) { @@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) { b |= 0x80; } - buf[(writePos + packedBytes) & mask] = b; + mBuf[(mWritePos + packedBytes) & mask] = b; packedBytes++; } - writePos = (writePos + packedBytes) & mask; + mWritePos = (mWritePos + packedBytes) & mask; } -void Buffer::packInt64 (int64_t x) { +void Buffer::packInt64(int64_t x) { int packedBytes = 0; int more = true; while (more) { @@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) { b |= 0x80; } - buf[(writePos + packedBytes) & mask] = b; + mBuf[(mWritePos + packedBytes) & mask] = b; packedBytes++; } - writePos = (writePos + packedBytes) & mask; + mWritePos = (mWritePos + packedBytes) & mask; } -void Buffer::frame () { +void Buffer::writeBytes(const void *const data, size_t count) { + size_t i; + for (i = 0; i < count; ++i) { + mBuf[(mWritePos + i) & mask] = static_cast(data)[i]; + } + + mWritePos = (mWritePos + i) & mask; +} + +void Buffer::writeString(const char *const str) { + const int len = strlen(str); + packInt(len); + writeBytes(str, len); +} + +void Buffer::frame() { if (!gSessionData->mLocalCapture) { packInt(RESPONSE_APC_DATA); } // Reserve space for the length - writePos += sizeof(int32_t); - packInt(buftype); - packInt(core); + mWritePos += sizeof(int32_t); + packInt(mBufType); + packInt(mCore); } -bool Buffer::eventHeader (const uint64_t curr_time) { +void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { + packInt(MESSAGE_SUMMARY); + writeString(NEWLINE_CANARY); + packInt64(timestamp); + packInt64(uptime); + packInt64(monotonicDelta); + writeString("uname"); + writeString(uname); + writeString(""); + check(1); +} + +void Buffer::coreName(const int core, const int cpuid, const char *const name) { + if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) { + packInt(MESSAGE_CORE_NAME); + packInt(core); + packInt(cpuid); + writeString(name); + } + check(1); +} + +bool Buffer::eventHeader(const uint64_t curr_time) { bool retval = false; if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { packInt(0); // key of zero indicates a timestamp @@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) { return retval; } -bool Buffer::eventTid (const int tid) { +bool Buffer::eventTid(const int tid) { bool retval = false; - if (checkSpace(2*MAXSIZE_PACK32)) { + if (checkSpace(2 * MAXSIZE_PACK32)) { packInt(1); // key of 1 indicates a tid packInt(tid); retval = true; @@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) { return retval; } -void Buffer::event (const int32_t key, const int32_t value) { +void Buffer::event(const int32_t key, const int32_t value) { if (checkSpace(2 * MAXSIZE_PACK32)) { packInt(key); packInt(value); } } -void Buffer::event64 (const int64_t key, const int64_t value) { +void Buffer::event64(const int64_t key, const int64_t value) { if (checkSpace(2 * MAXSIZE_PACK64)) { packInt64(key); packInt64(value); } } -void Buffer::setDone () { - done = true; +void Buffer::pea(const struct perf_event_attr *const pea, int key) { + if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { + packInt(CODE_PEA); + writeBytes(pea, pea->size); + packInt(key); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + // Don't know the real perf time so use 1 as it will work for now + check(1); +} + +void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) { + if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { + packInt(CODE_KEYS); + packInt(count); + for (int i = 0; i < count; ++i) { + packInt64(ids[i]); + packInt(keys[i]); + } + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::format(const int length, const char *const format) { + if (checkSpace(MAXSIZE_PACK32 + length + 1)) { + packInt(CODE_FORMAT); + writeBytes(format, length + 1); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::maps(const int pid, const int tid, const char *const maps) { + const int mapsLen = strlen(maps) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { + packInt(CODE_MAPS); + packInt(pid); + packInt(tid); + writeBytes(maps, mapsLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) { + const int imageLen = strlen(image) + 1; + const int commLen = strlen(comm) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { + packInt(CODE_COMM); + packInt(pid); + packInt(tid); + writeBytes(image, imageLen); + writeBytes(comm, commLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::setDone() { + mIsDone = true; commit(0); } -bool Buffer::isDone () const { - return done && readPos == commitPos && commitPos == writePos; +bool Buffer::isDone() const { + return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; } diff --git a/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h index b3c8d78cf758..50237771860c 100644 --- a/tools/gator/daemon/Buffer.h +++ b/tools/gator/daemon/Buffer.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,54 +9,89 @@ #ifndef BUFFER_H #define BUFFER_H -#include #include #include +#include "k/perf_event.h" + class Sender; +enum { + FRAME_SUMMARY = 1, + FRAME_BLOCK_COUNTER = 5, + FRAME_EXTERNAL = 10, + FRAME_PERF_ATTRS = 11, + FRAME_PERF = 12, +}; + class Buffer { public: static const size_t MAXSIZE_PACK32 = 5; static const size_t MAXSIZE_PACK64 = 10; - Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem); - ~Buffer (); + Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem); + ~Buffer(); - void write (Sender * sender); + void write(Sender *sender); - int bytesAvailable () const; - void commit (const uint64_t time); - void check (const uint64_t time); + int bytesAvailable() const; + int contiguousSpaceAvailable() const; + void commit(const uint64_t time); + void check(const uint64_t time); - void frame (); + void frame(); - bool eventHeader (uint64_t curr_time); - bool eventTid (int tid); - void event (int32_t key, int32_t value); - void event64 (int64_t key, int64_t value); + // Summary messages + void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); + void coreName(const int core, const int cpuid, const char *const name); - void setDone (); - bool isDone () const; + // Block Counter messages + bool eventHeader(uint64_t curr_time); + bool eventTid(int tid); + void event(int32_t key, int32_t value); + void event64(int64_t key, int64_t value); + + // Perf Attrs messages + void pea(const struct perf_event_attr *const pea, int key); + void keys(const int count, const __u64 *const ids, const int *const keys); + void format(const int length, const char *const format); + void maps(const int pid, const int tid, const char *const maps); + void comm(const int pid, const int tid, const char *const image, const char *const comm); + + void setDone(); + bool isDone() const; + + // Prefer a new member to using these functions if possible + char *getWritePos() { return mBuf + mWritePos; } + void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } + + static void writeLEInt(unsigned char *buf, int v) { + buf[0] = (v >> 0) & 0xFF; + buf[1] = (v >> 8) & 0xFF; + buf[2] = (v >> 16) & 0xFF; + buf[3] = (v >> 24) & 0xFF; + } private: - bool commitReady () const; - bool checkSpace (int bytes); + bool commitReady() const; + bool checkSpace(int bytes); - void packInt (int32_t x); - void packInt64 (int64_t x); + void packInt(int32_t x); + void packInt64(int64_t x); + void writeBytes(const void *const data, size_t count); + void writeString(const char *const str); - const int32_t core; - const int32_t buftype; - const int size; - int readPos; - int writePos; - int commitPos; - bool available; - bool done; - char *const buf; - uint64_t commitTime; - sem_t *const readerSem; + const int32_t mCore; + const int32_t mBufType; + const int mSize; + int mReadPos; + int mWritePos; + int mCommitPos; + bool mAvailable; + bool mIsDone; + char *const mBuf; + uint64_t mCommitTime; + sem_t *const mReaderSem; // Intentionally unimplemented Buffer(const Buffer &); diff --git a/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp index 30c4c44c5d92..cf79b72a1166 100644 --- a/tools/gator/daemon/CapturedXML.cpp +++ b/tools/gator/daemon/CapturedXML.cpp @@ -1,16 +1,18 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "CapturedXML.h" + #include #include #include + #include "SessionData.h" -#include "CapturedXML.h" #include "Logging.h" #include "OlyUtility.h" @@ -30,6 +32,9 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { captured = mxmlNewElement(xml, "captured"); mxmlElementSetAttr(captured, "version", "1"); + if (gSessionData->perf.isSetup()) { + mxmlElementSetAttr(captured, "type", "Perf"); + } mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); if (includeTime) { // Send the following only after the capture is complete if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) @@ -41,7 +46,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { mxmlElementSetAttr(target, "name", gSessionData->mCoreName); mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); - mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { mxmlElementSetAttr(target, "supports_live", "yes"); diff --git a/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h index b0482f593c6f..efc1e52bdba3 100644 --- a/tools/gator/daemon/CapturedXML.h +++ b/tools/gator/daemon/CapturedXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp index 9ee2ef8afb9d..ca33561ffdca 100644 --- a/tools/gator/daemon/Child.cpp +++ b/tools/gator/daemon/Child.cpp @@ -1,38 +1,39 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "Child.h" + #include #include #include #include #include + #include "Logging.h" #include "CapturedXML.h" #include "SessionData.h" -#include "Child.h" #include "LocalCapture.h" -#include "Collector.h" #include "Sender.h" #include "OlyUtility.h" +#include "OlySocket.h" #include "StreamlineSetup.h" #include "ConfigurationXML.h" #include "Driver.h" -#include "Fifo.h" -#include "Buffer.h" - -#define NS_PER_S ((uint64_t)1000000000) -#define NS_PER_US 1000 +#include "PerfSource.h" +#include "DriverSource.h" +#include "UserSpaceSource.h" +#include "ExternalSource.h" static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads -static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads -static Buffer* buffer = NULL; +static Source *primarySource = NULL; +static Source *userSpaceSource = NULL; +static Source *externalSource = NULL; static Sender* sender = NULL; // Shared by Child.cpp and spawned threads -static Collector* collector = NULL; Child* child = NULL; // shared by Child.cpp and main.cpp extern void cleanUp(); @@ -78,7 +79,7 @@ static void child_handler(int signum) { } beenHere = true; logg->logMessage("Gator is shutting down."); - if (signum == SIGALRM || !collector) { + if (signum == SIGALRM || !primarySource) { exit(1); } else { child->endSession(); @@ -139,77 +140,22 @@ static void *stopThread(void *) { return 0; } -static void *countersThread(void *) { - prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); - - gSessionData->hwmon.start(); - - int64_t monotonic_started = 0; - while (monotonic_started <= 0) { - usleep(10); - - if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); - handleException(); - } - } - - uint64_t next_time = 0; - while (gSessionData->mSessionIsActive) { - struct timespec ts; -#ifndef CLOCK_MONOTONIC_RAW - // Android doesn't have this defined but it was added in Linux 2.6.28 -#define CLOCK_MONOTONIC_RAW 4 -#endif - if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { - logg->logError(__FILE__, __LINE__, "Failed to get uptime"); - handleException(); - } - const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; - // Sample ten times a second ignoring gSessionData->mSampleRate - next_time += NS_PER_S/10;//gSessionData->mSampleRate; - if (next_time < curr_time) { - logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); - next_time = curr_time; - } - - if (buffer->eventHeader(curr_time)) { - gSessionData->hwmon.read(buffer); - // Only check after writing all counters so that time and corresponding counters appear in the same frame - buffer->check(curr_time); - } - - if (buffer->bytesAvailable() <= 0) { - logg->logMessage("One shot (counters)"); - child->endSession(); - } - - usleep((next_time - curr_time)/NS_PER_US); - } - - buffer->setDone(); - - return NULL; -} - static void *senderThread(void *) { - int length = 1; - char* data; char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; sem_post(&senderThreadStarted); prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); sem_wait(&haltPipeline); - while (length > 0 || !buffer->isDone()) { + while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) { sem_wait(&senderSem); - data = collectorFifo->read(&length); - if (data != NULL) { - sender->writeData(data, length, RESPONSE_APC_DATA); - collectorFifo->release(); + + primarySource->write(sender); + if (userSpaceSource != NULL) { + userSpaceSource->write(sender); } - if (!buffer->isDone()) { - buffer->write(sender); + if (externalSource != NULL) { + externalSource->write(sender); } } @@ -255,15 +201,13 @@ void Child::initialization() { void Child::endSession() { gSessionData->mSessionIsActive = false; - collector->stop(); + primarySource->interrupt(); sem_post(&haltPipeline); } void Child::run() { - char* collectBuffer; - int bytesCollected = 0; LocalCapture* localCapture = NULL; - pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; + pthread_t durationThreadID, stopThreadID, senderThreadID; prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); @@ -282,7 +226,11 @@ void Child::run() { { ConfigurationXML configuration; } // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated - collector = new Collector(); + if (!gSessionData->perf.isSetup()) { + primarySource = new DriverSource(&senderSem, &startProfile); + } else { + primarySource = new PerfSource(&senderSem, &startProfile); + } // Initialize all drivers for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { @@ -317,15 +265,11 @@ void Child::run() { free(xmlString); } - // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length - logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); - collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); - - // Get the initial pointer to the collect buffer - collectBuffer = collectorFifo->start(); - - // Create a new Block Counter Buffer - buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); + // Must be after session XML is parsed + if (!primarySource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } // Sender thread shall be halted until it is signaled for one shot mode sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); @@ -340,14 +284,21 @@ void Child::run() { thread_creation_success = false; } - bool startcountersThread = gSessionData->hwmon.countersEnabled(); - if (startcountersThread) { - if (pthread_create(&countersThreadID, NULL, countersThread, this)) { - thread_creation_success = false; + if (gSessionData->hwmon.countersEnabled()) { + userSpaceSource = new UserSpaceSource(&senderSem); + if (!userSpaceSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); } - } else { - // Let senderThread know there is no buffer data to send - buffer->setDone(); + userSpaceSource->start(); + } + if (access("/tmp/gator", F_OK) == 0) { + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + externalSource->start(); } if (!thread_creation_success) { @@ -359,28 +310,13 @@ void Child::run() { sem_wait(&senderThreadStarted); // Start profiling - logg->logMessage("********** Profiling started **********"); - collector->start(); - sem_post(&startProfile); + primarySource->run(); - // Collect Data - do { - // This command will stall until data is received from the driver - bytesCollected = collector->collect(collectBuffer); - - // In one shot mode, stop collection once all the buffers are filled - if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { - if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { - logg->logMessage("One shot"); - endSession(); - } - } - collectBuffer = collectorFifo->write(bytesCollected); - } while (bytesCollected > 0); - logg->logMessage("Exit collect data loop"); - - if (startcountersThread) { - pthread_join(countersThreadID, NULL); + if (externalSource != NULL) { + externalSource->join(); + } + if (userSpaceSource != NULL) { + userSpaceSource->join(); } // Wait for the other threads to exit @@ -401,9 +337,9 @@ void Child::run() { logg->logMessage("Profiling ended."); - delete buffer; - delete collectorFifo; + delete externalSource; + delete userSpaceSource; + delete primarySource; delete sender; - delete collector; delete localCapture; } diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h index 0330e9d78027..9e206d7113b8 100644 --- a/tools/gator/daemon/Child.h +++ b/tools/gator/daemon/Child.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,8 +9,6 @@ #ifndef __CHILD_H__ #define __CHILD_H__ -#include - class OlySocket; class Child { diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h deleted file mode 100644 index c5e9eac573dd..000000000000 --- a/tools/gator/daemon/Collector.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __COLLECTOR_H__ -#define __COLLECTOR_H__ - -#include - -class Collector { -public: - Collector(); - ~Collector(); - void start(); - void stop(); - int collect(char* buffer); - int getBufferSize() {return mBufferSize;} - - static int readIntDriver(const char* path, int* value); - static int readInt64Driver(const char* path, int64_t* value); - static int writeDriver(const char* path, int value); - static int writeDriver(const char* path, int64_t value); - static int writeDriver(const char* path, const char* data); - static int writeReadDriver(const char* path, int* value); - static int writeReadDriver(const char* path, int64_t* value); - -private: - int mBufferSize; - int mBufferFD; - - void checkVersion(); -}; - -#endif //__COLLECTOR_H__ diff --git a/tools/gator/daemon/Config.h b/tools/gator/daemon/Config.h new file mode 100644 index 000000000000..6f5e2aae50e1 --- /dev/null +++ b/tools/gator/daemon/Config.h @@ -0,0 +1,17 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define ARRAY_LENGTH(A) static_cast(sizeof(A)/sizeof((A)[0])) + +#define MAX_PERFORMANCE_COUNTERS 50 +#define NR_CPUS 16 + +#endif // CONFIG_H diff --git a/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp index 2a5252a5bb04..fd479f2452cd 100644 --- a/tools/gator/daemon/ConfigurationXML.cpp +++ b/tools/gator/daemon/ConfigurationXML.cpp @@ -1,15 +1,17 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "ConfigurationXML.h" + #include #include #include -#include "ConfigurationXML.h" + #include "Driver.h" #include "Logging.h" #include "OlyUtility.h" @@ -67,6 +69,7 @@ int ConfigurationXML::parse(const char* configurationXML) { // clear counter overflow gSessionData->mCounterOverflow = 0; + gSessionData->mIsEBS = false; mIndex = 0; // disable all counters prior to parsing the configuration xml @@ -155,6 +158,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); + if (counter.getCount() > 0) { + gSessionData->mIsEBS = true; + } counter.setEnabled(true); // Associate a driver with each counter @@ -181,9 +187,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { } void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { -#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len - xml = (const char *)configuration_xml; - len = configuration_xml_len; +#include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len + xml = (const char *)defaults_xml; + len = defaults_xml_len; } void ConfigurationXML::getPath(char* path) { diff --git a/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h index 5650f487b990..efa415e508b6 100644 --- a/tools/gator/daemon/ConfigurationXML.h +++ b/tools/gator/daemon/ConfigurationXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h index 231a85d6e3b5..689174573e4e 100644 --- a/tools/gator/daemon/Counter.h +++ b/tools/gator/daemon/Counter.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -25,7 +25,7 @@ public: void clear () { mType[0] = '\0'; mEnabled = false; - mEvent = 0; + mEvent = -1; mCount = 0; mKey = 0; mDriver = NULL; diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp index c262467dc219..09e040162912 100644 --- a/tools/gator/daemon/Driver.cpp +++ b/tools/gator/daemon/Driver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h index f3a932f852cb..e5ed7b6c1295 100644 --- a/tools/gator/daemon/Driver.h +++ b/tools/gator/daemon/Driver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -27,7 +27,7 @@ public: virtual void setupCounter(Counter &counter) = 0; // Emits available counters - virtual void writeCounters(mxml_node_t *root) const = 0; + virtual int writeCounters(mxml_node_t *root) const = 0; // Emits possible dynamically generated events/counters virtual void writeEvents(mxml_node_t *) const {} diff --git a/tools/gator/daemon/Collector.cpp b/tools/gator/daemon/DriverSource.cpp similarity index 58% rename from tools/gator/daemon/Collector.cpp rename to tools/gator/daemon/DriverSource.cpp index bf73534692a0..f78ec6b7ce41 100644 --- a/tools/gator/daemon/Collector.cpp +++ b/tools/gator/daemon/DriverSource.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -8,53 +8,21 @@ #define __STDC_FORMAT_MACROS +#include "DriverSource.h" + #include -#include -#include -#include -#include -#include #include -#include "Collector.h" -#include "SessionData.h" +#include + +#include "Child.h" +#include "Fifo.h" #include "Logging.h" #include "Sender.h" +#include "SessionData.h" -// Driver initialization independent of session settings -Collector::Collector() { - mBufferFD = 0; +extern Child *child; - checkVersion(); - - int enable = -1; - if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { - logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); - handleException(); - } - - readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); - if (gSessionData->mCores == 0) { - gSessionData->mCores = 1; - } - - mBufferSize = 0; - if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { - logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); - handleException(); - } -} - -Collector::~Collector() { - // Write zero for safety, as a zero should have already been written - writeDriver("/dev/gator/enable", "0"); - - // Calls event_buffer_release in the driver - if (mBufferFD) { - close(mBufferFD); - } -} - -void Collector::checkVersion() { +DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { int driver_version = 0; if (readIntDriver("/dev/gator/version", &driver_version) == -1) { @@ -81,9 +49,51 @@ void Collector::checkVersion() { handleException(); } } + + int enable = -1; + if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { + logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + handleException(); + } + + readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); + if (gSessionData->mCores == 0) { + gSessionData->mCores = 1; + } + + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + handleException(); + } } -void Collector::start() { +DriverSource::~DriverSource() { + delete mFifo; + + // Write zero for safety, as a zero should have already been written + writeDriver("/dev/gator/enable", "0"); + + // Calls event_buffer_release in the driver + if (mBufferFD) { + close(mBufferFD); + } +} + +bool DriverSource::prepare() { + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); + mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); + + return true; +} + +void DriverSource::run() { + // Get the initial pointer to the collect buffer + char *collectBuffer = mFifo->start(); + int bytesCollected = 0; + + logg->logMessage("********** Profiling started **********"); + // Set the maximum backtrace depth if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); @@ -125,79 +135,112 @@ void Collector::start() { } lseek(mBufferFD, 0, SEEK_SET); + + sem_post(mStartProfile); + + // Collect Data + do { + // This command will stall until data is received from the driver + // Calls event_buffer_read in the driver + errno = 0; + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + + // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data + if (bytesCollected == -1 && errno == EINTR) { + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + } + + // return the total bytes written + logg->logMessage("Driver read of %d bytes", bytesCollected); + + // In one shot mode, stop collection once all the buffers are filled + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { + logg->logMessage("One shot"); + child->endSession(); + } + } + collectBuffer = mFifo->write(bytesCollected); + } while (bytesCollected > 0); + + logg->logMessage("Exit collect data loop"); } -// These commands should cause the read() function in collect() to return -void Collector::stop() { - // This will stop the driver from profiling +void DriverSource::interrupt() { + // This command should cause the read() function in collect() to return and stop the driver from profiling if (writeDriver("/dev/gator/enable", "0") != 0) { logg->logMessage("Stopping kernel failed"); } } -int Collector::collect(char* buffer) { - // Calls event_buffer_read in the driver - int bytesRead; +bool DriverSource::isDone() { + return mLength <= 0; +} +void DriverSource::write(Sender *sender) { + char *data = mFifo->read(&mLength); + if (data != NULL) { + sender->writeData(data, mLength, RESPONSE_APC_DATA); + mFifo->release(); + } +} + +int DriverSource::readIntDriver(const char *fullpath, int *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { + return -1; + } + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { + return -1; + } + data[bytes] = '\0'; + + char *endptr; errno = 0; - bytesRead = read(mBufferFD, buffer, mBufferSize); - - // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data - if (bytesRead == -1 && errno == EINTR) { - bytesRead = read(mBufferFD, buffer, mBufferSize); - } - - // return the total bytes written - logg->logMessage("Driver read of %d bytes", bytesRead); - return bytesRead; -} - -int Collector::readIntDriver(const char* fullpath, int* value) { - FILE* file = fopen(fullpath, "r"); - if (file == NULL) { - return -1; - } - if (fscanf(file, "%u", value) != 1) { - fclose(file); + *value = strtol(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { logg->logMessage("Invalid value in file %s", fullpath); return -1; } - fclose(file); + return 0; } -int Collector::readInt64Driver(const char* fullpath, int64_t* value) { - FILE* file = fopen(fullpath, "r"); - if (file == NULL) { +int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { return -1; } - if (fscanf(file, "%" SCNi64, value) != 1) { - fclose(file); + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { + return -1; + } + data[bytes] = '\0'; + + char *endptr; + errno = 0; + *value = strtoll(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { logg->logMessage("Invalid value in file %s", fullpath); return -1; } - fclose(file); + return 0; } -int Collector::writeDriver(const char* path, int value) { - char data[40]; // Sufficiently large to hold any integer - snprintf(data, sizeof(data), "%d", value); - return writeDriver(path, data); -} - -int Collector::writeDriver(const char* path, int64_t value) { - char data[40]; // Sufficiently large to hold any integer - snprintf(data, sizeof(data), "%" PRIi64, value); - return writeDriver(path, data); -} - -int Collector::writeDriver(const char* fullpath, const char* data) { +int DriverSource::writeDriver(const char *fullpath, const char *data) { int fd = open(fullpath, O_WRONLY); if (fd < 0) { return -1; } - if (write(fd, data, strlen(data)) < 0) { + if (::write(fd, data, strlen(data)) < 0) { close(fd); logg->logMessage("Opened but could not write to %s", fullpath); return -1; @@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) { return 0; } -int Collector::writeReadDriver(const char* path, int* value) { +int DriverSource::writeDriver(const char *path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int DriverSource::writeDriver(const char *path, int64_t value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%" PRIi64, value); + return writeDriver(path, data); +} + +int DriverSource::writeReadDriver(const char *path, int *value) { if (writeDriver(path, *value) || readIntDriver(path, value)) { return -1; } return 0; } -int Collector::writeReadDriver(const char* path, int64_t* value) { +int DriverSource::writeReadDriver(const char *path, int64_t *value) { if (writeDriver(path, *value) || readInt64Driver(path, value)) { return -1; } diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h new file mode 100644 index 000000000000..dcf1078a239c --- /dev/null +++ b/tools/gator/daemon/DriverSource.h @@ -0,0 +1,52 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef DRIVERSOURCE_H +#define DRIVERSOURCE_H + +#include +#include + +#include "Source.h" + +class Fifo; + +class DriverSource : public Source { +public: + DriverSource(sem_t *senderSem, sem_t *startProfile); + ~DriverSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + + static int readIntDriver(const char *fullpath, int *value); + static int readInt64Driver(const char *fullpath, int64_t *value); + static int writeDriver(const char *fullpath, const char *data); + static int writeDriver(const char *path, int value); + static int writeDriver(const char *path, int64_t value); + static int writeReadDriver(const char *path, int *value); + static int writeReadDriver(const char *path, int64_t *value); + +private: + Fifo *mFifo; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mBufferSize; + int mBufferFD; + int mLength; + + // Intentionally unimplemented + DriverSource(const DriverSource &); + DriverSource &operator=(const DriverSource &); +}; + +#endif // DRIVERSOURCE_H diff --git a/tools/gator/daemon/DynBuf.cpp b/tools/gator/daemon/DynBuf.cpp new file mode 100644 index 000000000000..6f92b336ae19 --- /dev/null +++ b/tools/gator/daemon/DynBuf.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "DynBuf.h" + +#include +#include +#include +#include +#include + +#include "Logging.h" + +// Pick an aggressive size as buffer is primarily used for disk IO +#define MIN_BUFFER_FREE (1 << 12) + +int DynBuf::resize(const size_t minCapacity) { + size_t scaledCapacity = 2 * capacity; + if (scaledCapacity < minCapacity) { + scaledCapacity = minCapacity; + } + if (scaledCapacity < 2 * MIN_BUFFER_FREE) { + scaledCapacity = 2 * MIN_BUFFER_FREE; + } + capacity = scaledCapacity; + + buf = static_cast(realloc(buf, capacity)); + if (buf == NULL) { + return -errno; + } + + return 0; +} + +bool DynBuf::read(const char *const path) { + int result = false; + + const int fd = open(path, O_RDONLY); + if (fd < 0) { + logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + length = 0; + + for (;;) { + const size_t minCapacity = length + MIN_BUFFER_FREE + 1; + if (capacity < minCapacity) { + if (resize(minCapacity) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } else if (bytes == 0) { + break; + } + length += bytes; + } + + buf[length] = '\0'; + result = true; + + fail: + close(fd); + + return result; +} + +int DynBuf::readlink(const char *const path) { + ssize_t bytes = MIN_BUFFER_FREE; + + for (;;) { + if (static_cast(bytes) >= capacity) { + const int err = resize(2 * bytes); + if (err != 0) { + return err; + } + } + bytes = ::readlink(path, buf, capacity); + if (bytes < 0) { + return -errno; + } else if (static_cast(bytes) < capacity) { + break; + } + } + + length = bytes; + buf[bytes] = '\0'; + + return 0; +} + +bool DynBuf::printf(const char *format, ...) { + va_list ap; + + if (capacity <= 0) { + if (resize(2 * MIN_BUFFER_FREE) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + va_start(ap, format); + int bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (static_cast(bytes) > capacity) { + if (resize(bytes + 1) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + va_start(ap, format); + bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + length = bytes; + + return true; +} diff --git a/tools/gator/daemon/DynBuf.h b/tools/gator/daemon/DynBuf.h new file mode 100644 index 000000000000..2f4554ab2e49 --- /dev/null +++ b/tools/gator/daemon/DynBuf.h @@ -0,0 +1,52 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef DYNBUF_H +#define DYNBUF_H + +#include + +class DynBuf { +public: + DynBuf() : capacity(0), length(0), buf(NULL) {} + ~DynBuf() { + reset(); + } + + inline void reset() { + capacity = 0; + length = 0; + if (buf != NULL) { + free(buf); + buf = NULL; + } + } + + bool read(const char *const path); + // On error instead of printing the error and returning false, this returns -errno + int readlink(const char *const path); + __attribute__ ((format(printf, 2, 3))) + bool printf(const char *format, ...); + + size_t getLength() const { return length; } + const char *getBuf() const { return buf; } + char *getBuf() { return buf; } + +private: + int resize(const size_t minCapacity); + + size_t capacity; + size_t length; + char *buf; + + // Intentionally undefined + DynBuf(const DynBuf &); + DynBuf &operator=(const DynBuf &); +}; + +#endif // DYNBUF_H diff --git a/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp index 2a80482e0b8d..a07a046f3353 100644 --- a/tools/gator/daemon/EventsXML.cpp +++ b/tools/gator/daemon/EventsXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -35,7 +35,7 @@ char* EventsXML::getXML() { fclose(fl); } else { logg->logMessage("Unable to locate events.xml, using default"); - xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); + xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); } // Add dynamic events from the drivers diff --git a/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h index 8e693efab202..6cd1560f7d4e 100644 --- a/tools/gator/daemon/EventsXML.h +++ b/tools/gator/daemon/EventsXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp new file mode 100644 index 000000000000..fe5824b04812 --- /dev/null +++ b/tools/gator/daemon/ExternalSource.cpp @@ -0,0 +1,56 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "ExternalSource.h" + +#include + +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") { +} + +ExternalSource::~ExternalSource() { +} + +bool ExternalSource::prepare() { + return true; +} + +void ExternalSource::run() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0); + + while (gSessionData->mSessionIsActive) { + // Will be aborted when the socket is closed at the end of the capture + int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable()); + if (length <= 0) { + break; + } + + mBuffer.advanceWrite(length); + mBuffer.check(0); + } + + mBuffer.setDone(); +} + +void ExternalSource::interrupt() { + // Do nothing +} + +bool ExternalSource::isDone() { + return mBuffer.isDone(); +} + +void ExternalSource::write(Sender *sender) { + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } +} diff --git a/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h new file mode 100644 index 000000000000..2052bdf2823e --- /dev/null +++ b/tools/gator/daemon/ExternalSource.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef EXTERNALSOURCE_H +#define EXTERNALSOURCE_H + +#include + +#include "Buffer.h" +#include "OlySocket.h" +#include "Source.h" + +// Unix domain socket counters from external sources like graphics drivers +class ExternalSource : public Source { +public: + ExternalSource(sem_t *senderSem); + ~ExternalSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + Buffer mBuffer; + OlySocket mSock; + + // Intentionally unimplemented + ExternalSource(const ExternalSource &); + ExternalSource &operator=(const ExternalSource &); +}; + +#endif // EXTERNALSOURCE_H diff --git a/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp index 250a4d023bf2..f672e92a6807 100644 --- a/tools/gator/daemon/Fifo.cpp +++ b/tools/gator/daemon/Fifo.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h index d25cd6882561..7dd7426132d8 100644 --- a/tools/gator/daemon/Fifo.h +++ b/tools/gator/daemon/Fifo.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,7 +12,7 @@ #ifdef WIN32 #include #define sem_t HANDLE -#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL) +#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL) #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) #define sem_destroy(sem) CloseHandle(*(sem)) diff --git a/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp index 1d7c0da9cc83..778f30755dfe 100644 --- a/tools/gator/daemon/Hwmon.cpp +++ b/tools/gator/daemon/Hwmon.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,7 +17,7 @@ class HwmonCounter { public: - HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature); + HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature); ~HwmonCounter(); HwmonCounter *getNext() const { return next; } @@ -69,7 +69,7 @@ private: HwmonCounter &operator=(const HwmonCounter &); }; -HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { +HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; char *chip_name = new char[len]; @@ -205,6 +205,23 @@ bool HwmonCounter::canRead() { } Hwmon::Hwmon() : counters(NULL) { +} + +Hwmon::~Hwmon() { + while (counters != NULL) { + HwmonCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } + sensors_cleanup(); +} + +void Hwmon::setup() { + // hwmon does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + int err = sensors_init(NULL); if (err) { logg->logMessage("Failed to initialize libsensors! (%d)", err); @@ -218,20 +235,11 @@ Hwmon::Hwmon() : counters(NULL) { int feature_nr = 0; const sensors_feature *feature; while ((feature = sensors_get_features(chip, &feature_nr))) { - counters = new HwmonCounter(counters, getEventKey(), chip, feature); + counters = new HwmonCounter(counters, chip, feature); } } } -Hwmon::~Hwmon() { - while (counters != NULL) { - HwmonCounter * counter = counters; - counters = counter->getNext(); - delete counter; - } - sensors_cleanup(); -} - HwmonCounter *Hwmon::findCounter(const Counter &counter) const { for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { @@ -271,14 +279,18 @@ void Hwmon::setupCounter(Counter &counter) { counter.setKey(hwmonCounter->getKey()); } -void Hwmon::writeCounters(mxml_node_t *root) const { +int Hwmon::writeCounters(mxml_node_t *root) const { + int count = 0; for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { if (!counter->canRead()) { continue; } mxml_node_t *node = mxmlNewElement(root, "counter"); mxmlElementSetAttr(node, "name", counter->getName()); + ++count; } + + return count; } void Hwmon::writeEvents(mxml_node_t *root) const { diff --git a/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h index 46bb42e898d7..a22a3609f99f 100644 --- a/tools/gator/daemon/Hwmon.h +++ b/tools/gator/daemon/Hwmon.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,12 +19,14 @@ public: Hwmon(); ~Hwmon(); + void setup(); + bool claimCounter(const Counter &counter) const; bool countersEnabled() const; void resetCounters(); void setupCounter(Counter &counter); - void writeCounters(mxml_node_t *root) const; + int writeCounters(mxml_node_t *root) const; void writeEvents(mxml_node_t *root) const; void start(); diff --git a/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp index 559297fe2274..9300002f3fb2 100644 --- a/tools/gator/daemon/KMod.cpp +++ b/tools/gator/daemon/KMod.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,9 +12,9 @@ #include #include -#include "Collector.h" #include "ConfigurationXML.h" #include "Counter.h" +#include "DriverSource.h" #include "Logging.h" // Claim all the counters in /dev/gator/events @@ -38,9 +38,9 @@ void KMod::resetCounters() { continue; snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); snprintf(text, sizeof(text), "%s/enabled", base); - Collector::writeDriver(text, 0); + DriverSource::writeDriver(text, 0); snprintf(text, sizeof(text), "%s/count", base); - Collector::writeDriver(text, 0); + DriverSource::writeDriver(text, 0); } closedir(dir); } @@ -53,22 +53,22 @@ void KMod::setupCounter(Counter &counter) { snprintf(text, sizeof(text), "%s/enabled", base); int enabled = true; - if (Collector::writeReadDriver(text, &enabled) || !enabled) { + if (DriverSource::writeReadDriver(text, &enabled) || !enabled) { counter.setEnabled(false); return; } snprintf(text, sizeof(text), "%s/key", base); int key = 0; - Collector::readIntDriver(text, &key); + DriverSource::readIntDriver(text, &key); counter.setKey(key); snprintf(text, sizeof(text), "%s/event", base); - Collector::writeDriver(text, counter.getEvent()); + DriverSource::writeDriver(text, counter.getEvent()); snprintf(text, sizeof(text), "%s/count", base); if (access(text, F_OK) == 0) { int count = counter.getCount(); - if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { + if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); handleException(); } @@ -80,23 +80,26 @@ void KMod::setupCounter(Counter &counter) { } } -void KMod::writeCounters(mxml_node_t *root) const { +int KMod::writeCounters(mxml_node_t *root) const { struct dirent *ent; mxml_node_t *counter; // counters.xml is simply a file listing of /dev/gator/events DIR* dir = opendir("/dev/gator/events"); if (dir == NULL) { - logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); - handleException(); + return 0; } + int count = 0; while ((ent = readdir(dir)) != NULL) { // skip hidden files, current dir, and parent dir if (ent->d_name[0] == '.') continue; counter = mxmlNewElement(root, "counter"); mxmlElementSetAttr(counter, "name", ent->d_name); + ++count; } closedir(dir); + + return count; } diff --git a/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h index 797426290dfc..fb7fc8a8f9c6 100644 --- a/tools/gator/daemon/KMod.h +++ b/tools/gator/daemon/KMod.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * Copyright (C) ARM Limited 2013-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,7 +21,7 @@ public: void resetCounters(); void setupCounter(Counter &counter); - void writeCounters(mxml_node_t *root) const; + int writeCounters(mxml_node_t *root) const; }; #endif // KMOD_H diff --git a/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp index 3235a34ae9c7..d2a4b799d7ac 100644 --- a/tools/gator/daemon/LocalCapture.cpp +++ b/tools/gator/daemon/LocalCapture.cpp @@ -1,18 +1,20 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "LocalCapture.h" + #include #include #include #include #include #include -#include "LocalCapture.h" + #include "SessionData.h" #include "Logging.h" #include "OlyUtility.h" diff --git a/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h index 8042d6a8dc37..aadeccecf0cc 100644 --- a/tools/gator/daemon/LocalCapture.h +++ b/tools/gator/daemon/LocalCapture.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp index 5fd45b54f90a..b8d3178950d6 100644 --- a/tools/gator/daemon/Logging.cpp +++ b/tools/gator/daemon/Logging.cpp @@ -1,11 +1,13 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "Logging.h" + #include #include #include @@ -23,8 +25,6 @@ #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) #endif -#include "Logging.h" - // Global thread-safe logging Logging* logg = NULL; diff --git a/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h index 8f960de27bf3..6ae328046989 100644 --- a/tools/gator/daemon/Logging.h +++ b/tools/gator/daemon/Logging.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,14 +9,7 @@ #ifndef __LOGGING_H__ #define __LOGGING_H__ -#include -#include -#include -#ifdef WIN32 -#include -#else #include -#endif #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" @@ -33,11 +26,7 @@ private: char mErrBuf[4096]; // Arbitrarily large buffer to hold a string char mLogBuf[4096]; // Arbitrarily large buffer to hold a string bool mDebug; -#ifdef WIN32 - HANDLE mLoggingMutex; -#else pthread_mutex_t mLoggingMutex; -#endif }; extern Logging* logg; diff --git a/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp new file mode 100644 index 000000000000..90d5c47706c7 --- /dev/null +++ b/tools/gator/daemon/Monitor.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Monitor.h" + +#include +#include +#include + +#include "Logging.h" + +Monitor::Monitor() : mFd(-1) { +} + +Monitor::~Monitor() { + if (mFd >= -1) { + close(mFd); + } +} + +bool Monitor::init() { + mFd = epoll_create(16); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool Monitor::add(const int fd) { + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.data.fd = fd; + event.events = EPOLLIN; + if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { + logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) { + int result = epoll_wait(mFd, events, maxevents, timeout); + if (result < 0) { + // Ignore if the call was interrupted as this will happen when SIGINT is received + if (errno == EINTR) { + result = 0; + } else { + logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); + } + } + + return result; +} diff --git a/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h new file mode 100644 index 000000000000..6e268b6e1bed --- /dev/null +++ b/tools/gator/daemon/Monitor.h @@ -0,0 +1,32 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MONITOR_H +#define MONITOR_H + +#include + +class Monitor { +public: + Monitor(); + ~Monitor(); + + bool init(); + bool add(const int fd); + int wait(struct epoll_event *const events, int maxevents, int timeout); + +private: + + int mFd; + + // Intentionally unimplemented + Monitor(const Monitor &); + Monitor &operator=(const Monitor &); +}; + +#endif // MONITOR_H diff --git a/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp index ab5c3c2c8938..26e4768f3934 100644 --- a/tools/gator/daemon/OlySocket.cpp +++ b/tools/gator/daemon/OlySocket.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,6 +15,7 @@ #else #include #include +#include #include #include #endif @@ -30,7 +31,7 @@ #define SHUTDOWN_RX_TX SHUT_RDWR #endif -OlySocket::OlySocket(int port, bool multiple) { +OlyServerSocket::OlyServerSocket(int port) { #ifdef WIN32 WSADATA wsaData; if (WSAStartup(0x0202, &wsaData) != 0) { @@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) { } #endif - if (multiple) { - createServerSocket(port); - } else { - createSingleServerConnection(port); + createServerSocket(port); +} + +OlySocket::OlySocket(int port, const char* host) { + createClientSocket(host, port); +} + +OlySocket::OlySocket(int socketID) : mSocketID(socketID) { +} + +#ifndef WIN32 + +OlyServerSocket::OlyServerSocket(const char* path) { + // Create socket + mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + unlink(path); + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + // Bind the socket to an address + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "Binding of server socket failed."); + handleException(); + } + + // Listen for connections on this socket + if (listen(mFDServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); } } -OlySocket::OlySocket(int port, char* host) { - mFDServer = 0; - createClientSocket(host, port); +OlySocket::OlySocket(const char* path) { + mSocketID = socket(PF_UNIX, SOCK_STREAM, 0); + if (mSocketID < 0) { + return; + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + close(mSocketID); + mSocketID = -1; + return; + } } +#endif + OlySocket::~OlySocket() { if (mSocketID > 0) { CLOSE_SOCKET(mSocketID); } } +OlyServerSocket::~OlyServerSocket() { + if (mFDServer > 0) { + CLOSE_SOCKET(mFDServer); + } +} + void OlySocket::shutdownConnection() { // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions shutdown(mSocketID, SHUTDOWN_RX_TX); @@ -70,7 +129,7 @@ void OlySocket::closeSocket() { } } -void OlySocket::closeServerSocket() { +void OlyServerSocket::closeServerSocket() { if (CLOSE_SOCKET(mFDServer) != 0) { logg->logError(__FILE__, __LINE__, "Failed to close server socket."); handleException(); @@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() { mFDServer = 0; } -void OlySocket::createClientSocket(char* hostname, int portno) { +void OlySocket::createClientSocket(const char* hostname, int portno) { #ifdef WIN32 // TODO: Implement for Windows #else @@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) { #endif } -void OlySocket::createSingleServerConnection(int port) { - createServerSocket(port); - - mSocketID = acceptConnection(); - closeServerSocket(); -} - -void OlySocket::createServerSocket(int port) { +void OlyServerSocket::createServerSocket(int port) { int family = AF_INET6; // Create socket @@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) { // mSocketID is always set to the most recently accepted connection // The user of this class should maintain the different socket connections, e.g. by forking the process -int OlySocket::acceptConnection() { +int OlyServerSocket::acceptConnection() { + int socketID; if (mFDServer <= 0) { logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); handleException(); } // Accept a connection, note that this call blocks until a client connects - mSocketID = accept(mFDServer, NULL, NULL); - if (mSocketID < 0) { + socketID = accept(mFDServer, NULL, NULL); + if (socketID < 0) { logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); handleException(); } - return mSocketID; + return socketID; } -void OlySocket::send(char* buffer, int size) { +void OlySocket::send(const char* buffer, int size) { if (size <= 0 || buffer == NULL) { return; } diff --git a/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h index 5bab7d1f4cc4..eab786b304bf 100644 --- a/tools/gator/daemon/OlySocket.h +++ b/tools/gator/daemon/OlySocket.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,27 +9,44 @@ #ifndef __OLY_SOCKET_H__ #define __OLY_SOCKET_H__ -#include - class OlySocket { public: - OlySocket(int port, bool multipleConnections = false); - OlySocket(int port, char* hostname); + OlySocket(int port, const char* hostname); + OlySocket(int socketID); +#ifndef WIN32 + OlySocket(const char* path); +#endif ~OlySocket(); - int acceptConnection(); + void closeSocket(); - void closeServerSocket(); void shutdownConnection(); - void send(char* buffer, int size); - void sendString(const char* string) {send((char*)string, strlen(string));} + void send(const char* buffer, int size); int receive(char* buffer, int size); int receiveNBytes(char* buffer, int size); int receiveString(char* buffer, int size); - int getSocketID() {return mSocketID;} + + bool isValid() const { return mSocketID >= 0; } + private: - int mSocketID, mFDServer; - void createClientSocket(char* hostname, int port); - void createSingleServerConnection(int port); + int mSocketID; + + void createClientSocket(const char* hostname, int port); +}; + +class OlyServerSocket { +public: + OlyServerSocket(int port); +#ifndef WIN32 + OlyServerSocket(const char* path); +#endif + ~OlyServerSocket(); + + int acceptConnection(); + void closeServerSocket(); + +private: + int mFDServer; + void createServerSocket(int port); }; diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp index 0b22d6ebd027..45340a27d9fa 100644 --- a/tools/gator/daemon/OlyUtility.cpp +++ b/tools/gator/daemon/OlyUtility.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h index abab0a510a7c..1d26beb596fa 100644 --- a/tools/gator/daemon/OlyUtility.h +++ b/tools/gator/daemon/OlyUtility.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/PerfBuffer.cpp b/tools/gator/daemon/PerfBuffer.cpp new file mode 100644 index 000000000000..5fad583f7bd0 --- /dev/null +++ b/tools/gator/daemon/PerfBuffer.cpp @@ -0,0 +1,139 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "PerfBuffer.h" + +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +PerfBuffer::PerfBuffer() { + for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) { + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + } +} + +PerfBuffer::~PerfBuffer() { + for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { + if (mBuf[cpu] != MAP_FAILED) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + } + } +} + +bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) { + if (fd == groupFd) { + if (mBuf[cpu] != MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); + return false; + } + + // The buffer isn't mapped yet + mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Check the version + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + if (pemp->compat_version != 0) { + logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } else { + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +void PerfBuffer::discard(const int cpu) { + if (mBuf[cpu] != MAP_FAILED) { + mDiscard[cpu] = true; + } +} + +bool PerfBuffer::isEmpty() { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] != MAP_FAILED) { + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head != tail) { + return false; + } + } + } + + return true; +} + +bool PerfBuffer::send(Sender *const sender) { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] == MAP_FAILED) { + continue; + } + + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head > tail) { + const uint8_t *const b = static_cast(mBuf[cpu]) + gSessionData->mPageSize; + const int offset = gSessionData->mLocalCapture ? 1 : 0; + unsigned char header[7]; + header[0] = RESPONSE_APC_DATA; + Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5); + // Should use real packing functions + header[5] = FRAME_PERF; + header[6] = cpu; + + // Write header + sender->writeData(reinterpret_cast(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA); + + // Write data + if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) { + // Not wrapped + sender->writeData(reinterpret_cast(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA); + } else { + // Wrapped + sender->writeData(reinterpret_cast(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA); + sender->writeData(reinterpret_cast(b), head & BUF_MASK, RESPONSE_APC_DATA); + } + + // Update tail with the data read + pemp->data_tail = head; + } + + if (mDiscard[cpu]) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + } + } + + return true; +} diff --git a/tools/gator/daemon/PerfBuffer.h b/tools/gator/daemon/PerfBuffer.h new file mode 100644 index 000000000000..278a3b9d6db7 --- /dev/null +++ b/tools/gator/daemon/PerfBuffer.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PERF_BUFFER +#define PERF_BUFFER + +#include "Config.h" + +#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) +#define BUF_MASK (BUF_SIZE - 1) + +class Sender; + +class PerfBuffer { +public: + PerfBuffer(); + ~PerfBuffer(); + + bool useFd(const int cpu, const int fd, const int groupFd); + void discard(const int cpu); + bool isEmpty(); + bool send(Sender *const sender); + +private: + void *mBuf[NR_CPUS]; + // After the buffer is flushed it should be unmaped + bool mDiscard[NR_CPUS]; + + // Intentionally undefined + PerfBuffer(const PerfBuffer &); + PerfBuffer &operator=(const PerfBuffer &); +}; + +#endif // PERF_BUFFER diff --git a/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp new file mode 100644 index 000000000000..8e25c22f6798 --- /dev/null +++ b/tools/gator/daemon/PerfDriver.cpp @@ -0,0 +1,355 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "PerfDriver.h" + +#include +#include +#include + +#include "Buffer.h" +#include "Config.h" +#include "ConfigurationXML.h" +#include "Counter.h" +#include "DriverSource.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfGroup.h" +#include "SessionData.h" + +#define PERF_DEVICES "/sys/bus/event_source/devices" + +#define TYPE_DERIVED ~0U + +// From gator.h +struct gator_cpu { + const int cpuid; + // Human readable name + const char core_name[32]; + // gatorfs event and Perf PMU name + const char *const pmnc_name; + const int pmnc_counters; +}; + +// From gator_main.c +static const struct gator_cpu gator_cpus[] = { + { 0xb36, "ARM1136", "ARM_ARM11", 3 }, + { 0xb56, "ARM1156", "ARM_ARM11", 3 }, + { 0xb76, "ARM1176", "ARM_ARM11", 3 }, + { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, + { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, + { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, + { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, + { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, + { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 }, + { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, + { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, + { 0x00f, "Scorpion", "Scorpion", 4 }, + { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, + { 0x049, "KraitSIM", "Krait", 4 }, + { 0x04d, "Krait", "Krait", 4 }, + { 0x06f, "Krait S4 Pro", "Krait", 4 }, + { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, + { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, + { 0xd0f, "AArch64", "ARM_AArch64", 6 }, +}; + +static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; +static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; + +class PerfCounter { +public: + 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) {} + ~PerfCounter() { + delete [] mName; + } + + PerfCounter *getNext() const { return mNext; } + const char *getName() const { return mName; } + uint32_t getType() const { return mType; } + int getCount() const { return mCount; } + void setCount(const int count) { mCount = count; } + int getKey() const { return mKey; } + uint64_t getConfig() const { return mConfig; } + void setConfig(const uint64_t config) { mConfig = config; } + bool isEnabled() const { return mEnabled; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + +private: + PerfCounter *const mNext; + const char *const mName; + const uint32_t mType; + int mCount; + const int mKey; + uint64_t mConfig; + bool mEnabled; +}; + +PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) { +} + +PerfDriver::~PerfDriver() { + while (mCounters != NULL) { + PerfCounter *counter = mCounters; + mCounters = counter->getNext(); + delete counter; + } +} + +void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { + int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + char *name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + mCounters = new PerfCounter(mCounters, name, type, -1); + + for (int j = 0; j < numCounters; ++j) { + len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; + name = new char[len]; + snprintf(name, len, "%s_cnt%d", counterName, j); + mCounters = new PerfCounter(mCounters, name, type, -1); + } +} + +// From include/generated/uapi/linux/version.h +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +bool PerfDriver::setup() { + // Check the kernel version + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + int release[3] = { 0, 0, 0 }; + int part = 0; + char *ch = utsname.release; + while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) { + release[part] = 10*release[part] + *ch - '0'; + + ++ch; + if (*ch == '.') { + ++part; + ++ch; + } + } + + if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) { + logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Add supported PMUs + bool foundCpu = false; + DIR *dir = opendir(PERF_DEVICES); + if (dir == NULL) { + logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + // Do the names match exactly? + if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 && + // Do these names match but have the old vs new prefix? + (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || + strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || + strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) { + continue; + } + + int type; + char buf[256]; + snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); + if (DriverSource::readIntDriver(buf, &type) != 0) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); + } + } + closedir(dir); + + if (!foundCpu) { + // If no cpu was found based on pmu names, try by cpuid + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); + } + } + + /* + if (!foundCpu) { + // If all else fails, use the perf architected counters + // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once + addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); + } + */ + + // Add supported software counters + long long id; + DynBuf printb; + + id = getTracepointId("irq/softirq_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id); + } + + id = getTracepointId("irq/irq_handler_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id); + } + + //Linux_block_rq_wr + //Linux_block_rq_rd + //Linux_net_rx + //Linux_net_tx + + id = getTracepointId(SCHED_SWITCH, &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id); + } + + //Linux_meminfo_memused + //Linux_meminfo_memfree + //Linux_meminfo_bufferram + //Linux_power_cpu_freq + //Linux_power_cpu_idle + + mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1); + + //Linux_cpu_wait_io + + mIsSetup = true; + return true; +} + +bool PerfDriver::summary(Buffer *const buffer) { + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + char buf[512]; + snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); + + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + + buffer->summary(timestamp, uptime, 0, buf); + + for (int i = 0; i < gSessionData->mCores; ++i) { + int j; + for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + break; + } + } + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); + } else { + snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); + buffer->coreName(i, gSessionData->mCpuIds[i], buf); + } + } + buffer->commit(1); + + return true; +} + +PerfCounter *PerfDriver::findCounter(const Counter &counter) const { + for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) { + if (strcmp(perfCounter->getName(), counter.getType()) == 0) { + return perfCounter; + } + } + + return NULL; +} + +bool PerfDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +void PerfDriver::resetCounters() { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void PerfDriver::setupCounter(Counter &counter) { + PerfCounter *const perfCounter = findCounter(counter); + if (perfCounter == NULL) { + counter.setEnabled(false); + return; + } + + // Don't use the config from counters XML if it's not set, ex: software counters + if (counter.getEvent() != -1) { + perfCounter->setConfig(counter.getEvent()); + } + perfCounter->setCount(counter.getCount()); + perfCounter->setEnabled(true); + counter.setKey(perfCounter->getKey()); +} + +int PerfDriver::writeCounters(mxml_node_t *root) const { + int count = 0; + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { + if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) { + logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return true; +} + +long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { + if (!printb->printf(EVENTS_PATH "/%s/id", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + int64_t result; + if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { + logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + return result; +} diff --git a/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h new file mode 100644 index 000000000000..3181b74f5570 --- /dev/null +++ b/tools/gator/daemon/PerfDriver.h @@ -0,0 +1,56 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PERFDRIVER_H +#define PERFDRIVER_H + +#include "Driver.h" + +// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH +#define DEBUGFS_PATH "/sys/kernel/debug" +#define EVENTS_PATH DEBUGFS_PATH "/tracing/events" + +#define SCHED_SWITCH "sched/sched_switch" + +class Buffer; +class DynBuf; +class PerfCounter; +class PerfGroup; + +class PerfDriver : public Driver { +public: + PerfDriver(); + ~PerfDriver(); + + bool setup(); + bool summary(Buffer *const buffer); + bool isSetup() const { return mIsSetup; } + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + bool enable(PerfGroup *group, Buffer *const buffer) const; + + static long long getTracepointId(const char *const name, DynBuf *const printb); + +private: + PerfCounter *findCounter(const Counter &counter) const; + void addCpuCounters(const char *const counterName, const int type, const int numCounters); + + PerfCounter *mCounters; + bool mIsSetup; + + // Intentionally undefined + PerfDriver(const PerfDriver &); + PerfDriver &operator=(const PerfDriver &); +}; + +#endif // PERFDRIVER_H diff --git a/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp new file mode 100644 index 000000000000..faf5fcaf15e6 --- /dev/null +++ b/tools/gator/daemon/PerfGroup.cpp @@ -0,0 +1,206 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "PerfGroup.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "Logging.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "SessionData.h" + +#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ + pea.size = sizeof(pea); \ + /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ + pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \ + /* Emit emit value in group format */ \ + pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ + /* start out disabled */ \ + pea.disabled = 1; \ + /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \ + pea.watermark = 1; \ + /* Be conservative in flush size as only one buffer set is monitored */ \ + pea.wakeup_watermark = 3 * BUF_SIZE / 4 + +static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) { + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { + memset(&mAttrs, 0, sizeof(mAttrs)); + memset(&mKeys, -1, sizeof(mKeys)); + memset(&mFds, -1, sizeof(mFds)); +} + +PerfGroup::~PerfGroup() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + close(mFds[pos]); + } + } +} + +bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { + int i; + for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + break; + } + } + + if (i >= ARRAY_LENGTH(mKeys)) { + logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + DEFAULT_PEA_ARGS(mAttrs[i], sampleType); + mAttrs[i].type = type; + mAttrs[i].config = config; + mAttrs[i].sample_period = sample; + // always be on the CPU but only a group leader can be pinned + mAttrs[i].pinned = (i == 0 ? 1 : 0); + mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0); + mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0); + mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); + mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); + mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); + + mKeys[i] = key; + + buffer->pea(&mAttrs[i], key); + + return true; +} + +bool PerfGroup::prepareCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + const int offset = i * gSessionData->mCores; + if (mFds[cpu + offset] >= 0) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %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); + mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); + if (mFds[cpu + offset] < 0) { + logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); + continue; + } + + if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) { + logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) { + __u64 ids[ARRAY_LENGTH(mKeys)]; + int coreKeys[ARRAY_LENGTH(mKeys)]; + int idCount = 0; + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + const int fd = mFds[cpu + i * gSessionData->mCores]; + if (fd < 0) { + continue; + } + + coreKeys[idCount] = mKeys[i]; + if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++idCount; + } + + if (!monitor->add(mFds[cpu])) { + logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + buffer->keys(idCount, ids, coreKeys); + + if (start) { + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return idCount; +} + +bool PerfGroup::offlineCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + // Mark the buffer so that it will be released next time it's read + mPb->discard(cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0) { + close(mFds[offset]); + mFds[offset] = -1; + } + } + + return true; +} + +bool PerfGroup::start() { + for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) { + if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + return true; + + fail: + stop(); + + return false; +} + +void PerfGroup::stop() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE); + } + } +} diff --git a/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h new file mode 100644 index 000000000000..af496d41334c --- /dev/null +++ b/tools/gator/daemon/PerfGroup.h @@ -0,0 +1,55 @@ + /** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PERF_GROUP +#define PERF_GROUP + +// Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways +#include "k/perf_event.h" + +#include "Config.h" + +class Buffer; +class Monitor; +class PerfBuffer; + +enum PerfGroupFlags { + PERF_GROUP_MMAP = 1 << 0, + PERF_GROUP_COMM = 1 << 1, + PERF_GROUP_FREQ = 1 << 2, + PERF_GROUP_TASK = 1 << 3, + PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, +}; + +class PerfGroup { +public: + PerfGroup(PerfBuffer *const pb); + ~PerfGroup(); + + bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); + // Safe to call concurrently + bool prepareCPU(const int cpu); + // Not safe to call concurrently. Returns the number of events enabled + int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor); + bool offlineCPU(int cpu); + bool start(); + void stop(); + +private: + // +1 for the group leader + struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; + int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; + int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; + PerfBuffer *const mPb; + + // Intentionally undefined + PerfGroup(const PerfGroup &); + PerfGroup &operator=(const PerfGroup &); +}; + +#endif // PERF_GROUP diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp new file mode 100644 index 000000000000..1f1cb1988f00 --- /dev/null +++ b/tools/gator/daemon/PerfSource.cpp @@ -0,0 +1,271 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "PerfSource.h" + +#include +#include +#include + +#include "Child.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfDriver.h" +#include "Proc.h" +#include "SessionData.h" + +#define MS_PER_US 1000000 + +extern Child *child; + +static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) { + if (!printb->printf(EVENTS_PATH "/%s/format", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (!b->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + buffer->format(b->getLength(), b->getBuf()); + + return true; +} + +PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { + long l = sysconf(_SC_PAGE_SIZE); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the page size"); + handleException(); + } + gSessionData->mPageSize = static_cast(l); + + l = sysconf(_SC_NPROCESSORS_CONF); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores"); + handleException(); + } + gSessionData->mCores = static_cast(l); +} + +PerfSource::~PerfSource() { +} + +struct PrepareParallelArgs { + PerfGroup *pg; + int cpu; +}; + +void *prepareParallel(void *arg) { + const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg; + args->pg->prepareCPU(args->cpu); + return NULL; +} + +bool PerfSource::prepare() { + DynBuf printb; + DynBuf b1; + DynBuf b2; + DynBuf b3; + long long schedSwitchId; + + if (0 + || !mMonitor.init() + || !mUEvent.init() + || !mMonitor.add(mUEvent.getFd()) + + || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0 + || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1) + + // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID + || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL) + + // Only want TID and IP but not RAW on timer + || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, 0)) + + || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) + || 0) { + logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.12 or later?", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!gSessionData->perf.summary(&mSummary)) { + logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + { + // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases + pthread_t threads[NR_CPUS]; + PrepareParallelArgs args[NR_CPUS]; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + args[cpu].pg = &mCountersGroup; + args[cpu].cpu = cpu; + if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) { + logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (pthread_join(threads[cpu], NULL) != 0) { + logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + int numEvents = 0; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor); + } + if (numEvents <= 0) { + logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Start events before reading proc to avoid race conditions + if (!mCountersGroup.start()) { + logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!readProc(&mBuffer, &printb, &b1, &b2, &b3)) { + logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + mBuffer.commit(1); + + return true; +} + +static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu"; + +void PerfSource::run() { + int pipefd[2]; + + if (pipe(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + handleException(); + } + + int timeout = -1; + if (gSessionData->mLiveRate > 0) { + timeout = gSessionData->mLiveRate/MS_PER_US; + } + + sem_post(mStartProfile); + + while (gSessionData->mSessionIsActive) { + // +1 for uevents, +1 for pipe + struct epoll_event events[NR_CPUS + 2]; + int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); + } + + for (int i = 0; i < ready; ++i) { + if (events[i].data.fd == mUEvent.getFd()) { + if (!handleUEvent()) { + logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); + handleException(); + } + break; + } + } + + // send a notification that data is ready + sem_post(mSenderSem); + + // In one shot mode, stop collection once all the buffers are filled + // Assume timeout == 0 in this case + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); + child->endSession(); + } + } + + mCountersGroup.stop(); + mBuffer.setDone(); + mIsDone = true; + + // send a notification that data is ready + sem_post(mSenderSem); + + mInterruptFd = -1; + close(pipefd[0]); + close(pipefd[1]); +} + +bool PerfSource::handleUEvent() { + UEventResult result; + if (!mUEvent.read(&result)) { + logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (strcmp(result.mSubsystem, "cpu") == 0) { + if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) { + logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__); + return false; + } + char *endptr; + errno = 0; + int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10); + if (errno != 0 || *endptr != '\0') { + logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (strcmp(result.mAction, "online") == 0) { + // Only call onlineCPU if prepareCPU succeeded + const bool result = mCountersGroup.prepareCPU(cpu) && + mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor); + mBuffer.commit(1); + return result; + } else if (strcmp(result.mAction, "offline") == 0) { + return mCountersGroup.offlineCPU(cpu); + } + } + + return true; +} + +void PerfSource::interrupt() { + if (mInterruptFd >= 0) { + int8_t c = 0; + // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread + if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { + logg->logError(__FILE__, __LINE__, "write failed"); + handleException(); + } + } +} + +bool PerfSource::isDone () { + return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty(); +} + +void PerfSource::write (Sender *sender) { + if (!mSummary.isDone()) { + mSummary.write(sender); + } + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } + if (!mCountersBuf.send(sender)) { + logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); + handleException(); + } +} diff --git a/tools/gator/daemon/PerfSource.h b/tools/gator/daemon/PerfSource.h new file mode 100644 index 000000000000..3f471c8de414 --- /dev/null +++ b/tools/gator/daemon/PerfSource.h @@ -0,0 +1,54 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PERFSOURCE_H +#define PERFSOURCE_H + +#include + +#include "Buffer.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "PerfGroup.h" +#include "Source.h" +#include "UEvent.h" + +class Sender; + +class PerfSource : public Source { +public: + PerfSource(sem_t *senderSem, sem_t *startProfile); + ~PerfSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + bool handleUEvent(); + + Buffer mSummary; + Buffer mBuffer; + PerfBuffer mCountersBuf; + PerfGroup mCountersGroup; + Monitor mMonitor; + UEvent mUEvent; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mInterruptFd; + bool mIsDone; + + // Intentionally undefined + PerfSource(const PerfSource &); + PerfSource &operator=(const PerfSource &); +}; + +#endif // PERFSOURCE_H diff --git a/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp new file mode 100644 index 000000000000..e0b9e2259cf9 --- /dev/null +++ b/tools/gator/daemon/Proc.cpp @@ -0,0 +1,179 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Proc.h" + +#include +#include +#include +#include +#include + +#include "Buffer.h" +#include "DynBuf.h" +#include "Logging.h" + +struct ProcStat { + // From linux-dev/include/linux/sched.h +#define TASK_COMM_LEN 16 + // TASK_COMM_LEN may grow, so be ready for it to get larger + char comm[2*TASK_COMM_LEN]; + long numThreads; +}; + +static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) { + if (!b->read(pathname)) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the thread just doesn't exist any more + return true; + } + + char *comm = strchr(b->getBuf(), '('); + if (comm == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++comm; + char *const str = strrchr(comm, ')'); + if (str == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + *str = '\0'; + strncpy(ps->comm, comm, sizeof(ps->comm) - 1); + ps->comm[sizeof(ps->comm) - 1] = '\0'; + + const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads); + if (count != 1) { + logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) { + bool result = false; + + if (!b->printf("/proc/%i/task", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + DIR *task = opendir(b->getBuf()); + if (task == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(task)) != NULL) { + char *endptr; + const int tid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore task items that are not integers like ., etc... + continue; + } + + if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + buffer->comm(pid, tid, image, ps.comm); + } + + result = true; + + fail: + closedir(task); + + return result; +} + +bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { + bool result = false; + + DIR *proc = opendir("/proc"); + if (proc == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(proc)) != NULL) { + char *endptr; + const int pid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore proc items that are not integers like ., cpuinfo, etc... + continue; + } + + if (!printb->printf("/proc/%i/stat", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b1)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + if (!printb->printf("/proc/%i/exe", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + const int err = b1->readlink(printb->getBuf()); + const char *image; + if (err == 0) { + image = strrchr(b1->getBuf(), '/'); + if (image == NULL) { + image = b1->getBuf(); + } else { + ++image; + } + } else if (err == -ENOENT) { + // readlink /proc/[pid]/exe returns ENOENT for kernel threads + image = "\0"; + } else { + logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + if (!printb->printf("/proc/%i/maps", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + if (!b2->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the process just doesn't exist any more + continue; + } + + buffer->maps(pid, pid, b2->getBuf()); + if (ps.numThreads <= 1) { + buffer->comm(pid, pid, image, ps.comm); + } else { + if (!readProcTask(buffer, pid, image, printb, b3)) { + logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + } + + result = true; + + fail: + closedir(proc); + + return result; +} diff --git a/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h new file mode 100644 index 000000000000..057b6109848a --- /dev/null +++ b/tools/gator/daemon/Proc.h @@ -0,0 +1,17 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PROC_H +#define PROC_H + +class Buffer; +class DynBuf; + +bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); + +#endif // PROC_H diff --git a/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp index 8eb348ff3a06..3a981a6427be 100644 --- a/tools/gator/daemon/Sender.cpp +++ b/tools/gator/daemon/Sender.cpp @@ -1,19 +1,18 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include -#include -#include -#include -#include -#include -#include #include "Sender.h" + +#include +#include +#include + +#include "Buffer.h" #include "Logging.h" #include "OlySocket.h" #include "SessionData.h" @@ -49,9 +48,12 @@ Sender::Sender(OlySocket* socket) { } Sender::~Sender() { - delete mDataSocket; - mDataSocket = NULL; - if (mDataFile) { + // Just close it as the client socket is on the stack + if (mDataSocket != NULL) { + mDataSocket->closeSocket(); + mDataSocket = NULL; + } + if (mDataFile != NULL) { fclose(mDataFile); } } @@ -95,10 +97,7 @@ void Sender::writeData(const char* data, int length, int type) { // type and length already added by the Collector for apc data unsigned char header[5]; header[0] = type; - header[1] = (length >> 0) & 0xff; - header[2] = (length >> 8) & 0xff; - header[3] = (length >> 16) & 0xff; - header[4] = (length >> 24) & 0xff; + Buffer::writeLEInt(header + 1, length); mDataSocket->send((char*)&header, sizeof(header)); } @@ -106,7 +105,7 @@ void Sender::writeData(const char* data, int length, int type) { const int chunkSize = 100*1000 * alarmDuration / 8; int pos = 0; while (true) { - mDataSocket->send((char*)data + pos, min(length - pos, chunkSize)); + mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize)); pos += chunkSize; if (pos >= length) { break; diff --git a/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h index b388f039bad7..4c359dba82f8 100644 --- a/tools/gator/daemon/Sender.h +++ b/tools/gator/daemon/Sender.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp index cf844075401f..c169299af872 100644 --- a/tools/gator/daemon/SessionData.cpp +++ b/tools/gator/daemon/SessionData.cpp @@ -1,13 +1,15 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include #include "SessionData.h" + +#include + #include "SessionXML.h" #include "Logging.h" @@ -38,6 +40,7 @@ void SessionData::initialize() { mTotalBufferSize = 0; // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module mCores = 1; + mPageSize = 0; } void SessionData::parseSessionXML(char* xmlString) { @@ -88,7 +91,8 @@ void SessionData::parseSessionXML(char* xmlString) { void SessionData::readCpuInfo() { char temp[256]; // arbitrarily large amount strcpy(mCoreName, "unknown"); - mCpuId = -1; + memset(&mCpuIds, -1, sizeof(mCpuIds)); + mMaxCpuId = -1; FILE* f = fopen("/proc/cpuinfo", "r"); if (f == NULL) { @@ -98,15 +102,16 @@ void SessionData::readCpuInfo() { } bool foundCoreName = false; - bool foundCpuId = false; - while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) { + int processor = 0; + while (fgets(temp, sizeof(temp), f)) { if (strlen(temp) > 0) { temp[strlen(temp) - 1] = 0; // Replace the line feed with a null } const bool foundHardware = strstr(temp, "Hardware") != 0; const bool foundCPUPart = strstr(temp, "CPU part") != 0; - if (foundHardware || foundCPUPart) { + const bool foundProcessor = strstr(temp, "processor") != 0; + if (foundHardware || foundCPUPart || foundProcessor) { char* position = strchr(temp, ':'); if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { logg->logMessage("Unknown format of /proc/cpuinfo\n" @@ -122,11 +127,15 @@ void SessionData::readCpuInfo() { } if (foundCPUPart) { - int cpuId = strtol(position, NULL, 16); - if (cpuId > mCpuId) { - mCpuId = cpuId; + mCpuIds[processor] = strtol(position, NULL, 0); + // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId + if (mCpuIds[processor] > mMaxCpuId) { + mMaxCpuId = mCpuIds[processor]; } - foundCpuId = true; + } + + if (foundProcessor) { + processor = strtol(position, NULL, 0); } } } diff --git a/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h index c834251527cf..ea34240e2df7 100644 --- a/tools/gator/daemon/SessionData.h +++ b/tools/gator/daemon/SessionData.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -11,12 +11,12 @@ #include +#include "Config.h" #include "Counter.h" #include "Hwmon.h" +#include "PerfDriver.h" -#define MAX_PERFORMANCE_COUNTERS 50 - -#define PROTOCOL_VERSION 17 +#define PROTOCOL_VERSION 18 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { @@ -34,6 +34,7 @@ public: void parseSessionXML(char* xmlString); Hwmon hwmon; + PerfDriver perf; char mCoreName[MAX_STRING_LEN]; struct ImageLinkList *mImages; @@ -47,6 +48,7 @@ public: bool mSessionIsActive; bool mLocalCapture; bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + bool mIsEBS; int mBacktraceDepth; int mTotalBufferSize; // number of MB to use for the entire collection buffer @@ -54,7 +56,9 @@ public: int64_t mLiveRate; int mDuration; int mCores; - int mCpuId; + int mPageSize; + int mCpuIds[NR_CPUS]; + int mMaxCpuId; // PMU Counters int mCounterOverflow; diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp index 0a0a02779176..55b2f9280709 100644 --- a/tools/gator/daemon/SessionXML.cpp +++ b/tools/gator/daemon/SessionXML.cpp @@ -1,15 +1,17 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include "SessionXML.h" + #include #include #include -#include "SessionXML.h" + #include "Logging.h" #include "OlyUtility.h" #include "SessionData.h" @@ -25,7 +27,7 @@ static const char* ATTR_DURATION = "duration"; static const char* ATTR_PATH = "path"; static const char* ATTR_LIVE_RATE = "live_rate"; -SessionXML::SessionXML(const char* str) { +SessionXML::SessionXML(const char *str) { parameters.buffer_mode[0] = 0; parameters.sample_rate[0] = 0; parameters.duration = 0; @@ -33,13 +35,13 @@ SessionXML::SessionXML(const char* str) { parameters.live_rate = 0; parameters.images = NULL; mPath = 0; - mSessionXML = (char*)str; + mSessionXML = (const char *)str; logg->logMessage(mSessionXML); } SessionXML::~SessionXML() { if (mPath != 0) { - free(mSessionXML); + free((char *)mSessionXML); } } diff --git a/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h index 0fb03bd6627c..e146094a4d17 100644 --- a/tools/gator/daemon/SessionXML.h +++ b/tools/gator/daemon/SessionXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,13 +24,13 @@ struct ConfigParameters { class SessionXML { public: - SessionXML(const char* str); + SessionXML(const char *str); ~SessionXML(); void parse(); ConfigParameters parameters; private: - char* mSessionXML; - char* mPath; + const char *mSessionXML; + const char *mPath; void sessionTag(mxml_node_t *tree, mxml_node_t *node); void sessionImage(mxml_node_t *node); diff --git a/tools/gator/daemon/Source.cpp b/tools/gator/daemon/Source.cpp new file mode 100644 index 000000000000..60cf704e599b --- /dev/null +++ b/tools/gator/daemon/Source.cpp @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Source.h" + +#include "Logging.h" + +Source::Source() : mThreadID() { +} + +Source::~Source() { +} + +void Source::start() { + if (pthread_create(&mThreadID, NULL, runStatic, this)) { + logg->logError(__FILE__, __LINE__, "Failed to create source thread"); + handleException(); + } +} + +void Source::join() { + pthread_join(mThreadID, NULL); +} + +void *Source::runStatic(void *arg) { + static_cast(arg)->run(); + return NULL; +} diff --git a/tools/gator/daemon/Source.h b/tools/gator/daemon/Source.h new file mode 100644 index 000000000000..56ac3d6e94f3 --- /dev/null +++ b/tools/gator/daemon/Source.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SOURCE_H +#define SOURCE_H + +#include + +class Sender; + +class Source { +public: + Source(); + virtual ~Source(); + + virtual bool prepare() = 0; + void start(); + virtual void run() = 0; + virtual void interrupt() = 0; + void join(); + + virtual bool isDone() = 0; + virtual void write(Sender *sender) = 0; + +private: + static void *runStatic(void *arg); + + pthread_t mThreadID; + + // Intentionally undefined + Source(const Source &); + Source &operator=(const Source &); +}; + +#endif // SOURCE_H diff --git a/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp index 2faada23f842..caa665e67193 100644 --- a/tools/gator/daemon/StreamlineSetup.cpp +++ b/tools/gator/daemon/StreamlineSetup.cpp @@ -1,26 +1,23 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include -#include -#include -#include -#include -#include -#include "Sender.h" -#include "Logging.h" -#include "OlyUtility.h" -#include "SessionData.h" -#include "CapturedXML.h" #include "StreamlineSetup.h" + +#include "Buffer.h" +#include "CapturedXML.h" #include "ConfigurationXML.h" #include "Driver.h" #include "EventsXML.h" +#include "Logging.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "Sender.h" +#include "SessionData.h" static const char* TAG_SESSION = "session"; static const char* TAG_REQUEST = "request"; @@ -198,12 +195,9 @@ void StreamlineSetup::handleDeliver(char* xml) { void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { unsigned char header[5]; header[0] = type; - header[1] = (length >> 0) & 0xff; - header[2] = (length >> 8) & 0xff; - header[3] = (length >> 16) & 0xff; - header[4] = (length >> 24) & 0xff; + Buffer::writeLEInt(header + 1, length); mSocket->send((char*)&header, sizeof(header)); - mSocket->send((char*)data, length); + mSocket->send((const char*)data, length); } void StreamlineSetup::sendEvents() { @@ -241,8 +235,14 @@ void StreamlineSetup::sendCounters() { xml = mxmlNewXML("1.0"); counters = mxmlNewElement(xml, "counters"); + int count = 0; for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { - driver->writeCounters(counters); + count += driver->writeCounters(counters); + } + + if (count == 0) { + logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); + handleException(); } char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); diff --git a/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h index d6d9a6ea2991..74bb197e35ff 100644 --- a/tools/gator/daemon/StreamlineSetup.h +++ b/tools/gator/daemon/StreamlineSetup.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,7 +9,10 @@ #ifndef __STREAMLINE_SETUP_H__ #define __STREAMLINE_SETUP_H__ -#include "OlySocket.h" +#include +#include + +class OlySocket; // Commands from Streamline enum { diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp new file mode 100644 index 000000000000..282e965fa67a --- /dev/null +++ b/tools/gator/daemon/UEvent.cpp @@ -0,0 +1,75 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "UEvent.h" + +#include +#include +#include +#include + +#include "Logging.h" + +static const char EMPTY[] = ""; +static const char ACTION[] = "ACTION="; +static const char DEVPATH[] = "DEVPATH="; +static const char SUBSYSTEM[] = "SUBSYSTEM="; + +UEvent::UEvent() : mFd(-1) { +} + +UEvent::~UEvent() { + if (mFd >= 0) { + close(mFd); + } +} + +bool UEvent::init() { + mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct sockaddr_nl sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.nl_family = AF_NETLINK; + sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events + sockaddr.nl_pid = 0; + if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) { + logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool UEvent::read(UEventResult *const result) { + ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0); + if (bytes <= 0) { + logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + result->mAction = EMPTY; + result->mDevPath = EMPTY; + result->mSubsystem = EMPTY; + + for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) { + char *const str = result->mBuf + pos; + if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) { + result->mAction = str + sizeof(ACTION) - 1; + } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) { + result->mDevPath = str + sizeof(DEVPATH) - 1; + } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) { + result->mSubsystem = str + sizeof(SUBSYSTEM) - 1; + } + } + + return true; +} diff --git a/tools/gator/daemon/UEvent.h b/tools/gator/daemon/UEvent.h new file mode 100644 index 000000000000..2f7ef2c93f5d --- /dev/null +++ b/tools/gator/daemon/UEvent.h @@ -0,0 +1,36 @@ +/** + * Copyright (C) ARM Limited 2013-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef UEVENT_H +#define UEVENT_H + +struct UEventResult { + const char *mAction; + const char *mDevPath; + const char *mSubsystem; + char mBuf[1<<13]; +}; + +class UEvent { +public: + UEvent(); + ~UEvent(); + + bool init(); + bool read(UEventResult *const result); + int getFd() const { return mFd; } + +private: + int mFd; + + // Intentionally undefined + UEvent(const UEvent &); + UEvent &operator=(const UEvent &); +}; + +#endif // UEVENT_H diff --git a/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp new file mode 100644 index 000000000000..debe69636cff --- /dev/null +++ b/tools/gator/daemon/UserSpaceSource.cpp @@ -0,0 +1,97 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "UserSpaceSource.h" + +#include +#include + +#include "Child.h" +#include "DriverSource.h" +#include "Logging.h" +#include "SessionData.h" + +#define NS_PER_S ((uint64_t)1000000000) +#define NS_PER_US 1000 + +extern Child *child; + +UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) { +} + +UserSpaceSource::~UserSpaceSource() { +} + +bool UserSpaceSource::prepare() { + return true; +} + +void UserSpaceSource::run() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); + + gSessionData->hwmon.start(); + + int64_t monotonic_started = 0; + while (monotonic_started <= 0) { + usleep(10); + + if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); + handleException(); + } + } + + uint64_t next_time = 0; + while (gSessionData->mSessionIsActive) { + struct timespec ts; +#ifndef CLOCK_MONOTONIC_RAW + // Android doesn't have this defined but it was added in Linux 2.6.28 +#define CLOCK_MONOTONIC_RAW 4 +#endif + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + handleException(); + } + const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; + // Sample ten times a second ignoring gSessionData->mSampleRate + next_time += NS_PER_S/10;//gSessionData->mSampleRate; + if (next_time < curr_time) { + logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); + next_time = curr_time; + } + + if (mBuffer.eventHeader(curr_time)) { + gSessionData->hwmon.read(&mBuffer); + // Only check after writing all counters so that time and corresponding counters appear in the same frame + mBuffer.check(curr_time); + } + + if (mBuffer.bytesAvailable() <= 0) { + logg->logMessage("One shot (counters)"); + child->endSession(); + } + + usleep((next_time - curr_time)/NS_PER_US); + } + + mBuffer.setDone(); +} + +void UserSpaceSource::interrupt() { + // Do nothing +} + +bool UserSpaceSource::isDone() { + return mBuffer.isDone(); +} + +void UserSpaceSource::write(Sender *sender) { + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } +} diff --git a/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h new file mode 100644 index 000000000000..fb5889d26ffb --- /dev/null +++ b/tools/gator/daemon/UserSpaceSource.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef USERSPACESOURCE_H +#define USERSPACESOURCE_H + +#include + +#include "Buffer.h" +#include "Source.h" + +// User space counters - currently just hwmon +class UserSpaceSource : public Source { +public: + UserSpaceSource(sem_t *senderSem); + ~UserSpaceSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + Buffer mBuffer; + + // Intentionally unimplemented + UserSpaceSource(const UserSpaceSource &); + UserSpaceSource &operator=(const UserSpaceSource &); +}; + +#endif // USERSPACESOURCE_H diff --git a/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk index 031d16906881..d9dc14606b07 100644 --- a/tools/gator/daemon/common.mk +++ b/tools/gator/daemon/common.mk @@ -25,7 +25,7 @@ include $(wildcard *.d) include $(wildcard mxml/*.d) EventsXML.cpp: events_xml.h -ConfigurationXML.cpp: configuration_xml.h +ConfigurationXML.cpp: defaults_xml.h # Don't regenerate conf-lex.c or conf-parse.c libsensors/conf-lex.c: ; @@ -47,4 +47,4 @@ escape: escape.c gcc $^ -o $@ clean: - rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h + rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h diff --git a/tools/gator/daemon/configuration.xml b/tools/gator/daemon/defaults.xml similarity index 51% rename from tools/gator/daemon/configuration.xml rename to tools/gator/daemon/defaults.xml index b44c00a79e88..5bf096cb2a45 100644 --- a/tools/gator/daemon/configuration.xml +++ b/tools/gator/daemon/defaults.xml @@ -6,29 +6,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c index 3eec1f8d38d3..c54aa1c3e75d 100644 --- a/tools/gator/daemon/escape.c +++ b/tools/gator/daemon/escape.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml index 20a4772c4587..9c04354ad137 100644 --- a/tools/gator/daemon/events-Cortex-A12.xml +++ b/tools/gator/daemon/events-Cortex-A12.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml index faa8b1cbcfb2..f50e55d66195 100644 --- a/tools/gator/daemon/events-Cortex-A15.xml +++ b/tools/gator/daemon/events-Cortex-A15.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml index a5b15466be52..d67581d77c08 100644 --- a/tools/gator/daemon/events-Cortex-A5.xml +++ b/tools/gator/daemon/events-Cortex-A5.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml index 54d7264bc08e..6e078b3cffa3 100644 --- a/tools/gator/daemon/events-Cortex-A7.xml +++ b/tools/gator/daemon/events-Cortex-A7.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml index f2518237983a..a69e25ab2c34 100644 --- a/tools/gator/daemon/events-Cortex-A8.xml +++ b/tools/gator/daemon/events-Cortex-A8.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml index 75f09c80425e..3e7f8289062e 100644 --- a/tools/gator/daemon/events-Cortex-A9.xml +++ b/tools/gator/daemon/events-Cortex-A9.xml @@ -1,6 +1,6 @@ - - - + + + diff --git a/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml index 31a90a1d6335..4d677e15db7e 100644 --- a/tools/gator/daemon/events-Linux.xml +++ b/tools/gator/daemon/events-Linux.xml @@ -6,12 +6,12 @@ - - - - - - - + + + + + + + diff --git a/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml index 8772ce410b95..5a71386830ba 100644 --- a/tools/gator/daemon/events-Mali-4xx.xml +++ b/tools/gator/daemon/events-Mali-4xx.xml @@ -207,7 +207,7 @@ - + diff --git a/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml index 2465238a8bda..ec9ca006f85f 100644 --- a/tools/gator/daemon/events-Mali-T6xx.xml +++ b/tools/gator/daemon/events-Mali-T6xx.xml @@ -4,14 +4,14 @@ - - - - - - - - + + + + + + + + diff --git a/tools/gator/daemon/events-Perf-Hardware.xml b/tools/gator/daemon/events-Perf-Hardware.xml new file mode 100644 index 000000000000..423696f82420 --- /dev/null +++ b/tools/gator/daemon/events-Perf-Hardware.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tools/gator/daemon/k/perf_event.3.12.h b/tools/gator/daemon/k/perf_event.3.12.h new file mode 100644 index 000000000000..e886c48cadf6 --- /dev/null +++ b/tools/gator/daemon/k/perf_event.3.12.h @@ -0,0 +1,792 @@ +/* + * Performance events: + * + * Copyright (C) 2008-2009, Thomas Gleixner + * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra + * + * Data type definitions, declarations, prototypes. + * + * Started by: Thomas Gleixner and Ingo Molnar + * + * For licencing details see kernel-base/COPYING + */ +#ifndef _LINUX_PERF_EVENT_H +#define _LINUX_PERF_EVENT_H + +#include +#include +#include + +/* + * User-space ABI bits: + */ + +/* + * attr.type + */ +enum perf_type_id { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, + PERF_TYPE_BREAKPOINT = 5, + + PERF_TYPE_MAX, /* non-ABI */ +}; + +/* + * Generalized performance event event_id types, used by the + * attr.event_id parameter of the sys_perf_event_open() + * syscall: + */ +enum perf_hw_id { + /* + * Common hardware events, generalized by the kernel: + */ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, + PERF_COUNT_HW_REF_CPU_CYCLES = 9, + + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + PERF_COUNT_HW_CACHE_NODE = 6, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/* + * Special "software" events provided by the kernel, even if the hardware + * does not support performance events. These events measure various + * physical and sw events of the kernel (and allow the profiling of them as + * well): + */ +enum perf_sw_ids { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, + PERF_COUNT_SW_DUMMY = 9, + + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +/* + * Bits that can be set in attr.sample_type to request information + * in the overflow packets. + */ +enum perf_event_sample_format { + PERF_SAMPLE_IP = 1U << 0, + PERF_SAMPLE_TID = 1U << 1, + PERF_SAMPLE_TIME = 1U << 2, + PERF_SAMPLE_ADDR = 1U << 3, + PERF_SAMPLE_READ = 1U << 4, + PERF_SAMPLE_CALLCHAIN = 1U << 5, + PERF_SAMPLE_ID = 1U << 6, + PERF_SAMPLE_CPU = 1U << 7, + PERF_SAMPLE_PERIOD = 1U << 8, + PERF_SAMPLE_STREAM_ID = 1U << 9, + PERF_SAMPLE_RAW = 1U << 10, + PERF_SAMPLE_BRANCH_STACK = 1U << 11, + PERF_SAMPLE_REGS_USER = 1U << 12, + PERF_SAMPLE_STACK_USER = 1U << 13, + PERF_SAMPLE_WEIGHT = 1U << 14, + PERF_SAMPLE_DATA_SRC = 1U << 15, + PERF_SAMPLE_IDENTIFIER = 1U << 16, + + PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */ +}; + +/* + * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set + * + * If the user does not pass priv level information via branch_sample_type, + * the kernel uses the event's priv level. Branch and event priv levels do + * not have to match. Branch priv level is checked for permissions. + * + * The branch types can be combined, however BRANCH_ANY covers all types + * of branches and therefore it supersedes all the other types. + */ +enum perf_branch_sample_type { + PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ + PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ + PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ + + PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ + PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ + PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ + PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ + PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */ + PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */ + PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */ + + PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */ +}; + +#define PERF_SAMPLE_BRANCH_PLM_ALL \ + (PERF_SAMPLE_BRANCH_USER|\ + PERF_SAMPLE_BRANCH_KERNEL|\ + PERF_SAMPLE_BRANCH_HV) + +/* + * Values to determine ABI of the registers dump. + */ +enum perf_sample_regs_abi { + PERF_SAMPLE_REGS_ABI_NONE = 0, + PERF_SAMPLE_REGS_ABI_32 = 1, + PERF_SAMPLE_REGS_ABI_64 = 2, +}; + +/* + * The format of the data returned by read() on a perf event fd, + * as specified by attr.read_format: + * + * struct read_format { + * { u64 value; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 id; } && PERF_FORMAT_ID + * } && !PERF_FORMAT_GROUP + * + * { u64 nr; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 value; + * { u64 id; } && PERF_FORMAT_ID + * } cntr[nr]; + * } && PERF_FORMAT_GROUP + * }; + */ +enum perf_event_read_format { + PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, + PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, + PERF_FORMAT_ID = 1U << 2, + PERF_FORMAT_GROUP = 1U << 3, + + PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ +}; + +#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ +#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ +#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ +#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ + /* add: sample_stack_user */ + +/* + * Hardware event_id to monitor via a performance monitoring event: + */ +struct perf_event_attr { + + /* + * Major type: hardware/software/tracepoint/etc. + */ + __u32 type; + + /* + * Size of the attr structure, for fwd/bwd compat. + */ + __u32 size; + + /* + * Type specific configuration information. + */ + __u64 config; + + union { + __u64 sample_period; + __u64 sample_freq; + }; + + __u64 sample_type; + __u64 read_format; + + __u64 disabled : 1, /* off by default */ + inherit : 1, /* children inherit it */ + pinned : 1, /* must always be on PMU */ + exclusive : 1, /* only group on PMU */ + exclude_user : 1, /* don't count user */ + exclude_kernel : 1, /* ditto kernel */ + exclude_hv : 1, /* ditto hypervisor */ + exclude_idle : 1, /* don't count when idle */ + mmap : 1, /* include mmap data */ + comm : 1, /* include comm data */ + freq : 1, /* use freq, not period */ + inherit_stat : 1, /* per task counts */ + enable_on_exec : 1, /* next exec enables */ + task : 1, /* trace fork/exit */ + watermark : 1, /* wakeup_watermark */ + /* + * precise_ip: + * + * 0 - SAMPLE_IP can have arbitrary skid + * 1 - SAMPLE_IP must have constant skid + * 2 - SAMPLE_IP requested to have 0 skid + * 3 - SAMPLE_IP must have 0 skid + * + * See also PERF_RECORD_MISC_EXACT_IP + */ + precise_ip : 2, /* skid constraint */ + mmap_data : 1, /* non-exec mmap data */ + sample_id_all : 1, /* sample_type all events */ + + exclude_host : 1, /* don't count in host */ + exclude_guest : 1, /* don't count in guest */ + + exclude_callchain_kernel : 1, /* exclude kernel callchains */ + exclude_callchain_user : 1, /* exclude user callchains */ + mmap2 : 1, /* include mmap with inode data */ + + __reserved_1 : 40; + + union { + __u32 wakeup_events; /* wakeup every n events */ + __u32 wakeup_watermark; /* bytes before wakeup */ + }; + + __u32 bp_type; + union { + __u64 bp_addr; + __u64 config1; /* extension of config */ + }; + union { + __u64 bp_len; + __u64 config2; /* extension of config1 */ + }; + __u64 branch_sample_type; /* enum perf_branch_sample_type */ + + /* + * Defines set of user regs to dump on samples. + * See asm/perf_regs.h for details. + */ + __u64 sample_regs_user; + + /* + * Defines size of the user stack to dump on samples. + */ + __u32 sample_stack_user; + + /* Align to u64. */ + __u32 __reserved_2; +}; + +#define perf_flags(attr) (*(&(attr)->read_format + 1)) + +/* + * Ioctls that can be done on a perf event fd: + */ +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) +#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) + +enum perf_event_ioc_flags { + PERF_IOC_FLAG_GROUP = 1U << 0, +}; + +/* + * Structure of the page that can be mapped via mmap + */ +struct perf_event_mmap_page { + __u32 version; /* version number of this structure */ + __u32 compat_version; /* lowest version this is compat with */ + + /* + * Bits needed to read the hw events in user-space. + * + * u32 seq, time_mult, time_shift, idx, width; + * u64 count, enabled, running; + * u64 cyc, time_offset; + * s64 pmc = 0; + * + * do { + * seq = pc->lock; + * barrier() + * + * enabled = pc->time_enabled; + * running = pc->time_running; + * + * if (pc->cap_usr_time && enabled != running) { + * cyc = rdtsc(); + * time_offset = pc->time_offset; + * time_mult = pc->time_mult; + * time_shift = pc->time_shift; + * } + * + * idx = pc->index; + * count = pc->offset; + * if (pc->cap_usr_rdpmc && idx) { + * width = pc->pmc_width; + * pmc = rdpmc(idx - 1); + * } + * + * barrier(); + * } while (pc->lock != seq); + * + * NOTE: for obvious reason this only works on self-monitoring + * processes. + */ + __u32 lock; /* seqlock for synchronization */ + __u32 index; /* hardware event identifier */ + __s64 offset; /* add to hardware event value */ + __u64 time_enabled; /* time event active */ + __u64 time_running; /* time event on cpu */ + union { + __u64 capabilities; + struct { + __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */ + cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */ + + cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */ + cap_user_time : 1, /* The time_* fields are used */ + cap_user_time_zero : 1, /* The time_zero field is used */ + cap_____res : 59; + }; + }; + + /* + * If cap_usr_rdpmc this field provides the bit-width of the value + * read using the rdpmc() or equivalent instruction. This can be used + * to sign extend the result like: + * + * pmc <<= 64 - width; + * pmc >>= 64 - width; // signed shift right + * count += pmc; + */ + __u16 pmc_width; + + /* + * If cap_usr_time the below fields can be used to compute the time + * delta since time_enabled (in ns) using rdtsc or similar. + * + * u64 quot, rem; + * u64 delta; + * + * quot = (cyc >> time_shift); + * rem = cyc & ((1 << time_shift) - 1); + * delta = time_offset + quot * time_mult + + * ((rem * time_mult) >> time_shift); + * + * Where time_offset,time_mult,time_shift and cyc are read in the + * seqcount loop described above. This delta can then be added to + * enabled and possible running (if idx), improving the scaling: + * + * enabled += delta; + * if (idx) + * running += delta; + * + * quot = count / running; + * rem = count % running; + * count = quot * enabled + (rem * enabled) / running; + */ + __u16 time_shift; + __u32 time_mult; + __u64 time_offset; + /* + * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated + * from sample timestamps. + * + * time = timestamp - time_zero; + * quot = time / time_mult; + * rem = time % time_mult; + * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; + * + * And vice versa: + * + * quot = cyc >> time_shift; + * rem = cyc & ((1 << time_shift) - 1); + * timestamp = time_zero + quot * time_mult + + * ((rem * time_mult) >> time_shift); + */ + __u64 time_zero; + __u32 size; /* Header size up to __reserved[] fields. */ + + /* + * Hole for extension of the self monitor capabilities + */ + + __u8 __reserved[118*8+4]; /* align to 1k. */ + + /* + * Control data for the mmap() data buffer. + * + * User-space reading the @data_head value should issue an smp_rmb(), + * after reading this value. + * + * When the mapping is PROT_WRITE the @data_tail value should be + * written by userspace to reflect the last read data, after issueing + * an smp_mb() to separate the data read from the ->data_tail store. + * In this case the kernel will not over-write unread data. + * + * See perf_output_put_handle() for the data ordering. + */ + __u64 data_head; /* head in the data section */ + __u64 data_tail; /* user-space written tail */ +}; + +#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) +#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (2 << 0) +#define PERF_RECORD_MISC_HYPERVISOR (3 << 0) +#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) +#define PERF_RECORD_MISC_GUEST_USER (5 << 0) + +#define PERF_RECORD_MISC_MMAP_DATA (1 << 13) +/* + * Indicates that the content of PERF_SAMPLE_IP points to + * the actual instruction that triggered the event. See also + * perf_event_attr::precise_ip. + */ +#define PERF_RECORD_MISC_EXACT_IP (1 << 14) +/* + * Reserve the last bit to indicate some extended misc field + */ +#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) + +struct perf_event_header { + __u32 type; + __u16 misc; + __u16 size; +}; + +enum perf_event_type { + + /* + * If perf_event_attr.sample_id_all is set then all event types will + * have the sample_type selected fields related to where/when + * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, + * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed + * just after the perf_event_header and the fields already present for + * the existing fields, i.e. at the end of the payload. That way a newer + * perf.data file will be supported by older perf tools, with these new + * optional fields being ignored. + * + * struct sample_id { + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * } && perf_event_attr::sample_id_all + * + * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The + * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed + * relative to header.size. + */ + + /* + * The MMAP events record the PROT_EXEC mappings so that we can + * correlate userspace IPs to code. They have the following structure: + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP = 1, + + /* + * struct { + * struct perf_event_header header; + * u64 id; + * u64 lost; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_LOST = 2, + + /* + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * char comm[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_COMM = 3, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_EXIT = 4, + + /* + * struct { + * struct perf_event_header header; + * u64 time; + * u64 id; + * u64 stream_id; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_THROTTLE = 5, + PERF_RECORD_UNTHROTTLE = 6, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_FORK = 7, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, tid; + * + * struct read_format values; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_READ = 8, + + /* + * struct { + * struct perf_event_header header; + * + * # + * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. + * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position + * # is fixed relative to header. + * # + * + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * { u64 ip; } && PERF_SAMPLE_IP + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 addr; } && PERF_SAMPLE_ADDR + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 period; } && PERF_SAMPLE_PERIOD + * + * { struct read_format values; } && PERF_SAMPLE_READ + * + * { u64 nr, + * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN + * + * # + * # The RAW record below is opaque data wrt the ABI + * # + * # That is, the ABI doesn't make any promises wrt to + * # the stability of its content, it may vary depending + * # on event, hardware, kernel version and phase of + * # the moon. + * # + * # In other words, PERF_SAMPLE_RAW contents are not an ABI. + * # + * + * { u32 size; + * char data[size];}&& PERF_SAMPLE_RAW + * + * { u64 nr; + * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * + * { u64 abi; # enum perf_sample_regs_abi + * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER + * + * { u64 size; + * char data[size]; + * u64 dyn_size; } && PERF_SAMPLE_STACK_USER + * + * { u64 weight; } && PERF_SAMPLE_WEIGHT + * { u64 data_src; } && PERF_SAMPLE_DATA_SRC + * }; + */ + PERF_RECORD_SAMPLE = 9, + + /* + * The MMAP2 records are an augmented version of MMAP, they add + * maj, min, ino numbers to be used to uniquely identify each mapping + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * u32 maj; + * u32 min; + * u64 ino; + * u64 ino_generation; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP2 = 10, + + PERF_RECORD_MAX, /* non-ABI */ +}; + +#define PERF_MAX_STACK_DEPTH 127 + +enum perf_callchain_context { + PERF_CONTEXT_HV = (__u64)-32, + PERF_CONTEXT_KERNEL = (__u64)-128, + PERF_CONTEXT_USER = (__u64)-512, + + PERF_CONTEXT_GUEST = (__u64)-2048, + PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, + PERF_CONTEXT_GUEST_USER = (__u64)-2560, + + PERF_CONTEXT_MAX = (__u64)-4095, +}; + +#define PERF_FLAG_FD_NO_GROUP (1U << 0) +#define PERF_FLAG_FD_OUTPUT (1U << 1) +#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ + +union perf_mem_data_src { + __u64 val; + struct { + __u64 mem_op:5, /* type of opcode */ + mem_lvl:14, /* memory hierarchy level */ + mem_snoop:5, /* snoop mode */ + mem_lock:2, /* lock instr */ + mem_dtlb:7, /* tlb access */ + mem_rsvd:31; + }; +}; + +/* type of opcode (load/store/prefetch,code) */ +#define PERF_MEM_OP_NA 0x01 /* not available */ +#define PERF_MEM_OP_LOAD 0x02 /* load instruction */ +#define PERF_MEM_OP_STORE 0x04 /* store instruction */ +#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ +#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ +#define PERF_MEM_OP_SHIFT 0 + +/* memory hierarchy (memory level, hit or miss) */ +#define PERF_MEM_LVL_NA 0x01 /* not available */ +#define PERF_MEM_LVL_HIT 0x02 /* hit level */ +#define PERF_MEM_LVL_MISS 0x04 /* miss level */ +#define PERF_MEM_LVL_L1 0x08 /* L1 */ +#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ +#define PERF_MEM_LVL_L2 0x20 /* L2 */ +#define PERF_MEM_LVL_L3 0x40 /* L3 */ +#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ +#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ +#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ +#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ +#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ +#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ +#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ +#define PERF_MEM_LVL_SHIFT 5 + +/* snoop mode */ +#define PERF_MEM_SNOOP_NA 0x01 /* not available */ +#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ +#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ +#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ +#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ +#define PERF_MEM_SNOOP_SHIFT 19 + +/* locked instruction */ +#define PERF_MEM_LOCK_NA 0x01 /* not available */ +#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ +#define PERF_MEM_LOCK_SHIFT 24 + +/* TLB access */ +#define PERF_MEM_TLB_NA 0x01 /* not available */ +#define PERF_MEM_TLB_HIT 0x02 /* hit level */ +#define PERF_MEM_TLB_MISS 0x04 /* miss level */ +#define PERF_MEM_TLB_L1 0x08 /* L1 */ +#define PERF_MEM_TLB_L2 0x10 /* L2 */ +#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ +#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ +#define PERF_MEM_TLB_SHIFT 26 + +#define PERF_MEM_S(a, s) \ + (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) + +/* + * single taken branch record layout: + * + * from: source instruction (may not always be a branch insn) + * to: branch target + * mispred: branch target was mispredicted + * predicted: branch target was predicted + * + * support for mispred, predicted is optional. In case it + * is not supported mispred = predicted = 0. + * + * in_tx: running in a hardware transaction + * abort: aborting a hardware transaction + */ +struct perf_branch_entry { + __u64 from; + __u64 to; + __u64 mispred:1, /* target mispredicted */ + predicted:1,/* target predicted */ + in_tx:1, /* in transaction */ + abort:1, /* transaction abort */ + reserved:60; +}; + +#endif /* _LINUX_PERF_EVENT_H */ diff --git a/tools/gator/daemon/k/perf_event.h b/tools/gator/daemon/k/perf_event.h new file mode 120000 index 000000000000..e5dff8c21ef4 --- /dev/null +++ b/tools/gator/daemon/k/perf_event.h @@ -0,0 +1 @@ +perf_event.3.12.h \ No newline at end of file diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp index bfd36b98766c..1275aef1cb79 100644 --- a/tools/gator/daemon/main.cpp +++ b/tools/gator/daemon/main.cpp @@ -1,32 +1,30 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * Copyright (C) ARM Limited 2010-2014. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include "Child.h" -#include "SessionData.h" -#include "OlySocket.h" -#include "Logging.h" -#include "OlyUtility.h" #include "KMod.h" +#include "Logging.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "SessionData.h" #define DEBUG false @@ -34,7 +32,7 @@ extern Child* child; static int shutdownFilesystem(); static pthread_mutex_t numSessions_mutex; static int numSessions = 0; -static OlySocket* sock = NULL; +static OlyServerSocket* sock = NULL; static bool driverRunningAtStart = false; static bool driverMountedAtStart = false; @@ -157,6 +155,7 @@ typedef struct { static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; static void* answerThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-discover", 0, 0, 0); const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid; RVIConfigureInfo dstAns; int req = udpPort(UDP_REQ_PORT); @@ -231,16 +230,7 @@ static bool init_module (const char * const location) { return ret; } -static int setupFilesystem(char* module) { - int retval; - - // Verify root permissions - uid_t euid = geteuid(); - if (euid) { - logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); - handleException(); - } - +static bool setupFilesystem(char* module) { if (module) { // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running shutdownFilesystem(); @@ -252,7 +242,7 @@ static int setupFilesystem(char* module) { } } - retval = mountGatorFS(); + const int retval = mountGatorFS(); if (retval == 1) { logg->logMessage("Driver already running at startup"); driverRunningAtStart = true; @@ -274,8 +264,8 @@ static int setupFilesystem(char* module) { } if (access(location, F_OK) == -1) { - logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); - handleException(); + // The gator kernel is not already loaded and unable to locate gator.ko + return false; } // Load driver @@ -296,7 +286,7 @@ static int setupFilesystem(char* module) { } } - return 0; + return true; } static int shutdownFilesystem() { @@ -418,8 +408,28 @@ int main(int argc, char** argv) { // Parse the command line parameters struct cmdline_t cmdline = parseCommandLine(argc, argv); + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + // Call before setting up the SIGCHLD handler, as system() spawns child processes - setupFilesystem(cmdline.module); + if (!setupFilesystem(cmdline.module)) { + logg->logMessage("Unable to setup gatorfs, trying perf"); + if (!gSessionData->perf.setup()) { + logg->logError(__FILE__, __LINE__, + "Unable to locate gator.ko driver:\n" + " >>> gator.ko should be co-located with gatord in the same directory\n" + " >>> OR insmod gator.ko prior to launching gatord\n" + " >>> OR specify the location of gator.ko on the command line\n" + " >>> OR run Linux 3.12 or later with perf support to collect data via userspace only"); + handleException(); + } + } + + gSessionData->hwmon.setup(); // Handle child exit codes signal(SIGCHLD, child_exit); @@ -439,11 +449,11 @@ int main(int argc, char** argv) { logg->logError(__FILE__, __LINE__, "Failed to create answer thread"); handleException(); } - sock = new OlySocket(cmdline.port, true); + sock = new OlyServerSocket(cmdline.port); // Forever loop, can be exited via a signal or exception while (1) { logg->logMessage("Waiting on connection..."); - sock->acceptConnection(); + OlySocket client(sock->acceptConnection()); int pid = fork(); if (pid < 0) { @@ -452,13 +462,13 @@ int main(int argc, char** argv) { } else if (pid == 0) { // Child sock->closeServerSocket(); - child = new Child(sock, numSessions + 1); + child = new Child(&client, numSessions + 1); child->run(); delete child; exit(0); } else { // Parent - sock->closeSocket(); + client.closeSocket(); pthread_mutex_lock(&numSessions_mutex); numSessions++; From 7bf87fba32f5609f0bddcd83c18241d65619b36e Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 9 Apr 2014 16:09:46 +0100 Subject: [PATCH 6/7] gator-daemon: Fix compilation error: 'sa_family_t' does not name a type When compiling on Linaro Android we get the following error: bionic/libc/kernel/common/linux/netlink.h:52:2: error: 'sa_family_t' does not name a type sa_family_t nl_family; This is related to an issue that was fixed in Linux 3.1 by commit 6602a4baf4d1a73c (net: Make userland include of netlink.h more sane) but it seems that the Linux headers used by bionic predate that. Fortunately, the fix for us is simple: reorder the header file includes so that the definition of sa_family_t from sys/socket.h is picked up before being used by linux/netlink.h Signed-off-by: Jon Medhurst --- tools/gator/daemon/UEvent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp index 282e965fa67a..d977cd080b40 100644 --- a/tools/gator/daemon/UEvent.cpp +++ b/tools/gator/daemon/UEvent.cpp @@ -8,9 +8,10 @@ #include "UEvent.h" +#include #include #include -#include + #include #include "Logging.h" From 6d0ae28ce59feba55f718ffaf43dfb6ad394cb4f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 17:35:03 +0100 Subject: [PATCH 7/7] gator: Add config for building the module in-tree Signed-off-by: Jon Medhurst --- drivers/Kconfig | 2 ++ drivers/Makefile | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/Kconfig b/drivers/Kconfig index 9953a42809ec..d27feb5460f3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -166,4 +166,6 @@ source "drivers/ipack/Kconfig" source "drivers/reset/Kconfig" +source "drivers/gator/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 130abc1dfd65..092a62e79688 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -152,3 +152,5 @@ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ + +obj-$(CONFIG_GATOR) += gator/