From 15ce78dafc08b1c5c3ec8f42070ae37160b5154c Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 Apr 2014 09:02:02 +0100 Subject: 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 +++++ drivers/gator/gator_buffer_write.c | 80 +++ 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 +- drivers/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_pack.c | 58 -- 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 +- .../gator/mali/mali_mjollnir_profiling_gator_api.h | 2 +- .../gator/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 | 259 +++++-- tools/gator/daemon/Buffer.h | 99 ++- 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.cpp | 221 ------ 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 +- tools/gator/daemon/DriverSource.cpp | 276 +++++++ 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 | 29 +- 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 +- tools/gator/daemon/configuration.xml | 57 -- tools/gator/daemon/defaults.xml | 62 ++ 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 | 80 ++- 121 files changed, 4582 insertions(+), 1362 deletions(-) create mode 100644 drivers/gator/Kconfig create mode 100644 drivers/gator/gator_buffer.c create mode 100644 drivers/gator/gator_buffer_write.c delete mode 100644 drivers/gator/gator_hrtimer_perf.c delete mode 100644 drivers/gator/gator_pack.c delete mode 100644 tools/gator/daemon/Collector.cpp delete mode 100644 tools/gator/daemon/Collector.h create mode 100644 tools/gator/daemon/Config.h create mode 100644 tools/gator/daemon/DriverSource.cpp create mode 100644 tools/gator/daemon/DriverSource.h create mode 100644 tools/gator/daemon/DynBuf.cpp create mode 100644 tools/gator/daemon/DynBuf.h create mode 100644 tools/gator/daemon/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 delete mode 100644 tools/gator/daemon/configuration.xml create mode 100644 tools/gator/daemon/defaults.xml 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 00000000000..e46ccb9b806 --- /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 d8981ed85a6..586cd9e742f 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 5b9399bea23..7e2c6e5d871 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 a406e488297..01080682552 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 ffacb490194..9f305cf7242 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 00000000000..eba22dfe3bf --- /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_buffer_write.c b/drivers/gator/gator_buffer_write.c new file mode 100644 index 00000000000..b621ba93ee5 --- /dev/null +++ b/drivers/gator/gator_buffer_write.c @@ -0,0 +1,80 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +static void gator_buffer_write_packed_int(int cpu, int buftype, int x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} + +static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buffer[(write + packedBytes) & mask] = b; + packedBytes++; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; +} + +static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len) +{ + int i; + u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; + u32 mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + + for (i = 0; i < len; i++) { + buffer[write] = x[i]; + write = (write + 1) & mask; + } + + per_cpu(gator_buffer_write, cpu)[buftype] = write; +} + +static void gator_buffer_write_string(int cpu, int buftype, const char *x) +{ + int len = strlen(x); + gator_buffer_write_packed_int(cpu, buftype, len); + gator_buffer_write_bytes(cpu, buftype, x, len); +} diff --git a/drivers/gator/gator_cookies.c b/drivers/gator/gator_cookies.c index 91adfdde9be..5c7d842070e 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 dd7974090b8..35364562230 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 30881c8fd3f..153119b463e 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 691ef257453..b2bc414e462 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 b89231967c7..024ffc2856a 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 b11879a248f..facbdd62325 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 ee521af2251..553f9707bdb 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 6719c1ec73a..85d47645a9d 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 413ad0ffe79..976ca8c4cfa 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 466ca1683c7..dc58dcf0c66 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 509f9b61884..41c2a3c13fa 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 7bf7d6a6dbf..76f14eee767 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 e406991398d..dfbc91ffd76 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 efb32ddf548..ba6553f3540 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 451290d9af1..c633dfdce30 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 f055e48d317..3b248ec24e6 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 9c8d3a43eae..11c10e37551 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 d472df918ab..8b2d67a058b 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 29f4e39e261..9e391583018 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 c91db1219d0..8ca251af0e2 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 fe6f83d547e..166cfe7d681 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 b0c947afe1e..76584554b00 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 7b95399478e..00000000000 --- 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 24233d77558..e90dfcce938 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 9773ae24d6f..e67f7c5cc61 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 af80ff62e71..fd413ad1331 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_pack.c b/drivers/gator/gator_pack.c deleted file mode 100644 index 2c082f283ad..00000000000 --- a/drivers/gator/gator_pack.c +++ /dev/null @@ -1,58 +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. - * - */ - -static void gator_buffer_write_packed_int(int cpu, int buftype, int x) -{ - uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; - uint32_t mask = gator_buffer_mask[buftype]; - char *buffer = per_cpu(gator_buffer, cpu)[buftype]; - int packedBytes = 0; - int more = true; - while (more) { - // low order 7 bits of x - char b = x & 0x7f; - x >>= 7; - - if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { - more = false; - } else { - b |= 0x80; - } - - buffer[(write + packedBytes) & mask] = b; - packedBytes++; - } - - per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; -} - -static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x) -{ - uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; - uint32_t mask = gator_buffer_mask[buftype]; - char *buffer = per_cpu(gator_buffer, cpu)[buftype]; - int packedBytes = 0; - int more = true; - while (more) { - // low order 7 bits of x - char b = x & 0x7f; - x >>= 7; - - if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { - more = false; - } else { - b |= 0x80; - } - - buffer[(write + packedBytes) & mask] = b; - packedBytes++; - } - - per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask; -} diff --git a/drivers/gator/gator_trace_gpu.c b/drivers/gator/gator_trace_gpu.c index be135b4aac5..6332098e595 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 bb0f42d290d..5113d459e24 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 272e05684ee..1895bb988c9 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 332b3f6ba96..52990e9d481 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 347a4fe404b..ff00d90cee7 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 559647a76d2..43c57604288 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 1a98c1c6a73..059d47aec91 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 a0429712fa8..045d028fda5 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 090a7155327..93557dabed9 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) - -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) { +#define mask (mSize - 1) + +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; +} + +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) { +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::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::frame () { +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 b3c8d78cf75..50237771860 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); + + int bytesAvailable() const; + int contiguousSpaceAvailable() const; + void commit(const uint64_t time); + void check(const uint64_t time); - void write (Sender * sender); + void frame(); - int bytesAvailable () const; - void commit (const uint64_t time); - void check (const uint64_t time); + // 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 frame (); + // 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); - 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; + 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); - - void packInt (int32_t x); - void packInt64 (int64_t x); - - const int32_t core; - const int32_t buftype; - const int size; - int readPos; - int writePos; - int commitPos; - bool available; - bool done; - char *const buf; - uint64_t commitTime; - sem_t *const readerSem; + bool commitReady() const; + bool checkSpace(int bytes); + + 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 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 30c4c44c5d9..cf79b72a116 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 b0482f593c6..efc1e52bdba 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 9ee2ef8afb9..ca33561ffdc 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); - - // 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"); + primarySource->run(); - 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 0330e9d7802..9e206d7113b 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.cpp b/tools/gator/daemon/Collector.cpp deleted file mode 100644 index bf73534692a..00000000000 --- a/tools/gator/daemon/Collector.cpp +++ /dev/null @@ -1,221 +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. - */ - -#define __STDC_FORMAT_MACROS - -#include -#include -#include -#include -#include -#include -#include -#include "Collector.h" -#include "SessionData.h" -#include "Logging.h" -#include "Sender.h" - -// Driver initialization independent of session settings -Collector::Collector() { - mBufferFD = 0; - - 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() { - int driver_version = 0; - - if (readIntDriver("/dev/gator/version", &driver_version) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); - handleException(); - } - - // Verify the driver version matches the daemon version - if (driver_version != PROTOCOL_VERSION) { - if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { - // One of the mismatched versions is development version - logg->logError(__FILE__, __LINE__, - "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" - ">> The following must be synchronized from engineering repository:\n" - ">> * gator driver\n" - ">> * gator daemon\n" - ">> * Streamline", driver_version, PROTOCOL_VERSION); - handleException(); - } else { - // Release version mismatch - logg->logError(__FILE__, __LINE__, - "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" - ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); - handleException(); - } - } -} - -void Collector::start() { - // Set the maximum backtrace depth - if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); - handleException(); - } - - // open the buffer which calls userspace_buffer_open() in the driver - mBufferFD = open("/dev/gator/buffer", O_RDONLY); - if (mBufferFD < 0) { - logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); - handleException(); - } - - // set the tick rate of the profiling timer - if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); - handleException(); - } - - // notify the kernel of the response type - int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; - if (writeDriver("/dev/gator/response_type", response_type)) { - logg->logError(__FILE__, __LINE__, "Unable to write the response type"); - handleException(); - } - - // Set the live rate - if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate"); - handleException(); - } - - logg->logMessage("Start the driver"); - - // This command makes the driver start profiling by calling gator_op_start() in the driver - if (writeDriver("/dev/gator/enable", "1") != 0) { - logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); - handleException(); - } - - lseek(mBufferFD, 0, SEEK_SET); -} - -// These commands should cause the read() function in collect() to return -void Collector::stop() { - // This will 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; - - 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); - 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) { - return -1; - } - if (fscanf(file, "%" SCNi64, value) != 1) { - fclose(file); - 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 fd = open(fullpath, O_WRONLY); - if (fd < 0) { - return -1; - } - if (write(fd, data, strlen(data)) < 0) { - close(fd); - logg->logMessage("Opened but could not write to %s", fullpath); - return -1; - } - close(fd); - return 0; -} - -int Collector::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) { - if (writeDriver(path, *value) || readInt64Driver(path, value)) { - return -1; - } - return 0; -} diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h deleted file mode 100644 index c5e9eac573d..00000000000 --- 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 00000000000..6f5e2aae50e --- /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 2a5252a5bb0..fd479f2452c 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 5650f487b99..efa415e508b 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 231a85d6e3b..689174573e4 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 c262467dc21..09e04016291 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 f3a932f852c..e5ed7b6c129 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/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp new file mode 100644 index 00000000000..f78ec6b7ce4 --- /dev/null +++ b/tools/gator/daemon/DriverSource.cpp @@ -0,0 +1,276 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define __STDC_FORMAT_MACROS + +#include "DriverSource.h" + +#include +#include +#include + +#include "Child.h" +#include "Fifo.h" +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +extern Child *child; + +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) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driver_version != PROTOCOL_VERSION) { + if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg->logError(__FILE__, __LINE__, + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driver_version, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg->logError(__FILE__, __LINE__, + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); + handleException(); + } + } + + int enable = -1; + if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { + logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + handleException(); + } + + readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); + if (gSessionData->mCores == 0) { + gSessionData->mCores = 1; + } + + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); + handleException(); + } +} + +DriverSource::~DriverSource() { + delete mFifo; + + // Write zero for safety, as a zero should have already been written + writeDriver("/dev/gator/enable", "0"); + + // Calls event_buffer_release in the driver + if (mBufferFD) { + close(mBufferFD); + } +} + +bool DriverSource::prepare() { + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); + mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); + + return true; +} + +void DriverSource::run() { + // Get the initial pointer to the collect buffer + char *collectBuffer = mFifo->start(); + int bytesCollected = 0; + + logg->logMessage("********** Profiling started **********"); + + // Set the maximum backtrace depth + if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); + handleException(); + } + + // open the buffer which calls userspace_buffer_open() in the driver + mBufferFD = open("/dev/gator/buffer", O_RDONLY); + if (mBufferFD < 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + // set the tick rate of the profiling timer + if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); + handleException(); + } + + // notify the kernel of the response type + int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; + if (writeDriver("/dev/gator/response_type", response_type)) { + logg->logError(__FILE__, __LINE__, "Unable to write the response type"); + handleException(); + } + + // Set the live rate + if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate"); + handleException(); + } + + logg->logMessage("Start the driver"); + + // This command makes the driver start profiling by calling gator_op_start() in the driver + if (writeDriver("/dev/gator/enable", "1") != 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + lseek(mBufferFD, 0, SEEK_SET); + + sem_post(mStartProfile); + + // 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"); +} + +void DriverSource::interrupt() { + // This command should cause the read() function in collect() to return and stop the driver from profiling + if (writeDriver("/dev/gator/enable", "0") != 0) { + logg->logMessage("Stopping kernel failed"); + } +} + +bool DriverSource::isDone() { + return mLength <= 0; +} + +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; + *value = strtol(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + + return 0; +} + +int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { + return -1; + } + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { + return -1; + } + data[bytes] = '\0'; + + char *endptr; + errno = 0; + *value = strtoll(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + + return 0; +} + +int DriverSource::writeDriver(const char *fullpath, const char *data) { + int fd = open(fullpath, O_WRONLY); + if (fd < 0) { + return -1; + } + if (::write(fd, data, strlen(data)) < 0) { + close(fd); + logg->logMessage("Opened but could not write to %s", fullpath); + return -1; + } + close(fd); + return 0; +} + +int DriverSource::writeDriver(const char *path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int DriverSource::writeDriver(const char *path, int64_t value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%" PRIi64, value); + return writeDriver(path, data); +} + +int DriverSource::writeReadDriver(const char *path, int *value) { + if (writeDriver(path, *value) || readIntDriver(path, value)) { + return -1; + } + return 0; +} + +int DriverSource::writeReadDriver(const char *path, int64_t *value) { + if (writeDriver(path, *value) || readInt64Driver(path, value)) { + return -1; + } + return 0; +} diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h new file mode 100644 index 00000000000..dcf1078a239 --- /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 00000000000..6f92b336ae1 --- /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 00000000000..2f4554ab2e4 --- /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 2a80482e0b8..a07a046f335 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 8e693efab20..6cd1560f7d4 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 00000000000..fe5824b0481 --- /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 00000000000..2052bdf2823 --- /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 250a4d023bf..f672e92a680 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 d25cd688256..7dd7426132d 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 1d7c0da9cc8..778f30755df 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 46bb42e898d..a22a3609f99 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 559297fe227..9300002f3fb 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 797426290df..fb7fc8a8f9c 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 3235a34ae9c..d2a4b799d7a 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 8042d6a8dc3..aadeccecf0c 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 5fd45b54f90..b8d3178950d 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 8f960de27bf..6ae32804698 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 00000000000..90d5c47706c --- /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 00000000000..6e268b6e1be --- /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 ab5c3c2c893..26e4768f393 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, char* host) { - mFDServer = 0; +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(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 5bab7d1f4cc..eab786b304b 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; + + 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 mSocketID, mFDServer; - void createClientSocket(char* hostname, int port); - void createSingleServerConnection(int port); + int mFDServer; + void createServerSocket(int port); }; diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp index 0b22d6ebd02..45340a27d9f 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 abab0a510a7..1d26beb596f 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 00000000000..5fad583f7bd --- /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 00000000000..278a3b9d6db --- /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 00000000000..8e25c22f679 --- /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 00000000000..3181b74f557 --- /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 00000000000..faf5fcaf15e --- /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 00000000000..af496d41334 --- /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 00000000000..1f1cb1988f0 --- /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 00000000000..3f471c8de41 --- /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 00000000000..e0b9e2259cf --- /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 00000000000..057b6109848 --- /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 8eb348ff3a0..3a981a6427b 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 "Sender.h" + #include +#include #include -#include "Sender.h" + +#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 b388f039bad..4c359dba82f 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 cf844075401..c169299af87 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 c834251527c..ea34240e2df 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 0a0a0277917..55b2f928070 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 0fb03bd6627..e146094a4d1 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 00000000000..60cf704e599 --- /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 00000000000..56ac3d6e94f --- /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 2faada23f84..caa665e6719 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 d6d9a6ea299..74bb197e35f 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 00000000000..282e965fa67 --- /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 00000000000..2f7ef2c93f5 --- /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 00000000000..debe69636cf --- /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 00000000000..fb5889d26ff --- /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 031d1690688..d9dc14606b0 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/configuration.xml deleted file mode 100644 index b44c00a79e8..00000000000 --- a/tools/gator/daemon/configuration.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml new file mode 100644 index 00000000000..5bf096cb2a4 --- /dev/null +++ b/tools/gator/daemon/defaults.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c index 3eec1f8d38d..c54aa1c3e75 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 20a4772c458..9c04354ad13 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 faa8b1cbcfb..f50e55d6619 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 a5b15466be5..d67581d77c0 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 54d7264bc08..6e078b3cffa 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 f2518237983..a69e25ab2c3 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 75f09c80425..3e7f8289062 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 31a90a1d633..4d677e15db7 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 8772ce410b9..5a71386830b 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 2465238a8bd..ec9ca006f85 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 00000000000..423696f8242 --- /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 00000000000..e886c48cadf --- /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 00000000000..e5dff8c21ef --- /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 bfd36b98766..1275aef1cb7 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 "Child.h" -#include "SessionData.h" -#include "OlySocket.h" +#include "KMod.h" #include "Logging.h" +#include "OlySocket.h" #include "OlyUtility.h" -#include "KMod.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++; -- cgit v1.2.3