Merge branch 'v3.10/topic/gator' of git://git.linaro.org/kernel/linux-linaro-stable into linux-linaro-lsk
This commit is contained in:
commit
fb8cade31f
118 changed files with 4275 additions and 1115 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
******************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
168
drivers/gator/gator_buffer.c
Normal file
168
drivers/gator/gator_buffer.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 &);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
17
tools/gator/daemon/Config.h
Normal file
17
tools/gator/daemon/Config.h
Normal 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
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
52
tools/gator/daemon/DriverSource.h
Normal file
52
tools/gator/daemon/DriverSource.h
Normal 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
|
||||
139
tools/gator/daemon/DynBuf.cpp
Normal file
139
tools/gator/daemon/DynBuf.cpp
Normal 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;
|
||||
}
|
||||
52
tools/gator/daemon/DynBuf.h
Normal file
52
tools/gator/daemon/DynBuf.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
56
tools/gator/daemon/ExternalSource.cpp
Normal file
56
tools/gator/daemon/ExternalSource.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
40
tools/gator/daemon/ExternalSource.h
Normal file
40
tools/gator/daemon/ExternalSource.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
61
tools/gator/daemon/Monitor.cpp
Normal file
61
tools/gator/daemon/Monitor.cpp
Normal 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;
|
||||
}
|
||||
32
tools/gator/daemon/Monitor.h
Normal file
32
tools/gator/daemon/Monitor.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
139
tools/gator/daemon/PerfBuffer.cpp
Normal file
139
tools/gator/daemon/PerfBuffer.cpp
Normal 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;
|
||||
}
|
||||
39
tools/gator/daemon/PerfBuffer.h
Normal file
39
tools/gator/daemon/PerfBuffer.h
Normal 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
|
||||
355
tools/gator/daemon/PerfDriver.cpp
Normal file
355
tools/gator/daemon/PerfDriver.cpp
Normal 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;
|
||||
}
|
||||
56
tools/gator/daemon/PerfDriver.h
Normal file
56
tools/gator/daemon/PerfDriver.h
Normal 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
|
||||
206
tools/gator/daemon/PerfGroup.cpp
Normal file
206
tools/gator/daemon/PerfGroup.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
tools/gator/daemon/PerfGroup.h
Normal file
55
tools/gator/daemon/PerfGroup.h
Normal 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
|
||||
271
tools/gator/daemon/PerfSource.cpp
Normal file
271
tools/gator/daemon/PerfSource.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
54
tools/gator/daemon/PerfSource.h
Normal file
54
tools/gator/daemon/PerfSource.h
Normal 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
179
tools/gator/daemon/Proc.cpp
Normal 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
17
tools/gator/daemon/Proc.h
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
33
tools/gator/daemon/Source.cpp
Normal file
33
tools/gator/daemon/Source.cpp
Normal 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;
|
||||
}
|
||||
40
tools/gator/daemon/Source.h
Normal file
40
tools/gator/daemon/Source.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
76
tools/gator/daemon/UEvent.cpp
Normal file
76
tools/gator/daemon/UEvent.cpp
Normal 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;
|
||||
}
|
||||
36
tools/gator/daemon/UEvent.h
Normal file
36
tools/gator/daemon/UEvent.h
Normal 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
Loading…
Add table
Add a link
Reference in a new issue