Merge branch 'v3.10/topic/gator' of git://git.linaro.org/kernel/linux-linaro-stable into linux-linaro-lsk

This commit is contained in:
Mark Brown 2014-04-10 17:10:22 +01:00
commit fb8cade31f
118 changed files with 4275 additions and 1115 deletions

View file

@ -4,7 +4,11 @@ config GATOR
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
@ -13,9 +17,11 @@ choice
prompt "Enable Mali GPU support in Gator"
depends on GATOR
optional
help
Enable Mali GPU support in Gator
config GATOR_MALI_400MP
bool "Mali-400MP"
config GATOR_MALI_4XXMP
bool "Mali-400MP or Mali-450MP"
select GATOR_WITH_MALI_SUPPORT
config GATOR_MALI_T6XX

View file

@ -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
******************************************************************************/

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -1,5 +1,5 @@
/**
* Copyright (C) ARM Limited 2010-2013. All rights reserved.
* Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -56,3 +56,25 @@ static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x)
per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
}
static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len)
{
int i;
u32 write = per_cpu(gator_buffer_write, cpu)[buftype];
u32 mask = gator_buffer_mask[buftype];
char *buffer = per_cpu(gator_buffer, cpu)[buftype];
for (i = 0; i < len; i++) {
buffer[write] = x[i];
write = (write + 1) & mask;
}
per_cpu(gator_buffer_write, cpu)[buftype] = write;
}
static void gator_buffer_write_string(int cpu, int buftype, const char *x)
{
int len = strlen(x);
gator_buffer_write_packed_int(cpu, buftype, len);
gator_buffer_write_bytes(cpu, buftype, x, len);
}

View file

@ -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,7 +30,7 @@ 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);
@ -131,7 +131,9 @@ static void translate_buffer_write_args(int cpu, struct task_struct *task, const
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;
}
@ -165,7 +167,9 @@ static void wq_cookie_handler(struct work_struct *unused)
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
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <linux/slab.h>
#include <asm/io.h>
#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"

View file

@ -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 <asm/io.h>
/* 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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <linux/slab.h>
@ -55,9 +55,9 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION;
#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT))
#ifndef CONFIG_PERF_EVENTS
#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters
#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters
#elif !defined CONFIG_HW_PERF_EVENTS
#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters
#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters
#endif
#endif
@ -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;

View file

@ -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

View file

@ -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 <linux/math64.h>
#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) {

View file

@ -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

View file

@ -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

View file

@ -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++) {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 \

View file

@ -1,5 +1,5 @@
/**
* Copyright (C) ARM Limited 2013. All rights reserved.
* Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -12,33 +12,60 @@
#include "Sender.h"
#include "SessionData.h"
#define mask (size - 1)
#define mask (mSize - 1)
Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) {
if ((size & mask) != 0) {
enum {
CODE_PEA = 1,
CODE_KEYS = 2,
CODE_FORMAT = 3,
CODE_MAPS = 4,
CODE_COMM = 5,
};
// Summary Frame Messages
enum {
MESSAGE_SUMMARY = 1,
MESSAGE_CORE_NAME = 3,
};
// From gator_marshaling.c
#define NEWLINE_CANARY \
/* Unix */ \
"1\n" \
/* Windows */ \
"2\r\n" \
/* Mac OS */ \
"3\r" \
/* RISC OS */ \
"4\n\r" \
/* Add another character so the length isn't 0x0a bytes */ \
"5"
Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) {
if ((mSize & mask) != 0) {
logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2");
handleException();
}
frame();
}
Buffer::~Buffer () {
delete [] buf;
Buffer::~Buffer() {
delete [] mBuf;
}
void Buffer::write (Sender * const sender) {
void Buffer::write(Sender *const sender) {
if (!commitReady()) {
return;
}
// determine the size of two halves
int length1 = commitPos - readPos;
char * buffer1 = buf + readPos;
int length1 = mCommitPos - mReadPos;
char *buffer1 = mBuf + mReadPos;
int length2 = 0;
char * buffer2 = buf;
char *buffer2 = mBuf;
if (length1 < 0) {
length1 = size - readPos;
length2 = commitPos;
length1 = mSize - mReadPos;
length2 = mCommitPos;
}
logg->logMessage("Sending data length1: %i length2: %i", length1, length2);
@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) {
sender->writeData(buffer2, length2, RESPONSE_APC_DATA);
}
readPos = commitPos;
mReadPos = mCommitPos;
}
bool Buffer::commitReady () const {
return commitPos != readPos;
bool Buffer::commitReady() const {
return mCommitPos != mReadPos;
}
int Buffer::bytesAvailable () const {
int filled = writePos - readPos;
int Buffer::bytesAvailable() const {
int filled = mWritePos - mReadPos;
if (filled < 0) {
filled += size;
filled += mSize;
}
int remaining = size - filled;
int remaining = mSize - filled;
if (available) {
if (mAvailable) {
// Give some extra room; also allows space to insert the overflow error packet
remaining -= 200;
} else {
@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const {
return remaining;
}
bool Buffer::checkSpace (const int bytes) {
bool Buffer::checkSpace(const int bytes) {
const int remaining = bytesAvailable();
if (remaining < bytes) {
available = false;
mAvailable = false;
} else {
available = true;
mAvailable = true;
}
return available;
return mAvailable;
}
void Buffer::commit (const uint64_t time) {
int Buffer::contiguousSpaceAvailable() const {
int remaining = bytesAvailable();
int contiguous = mSize - mWritePos;
if (remaining < contiguous) {
return remaining;
} else {
return contiguous;
}
}
void Buffer::commit(const uint64_t time) {
// post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
int length = writePos - commitPos;
int length = mWritePos - mCommitPos;
if (length < 0) {
length += size;
length += mSize;
}
length = length - typeLength - sizeof(int32_t);
for (size_t byte = 0; byte < sizeof(int32_t); byte++) {
buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
}
logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos);
commitPos = writePos;
logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos);
mCommitPos = mWritePos;
if (gSessionData->mLiveRate > 0) {
while (time > commitTime) {
commitTime += gSessionData->mLiveRate;
while (time > mCommitTime) {
mCommitTime += gSessionData->mLiveRate;
}
}
if (!done) {
if (!mIsDone) {
frame();
}
// send a notification that data is ready
sem_post(readerSem);
sem_post(mReaderSem);
}
void Buffer::check (const uint64_t time) {
int filled = writePos - commitPos;
void Buffer::check(const uint64_t time) {
int filled = mWritePos - mCommitPos;
if (filled < 0) {
filled += size;
filled += mSize;
}
if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) {
if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) {
commit(time);
}
}
void Buffer::packInt (int32_t x) {
void Buffer::packInt(int32_t x) {
int packedBytes = 0;
int more = true;
while (more) {
@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) {
b |= 0x80;
}
buf[(writePos + packedBytes) & mask] = b;
mBuf[(mWritePos + packedBytes) & mask] = b;
packedBytes++;
}
writePos = (writePos + packedBytes) & mask;
mWritePos = (mWritePos + packedBytes) & mask;
}
void Buffer::packInt64 (int64_t x) {
void Buffer::packInt64(int64_t x) {
int packedBytes = 0;
int more = true;
while (more) {
@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) {
b |= 0x80;
}
buf[(writePos + packedBytes) & mask] = b;
mBuf[(mWritePos + packedBytes) & mask] = b;
packedBytes++;
}
writePos = (writePos + packedBytes) & mask;
mWritePos = (mWritePos + packedBytes) & mask;
}
void Buffer::frame () {
void Buffer::writeBytes(const void *const data, size_t count) {
size_t i;
for (i = 0; i < count; ++i) {
mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i];
}
mWritePos = (mWritePos + i) & mask;
}
void Buffer::writeString(const char *const str) {
const int len = strlen(str);
packInt(len);
writeBytes(str, len);
}
void Buffer::frame() {
if (!gSessionData->mLocalCapture) {
packInt(RESPONSE_APC_DATA);
}
// Reserve space for the length
writePos += sizeof(int32_t);
packInt(buftype);
packInt(core);
mWritePos += sizeof(int32_t);
packInt(mBufType);
packInt(mCore);
}
bool Buffer::eventHeader (const uint64_t curr_time) {
void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) {
packInt(MESSAGE_SUMMARY);
writeString(NEWLINE_CANARY);
packInt64(timestamp);
packInt64(uptime);
packInt64(monotonicDelta);
writeString("uname");
writeString(uname);
writeString("");
check(1);
}
void Buffer::coreName(const int core, const int cpuid, const char *const name) {
if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) {
packInt(MESSAGE_CORE_NAME);
packInt(core);
packInt(cpuid);
writeString(name);
}
check(1);
}
bool Buffer::eventHeader(const uint64_t curr_time) {
bool retval = false;
if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
packInt(0); // key of zero indicates a timestamp
@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) {
return retval;
}
bool Buffer::eventTid (const int tid) {
bool Buffer::eventTid(const int tid) {
bool retval = false;
if (checkSpace(2*MAXSIZE_PACK32)) {
if (checkSpace(2 * MAXSIZE_PACK32)) {
packInt(1); // key of 1 indicates a tid
packInt(tid);
retval = true;
@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) {
return retval;
}
void Buffer::event (const int32_t key, const int32_t value) {
void Buffer::event(const int32_t key, const int32_t value) {
if (checkSpace(2 * MAXSIZE_PACK32)) {
packInt(key);
packInt(value);
}
}
void Buffer::event64 (const int64_t key, const int64_t value) {
void Buffer::event64(const int64_t key, const int64_t value) {
if (checkSpace(2 * MAXSIZE_PACK64)) {
packInt64(key);
packInt64(value);
}
}
void Buffer::setDone () {
done = true;
void Buffer::pea(const struct perf_event_attr *const pea, int key) {
if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) {
packInt(CODE_PEA);
writeBytes(pea, pea->size);
packInt(key);
} else {
logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
handleException();
}
// Don't know the real perf time so use 1 as it will work for now
check(1);
}
void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) {
if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) {
packInt(CODE_KEYS);
packInt(count);
for (int i = 0; i < count; ++i) {
packInt64(ids[i]);
packInt(keys[i]);
}
} else {
logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
handleException();
}
check(1);
}
void Buffer::format(const int length, const char *const format) {
if (checkSpace(MAXSIZE_PACK32 + length + 1)) {
packInt(CODE_FORMAT);
writeBytes(format, length + 1);
} else {
logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
handleException();
}
check(1);
}
void Buffer::maps(const int pid, const int tid, const char *const maps) {
const int mapsLen = strlen(maps) + 1;
if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) {
packInt(CODE_MAPS);
packInt(pid);
packInt(tid);
writeBytes(maps, mapsLen);
} else {
logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
handleException();
}
check(1);
}
void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) {
const int imageLen = strlen(image) + 1;
const int commLen = strlen(comm) + 1;
if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) {
packInt(CODE_COMM);
packInt(pid);
packInt(tid);
writeBytes(image, imageLen);
writeBytes(comm, commLen);
} else {
logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
handleException();
}
check(1);
}
void Buffer::setDone() {
mIsDone = true;
commit(0);
}
bool Buffer::isDone () const {
return done && readPos == commitPos && commitPos == writePos;
bool Buffer::isDone() const {
return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos;
}

View file

@ -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 <stddef.h>
#include <stdint.h>
#include <semaphore.h>
#include "k/perf_event.h"
class Sender;
enum {
FRAME_SUMMARY = 1,
FRAME_BLOCK_COUNTER = 5,
FRAME_EXTERNAL = 10,
FRAME_PERF_ATTRS = 11,
FRAME_PERF = 12,
};
class Buffer {
public:
static const size_t MAXSIZE_PACK32 = 5;
static const size_t MAXSIZE_PACK64 = 10;
Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
~Buffer ();
Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
~Buffer();
void write (Sender * sender);
void write(Sender *sender);
int bytesAvailable () const;
void commit (const uint64_t time);
void check (const uint64_t time);
int bytesAvailable() const;
int contiguousSpaceAvailable() const;
void commit(const uint64_t time);
void check(const uint64_t time);
void frame ();
void frame();
bool eventHeader (uint64_t curr_time);
bool eventTid (int tid);
void event (int32_t key, int32_t value);
void event64 (int64_t key, int64_t value);
// Summary messages
void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname);
void coreName(const int core, const int cpuid, const char *const name);
void setDone ();
bool isDone () const;
// Block Counter messages
bool eventHeader(uint64_t curr_time);
bool eventTid(int tid);
void event(int32_t key, int32_t value);
void event64(int64_t key, int64_t value);
// Perf Attrs messages
void pea(const struct perf_event_attr *const pea, int key);
void keys(const int count, const __u64 *const ids, const int *const keys);
void format(const int length, const char *const format);
void maps(const int pid, const int tid, const char *const maps);
void comm(const int pid, const int tid, const char *const image, const char *const comm);
void setDone();
bool isDone() const;
// Prefer a new member to using these functions if possible
char *getWritePos() { return mBuf + mWritePos; }
void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); }
static void writeLEInt(unsigned char *buf, int v) {
buf[0] = (v >> 0) & 0xFF;
buf[1] = (v >> 8) & 0xFF;
buf[2] = (v >> 16) & 0xFF;
buf[3] = (v >> 24) & 0xFF;
}
private:
bool commitReady () const;
bool checkSpace (int bytes);
bool commitReady() const;
bool checkSpace(int bytes);
void packInt (int32_t x);
void packInt64 (int64_t x);
void packInt(int32_t x);
void packInt64(int64_t x);
void writeBytes(const void *const data, size_t count);
void writeString(const char *const str);
const int32_t core;
const int32_t buftype;
const int size;
int readPos;
int writePos;
int commitPos;
bool available;
bool done;
char *const buf;
uint64_t commitTime;
sem_t *const readerSem;
const int32_t mCore;
const int32_t mBufType;
const int mSize;
int mReadPos;
int mWritePos;
int mCommitPos;
bool mAvailable;
bool mIsDone;
char *const mBuf;
uint64_t mCommitTime;
sem_t *const mReaderSem;
// Intentionally unimplemented
Buffer(const Buffer &);

View file

@ -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 <stdlib.h>
#include <string.h>
#include <dirent.h>
#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");

View file

@ -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

View file

@ -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 <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include "Logging.h"
#include "CapturedXML.h"
#include "SessionData.h"
#include "Child.h"
#include "LocalCapture.h"
#include "Collector.h"
#include "Sender.h"
#include "OlyUtility.h"
#include "OlySocket.h"
#include "StreamlineSetup.h"
#include "ConfigurationXML.h"
#include "Driver.h"
#include "Fifo.h"
#include "Buffer.h"
#define NS_PER_S ((uint64_t)1000000000)
#define NS_PER_US 1000
#include "PerfSource.h"
#include "DriverSource.h"
#include "UserSpaceSource.h"
#include "ExternalSource.h"
static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads
static Buffer* buffer = NULL;
static Source *primarySource = NULL;
static Source *userSpaceSource = NULL;
static Source *externalSource = NULL;
static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
static Collector* collector = NULL;
Child* child = NULL; // shared by Child.cpp and main.cpp
extern void cleanUp();
@ -78,7 +79,7 @@ static void child_handler(int signum) {
}
beenHere = true;
logg->logMessage("Gator is shutting down.");
if (signum == SIGALRM || !collector) {
if (signum == SIGALRM || !primarySource) {
exit(1);
} else {
child->endSession();
@ -139,77 +140,22 @@ static void *stopThread(void *) {
return 0;
}
static void *countersThread(void *) {
prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
gSessionData->hwmon.start();
int64_t monotonic_started = 0;
while (monotonic_started <= 0) {
usleep(10);
if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
handleException();
}
}
uint64_t next_time = 0;
while (gSessionData->mSessionIsActive) {
struct timespec ts;
#ifndef CLOCK_MONOTONIC_RAW
// Android doesn't have this defined but it was added in Linux 2.6.28
#define CLOCK_MONOTONIC_RAW 4
#endif
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
logg->logError(__FILE__, __LINE__, "Failed to get uptime");
handleException();
}
const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
// Sample ten times a second ignoring gSessionData->mSampleRate
next_time += NS_PER_S/10;//gSessionData->mSampleRate;
if (next_time < curr_time) {
logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
next_time = curr_time;
}
if (buffer->eventHeader(curr_time)) {
gSessionData->hwmon.read(buffer);
// Only check after writing all counters so that time and corresponding counters appear in the same frame
buffer->check(curr_time);
}
if (buffer->bytesAvailable() <= 0) {
logg->logMessage("One shot (counters)");
child->endSession();
}
usleep((next_time - curr_time)/NS_PER_US);
}
buffer->setDone();
return NULL;
}
static void *senderThread(void *) {
int length = 1;
char* data;
char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
sem_post(&senderThreadStarted);
prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
sem_wait(&haltPipeline);
while (length > 0 || !buffer->isDone()) {
while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) {
sem_wait(&senderSem);
data = collectorFifo->read(&length);
if (data != NULL) {
sender->writeData(data, length, RESPONSE_APC_DATA);
collectorFifo->release();
primarySource->write(sender);
if (userSpaceSource != NULL) {
userSpaceSource->write(sender);
}
if (!buffer->isDone()) {
buffer->write(sender);
if (externalSource != NULL) {
externalSource->write(sender);
}
}
@ -255,15 +201,13 @@ void Child::initialization() {
void Child::endSession() {
gSessionData->mSessionIsActive = false;
collector->stop();
primarySource->interrupt();
sem_post(&haltPipeline);
}
void Child::run() {
char* collectBuffer;
int bytesCollected = 0;
LocalCapture* localCapture = NULL;
pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
pthread_t durationThreadID, stopThreadID, senderThreadID;
prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
@ -282,7 +226,11 @@ void Child::run() {
{ ConfigurationXML configuration; }
// Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
collector = new Collector();
if (!gSessionData->perf.isSetup()) {
primarySource = new DriverSource(&senderSem, &startProfile);
} else {
primarySource = new PerfSource(&senderSem, &startProfile);
}
// Initialize all drivers
for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
@ -317,15 +265,11 @@ void Child::run() {
free(xmlString);
}
// Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
// Get the initial pointer to the collect buffer
collectBuffer = collectorFifo->start();
// Create a new Block Counter Buffer
buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
// Must be after session XML is parsed
if (!primarySource->prepare()) {
logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
handleException();
}
// Sender thread shall be halted until it is signaled for one shot mode
sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
@ -340,14 +284,21 @@ void Child::run() {
thread_creation_success = false;
}
bool startcountersThread = gSessionData->hwmon.countersEnabled();
if (startcountersThread) {
if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
thread_creation_success = false;
if (gSessionData->hwmon.countersEnabled()) {
userSpaceSource = new UserSpaceSource(&senderSem);
if (!userSpaceSource->prepare()) {
logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
handleException();
}
} else {
// Let senderThread know there is no buffer data to send
buffer->setDone();
userSpaceSource->start();
}
if (access("/tmp/gator", F_OK) == 0) {
externalSource = new ExternalSource(&senderSem);
if (!externalSource->prepare()) {
logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
handleException();
}
externalSource->start();
}
if (!thread_creation_success) {
@ -359,28 +310,13 @@ void Child::run() {
sem_wait(&senderThreadStarted);
// Start profiling
logg->logMessage("********** Profiling started **********");
collector->start();
sem_post(&startProfile);
primarySource->run();
// Collect Data
do {
// This command will stall until data is received from the driver
bytesCollected = collector->collect(collectBuffer);
// In one shot mode, stop collection once all the buffers are filled
if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
logg->logMessage("One shot");
endSession();
}
}
collectBuffer = collectorFifo->write(bytesCollected);
} while (bytesCollected > 0);
logg->logMessage("Exit collect data loop");
if (startcountersThread) {
pthread_join(countersThreadID, NULL);
if (externalSource != NULL) {
externalSource->join();
}
if (userSpaceSource != NULL) {
userSpaceSource->join();
}
// Wait for the other threads to exit
@ -401,9 +337,9 @@ void Child::run() {
logg->logMessage("Profiling ended.");
delete buffer;
delete collectorFifo;
delete externalSource;
delete userSpaceSource;
delete primarySource;
delete sender;
delete collector;
delete localCapture;
}

View file

@ -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 <pthread.h>
class OlySocket;
class Child {

View file

@ -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 <stdio.h>
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__

View file

@ -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<int>(sizeof(A)/sizeof((A)[0]))
#define MAX_PERFORMANCE_COUNTERS 50
#define NR_CPUS 16
#endif // CONFIG_H

View file

@ -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 <string.h>
#include <stdlib.h>
#include <dirent.h>
#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) {

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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 {}

View file

@ -1,5 +1,5 @@
/**
* Copyright (C) ARM Limited 2010-2013. All rights reserved.
* Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -8,53 +8,21 @@
#define __STDC_FORMAT_MACROS
#include "DriverSource.h"
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <inttypes.h>
#include "Collector.h"
#include "SessionData.h"
#include <unistd.h>
#include "Child.h"
#include "Fifo.h"
#include "Logging.h"
#include "Sender.h"
#include "SessionData.h"
// Driver initialization independent of session settings
Collector::Collector() {
mBufferFD = 0;
extern Child *child;
checkVersion();
int enable = -1;
if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
handleException();
}
readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
if (gSessionData->mCores == 0) {
gSessionData->mCores = 1;
}
mBufferSize = 0;
if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
handleException();
}
}
Collector::~Collector() {
// Write zero for safety, as a zero should have already been written
writeDriver("/dev/gator/enable", "0");
// Calls event_buffer_release in the driver
if (mBufferFD) {
close(mBufferFD);
}
}
void Collector::checkVersion() {
DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
int driver_version = 0;
if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
@ -81,9 +49,51 @@ void Collector::checkVersion() {
handleException();
}
}
int enable = -1;
if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
handleException();
}
readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
if (gSessionData->mCores == 0) {
gSessionData->mCores = 1;
}
if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
handleException();
}
}
void Collector::start() {
DriverSource::~DriverSource() {
delete mFifo;
// Write zero for safety, as a zero should have already been written
writeDriver("/dev/gator/enable", "0");
// Calls event_buffer_release in the driver
if (mBufferFD) {
close(mBufferFD);
}
}
bool DriverSource::prepare() {
// Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize);
mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem);
return true;
}
void DriverSource::run() {
// Get the initial pointer to the collect buffer
char *collectBuffer = mFifo->start();
int bytesCollected = 0;
logg->logMessage("********** Profiling started **********");
// Set the maximum backtrace depth
if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
@ -125,79 +135,112 @@ void Collector::start() {
}
lseek(mBufferFD, 0, SEEK_SET);
sem_post(mStartProfile);
// Collect Data
do {
// This command will stall until data is received from the driver
// Calls event_buffer_read in the driver
errno = 0;
bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
// If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
if (bytesCollected == -1 && errno == EINTR) {
bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
}
// return the total bytes written
logg->logMessage("Driver read of %d bytes", bytesCollected);
// In one shot mode, stop collection once all the buffers are filled
if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) {
logg->logMessage("One shot");
child->endSession();
}
}
collectBuffer = mFifo->write(bytesCollected);
} while (bytesCollected > 0);
logg->logMessage("Exit collect data loop");
}
// These commands should cause the read() function in collect() to return
void Collector::stop() {
// This will stop the driver from profiling
void DriverSource::interrupt() {
// This command should cause the read() function in collect() to return and stop the driver from profiling
if (writeDriver("/dev/gator/enable", "0") != 0) {
logg->logMessage("Stopping kernel failed");
}
}
int Collector::collect(char* buffer) {
// Calls event_buffer_read in the driver
int bytesRead;
bool DriverSource::isDone() {
return mLength <= 0;
}
void DriverSource::write(Sender *sender) {
char *data = mFifo->read(&mLength);
if (data != NULL) {
sender->writeData(data, mLength, RESPONSE_APC_DATA);
mFifo->release();
}
}
int DriverSource::readIntDriver(const char *fullpath, int *value) {
char data[40]; // Sufficiently large to hold any integer
const int fd = open(fullpath, O_RDONLY);
if (fd < 0) {
return -1;
}
const ssize_t bytes = read(fd, data, sizeof(data) - 1);
close(fd);
if (bytes < 0) {
return -1;
}
data[bytes] = '\0';
char *endptr;
errno = 0;
bytesRead = read(mBufferFD, buffer, mBufferSize);
// If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
if (bytesRead == -1 && errno == EINTR) {
bytesRead = read(mBufferFD, buffer, mBufferSize);
}
// return the total bytes written
logg->logMessage("Driver read of %d bytes", bytesRead);
return bytesRead;
}
int Collector::readIntDriver(const char* fullpath, int* value) {
FILE* file = fopen(fullpath, "r");
if (file == NULL) {
return -1;
}
if (fscanf(file, "%u", value) != 1) {
fclose(file);
*value = strtol(data, &endptr, 10);
if (errno != 0 || *endptr != '\n') {
logg->logMessage("Invalid value in file %s", fullpath);
return -1;
}
fclose(file);
return 0;
}
int Collector::readInt64Driver(const char* fullpath, int64_t* value) {
FILE* file = fopen(fullpath, "r");
if (file == NULL) {
int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) {
char data[40]; // Sufficiently large to hold any integer
const int fd = open(fullpath, O_RDONLY);
if (fd < 0) {
return -1;
}
if (fscanf(file, "%" SCNi64, value) != 1) {
fclose(file);
const ssize_t bytes = read(fd, data, sizeof(data) - 1);
close(fd);
if (bytes < 0) {
return -1;
}
data[bytes] = '\0';
char *endptr;
errno = 0;
*value = strtoll(data, &endptr, 10);
if (errno != 0 || *endptr != '\n') {
logg->logMessage("Invalid value in file %s", fullpath);
return -1;
}
fclose(file);
return 0;
}
int Collector::writeDriver(const char* path, int value) {
char data[40]; // Sufficiently large to hold any integer
snprintf(data, sizeof(data), "%d", value);
return writeDriver(path, data);
}
int Collector::writeDriver(const char* path, int64_t value) {
char data[40]; // Sufficiently large to hold any integer
snprintf(data, sizeof(data), "%" PRIi64, value);
return writeDriver(path, data);
}
int Collector::writeDriver(const char* fullpath, const char* data) {
int DriverSource::writeDriver(const char *fullpath, const char *data) {
int fd = open(fullpath, O_WRONLY);
if (fd < 0) {
return -1;
}
if (write(fd, data, strlen(data)) < 0) {
if (::write(fd, data, strlen(data)) < 0) {
close(fd);
logg->logMessage("Opened but could not write to %s", fullpath);
return -1;
@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) {
return 0;
}
int Collector::writeReadDriver(const char* path, int* value) {
int DriverSource::writeDriver(const char *path, int value) {
char data[40]; // Sufficiently large to hold any integer
snprintf(data, sizeof(data), "%d", value);
return writeDriver(path, data);
}
int DriverSource::writeDriver(const char *path, int64_t value) {
char data[40]; // Sufficiently large to hold any integer
snprintf(data, sizeof(data), "%" PRIi64, value);
return writeDriver(path, data);
}
int DriverSource::writeReadDriver(const char *path, int *value) {
if (writeDriver(path, *value) || readIntDriver(path, value)) {
return -1;
}
return 0;
}
int Collector::writeReadDriver(const char* path, int64_t* value) {
int DriverSource::writeReadDriver(const char *path, int64_t *value) {
if (writeDriver(path, *value) || readInt64Driver(path, value)) {
return -1;
}

View file

@ -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 <semaphore.h>
#include <stdint.h>
#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

View file

@ -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 <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#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<char *>(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<size_t>(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<size_t>(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<size_t>(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;
}

View file

@ -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 <stdlib.h>
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

View file

@ -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

View file

@ -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

View file

@ -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 <sys/prctl.h>
#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);
}
}

View file

@ -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 <semaphore.h>
#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

View file

@ -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

View file

@ -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 <windows.h>
#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))

View file

@ -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 {

View file

@ -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();

View file

@ -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 <dirent.h>
#include <unistd.h>
#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;
}

View file

@ -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

View file

@ -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 <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "LocalCapture.h"
#include "SessionData.h"
#include "Logging.h"
#include "OlyUtility.h"

View file

@ -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

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@ -23,8 +25,6 @@
#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex)
#endif
#include "Logging.h"
// Global thread-safe logging
Logging* logg = NULL;

View file

@ -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 <stdio.h>
#include <string.h>
#include <limits.h>
#ifdef WIN32
#include <windows.h>
#else
#include <pthread.h>
#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;

View file

@ -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 <errno.h>
#include <string.h>
#include <unistd.h>
#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;
}

View file

@ -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 <sys/epoll.h>
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

View file

@ -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 <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <netdb.h>
#endif
@ -30,7 +31,7 @@
#define SHUTDOWN_RX_TX SHUT_RDWR
#endif
OlySocket::OlySocket(int port, bool multiple) {
OlyServerSocket::OlyServerSocket(int port) {
#ifdef WIN32
WSADATA wsaData;
if (WSAStartup(0x0202, &wsaData) != 0) {
@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) {
}
#endif
if (multiple) {
createServerSocket(port);
} else {
createSingleServerConnection(port);
createServerSocket(port);
}
OlySocket::OlySocket(int port, const char* host) {
createClientSocket(host, port);
}
OlySocket::OlySocket(int socketID) : mSocketID(socketID) {
}
#ifndef WIN32
OlyServerSocket::OlyServerSocket(const char* path) {
// Create socket
mFDServer = socket(PF_UNIX, SOCK_STREAM, 0);
if (mFDServer < 0) {
logg->logError(__FILE__, __LINE__, "Error creating server socket");
handleException();
}
unlink(path);
// Create sockaddr_in structure, ensuring non-populated fields are zero
struct sockaddr_un sockaddr;
memset((void*)&sockaddr, 0, sizeof(sockaddr));
sockaddr.sun_family = AF_UNIX;
strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
// Bind the socket to an address
if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
logg->logError(__FILE__, __LINE__, "Binding of server socket failed.");
handleException();
}
// Listen for connections on this socket
if (listen(mFDServer, 1) < 0) {
logg->logError(__FILE__, __LINE__, "Listening of server socket failed");
handleException();
}
}
OlySocket::OlySocket(int port, char* host) {
mFDServer = 0;
createClientSocket(host, port);
OlySocket::OlySocket(const char* path) {
mSocketID = socket(PF_UNIX, SOCK_STREAM, 0);
if (mSocketID < 0) {
return;
}
// Create sockaddr_in structure, ensuring non-populated fields are zero
struct sockaddr_un sockaddr;
memset((void*)&sockaddr, 0, sizeof(sockaddr));
sockaddr.sun_family = AF_UNIX;
strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
close(mSocketID);
mSocketID = -1;
return;
}
}
#endif
OlySocket::~OlySocket() {
if (mSocketID > 0) {
CLOSE_SOCKET(mSocketID);
}
}
OlyServerSocket::~OlyServerSocket() {
if (mFDServer > 0) {
CLOSE_SOCKET(mFDServer);
}
}
void OlySocket::shutdownConnection() {
// Shutdown is primarily used to unblock other threads that are blocking on send/receive functions
shutdown(mSocketID, SHUTDOWN_RX_TX);
@ -70,7 +129,7 @@ void OlySocket::closeSocket() {
}
}
void OlySocket::closeServerSocket() {
void OlyServerSocket::closeServerSocket() {
if (CLOSE_SOCKET(mFDServer) != 0) {
logg->logError(__FILE__, __LINE__, "Failed to close server socket.");
handleException();
@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() {
mFDServer = 0;
}
void OlySocket::createClientSocket(char* hostname, int portno) {
void OlySocket::createClientSocket(const char* hostname, int portno) {
#ifdef WIN32
// TODO: Implement for Windows
#else
@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) {
#endif
}
void OlySocket::createSingleServerConnection(int port) {
createServerSocket(port);
mSocketID = acceptConnection();
closeServerSocket();
}
void OlySocket::createServerSocket(int port) {
void OlyServerSocket::createServerSocket(int port) {
int family = AF_INET6;
// Create socket
@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) {
// mSocketID is always set to the most recently accepted connection
// The user of this class should maintain the different socket connections, e.g. by forking the process
int OlySocket::acceptConnection() {
int OlyServerSocket::acceptConnection() {
int socketID;
if (mFDServer <= 0) {
logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket");
handleException();
}
// Accept a connection, note that this call blocks until a client connects
mSocketID = accept(mFDServer, NULL, NULL);
if (mSocketID < 0) {
socketID = accept(mFDServer, NULL, NULL);
if (socketID < 0) {
logg->logError(__FILE__, __LINE__, "Socket acceptance failed");
handleException();
}
return mSocketID;
return socketID;
}
void OlySocket::send(char* buffer, int size) {
void OlySocket::send(const char* buffer, int size) {
if (size <= 0 || buffer == NULL) {
return;
}

View file

@ -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 <string.h>
class OlySocket {
public:
OlySocket(int port, bool multipleConnections = false);
OlySocket(int port, char* hostname);
OlySocket(int port, const char* hostname);
OlySocket(int socketID);
#ifndef WIN32
OlySocket(const char* path);
#endif
~OlySocket();
int acceptConnection();
void closeSocket();
void closeServerSocket();
void shutdownConnection();
void send(char* buffer, int size);
void sendString(const char* string) {send((char*)string, strlen(string));}
void send(const char* buffer, int size);
int receive(char* buffer, int size);
int receiveNBytes(char* buffer, int size);
int receiveString(char* buffer, int size);
int getSocketID() {return mSocketID;}
bool isValid() const { return mSocketID >= 0; }
private:
int mSocketID, mFDServer;
void createClientSocket(char* hostname, int port);
void createSingleServerConnection(int port);
int mSocketID;
void createClientSocket(const char* hostname, int port);
};
class OlyServerSocket {
public:
OlyServerSocket(int port);
#ifndef WIN32
OlyServerSocket(const char* path);
#endif
~OlyServerSocket();
int acceptConnection();
void closeServerSocket();
private:
int mFDServer;
void createServerSocket(int port);
};

View file

@ -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

View file

@ -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

View file

@ -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 <sys/ioctl.h>
#include <sys/mman.h>
#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<struct perf_event_mmap_page *>(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<struct perf_event_mmap_page *>(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<struct perf_event_mmap_page *>(mBuf[cpu]);
const __u64 head = pemp->data_head;
const __u64 tail = pemp->data_tail;
if (head > tail) {
const uint8_t *const b = static_cast<uint8_t *>(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<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA);
// Write data
if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) {
// Not wrapped
sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA);
} else {
// Wrapped
sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA);
sender->writeData(reinterpret_cast<const char *>(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;
}

View file

@ -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

View file

@ -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 <dirent.h>
#include <sys/utsname.h>
#include <time.h>
#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;
}

View file

@ -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

View file

@ -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 <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#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);
}
}
}

View file

@ -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

View file

@ -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 <errno.h>
#include <string.h>
#include <unistd.h>
#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<int>(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<int>(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();
}
}

View file

@ -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 <semaphore.h>
#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

179
tools/gator/daemon/Proc.cpp Normal file
View file

@ -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 <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

17
tools/gator/daemon/Proc.h Normal file
View file

@ -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

View file

@ -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 <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include "Sender.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.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;

View file

@ -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

View file

@ -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 <string.h>
#include "SessionData.h"
#include <string.h>
#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);
}
}
}

View file

@ -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 <stdint.h>
#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;

View file

@ -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 <string.h>
#include <stdlib.h>
#include <limits.h>
#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);
}
}

View file

@ -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);

View file

@ -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<Source *>(arg)->run();
return NULL;
}

View file

@ -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 <pthread.h>
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

View file

@ -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 <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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);

View file

@ -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 <stdint.h>
#include <string.h>
class OlySocket;
// Commands from Streamline
enum {

View file

@ -0,0 +1,76 @@
/**
* 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 <sys/socket.h>
#include <linux/netlink.h>
#include <string.h>
#include <unistd.h>
#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;
}

View file

@ -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

Some files were not shown because too many files have changed in this diff Show more