silent_chk_events.h = :
$(obj)/gator_src_md5.h: FORCE
@$($(quiet)chk_events.h)
- $(Q)cd $(srctree) && cd $(src) ; $(CONFIG_SHELL) -c "echo 'static char *gator_src_md5 = \"'\`ls *.c *.h mali/*.h | grep -Ev '^(gator_src_md5\.c|gator\.mod\.c)$$' | LC_ALL=C sort | xargs cat | md5sum | cut -b 1-32\`'\";'" > $(abspath $@)
+ $(Q)cd $(srctree) && cd $(src) ; $(CONFIG_SHELL) -c "echo 'static char *gator_src_md5 = \"'\`ls *.c *.h mali/*.h | grep -Ev '^(gator_src_md5\.h|gator\.mod\.c)$$' | LC_ALL=C sort | xargs cat | md5sum | cut -b 1-32\`'\";'" > $(abspath $@)
else
/******************************************************************************
* Tracepoints
******************************************************************************/
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
-# error Kernels prior to 2.6.32 not supported
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+# error Kernels prior to 3.4.0 not supported. DS-5 v5.21 and earlier supported 2.6.32 and later.
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
# define GATOR_DEFINE_PROBE(probe_name, proto) \
static void probe_##probe_name(PARAMS(proto))
tracepoint_probe_unregister(gator_tracepoint_##probe_name, probe_##probe_name, NULL)
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+#define setup_deferrable_timer_on_stack setup_timer
+#endif
+
/******************************************************************************
* Events
******************************************************************************/
struct gator_interface {
+ const char *const name;
/* Complementary function to init */
void (*shutdown)(void);
int (*create_files)(struct super_block *sb, struct dentry *root);
/* called in process context but may not be running on core 'cpu' */
void (*offline_dispatch)(int cpu, bool migrate);
int (*read)(int **buffer, bool sched_switch);
- int (*read64)(long long **buffer);
+ int (*read64)(long long **buffer, bool sched_switch);
int (*read_proc)(long long **buffer, struct task_struct *);
struct list_head list;
};
+u64 gator_get_time(void);
int gator_events_install(struct gator_interface *interface);
int gator_events_get_key(void);
u32 gator_cpuid(void);
*
*/
+#include <linux/mount.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+
+struct mount {
+ struct mount *mnt_parent;
+ struct dentry *mnt_mountpoint;
+ struct vfsmount mnt;
+};
+
+static inline struct mount *real_mount(struct vfsmount *mnt)
+{
+ return container_of(mnt, struct mount, mnt);
+}
+
+#define GET_MNT_ROOT(mount) (mount)->mnt.mnt_root
+
+#else
+
+#define GET_MNT_ROOT(mount) (mount)->mnt_root
+
+#endif
+
/* must be power of 2 */
#define COOKIEMAP_ENTRIES 1024
/* must be a power of 2 - 512/4 = 128 entries */
};
static DEFINE_PER_CPU(char *, translate_text);
+static DEFINE_PER_CPU(char *, scratch);
static DEFINE_PER_CPU(uint32_t, cookie_next_key);
static DEFINE_PER_CPU(uint64_t *, cookie_keys);
static DEFINE_PER_CPU(uint32_t *, cookie_values);
return cookie;
/* On 64-bit android app_process can be app_process32 or app_process64 */
- if (strncmp(text, APP_PROCESS, sizeof(APP_PROCESS) - 1) == 0) {
+ if (strstr(text, APP_PROCESS) != NULL) {
if (!translate_app_process(&text, cpu, task, from_wq))
return UNRESOLVED_COOKIE;
}
return cookie;
}
+/* Can't call d_path in interrupt context so create something similar */
+static const char *gator_d_path(const struct path *path, char *buf, int buflen)
+{
+ struct dentry *dentry = path->dentry;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+ struct mount *mount = real_mount(path->mnt);
+#else
+ struct vfsmount *mount = path->mnt;
+#endif
+ int pos = buflen - 1;
+ int len;
+
+ buf[pos] = '\0';
+
+ for (;;) {
+ if (dentry == NULL) {
+ pr_err("gator: dentry is null!\n");
+ break;
+ }
+ if (dentry->d_name.name[0] == '\0') {
+ pr_err("gator: path is empty string\n");
+ break;
+ }
+ if (dentry->d_name.name[0] == '/' && dentry->d_name.name[1] == '\0') {
+ /* Normal operation */
+ /* pr_err("gator: path is /\n"); */
+ break;
+ }
+
+ len = strlen(dentry->d_name.name);
+ if (pos < len) {
+ pr_err("gator: path is too long\n");
+ break;
+ }
+ pos -= len;
+ memcpy(buf + pos, dentry->d_name.name, len);
+
+ if (pos == 0) {
+ pr_err("gator: no room for slash\n");
+ /* Fall back to name only */
+ return path->dentry->d_name.name;
+ }
+ --pos;
+ buf[pos] = '/';
+
+ if (dentry->d_parent == GET_MNT_ROOT(mount)) {
+ /* pr_err("gator: filesystem is complete, moving to next '%s'\n", buf + pos); */
+ dentry = mount->mnt_mountpoint;
+ mount = mount->mnt_parent;
+ continue;
+ }
+ if (dentry == dentry->d_parent) {
+ pr_err("gator: parent is self\n");
+ break;
+ }
+ dentry = dentry->d_parent;
+ }
+
+ if (pos < 0) {
+ pr_err("gator: pos is somenow negative\n");
+ /* Fall back to name only */
+ return path->dentry->d_name.name;
+ }
+
+ return buf + pos;
+}
+
+#define d_path gator_d_path
+
static int get_exec_cookie(int cpu, struct task_struct *task)
{
struct mm_struct *mm = task->mm;
return NO_COOKIE;
if (task && task->mm && task->mm->exe_file) {
- text = task->mm->exe_file->f_path.dentry->d_name.name;
+ text = d_path(&task->mm->exe_file->f_path, per_cpu(scratch, get_physical_cpu()), PAGE_SIZE);
return get_cookie(cpu, task, text, false);
}
continue;
if (vma->vm_file) {
- text = vma->vm_file->f_path.dentry->d_name.name;
+ text = d_path(&vma->vm_file->f_path, per_cpu(scratch, get_physical_cpu()), PAGE_SIZE);
cookie = get_cookie(cpu, task, text, false);
*offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start;
} else {
err = -ENOMEM;
goto cookie_setup_error;
}
+
+ per_cpu(scratch, cpu) = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!per_cpu(scratch, cpu)) {
+ err = -ENOMEM;
+ goto cookie_setup_error;
+ }
}
/* build CRC32 table */
gator_crc32_table[i] = crc;
}
- setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0);
+ setup_deferrable_timer_on_stack(&app_process_wake_up_timer, app_process_wake_up_handler, 0);
cookie_setup_error:
return err;
kfree(per_cpu(translate_text, cpu));
per_cpu(translate_text, cpu) = NULL;
+
+ kfree(per_cpu(scratch, cpu));
+ per_cpu(scratch, cpu) = NULL;
}
del_timer_sync(&app_process_wake_up_timer);
}
static struct gator_interface gator_events_armv6_interface = {
+ .name = "armv6",
.create_files = gator_events_armv6_create_files,
.stop = gator_events_armv6_stop,
.online = gator_events_armv6_online,
}
static struct gator_interface gator_events_armv7_interface = {
+ .name = "armv7",
.create_files = gator_events_armv7_create_files,
.stop = gator_events_armv7_stop,
.online = gator_events_armv7_online,
}
static struct gator_interface gator_events_block_interface = {
+ .name = "block",
.create_files = gator_events_block_create_files,
.start = gator_events_block_start,
.stop = gator_events_block_stop,
}
static struct gator_interface gator_events_irq_interface = {
+ .name = "irq",
.create_files = gator_events_irq_create_files,
.online = gator_events_irq_online,
.start = gator_events_irq_start,
}
static struct gator_interface gator_events_l2c310_interface = {
+ .name = "l2c-310",
.create_files = gator_events_l2c310_create_files,
.start = gator_events_l2c310_start,
.stop = gator_events_l2c310_stop,
static unsigned long counter_key[NUMBER_OF_EVENTS];
/* The data we have recorded */
-static u32 counter_data[NUMBER_OF_EVENTS];
-/* The address to sample (or 0 if samples are sent to us) */
-static u32 *counter_address[NUMBER_OF_EVENTS];
+static atomic_t counter_data[NUMBER_OF_EVENTS];
/* An array used to return the data we recorded
* as key,value pairs hence the *2
GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value))
{
if (is_hw_counter(event_id))
- counter_data[event_id] = value;
+ atomic_add(value, &counter_data[event_id]);
}
GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void *surface_id, unsigned int *counters))
/* Copy over the values for those counters which are enabled. */
for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) {
if (counter_enabled[i])
- counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]);
+ atomic_add(counters[i - FIRST_SW_COUNTER], &counter_data[i]);
}
}
if (mali_get_counters)
pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters);
else
- pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined\n");
+ pr_debug("gator: mali _mali_profiling_get_counters symbol not defined\n");
mali_get_l2_counters = symbol_get(_mali_profiling_get_l2_counters);
if (mali_get_l2_counters)
pr_debug("gator: mali online _mali_profiling_get_l2_counters symbol @ %p\n", mali_get_l2_counters);
else
- pr_debug("gator WARNING: mali _mali_profiling_get_l2_counters symbol not defined\n");
+ pr_debug("gator: mali _mali_profiling_get_l2_counters symbol not defined\n");
if (!mali_get_counters && !mali_get_l2_counters) {
- pr_debug("gator: WARNING: no L2 counters available\n");
+ pr_err("gator: no L2 counters available\n");
n_l2_cores = 0;
}
/* Clear counters in the start */
for (i = 0; i < NUMBER_OF_EVENTS; i++) {
- counter_data[i] = 0;
+ atomic_set(&counter_data[i], 0);
prev_set[i] = false;
}
}
for (cnt = 0; cnt < NUMBER_OF_EVENTS; cnt++) {
counter_enabled[cnt] = 0;
counter_event[cnt] = 0;
- counter_address[cnt] = NULL;
}
mali_counter_deinitialize();
for (counter_id = from_counter; counter_id <= to_counter; counter_id++) {
if (counter_enabled[counter_id]) {
- counter_dump[(*len)++] = counter_key[counter_id];
- counter_dump[(*len)++] = counter_data[counter_id];
+ unsigned int value;
- counter_data[counter_id] = 0;
+ counter_dump[(*len)++] = counter_key[counter_id];
+ value = atomic_read(&counter_data[counter_id]);
+ atomic_sub(value, &counter_data[counter_id]);
+ counter_dump[(*len)++] = value;
}
}
}
* Add in the voltage and frequency counters if enabled. Note
* that, since these are actually passed as events, the counter
* value should not be cleared.
+ *
+ * Intentionally use atomic_read as these are absolute counters
*/
cnt = COUNTER_FREQUENCY;
if (counter_enabled[cnt]) {
counter_dump[len++] = counter_key[cnt];
- counter_dump[len++] = counter_data[cnt];
+ counter_dump[len++] = atomic_read(&counter_data[cnt]);
}
cnt = COUNTER_VOLTAGE;
if (counter_enabled[cnt]) {
counter_dump[len++] = counter_key[cnt];
- counter_dump[len++] = counter_data[cnt];
+ counter_dump[len++] = atomic_read(&counter_data[cnt]);
}
}
#endif
}
static struct gator_interface gator_events_mali_interface = {
+ .name = "mali_4xx",
.create_files = create_files,
.start = start,
.stop = stop,
extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv)
{
#ifdef DVFS_REPORTED_BY_DDK
- counter_data[COUNTER_FREQUENCY] = frequency_mhz;
- counter_data[COUNTER_VOLTAGE] = voltage_mv;
+ /* Intentionally use atomic_set as these are absolute counters */
+ atomic_set(&counter_data[COUNTER_FREQUENCY], frequency_mhz);
+ atomic_set(&counter_data[COUNTER_VOLTAGE], voltage_mv);
#endif
}
counter_enabled[cnt] = 0;
counter_event[cnt] = 0;
counter_key[cnt] = gator_events_get_key();
- counter_address[cnt] = NULL;
- counter_data[cnt] = 0;
+ atomic_set(&counter_data[cnt], 0);
}
trace_registered = 0;
dir = gatorfs_mkdir(sb, root, buf);
if (dir == NULL) {
- pr_debug("gator: %s: error creating file system for: %s (%s)\n", mali_name, event_name, buf);
+ pr_err("gator: %s: error creating file system for: %s (%s)\n", mali_name, event_name, buf);
return -1;
}
err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled);
if (err != 0) {
- pr_debug("gator: %s: error calling gatorfs_create_ulong for: %s (%s)\n", mali_name, event_name, buf);
+ pr_err("gator: %s: error calling gatorfs_create_ulong for: %s (%s)\n", mali_name, event_name, buf);
return -1;
}
err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key);
if (err != 0) {
- pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
+ pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
return -1;
}
if (counter->cores != -1) {
err = gatorfs_create_ro_ulong(sb, dir, "cores", &counter->cores);
if (err != 0) {
- pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
+ pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
return -1;
}
}
if (event != NULL) {
err = gatorfs_create_ulong(sb, dir, "event", event);
if (err != 0) {
- pr_debug("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
+ pr_err("gator: %s: error calling gatorfs_create_ro_ulong for: %s (%s)\n", mali_name, event_name, buf);
return -1;
}
}
/* Hold the previous timestamp, used to calculate the sample interval. */
static struct timespec prev_timestamp;
+static unsigned long long previous_shader_bitmask;
+static unsigned long long previous_tiler_bitmask;
+static unsigned long long previous_l2_bitmask;
+
/**
* Returns the timespan (in microseconds) between the two specified timestamps.
*
#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */
#define BIT_AT(value, pos) ((value >> pos) & 1)
- static unsigned long long previous_shader_bitmask;
- static unsigned long long previous_tiler_bitmask;
- static unsigned long long previous_l2_bitmask;
-
switch (event_id) {
case SHADER_PRESENT_LO:
{
static int register_tracepoints(void)
{
if (GATOR_REGISTER_TRACE(mali_pm_status)) {
- pr_debug("gator: Mali-Midgard: mali_pm_status tracepoint failed to activate\n");
+ pr_err("gator: Mali-Midgard: mali_pm_status tracepoint failed to activate\n");
return 0;
}
if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) {
- pr_debug("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint failed to activate\n");
+ pr_err("gator: Mali-Midgard: mali_page_fault_insert_pages tracepoint failed to activate\n");
return 0;
}
if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) {
- pr_debug("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint failed to activate\n");
+ pr_err("gator: Mali-Midgard: mali_mmu_as_in_use tracepoint failed to activate\n");
return 0;
}
if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) {
- pr_debug("gator: Mali-Midgard: mali_mmu_as_released tracepoint failed to activate\n");
+ pr_err("gator: Mali-Midgard: mali_mmu_as_released tracepoint failed to activate\n");
return 0;
}
if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) {
- pr_debug("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint failed to activate\n");
+ pr_err("gator: Mali-Midgard: mali_total_alloc_pages_change tracepoint failed to activate\n");
return 0;
}
unsigned int cnt;
mali_profiling_control_type *mali_control;
+ previous_shader_bitmask = 0;
+ previous_tiler_bitmask = 0;
+ previous_l2_bitmask = 0;
+
/* Clean all data for the next capture */
for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) {
timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0;
}
static struct gator_interface gator_events_mali_midgard_interface = {
+ .name = "mali_midgard",
.create_files = create_files,
.start = start,
.stop = stop,
static struct mali_counter *counters;
/* An array used to return the data we recorded as key,value pairs */
-static int *counter_dump;
+static long long *counter_dump;
+static uint64_t last_read_time;
extern struct mali_counter mali_activity[3];
#endif
}
-/**
- * Determines whether a read should take place
- * @param current_time The current time, obtained from getnstimeofday()
- * @param prev_time_s The number of seconds at the previous read attempt.
- * @param next_read_time_ns The time (in ns) when the next read should be allowed.
- *
- * Note that this function has been separated out here to allow it to be tested.
- */
-static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns)
-{
- /* If the current ns count rolls over a second, roll the next read time too. */
- if (current_time->tv_sec != *prev_time_s)
- *next_read_time_ns = *next_read_time_ns - NSEC_PER_SEC;
-
- /* Abort the read if the next read time has not arrived. */
- if (current_time->tv_nsec < *next_read_time_ns)
- return 0;
-
- /* Set the next read some fixed time after this one, and update the read timestamp. */
- *next_read_time_ns = current_time->tv_nsec + READ_INTERVAL_NSEC;
-
- *prev_time_s = current_time->tv_sec;
- return 1;
-}
-
static int start(void)
{
#if MALI_DDK_GATOR_API_VERSION != 3
#endif
int cnt;
+ last_read_time = 0;
+
#if MALI_DDK_GATOR_API_VERSION == 3
/* Setup HW counters */
num_hardware_counters_enabled = 0;
/* If we already got a context, fail */
if (kbcontext) {
- pr_debug("gator: Mali-Midgard: error context already present\n");
+ pr_err("gator: Mali-Midgard: error context already present\n");
goto out;
}
/* kbcontext will only be valid after all the Mali symbols are loaded successfully */
kbcontext = kbase_create_context_symbol(kbdevice);
if (!kbcontext) {
- pr_debug("gator: Mali-Midgard: error creating kbase context\n");
+ pr_err("gator: Mali-Midgard: error creating kbase context\n");
goto out;
}
kernel_dump_buffer = kbase_va_alloc_symbol(kbcontext, 4096, &kernel_dump_buffer_handle);
#endif
if (!kernel_dump_buffer) {
- pr_debug("gator: Mali-Midgard: error trying to allocate va\n");
+ pr_err("gator: Mali-Midgard: error trying to allocate va\n");
goto destroy_context;
}
/* Use kbase API to enable hardware counters and provide dump buffer */
err = kbase_instr_hwcnt_enable_symbol(kbcontext, &setup);
if (err != MALI_ERROR_NONE) {
- pr_debug("gator: Mali-Midgard: can't setup hardware counters\n");
+ pr_err("gator: Mali-Midgard: can't setup hardware counters\n");
goto free_buffer;
}
pr_debug("gator: Mali-Midgard: hardware counters enabled\n");
return 2;
}
-static int read(int **buffer, bool sched_switch)
+static int read(long long **buffer, bool sched_switch)
{
int cnt;
int len = 0;
uint32_t success;
- struct timespec current_time;
- static u32 prev_time_s;
- static s32 next_read_time_ns;
+ uint64_t curr_time;
if (!on_primary_core() || sched_switch)
return 0;
- getnstimeofday(¤t_time);
+ /*
+ * Report the HW counters
+ * Only process hardware counters if at least one of the hardware counters is enabled.
+ */
+ if (num_hardware_counters_enabled <= 0)
+ return 0;
+
+ curr_time = gator_get_time();
/*
* Discard reads unless a respectable time has passed. This
* reduces the load on the GPU without sacrificing accuracy on
* the Streamline display.
*/
- if (!is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns))
+ if (curr_time - last_read_time < READ_INTERVAL_NSEC)
return 0;
- /*
- * Report the HW counters
- * Only process hardware counters if at least one of the hardware counters is enabled.
- */
- if (num_hardware_counters_enabled > 0) {
#if MALI_DDK_GATOR_API_VERSION == 3
- if (!handles)
- return -1;
+ if (!handles)
+ return -1;
- /* Mali symbols can be called safely since a kbcontext is valid */
- if (kbase_gator_instr_hwcnt_dump_complete_symbol(handles, &success)) {
+ /* Mali symbols can be called safely since a kbcontext is valid */
+ if (kbase_gator_instr_hwcnt_dump_complete_symbol(handles, &success)) {
#else
- if (!kbcontext)
- return -1;
+ if (!kbcontext)
+ return -1;
- /* Mali symbols can be called safely since a kbcontext is valid */
- if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success)) {
+ /* Mali symbols can be called safely since a kbcontext is valid */
+ if (kbase_instr_hwcnt_dump_complete_symbol(kbcontext, &success)) {
#endif
- kbase_device_busy = false;
+ kbase_device_busy = false;
+
+ /*
+ * If last_read_time is zero, then this result is from a previous
+ * capture or in error.
+ */
+ if (success && last_read_time > 0) {
+ /* Backdate these events to when they were requested */
+ counter_dump[len++] = 0;
+ counter_dump[len++] = last_read_time;
- if (success) {
- /* Cycle through hardware counters and accumulate totals */
- for (cnt = 0; cnt < number_of_hardware_counters; cnt++) {
- const struct mali_counter *counter = &counters[cnt];
+ /* Cycle through hardware counters and accumulate totals */
+ for (cnt = 0; cnt < number_of_hardware_counters; cnt++) {
+ const struct mali_counter *counter = &counters[cnt];
- if (counter->enabled)
- len += read_counter(cnt, len, counter);
- }
+ if (counter->enabled)
+ len += read_counter(cnt, len, counter);
}
+
+ /* Restore the timestamp */
+ counter_dump[len++] = 0;
+ counter_dump[len++] = curr_time;
}
+ }
- if (!kbase_device_busy) {
- kbase_device_busy = true;
+ if (!kbase_device_busy) {
+ kbase_device_busy = true;
+ last_read_time = curr_time;
#if MALI_DDK_GATOR_API_VERSION == 3
- kbase_gator_instr_hwcnt_dump_irq_symbol(handles);
+ kbase_gator_instr_hwcnt_dump_irq_symbol(handles);
#else
- kbase_instr_hwcnt_dump_irq_symbol(kbcontext);
+ kbase_instr_hwcnt_dump_irq_symbol(kbcontext);
#endif
- }
}
/* Update the buffer */
hardware_counter_names = NULL;
if (kbase_gator_hwcnt_term_names_symbol != NULL) {
kbase_gator_hwcnt_term_names_symbol();
- pr_err("Released symbols\n");
+ pr_debug("gator: Released symbols\n");
}
SYMBOL_CLEANUP(kbase_gator_hwcnt_term_names);
}
static struct gator_interface gator_events_mali_midgard_interface = {
+ .name = "mali_midgard_hw",
.shutdown = shutdown,
.create_files = create_files,
.start = start,
.stop = stop,
- .read = read
+ .read64 = read
};
int gator_events_mali_midgard_hw_init(void)
#endif
counters = kmalloc(sizeof(*counters)*number_of_hardware_counters, GFP_KERNEL);
- counter_dump = kmalloc(sizeof(*counter_dump)*number_of_hardware_counters*2, GFP_KERNEL);
+ counter_dump = kmalloc(sizeof(*counter_dump)*number_of_hardware_counters*2 + 4, GFP_KERNEL);
gator_mali_initialise_counters(mali_activity, ARRAY_SIZE(mali_activity));
gator_mali_initialise_counters(counters, number_of_hardware_counters);
/**
* Test functions for mali_t600_hw code.
*/
-
-static int is_read_scheduled(const struct timespec *current_time, u32 *prev_time_s, s32 *next_read_time_ns);
-
-static int test_is_read_scheduled(u32 s, u32 ns, u32 prev_s, s32 next_ns, int expected_result, s32 expected_next_ns)
-{
- struct timespec current_time;
- u32 prev_time_s = prev_s;
- s32 next_read_time_ns = next_ns;
-
- current_time.tv_sec = s;
- current_time.tv_nsec = ns;
-
- if (is_read_scheduled(¤t_time, &prev_time_s, &next_read_time_ns) != expected_result) {
- pr_err("Failed do_read(%u, %u, %u, %d): expected %d\n", s, ns, prev_s, next_ns, expected_result);
- return 0;
- }
-
- if (next_read_time_ns != expected_next_ns) {
- pr_err("Failed: next_read_ns expected=%d, actual=%d\n", expected_next_ns, next_read_time_ns);
- return 0;
- }
-
- return 1;
-}
-
-static void test_all_is_read_scheduled(void)
-{
- const int HIGHEST_NS = 999999999;
- int n_tests_passed = 0;
-
- pr_err("gator: running tests on %s\n", __FILE__);
-
- n_tests_passed += test_is_read_scheduled(0, 0, 0, 0, 1, READ_INTERVAL_NSEC); /* Null time */
- n_tests_passed += test_is_read_scheduled(100, 1000, 0, 0, 1, READ_INTERVAL_NSEC + 1000); /* Initial values */
-
- n_tests_passed += test_is_read_scheduled(100, HIGHEST_NS, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500);
- n_tests_passed += test_is_read_scheduled(101, 0001, 100, HIGHEST_NS + 500, 0, HIGHEST_NS + 500 - NSEC_PER_SEC);
- n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500 - NSEC_PER_SEC, 1, 600 + READ_INTERVAL_NSEC);
-
- n_tests_passed += test_is_read_scheduled(101, 600, 100, HIGHEST_NS + 500, 1, 600 + READ_INTERVAL_NSEC);
-
- pr_err("gator: %d tests passed\n", n_tests_passed);
-}
static unsigned int mem_event;
static void wq_sched_handler(struct work_struct *wsptr);
-DECLARE_WORK(work, wq_sched_handler);
+static DECLARE_WORK(work, wq_sched_handler);
static struct timer_list meminfo_wake_up_timer;
static void meminfo_wake_up_handler(unsigned long unused_data);
if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo")))
goto kthread_run_exit;
#else
- setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0);
+ setup_deferrable_timer_on_stack(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0);
#endif
return 0;
#endif
-static int gator_events_meminfo_read(long long **buffer)
+static int gator_events_meminfo_read(long long **buffer, bool sched_switch)
{
#if !USE_THREAD
static unsigned int last_mem_event;
}
static struct gator_interface gator_events_meminfo_interface = {
+ .name = "meminfo",
.create_files = gator_events_meminfo_create_files,
.start = gator_events_meminfo_start,
.stop = gator_events_meminfo_stop,
}
static struct gator_interface gator_events_mmapped_interface = {
+ .name = "mmapped",
.create_files = gator_events_mmapped_create_files,
.start = gator_events_mmapped_start,
.stop = gator_events_mmapped_stop,
tx_total = tx;
}
-DECLARE_WORK(wq_get_stats, get_network_stats);
+static DECLARE_WORK(wq_get_stats, get_network_stats);
static void net_wake_up_handler(unsigned long unused_data)
{
get_network_stats(0);
netPrev[NETRX] = rx_total;
netPrev[NETTX] = tx_total;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
- setup_timer(&net_wake_up_timer, net_wake_up_handler, 0);
-#else
setup_deferrable_timer_on_stack(&net_wake_up_timer, net_wake_up_handler, 0);
-#endif
return 0;
}
}
static struct gator_interface gator_events_net_interface = {
+ .name = "net",
.create_files = gator_events_net_create_files,
.start = gator_events_net_start,
.stop = gator_events_net_stop,
event->pevent = perf_event_create_kernel_counter(event->pevent_attr, cpu, 0, handler, 0);
#endif
if (IS_ERR(event->pevent)) {
- pr_debug("gator: unable to online a counter on cpu %d\n", cpu);
+ pr_err("gator: unable to online a counter on cpu %d\n", cpu);
event->pevent = NULL;
return;
}
if (event->pevent->state != PERF_EVENT_STATE_ACTIVE) {
- pr_debug("gator: inactive counter on cpu %d\n", cpu);
+ pr_err("gator: inactive counter on cpu %d\n", cpu);
perf_event_release_kernel(event->pevent);
event->pevent = NULL;
return;
}
static struct gator_interface gator_events_perf_pmu_interface = {
+ .name = "perf_pmu",
.create_files = gator_events_perf_pmu_create_files,
.start = gator_events_perf_pmu_start,
.stop = gator_events_perf_pmu_stop,
cci_name = "CCI_400";
break;
case 1:
- cci_name = "CCI_400-r1";
+ cci_name = "CCI_400_r1";
break;
default:
- pr_debug("gator: unrecognized cci-400 revision\n");
+ pr_err("gator: unrecognized cci-400 revision\n");
return;
}
}
if (pe->pmu != NULL && type == pe->pmu->type) {
- if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400-r1", pe->pmu->name) == 0) {
+ pr_notice("gator: perf pmu: %s\n", pe->pmu->name);
+ if (strcmp("CCI", pe->pmu->name) == 0 || strcmp("CCI_400", pe->pmu->name) == 0 || strcmp("CCI_400_r1", pe->pmu->name) == 0) {
+ pr_notice("gator: Adding CCI-400 counters with type %i\n", type);
gator_events_perf_pmu_cci_400_init(type);
} else if (strcmp("CCI_500", pe->pmu->name) == 0) {
+ pr_notice("gator: Adding CCI-500 counters with type %i\n", type);
gator_events_perf_pmu_uncore_init("CCI_500", type, CCI_500, false);
} else if (strcmp("ccn", pe->pmu->name) == 0) {
+ pr_notice("gator: Adding CCN counters with type %i\n", type);
gator_events_perf_pmu_uncore_init("ARM_CCN_5XX", type, CCN_5XX, true);
} else if ((gator_cpu = gator_find_cpu_by_pmu_name(pe->pmu->name)) != NULL) {
found_cpu = true;
+ pr_notice("gator: Adding cpu counters for %s with type %i\n", gator_cpu->pmnc_name, type);
gator_events_perf_pmu_cpu_init(gator_cpu, type);
}
/* Initialize gator_attrs for dynamic PMUs here */
return -1;
}
}
+ pr_notice("gator: Adding cpu counters (based on cpuid) for %s\n", gator_cpu->pmnc_name);
gator_events_perf_pmu_cpu_init(gator_cpu, PERF_TYPE_RAW);
}
}
static struct gator_interface gator_events_sched_interface = {
+ .name = "sched",
.create_files = gator_events_sched_create_files,
.start = gator_events_sched_start,
.stop = gator_events_sched_stop,
}
static struct gator_interface gator_events_scorpion_interface = {
+ .name = "scorpion",
.create_files = gator_events_scorpion_create_files,
.stop = gator_events_scorpion_stop,
.online = gator_events_scorpion_online,
#define gatorfs_MAGIC 0x24051020
#define TMPBUFSIZE 50
-DEFINE_SPINLOCK(gatorfs_lock);
+static DEFINE_SPINLOCK(gatorfs_lock);
static struct inode *gatorfs_get_inode(struct super_block *sb, int mode)
{
*
*/
-void (*callback)(void);
-DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
-DEFINE_PER_CPU(ktime_t, hrtimer_expire);
-DEFINE_PER_CPU(int, hrtimer_is_active);
+static void (*callback)(void);
+static DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
+static DEFINE_PER_CPU(ktime_t, hrtimer_expire);
+static DEFINE_PER_CPU(int, hrtimer_is_active);
static ktime_t profiling_interval;
static void gator_hrtimer_online(void);
static void gator_hrtimer_offline(void);
*/
/* This version must match the gator daemon version */
-#define PROTOCOL_VERSION 21
+#define PROTOCOL_VERSION 22
static unsigned long gator_protocol_version = PROTOCOL_VERSION;
#include <linux/slab.h>
/******************************************************************************
* Prototypes
******************************************************************************/
-static u64 gator_get_time(void);
static void gator_emit_perf_time(u64 time);
static void gator_op_create_files(struct super_block *sb, struct dentry *root);
{
.cpuid = CORTEX_A53,
.core_name = "Cortex-A53",
- .pmnc_name = "ARM_Cortex-A53",
+ .pmnc_name = "ARMv8_Cortex_A53",
.dt_name = "arm,cortex-a53",
.pmnc_counters = 6,
},
{
.cpuid = CORTEX_A57,
.core_name = "Cortex-A57",
- .pmnc_name = "ARM_Cortex-A57",
+ .pmnc_name = "ARMv8_Cortex_A57",
.dt_name = "arm,cortex-a57",
.pmnc_counters = 6,
},
{
.cpuid = CORTEX_A72,
.core_name = "Cortex-A72",
- .pmnc_name = "ARM_Cortex-A72",
+ .pmnc_name = "ARMv8_Cortex_A72",
.dt_name = "arm,cortex-a72",
.pmnc_counters = 6,
},
list_for_each_entry(gi, &gator_events, list) {
if (gi->offline) {
len = gi->offline(&buffer, migrate);
+ if (len < 0)
+ pr_err("gator: offline failed for %s\n", gi->name);
marshal_event(len, buffer);
}
}
list_for_each_entry(gi, &gator_events, list) {
if (gi->online) {
len = gi->online(&buffer, migrate);
+ if (len < 0)
+ pr_err("gator: online failed for %s\n", gi->name);
marshal_event(len, buffer);
}
}
return 0;
}
-static u64 gator_get_time(void)
+u64 gator_get_time(void)
{
struct timespec ts;
u64 timestamp;
struct dentry *dir;
struct gator_interface *gi;
int cpu;
+ int err;
/* reinitialize default values */
gator_cpu_cores = 0;
/* Linux Events */
dir = gatorfs_mkdir(sb, root, "events");
list_for_each_entry(gi, &gator_events, list)
- if (gi->create_files)
- gi->create_files(sb, dir);
+ if (gi->create_files) {
+ err = gi->create_files(sb, dir);
+ if (err != 0) {
+ pr_err("gator: create_files failed for %s\n", gi->name);
+ }
+ }
/* Sched Events */
sched_trace_create_files(sb, dir);
return -1;
}
- setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0);
+ setup_deferrable_timer_on_stack(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0);
/* Initialize the list of cpuids */
memset(gator_cpuids, -1, sizeof(gator_cpuids));
{
unsigned long flags;
int cpu = 0;
+ char buf[32];
local_irq_save(flags);
gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY);
gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, monotonic_delta);
gator_buffer_write_string(cpu, SUMMARY_BUF, "uname");
gator_buffer_write_string(cpu, SUMMARY_BUF, uname);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, "PAGESIZE");
+ snprintf(buf, sizeof(buf), "%lu", PAGE_SIZE);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, buf);
#if GATOR_IKS_SUPPORT
gator_buffer_write_string(cpu, SUMMARY_BUF, "iks");
gator_buffer_write_string(cpu, SUMMARY_BUF, "");
gator_marshal_activity_switch(core, key, last_activity, last_pid);
}
-void mali_activity_clear(struct mali_counter mali_activity[], size_t mali_activity_size)
+static void mali_activity_clear(struct mali_counter mali_activity[], size_t mali_activity_size)
{
int activity;
int cores;
list_for_each_entry(gi, &gator_events, list) {
if (gi->read) {
len = gi->read(&buffer, sched_switch);
+ if (len < 0)
+ pr_err("gator: read failed for %s\n", gi->name);
marshal_event(len, buffer);
} else if (gi->read64) {
- len = gi->read64(&buffer64);
+ len = gi->read64(&buffer64, sched_switch);
+ if (len < 0)
+ pr_err("gator: read64 failed for %s\n", gi->name);
marshal_event64(len, buffer64);
}
if (gi->read_proc && task != NULL) {
len = gi->read_proc(&buffer64, task);
+ if (len < 0)
+ pr_err("gator: read_proc failed for %s\n", gi->name);
marshal_event64(len, buffer64);
}
}
-I$(DDK_DIR)/include \
-I$(KBASE_DIR)/osk/src/linux/include \
-I$(KBASE_DIR)/platform_dummy \
- -I$(KBASE_DIR)/src
+ -I$(KBASE_DIR)/src \
+ -Idrivers/staging/android \
LOCAL_SRC_FILES := \
AnnotateListener.cpp \
+ AtraceDriver.cpp \
Buffer.cpp \
CCNDriver.cpp \
CapturedXML.cpp \
DriverSource.cpp \
DynBuf.cpp \
EventsXML.cpp \
+ ExternalDriver.cpp \
ExternalSource.cpp \
FSDriver.cpp \
Fifo.cpp \
FtraceDriver.cpp \
- FtraceSource.cpp \
HwmonDriver.cpp \
KMod.cpp \
LocalCapture.cpp \
--- /dev/null
+/**
+ * Copyright (C) ARM Limited 2014-2015. All rights reserved.
+ *
+ * This 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 "AtraceDriver.h"
+
+#include <unistd.h>
+
+/*
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "DriverSource.h"
+#include "Setup.h"
+*/
+
+#include "Logging.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
+
+class AtraceCounter : public DriverCounter {
+public:
+ AtraceCounter(DriverCounter *next, char *name, int flag);
+ ~AtraceCounter();
+
+ int getFlag() const { return mFlag; }
+
+private:
+ const int mFlag;
+
+ // Intentionally unimplemented
+ AtraceCounter(const AtraceCounter &);
+ AtraceCounter &operator=(const AtraceCounter &);
+};
+
+AtraceCounter::AtraceCounter(DriverCounter *next, char *name, int flag) : DriverCounter(next, name), mFlag(flag) {
+}
+
+AtraceCounter::~AtraceCounter() {
+}
+
+AtraceDriver::AtraceDriver() : mSupported(false), mNotifyPath() {
+}
+
+AtraceDriver::~AtraceDriver() {
+}
+
+void AtraceDriver::readEvents(mxml_node_t *const xml) {
+ if (!gSessionData->mFtraceDriver.isSupported()) {
+ logg->logMessage("Atrace support disabled, ftrace support is required");
+ return;
+ }
+ if (access("/system/bin/setprop", X_OK) != 0) {
+ logg->logMessage("Atrace support disabled, setprop is not found, this is not an Android target");
+ return;
+ }
+
+ if (util->getApplicationFullPath(mNotifyPath, sizeof(mNotifyPath)) != 0) {
+ logg->logMessage("Unable to determine the full path of gatord, the cwd will be used");
+ }
+ strncat(mNotifyPath, "notify.dex", sizeof(mNotifyPath) - strlen(mNotifyPath) - 1);
+ if (access(mNotifyPath, W_OK) != 0) {
+ logg->logMessage("Atrace support disabled, unable to locate notify.dex");
+ return;
+ }
+
+ mSupported = true;
+
+ mxml_node_t *node = xml;
+ while (true) {
+ node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
+ if (node == NULL) {
+ break;
+ }
+ const char *counter = mxmlElementGetAttr(node, "counter");
+ if (counter == NULL) {
+ continue;
+ }
+
+ if (strncmp(counter, "atrace_", 7) != 0) {
+ continue;
+ }
+
+ const char *flag = mxmlElementGetAttr(node, "flag");
+ if (flag == NULL) {
+ logg->logError("The atrace counter %s is missing the required flag attribute", counter);
+ handleException();
+ }
+ setCounters(new AtraceCounter(getCounters(), strdup(counter), strtol(flag, NULL, 16)));
+ }
+}
+
+void AtraceDriver::setAtrace(const int flags) {
+ logg->logMessage("Setting atrace flags to %i\n", flags);
+ pid_t pid = fork();
+ if (pid < 0) {
+ logg->logError("fork failed");
+ handleException();
+ } else if (pid == 0) {
+ char buf[1<<10];
+ snprintf(buf, sizeof(buf), "setprop debug.atrace.tags.enableflags %i; "
+ "dalvikvm -cp %s com.android.internal.util.WithFramework Notify", flags, mNotifyPath);
+ execlp("sh", "sh", "-c", buf, NULL);
+ exit(0);
+ }
+}
+
+void AtraceDriver::start() {
+ if (!mSupported) {
+ return;
+ }
+
+ int flags = 0;
+ for (AtraceCounter *counter = static_cast<AtraceCounter *>(getCounters()); counter != NULL; counter = static_cast<AtraceCounter *>(counter->getNext())) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ flags |= counter->getFlag();
+ }
+
+ setAtrace(flags);
+}
+
+void AtraceDriver::stop() {
+ if (!mSupported) {
+ return;
+ }
+
+ setAtrace(0);
+}
--- /dev/null
+/**
+ * Copyright (C) ARM Limited 2015. All rights reserved.
+ *
+ * This 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 ATRACEDRIVER_H
+#define ATRACEDRIVER_H
+
+#include "mxml/mxml.h"
+
+#include "Driver.h"
+
+class AtraceDriver : public SimpleDriver {
+public:
+ AtraceDriver();
+ ~AtraceDriver();
+
+ void readEvents(mxml_node_t *const xml);
+
+ void start();
+ void stop();
+
+ bool isSupported() const { return mSupported; }
+
+private:
+ void setAtrace(const int flags);
+
+ bool mSupported;
+ char mNotifyPath[256];
+
+ // Intentionally unimplemented
+ AtraceDriver(const AtraceDriver &);
+ AtraceDriver &operator=(const AtraceDriver &);
+};
+
+#endif // ATRACEDRIVER_H
}
}
+bool Buffer::hasUncommittedMessages() const {
+ const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
+ int length = mWritePos - mCommitPos;
+ if (length < 0) {
+ length += mSize;
+ }
+ length = length - typeLength - sizeof(int32_t);
+ return length > FRAME_HEADER_SIZE;
+}
+
void Buffer::commit(const uint64_t time, const bool force) {
// 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;
}
}
-void Buffer::summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) {
+void Buffer::summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname, const long pageSize) {
packInt(MESSAGE_SUMMARY);
writeString(NEWLINE_CANARY);
packInt64(timestamp);
packInt64(monotonicDelta);
writeString("uname");
writeString(uname);
+ writeString("PAGESIZE");
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%li", pageSize);
+ writeString(buf);
writeString("");
check(currTime);
}
int bytesAvailable() const;
int contiguousSpaceAvailable() const;
+ bool hasUncommittedMessages() const;
void commit(const uint64_t time, const bool force = false);
void check(const uint64_t time);
// Summary messages
- void summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname);
+ void summary(const uint64_t currTime, const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname, const long pageSize);
void coreName(const uint64_t currTime, const int core, const int cpuid, const char *const name);
// Block Counter messages
#include "Config.h"
#include "DriverSource.h"
#include "Logging.h"
+#include "SessionData.h"
static const char TAG_CATEGORY[] = "category";
static const char TAG_COUNTER_SET[] = "counter_set";
static const char SBAS_REGION[] = "SBAS_Region";
static const char CCN_5XX[] = "CCN-5xx";
#define ARM_CCN_5XX "ARM_CCN_5XX_"
+#define CCN_COUNT 8
+static const char ARM_CCN_5XX_CNT[] = ARM_CCN_5XX "cnt";
static const char *const VC_TYPES[] = { "REQ", "RSP", "SNP", "DAT" };
static const char *const XP_EVENT_NAMES[] = { NULL, "H-bit", "S-bit", "P-Cnt", "TknV" };
void CCNDriver::writeEvents(mxml_node_t *const root) const {
mxml_node_t *const counter_set = mxmlNewElement(root, TAG_COUNTER_SET);
- mxmlElementSetAttr(counter_set, ATTR_NAME, ARM_CCN_5XX "cnt");
- mxmlElementSetAttr(counter_set, ATTR_COUNT, "8");
+ mxmlElementSetAttr(counter_set, ATTR_NAME, ARM_CCN_5XX_CNT);
+ mxmlElementSetAttr(counter_set, ATTR_COUNT, STRIFY(CCN_COUNT));
mxml_node_t *const category = mxmlNewElement(root, TAG_CATEGORY);
mxmlElementSetAttr(category, ATTR_NAME, CCN_5XX);
- mxmlElementSetAttr(category, TAG_COUNTER_SET, ARM_CCN_5XX "cnt");
+ mxmlElementSetAttr(category, TAG_COUNTER_SET, ARM_CCN_5XX_CNT);
mxml_node_t *const clock_event = mxmlNewElement(category, TAG_EVENT);
mxmlElementSetAttr(clock_event, ATTR_COUNTER, ARM_CCN_5XX "ccnt");
}
}
}
+
+void CCNDriver::validateCounters() const {
+ int counts[CCN_COUNT][2] = { { 0 } };
+ const unsigned int mask = getConfig(0xff, 0xff, 0, 0, 0);
+
+ for (int i = 0; i < ARRAY_LENGTH(gSessionData->mCounters); ++i) {
+ const Counter *const counter = &gSessionData->mCounters[i];
+
+ if (!counter->isEnabled()) {
+ continue;
+ }
+
+ if (strncmp(counter->getType(), ARM_CCN_5XX_CNT, sizeof(ARM_CCN_5XX_CNT) - 1) == 0) {
+ const int node = counter->getEvent() & mask;
+
+ for (int j = 0; j < ARRAY_LENGTH(counts); ++j) {
+ if (counts[j][0] == 0) {
+ counts[j][0] = node;
+ }
+ if (counts[j][0] == node) {
+ ++counts[j][1];
+ if (counts[j][1] > 4) {
+ if (asprintf(&gSessionData->mCountersError, "More than 4 events are assigned to the same CCN node") <= 0) {
+ logg->logError("asprintf failed");
+ handleException();
+ }
+ }
+ }
+ }
+ }
+ }
+}
int writeCounters(mxml_node_t *const root) const;
void writeEvents(mxml_node_t *const) const;
+ void validateCounters() const;
+
private:
enum NodeType {
NT_UNKNOWN,
captured = mxmlNewElement(xml, "captured");
mxmlElementSetAttr(captured, "version", "1");
- if (gSessionData->perf.isSetup()) {
+ if (gSessionData->mPerf.isSetup()) {
mxmlElementSetAttr(captured, "type", "Perf");
}
mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION);
#include "Driver.h"
#include "DriverSource.h"
#include "ExternalSource.h"
-#include "FtraceSource.h"
#include "LocalCapture.h"
#include "Logging.h"
#include "OlySocket.h"
static Source *primarySource = NULL;
static Source *externalSource = NULL;
static Source *userSpaceSource = NULL;
-static Source *ftraceSource = NULL;
static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
Child* child = NULL; // shared by Child.cpp and main.cpp
if (child && child->numExceptions++ > 0) {
// it is possible one of the below functions itself can cause an exception, thus allow only one exception
logg->logMessage("Received multiple exceptions, terminating the child");
- exit(1);
+ // Something is really wrong, exit immediately
+ _exit(1);
}
fprintf(stderr, "%s", logg->getLastError());
}
}
- if (gSessionData->mLocalCapture)
+ if (gSessionData->mLocalCapture) {
cleanUp();
+ }
exit(1);
}
while (!externalSource->isDone() ||
(userSpaceSource != NULL && !userSpaceSource->isDone()) ||
- (ftraceSource != NULL && !ftraceSource->isDone()) ||
!primarySource->isDone()) {
sem_wait(&senderSem);
if (userSpaceSource != NULL) {
userSpaceSource->write(sender);
}
- if (ftraceSource != NULL) {
- ftraceSource->write(sender);
- }
primarySource->write(sender);
}
if (userSpaceSource != NULL) {
userSpaceSource->interrupt();
}
- if (ftraceSource != NULL) {
- ftraceSource->interrupt();
- }
sem_post(&haltPipeline);
}
{ ConfigurationXML configuration; }
// Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
- if (!gSessionData->perf.isSetup()) {
+ if (!gSessionData->mPerf.isSetup()) {
primarySource = new DriverSource(&senderSem, &startProfile);
} else {
primarySource = new PerfSource(&senderSem, &startProfile);
free(xmlString);
}
- if (gSessionData->kmod.isMaliCapture() && (gSessionData->mSampleRate == 0)) {
+ if (gSessionData->mKmod.isMaliCapture() && (gSessionData->mSampleRate == 0)) {
logg->logError("Mali counters are not supported with Sample Rate: None.");
handleException();
}
// Initialize ftrace source before child as it's slow and dependens on nothing else
// If initialized later, us gator with ftrace has time sync issues
- if (gSessionData->ftraceDriver.countersEnabled()) {
- ftraceSource = new FtraceSource(&senderSem);
- if (!ftraceSource->prepare()) {
- logg->logError("Unable to prepare userspace source for capture");
- handleException();
- }
- ftraceSource->start();
+ // Must be initialized before senderThread is started as senderThread checks externalSource
+ externalSource = new ExternalSource(&senderSem);
+ if (!externalSource->prepare()) {
+ logg->logError("Unable to prepare external source for capture");
+ handleException();
}
+ externalSource->start();
// Must be after session XML is parsed
if (!primarySource->prepare()) {
- if (gSessionData->perf.isSetup()) {
+ if (gSessionData->mPerf.isSetup()) {
logg->logError("Unable to communicate with the perf API, please ensure that CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER are enabled. Please refer to README_Streamline.txt for more information.");
} else {
logg->logError("Unable to prepare gator driver for capture");
// Sender thread shall be halted until it is signaled for one shot mode
sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
- // Must be initialized before senderThread is started as senderThread checks externalSource
- externalSource = new ExternalSource(&senderSem);
- if (!externalSource->prepare()) {
- logg->logError("Unable to prepare external source for capture");
- handleException();
- }
- externalSource->start();
-
// Create the duration, stop, and sender threads
bool thread_creation_success = true;
if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
}
bool startUSSource = false;
- for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) {
- if (gSessionData->usDrivers[i]->countersEnabled()) {
+ for (int i = 0; i < ARRAY_LENGTH(gSessionData->mUsDrivers); ++i) {
+ if (gSessionData->mUsDrivers[i]->countersEnabled()) {
startUSSource = true;
}
}
primarySource->run();
// Wait for the other threads to exit
- if (ftraceSource != NULL) {
- ftraceSource->join();
- }
if (userSpaceSource != NULL) {
userSpaceSource->join();
}
logg->logMessage("Profiling ended.");
- delete ftraceSource;
delete userSpaceSource;
delete externalSource;
delete primarySource;
#include "Command.h"
#include <fcntl.h>
+#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/prctl.h>
#include "Logging.h"
#include "SessionData.h"
-static int getUid(const char *const name, char *const shPath, const char *const tmpDir) {
+static int getUid(const char *const name, const char *const tmpDir, uid_t *const uid) {
// Lookups may fail when using a different libc or a statically compiled executable
char gatorTemp[32];
snprintf(gatorTemp, sizeof(gatorTemp), "%s/gator_temp", tmpDir);
const int fd = open(gatorTemp, 600, O_CREAT | O_CLOEXEC);
if (fd < 0) {
- return -1;
+ return false;
}
close(fd);
handleException();
}
if (pid == 0) {
- char cargv1[] = "-c";
- char *cargv[] = {
- shPath,
- cargv1,
- cmd,
- NULL,
- };
-
- execv(cargv[0], cargv);
+ execlp("sh", "sh", "-c", cmd, NULL);
exit(-1);
}
while ((waitpid(pid, NULL, 0) < 0) && (errno == EINTR));
result = st.st_uid;
}
unlink(gatorTemp);
- return result;
+ *uid = result;
+ return true;
}
-static int getUid(const char *const name) {
+static bool getUid(const char *const name, uid_t *const uid, gid_t *const gid) {
// Look up the username
struct passwd *const user = getpwnam(name);
if (user != NULL) {
- return user->pw_uid;
+ *uid = user->pw_uid;
+ *gid = user->pw_gid;
+ return true;
}
+ // Unable to get the user without getpwanm, so create a unique uid by adding a fixed number to the pid
+ *gid = 0x484560f8 + getpid();
// Are we on Linux
- char cargv0l[] = "/bin/sh";
- if ((access(cargv0l, X_OK) == 0) && (access("/tmp", W_OK) == 0)) {
- return getUid(name, cargv0l, "/tmp");
+ if (access("/tmp", W_OK) == 0) {
+ return getUid(name, "/tmp", uid);
}
// Are we on android
- char cargv0a[] = "/system/bin/sh";
- if ((access(cargv0a, X_OK) == 0) && (access("/data", W_OK) == 0)) {
- return getUid(name, cargv0a, "/data");
+ if (access("/data", W_OK) == 0) {
+ return getUid(name, "/data", uid);
}
- return -1;
+ return false;
}
void *commandThread(void *) {
prctl(PR_SET_NAME, (unsigned long)&"gatord-command", 0, 0, 0);
const char *const name = gSessionData->mCaptureUser == NULL ? "nobody" : gSessionData->mCaptureUser;
- const int uid = getUid(name);
- if (uid < 0) {
+ uid_t uid;
+ gid_t gid;
+ if (!getUid(name, &uid, &gid)) {
logg->logError("Unable to look up the user %s, please double check that the user exists", name);
handleException();
}
handleException();
}
if (pid == 0) {
- char cargv0l[] = "/bin/sh";
- char cargv0a[] = "/system/bin/sh";
- char cargv1[] = "-c";
- char *cargv[] = {
- cargv0l,
- cargv1,
- gSessionData->mCaptureCommand,
- NULL,
- };
-
buf[0] = '\0';
close(pipefd[0]);
goto fail_exit;
}
- if (setuid(uid) != 0) {
- snprintf(buf, sizeof(buf), "setuid failed");
+ if (setgroups(1, &gid) != 0) {
+ snprintf(buf, sizeof(buf), "setgroups failed");
+ goto fail_exit;
+ }
+ if (setresgid(gid, gid, gid) != 0) {
+ snprintf(buf, sizeof(buf), "setresgid failed");
+ goto fail_exit;
+ }
+ if (setresuid(uid, uid, uid) != 0) {
+ snprintf(buf, sizeof(buf), "setresuid failed");
goto fail_exit;
}
}
}
- execv(cargv[0], cargv);
- cargv[0] = cargv0a;
- execv(cargv[0], cargv);
+ execlp("sh", "sh", "-c", gSessionData->mCaptureCommand, NULL);
snprintf(buf, sizeof(buf), "execv failed");
fail_exit:
#ifndef CONFIG_H
#define CONFIG_H
+#define STRIFY2(ARG) #ARG
+#define STRIFY(ARG) STRIFY2(ARG)
+
#define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0]))
#define ACCESS_ONCE(x) (*(volatile typeof(x)*)&(x))
#define MAX_PERFORMANCE_COUNTERS 50
#define NR_CPUS 32
+// If debugfs is not mounted at /sys/kernel/debug, update TRACING_PATH
+#define TRACING_PATH "/sys/kernel/debug/tracing"
+#define EVENTS_PATH TRACING_PATH "/events"
+
template<typename T>
static inline T min(const T a, const T b) {
return (a < b ? a : b);
mxml_node_t *tree, *node;
int ret;
- // clear counter overflow
- gSessionData->mCounterOverflow = 0;
+ if (gSessionData->mCountersError != NULL) {
+ free(gSessionData->mCountersError);
+ gSessionData->mCountersError = NULL;
+ }
gSessionData->mIsEBS = false;
mIndex = 0;
mxmlDelete(tree);
+ if (gSessionData->mCountersError == NULL && mIndex > MAX_PERFORMANCE_COUNTERS) {
+ if (asprintf(&gSessionData->mCountersError, "Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, mIndex) <= 0) {
+ logg->logError("asprintf failed");
+ handleException();
+ }
+ }
+ gSessionData->mCcnDriver.validateCounters();
+
return ret;
}
// handle all other performance counters
if (mIndex >= MAX_PERFORMANCE_COUNTERS) {
mIndex++;
- gSessionData->mCounterOverflow = mIndex;
return;
}
void DiskIODriver::readEvents(mxml_node_t *const) {
// Only for use with perf
- if (!gSessionData->perf.isSetup()) {
+ if (!gSessionData->mPerf.isSetup()) {
return;
}
extern Child *child;
DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mBuffer(NULL), mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
- int driver_version = 0;
-
mBuffer = new Buffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem);
- if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
- logg->logError("Error reading gator driver version");
- handleException();
- }
-
- // Verify the driver version matches the daemon version
- if (driver_version != PROTOCOL_VERSION) {
- if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
- // One of the mismatched versions is development version
- logg->logError(
- "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
- ">> The following must be synchronized from engineering repository:\n"
- ">> * gator driver\n"
- ">> * gator daemon\n"
- ">> * Streamline", driver_version, PROTOCOL_VERSION);
- handleException();
- } else {
- // Release version mismatch
- logg->logError(
- "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
- ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
- handleException();
- }
- }
+ checkVersion();
int enable = -1;
if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
}
}
+void DriverSource::checkVersion() {
+ int driverVersion = 0;
+
+ if (readIntDriver("/dev/gator/version", &driverVersion) == -1) {
+ logg->logError("Error reading gator driver version");
+ handleException();
+ }
+
+ // Verify the driver version matches the daemon version
+ if (driverVersion != PROTOCOL_VERSION) {
+ if ((driverVersion > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
+ // One of the mismatched versions is development version
+ logg->logError(
+ "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
+ ">> The following must be synchronized from engineering repository:\n"
+ ">> * gator driver\n"
+ ">> * gator daemon\n"
+ ">> * Streamline", driverVersion, PROTOCOL_VERSION);
+ handleException();
+ } else {
+ // Release version mismatch
+ logg->logError(
+ "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
+ ">> Please upgrade the driver and daemon to the latest versions.", driverVersion, PROTOCOL_VERSION);
+ handleException();
+ }
+ }
+}
+
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);
bool isDone();
void write(Sender *sender);
+ static void checkVersion();
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);
// Load the provided or default events xml
if (gSessionData->mEventsXMLPath) {
strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX);
- fl = fopen(path, "r");
+ fl = fopen_cloexec(path, "r");
if (fl) {
xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK);
fclose(fl);
// Append additional events XML
if (gSessionData->mEventsXMLAppend) {
- fl = fopen(gSessionData->mEventsXMLAppend, "r");
+ fl = fopen_cloexec(gSessionData->mEventsXMLAppend, "r");
if (fl == NULL) {
logg->logError("Unable to open additional events XML %s", gSessionData->mEventsXMLAppend);
handleException();
--- /dev/null
+/**
+ * Copyright (C) ARM Limited 2010-2015. All rights reserved.
+ *
+ * This 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 "ExternalDriver.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "SessionData.h"
+
+static const char MALI_UTGARD_SETUP[] = "\0mali-utgard-setup";
+static const char SETUP_VERSION[] = "ANNOTATE_SETUP 1\n";
+static const size_t HEADER_SIZE = 1 + sizeof(uint32_t);
+
+#define HEADER_ERROR 0x80
+#define HEADER_ACK 0x81
+#define HEADER_REQUEST_COUNTERS 0x82
+#define HEADER_COUNTERS 0x83
+#define HEADER_ENABLE_COUNTERS 0x84
+#define HEADER_START 0x85
+
+static uint32_t readLEInt(char *const buf)
+{
+ size_t i;
+ uint32_t v;
+
+ v = 0;
+ for (i = 0; i < sizeof(v); ++i)
+ v |= (uint32_t)buf[i] << 8*i;
+
+ return v;
+}
+
+static int readPackedInt(char *const buf, const size_t bufSize, size_t *const pos, uint64_t *const l)
+{
+ uint8_t shift = 0;
+ uint8_t b = ~0;
+
+ *l = 0;
+ while ((b & 0x80) != 0) {
+ if (*pos >= bufSize) {
+ return -1;
+ }
+ b = buf[*pos];
+ *pos += 1;
+ *l |= (uint64_t)(b & 0x7f) << shift;
+ shift += 7;
+ }
+
+ if (shift < 8*sizeof(*l) && (b & 0x40) != 0) {
+ *l |= -(1 << shift);
+ }
+
+ return 0;
+}
+
+class ExternalCounter : public DriverCounter {
+public:
+ ExternalCounter(DriverCounter *next, const char *name, int cores) : DriverCounter(next, name), mCores(cores), mEvent(-1) {}
+
+ ~ExternalCounter() {
+ }
+
+ int getCores() const { return mCores; }
+ void setEvent(const int event) { mEvent = event; }
+ int getEvent() const { return mEvent; }
+
+private:
+ const int mCores;
+ int mEvent;
+
+ // Intentionally undefined
+ ExternalCounter(const ExternalCounter &);
+ ExternalCounter &operator=(const ExternalCounter &);
+};
+
+ExternalDriver::ExternalDriver() : mUds(-1), mQueried(false), mStarted(false) {
+}
+
+bool ExternalDriver::connect() const {
+ if (mUds < 0) {
+ mUds = OlySocket::connect(MALI_UTGARD_SETUP, sizeof(MALI_UTGARD_SETUP));
+ if (mUds >= 0 && !writeAll(mUds, SETUP_VERSION, sizeof(SETUP_VERSION) - 1)) {
+ logg->logError("Unable to send setup version");
+ handleException();
+ }
+ }
+ return mUds >= 0;
+}
+
+void ExternalDriver::disconnect() {
+ if (mUds >= 0) {
+ close(mUds);
+ mUds = -1;
+ mStarted = false;
+ }
+}
+
+void ExternalDriver::query() const {
+ if (mQueried) {
+ return;
+ }
+ // Only try once even if it fails otherwise not all the possible counters may be shown
+ mQueried = true;
+
+ char *const buf = gSessionData->mSharedData->mMaliUtgardCounters;
+ const size_t bufSize = sizeof(gSessionData->mSharedData->mMaliUtgardCounters);
+ size_t size = 0;
+
+ if (!connect()) {
+ size = gSessionData->mSharedData->mMaliUtgardCountersSize;
+ logg->logMessage("Unable to connect, using cached version; size: %zi", size);
+ } else {
+ gSessionData->mSharedData->mMaliUtgardCountersSize = 0;
+
+ buf[0] = HEADER_REQUEST_COUNTERS;
+ size_t pos = HEADER_SIZE;
+ Buffer::writeLEInt((unsigned char *)(buf + 1), pos);
+ if (!writeAll(mUds, buf, pos)) {
+ logg->logError("Unable to send request counters message");
+ handleException();
+ }
+
+ if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_COUNTERS) {
+ logg->logError("Unable to read request counters response header");
+ handleException();
+ }
+ size = readLEInt(buf + 1);
+ if (size > bufSize || !readAll(mUds, buf, size - HEADER_SIZE)) {
+ logg->logError("Unable to read request counters response");
+ handleException();
+ }
+
+ size -= HEADER_SIZE;
+ gSessionData->mSharedData->mMaliUtgardCountersSize = size;
+ logg->logMessage("Requested counters; size: %zi", size);
+ }
+
+ size_t pos = 0;
+ while (pos < size) {
+ size_t begin = pos;
+ char *name = NULL;
+ uint64_t cores = -1;
+ while (pos < size && buf[pos] != '\0') {
+ ++pos;
+ }
+ if (pos > begin) {
+ name = strndup(buf + begin, pos - begin);
+ }
+ if (pos < size && buf[pos] == '\0') {
+ ++pos;
+ }
+ ;
+ if (name != NULL && readPackedInt(buf, bufSize, &pos, &cores) == 0) {
+ // Cheat so that this can be 'const'
+ ((ExternalDriver *)(this))->setCounters(new ExternalCounter(getCounters(), name, cores));
+ }
+ }
+
+ if (pos != size) {
+ logg->logError("Unable to parse request counters response");
+ handleException();
+ }
+}
+
+void ExternalDriver::start() {
+ if (!connect()) {
+ return;
+ }
+
+ if (mStarted) {
+ return;
+ }
+ // Only start once
+ mStarted = true;
+
+ char buf[1<<12];
+ int pos;
+
+ buf[0] = HEADER_ENABLE_COUNTERS;
+ pos = HEADER_SIZE;
+ for (ExternalCounter *counter = static_cast<ExternalCounter *>(getCounters()); counter != NULL; counter = static_cast<ExternalCounter *>(counter->getNext())) {
+ if (!counter->isEnabled()) {
+ continue;
+ }
+ size_t nameLen = strlen(counter->getName());
+ if (pos + nameLen + 1 + 2*Buffer::MAXSIZE_PACK32 > sizeof(buf)) {
+ logg->logError("Unable to enable counters, message is too large");
+ handleException();
+ }
+ memcpy(buf + pos, counter->getName(), nameLen + 1);
+ pos += nameLen + 1;
+ Buffer::packInt(buf, sizeof(buf), pos, counter->getEvent());
+ Buffer::packInt(buf, sizeof(buf), pos, counter->getKey());
+ }
+ Buffer::writeLEInt((unsigned char *)(buf + 1), pos);
+ if (!writeAll(mUds, buf, pos)) {
+ logg->logError("Unable to send enable counters message");
+ handleException();
+ }
+
+ size_t size = 0;
+ if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) {
+ logg->logError("Unable to read enable counters response header");
+ handleException();
+ }
+ size = readLEInt(buf + 1);
+ if (size != HEADER_SIZE) {
+ logg->logError("Unable to parse enable counters response");
+ handleException();
+ }
+
+ buf[0] = HEADER_START;
+ pos = HEADER_SIZE;
+ // ns/sec / samples/sec = ns/sample
+ // For sample rate of none, sample every 100ms
+ Buffer::packInt(buf, sizeof(buf), pos, NS_PER_S / (gSessionData->mSampleRate == 0 ? 10 : gSessionData->mSampleRate));
+ Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate);
+ Buffer::writeLEInt((unsigned char *)(buf + 1), pos);
+ if (!writeAll(mUds, buf, pos)) {
+ logg->logError("Unable to send start message");
+ handleException();
+ }
+
+ size = 0;
+ if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) {
+ logg->logError("Unable to read start response header");
+ handleException();
+ }
+ size = readLEInt(buf + 1);
+ if (size != HEADER_SIZE) {
+ logg->logError("Unable to parse start response");
+ handleException();
+ }
+}
+
+bool ExternalDriver::claimCounter(const Counter &counter) const {
+ query();
+ return super::claimCounter(counter);
+}
+
+void ExternalDriver::setupCounter(Counter &counter) {
+ ExternalCounter *const externalCounter = static_cast<ExternalCounter *>(findCounter(counter));
+ if (externalCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+ externalCounter->setEnabled(true);
+ counter.setKey(externalCounter->getKey());
+ if (counter.getEvent() != -1) {
+ externalCounter->setEvent(counter.getEvent());
+ }
+ if (externalCounter->getCores() > 0) {
+ counter.setCores(externalCounter->getCores());
+ }
+}
+
+void ExternalDriver::resetCounters() {
+ query();
+ super::resetCounters();
+}
--- /dev/null
+/**
+ * Copyright (C) ARM Limited 2010-2015. All rights reserved.
+ *
+ * This 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 EXTERNALDRIVER_H
+#define EXTERNALDRIVER_H
+
+#include "Driver.h"
+
+class ExternalDriver : public SimpleDriver {
+public:
+ ExternalDriver();
+
+ bool claimCounter(const Counter &counter) const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ void start();
+
+ void disconnect();
+
+private:
+ typedef SimpleDriver super;
+
+ bool connect() const;
+ void query() const;
+
+ mutable int mUds;
+ mutable bool mQueried;
+ bool mStarted;
+
+ // Intentionally unimplemented
+ ExternalDriver(const ExternalDriver &);
+ ExternalDriver &operator=(const ExternalDriver &);
+};
+
+#endif // EXTERNALDRIVER_H
#include <unistd.h>
#include "Child.h"
+#include "DriverSource.h"
#include "Logging.h"
#include "OlySocket.h"
#include "SessionData.h"
static const char MALI_GRAPHICS[] = "\0mali_thirdparty_server";
static const char MALI_GRAPHICS_STARTUP[] = "\0mali_thirdparty_client";
static const char MALI_GRAPHICS_V1[] = "MALI_GRAPHICS 1\n";
+static const char MALI_UTGARD_STARTUP[] = "\0mali-utgard-startup";
+static const char FTRACE_V1[] = "FTRACE 1\n";
-static bool setNonblock(const int fd) {
- int flags;
-
- flags = fcntl(fd, F_GETFL);
- if (flags < 0) {
- logg->logMessage("fcntl getfl failed");
- return false;
- }
-
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
- logg->logMessage("fcntl setfl failed");
- return false;
- }
-
- return true;
-}
-
-ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mAnnotate(8083), mAnnotateUds(STREAMLINE_ANNOTATE, sizeof(STREAMLINE_ANNOTATE), true), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1) {
+ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mUtgardStartupUds(MALI_UTGARD_STARTUP, sizeof(MALI_UTGARD_STARTUP)), mAnnotate(8083), mAnnotateUds(STREAMLINE_ANNOTATE, sizeof(STREAMLINE_ANNOTATE), true), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1), mFtraceFd(-1) {
sem_init(&mBufferSem, 0, 0);
}
}
bool ExternalSource::connectMve() {
- if (!gSessionData->maliVideo.countersEnabled()) {
+ if (!gSessionData->mMaliVideo.countersEnabled()) {
return true;
}
return false;
}
- if (!gSessionData->maliVideo.start(mMveUds)) {
+ if (!gSessionData->mMaliVideo.start(mMveUds)) {
return false;
}
return true;
}
+void ExternalSource::connectFtrace() {
+ if (!gSessionData->mFtraceDriver.isSupported()) {
+ return;
+ }
+
+ gSessionData->mFtraceDriver.prepare();
+
+ mFtraceFd = open(TRACING_PATH "/trace_pipe", O_RDONLY | O_CLOEXEC);
+ if (mFtraceFd < 0) {
+ logg->logError("Unable to open trace_pipe");
+ handleException();
+ }
+
+ configureConnection(mFtraceFd, FTRACE_V1, sizeof(FTRACE_V1));
+}
+
bool ExternalSource::prepare() {
if (!mMonitor.init() ||
!setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd()) ||
!setNonblock(mMaliStartupUds.getFd()) || !mMonitor.add(mMaliStartupUds.getFd()) ||
+ !setNonblock(mUtgardStartupUds.getFd()) || !mMonitor.add(mUtgardStartupUds.getFd()) ||
!setNonblock(mAnnotate.getFd()) || !mMonitor.add(mAnnotate.getFd()) ||
!setNonblock(mAnnotateUds.getFd()) || !mMonitor.add(mAnnotateUds.getFd()) ||
false) {
connectMali();
connectMve();
+ connectFtrace();
+ gSessionData->mExternalDriver.start();
return true;
}
logg->logMessage("Writing to annotate pipe failed");
}
+ if (mFtraceFd >= 0) {
+ gSessionData->mAtraceDriver.start();
+
+ if (DriverSource::writeDriver(TRACING_PATH "/tracing_on", "1") != 0) {
+ logg->logError("Unable to turn ftrace on");
+ handleException();
+ }
+ }
+
+ // Wait until monotonicStarted is set before sending data
+ int64_t monotonicStarted = 0;
+ while (monotonicStarted <= 0 && gSessionData->mSessionIsActive) {
+ usleep(10);
+
+ if (gSessionData->mPerf.isSetup()) {
+ monotonicStarted = gSessionData->mMonotonicStarted;
+ } else {
+ if (DriverSource::readInt64Driver("/dev/gator/started", &monotonicStarted) == -1) {
+ logg->logError("Error reading gator driver start time");
+ handleException();
+ }
+ }
+ }
+
while (gSessionData->mSessionIsActive) {
struct epoll_event events[16];
// Clear any pending sem posts
logg->logError("Unable to configure incoming Mali graphics connection");
handleException();
}
+ } else if (fd == mUtgardStartupUds.getFd()) {
+ // Mali Utgard says it's alive
+ int client = mUtgardStartupUds.acceptConnection();
+ // Don't read from this connection, configure utgard and expect them to reconnect with annotations
+ close(client);
+ gSessionData->mExternalDriver.disconnect();
+ gSessionData->mExternalDriver.start();
} else if (fd == mAnnotate.getFd()) {
int client = mAnnotate.acceptConnection();
if (!setNonblock(client) || !mMonitor.add(client)) {
mBuffer.setDone();
+ if (mFtraceFd >= 0) {
+ gSessionData->mFtraceDriver.stop();
+ gSessionData->mAtraceDriver.stop();
+ close(mFtraceFd);
+ }
+
if (mMveUds >= 0) {
- gSessionData->maliVideo.stop(mMveUds);
+ gSessionData->mMaliVideo.stop(mMveUds);
}
mInterruptFd = -1;
void configureConnection(const int fd, const char *const handshake, size_t size);
bool connectMali();
bool connectMve();
+ void connectFtrace();
sem_t mBufferSem;
Buffer mBuffer;
Monitor mMonitor;
OlyServerSocket mMveStartupUds;
OlyServerSocket mMaliStartupUds;
+ OlyServerSocket mUtgardStartupUds;
OlyServerSocket mAnnotate;
OlyServerSocket mAnnotateUds;
int mInterruptFd;
int mMaliUds;
int mMveUds;
+ int mFtraceFd;
// Intentionally unimplemented
ExternalSource(const ExternalSource &);
#include <regex.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "Config.h"
#include "DriverSource.h"
#include "Logging.h"
+#include "SessionData.h"
#include "Setup.h"
class FtraceCounter : public DriverCounter {
public:
- FtraceCounter(DriverCounter *next, char *name, const char *regex, const char *enable);
+ FtraceCounter(DriverCounter *next, char *name, const char *enable);
~FtraceCounter();
void prepare();
- int read(const char *const line, int64_t *values);
void stop();
private:
- regex_t mReg;
char *const mEnable;
int mWasEnabled;
FtraceCounter &operator=(const FtraceCounter &);
};
-FtraceCounter::FtraceCounter(DriverCounter *next, char *name, const char *regex, const char *enable) : DriverCounter(next, name), mEnable(enable == NULL ? NULL : strdup(enable)) {
- int result = regcomp(&mReg, regex, REG_EXTENDED);
- if (result != 0) {
- char buf[128];
- regerror(result, &mReg, buf, sizeof(buf));
- logg->logError("Invalid regex '%s': %s", regex, buf);
- handleException();
- }
+FtraceCounter::FtraceCounter(DriverCounter *next, char *name, const char *enable) : DriverCounter(next, name), mEnable(enable == NULL ? NULL : strdup(enable)) {
}
FtraceCounter::~FtraceCounter() {
- regfree(&mReg);
if (mEnable != NULL) {
free(mEnable);
}
}
char buf[1<<10];
- snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/enable", mEnable);
+ snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", mEnable);
if ((DriverSource::readIntDriver(buf, &mWasEnabled) != 0) ||
(DriverSource::writeDriver(buf, 1) != 0)) {
logg->logError("Unable to read or write to %s", buf);
}
}
-int FtraceCounter::read(const char *const line, int64_t *values) {
- regmatch_t match[2];
- int result = regexec(&mReg, line, 2, match, 0);
- if (result != 0) {
- // No match
- return 0;
- }
-
- int64_t value;
- if (match[1].rm_so < 0) {
- value = 1;
- } else {
- errno = 0;
- value = strtoll(line + match[1].rm_so, NULL, 0);
- if (errno != 0) {
- logg->logError("Parsing %s failed: %s", getName(), strerror(errno));
- handleException();
- }
- }
-
- values[0] = getKey();
- values[1] = value;
-
- return 1;
-}
-
void FtraceCounter::stop() {
if (mEnable == NULL) {
return;
}
char buf[1<<10];
- snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/enable", mEnable);
+ snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", mEnable);
DriverSource::writeDriver(buf, mWasEnabled);
}
-FtraceDriver::FtraceDriver() : mValues(NULL) {
+FtraceDriver::FtraceDriver() : mValues(NULL), mSupported(false), mTracingOn(0) {
}
FtraceDriver::~FtraceDriver() {
// The perf clock was added in 3.10
if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 10, 0)) {
+ mSupported = false;
logg->logMessage("Unsupported kernel version, to use ftrace please upgrade to Linux 3.10 or later");
return;
}
+ // Is debugfs or tracefs available?
+ if (access(TRACING_PATH, R_OK) != 0) {
+ mSupported = false;
+ logg->logMessage("Unable to locate the tracing directory, disabling ftrace");
+ return;
+ }
+
+ mSupported = true;
+
mxml_node_t *node = xml;
int count = 0;
while (true) {
logg->logError("The regex counter %s is missing the required regex attribute", counter);
handleException();
}
- bool addCounter = true;
+
+ const char *tracepoint = mxmlElementGetAttr(node, "tracepoint");
const char *enable = mxmlElementGetAttr(node, "enable");
+ if (enable == NULL) {
+ enable = tracepoint;
+ }
+ if (gSessionData->mPerf.isSetup() && tracepoint != NULL) {
+ logg->logMessage("Not using ftrace for counter %s", counter);
+ continue;
+ }
if (enable != NULL) {
char buf[1<<10];
- snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/%s/enable", enable);
+ snprintf(buf, sizeof(buf), EVENTS_PATH "/%s/enable", enable);
if (access(buf, W_OK) != 0) {
logg->logMessage("Disabling counter %s, %s not found", counter, buf);
- addCounter = false;
+ continue;
}
}
- if (addCounter) {
- setCounters(new FtraceCounter(getCounters(), strdup(counter), regex, enable));
- ++count;
- }
+
+ logg->logMessage("Using ftrace for %s", counter);
+ setCounters(new FtraceCounter(getCounters(), strdup(counter), enable));
+ ++count;
}
mValues = new int64_t[2*count];
}
counter->prepare();
}
-}
-int FtraceDriver::read(const char *line, int64_t **buf) {
- int count = 0;
+ if (DriverSource::readIntDriver(TRACING_PATH "/tracing_on", &mTracingOn)) {
+ logg->logError("Unable to read if ftrace is enabled");
+ handleException();
+ }
- for (FtraceCounter *counter = static_cast<FtraceCounter *>(getCounters()); counter != NULL; counter = static_cast<FtraceCounter *>(counter->getNext())) {
- if (!counter->isEnabled()) {
- continue;
+ if (DriverSource::writeDriver(TRACING_PATH "/tracing_on", "0") != 0) {
+ logg->logError("Unable to turn ftrace off before truncating the buffer");
+ handleException();
+ }
+
+ {
+ int fd;
+ fd = open(TRACING_PATH "/trace", O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
+ if (fd < 0) {
+ logg->logError("Unable truncate ftrace buffer: %s", strerror(errno));
+ handleException();
}
- count += counter->read(line, mValues + 2*count);
+ close(fd);
}
- *buf = mValues;
- return count;
+ if (DriverSource::writeDriver(TRACING_PATH "/trace_clock", "perf") != 0) {
+ logg->logError("Unable to switch ftrace to the perf clock, please ensure you are running Linux 3.10 or later");
+ handleException();
+ }
}
void FtraceDriver::stop() {
+ DriverSource::writeDriver(TRACING_PATH "/tracing_on", mTracingOn);
+ DriverSource::writeDriver(TRACING_PATH "/trace_clock", "local");
+
for (FtraceCounter *counter = static_cast<FtraceCounter *>(getCounters()); counter != NULL; counter = static_cast<FtraceCounter *>(counter->getNext())) {
if (!counter->isEnabled()) {
continue;
void readEvents(mxml_node_t *const xml);
void prepare();
- int read(const char *line, int64_t **buf);
void stop();
+ bool isSupported() const { return mSupported; }
+
private:
int64_t *mValues;
+ bool mSupported;
+ int mTracingOn;
// Intentionally unimplemented
FtraceDriver(const FtraceDriver &);
+++ /dev/null
-/**
- * Copyright (C) ARM Limited 2010-2015. All rights reserved.
- *
- * This 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 "FtraceSource.h"
-
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include "Child.h"
-#include "DriverSource.h"
-#include "Logging.h"
-#include "SessionData.h"
-
-extern Child *child;
-
-static void handler(int signum)
-{
- (void)signum;
-};
-
-FtraceSource::FtraceSource(sem_t *senderSem) : mFtraceFh(NULL), mBuffer(0, FRAME_BLOCK_COUNTER, 128*1024, senderSem), mTid(-1), mTracingOn(0) {
-}
-
-FtraceSource::~FtraceSource() {
-}
-
-bool FtraceSource::prepare() {
- {
- struct sigaction act;
- act.sa_handler = handler;
- act.sa_flags = (int)SA_RESETHAND;
- if (sigaction(SIGUSR1, &act, NULL) != 0) {
- logg->logError("sigaction failed: %s\n", strerror(errno));
- handleException();
- }
- }
-
- gSessionData->ftraceDriver.prepare();
-
- if (DriverSource::readIntDriver("/sys/kernel/debug/tracing/tracing_on", &mTracingOn)) {
- logg->logError("Unable to read if ftrace is enabled");
- handleException();
- }
-
- if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "0") != 0) {
- logg->logError("Unable to turn ftrace off before truncating the buffer");
- handleException();
- }
-
- {
- int fd;
- fd = open("/sys/kernel/debug/tracing/trace", O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
- if (fd < 0) {
- logg->logError("Unable truncate ftrace buffer: %s", strerror(errno));
- handleException();
- }
- close(fd);
- }
-
- if (DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "perf") != 0) {
- logg->logError("Unable to switch ftrace to the perf clock, please ensure you are running Linux 3.10 or later");
- handleException();
- }
-
- mFtraceFh = fopen_cloexec("/sys/kernel/debug/tracing/trace_pipe", "rb");
- if (mFtraceFh == NULL) {
- logg->logError("Unable to open trace_pipe");
- handleException();
- }
-
- return true;
-}
-
-void FtraceSource::run() {
- prctl(PR_SET_NAME, (unsigned long)&"gatord-ftrace", 0, 0, 0);
- mTid = syscall(__NR_gettid);
-
- if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "1") != 0) {
- logg->logError("Unable to turn ftrace on");
- handleException();
- }
-
- // Wait until monotonicStarted is set before sending data
- int64_t monotonicStarted = 0;
- while (monotonicStarted <= 0 && gSessionData->mSessionIsActive) {
- usleep(10);
-
- if (gSessionData->perf.isSetup()) {
- monotonicStarted = gSessionData->mMonotonicStarted;
- } else {
- if (DriverSource::readInt64Driver("/dev/gator/started", &monotonicStarted) == -1) {
- logg->logError("Error reading gator driver start time");
- handleException();
- }
- }
- }
-
- while (gSessionData->mSessionIsActive) {
- char buf[1<<12];
-
- if (fgets(buf, sizeof(buf), mFtraceFh) == NULL) {
- if (errno == EINTR) {
- // Interrupted by interrupt - likely user request to terminate
- break;
- }
- logg->logError("Unable read trace data: %s", strerror(errno));
- handleException();
- }
-
- const uint64_t currTime = getTime() - gSessionData->mMonotonicStarted;
-
- char *const colon = strstr(buf, ": ");
- if (colon == NULL) {
- if (strstr(buf, " [LOST ") != NULL) {
- logg->logError("Ftrace events lost, aborting the capture. It is recommended to discard this report and collect a new capture. If this error occurs often, please reduce the number of ftrace counters selected or the amount of ftrace events generated.");
- } else {
- logg->logError("Unable to find colon: %s", buf);
- }
- handleException();
- }
- *colon = '\0';
-
- char *const space = strrchr(buf, ' ');
- if (space == NULL) {
- logg->logError("Unable to find space: %s", buf);
- handleException();
- }
- *colon = ':';
-
- int64_t *data = NULL;
- int count = gSessionData->ftraceDriver.read(colon + 2, &data);
- if (count > 0) {
- errno = 0;
- const long long time = strtod(space, NULL) * 1000000000;
- if (errno != 0) {
- logg->logError("Unable to parse time: %s", strerror(errno));
- handleException();
- }
- mBuffer.event64(-1, time);
-
- for (int i = 0; i < count; ++i) {
- mBuffer.event64(data[2*i + 0], data[2*i + 1]);
- }
-
- mBuffer.check(currTime);
-
- if (gSessionData->mOneShot && gSessionData->mSessionIsActive && (mBuffer.bytesAvailable() <= 0)) {
- logg->logMessage("One shot (ftrace)");
- child->endSession();
- }
- }
-
- }
-
- mBuffer.setDone();
-
- DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", mTracingOn);
- fclose(mFtraceFh);
- DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "local");
- gSessionData->ftraceDriver.stop();
-}
-
-void FtraceSource::interrupt() {
- // Closing the underlying file handle does not result in the read on the ftrace file handle to return, so send a signal to the thread
- syscall(__NR_tgkill, getpid(), mTid, SIGUSR1);
-}
-
-bool FtraceSource::isDone() {
- return mBuffer.isDone();
-}
-
-void FtraceSource::write(Sender *sender) {
- // Don't send ftrace data until the summary packet is sent so that monotonic delta is available
- if (!gSessionData->mSentSummary) {
- return;
- }
- if (!mBuffer.isDone()) {
- mBuffer.write(sender);
- }
-}
+++ /dev/null
-/**
- * Copyright (C) ARM Limited 2010-2015. All rights reserved.
- *
- * This 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 FTRACESOURCE_H
-#define FTRACESOURCE_H
-
-#include <semaphore.h>
-#include <stdio.h>
-
-#include "Buffer.h"
-#include "Source.h"
-
-class FtraceSource : public Source {
-public:
- FtraceSource(sem_t *senderSem);
- ~FtraceSource();
-
- bool prepare();
- void run();
- void interrupt();
-
- bool isDone();
- void write(Sender *sender);
-
-private:
- void waitFor(const int bytes);
-
- FILE *mFtraceFh;
- Buffer mBuffer;
- int mTid;
- int mTracingOn;
-
- // Intentionally unimplemented
- FtraceSource(const FtraceSource &);
- FtraceSource &operator=(const FtraceSource &);
-};
-
-#endif // FTRACESOURCE_H
}
void MaliVideoDriver::readEvents(mxml_node_t *const xml) {
+ // Always create the counters as /dev/mv500 may show up after gatord starts
mxml_node_t *node = xml;
while (true) {
node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
int MaliVideoDriver::writeCounters(mxml_node_t *root) const {
if (access("/dev/mv500", F_OK) != 0) {
+ // Don't show the counters in counter configuration
return 0;
}
return super::writeCounters(root);
}
+bool MaliVideoDriver::claimCounter(const Counter &counter) const {
+ if (access("/dev/mv500", F_OK) != 0) {
+ // Don't add the counters to captured XML
+ return 0;
+ }
+
+ return super::claimCounter(counter);
+}
+
void MaliVideoDriver::marshalEnable(const MaliVideoCounterType type, char *const buf, const size_t bufsize, int &pos) {
// size
int numEnabled = 0;
}
}
-static bool writeAll(const int mveUds, const char *const buf, const int pos) {
- int written = 0;
- while (written < pos) {
- size_t bytes = ::write(mveUds, buf + written, pos - written);
- if (bytes <= 0) {
- logg->logMessage("write failed");
- return false;
- }
- written += bytes;
- }
-
- return true;
-}
-
bool MaliVideoDriver::start(const int mveUds) {
char buf[256];
int pos = 0;
// data_protocol_version
Buffer::packInt(buf, sizeof(buf), pos, 1);
// sample_rate - convert samples/second to ms/sample
- Buffer::packInt(buf, sizeof(buf), pos, 1000/gSessionData->mSampleRate);
+ Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mSampleRate/1000);
// live_rate - convert ns/flush to ms/flush
Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate/1000000);
buf[pos++] = 'T';
buf[pos++] = 'O';
buf[pos++] = 'P';
- marshalEnable(MVCT_COUNTER, buf, sizeof(buf), pos);
writeAll(mveUds, buf, pos);
void readEvents(mxml_node_t *const root);
int writeCounters(mxml_node_t *root) const;
+ bool claimCounter(const Counter &counter) const;
bool start(const int mveUds);
void stop(const int mveUds);
void MemInfoDriver::readEvents(mxml_node_t *const) {
// Only for use with perf
- if (!gSessionData->perf.isSetup()) {
+ if (!gSessionData->mPerf.isSetup()) {
return;
}
void NetDriver::readEvents(mxml_node_t *const) {
// Only for use with perf
- if (!gSessionData->perf.isSetup()) {
+ if (!gSessionData->mPerf.isSetup()) {
return;
}
{ 0x51049, "KraitSIM", "Krait", 4 },
{ 0x5104d, "Krait", "Krait", 4 },
{ 0x5106f, "Krait S4 Pro", "Krait", 4 },
- { 0x41d03, "Cortex-A53", "ARM_Cortex-A53", 6 },
- { 0x41d07, "Cortex-A57", "ARM_Cortex-A57", 6 },
- { 0x41d08, "Cortex-A72", "ARM_Cortex-A72", 6 },
+ { 0x41d03, "Cortex-A53", "ARMv8_Cortex_A53", 6 },
+ { 0x41d07, "Cortex-A57", "ARMv8_Cortex_A57", 6 },
+ { 0x41d08, "Cortex-A72", "ARMv8_Cortex_A72", 6 },
};
static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-";
static const struct uncore_counter uncore_counters[] = {
{ "CCI_400", "CCI_400", 4, true },
- { "CCI_400-r1", "CCI_400-r1", 4, true },
+ { "CCI_400_r1", "CCI_400_r1", 4, true },
{ "CCI_500", "CCI_500", 8, false },
{ "ccn", "ARM_CCN_5XX", 8, true },
};
class PerfCounter : public DriverCounter {
public:
- PerfCounter(DriverCounter *next, const char *name, uint32_t type, uint64_t config, uint64_t sampleType, uint64_t flags) : DriverCounter(next, name), mType(type), mConfig(config), mSampleType(sampleType), mFlags(flags), mCount(0) {}
+ PerfCounter(DriverCounter *next, const char *name, uint32_t type, uint64_t config, uint64_t sampleType, uint64_t flags, const int count) : DriverCounter(next, name), mType(type), mConfig(config), mSampleType(sampleType), mFlags(flags), mCount(count) {}
~PerfCounter() {
}
uint64_t getConfig() const { return mConfig; }
void setConfig(const uint64_t config) { mConfig = config; }
uint64_t getSampleType() const { return mSampleType; }
+ void setSampleType(uint64_t sampleType) { mSampleType = sampleType; }
uint64_t getFlags() const { return mFlags; }
virtual void read(Buffer *const, const int) {}
private:
const uint32_t mType;
uint64_t mConfig;
- const uint64_t mSampleType;
+ uint64_t mSampleType;
const uint64_t mFlags;
int mCount;
class CPUFreqDriver : public PerfCounter {
public:
- CPUFreqDriver(DriverCounter *next, uint64_t id) : PerfCounter(next, "Linux_power_cpu_freq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU) {}
+ CPUFreqDriver(DriverCounter *next, uint64_t id) : PerfCounter(next, "Linux_power_cpu_freq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU, 1) {}
void read(Buffer *const buffer, const int cpu) {
char buf[64];
PerfDriver::~PerfDriver() {
}
+class PerfTracepoint {
+public:
+ PerfTracepoint(PerfTracepoint *const next, const DriverCounter *const counter, const char *const tracepoint) : mNext(next), mCounter(counter), mTracepoint(tracepoint) {}
+
+ PerfTracepoint *getNext() const { return mNext; }
+ const DriverCounter *getCounter() const { return mCounter; }
+ const char *getTracepoint() const { return mTracepoint; }
+
+private:
+ PerfTracepoint *const mNext;
+ const DriverCounter *const mCounter;
+ const char *const mTracepoint;
+
+ // Intentionally undefined
+ PerfTracepoint(const PerfTracepoint &);
+ PerfTracepoint &operator=(const PerfTracepoint &);
+};
+
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);
- setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
+ setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0));
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);
- setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
+ setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0));
}
}
len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
name = new char[len];
snprintf(name, len, "%s_ccnt", counterName);
- setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0));
+ setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0, 0));
}
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);
- setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0));
+ setCounters(new PerfCounter(getCounters(), name, type, -1, PERF_SAMPLE_READ, 0, 0));
+ }
+}
+
+void PerfDriver::readEvents(mxml_node_t *const xml) {
+ mxml_node_t *node = xml;
+ DynBuf printb;
+
+ // Only for use with perf
+ if (!isSetup()) {
+ return;
+ }
+
+ while (true) {
+ node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
+ if (node == NULL) {
+ break;
+ }
+ const char *counter = mxmlElementGetAttr(node, "counter");
+ if (counter == NULL) {
+ continue;
+ }
+
+ if (strncmp(counter, "ftrace_", 7) != 0) {
+ continue;
+ }
+
+ const char *tracepoint = mxmlElementGetAttr(node, "tracepoint");
+ if (tracepoint == NULL) {
+ const char *regex = mxmlElementGetAttr(node, "regex");
+ if (regex == NULL) {
+ logg->logError("The tracepoint counter %s is missing the required tracepoint attribute", counter);
+ handleException();
+ } else {
+ logg->logMessage("Not using perf for counter %s", counter);
+ continue;
+ }
+ }
+
+ const char *arg = mxmlElementGetAttr(node, "arg");
+
+ long long id = getTracepointId(tracepoint, &printb);
+ if (id >= 0) {
+ logg->logMessage("Using perf for %s", counter);
+ setCounters(new PerfCounter(getCounters(), strdup(counter), PERF_TYPE_TRACEPOINT, id, arg == NULL ? 0 : PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU, 1));
+ mTracepoints = new PerfTracepoint(mTracepoints, getCounters(), strdup(tracepoint));
+ }
}
}
struct dirent *dirent;
while ((dirent = readdir(dir)) != NULL) {
+ logg->logMessage("perf pmu: %s", dirent->d_name);
for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
const struct gator_cpu *const gator_cpu = &gator_cpus[i];
}
foundCpu = true;
- logg->logMessage("Adding cpu counters for %s", gator_cpu->pmnc_name);
+ logg->logMessage("Adding cpu counters for %s with type %i", gator_cpu->pmnc_name, type);
addCpuCounters(gator_cpu->pmnc_name, type, gator_cpu->pmnc_counters);
}
continue;
}
- logg->logMessage("Adding uncore counters for %s", uncore_counters[i].gatorName);
+ logg->logMessage("Adding uncore counters for %s with type %i", uncore_counters[i].gatorName, type);
addUncoreCounters(uncore_counters[i].gatorName, type, uncore_counters[i].count, uncore_counters[i].hasCyclesCounter);
}
}
id = getTracepointId("irq/softirq_exit", &printb);
if (id >= 0) {
- setCounters(new PerfCounter(getCounters(), "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
+ setCounters(new PerfCounter(getCounters(), "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0));
}
id = getTracepointId("irq/irq_handler_exit", &printb);
if (id >= 0) {
- setCounters(new PerfCounter(getCounters(), "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
+ setCounters(new PerfCounter(getCounters(), "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0));
}
id = getTracepointId(SCHED_SWITCH, &printb);
if (id >= 0) {
- setCounters(new PerfCounter(getCounters(), "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU));
+ setCounters(new PerfCounter(getCounters(), "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id, PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU, 0));
}
id = getTracepointId(CPU_FREQUENCY, &printb);
- if (id >= 0) {
+ if (id >= 0 && access("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq", R_OK) == 0) {
setCounters(new CPUFreqDriver(getCounters(), id));
}
- setCounters(new PerfCounter(getCounters(), "Linux_cpu_wait_contention", TYPE_DERIVED, -1, 0, 0));
+ setCounters(new PerfCounter(getCounters(), "Linux_cpu_wait_contention", TYPE_DERIVED, -1, 0, 0, 0));
//Linux_cpu_wait_io
char buf[512];
snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine);
+ long pageSize = sysconf(_SC_PAGESIZE);
+ if (pageSize < 0) {
+ logg->logMessage("sysconf _SC_PAGESIZE failed");
+ return false;
+ }
+
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
logg->logMessage("clock_gettime failed");
gSessionData->mMonotonicStarted = monotonicStarted;
const uint64_t currTime = 0;//getTime() - gSessionData->mMonotonicStarted;
- buffer->summary(currTime, timestamp, monotonicStarted, monotonicStarted, buf);
+ buffer->summary(currTime, timestamp, monotonicStarted, monotonicStarted, buf, pageSize);
for (int i = 0; i < gSessionData->mCores; ++i) {
coreName(currTime, buffer, i);
}
void PerfDriver::coreName(const uint64_t currTime, Buffer *const buffer, const int cpu) {
+ const SharedData *const sharedData = gSessionData->mSharedData;
// Don't send information on a cpu we know nothing about
- if (gSessionData->mCpuIds[cpu] == -1) {
+ if (sharedData->mCpuIds[cpu] == -1) {
return;
}
int j;
for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) {
- if (gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) {
+ if (gator_cpus[j].cpuid == sharedData->mCpuIds[cpu]) {
break;
}
}
- if (j < ARRAY_LENGTH(gator_cpus) && gator_cpus[j].cpuid == gSessionData->mCpuIds[cpu]) {
- buffer->coreName(currTime, cpu, gSessionData->mCpuIds[cpu], gator_cpus[j].core_name);
+ if (j < ARRAY_LENGTH(gator_cpus) && gator_cpus[j].cpuid == sharedData->mCpuIds[cpu]) {
+ buffer->coreName(currTime, cpu, sharedData->mCpuIds[cpu], gator_cpus[j].core_name);
} else {
char buf[32];
- if (gSessionData->mCpuIds[cpu] == -1) {
+ if (sharedData->mCpuIds[cpu] == -1) {
snprintf(buf, sizeof(buf), "Unknown");
} else {
- snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[cpu]);
+ snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", sharedData->mCpuIds[cpu]);
}
- buffer->coreName(currTime, cpu, gSessionData->mCpuIds[cpu], buf);
+ buffer->coreName(currTime, cpu, sharedData->mCpuIds[cpu], buf);
}
}
if (counter.getEvent() != -1) {
perfCounter->setConfig(counter.getEvent());
}
- perfCounter->setCount(counter.getCount());
+ if (counter.getCount() > 0) {
+ // EBS
+ perfCounter->setCount(counter.getCount());
+ // Collect samples
+ perfCounter->setSampleType(perfCounter->getSampleType() | PERF_SAMPLE_TID | PERF_SAMPLE_IP);
+ }
perfCounter->setEnabled(true);
counter.setKey(perfCounter->getKey());
}
bool PerfDriver::enable(const uint64_t currTime, PerfGroup *const group, Buffer *const buffer) const {
for (PerfCounter *counter = static_cast<PerfCounter *>(getCounters()); counter != NULL; counter = static_cast<PerfCounter *>(counter->getNext())) {
- if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) {
- int count = counter->getCount();
- uint64_t sampleType = counter->getSampleType();
- if (sampleType & PERF_SAMPLE_RAW) {
- // If raw is enabled, every sample is needed
- count = 1;
- }
- if (!group->add(currTime, buffer, counter->getKey(), counter->getType(), counter->getConfig(), count,
- // use getCount instead of count as EBS counters need TID and IP but RAW tracepoints don't
- (counter->getCount() > 0 ? PERF_SAMPLE_TID | PERF_SAMPLE_IP : 0) | sampleType,
- counter->getFlags())) {
- logg->logMessage("PerfGroup::add failed");
- return false;
- }
+ if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED) &&
+ !group->add(currTime, buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), counter->getSampleType(), counter->getFlags())) {
+ logg->logMessage("PerfGroup::add failed");
+ return false;
}
}
}
}
+static bool sendTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
+ if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
+ logg->logMessage("DynBuf::printf failed");
+ return false;
+ }
+ if (!b->read(printb->getBuf())) {
+ logg->logMessage("DynBuf::read failed");
+ return false;
+ }
+ buffer->marshalFormat(currTime, b->getLength(), b->getBuf());
+
+ return true;
+}
+
+bool PerfDriver::sendTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b) {
+ if (
+ !sendTracepointFormat(currTime, buffer, SCHED_SWITCH, printb, b) ||
+ !sendTracepointFormat(currTime, buffer, CPU_IDLE, printb, b) ||
+ !sendTracepointFormat(currTime, buffer, CPU_FREQUENCY, printb, b) ||
+ false) {
+ return false;
+ }
+
+ for (PerfTracepoint *tracepoint = mTracepoints; tracepoint != NULL; tracepoint = tracepoint->getNext()) {
+ if (tracepoint->getCounter()->isEnabled() && !sendTracepointFormat(currTime, buffer, tracepoint->getTracepoint(), printb, b)) {
+ 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("DynBuf::printf failed");
int64_t result;
if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) {
- logg->logMessage("DriverSource::readInt64Driver failed");
+ logg->logMessage("Unable to read tracepoint id for %s", printb->getBuf());
return -1;
}
#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"
#define CPU_IDLE "power/cpu_idle"
#define CPU_FREQUENCY "power/cpu_frequency"
class Buffer;
class DynBuf;
class PerfGroup;
+class PerfTracepoint;
class PerfDriver : public SimpleDriver {
public:
bool getLegacySupport() const { return mLegacySupport; }
+ void readEvents(mxml_node_t *const xml);
bool setup();
bool summary(Buffer *const buffer);
void coreName(const uint64_t currTime, Buffer *const buffer, const int cpu);
bool enable(const uint64_t currTime, PerfGroup *const group, Buffer *const buffer) const;
void read(Buffer *const buffer, const int cpu);
+ bool sendTracepointFormats(const uint64_t currTime, Buffer *const buffer, DynBuf *const printb, DynBuf *const b);
static long long getTracepointId(const char *const name, DynBuf *const printb);
bool mIsSetup;
bool mLegacySupport;
+ PerfTracepoint *mTracepoints;
// Intentionally undefined
PerfDriver(const PerfDriver &);
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include "SessionData.h"
static const int schedSwitchKey = getEventKey();
-static const int clockKey = getEventKey();
#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 = (gSessionData->perf.getLegacySupport() \
+ pea.sample_type = (gSessionData->mPerf.getLegacySupport() \
? PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_ID \
: PERF_SAMPLE_IDENTIFIER ) | PERF_SAMPLE_TIME | additionalSampleType; \
/* Emit emit value in group format */ \
return false;
}
- if (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && doAdd(currTime, buffer, clockKey, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU) < 0) {
+ if (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && doAdd(currTime, buffer, INT_MAX-PERF_TYPE_HARDWARE, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_PER_CPU | PERF_GROUP_CPU) < 0) {
return false;
}
} else {
// Non-CPU PMUs are sampled every 100ms for Sample Rate: None and EBS, otherwise they would never be sampled
const uint64_t timeout = gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS ? 1000000000UL / gSessionData->mSampleRate : 100000000UL;
- // PERF_SAMPLE_TID | PERF_SAMPLE_IP aren't helpful on non-CPU or 'uncore' PMUs - which CPU is the right one to sample? But removing it causes problems, remove it later.
- mLeaders[effectiveType] = doAdd(currTime, buffer, clockKey, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, timeout, PERF_SAMPLE_TID | PERF_SAMPLE_IP | PERF_SAMPLE_READ, PERF_GROUP_LEADER);
+ mLeaders[effectiveType] = doAdd(currTime, buffer, INT_MAX-effectiveType, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, timeout, PERF_SAMPLE_READ, PERF_GROUP_LEADER);
if (mLeaders[effectiveType] < 0) {
return false;
}
// The core is offline
return PG_CPU_OFFLINE;
}
-#ifndef USE_STRICTER_CHECK
- continue;
-#else
if (errno == ENOENT) {
// This event doesn't apply to this CPU but should apply to a different one, ex bL
continue;
}
logg->logMessage("perf_event_open failed");
return PG_FAILURE;
-#endif
}
if (!mPb->useFd(cpu, mFds[offset])) {
int PerfGroup::onlineCPU(const uint64_t currTime, const int cpu, const bool enable, Buffer *const buffer) {
bool addedEvents = false;
- if (!gSessionData->perf.getLegacySupport()) {
+ if (!gSessionData->mPerf.getLegacySupport()) {
int idCount = 0;
int coreKeys[ARRAY_LENGTH(mKeys)];
__u64 ids[ARRAY_LENGTH(mKeys)];
static const int cpuIdleKey = getEventKey();
-static bool sendTracepointFormat(const uint64_t currTime, Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
- if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
- logg->logMessage("DynBuf::printf failed");
- return false;
- }
- if (!b->read(printb->getBuf())) {
- logg->logMessage("DynBuf::read failed");
- return false;
- }
- buffer->marshalFormat(currTime, b->getLength(), b->getBuf());
-
- return true;
-}
-
static void *syncFunc(void *arg)
{
struct timespec ts;
|| !mUEvent.init()
|| !mMonitor.add(mUEvent.getFd())
- || !sendTracepointFormat(currTime, mBuffer, SCHED_SWITCH, &printb, &b1)
-
|| (cpuIdleId = PerfDriver::getTracepointId(CPU_IDLE, &printb)) < 0
- || !sendTracepointFormat(currTime, mBuffer, CPU_IDLE, &printb, &b1)
- || !sendTracepointFormat(currTime, mBuffer, CPU_FREQUENCY, &printb, &b1)
+ || !gSessionData->mPerf.sendTracepointFormats(currTime, mBuffer, &printb, &b1)
|| !mCountersGroup.createCpuGroup(currTime, mBuffer)
|| !mCountersGroup.add(currTime, mBuffer, cpuIdleKey, PERF_TYPE_TRACEPOINT, cpuIdleId, 1, PERF_SAMPLE_RAW, PERF_GROUP_LEADER | PERF_GROUP_PER_CPU)
- || !gSessionData->perf.enable(currTime, &mCountersGroup, mBuffer)
+ || !gSessionData->mPerf.enable(currTime, &mCountersGroup, mBuffer)
|| 0) {
logg->logMessage("perf setup failed, are you running Linux 3.4 or later?");
return false;
}
// Send the summary right before the start so that the monotonic delta is close to the start time
- if (!gSessionData->perf.summary(&mSummary)) {
+ if (!gSessionData->mPerf.summary(&mSummary)) {
logg->logError("PerfDriver::summary failed");
handleException();
}
mBuffer->perfCounterHeader(currTime);
for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
- gSessionData->perf.read(mBuffer, cpu);
+ gSessionData->mPerf.read(mBuffer, cpu);
}
mBuffer->perfCounterFooter(currTime);
} else if (err == PG_SUCCESS) {
if (mCountersGroup.onlineCPU(currTime, cpu, true, mBuffer) > 0) {
mBuffer->perfCounterHeader(currTime);
- gSessionData->perf.read(mBuffer, cpu);
+ gSessionData->mPerf.read(mBuffer, cpu);
mBuffer->perfCounterFooter(currTime);
ret = true;
}
mBuffer->commit(currTime);
gSessionData->readCpuInfo();
- gSessionData->perf.coreName(currTime, &mSummary, cpu);
+ gSessionData->mPerf.coreName(currTime, &mSummary, cpu);
mSummary.commit(currTime);
return ret;
} else if (strcmp(result.mAction, "offline") == 0) {
const int err = b->readlink(printb->getBuf());
const char *image;
if (err == 0) {
- image = strrchr(b->getBuf(), '/');
- if (image == NULL) {
- image = b->getBuf();
- } else {
- ++image;
- }
+ image = b->getBuf();
} else if (err == -ENOENT) {
// readlink /proc/[pid]/exe returns ENOENT for kernel threads
image = "\0";
// Android apps are run by app_process but the cmdline is changed to reference the actual app name
// On 64-bit android app_process can be app_process32 or app_process64
- if (strncmp(image, APP_PROCESS, sizeof(APP_PROCESS) - 1) != 0) {
+ if (strstr(image, APP_PROCESS) == NULL) {
return image;
}
logg->logMessage("Completed magic sequence");
}
- pthread_mutex_init(&mSendMutex, NULL);
+ pthread_mutexattr_t attr;
+ if (pthread_mutexattr_init(&attr) != 0 ||
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0 ||
+ pthread_mutex_init(&mSendMutex, &attr) != 0 ||
+ pthread_mutexattr_destroy(&attr) != 0 ||
+ false) {
+ logg->logError("Unable to setup mutex");
+ handleException();
+ }
}
Sender::~Sender() {
}
// Multiple threads call writeData()
- pthread_mutex_lock(&mSendMutex);
+ if (pthread_mutex_lock(&mSendMutex) != 0) {
+ logg->logError("pthread_mutex_lock failed");
+ handleException();
+ }
// Send data over the socket connection
if (mDataSocket) {
const int chunkSize = 100*1000 * alarmDuration / 8;
int pos = 0;
while (true) {
- mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize));
+ mDataSocket->send(data + pos, min(length - pos, chunkSize));
pos += chunkSize;
if (pos >= length) {
break;
}
}
- pthread_mutex_unlock(&mSendMutex);
+ if (pthread_mutex_unlock(&mSendMutex) != 0) {
+ logg->logError("pthread_mutex_unlock failed");
+ handleException();
+ }
}
SessionData* gSessionData = NULL;
+SharedData::SharedData() : mMaliUtgardCountersSize(0) {
+ memset(mCpuIds, -1, sizeof(mCpuIds));
+}
+
SessionData::SessionData() {
- usDrivers[0] = new HwmonDriver();
- usDrivers[1] = new FSDriver();
- usDrivers[2] = new MemInfoDriver();
- usDrivers[3] = new NetDriver();
- usDrivers[4] = new DiskIODriver();
+ mUsDrivers[0] = new HwmonDriver();
+ mUsDrivers[1] = new FSDriver();
+ mUsDrivers[2] = new MemInfoDriver();
+ mUsDrivers[3] = new NetDriver();
+ mUsDrivers[4] = new DiskIODriver();
initialize();
}
SessionData::~SessionData() {
}
+// Needed to use placement new
+inline void *operator new(size_t, void *ptr) { return ptr; }
+
void SessionData::initialize() {
+ mSharedData = (SharedData *)mmap(NULL, sizeof(*mSharedData), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (mSharedData == MAP_FAILED) {
+ logg->logError("Unable to mmap shared memory for cpuids");
+ handleException();
+ }
+ // Use placement new to construct but not allocate the object
+ new ((char *)mSharedData) SharedData();
+
mWaitingOnCommand = false;
mSessionIsActive = false;
mLocalCapture = false;
mOneShot = false;
mSentSummary = false;
mAllowCommands = false;
- const size_t cpuIdSize = sizeof(int)*NR_CPUS;
- // Share mCpuIds across all instances of gatord
- mCpuIds = (int *)mmap(NULL, cpuIdSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- if (mCpuIds == MAP_FAILED) {
- logg->logError("Unable to mmap shared memory for cpuids");
- handleException();
- }
- memset(mCpuIds, -1, cpuIdSize);
strcpy(mCoreName, CORE_NAME_UNKNOWN);
readModel();
readCpuInfo();
// Set session data values - use prime numbers just below the desired value to reduce the chance of events firing at the same time
if (strcmp(session.parameters.sample_rate, "high") == 0) {
- mSampleRate = 9973; // 10000
+ mSampleRate = 10007; // 10000
} else if (strcmp(session.parameters.sample_rate, "normal") == 0) {
- mSampleRate = 997; // 1000
+ mSampleRate = 1009; // 1000
} else if (strcmp(session.parameters.sample_rate, "low") == 0) {
- mSampleRate = 97; // 100
+ mSampleRate = 101; // 100
} else if (strcmp(session.parameters.sample_rate, "none") == 0) {
mSampleRate = 0;
} else {
}
void SessionData::readModel() {
- FILE *fh = fopen("/proc/device-tree/model", "rb");
+ FILE *fh = fopen_cloexec("/proc/device-tree/model", "rb");
if (fh == NULL) {
return;
}
char temp[256]; // arbitrarily large amount
mMaxCpuId = -1;
- FILE *f = fopen("/proc/cpuinfo", "r");
+ FILE *f = fopen_cloexec("/proc/cpuinfo", "r");
if (f == NULL) {
logg->logMessage("Error opening /proc/cpuinfo\n"
"The core name in the captured xml file will be 'unknown'.");
while (fgets(temp, sizeof(temp), f)) {
const size_t len = strlen(temp);
+ if (len > 0) {
+ // Replace the line feed with a null
+ temp[len - 1] = '\0';
+ }
+
+ logg->logMessage("cpuinfo: %s", temp);
+
if (len == 1) {
// New section, clear the processor. Streamline will not know the cpus if the pre Linux 3.8 format of cpuinfo is encountered but also that no incorrect information will be transmitted.
processor = -1;
continue;
}
- if (len > 0) {
- // Replace the line feed with a null
- temp[len - 1] = '\0';
- }
-
const bool foundHardware = !foundCoreName && strstr(temp, "Hardware") != 0;
const bool foundCPUImplementer = strstr(temp, "CPU implementer") != 0;
const bool foundCPUPart = strstr(temp, "CPU part") != 0;
if (processor >= NR_CPUS) {
logg->logMessage("Too many processors, please increase NR_CPUS");
} else if (processor >= 0) {
- setImplementer(mCpuIds[processor], implementer);
+ setImplementer(mSharedData->mCpuIds[processor], implementer);
} else {
setImplementer(mMaxCpuId, implementer);
}
if (processor >= NR_CPUS) {
logg->logMessage("Too many processors, please increase NR_CPUS");
} else if (processor >= 0) {
- setPart(mCpuIds[processor], cpuId);
+ setPart(mSharedData->mCpuIds[processor], cpuId);
} else {
setPart(mMaxCpuId, cpuId);
}
// 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
for (int i = 0; i < NR_CPUS; ++i) {
- if (mCpuIds[i] > mMaxCpuId) {
- mMaxCpuId = mCpuIds[i];
+ if (mSharedData->mCpuIds[i] > mMaxCpuId) {
+ mMaxCpuId = mSharedData->mCpuIds[i];
}
}
}
return fh;
}
+
+bool setNonblock(const int fd) {
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ logg->logMessage("fcntl getfl failed");
+ return false;
+ }
+
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
+ logg->logMessage("fcntl setfl failed");
+ return false;
+ }
+
+ return true;
+}
+
+bool writeAll(const int fd, const void *const buf, const size_t pos) {
+ size_t written = 0;
+ while (written < pos) {
+ ssize_t bytes = write(fd, (const uint8_t *)buf + written, pos - written);
+ if (bytes <= 0) {
+ logg->logMessage("write failed");
+ return false;
+ }
+ written += bytes;
+ }
+
+ return true;
+}
+
+bool readAll(const int fd, void *const buf, const size_t count) {
+ size_t pos = 0;
+ while (pos < count) {
+ ssize_t bytes = read(fd, (uint8_t *)buf + pos, count - pos);
+ if (bytes <= 0) {
+ logg->logMessage("read failed");
+ return false;
+ }
+ pos += bytes;
+ }
+
+ return true;
+}
#include <stdint.h>
+#include "AtraceDriver.h"
+#include "CCNDriver.h"
#include "Config.h"
#include "Counter.h"
+#include "ExternalDriver.h"
#include "FtraceDriver.h"
#include "KMod.h"
#include "MaliVideoDriver.h"
#include "PerfDriver.h"
-#define PROTOCOL_VERSION 21
+#define PROTOCOL_VERSION 22
// Differentiates development versions (timestamp) from release versions
#define PROTOCOL_DEV 1000
struct ImageLinkList *next;
};
+class SharedData {
+public:
+ SharedData();
+
+ int mCpuIds[NR_CPUS];
+ size_t mMaliUtgardCountersSize;
+ char mMaliUtgardCounters[1<<12];
+
+private:
+ // Intentionally unimplemented
+ SharedData(const SharedData &);
+ SharedData &operator=(const SharedData &);
+};
+
class SessionData {
public:
static const size_t MAX_STRING_LEN = 80;
SessionData();
~SessionData();
- void initialize();
void parseSessionXML(char* xmlString);
void readModel();
void readCpuInfo();
- PolledDriver *usDrivers[5];
- KMod kmod;
- PerfDriver perf;
- MaliVideoDriver maliVideo;
- FtraceDriver ftraceDriver;
+ SharedData *mSharedData;
+
+ PolledDriver *mUsDrivers[5];
+ KMod mKmod;
+ PerfDriver mPerf;
+ MaliVideoDriver mMaliVideo;
+ // Intentionally above FtraceDriver as drivers are initialized in reverse order AtraceDriver references AtraceDriver
+ AtraceDriver mAtraceDriver;
+ FtraceDriver mFtraceDriver;
+ ExternalDriver mExternalDriver;
+ CCNDriver mCcnDriver;
char mCoreName[MAX_STRING_LEN];
struct ImageLinkList *mImages;
int mDuration;
int mCores;
int mPageSize;
- int *mCpuIds;
int mMaxCpuId;
int mAnnotateStart;
// PMU Counters
- int mCounterOverflow;
+ char *mCountersError;
Counter mCounters[MAX_PERFORMANCE_COUNTERS];
private:
+ void initialize();
+
// Intentionally unimplemented
SessionData(const SessionData &);
SessionData &operator=(const SessionData &);
int getEventKey();
int pipe_cloexec(int pipefd[2]);
FILE *fopen_cloexec(const char *path, const char *mode);
+bool setNonblock(const int fd);
+bool writeAll(const int fd, const void *const buf, const size_t pos);
+bool readAll(const int fd, void *const buf, const size_t count);
#endif // SESSION_DATA_H
handleException();
}
- if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(2, 6, 32)) {
- logg->logError(GATOR_ERROR "Streamline can't automatically setup gator as this kernel version is not supported. Please upgrade the kernel on your device.");
- handleException();
- }
-
if (KERNEL_VERSION(version[0], version[1], version[2]) < KERNEL_VERSION(3, 4, 0)) {
- logg->logError(GATOR_ERROR "Streamline can't automatically setup gator as gator.ko is required for this version of Linux. Please build gator.ko and gatord and install them on your device.");
+ logg->logError(GATOR_ERROR "Streamline can't automatically setup gator as this kernel version is not supported. Please upgrade the kernel on your device.");
handleException();
}
if (geteuid() != 0) {
- printf(GATOR_MSG "trying sudo\n");
- execlp("sudo", "sudo", gatorPath, "-u", NULL);
- // Streamline will provide the password if needed
-
- printf(GATOR_MSG "trying su\n");
char buf[1<<10];
+ snprintf(buf, sizeof(buf),
+ "which sudo &&"
+ "("
+ "sudo -n %1$s -u ||"
+ "("
+ "echo " GATOR_MSG "trying sudo;"
+ // Streamline will provide the password
+ "sudo %1$s -u"
+ ")"
+ ") || ("
+ "echo " GATOR_MSG "trying su;"
/*
* Different versions of su handle additional -c command line options differently and expect the
* arguments in different ways. Try both ways wrapped in a shell.
*
* Then invoke another shell after su as it avoids odd failures on some Android systems
*/
- snprintf(buf, sizeof(buf), "su -c \"sh -c '%s -u'\" || su -c sh -c '%s -u'", gatorPath, gatorPath);
+ "su -c \"sh -c '%1$s -u'\" ||"
+ "su -c sh -c '%1$s -u'"
+ ")",
+ gatorPath);
execlp("sh", "sh", "-c", buf, NULL);
// Streamline will provide the password if needed
umount("/dev/gator");
syscall(__NR_delete_module, "gator", O_NONBLOCK);
+ if (access("/sys/module/gator", F_OK) == 0) {
+ logg->logError(GATOR_ERROR "Unable to unload gator.ko, the gator module may be built into the kernel or gator.ko cannot be unloaded. Rebooting the device may resolve the issue.");
+ handleException();
+ }
+
rename("gatord", "gatord.old");
rename("gator.ko", "gator.ko.old");
close(pipefd[1]);
const ssize_t bytes = read(pipefd[0], buf, sizeof(buf));
if (bytes > 0) {
- logg->logError("%s", buf);
- handleException();
+ printf("%s\n", buf);
}
close(pipefd[0]);
free(data);
}
- if (gSessionData->mCounterOverflow > 0) {
- logg->logError("Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ if (gSessionData->mCountersError != NULL) {
+ logg->logError("%s", gSessionData->mCountersError);
handleException();
}
}
header[0] = type;
Buffer::writeLEInt(header + 1, length);
mSocket->send((char*)&header, sizeof(header));
- mSocket->send((const char*)data, length);
+ mSocket->send(data, length);
}
void StreamlineSetup::sendEvents() {
// Re-populate gSessionData with the configuration, as it has now changed
{ ConfigurationXML configuration; }
- if (gSessionData->mCounterOverflow > 0) {
- logg->logError("Only %i performance counters are permitted, %i are selected", MAX_PERFORMANCE_COUNTERS, gSessionData->mCounterOverflow);
+ if (gSessionData->mCountersError != NULL) {
+ logg->logError("%s", gSessionData->mCountersError);
handleException();
}
}
void UserSpaceSource::run() {
prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
- for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) {
- gSessionData->usDrivers[i]->start();
+ for (int i = 0; i < ARRAY_LENGTH(gSessionData->mUsDrivers); ++i) {
+ gSessionData->mUsDrivers[i]->start();
}
int64_t monotonicStarted = 0;
while (monotonicStarted <= 0 && gSessionData->mSessionIsActive) {
usleep(10);
- if (gSessionData->perf.isSetup()) {
+ if (gSessionData->mPerf.isSetup()) {
monotonicStarted = gSessionData->mMonotonicStarted;
} else {
if (DriverSource::readInt64Driver("/dev/gator/started", &monotonicStarted) == -1) {
}
if (mBuffer.eventHeader(currTime)) {
- for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) {
- gSessionData->usDrivers[i]->read(&mBuffer);
+ for (int i = 0; i < ARRAY_LENGTH(gSessionData->mUsDrivers); ++i) {
+ gSessionData->mUsDrivers[i]->read(&mBuffer);
}
// Only check after writing all counters so that time and corresponding counters appear in the same frame
mBuffer.check(currTime);
<configuration counter="ARMv7_Cortex_A17_cnt1" event="0x16"/>
<configuration counter="ARMv7_Cortex_A17_cnt2" event="0x10"/>
<configuration counter="ARMv7_Cortex_A17_cnt3" event="0x19"/>
- <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/>
- <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/>
- <configuration counter="ARM_Cortex-A53_cnt2" event="0x10"/>
- <configuration counter="ARM_Cortex-A53_cnt3" event="0x19"/>
- <configuration counter="ARM_Cortex-A57_ccnt" event="0x11"/>
- <configuration counter="ARM_Cortex-A57_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A57_cnt1" event="0x16"/>
- <configuration counter="ARM_Cortex-A57_cnt2" event="0x10"/>
- <configuration counter="ARM_Cortex-A57_cnt3" event="0x19"/>
- <configuration counter="ARM_Cortex-A72_ccnt" event="0x11"/>
- <configuration counter="ARM_Cortex-A72_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A72_cnt1" event="0x16"/>
- <configuration counter="ARM_Cortex-A72_cnt2" event="0x10"/>
- <configuration counter="ARM_Cortex-A72_cnt3" event="0x19"/>
+ <configuration counter="ARMv8_Cortex_A53_ccnt" event="0x11"/>
+ <configuration counter="ARMv8_Cortex_A53_cnt0" event="0x8"/>
+ <configuration counter="ARMv8_Cortex_A53_cnt1" event="0x16"/>
+ <configuration counter="ARMv8_Cortex_A53_cnt2" event="0x10"/>
+ <configuration counter="ARMv8_Cortex_A53_cnt3" event="0x19"/>
+ <configuration counter="ARMv8_Cortex_A57_ccnt" event="0x11"/>
+ <configuration counter="ARMv8_Cortex_A57_cnt0" event="0x8"/>
+ <configuration counter="ARMv8_Cortex_A57_cnt1" event="0x16"/>
+ <configuration counter="ARMv8_Cortex_A57_cnt2" event="0x10"/>
+ <configuration counter="ARMv8_Cortex_A57_cnt3" event="0x19"/>
+ <configuration counter="ARMv8_Cortex_A72_ccnt" event="0x11"/>
+ <configuration counter="ARMv8_Cortex_A72_cnt0" event="0x8"/>
+ <configuration counter="ARMv8_Cortex_A72_cnt1" event="0x16"/>
+ <configuration counter="ARMv8_Cortex_A72_cnt2" event="0x10"/>
+ <configuration counter="ARMv8_Cortex_A72_cnt3" event="0x19"/>
<configuration counter="Scorpion_ccnt" event="0xff"/>
<configuration counter="Scorpion_cnt0" event="0x08"/>
<configuration counter="Scorpion_cnt1" event="0x10"/>
<event event="0x19" option_set="Master" title="CCI-400" name="Write stall: barrier hazard" description="Write request stall cycle because of a barrier hazard"/>
<event event="0x1a" option_set="Master" title="CCI-400" name="Write stall: tracker full" description="Write request stall cycle because the transaction tracker is full. Increase MIx_W_MAX to avoid this stall. See the CoreLink CCI-400 Cache Coherent Interconnect Integration Manual"/>
</category>
- <counter_set name="CCI_400-r1_cnt" count="4"/>
- <category name="CCI-400" counter_set="CCI_400-r1_cnt" per_cpu="no">
- <event counter="CCI_400-r1_ccnt" event="0xff" title="CCI-400 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
+ <counter_set name="CCI_400_r1_cnt" count="4"/>
+ <category name="CCI-400" counter_set="CCI_400_r1_cnt" per_cpu="no">
+ <event counter="CCI_400_r1_ccnt" event="0xff" title="CCI-400 Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" description="The number of core clock cycles"/>
<option_set name="Slave">
<option event_delta="0x00" name="S0" description="Slave interface 0"/>
<option event_delta="0x20" name="S1" description="Slave interface 1"/>
- <counter_set name="ARM_Cortex-A53_cnt" count="6"/>
- <category name="Cortex-A53" counter_set="ARM_Cortex-A53_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A53_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv8_Cortex_A53_cnt" count="6"/>
+ <category name="Cortex-A53" counter_set="ARMv8_Cortex_A53_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv8_Cortex_A53_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Software increment. The register is incremented only on writes to the Software Increment Register."/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
- <counter_set name="ARM_Cortex-A57_cnt" count="6"/>
- <category name="Cortex-A57" counter_set="ARM_Cortex-A57_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A57_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv8_Cortex_A57_cnt" count="6"/>
+ <category name="Cortex-A57" counter_set="ARMv8_Cortex_A57_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv8_Cortex_A57_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
- <counter_set name="ARM_Cortex_A72_cnt" count="6"/>
- <category name="Cortex-A72" counter_set="ARM_Cortex_A72_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex_A72_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv8_Cortex_A72_cnt" count="6"/>
+ <category name="Cortex-A72" counter_set="ARMv8_Cortex_A72_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv8_Cortex_A72_ccnt" event="0x11" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
<event counter="ARM_Mali-4xx_SW_39" title="Geometry Statistics" name="Strip Lines" description="Number of lines passed to GLES using the mode GL_LINE_STRIP."/>
<event counter="ARM_Mali-4xx_SW_40" title="Geometry Statistics" name="Loop Lines" description="Number of lines passed to GLES using the mode GL_LINE_LOOP."/>
</category>
+ <category name="ARM_Mali-4xx_Total_alloc_pages" per_cpu="no">
+ <event counter="ARM_Mali-4xx_Total_alloc_pages" title="Mali GPU Alloc" name="pages" class="absolute" display="average" average_selection="yes" description="Total number of allocated pages"/>
+ </category>
+ <category name="Mali-4xx Session Memory usage" per_cpu="no">
+ <event counter="ARM_Mali-4xx_vertex_index_buffer" title="Mali Memory" name="Vertex and Index buffer" units="B" dclass="absolute" display="average" average_selection="yes" description="Vertex and index input"/>
+ <event counter="ARM_Mali-4xx_texture_buffer" title="Mali Memory" name="Texture buffer" units="B" dclass="absolute" display="average" average_selection="yes" description="Texture data"/>
+ <event counter="ARM_Mali-4xx_varying_buffer" title="Mali Memory" name="Varying buffer" units="B" dclass="absolute" display="average" average_selection="yes" description="Varying buffer"/>
+ <event counter="ARM_Mali-4xx_render_target" title="Mali Memory" name="Render target buffer" units="B" dclass="absolute" display="average" average_selection="yes" description="Render target buffer"/>
+ <event counter="ARM_Mali-4xx_plbu_heap" title="Mali Memory" name="Plbu heap" units="B" dclass="absolute" display="average" average_selection="yes" description="The memory to store commands from PLBU when Slave tilelist memory isn't enough"/>
+ <event counter="ARM_Mali-4xx_slave_tilelist" title="Mali Memory" name="Slave tilelist" units="B" dclass="absolute" display="average" average_selection="yes" description="Slave tilelist memory"/>
+ </category>
<event counter="ARM_Mali-Midgard_FRAG_THREADS" title="Mali Core Threads" name="Fragment threads" description="Number of fragment threads started"/>
<event counter="ARM_Mali-Midgard_FRAG_DUMMY_THREADS" title="Mali Core Threads" name="Dummy fragment threads" description="Number of dummy fragment threads started"/>
- <event counter="ARM_Mali-Midgard_FRAG_QUADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS quads" description="Number of threads doing late ZS test"/>
- <event counter="ARM_Mali-Midgard_FRAG_QUADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS quads" description="Number of threads killed by late ZS test"/>
+ <event counter="ARM_Mali-Midgard_FRAG_QUADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
+ <event counter="ARM_Mali-Midgard_FRAG_QUADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
<event counter="ARM_Mali-Midgard_FRAG_THREADS_LZS_TEST" title="Mali Core Threads" name="Frag threads doing late ZS" description="Number of threads doing late ZS test"/>
<event counter="ARM_Mali-Midgard_FRAG_THREADS_LZS_KILLED" title="Mali Core Threads" name="Frag threads killed late ZS" description="Number of threads killed by late ZS test"/>
<event counter="ARM_Mali-V500_evn2" title="MVE-V500 Frames" name="Frame Processed" description="Generated when the MVE has finished processing a frame"/>
<event counter="ARM_Mali-V500_evn3" title="MVE-V500 Output" name="Output buffer received" description="Generated when an an output buffer is returned to us from the MVE"/>
<event counter="ARM_Mali-V500_evn4" title="MVE-V500 Input" name="Input buffer received" description="Generated when we an input buffer is returned to us from the MVE"/>
- <event counter="ARM_Mali-V500_act0" title="MVE-V500 Parsed" name="Activity" class="activity" activity1="activity" activity_color1="0x000000ff" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" cores="8" description="Mali-V500 Activity"/>
- <event counter="ARM_Mali-V500_act1" title="MVE-V500 Piped" name="Activity" class="activity" activity1="activity" activity_color1="0x0000ff00" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" cores="8" description="Mali-V500 Activity"/>
+ <event counter="ARM_Mali-V500_act0" title="MVE-V500 Parsed" name="Activity" class="activity" activity1="activity" activity_color1="0x000000ff" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" per_cpu="yes" cores="8" description="Mali-V500 Activity"/>
+ <event counter="ARM_Mali-V500_act1" title="MVE-V500 Piped" name="Activity" class="activity" activity1="activity" activity_color1="0x0000ff00" rendering_type="bar" average_selection="yes" average_cores="yes" percentage="yes" per_cpu="yes" cores="8" description="Mali-V500 Activity"/>
</category>
--- /dev/null
+ <category name="Atrace">
+ <event counter="atrace_graphics" flag="0x2" title="Atrace" name="Graphics" description="Graphics"/>
+ <event counter="atrace_input" flag="0x4" title="Atrace" name="Input" description="Input"/>
+ <event counter="atrace_view" flag="0x8" title="Atrace" name="View" description="View"/>
+ <event counter="atrace_webview" flag="0x10" title="Atrace" name="Webview" description="Webview"/>
+ <event counter="atrace_window_manager" flag="0x20" title="Atrace" name="Window manager" description="Window manager"/>
+ <event counter="atrace_activity_manager" flag="0x40" title="Atrace" name="Activity manager" description="Activity manager"/>
+ <event counter="atrace_sync_manager" flag="0x80" title="Atrace" name="Sync manager" description="Sync manager"/>
+ <event counter="atrace_audio" flag="0x100" title="Atrace" name="Audio" description="Audio"/>
+ <event counter="atrace_video" flag="0x200" title="Atrace" name="Video" description="Video"/>
+ <event counter="atrace_camera" flag="0x400" title="Atrace" name="Camera" description="Camera"/>
+ <event counter="atrace_hal" flag="0x800" title="Atrace" name="Hal" description="Hal"/>
+ <event counter="atrace_app" flag="0x1000" title="Atrace" name="App" description="App"/>
+ <event counter="atrace_resources" flag="0x2000" title="Atrace" name="Resources" description="Resources"/>
+ <event counter="atrace_dalvik" flag="0x4000" title="Atrace" name="Dalvik" description="Dalvik"/>
+ <event counter="atrace_rs" flag="0x8000" title="Atrace" name="Rs" description="Rs"/>
+ <event counter="atrace_bionic" flag="0x10000" title="Atrace" name="Bionic" description="Bionic"/>
+ <event counter="atrace_power" flag="0x20000" title="Atrace" name="Power" description="Power"/>
+ </category>
<category name="Ftrace">
<!--
- Ftrace counters require Linux 3.10 or later. If you do you see ftrace counters in counter configuration, please check your Linux version.
+ ftrace counters require Linux 3.10 or later; if you do not see ftrace counters in counter configuration, please check your Linux version
'counter' attribute must start with ftrace_ and be unique
the regex item in () is the value shown or, if the parentheses are missing, the number of regex matches is counted
'enable' (optional) is the ftrace event to enable associated with the gator event
+ 'tracepoint' (optional) same meaning as enable, but will use perf instead of ftrace when using user space gator
+ 'arg' (optional) used in conjunction with 'tracepoint' to specify the value to show otherwise the number of tracepoint events is counted
-->
<!--
- <event counter="ftrace_trace_marker_numbers" title="ftrace" name="trace_marker" regex="^tracing_mark_write: ([0-9]+)\s$" class="absolute" description="Numbers written to /sys/kernel/debug/tracing/trace_marker, ex: echo 42 > /sys/kernel/debug/tracing/trace_marker"/>
+ <event counter="ftrace_trace_marker_numbers" title="ftrace" name="trace_marker" regex="^tracing_mark_write: ([0-9]+)$" class="absolute" description="Numbers written to /sys/kernel/debug/tracing/trace_marker, ex: echo 42 > /sys/kernel/debug/tracing/trace_marker"/>
-->
<!-- ftrace counters -->
- <event counter="ftrace_kmem_kmalloc" title="Kmem" name="kmalloc" regex="^kmalloc:.* bytes_alloc=([0-9]+) " enable="kmem/kmalloc" class="incident" description="Number of bytes allocated in the kernel using kmalloc"/>
- <event counter="ftrace_ext4_ext4_da_write" title="Ext4" name="ext4_da_write" regex="^ext4_da_write_end:.* len ([0-9]+) " enable="ext4/ext4_da_write_end" class="incident" description="Number of bytes written to an ext4 filesystem"/>
- <event counter="ftrace_f2fs_f2fs_write" title="F2FS" name="f2fs_write" regex="^f2fs_write_end:.* len ([0-9]+), " enable="f2fs/f2fs_write_end" class="incident" description="Number of bytes written to an f2fs filesystem"/>
- <event counter="ftrace_power_clock_set_rate" title="Power" name="clock_set_rate" regex="^clock_set_rate:.* state=([0-9]+) " enable="power/clock_set_rate" class="absolute" description="Clock rate state"/>
+ <event counter="ftrace_kmem_kmalloc" title="Kmem" name="kmalloc" regex="^kmalloc:.* bytes_alloc=([0-9]+) " tracepoint="kmem/kmalloc" arg="bytes_alloc" class="incident" description="Number of bytes allocated in the kernel using kmalloc"/>
+ <event counter="ftrace_ext4_ext4_da_write" title="Ext4" name="ext4_da_write" regex="^ext4_da_write_end:.* len ([0-9]+) " tracepoint="ext4/ext4_da_write_end" arg="len" class="incident" description="Number of bytes written to an ext4 filesystem"/>
+ <event counter="ftrace_f2fs_f2fs_write" title="F2FS" name="f2fs_write" regex="^f2fs_write_end:.* len ([0-9]+), " tracepoint="f2fs/f2fs_write_end" arg="len" class="incident" description="Number of bytes written to an f2fs filesystem"/>
+ <event counter="ftrace_power_clock_set_rate" title="Power" name="clock_set_rate" regex="^clock_set_rate:.* state=([0-9]+) " tracepoint="power/clock_set_rate" arg="state" class="absolute" description="Clock rate state"/>
<!-- counting ftrace counters -->
- <event counter="ftrace_block_block_rq_complete" title="Block" name="block_rq_complete" regex="^block_rq_complete: " enable="block/block_rq_complete" class="delta" description="Number of block IO operations completed by device driver"/>
- <event counter="ftrace_block_block_rq_issue" title="Block" name="block_rq_issue" regex="^block_rq_issue: " enable="block/block_rq_issue" class="delta" description="Number of block IO operations issued to device driver"/>
- <event counter="ftrace_power_cpu_idle" title="Power" name="cpu_idle" regex="^cpu_idle: " enable="power/cpu_idle" class="delta" description="Number of times cpu_idle is entered or exited"/>
+ <event counter="ftrace_block_block_rq_complete" title="Block" name="block_rq_complete" regex="^block_rq_complete: " tracepoint="block/block_rq_complete" class="delta" description="Number of block IO operations completed by device driver"/>
+ <event counter="ftrace_block_block_rq_issue" title="Block" name="block_rq_issue" regex="^block_rq_issue: " tracepoint="block/block_rq_issue" class="delta" description="Number of block IO operations issued to device driver"/>
+ <event counter="ftrace_power_cpu_idle" title="Power" name="cpu_idle" regex="^cpu_idle: " tracepoint="power/cpu_idle" class="delta" description="Number of times cpu_idle is entered or exited"/>
</category>
+++ /dev/null
-/*
- * Performance events:
- *
- * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
- * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra
- *
- * Data type definitions, declarations, prototypes.
- *
- * Started by: Thomas Gleixner and Ingo Molnar
- *
- * For licencing details see kernel-base/COPYING
- */
-#ifndef _LINUX_PERF_EVENT_H
-#define _LINUX_PERF_EVENT_H
-
-#include <linux/types.h>
-#include <linux/ioctl.h>
-#include <asm/byteorder.h>
-
-/*
- * User-space ABI bits:
- */
-
-/*
- * attr.type
- */
-enum perf_type_id {
- PERF_TYPE_HARDWARE = 0,
- PERF_TYPE_SOFTWARE = 1,
- PERF_TYPE_TRACEPOINT = 2,
- PERF_TYPE_HW_CACHE = 3,
- PERF_TYPE_RAW = 4,
- PERF_TYPE_BREAKPOINT = 5,
-
- PERF_TYPE_MAX, /* non-ABI */
-};
-
-/*
- * Generalized performance event event_id types, used by the
- * attr.event_id parameter of the sys_perf_event_open()
- * syscall:
- */
-enum perf_hw_id {
- /*
- * Common hardware events, generalized by the kernel:
- */
- PERF_COUNT_HW_CPU_CYCLES = 0,
- PERF_COUNT_HW_INSTRUCTIONS = 1,
- PERF_COUNT_HW_CACHE_REFERENCES = 2,
- PERF_COUNT_HW_CACHE_MISSES = 3,
- PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
- PERF_COUNT_HW_BRANCH_MISSES = 5,
- PERF_COUNT_HW_BUS_CYCLES = 6,
- PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
- PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
- PERF_COUNT_HW_REF_CPU_CYCLES = 9,
-
- PERF_COUNT_HW_MAX, /* non-ABI */
-};
-
-/*
- * Generalized hardware cache events:
- *
- * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
- * { read, write, prefetch } x
- * { accesses, misses }
- */
-enum perf_hw_cache_id {
- PERF_COUNT_HW_CACHE_L1D = 0,
- PERF_COUNT_HW_CACHE_L1I = 1,
- PERF_COUNT_HW_CACHE_LL = 2,
- PERF_COUNT_HW_CACHE_DTLB = 3,
- PERF_COUNT_HW_CACHE_ITLB = 4,
- PERF_COUNT_HW_CACHE_BPU = 5,
- PERF_COUNT_HW_CACHE_NODE = 6,
-
- PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
-};
-
-enum perf_hw_cache_op_id {
- PERF_COUNT_HW_CACHE_OP_READ = 0,
- PERF_COUNT_HW_CACHE_OP_WRITE = 1,
- PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
-
- PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
-};
-
-enum perf_hw_cache_op_result_id {
- PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
- PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
-
- PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
-};
-
-/*
- * Special "software" events provided by the kernel, even if the hardware
- * does not support performance events. These events measure various
- * physical and sw events of the kernel (and allow the profiling of them as
- * well):
- */
-enum perf_sw_ids {
- PERF_COUNT_SW_CPU_CLOCK = 0,
- PERF_COUNT_SW_TASK_CLOCK = 1,
- PERF_COUNT_SW_PAGE_FAULTS = 2,
- PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
- PERF_COUNT_SW_CPU_MIGRATIONS = 4,
- PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
- PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
- PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
- PERF_COUNT_SW_EMULATION_FAULTS = 8,
- PERF_COUNT_SW_DUMMY = 9,
-
- PERF_COUNT_SW_MAX, /* non-ABI */
-};
-
-/*
- * Bits that can be set in attr.sample_type to request information
- * in the overflow packets.
- */
-enum perf_event_sample_format {
- PERF_SAMPLE_IP = 1U << 0,
- PERF_SAMPLE_TID = 1U << 1,
- PERF_SAMPLE_TIME = 1U << 2,
- PERF_SAMPLE_ADDR = 1U << 3,
- PERF_SAMPLE_READ = 1U << 4,
- PERF_SAMPLE_CALLCHAIN = 1U << 5,
- PERF_SAMPLE_ID = 1U << 6,
- PERF_SAMPLE_CPU = 1U << 7,
- PERF_SAMPLE_PERIOD = 1U << 8,
- PERF_SAMPLE_STREAM_ID = 1U << 9,
- PERF_SAMPLE_RAW = 1U << 10,
- PERF_SAMPLE_BRANCH_STACK = 1U << 11,
- PERF_SAMPLE_REGS_USER = 1U << 12,
- PERF_SAMPLE_STACK_USER = 1U << 13,
- PERF_SAMPLE_WEIGHT = 1U << 14,
- PERF_SAMPLE_DATA_SRC = 1U << 15,
- PERF_SAMPLE_IDENTIFIER = 1U << 16,
-
- PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */
-};
-
-/*
- * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
- *
- * If the user does not pass priv level information via branch_sample_type,
- * the kernel uses the event's priv level. Branch and event priv levels do
- * not have to match. Branch priv level is checked for permissions.
- *
- * The branch types can be combined, however BRANCH_ANY covers all types
- * of branches and therefore it supersedes all the other types.
- */
-enum perf_branch_sample_type {
- PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */
- PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */
- PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */
-
- PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */
- PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */
- PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */
- PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */
- PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */
- PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */
- PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */
-
- PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */
-};
-
-#define PERF_SAMPLE_BRANCH_PLM_ALL \
- (PERF_SAMPLE_BRANCH_USER|\
- PERF_SAMPLE_BRANCH_KERNEL|\
- PERF_SAMPLE_BRANCH_HV)
-
-/*
- * Values to determine ABI of the registers dump.
- */
-enum perf_sample_regs_abi {
- PERF_SAMPLE_REGS_ABI_NONE = 0,
- PERF_SAMPLE_REGS_ABI_32 = 1,
- PERF_SAMPLE_REGS_ABI_64 = 2,
-};
-
-/*
- * The format of the data returned by read() on a perf event fd,
- * as specified by attr.read_format:
- *
- * struct read_format {
- * { u64 value;
- * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
- * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
- * { u64 id; } && PERF_FORMAT_ID
- * } && !PERF_FORMAT_GROUP
- *
- * { u64 nr;
- * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
- * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
- * { u64 value;
- * { u64 id; } && PERF_FORMAT_ID
- * } cntr[nr];
- * } && PERF_FORMAT_GROUP
- * };
- */
-enum perf_event_read_format {
- PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
- PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
- PERF_FORMAT_ID = 1U << 2,
- PERF_FORMAT_GROUP = 1U << 3,
-
- PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
-};
-
-#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
-#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
-#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
-#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
- /* add: sample_stack_user */
-
-/*
- * Hardware event_id to monitor via a performance monitoring event:
- */
-struct perf_event_attr {
-
- /*
- * Major type: hardware/software/tracepoint/etc.
- */
- __u32 type;
-
- /*
- * Size of the attr structure, for fwd/bwd compat.
- */
- __u32 size;
-
- /*
- * Type specific configuration information.
- */
- __u64 config;
-
- union {
- __u64 sample_period;
- __u64 sample_freq;
- };
-
- __u64 sample_type;
- __u64 read_format;
-
- __u64 disabled : 1, /* off by default */
- inherit : 1, /* children inherit it */
- pinned : 1, /* must always be on PMU */
- exclusive : 1, /* only group on PMU */
- exclude_user : 1, /* don't count user */
- exclude_kernel : 1, /* ditto kernel */
- exclude_hv : 1, /* ditto hypervisor */
- exclude_idle : 1, /* don't count when idle */
- mmap : 1, /* include mmap data */
- comm : 1, /* include comm data */
- freq : 1, /* use freq, not period */
- inherit_stat : 1, /* per task counts */
- enable_on_exec : 1, /* next exec enables */
- task : 1, /* trace fork/exit */
- watermark : 1, /* wakeup_watermark */
- /*
- * precise_ip:
- *
- * 0 - SAMPLE_IP can have arbitrary skid
- * 1 - SAMPLE_IP must have constant skid
- * 2 - SAMPLE_IP requested to have 0 skid
- * 3 - SAMPLE_IP must have 0 skid
- *
- * See also PERF_RECORD_MISC_EXACT_IP
- */
- precise_ip : 2, /* skid constraint */
- mmap_data : 1, /* non-exec mmap data */
- sample_id_all : 1, /* sample_type all events */
-
- exclude_host : 1, /* don't count in host */
- exclude_guest : 1, /* don't count in guest */
-
- exclude_callchain_kernel : 1, /* exclude kernel callchains */
- exclude_callchain_user : 1, /* exclude user callchains */
- mmap2 : 1, /* include mmap with inode data */
-
- __reserved_1 : 40;
-
- union {
- __u32 wakeup_events; /* wakeup every n events */
- __u32 wakeup_watermark; /* bytes before wakeup */
- };
-
- __u32 bp_type;
- union {
- __u64 bp_addr;
- __u64 config1; /* extension of config */
- };
- union {
- __u64 bp_len;
- __u64 config2; /* extension of config1 */
- };
- __u64 branch_sample_type; /* enum perf_branch_sample_type */
-
- /*
- * Defines set of user regs to dump on samples.
- * See asm/perf_regs.h for details.
- */
- __u64 sample_regs_user;
-
- /*
- * Defines size of the user stack to dump on samples.
- */
- __u32 sample_stack_user;
-
- /* Align to u64. */
- __u32 __reserved_2;
-};
-
-#define perf_flags(attr) (*(&(attr)->read_format + 1))
-
-/*
- * Ioctls that can be done on a perf event fd:
- */
-#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
-#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
-#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
-#define PERF_EVENT_IOC_RESET _IO ('$', 3)
-#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
-#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
-#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
-#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
-
-enum perf_event_ioc_flags {
- PERF_IOC_FLAG_GROUP = 1U << 0,
-};
-
-/*
- * Structure of the page that can be mapped via mmap
- */
-struct perf_event_mmap_page {
- __u32 version; /* version number of this structure */
- __u32 compat_version; /* lowest version this is compat with */
-
- /*
- * Bits needed to read the hw events in user-space.
- *
- * u32 seq, time_mult, time_shift, idx, width;
- * u64 count, enabled, running;
- * u64 cyc, time_offset;
- * s64 pmc = 0;
- *
- * do {
- * seq = pc->lock;
- * barrier()
- *
- * enabled = pc->time_enabled;
- * running = pc->time_running;
- *
- * if (pc->cap_usr_time && enabled != running) {
- * cyc = rdtsc();
- * time_offset = pc->time_offset;
- * time_mult = pc->time_mult;
- * time_shift = pc->time_shift;
- * }
- *
- * idx = pc->index;
- * count = pc->offset;
- * if (pc->cap_usr_rdpmc && idx) {
- * width = pc->pmc_width;
- * pmc = rdpmc(idx - 1);
- * }
- *
- * barrier();
- * } while (pc->lock != seq);
- *
- * NOTE: for obvious reason this only works on self-monitoring
- * processes.
- */
- __u32 lock; /* seqlock for synchronization */
- __u32 index; /* hardware event identifier */
- __s64 offset; /* add to hardware event value */
- __u64 time_enabled; /* time event active */
- __u64 time_running; /* time event on cpu */
- union {
- __u64 capabilities;
- struct {
- __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
- cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */
-
- cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */
- cap_user_time : 1, /* The time_* fields are used */
- cap_user_time_zero : 1, /* The time_zero field is used */
- cap_____res : 59;
- };
- };
-
- /*
- * If cap_usr_rdpmc this field provides the bit-width of the value
- * read using the rdpmc() or equivalent instruction. This can be used
- * to sign extend the result like:
- *
- * pmc <<= 64 - width;
- * pmc >>= 64 - width; // signed shift right
- * count += pmc;
- */
- __u16 pmc_width;
-
- /*
- * If cap_usr_time the below fields can be used to compute the time
- * delta since time_enabled (in ns) using rdtsc or similar.
- *
- * u64 quot, rem;
- * u64 delta;
- *
- * quot = (cyc >> time_shift);
- * rem = cyc & ((1 << time_shift) - 1);
- * delta = time_offset + quot * time_mult +
- * ((rem * time_mult) >> time_shift);
- *
- * Where time_offset,time_mult,time_shift and cyc are read in the
- * seqcount loop described above. This delta can then be added to
- * enabled and possible running (if idx), improving the scaling:
- *
- * enabled += delta;
- * if (idx)
- * running += delta;
- *
- * quot = count / running;
- * rem = count % running;
- * count = quot * enabled + (rem * enabled) / running;
- */
- __u16 time_shift;
- __u32 time_mult;
- __u64 time_offset;
- /*
- * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
- * from sample timestamps.
- *
- * time = timestamp - time_zero;
- * quot = time / time_mult;
- * rem = time % time_mult;
- * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
- *
- * And vice versa:
- *
- * quot = cyc >> time_shift;
- * rem = cyc & ((1 << time_shift) - 1);
- * timestamp = time_zero + quot * time_mult +
- * ((rem * time_mult) >> time_shift);
- */
- __u64 time_zero;
- __u32 size; /* Header size up to __reserved[] fields. */
-
- /*
- * Hole for extension of the self monitor capabilities
- */
-
- __u8 __reserved[118*8+4]; /* align to 1k. */
-
- /*
- * Control data for the mmap() data buffer.
- *
- * User-space reading the @data_head value should issue an smp_rmb(),
- * after reading this value.
- *
- * When the mapping is PROT_WRITE the @data_tail value should be
- * written by userspace to reflect the last read data, after issueing
- * an smp_mb() to separate the data read from the ->data_tail store.
- * In this case the kernel will not over-write unread data.
- *
- * See perf_output_put_handle() for the data ordering.
- */
- __u64 data_head; /* head in the data section */
- __u64 data_tail; /* user-space written tail */
-};
-
-#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
-#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
-#define PERF_RECORD_MISC_KERNEL (1 << 0)
-#define PERF_RECORD_MISC_USER (2 << 0)
-#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
-#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
-#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
-
-#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
-/*
- * Indicates that the content of PERF_SAMPLE_IP points to
- * the actual instruction that triggered the event. See also
- * perf_event_attr::precise_ip.
- */
-#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
-/*
- * Reserve the last bit to indicate some extended misc field
- */
-#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
-
-struct perf_event_header {
- __u32 type;
- __u16 misc;
- __u16 size;
-};
-
-enum perf_event_type {
-
- /*
- * If perf_event_attr.sample_id_all is set then all event types will
- * have the sample_type selected fields related to where/when
- * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
- * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
- * just after the perf_event_header and the fields already present for
- * the existing fields, i.e. at the end of the payload. That way a newer
- * perf.data file will be supported by older perf tools, with these new
- * optional fields being ignored.
- *
- * struct sample_id {
- * { u32 pid, tid; } && PERF_SAMPLE_TID
- * { u64 time; } && PERF_SAMPLE_TIME
- * { u64 id; } && PERF_SAMPLE_ID
- * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
- * { u32 cpu, res; } && PERF_SAMPLE_CPU
- * { u64 id; } && PERF_SAMPLE_IDENTIFIER
- * } && perf_event_attr::sample_id_all
- *
- * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
- * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
- * relative to header.size.
- */
-
- /*
- * The MMAP events record the PROT_EXEC mappings so that we can
- * correlate userspace IPs to code. They have the following structure:
- *
- * struct {
- * struct perf_event_header header;
- *
- * u32 pid, tid;
- * u64 addr;
- * u64 len;
- * u64 pgoff;
- * char filename[];
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_MMAP = 1,
-
- /*
- * struct {
- * struct perf_event_header header;
- * u64 id;
- * u64 lost;
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_LOST = 2,
-
- /*
- * struct {
- * struct perf_event_header header;
- *
- * u32 pid, tid;
- * char comm[];
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_COMM = 3,
-
- /*
- * struct {
- * struct perf_event_header header;
- * u32 pid, ppid;
- * u32 tid, ptid;
- * u64 time;
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_EXIT = 4,
-
- /*
- * struct {
- * struct perf_event_header header;
- * u64 time;
- * u64 id;
- * u64 stream_id;
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_THROTTLE = 5,
- PERF_RECORD_UNTHROTTLE = 6,
-
- /*
- * struct {
- * struct perf_event_header header;
- * u32 pid, ppid;
- * u32 tid, ptid;
- * u64 time;
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_FORK = 7,
-
- /*
- * struct {
- * struct perf_event_header header;
- * u32 pid, tid;
- *
- * struct read_format values;
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_READ = 8,
-
- /*
- * struct {
- * struct perf_event_header header;
- *
- * #
- * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
- * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
- * # is fixed relative to header.
- * #
- *
- * { u64 id; } && PERF_SAMPLE_IDENTIFIER
- * { u64 ip; } && PERF_SAMPLE_IP
- * { u32 pid, tid; } && PERF_SAMPLE_TID
- * { u64 time; } && PERF_SAMPLE_TIME
- * { u64 addr; } && PERF_SAMPLE_ADDR
- * { u64 id; } && PERF_SAMPLE_ID
- * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
- * { u32 cpu, res; } && PERF_SAMPLE_CPU
- * { u64 period; } && PERF_SAMPLE_PERIOD
- *
- * { struct read_format values; } && PERF_SAMPLE_READ
- *
- * { u64 nr,
- * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
- *
- * #
- * # The RAW record below is opaque data wrt the ABI
- * #
- * # That is, the ABI doesn't make any promises wrt to
- * # the stability of its content, it may vary depending
- * # on event, hardware, kernel version and phase of
- * # the moon.
- * #
- * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
- * #
- *
- * { u32 size;
- * char data[size];}&& PERF_SAMPLE_RAW
- *
- * { u64 nr;
- * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
- *
- * { u64 abi; # enum perf_sample_regs_abi
- * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
- *
- * { u64 size;
- * char data[size];
- * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
- *
- * { u64 weight; } && PERF_SAMPLE_WEIGHT
- * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
- * };
- */
- PERF_RECORD_SAMPLE = 9,
-
- /*
- * The MMAP2 records are an augmented version of MMAP, they add
- * maj, min, ino numbers to be used to uniquely identify each mapping
- *
- * struct {
- * struct perf_event_header header;
- *
- * u32 pid, tid;
- * u64 addr;
- * u64 len;
- * u64 pgoff;
- * u32 maj;
- * u32 min;
- * u64 ino;
- * u64 ino_generation;
- * char filename[];
- * struct sample_id sample_id;
- * };
- */
- PERF_RECORD_MMAP2 = 10,
-
- PERF_RECORD_MAX, /* non-ABI */
-};
-
-#define PERF_MAX_STACK_DEPTH 127
-
-enum perf_callchain_context {
- PERF_CONTEXT_HV = (__u64)-32,
- PERF_CONTEXT_KERNEL = (__u64)-128,
- PERF_CONTEXT_USER = (__u64)-512,
-
- PERF_CONTEXT_GUEST = (__u64)-2048,
- PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
- PERF_CONTEXT_GUEST_USER = (__u64)-2560,
-
- PERF_CONTEXT_MAX = (__u64)-4095,
-};
-
-#define PERF_FLAG_FD_NO_GROUP (1U << 0)
-#define PERF_FLAG_FD_OUTPUT (1U << 1)
-#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
-
-union perf_mem_data_src {
- __u64 val;
- struct {
- __u64 mem_op:5, /* type of opcode */
- mem_lvl:14, /* memory hierarchy level */
- mem_snoop:5, /* snoop mode */
- mem_lock:2, /* lock instr */
- mem_dtlb:7, /* tlb access */
- mem_rsvd:31;
- };
-};
-
-/* type of opcode (load/store/prefetch,code) */
-#define PERF_MEM_OP_NA 0x01 /* not available */
-#define PERF_MEM_OP_LOAD 0x02 /* load instruction */
-#define PERF_MEM_OP_STORE 0x04 /* store instruction */
-#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */
-#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */
-#define PERF_MEM_OP_SHIFT 0
-
-/* memory hierarchy (memory level, hit or miss) */
-#define PERF_MEM_LVL_NA 0x01 /* not available */
-#define PERF_MEM_LVL_HIT 0x02 /* hit level */
-#define PERF_MEM_LVL_MISS 0x04 /* miss level */
-#define PERF_MEM_LVL_L1 0x08 /* L1 */
-#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */
-#define PERF_MEM_LVL_L2 0x20 /* L2 */
-#define PERF_MEM_LVL_L3 0x40 /* L3 */
-#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */
-#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */
-#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */
-#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */
-#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
-#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
-#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
-#define PERF_MEM_LVL_SHIFT 5
-
-/* snoop mode */
-#define PERF_MEM_SNOOP_NA 0x01 /* not available */
-#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */
-#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */
-#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */
-#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */
-#define PERF_MEM_SNOOP_SHIFT 19
-
-/* locked instruction */
-#define PERF_MEM_LOCK_NA 0x01 /* not available */
-#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */
-#define PERF_MEM_LOCK_SHIFT 24
-
-/* TLB access */
-#define PERF_MEM_TLB_NA 0x01 /* not available */
-#define PERF_MEM_TLB_HIT 0x02 /* hit level */
-#define PERF_MEM_TLB_MISS 0x04 /* miss level */
-#define PERF_MEM_TLB_L1 0x08 /* L1 */
-#define PERF_MEM_TLB_L2 0x10 /* L2 */
-#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/
-#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
-#define PERF_MEM_TLB_SHIFT 26
-
-#define PERF_MEM_S(a, s) \
- (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
-
-/*
- * single taken branch record layout:
- *
- * from: source instruction (may not always be a branch insn)
- * to: branch target
- * mispred: branch target was mispredicted
- * predicted: branch target was predicted
- *
- * support for mispred, predicted is optional. In case it
- * is not supported mispred = predicted = 0.
- *
- * in_tx: running in a hardware transaction
- * abort: aborting a hardware transaction
- */
-struct perf_branch_entry {
- __u64 from;
- __u64 to;
- __u64 mispred:1, /* target mispredicted */
- predicted:1,/* target predicted */
- in_tx:1, /* in transaction */
- abort:1, /* transaction abort */
- reserved:60;
-};
-
-#endif /* _LINUX_PERF_EVENT_H */
+++ /dev/null
-perf_event.3.12.h
\ No newline at end of file
--- /dev/null
+/*
+ * Performance events:
+ *
+ * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra
+ *
+ * Data type definitions, declarations, prototypes.
+ *
+ * Started by: Thomas Gleixner and Ingo Molnar
+ *
+ * For licencing details see kernel-base/COPYING
+ */
+#ifndef _LINUX_PERF_EVENT_H
+#define _LINUX_PERF_EVENT_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/byteorder.h>
+
+/*
+ * User-space ABI bits:
+ */
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+ PERF_TYPE_BREAKPOINT = 5,
+
+ PERF_TYPE_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+ PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
+ PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
+ PERF_COUNT_HW_REF_CPU_CYCLES = 9,
+
+ PERF_COUNT_HW_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized hardware cache events:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id {
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ PERF_COUNT_HW_CACHE_LL = 2,
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ PERF_COUNT_HW_CACHE_BPU = 5,
+ PERF_COUNT_HW_CACHE_NODE = 6,
+
+ PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_id {
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+
+ PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_result_id {
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+
+ PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
+};
+
+/*
+ * Special "software" events provided by the kernel, even if the hardware
+ * does not support performance events. These events measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+ PERF_COUNT_SW_DUMMY = 9,
+
+ PERF_COUNT_SW_MAX, /* non-ABI */
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+ PERF_SAMPLE_IP = 1U << 0,
+ PERF_SAMPLE_TID = 1U << 1,
+ PERF_SAMPLE_TIME = 1U << 2,
+ PERF_SAMPLE_ADDR = 1U << 3,
+ PERF_SAMPLE_READ = 1U << 4,
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ PERF_SAMPLE_ID = 1U << 6,
+ PERF_SAMPLE_CPU = 1U << 7,
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
+ PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ PERF_SAMPLE_REGS_USER = 1U << 12,
+ PERF_SAMPLE_STACK_USER = 1U << 13,
+ PERF_SAMPLE_WEIGHT = 1U << 14,
+ PERF_SAMPLE_DATA_SRC = 1U << 15,
+ PERF_SAMPLE_IDENTIFIER = 1U << 16,
+
+ PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */
+};
+
+/*
+ * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
+ *
+ * If the user does not pass priv level information via branch_sample_type,
+ * the kernel uses the event's priv level. Branch and event priv levels do
+ * not have to match. Branch priv level is checked for permissions.
+ *
+ * The branch types can be combined, however BRANCH_ANY covers all types
+ * of branches and therefore it supersedes all the other types.
+ */
+enum perf_branch_sample_type {
+ PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */
+ PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */
+ PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */
+
+ PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */
+ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */
+ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */
+ PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */
+ PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */
+ PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */
+ PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */
+
+ PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */
+};
+
+#define PERF_SAMPLE_BRANCH_PLM_ALL \
+ (PERF_SAMPLE_BRANCH_USER|\
+ PERF_SAMPLE_BRANCH_KERNEL|\
+ PERF_SAMPLE_BRANCH_HV)
+
+/*
+ * Values to determine ABI of the registers dump.
+ */
+enum perf_sample_regs_abi {
+ PERF_SAMPLE_REGS_ABI_NONE = 0,
+ PERF_SAMPLE_REGS_ABI_32 = 1,
+ PERF_SAMPLE_REGS_ABI_64 = 2,
+};
+
+/*
+ * The format of the data returned by read() on a perf event fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ */
+enum perf_event_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
+
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+};
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
+#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
+#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
+ /* add: sample_stack_user */
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ __u32 type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+
+ /*
+ * Type specific configuration information.
+ */
+ __u64 config;
+
+ union {
+ __u64 sample_period;
+ __u64 sample_freq;
+ };
+
+ __u64 sample_type;
+ __u64 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ comm : 1, /* include comm data */
+ freq : 1, /* use freq, not period */
+ inherit_stat : 1, /* per task counts */
+ enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
+ watermark : 1, /* wakeup_watermark */
+ /*
+ * precise_ip:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ precise_ip : 2, /* skid constraint */
+ mmap_data : 1, /* non-exec mmap data */
+ sample_id_all : 1, /* sample_type all events */
+
+ exclude_host : 1, /* don't count in host */
+ exclude_guest : 1, /* don't count in guest */
+
+ exclude_callchain_kernel : 1, /* exclude kernel callchains */
+ exclude_callchain_user : 1, /* exclude user callchains */
+ mmap2 : 1, /* include mmap with inode data */
+
+ __reserved_1 : 40;
+
+ union {
+ __u32 wakeup_events; /* wakeup every n events */
+ __u32 wakeup_watermark; /* bytes before wakeup */
+ };
+
+ __u32 bp_type;
+ union {
+ __u64 bp_addr;
+ __u64 config1; /* extension of config */
+ };
+ union {
+ __u64 bp_len;
+ __u64 config2; /* extension of config1 */
+ };
+ __u64 branch_sample_type; /* enum perf_branch_sample_type */
+
+ /*
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_user;
+
+ /*
+ * Defines size of the user stack to dump on samples.
+ */
+ __u32 sample_stack_user;
+
+ /* Align to u64. */
+ __u32 __reserved_2;
+};
+
+#define perf_flags(attr) (*(&(attr)->read_format + 1))
+
+/*
+ * Ioctls that can be done on a perf event fd:
+ */
+#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
+#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
+#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
+#define PERF_EVENT_IOC_RESET _IO ('$', 3)
+#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
+#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
+#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
+
+enum perf_event_ioc_flags {
+ PERF_IOC_FLAG_GROUP = 1U << 0,
+};
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw events in user-space.
+ *
+ * u32 seq, time_mult, time_shift, idx, width;
+ * u64 count, enabled, running;
+ * u64 cyc, time_offset;
+ * s64 pmc = 0;
+ *
+ * do {
+ * seq = pc->lock;
+ * barrier()
+ *
+ * enabled = pc->time_enabled;
+ * running = pc->time_running;
+ *
+ * if (pc->cap_usr_time && enabled != running) {
+ * cyc = rdtsc();
+ * time_offset = pc->time_offset;
+ * time_mult = pc->time_mult;
+ * time_shift = pc->time_shift;
+ * }
+ *
+ * idx = pc->index;
+ * count = pc->offset;
+ * if (pc->cap_usr_rdpmc && idx) {
+ * width = pc->pmc_width;
+ * pmc = rdpmc(idx - 1);
+ * }
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware event identifier */
+ __s64 offset; /* add to hardware event value */
+ __u64 time_enabled; /* time event active */
+ __u64 time_running; /* time event on cpu */
+ union {
+ __u64 capabilities;
+ struct {
+ __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
+ cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */
+
+ cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */
+ cap_user_time : 1, /* The time_* fields are used */
+ cap_user_time_zero : 1, /* The time_zero field is used */
+ cap_____res : 59;
+ };
+ };
+
+ /*
+ * If cap_usr_rdpmc this field provides the bit-width of the value
+ * read using the rdpmc() or equivalent instruction. This can be used
+ * to sign extend the result like:
+ *
+ * pmc <<= 64 - width;
+ * pmc >>= 64 - width; // signed shift right
+ * count += pmc;
+ */
+ __u16 pmc_width;
+
+ /*
+ * If cap_usr_time the below fields can be used to compute the time
+ * delta since time_enabled (in ns) using rdtsc or similar.
+ *
+ * u64 quot, rem;
+ * u64 delta;
+ *
+ * quot = (cyc >> time_shift);
+ * rem = cyc & ((1 << time_shift) - 1);
+ * delta = time_offset + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ *
+ * Where time_offset,time_mult,time_shift and cyc are read in the
+ * seqcount loop described above. This delta can then be added to
+ * enabled and possible running (if idx), improving the scaling:
+ *
+ * enabled += delta;
+ * if (idx)
+ * running += delta;
+ *
+ * quot = count / running;
+ * rem = count % running;
+ * count = quot * enabled + (rem * enabled) / running;
+ */
+ __u16 time_shift;
+ __u32 time_mult;
+ __u64 time_offset;
+ /*
+ * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+ * from sample timestamps.
+ *
+ * time = timestamp - time_zero;
+ * quot = time / time_mult;
+ * rem = time % time_mult;
+ * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+ *
+ * And vice versa:
+ *
+ * quot = cyc >> time_shift;
+ * rem = cyc & ((1 << time_shift) - 1);
+ * timestamp = time_zero + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ */
+ __u64 time_zero;
+ __u32 size; /* Header size up to __reserved[] fields. */
+
+ /*
+ * Hole for extension of the self monitor capabilities
+ */
+
+ __u8 __reserved[118*8+4]; /* align to 1k. */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
+ */
+ __u64 data_head; /* head in the data section */
+ __u64 data_tail; /* user-space written tail */
+};
+
+#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
+#define PERF_RECORD_MISC_KERNEL (1 << 0)
+#define PERF_RECORD_MISC_USER (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
+
+#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
+ *
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ *
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ *
+ * { u64 nr;
+ * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { u64 weight; } && PERF_SAMPLE_WEIGHT
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * };
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ PERF_RECORD_MAX, /* non-ABI */
+};
+
+#define PERF_MAX_STACK_DEPTH 127
+
+enum perf_callchain_context {
+ PERF_CONTEXT_HV = (__u64)-32,
+ PERF_CONTEXT_KERNEL = (__u64)-128,
+ PERF_CONTEXT_USER = (__u64)-512,
+
+ PERF_CONTEXT_GUEST = (__u64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
+ PERF_CONTEXT_GUEST_USER = (__u64)-2560,
+
+ PERF_CONTEXT_MAX = (__u64)-4095,
+};
+
+#define PERF_FLAG_FD_NO_GROUP (1U << 0)
+#define PERF_FLAG_FD_OUTPUT (1U << 1)
+#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
+
+union perf_mem_data_src {
+ __u64 val;
+ struct {
+ __u64 mem_op:5, /* type of opcode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_snoop:5, /* snoop mode */
+ mem_lock:2, /* lock instr */
+ mem_dtlb:7, /* tlb access */
+ mem_rsvd:31;
+ };
+};
+
+/* type of opcode (load/store/prefetch,code) */
+#define PERF_MEM_OP_NA 0x01 /* not available */
+#define PERF_MEM_OP_LOAD 0x02 /* load instruction */
+#define PERF_MEM_OP_STORE 0x04 /* store instruction */
+#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */
+#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */
+#define PERF_MEM_OP_SHIFT 0
+
+/* memory hierarchy (memory level, hit or miss) */
+#define PERF_MEM_LVL_NA 0x01 /* not available */
+#define PERF_MEM_LVL_HIT 0x02 /* hit level */
+#define PERF_MEM_LVL_MISS 0x04 /* miss level */
+#define PERF_MEM_LVL_L1 0x08 /* L1 */
+#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */
+#define PERF_MEM_LVL_L2 0x20 /* L2 */
+#define PERF_MEM_LVL_L3 0x40 /* L3 */
+#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */
+#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */
+#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */
+#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */
+#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
+#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
+#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
+#define PERF_MEM_LVL_SHIFT 5
+
+/* snoop mode */
+#define PERF_MEM_SNOOP_NA 0x01 /* not available */
+#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */
+#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */
+#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */
+#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */
+#define PERF_MEM_SNOOP_SHIFT 19
+
+/* locked instruction */
+#define PERF_MEM_LOCK_NA 0x01 /* not available */
+#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */
+#define PERF_MEM_LOCK_SHIFT 24
+
+/* TLB access */
+#define PERF_MEM_TLB_NA 0x01 /* not available */
+#define PERF_MEM_TLB_HIT 0x02 /* hit level */
+#define PERF_MEM_TLB_MISS 0x04 /* miss level */
+#define PERF_MEM_TLB_L1 0x08 /* L1 */
+#define PERF_MEM_TLB_L2 0x10 /* L2 */
+#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/
+#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
+#define PERF_MEM_TLB_SHIFT 26
+
+#define PERF_MEM_S(a, s) \
+ (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
+
+/*
+ * single taken branch record layout:
+ *
+ * from: source instruction (may not always be a branch insn)
+ * to: branch target
+ * mispred: branch target was mispredicted
+ * predicted: branch target was predicted
+ *
+ * support for mispred, predicted is optional. In case it
+ * is not supported mispred = predicted = 0.
+ *
+ * in_tx: running in a hardware transaction
+ * abort: aborting a hardware transaction
+ */
+struct perf_branch_entry {
+ __u64 from;
+ __u64 to;
+ __u64 mispred:1, /* target mispredicted */
+ predicted:1,/* target predicted */
+ in_tx:1, /* in transaction */
+ abort:1, /* transaction abort */
+ reserved:60;
+};
+
+#endif /* _LINUX_PERF_EVENT_H */
#include <unistd.h>
#include "AnnotateListener.h"
-#include "CCNDriver.h"
#include "Child.h"
+#include "DriverSource.h"
#include "EventsXML.h"
#include "Logging.h"
#include "Monitor.h"
}
// Load driver
- bool success = init_module(location);
- if (!success) {
- logg->logMessage("init_module failed, trying insmod");
- snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location);
- if (system(command) != 0) {
- logg->logMessage("Unable to load gator.ko driver with command: %s", command);
- logg->logError("Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details");
- handleException();
- }
+ if (!init_module(location)) {
+ logg->logMessage("Unable to load gator.ko driver with command: %s", command);
+ logg->logError("Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details");
+ handleException();
}
if (mountGatorFS() == -1) {
}
if (driverRunningAtStart == false) {
if (syscall(__NR_delete_module, "gator", O_NONBLOCK) != 0) {
- logg->logMessage("delete_module failed, trying rmmod");
- if (system("rmmod gator >/dev/null 2>&1") != 0) {
- return -1;
- }
+ return -1;
}
}
// e.g. it may not be the group leader when launched as 'sudo gatord'
setsid();
- // Set up global thread-safe logging
+ // Set up global thread-safe logging
logg = new Logging(hasDebugFlag(argc, argv));
// Global data class
gSessionData = new SessionData();
// Set up global utility class
util = new OlyUtility();
- // Initialize drivers
- new CCNDriver();
-
prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0);
pthread_mutex_init(&numSessions_mutex, NULL);
}
// Call before setting up the SIGCHLD handler, as system() spawns child processes
- if (!setupFilesystem(cmdline.module)) {
+ if (setupFilesystem(cmdline.module)) {
+ DriverSource::checkVersion();
+ } else {
logg->logMessage("Unable to set up gatorfs, trying perf");
- if (!gSessionData->perf.setup()) {
+ if (!gSessionData->mPerf.setup()) {
logg->logError(
"Unable to locate gator.ko driver:\n"
" >>> gator.ko should be co-located with gatord in the same directory\n"
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+ifneq ($(SDKDIR),)
+
+# Find the oldest SDK Platform installed >= 16
+SDK_PLATFORM := $(shell ls -d $(SDKDIR)/platforms/android-1[6-9] $(SDKDIR)/platforms/android-[2-9][0-9] | head -1)
+# Find the newest SDK Build-tools
+SDK_BUILD_TOOLS := $(shell ls -d $(SDKDIR)/build-tools/* | tail -1)
+
+all: notify.dex
+
+notify.dex: Notify.java
+ javac -cp $(SDK_PLATFORM)/data/layoutlib.jar -source 1.6 -target 1.6 $^
+ $(SDK_BUILD_TOOLS)/dx --dex --output=$@ $(^:%.java=%.class)
+
+else
+
+all:
+ $(error Please specify SDKDIR as the location to the Android SDK)
+
+endif
+
+.PHONY: clean
+
+clean:
+ rm -f *~ *.class *.dex
--- /dev/null
+/**
+ * Copyright (C) ARM Limited 2015. All rights reserved.
+ *
+ * This 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.
+ */
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+public class Notify {
+ public static void main(String[] args) throws RemoteException {
+ for (String service : ServiceManager.listServices()) {
+ IBinder b = ServiceManager.checkService(service);
+ if (b != null) {
+ Parcel p = null;
+ try {
+ p = Parcel.obtain();
+ b.transact(IBinder.SYSPROPS_TRANSACTION, p, null, 0);
+ } finally {
+ if (p != null) {
+ p.recycle();
+ }
+ }
+ }
+ }
+ }
+}