aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:04:47 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:04:47 -0800
commit7984f7ab3e13cda0c3b04ffeb2608f232e57f93a (patch)
tree480cc437dec853127d8a2f82dcd5a2f6de124a39
parent48ae5fc270ea3bbb965b4bd07cb1691a5c115642 (diff)
downloadoprofile-7984f7ab3e13cda0c3b04ffeb2608f232e57f93a.tar.gz
Code drop from //branches/cupcake/...@124589
-rw-r--r--daemon/Android.mk6
-rw-r--r--daemon/init.c117
-rw-r--r--daemon/opd_anon.c45
-rw-r--r--daemon/opd_anon.h4
-rw-r--r--daemon/opd_cookie.c14
-rw-r--r--daemon/opd_events.c5
-rw-r--r--daemon/opd_events.h4
-rw-r--r--daemon/opd_interface.h25
-rw-r--r--daemon/opd_kernel.c41
-rw-r--r--daemon/opd_kernel.h5
-rw-r--r--daemon/opd_mangling.c13
-rw-r--r--daemon/opd_perfmon.c32
-rw-r--r--daemon/opd_pipe.c96
-rw-r--r--daemon/opd_pipe.h45
-rw-r--r--daemon/opd_sfile.c49
-rw-r--r--daemon/opd_sfile.h9
-rw-r--r--daemon/opd_spu.c176
-rw-r--r--daemon/opd_stats.c16
-rw-r--r--daemon/opd_trans.c72
-rw-r--r--daemon/opd_trans.h25
-rw-r--r--daemon/oprofiled.c78
-rw-r--r--daemon/oprofiled.h7
-rw-r--r--libabi/Android.mk (renamed from libabic/Android.mk)4
-rw-r--r--libabi/abi.cpp75
-rw-r--r--libabi/abi.h42
-rw-r--r--libabi/op_abi.c94
-rw-r--r--libabi/op_abi.h (renamed from libabic/op_abi.h)13
-rw-r--r--libabi/opimport.cpp225
-rw-r--r--libabic/op_abi.cpp84
-rw-r--r--libdb/db_debug.c14
-rw-r--r--libdb/db_insert.c86
-rw-r--r--libdb/db_manage.c104
-rw-r--r--libdb/db_stat.c5
-rw-r--r--libdb/odb.h52
-rw-r--r--libop/Android.mk1
-rw-r--r--libop/op_alloc_counter.c50
-rw-r--r--libop/op_config.c77
-rw-r--r--libop/op_config.h44
-rw-r--r--libop/op_config_24.h30
-rw-r--r--libop/op_cpu_type.c24
-rw-r--r--libop/op_cpu_type.h23
-rw-r--r--libop/op_events.c85
-rw-r--r--libop/op_events.h16
-rw-r--r--libop/op_get_interface.c5
-rw-r--r--libop/op_mangle.c23
-rw-r--r--libop/op_mangle.h1
-rw-r--r--libop/op_sample_file.h4
-rw-r--r--libutil/op_file.c114
-rw-r--r--libutil/op_file.h45
-rw-r--r--libutil/op_growable_buffer.c46
-rw-r--r--libutil/op_growable_buffer.h45
-rw-r--r--libutil/op_libiberty.c25
-rw-r--r--libutil/op_libiberty.h9
-rw-r--r--libutil/op_list.h2
-rw-r--r--libutil/op_types.h3
-rw-r--r--opcontrol/Android.mk2
-rw-r--r--opcontrol/opcontrol.cpp612
-rwxr-xr-xopimport_pull70
58 files changed, 2506 insertions, 532 deletions
diff --git a/daemon/Android.mk b/daemon/Android.mk
index ce7acae..abee74c 100644
--- a/daemon/Android.mk
+++ b/daemon/Android.mk
@@ -9,20 +9,22 @@ LOCAL_SRC_FILES:= \
opd_kernel.c \
opd_mangling.c \
opd_perfmon.c \
+ opd_pipe.c \
opd_sfile.c \
+ opd_spu.c \
opd_stats.c \
opd_trans.c \
oprofiled.c
LOCAL_STATIC_LIBRARIES := \
- libpopt libutil libdb libabic libop
+ libpopt libutil libdb libabi libop
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \
$(LOCAL_PATH)/../libdb \
$(LOCAL_PATH)/../libutil \
$(LOCAL_PATH)/../libop \
- $(LOCAL_PATH)/../libabic
+ $(LOCAL_PATH)/../libabi
LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
LOCAL_MODULE_TAGS := debug
diff --git a/daemon/init.c b/daemon/init.c
index 162861b..be0b9da 100644
--- a/daemon/init.c
+++ b/daemon/init.c
@@ -7,6 +7,10 @@
*
* @author John Levon
* @author Philippe Elie
+ * @Modifications Daniel Hansel
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#include "config.h"
@@ -14,6 +18,7 @@
#include "oprofiled.h"
#include "opd_stats.h"
#include "opd_sfile.h"
+#include "opd_pipe.h"
#include "opd_kernel.h"
#include "opd_trans.h"
#include "opd_anon.h"
@@ -30,17 +35,30 @@
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
+#include <limits.h>
#include <stdlib.h>
+#include <sys/time.h>
+#if ANDROID
+#include <sys/wait.h>
+#else
+#include <wait.h>
+#endif
+#include <string.h>
size_t kernel_pointer_size;
static fd_t devfd;
static char * sbuf;
static size_t s_buf_bytesize;
+extern char * session_dir;
+static char start_time_str[32];
+static int jit_conversion_running;
static void opd_sighup(void);
static void opd_alarm(void);
static void opd_sigterm(void);
+static void opd_sigchild(void);
+static void opd_do_jitdumps(void);
/**
* opd_open_files - open necessary files
@@ -50,7 +68,7 @@ static void opd_sigterm(void);
*/
static void opd_open_files(void)
{
- devfd = op_open_device(OP_DRIVER_BASE"/buffer");
+ devfd = op_open_device("/dev/oprofile/buffer");
if (devfd == -1) {
if (errno == EINVAL)
fprintf(stderr, "Failed to open device. Possibly you have passed incorrect\n"
@@ -61,7 +79,7 @@ static void opd_open_files(void)
}
/* give output before re-opening stdout as the logfile */
- printf("Using log file " OP_LOG_FILE "\n");
+ printf("Using log file %s\n", op_log_file);
/* set up logfile */
close(0);
@@ -73,6 +91,7 @@ static void opd_open_files(void)
}
opd_open_logfile();
+ opd_create_pipe();
printf("oprofiled started %s", op_get_time());
printf("kernel pointer size: %lu\n",
@@ -87,12 +106,12 @@ static void complete_dump(void)
FILE * status_file;
retry:
- status_file = fopen(OP_DUMP_STATUS, "w");
+ status_file = fopen(op_dump_status, "w");
if (!status_file && errno == EMFILE) {
if (sfile_lru_clear()) {
printf("LRU cleared but file open fails for %s.\n",
- OP_DUMP_STATUS);
+ op_dump_status);
abort();
}
goto retry;
@@ -130,6 +149,48 @@ static void opd_do_samples(char const * opd_buf, ssize_t count)
complete_dump();
}
+static void opd_do_jitdumps(void)
+{
+ pid_t childpid;
+ int arg_num;
+ unsigned long long end_time = 0ULL;
+ struct timeval tv;
+ char end_time_str[32];
+ char opjitconv_path[PATH_MAX + 1];
+ char * exec_args[6];
+
+ if (jit_conversion_running)
+ return;
+ jit_conversion_running = 1;
+
+ childpid = fork();
+ switch (childpid) {
+ case -1:
+ perror("Error forking JIT dump process!");
+ break;
+ case 0:
+ gettimeofday(&tv, NULL);
+ end_time = tv.tv_sec;
+ sprintf(end_time_str, "%llu", end_time);
+ sprintf(opjitconv_path, "%s/%s", OP_BINDIR, "opjitconv");
+ arg_num = 0;
+ exec_args[arg_num++] = opjitconv_path;
+ if (vmisc)
+ exec_args[arg_num++] = "-d";
+ exec_args[arg_num++] = session_dir;
+ exec_args[arg_num++] = start_time_str;
+ exec_args[arg_num++] = end_time_str;
+ exec_args[arg_num] = (char *) NULL;
+ execvp("opjitconv", exec_args);
+ fprintf(stderr, "Failed to exec %s: %s\n",
+ exec_args[0], strerror(errno));
+ /* We don't want any cleanup in the child */
+ _exit(EXIT_FAILURE);
+ default:
+ break;
+ }
+
+}
/**
* opd_do_read - enter processing loop
@@ -141,6 +202,8 @@ static void opd_do_samples(char const * opd_buf, ssize_t count)
*/
static void opd_do_read(char * buf, size_t size)
{
+ opd_open_pipe();
+
while (1) {
ssize_t count = -1;
@@ -164,6 +227,9 @@ static void opd_do_read(char * buf, size_t size)
if (signal_term)
opd_sigterm();
+ if (signal_child)
+ opd_sigchild();
+
if (signal_usr1) {
signal_usr1 = 0;
perfmon_start();
@@ -173,10 +239,17 @@ static void opd_do_read(char * buf, size_t size)
signal_usr2 = 0;
perfmon_stop();
}
+
+ if (is_jitconv_requested()) {
+ verbprintf(vmisc, "Start opjitconv was triggered\n");
+ opd_do_jitdumps();
+ }
}
opd_do_samples(buf, count);
}
+
+ opd_close_pipe();
}
@@ -204,27 +277,45 @@ static void opd_sighup(void)
static void clean_exit(void)
{
perfmon_exit();
- unlink(OP_LOCK_FILE);
+ unlink(op_lock_file);
}
static void opd_sigterm(void)
{
+ opd_do_jitdumps();
opd_print_stats();
printf("oprofiled stopped %s", op_get_time());
exit(EXIT_FAILURE);
}
-
+/* SIGCHLD received from JIT dump child process. */
+static void opd_sigchild(void)
+{
+ int child_status;
+ wait(&child_status);
+ jit_conversion_running = 0;
+ if (WIFEXITED(child_status) && (!WEXITSTATUS(child_status))) {
+ verbprintf(vmisc, "JIT dump processing complete.\n");
+ } else {
+ printf("JIT dump processing exited abnormally: %d\n",
+ WEXITSTATUS(child_status));
+ }
+
+}
+
static void opd_26_init(void)
{
size_t i;
size_t opd_buf_size;
+ unsigned long long start_time = 0ULL;
+ struct timeval tv;
opd_create_vmlinux(vmlinux, kernel_range);
+ opd_create_xen(xenimage, xen_range);
- opd_buf_size = opd_read_fs_int(OP_DRIVER_BASE"/", "buffer_size", 1);
- kernel_pointer_size = opd_read_fs_int(OP_DRIVER_BASE"/", "pointer_size", 1);
+ opd_buf_size = opd_read_fs_int("/dev/oprofile/", "buffer_size", 1);
+ kernel_pointer_size = opd_read_fs_int("/dev/oprofile/", "pointer_size", 1);
s_buf_bytesize = opd_buf_size * kernel_pointer_size;
@@ -247,13 +338,19 @@ static void opd_26_init(void)
perror("oprofiled: couldn't set exit cleanup: ");
exit(EXIT_FAILURE);
}
+
+ /* trigger kernel module setup before returning control to opcontrol */
+ opd_open_files();
+ gettimeofday(&tv, NULL);
+ start_time = 0ULL;
+ start_time = tv.tv_sec;
+ sprintf(start_time_str, "%llu", start_time);
+
}
static void opd_26_start(void)
{
- opd_open_files();
-
/* simple sleep-then-process loop */
opd_do_read(sbuf, s_buf_bytesize);
}
diff --git a/daemon/opd_anon.c b/daemon/opd_anon.c
index b5e42d8..9caea3d 100644
--- a/daemon/opd_anon.c
+++ b/daemon/opd_anon.c
@@ -14,6 +14,7 @@
* @remark Read the file COPYING
*
* @author John Levon
+ * @Modifications Gisle Dankel
*/
#include "opd_anon.h"
@@ -25,21 +26,24 @@
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#define HASH_SIZE 1024
#define HASH_BITS (HASH_SIZE - 1)
-#define LRU_SIZE 1000
-#define LRU_AMOUNT (LRU_SIZE/5)
+/*
+ * Note that this value is tempered by the fact that when we miss in the
+ * anon cache, we'll tear down all the mappings for that tgid. Thus, LRU
+ * of a mapping can potentially clear out a much larger number of
+ * mappings.
+ */
+#define LRU_SIZE 8192
+#define LRU_AMOUNT (LRU_SIZE/8)
static struct list_head hashes[HASH_SIZE];
static struct list_head lru;
static size_t nr_lru;
-/* never called in buffer-processing context, so we don't need
- * to update struct transient. Note the 'U' of LRU is based on
- * parsing time, not actual use.
- */
static void do_lru(struct transient * trans)
{
size_t nr_to_kill = LRU_AMOUNT;
@@ -53,6 +57,7 @@ static void do_lru(struct transient * trans)
clear_trans_current(trans);
if (trans->last_anon == entry)
clear_trans_last(trans);
+ sfile_clear_anon(entry);
list_del(&entry->list);
list_del(&entry->lru_list);
--nr_lru;
@@ -60,8 +65,6 @@ static void do_lru(struct transient * trans)
if (nr_to_kill-- == 0)
break;
}
-
- sfile_clear_anon();
}
@@ -87,6 +90,7 @@ static void clear_anon_maps(struct transient * trans)
if (entry->tgid == tgid && entry->app_cookie == app) {
if (trans->last_anon == entry)
clear_trans_last(trans);
+ sfile_clear_anon(entry);
list_del(&entry->list);
list_del(&entry->lru_list);
--nr_lru;
@@ -94,7 +98,6 @@ static void clear_anon_maps(struct transient * trans)
}
}
- sfile_clear_anon();
if (vmisc) {
char const * name = verbose_cookie(app);
printf("Cleared anon maps for tgid %u (%s).\n", tgid, name);
@@ -103,7 +106,7 @@ static void clear_anon_maps(struct transient * trans)
static void
-add_anon_mapping(struct transient * trans, vma_t start, vma_t end)
+add_anon_mapping(struct transient * trans, vma_t start, vma_t end, char * name)
{
unsigned long hash = hash_anon(trans->tgid, trans->app_cookie);
struct anon_mapping * m = xmalloc(sizeof(struct anon_mapping));
@@ -111,6 +114,7 @@ add_anon_mapping(struct transient * trans, vma_t start, vma_t end)
m->app_cookie = trans->app_cookie;
m->start = start;
m->end = end;
+ strncpy(m->name, name, MAX_IMAGE_NAME_SIZE + 1);
list_add_tail(&m->list, &hashes[hash]);
list_add_tail(&m->lru_list, &lru);
if (++nr_lru == LRU_SIZE)
@@ -128,8 +132,7 @@ static void get_anon_maps(struct transient * trans)
{
FILE * fp = NULL;
char buf[PATH_MAX];
- unsigned long start;
- unsigned long end;
+ vma_t start, end;
int ret;
snprintf(buf, PATH_MAX, "/proc/%d/maps", trans->tgid);
@@ -138,16 +141,20 @@ static void get_anon_maps(struct transient * trans)
return;
while (fgets(buf, PATH_MAX, fp) != NULL) {
- char tmp[20];
- /* Note that this actually includes all mappings,
- * since we want stuff like [heap]
+ char tmp[MAX_IMAGE_NAME_SIZE + 1];
+ char name[MAX_IMAGE_NAME_SIZE + 1];
+ /* Some anon maps have labels like
+ * [heap], [stack], [vdso], [vsyscall] ...
+ * Keep track of these labels. If a map has no name, call it "anon".
+ * Ignore all mappings starting with "/" (file or shared memory object)
*/
- ret = sscanf(buf, "%lx-%lx %19s %19s %19s %19s %19s",
- &start, &end, tmp, tmp, tmp, tmp, tmp);
- if (ret < 6)
+ strcpy(name, "anon");
+ ret = sscanf(buf, "%llx-%llx %20s %20s %20s %20s %20s",
+ &start, &end, tmp, tmp, tmp, tmp, name);
+ if (ret < 6 || name[0] == '/')
continue;
- add_anon_mapping(trans, start, end);
+ add_anon_mapping(trans, start, end, name);
}
fclose(fp);
diff --git a/daemon/opd_anon.h b/daemon/opd_anon.h
index 63a7723..3f66b55 100644
--- a/daemon/opd_anon.h
+++ b/daemon/opd_anon.h
@@ -25,6 +25,9 @@ struct transient;
*/
#define VMA_SHIFT 13
+/* Maximum size of the image name considered */
+#define MAX_IMAGE_NAME_SIZE 20
+
struct anon_mapping {
/** start of the mapping */
vma_t start;
@@ -38,6 +41,7 @@ struct anon_mapping {
struct list_head list;
/** lru list */
struct list_head lru_list;
+ char name[MAX_IMAGE_NAME_SIZE+1];
};
/**
diff --git a/daemon/opd_cookie.c b/daemon/opd_cookie.c
index 48e1e80..3578e48 100644
--- a/daemon/opd_cookie.c
+++ b/daemon/opd_cookie.c
@@ -60,12 +60,24 @@
#endif /* __NR_lookup_dcookie */
#if (defined(__powerpc__) && !defined(__powerpc64__)) || defined(__hppa__)\
- || (defined(__s390__) && !defined(__s390x__))
+ || (defined(__s390__) && !defined(__s390x__)) \
+ || (defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) \
+ && defined(__MIPSEB__)) \
+ || (defined(__arm__) && defined(__ARM_EABI__) \
+ && defined(__ARMEB__))
static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
{
return syscall(__NR_lookup_dcookie, (unsigned long)(cookie >> 32),
(unsigned long)(cookie & 0xffffffff), buf, size);
}
+#elif (defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)) \
+ || (defined(__arm__) && defined(__ARM_EABI__))
+static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
+{
+ return syscall(__NR_lookup_dcookie,
+ (unsigned long)(cookie & 0xffffffff),
+ (unsigned long)(cookie >> 32), buf, size);
+}
#else
static inline int lookup_dcookie(cookie_t cookie, char * buf, size_t size)
{
diff --git a/daemon/opd_events.c b/daemon/opd_events.c
index f9346df..81a87d2 100644
--- a/daemon/opd_events.c
+++ b/daemon/opd_events.c
@@ -142,7 +142,8 @@ struct opd_event * find_counter_event(unsigned long counter)
void fill_header(struct opd_header * header, unsigned long counter,
vma_t anon_start, vma_t cg_to_anon_start,
- int is_kernel, int cg_to_is_kernel, time_t mtime)
+ int is_kernel, int cg_to_is_kernel,
+ int spu_samples, uint64_t embed_offset, time_t mtime)
{
struct opd_event * event = find_counter_event(counter);
@@ -158,5 +159,7 @@ void fill_header(struct opd_header * header, unsigned long counter,
header->cpu_speed = cpu_speed;
header->mtime = mtime;
header->anon_start = anon_start;
+ header->spu_profile = spu_samples;
+ header->embedded_offset = embed_offset;
header->cg_to_anon_start = cg_to_anon_start;
}
diff --git a/daemon/opd_events.h b/daemon/opd_events.h
index 5b86836..3bd0106 100644
--- a/daemon/opd_events.h
+++ b/daemon/opd_events.h
@@ -13,6 +13,7 @@
#include "op_types.h"
+#include <stdint.h>
#include <time.h>
/** event description for setup (perfmon) and mangling */
@@ -40,6 +41,7 @@ struct opd_header;
/** fill the sample file header with event info etc. */
void fill_header(struct opd_header * header, unsigned long counter,
vma_t anon_start, vma_t anon_end,
- int is_kernel, int cg_to_is_kernel, time_t mtime);
+ int is_kernel, int cg_to_is_kernel,
+ int spu_samples, uint64_t embed_offset, time_t mtime);
#endif /* OPD_EVENTS_H */
diff --git a/daemon/opd_interface.h b/daemon/opd_interface.h
index 8abb2c4..c876830 100644
--- a/daemon/opd_interface.h
+++ b/daemon/opd_interface.h
@@ -8,6 +8,9 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#ifndef OPD_INTERFACE_H
@@ -17,10 +20,26 @@
#define CPU_SWITCH_CODE 2
#define COOKIE_SWITCH_CODE 3
#define KERNEL_ENTER_SWITCH_CODE 4
-#define KERNEL_EXIT_SWITCH_CODE 5
-#define MODULE_LOADED_CODE 6
+#define USER_ENTER_SWITCH_CODE 5
+#define MODULE_LOADED_CODE 6
#define CTX_TGID_CODE 7
#define TRACE_BEGIN_CODE 8
-#define LAST_CODE 9
+/* Code 9 used to be TRACE_END_CODE which is not used anymore */
+/* Code 9 is now considered an unknown escape code */
+#define XEN_ENTER_SWITCH_CODE 10
+/*
+ * Ugly work-around for the unfortunate collision between Xenoprof's
+ * DOMAIN_SWITCH_CODE (in use on x86) and Cell's SPU_PROFILING_CODE
+ * (in use with Power):
+ */
+#if defined(__powerpc__)
+#define SPU_PROFILING_CODE 11
+#define SPU_CTX_SWITCH_CODE 12
+#define DOMAIN_SWITCH_CODE 13
+#define LAST_CODE 14
+#else
+#define DOMAIN_SWITCH_CODE 11
+#define LAST_CODE 12
+#endif
#endif /* OPD_INTERFACE_H */
diff --git a/daemon/opd_kernel.c b/daemon/opd_kernel.c
index d314bc9..5ebc210 100644
--- a/daemon/opd_kernel.c
+++ b/daemon/opd_kernel.c
@@ -7,6 +7,9 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#include "opd_kernel.h"
@@ -29,6 +32,8 @@ static LIST_HEAD(modules);
static struct kernel_image vmlinux_image;
+static struct kernel_image xen_image;
+
void opd_create_vmlinux(char const * name, char const * arg)
{
/* vmlinux is *not* on the list of modules */
@@ -54,6 +59,31 @@ void opd_create_vmlinux(char const * name, char const * arg)
}
}
+void opd_create_xen(char const * name, char const * arg)
+{
+ /* xen is *not* on the list of modules */
+ list_init(&xen_image.list);
+
+ /* for no xen */
+ if (no_xen) {
+ xen_image.name = "no-xen";
+ return;
+ }
+
+ xen_image.name = xstrdup(name);
+
+ sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end);
+
+ verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n",
+ xen_image.start, xen_image.end);
+
+ if (!xen_image.start && !xen_image.end) {
+ fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n",
+ xen_image.start, xen_image.end);
+ exit(EXIT_FAILURE);
+ }
+}
+
/**
* Allocate and initialise a kernel image description
@@ -112,11 +142,11 @@ void opd_reread_module_info(void)
char * line;
struct kernel_image * image;
int module_size;
- char ref_count[32];
+ char ref_count[32+1];
int ret;
char module_name[256+1];
- char live_info[32];
- char dependencies[4096];
+ char live_info[32+1];
+ char dependencies[4096+1];
unsigned long long start_address;
if (no_vmlinux)
@@ -145,7 +175,7 @@ void opd_reread_module_info(void)
continue;
}
- ret = sscanf(line, "%256s %u %31s %4095s %31s %llx",
+ ret = sscanf(line, "%256s %u %32s %4096s %32s %llx",
module_name, &module_size, ref_count,
dependencies, live_info, &start_address);
if (ret != 6) {
@@ -192,5 +222,8 @@ struct kernel_image * find_kernel_image(struct transient const * trans)
return image;
}
+ if (xen_image.start <= trans->pc && xen_image.end > trans->pc)
+ return &xen_image;
+
return NULL;
}
diff --git a/daemon/opd_kernel.h b/daemon/opd_kernel.h
index 7c035ee..cb71a30 100644
--- a/daemon/opd_kernel.h
+++ b/daemon/opd_kernel.h
@@ -7,6 +7,9 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#ifndef OPD_KERNEL_H
@@ -20,6 +23,8 @@ struct transient;
/** create the kernel image */
void opd_create_vmlinux(char const * name, char const * arg);
+void opd_create_xen(char const * name, char const * arg);
+
/** opd_reread_module_info - parse /proc/modules for kernel modules */
void opd_reread_module_info(void);
diff --git a/daemon/opd_mangling.c b/daemon/opd_mangling.c
index a22f98c..08a6079 100644
--- a/daemon/opd_mangling.c
+++ b/daemon/opd_mangling.c
@@ -57,8 +57,10 @@ static char const * get_dep_name(struct sfile const * sf)
static char * mangle_anon(struct anon_mapping const * anon)
{
char * name = xmalloc(PATH_MAX);
+
snprintf(name, 1024, "%u.0x%llx.0x%llx", (unsigned int)anon->tgid,
- anon->start, anon->end);
+ anon->start, anon->end);
+
return name;
}
@@ -78,6 +80,7 @@ mangle_filename(struct sfile * last, struct sfile const * sf, int counter, int c
} else if (sf->anon) {
values.flags |= MANGLE_ANON;
values.image_name = mangle_anon(sf->anon);
+ values.anon_name = sf->anon->name;
} else {
values.image_name = find_cookie(sf->cookie);
}
@@ -108,6 +111,7 @@ mangle_filename(struct sfile * last, struct sfile const * sf, int counter, int c
} else if (last->anon) {
values.flags |= MANGLE_CG_ANON;
values.cg_image_name = mangle_anon(last->anon);
+ values.anon_name = last->anon->name;
} else {
values.cg_image_name = find_cookie(last->cookie);
}
@@ -139,6 +143,7 @@ int opd_open_sample_file(odb_t * file, struct sfile * last,
{
char * mangled;
char const * binary;
+ int spu_profile = 0;
vma_t last_start = 0;
int err;
@@ -182,10 +187,14 @@ retry:
if (last && last->anon)
last_start = last->anon->start;
+ if (sf->embedded_offset != UNUSED_EMBEDDED_OFFSET)
+ spu_profile = 1;
+
fill_header(odb_get_data(file), counter,
sf->anon ? sf->anon->start : 0, last_start,
!!sf->kernel, last ? !!last->kernel : 0,
- binary ? op_get_mtime(binary) : 0);
+ spu_profile, sf->embedded_offset,
+ binary ? op_get_mtime(binary) : 0);
out:
sfile_put(sf);
diff --git a/daemon/opd_perfmon.c b/daemon/opd_perfmon.c
index d95030d..a1b158a 100644
--- a/daemon/opd_perfmon.c
+++ b/daemon/opd_perfmon.c
@@ -380,6 +380,7 @@ static void wait_for_child(struct child * child)
close(child->up_pipe[1]);
}
+static struct child* xen_ctx;
void perfmon_init(void)
{
@@ -389,6 +390,24 @@ void perfmon_init(void)
if (cpu_type == CPU_TIMER_INT)
return;
+ if (!no_xen) {
+ xen_ctx = xmalloc(sizeof(struct child));
+ xen_ctx->pid = getpid();
+ xen_ctx->up_pipe[0] = -1;
+ xen_ctx->up_pipe[1] = -1;
+ xen_ctx->sigusr1 = 0;
+ xen_ctx->sigusr2 = 0;
+ xen_ctx->sigterm = 0;
+
+ create_context(xen_ctx);
+
+ write_pmu(xen_ctx);
+
+ load_context(xen_ctx);
+ return;
+ }
+
+
nr = sysconf(_SC_NPROCESSORS_ONLN);
if (nr == -1) {
fprintf(stderr, "Couldn't determine number of CPUs.\n");
@@ -431,6 +450,9 @@ void perfmon_exit(void)
if (cpu_type == CPU_TIMER_INT)
return;
+ if (!no_xen)
+ return;
+
for (i = 0; i < nr_cpus; ++i) {
kill(children[i].pid, SIGKILL);
waitpid(children[i].pid, NULL, 0);
@@ -445,6 +467,11 @@ void perfmon_start(void)
if (cpu_type == CPU_TIMER_INT)
return;
+ if (!no_xen) {
+ perfmon_start_child(xen_ctx->ctx_fd);
+ return;
+ }
+
for (i = 0; i < nr_cpus; ++i)
kill(children[i].pid, SIGUSR1);
}
@@ -457,6 +484,11 @@ void perfmon_stop(void)
if (cpu_type == CPU_TIMER_INT)
return;
+ if (!no_xen) {
+ perfmon_stop_child(xen_ctx->ctx_fd);
+ return;
+ }
+
for (i = 0; i < nr_cpus; ++i)
kill(children[i].pid, SIGUSR2);
}
diff --git a/daemon/opd_pipe.c b/daemon/opd_pipe.c
new file mode 100644
index 0000000..a5c334a
--- /dev/null
+++ b/daemon/opd_pipe.c
@@ -0,0 +1,96 @@
+/**
+ * @file daemon/opd_pipe.c
+ * Functions handling the $SESSIONDIR/opd_pipe FIFO special file.
+ * NOTE: This code is dealing with potentially insecure input.
+ *
+ * @remark Copyright 2008 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Daniel Hansel
+ */
+
+#include "opd_pipe.h"
+#include "opd_printf.h"
+#include "op_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static int fifo;
+
+void opd_create_pipe(void)
+{
+ mode_t orig_umask = umask(0111);
+ if (mkfifo(op_pipe_file, 0666) == -1) {
+ if (errno != EEXIST) {
+ perror("oprofiled: couldn't create pipe: ");
+ exit(EXIT_FAILURE);
+ }
+ }
+ umask(orig_umask);
+}
+
+
+void opd_open_pipe(void)
+{
+ fifo = open(op_pipe_file, O_RDONLY | O_NONBLOCK);
+ if (fifo == -1) {
+ perror("oprofiled: couldn't open pipe: ");
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void opd_close_pipe(void)
+{
+ close(fifo);
+}
+
+
+int is_jitconv_requested(void)
+{
+ /* number of dropped (unknown) requests */
+ static long nr_drops = 0;
+ /* modulus to output only a few warnings to avoid flooding oprofiled.log */
+ static int mod_cnt_drops = 1;
+ FILE * fd;
+ char line[256];
+ int i, ret = 0;
+
+ /* get a file descriptor to the pipe */
+ fd = fdopen(fifo, "r");
+
+ if (fd == NULL) {
+ perror("oprofiled: couldn't create file descriptor: ");
+ exit(EXIT_FAILURE);
+ }
+
+ /* read up to 99 lines to check for 'do_jitconv' */
+ for (i = 0; i < 99; i++) {
+ /* just break if no new line is found */
+ if (fgets(line, 256, fd) == NULL)
+ break;
+ line[strlen(line) - 1] = '\0';
+
+ if (strstr(line, "do_jitconv") != NULL) {
+ ret = 1;
+ } else {
+ nr_drops++;
+
+ if (nr_drops % mod_cnt_drops == 0) {
+ printf(
+ "Warning: invalid pipe request received (dropped request(s): %ld)\n",
+ nr_drops);
+ /* increase modulus to avoid flooding log file */
+ mod_cnt_drops *= 5;
+ }
+ }
+ }
+
+ return ret;
+}
diff --git a/daemon/opd_pipe.h b/daemon/opd_pipe.h
new file mode 100644
index 0000000..7f96b07
--- /dev/null
+++ b/daemon/opd_pipe.h
@@ -0,0 +1,45 @@
+/**
+ * @file daemon/opd_pipe.h
+ * Functions handling the $SESSIONDIR/opd_pipe FIFO special file.
+ * NOTE: This code is dealing with potencially insecure input.
+ *
+ * @remark Copyright 2008 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Daniel Hansel
+ */
+
+#ifndef OPD_PIPE_H_
+#define OPD_PIPE_H_
+
+/**
+ * opd_create_pipe - creates the oprofiled fifo file
+ *
+ * Creates the Oprofile daemon fifo pipe to enable communication between
+ * the daemon and the 'opcontrol --dump' command. Failure to create the pipe
+ * is a fatal error.
+ */
+void opd_create_pipe(void);
+
+/**
+ * opd_open_pipe - opens the oprofiled fifo file
+ */
+void opd_open_pipe(void);
+
+/**
+ * opd_close_pipe - closes the oprofiled fifo file
+ *
+ * Closes the Oprofile daemon fifo pipe.
+ */
+void opd_close_pipe(void);
+
+/**
+ * is_jitconv_requested - check for request to jit conversion
+ *
+ * Checks the Oprofile daemon fifo pipe for do_jitconv request.
+ * If jit conversion is requested ('do_jitconv' is sent) the check returns 1.
+ * Otherwise it returns 0.
+ */
+int is_jitconv_requested(void);
+
+#endif /*OPD_PIPE_H_*/
diff --git a/daemon/opd_sfile.c b/daemon/opd_sfile.c
index 58fdccd..03ebf55 100644
--- a/daemon/opd_sfile.c
+++ b/daemon/opd_sfile.c
@@ -199,6 +199,14 @@ create_sfile(unsigned long hash, struct transient const * trans,
sf->ignored = is_sf_ignored(sf);
+ sf->embedded_offset = trans->embedded_offset;
+
+ /* If embedded_offset is a valid value, it means we're
+ * processing a Cell BE SPU profile; in which case, we
+ * want sf->app_cookie to hold trans->app_cookie.
+ */
+ if (trans->embedded_offset != UNUSED_EMBEDDED_OFFSET)
+ sf->app_cookie = trans->app_cookie;
return sf;
}
@@ -399,7 +407,7 @@ static void sfile_log_arc(struct transient const * trans)
key = to & (0xffffffff);
key |= ((uint64_t)from) << 32;
- err = odb_insert(file, key, 1);
+ err = odb_update_node(file, key);
if (err) {
fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
abort();
@@ -438,7 +446,7 @@ void sfile_log_sample(struct transient const * trans)
return;
}
- err = odb_insert(file, (uint64_t)pc, 1);
+ err = odb_update_node(file, (uint64_t)pc);
if (err) {
fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(err));
abort();
@@ -446,7 +454,7 @@ void sfile_log_sample(struct transient const * trans)
}
-static int close_sfile(struct sfile * sf)
+static int close_sfile(struct sfile * sf, void * data __attribute__((unused)))
{
size_t i;
@@ -460,13 +468,13 @@ static int close_sfile(struct sfile * sf)
static void kill_sfile(struct sfile * sf)
{
- close_sfile(sf);
+ close_sfile(sf, NULL);
list_del(&sf->hash);
list_del(&sf->lru);
}
-static int sync_sfile(struct sfile * sf)
+static int sync_sfile(struct sfile * sf, void * data __attribute__((unused)))
{
size_t i;
@@ -477,22 +485,25 @@ static int sync_sfile(struct sfile * sf)
}
-static int is_sfile_kernel(struct sfile * sf)
+static int is_sfile_kernel(struct sfile * sf, void * data __attribute__((unused)))
{
return !!sf->kernel;
}
-static int is_sfile_anon(struct sfile * sf)
+static int is_sfile_anon(struct sfile * sf, void * data)
{
- return !!sf->anon;
+ return sf->anon == data;
}
-static void for_one_sfile(struct sfile * sf, int (*func)(struct sfile *))
+typedef int (*sfile_func)(struct sfile *, void *);
+
+static void
+for_one_sfile(struct sfile * sf, sfile_func func, void * data)
{
size_t i;
- int free_sf = func(sf);
+ int free_sf = func(sf, data);
for (i = 0; i < CG_HASH_SIZE; ++i) {
struct list_head * pos;
@@ -500,7 +511,7 @@ static void for_one_sfile(struct sfile * sf, int (*func)(struct sfile *))
list_for_each_safe(pos, pos2, &sf->cg_hash[i]) {
struct cg_entry * cg =
list_entry(pos, struct cg_entry, hash);
- if (free_sf || func(&cg->to)) {
+ if (free_sf || func(&cg->to, data)) {
kill_sfile(&cg->to);
list_del(&cg->hash);
free(cg);
@@ -515,39 +526,39 @@ static void for_one_sfile(struct sfile * sf, int (*func)(struct sfile *))
}
-static void for_each_sfile(int (*func)(struct sfile *))
+static void for_each_sfile(sfile_func func, void * data)
{
struct list_head * pos;
struct list_head * pos2;
list_for_each_safe(pos, pos2, &lru_list) {
struct sfile * sf = list_entry(pos, struct sfile, lru);
- for_one_sfile(sf, func);
+ for_one_sfile(sf, func, data);
}
}
void sfile_clear_kernel(void)
{
- for_each_sfile(is_sfile_kernel);
+ for_each_sfile(is_sfile_kernel, NULL);
}
-void sfile_clear_anon(void)
+void sfile_clear_anon(struct anon_mapping * anon)
{
- for_each_sfile(is_sfile_anon);
+ for_each_sfile(is_sfile_anon, anon);
}
void sfile_sync_files(void)
{
- for_each_sfile(sync_sfile);
+ for_each_sfile(sync_sfile, NULL);
}
void sfile_close_files(void)
{
- for_each_sfile(close_sfile);
+ for_each_sfile(close_sfile, NULL);
}
@@ -578,7 +589,7 @@ int sfile_lru_clear(void)
if (!--amount)
break;
sf = list_entry(pos, struct sfile, lru);
- for_one_sfile(sf, (int (*)(struct sfile *))always_true);
+ for_one_sfile(sf, (sfile_func)always_true, NULL);
}
return 0;
diff --git a/daemon/opd_sfile.h b/daemon/opd_sfile.h
index 56ed25b..86d5025 100644
--- a/daemon/opd_sfile.h
+++ b/daemon/opd_sfile.h
@@ -25,6 +25,7 @@ struct kernel_image;
struct transient;
#define CG_HASH_SIZE 16
+#define UNUSED_EMBEDDED_OFFSET ~0LLU
/**
* Each set of sample files (where a set is over the physical counter
@@ -50,6 +51,8 @@ struct sfile {
struct kernel_image * kernel;
/** anonymous mapping */
struct anon_mapping * anon;
+ /** embedded offset for Cell BE SPU */
+ uint64_t embedded_offset;
/** hash table link */
struct list_head hash;
@@ -74,8 +77,10 @@ struct cg_entry {
/** clear any sfiles that are for the kernel */
void sfile_clear_kernel(void);
-/** clear any sfiles that are for anon mappings */
-void sfile_clear_anon(void);
+struct anon_mapping;
+
+/** clear any sfiles for the given anon mapping */
+void sfile_clear_anon(struct anon_mapping *);
/** sync sample files */
void sfile_sync_files(void);
diff --git a/daemon/opd_spu.c b/daemon/opd_spu.c
new file mode 100644
index 0000000..62a2c2b
--- /dev/null
+++ b/daemon/opd_spu.c
@@ -0,0 +1,176 @@
+/**
+ * @file daemon/opd_spu.c
+ * Processing the sample buffer for Cell BE SPU profile
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Maynard Johnson
+ * (C) Copyright IBM Corporation 2007
+ */
+
+#include "opd_interface.h"
+#include "opd_printf.h"
+#include "opd_sfile.h"
+#include "opd_stats.h"
+#include "opd_trans.h"
+#include "op_libiberty.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+struct spu_context_info {
+ pid_t tid;
+ pid_t tgid;
+ cookie_t app_cookie;
+ uint64_t embedded_offset;
+ cookie_t spu_cookie;
+};
+
+static struct spu_context_info * spu_context_cache;
+
+/* Forward declaration */
+static void process_spu_samples(struct transient * trans);
+
+void (*special_processor)(struct transient *);
+
+/*
+ * This function is called when the first value found in the
+ * buffer (after the beginning ESCAPE_CODE) is SPU_PROFILING_CODE.
+ * Once we get here, the rest of the processing of the buffer is
+ * Cell-SPU-specific, so we do not need to return until the
+ * trans.buffer is empty.
+ */
+void code_spu_profiling(struct transient * trans)
+{
+ /* Next value in buffer is the number of SPUs. */
+ unsigned long long num_spus = pop_buffer_value(trans);
+ /* Free the cache from previous run */
+ free(spu_context_cache);
+ spu_context_cache = xmalloc(sizeof(struct spu_context_info) * num_spus);
+ special_processor = process_spu_samples;
+ process_spu_samples(trans);
+}
+
+void code_spu_ctx_switch(struct transient * trans)
+{
+ clear_trans_current(trans);
+
+ if (!enough_remaining(trans, 6)) {
+ trans->remaining = 0;
+ return;
+ }
+
+ /* First value in the buffer for an SPU context switch is
+ * the SPU number. For SPU profiling, 'cpu' = 'spu'.
+ */
+ trans->cpu = pop_buffer_value(trans);
+ trans->tid = pop_buffer_value(trans);
+ trans->tgid = pop_buffer_value(trans);
+ trans->app_cookie = pop_buffer_value(trans);
+
+ if (vmisc) {
+ char const * app = find_cookie(trans->app_cookie);
+ printf("SPU_CTX_SWITCH to tid %lu, tgid %lu, cookie %llx(%s)\n",
+ (unsigned long)trans->tid, (unsigned long)trans->tgid,
+ trans->app_cookie, app ? app : "none");
+ }
+
+ /* The trans->cookie will point to the binary file where the SPU ELF
+ * can be found. If the SPU ELF is embedded, it may be embedded in
+ * either the executable application binary or a shared lib. If shared
+ * library, then trans->cookie will differ from the previously obtained
+ * trans->app_cookie. For the non-embedded case, trans->cookie always
+ * points to a separate binary file.
+ */
+ trans->cookie = pop_buffer_value(trans);
+ trans->embedded_offset = pop_buffer_value(trans);
+}
+
+
+static void cache_spu_context_info(struct transient * trans)
+{
+ int i = trans->cpu;
+ spu_context_cache[i].tid = trans->tid;
+ spu_context_cache[i].tgid = trans->tgid;
+ spu_context_cache[i].app_cookie = trans->app_cookie;
+ spu_context_cache[i].embedded_offset = trans->embedded_offset;
+ spu_context_cache[i].spu_cookie = trans->cookie;
+}
+
+static void update_trans_for_spu(struct transient * trans)
+{
+ int i = trans->cpu;
+ trans->tid = spu_context_cache[i].tid;
+ trans->tgid = spu_context_cache[i].tgid;
+ trans->app_cookie = spu_context_cache[i].app_cookie;
+ trans->embedded_offset = spu_context_cache[i].embedded_offset;
+ trans->cookie = spu_context_cache[i].spu_cookie;
+}
+#define SPU_NUM_MASK 0xFFFFFFFF00000000ULL
+#define SPU_CYCLES_COUNTER 0
+
+static void opd_put_spu_sample
+(struct transient * trans, unsigned long long pc)
+{
+ unsigned long spu_number = (pc & SPU_NUM_MASK) >> 32;
+ if (trans->cpu != spu_number) {
+ trans->cpu = spu_number;
+ clear_trans_current(trans);
+ update_trans_for_spu(trans);
+ }
+ /* get the current sfile if needed */
+ if (!trans->current)
+ trans->current = sfile_find(trans);
+
+ if (trans->tracing != TRACING_ON)
+ trans->event = SPU_CYCLES_COUNTER;
+
+ trans->pc = (pc & ~SPU_NUM_MASK);
+ /* log the sample or arc */
+ sfile_log_sample(trans);
+
+ /* switch to trace mode */
+ if (trans->tracing == TRACING_START)
+ trans->tracing = TRACING_ON;
+
+ update_trans_last(trans);
+}
+
+/*
+ * This function processes SPU context switches and
+ * SPU program counter samples. After processing a
+ * context switch (via handlers[code)), we cache the
+ * SPU context information that has been temporarily
+ * stored in trans.
+ */
+static void process_spu_samples(struct transient * trans)
+{
+ unsigned long long code;
+ trans->in_kernel = 0;
+ while (trans->remaining) {
+ code = pop_buffer_value(trans);
+
+ if (!is_escape_code(code)) {
+ opd_put_spu_sample(trans, code);
+ continue;
+ }
+
+ if (!trans->remaining) {
+ verbprintf(vmisc, "Dangling ESCAPE_CODE.\n");
+ opd_stats[OPD_DANGLING_CODE]++;
+ break;
+ }
+
+ /* started with ESCAPE_CODE, next is type */
+ code = pop_buffer_value(trans);
+
+ if (code >= LAST_CODE) {
+ fprintf(stderr, "Unknown code %llu\n", code);
+ abort();
+ }
+
+ handlers[code](trans);
+ cache_spu_context_info(trans);
+ }
+}
diff --git a/daemon/opd_stats.c b/daemon/opd_stats.c
index 1a7f0f7..ddb1940 100644
--- a/daemon/opd_stats.c
+++ b/daemon/opd_stats.c
@@ -9,12 +9,10 @@
* @author Philippe Elie
*/
-#include "config.h"
#include "opd_stats.h"
#include "oprofiled.h"
#include "op_get_time.h"
-#include "op_config.h"
#include <dirent.h>
#include <stdlib.h>
@@ -53,22 +51,22 @@ void opd_print_stats(void)
printf("Nr. samples lost due to no permanent mapping: %lu\n",
opd_stats[OPD_LOST_NO_MAPPING]);
print_if("Nr. event lost due to buffer overflow: %u\n",
- OP_DRIVER_BASE"/stats", "event_lost_overflow", 1);
+ "/dev/oprofile/stats", "event_lost_overflow", 1);
print_if("Nr. samples lost due to no mapping: %u\n",
- OP_DRIVER_BASE"/stats", "sample_lost_no_mapping", 1);
+ "/dev/oprofile/stats", "sample_lost_no_mapping", 1);
print_if("Nr. backtraces skipped due to no file mapping: %u\n",
- OP_DRIVER_BASE"/stats", "bt_lost_no_mapping", 0);
+ "/dev/oprofile/stats", "bt_lost_no_mapping", 0);
print_if("Nr. samples lost due to no mm: %u\n",
- OP_DRIVER_BASE"/stats", "sample_lost_no_mm", 1);
+ "/dev/oprofile/stats", "sample_lost_no_mm", 1);
- if (!(dir = opendir(OP_DRIVER_BASE"/stats/")))
+ if (!(dir = opendir("/dev/oprofile/stats/")))
goto out;
while ((dirent = readdir(dir))) {
int cpu_nr;
char path[256];
if (sscanf(dirent->d_name, "cpu%d", &cpu_nr) != 1)
continue;
- snprintf(path, 256, OP_DRIVER_BASE"/stats/%s", dirent->d_name);
+ snprintf(path, 256, "/dev/oprofile/stats/%s", dirent->d_name);
print_if("Nr. samples lost cpu buffer overflow: %u\n",
path, "sample_lost_overflow", 1);
@@ -78,6 +76,8 @@ void opd_print_stats(void)
path, "sample_received", 1);
print_if("Nr. backtrace aborted: %u\n",
path, "backtrace_aborted", 0);
+ print_if("Nr. samples lost invalid pc: %u\n",
+ path, "sample_invalid_eip", 0);
}
closedir(dir);
out:
diff --git a/daemon/opd_trans.c b/daemon/opd_trans.c
index eb22e5f..871e6e6 100644
--- a/daemon/opd_trans.c
+++ b/daemon/opd_trans.c
@@ -7,6 +7,13 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
+ *
+ * Modified by Maynard Johnson <maynardj@us.ibm.com>
+ * These modifications are:
+ * (C) Copyright IBM Corporation 2007
*/
#include "opd_trans.h"
@@ -41,21 +48,7 @@ void clear_trans_current(struct transient * trans)
}
-void update_trans_last(struct transient * trans)
-{
- trans->last = trans->current;
- trans->last_anon = trans->anon;
- trans->last_pc = trans->pc;
-}
-
-
-static inline int is_escape_code(uint64_t code)
-{
- return kernel_pointer_size == 4 ? code == ~0LU : code == ~0LLU;
-}
-
-
-static uint64_t pop_buffer_value(struct transient * trans)
+uint64_t pop_buffer_value(struct transient * trans)
{
uint64_t val;
@@ -78,7 +71,7 @@ static uint64_t pop_buffer_value(struct transient * trans)
}
-static int enough_remaining(struct transient * trans, size_t size)
+int enough_remaining(struct transient * trans, size_t size)
{
if (trans->remaining >= size)
return 1;
@@ -219,9 +212,9 @@ static void code_kernel_enter(struct transient * trans)
}
-static void code_kernel_exit(struct transient * trans)
+static void code_user_enter(struct transient * trans)
{
- verbprintf(vmisc, "KERNEL_EXIT_SWITCH to user-space\n");
+ verbprintf(vmisc, "USER_ENTER_SWITCH to user-space\n");
trans->in_kernel = 0;
clear_trans_current(trans);
clear_trans_last(trans);
@@ -248,22 +241,44 @@ static void code_trace_begin(struct transient * trans)
trans->tracing = TRACING_START;
}
+static void code_xen_enter(struct transient * trans)
+{
+ verbprintf(vmisc, "XEN_ENTER_SWITCH to xen\n");
+ trans->in_kernel = 1;
+ trans->current = NULL;
+ /* subtlety: we must keep trans->cookie cached, even though it's
+ * meaningless for Xen - we won't necessarily get a cookie switch
+ * on Xen exit. See comments in opd_sfile.c. It seems that we can
+ * get away with in_kernel = 1 as long as we supply the correct
+ * Xen image, and its address range in startup find_kernel_image
+ * is modified to look in the Xen image also
+ */
+}
-typedef void (*handler_t)(struct transient *);
+extern void code_spu_profiling(struct transient * trans);
+extern void code_spu_ctx_switch(struct transient * trans);
-static handler_t handlers[LAST_CODE + 1] = {
+handler_t handlers[LAST_CODE + 1] = {
&code_unknown,
&code_ctx_switch,
&code_cpu_switch,
&code_cookie_switch,
&code_kernel_enter,
- &code_kernel_exit,
+ &code_user_enter,
&code_module_loaded,
/* tgid handled differently */
&code_unknown,
&code_trace_begin,
+ &code_unknown,
+ &code_xen_enter,
+#if defined(__powerpc__)
+ &code_spu_profiling,
+ &code_spu_ctx_switch,
+#endif
+ &code_unknown,
};
+extern void (*special_processor)(struct transient *);
void opd_process_samples(char const * buffer, size_t count)
{
@@ -283,6 +298,7 @@ void opd_process_samples(char const * buffer, size_t count)
.in_kernel = -1,
.cpu = -1,
.tid = -1,
+ .embedded_offset = UNUSED_EMBEDDED_OFFSET,
.tgid = -1
};
@@ -292,9 +308,22 @@ void opd_process_samples(char const * buffer, size_t count)
*/
unsigned long long code;
+ if (special_processor) {
+ special_processor(&trans);
+ return;
+ }
+
+ int i;
+
+ for (i = 0; i < count && i < 200; i++) {
+ verbprintf(vmisc, "buffer[%d] is %x\n", i, buffer[i]);
+ }
+
while (trans.remaining) {
code = pop_buffer_value(&trans);
+ verbprintf(vmisc, "In opd_process_samples (code is %lld)\n", code);
+
if (!is_escape_code(code)) {
opd_put_sample(&trans, code);
continue;
@@ -309,6 +338,7 @@ void opd_process_samples(char const * buffer, size_t count)
// started with ESCAPE_CODE, next is type
code = pop_buffer_value(&trans);
+ verbprintf(vmisc, "next code is %lld\n", code);
if (code >= LAST_CODE) {
fprintf(stderr, "Unknown code %llu\n", code);
abort();
diff --git a/daemon/opd_trans.h b/daemon/opd_trans.h
index d5d601b..ab4e816 100644
--- a/daemon/opd_trans.h
+++ b/daemon/opd_trans.h
@@ -7,6 +7,10 @@
*
* @author John Levon
* @author Philippe Elie
+ *
+ * Modified by Maynard Johnson <maynardj@us.ibm.com>
+ * These modifications are:
+ * (C) Copyright IBM Corporation 2007
*/
#ifndef OPD_TRANS_H
@@ -15,6 +19,8 @@
#include "opd_cookie.h"
#include "op_types.h"
+#include <stdint.h>
+
struct sfile;
struct anon_mapping;
@@ -47,8 +53,27 @@ struct transient {
unsigned long cpu;
pid_t tid;
pid_t tgid;
+ uint64_t embedded_offset;
};
+typedef void (*handler_t)(struct transient *);
+extern handler_t handlers[];
+
+uint64_t pop_buffer_value(struct transient * trans);
+int enough_remaining(struct transient * trans, size_t size);
+static inline void update_trans_last(struct transient * trans)
+{
+ trans->last = trans->current;
+ trans->last_anon = trans->anon;
+ trans->last_pc = trans->pc;
+}
+
+extern size_t kernel_pointer_size;
+static inline int is_escape_code(uint64_t code)
+{
+ return kernel_pointer_size == 4 ? code == ~0LU : code == ~0LLU;
+}
+
void opd_process_samples(char const * buffer, size_t count);
/** used when we need to clear data that's been freed */
diff --git a/daemon/oprofiled.c b/daemon/oprofiled.c
index 281f10f..ec2ea1b 100644
--- a/daemon/oprofiled.c
+++ b/daemon/oprofiled.c
@@ -7,6 +7,9 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#include "config.h"
@@ -43,6 +46,7 @@
sig_atomic_t signal_alarm;
sig_atomic_t signal_hup;
sig_atomic_t signal_term;
+sig_atomic_t signal_child;
sig_atomic_t signal_usr1;
sig_atomic_t signal_usr2;
@@ -60,6 +64,10 @@ int separate_cpu;
int no_vmlinux;
char * vmlinux;
char * kernel_range;
+char * session_dir;
+int no_xen;
+char * xenimage;
+char * xen_range;
static char * verbose;
static char * binary_name_filter;
static char * events;
@@ -72,9 +80,12 @@ extern struct oprofiled_ops opd_26_ops;
static struct list_head images_filter[OPD_IMAGE_FILTER_HASH_SIZE];
static struct poptOption options[] = {
+ { "session-dir", 0, POPT_ARG_STRING, &session_dir, 0, "place sample database in dir instead of default location", "/var/lib/oprofile", },
{ "kernel-range", 'r', POPT_ARG_STRING, &kernel_range, 0, "Kernel VMA range", "start-end", },
{ "vmlinux", 'k', POPT_ARG_STRING, &vmlinux, 0, "vmlinux kernel image", "file", },
{ "no-vmlinux", 0, POPT_ARG_NONE, &no_vmlinux, 0, "vmlinux kernel image file not available", NULL, },
+ { "xen-range", 0, POPT_ARG_STRING, &xen_range, 0, "Xen VMA range", "start-end", },
+ { "xen-image", 0, POPT_ARG_STRING, &xenimage, 0, "Xen image", "file", },
{ "image", 0, POPT_ARG_STRING, &binary_name_filter, 0, "image name filter", "profile these comma separated image" },
{ "separate-lib", 0, POPT_ARG_INT, &separate_lib, 0, "separate library samples for each distinct application", "[0|1]", },
{ "separate-kernel", 0, POPT_ARG_INT, &separate_kernel, 0, "separate kernel samples for each distinct application", "[0|1]", },
@@ -90,7 +101,7 @@ static struct poptOption options[] = {
void opd_open_logfile(void)
{
- if (open(OP_LOG_FILE, O_WRONLY|O_CREAT|O_NOCTTY|O_APPEND, 0755) == -1) {
+ if (open(op_log_file, O_WRONLY|O_CREAT|O_NOCTTY|O_APPEND, 0644) == -1) {
perror("oprofiled: couldn't re-open stdout: ");
exit(EXIT_FAILURE);
}
@@ -129,9 +140,9 @@ static void opd_go_daemon(void)
{
opd_fork();
- if (chdir(OP_BASE_DIR)) {
- fprintf(stderr, "oprofiled: opd_go_daemon: couldn't chdir to "
- OP_BASE_DIR ": %s", strerror(errno));
+ if (chdir(op_session_dir)) {
+ fprintf(stderr, "oprofiled: opd_go_daemon: couldn't chdir to %s: %s",
+ op_session_dir, strerror(errno));
exit(EXIT_FAILURE);
}
@@ -146,15 +157,13 @@ static void opd_go_daemon(void)
static void opd_write_abi(void)
{
-#ifdef OPROF_ABI
char * cbuf;
- cbuf = xmalloc(strlen(OP_BASE_DIR) + 5);
- strcpy(cbuf, OP_BASE_DIR);
+ cbuf = xmalloc(strlen(op_session_dir) + 5);
+ strcpy(cbuf, op_session_dir);
strcat(cbuf, "/abi");
op_write_abi_to_file(cbuf);
free(cbuf);
-#endif
}
@@ -178,6 +187,11 @@ static void opd_sigterm(int val __attribute__((unused)))
{
signal_term = 1;
}
+
+static void opd_sigchild(int val __attribute__((unused)))
+{
+ signal_child = 1;
+}
static void opd_sigusr1(int val __attribute__((unused)))
@@ -225,6 +239,16 @@ static void opd_setup_signals(void)
exit(EXIT_FAILURE);
}
+ act.sa_handler = opd_sigchild;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaddset(&act.sa_mask, SIGCHLD);
+
+ if (sigaction(SIGCHLD, &act, NULL)) {
+ perror("oprofiled: install of SIGCHLD handler failed: ");
+ exit(EXIT_FAILURE);
+ }
+
act.sa_handler = opd_sigusr1;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
@@ -241,7 +265,7 @@ static void opd_setup_signals(void)
sigaddset(&act.sa_mask, SIGTERM);
if (sigaction(SIGUSR2, &act, NULL)) {
- perror("oprofiled: install of SIGUSR1 handler failed: ");
+ perror("oprofiled: install of SIGUSR2 handler failed: ");
exit(EXIT_FAILURE);
}
}
@@ -407,7 +431,26 @@ static void opd_options(int argc, char const * argv[])
poptPrintHelp(optcon, stderr, 0);
exit(EXIT_FAILURE);
}
-
+
+ if (!xenimage || !strcmp("", xenimage)) {
+ no_xen = 1;
+ } else {
+ no_xen = 0;
+
+ /* canonicalise xen image filename. */
+ tmp = xmalloc(PATH_MAX);
+ if (realpath(xenimage, tmp))
+ xenimage = tmp;
+ else
+ free(tmp);
+
+ if (!xen_range || !strcmp("", xen_range)) {
+ fprintf(stderr, "oprofiled: no Xen VMA range specified.\n");
+ poptPrintHelp(optcon, stderr, 0);
+ exit(EXIT_FAILURE);
+ }
+ }
+
opd_parse_events(events);
opd_parse_image_filter();
@@ -422,10 +465,11 @@ static void opd_options(int argc, char const * argv[])
static struct oprofiled_ops * get_ops(void)
{
switch (op_get_interface()) {
+#ifndef ANDROID
case OP_INTERFACE_24:
printf("Using 2.4 OProfile kernel interface.\n");
- //return &opd_24_ops;
- return 0; // android. we should never need that.
+ return &opd_24_ops;
+#endif
case OP_INTERFACE_26:
printf("Using 2.6+ OProfile kernel interface.\n");
return &opd_26_ops;
@@ -445,13 +489,13 @@ int main(int argc, char const * argv[])
struct rlimit rlim = { 2048, 2048 };
opd_options(argc, argv);
+ init_op_config_dirs(session_dir);
opd_setup_signals();
err = setrlimit(RLIMIT_NOFILE, &rlim);
- if (err) {
+ if (err)
perror("warning: could not set RLIMIT_NOFILE to 2048: ");
- }
opd_write_abi();
@@ -464,9 +508,9 @@ int main(int argc, char const * argv[])
/* clean up every 10 minutes */
alarm(60 * 10);
- if (op_write_lock_file(OP_LOCK_FILE)) {
- fprintf(stderr, "oprofiled: could not create lock file "
- OP_LOCK_FILE "\n");
+ if (op_write_lock_file(op_lock_file)) {
+ fprintf(stderr, "oprofiled: could not create lock file %s\n",
+ op_lock_file);
exit(EXIT_FAILURE);
}
diff --git a/daemon/oprofiled.h b/daemon/oprofiled.h
index 23f40d2..b319df1 100644
--- a/daemon/oprofiled.h
+++ b/daemon/oprofiled.h
@@ -7,6 +7,9 @@
*
* @author John Levon
* @author Philippe Elie
+ * Modified by Aravind Menon for Xen
+ * These modifications are:
+ * Copyright (C) 2005 Hewlett-Packard Co.
*/
#ifndef OPROFILED_H
@@ -47,6 +50,7 @@ int opd_read_fs_int(char const * path, char const * name, int is_fatal);
extern sig_atomic_t signal_alarm;
extern sig_atomic_t signal_hup;
extern sig_atomic_t signal_term;
+extern sig_atomic_t signal_child;
extern sig_atomic_t signal_usr1;
extern sig_atomic_t signal_usr2;
@@ -58,5 +62,8 @@ extern int separate_cpu;
extern int no_vmlinux;
extern char * vmlinux;
extern char * kernel_range;
+extern int no_xen;
+extern char * xenimage;
+extern char * xen_range;
#endif /* OPROFILED_H */
diff --git a/libabic/Android.mk b/libabi/Android.mk
index 416c067..3213c1e 100644
--- a/libabic/Android.mk
+++ b/libabi/Android.mk
@@ -1,9 +1,9 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= op_abi.cpp
+LOCAL_SRC_FILES:= op_abi.c
-LOCAL_MODULE := libabic
+LOCAL_MODULE := libabi
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \
diff --git a/libabi/abi.cpp b/libabi/abi.cpp
new file mode 100644
index 0000000..04a6b2d
--- /dev/null
+++ b/libabi/abi.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file abi.cpp
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ * @author John Levon
+ * @author Philippe Elie
+ */
+
+#include "abi.h"
+#include "op_abi.h"
+#include "odb.h"
+#include "op_sample_file.h"
+
+#include <iostream>
+#include <cassert>
+
+using namespace std;
+
+typedef map<string, int> abi_map;
+typedef abi_map::const_iterator abi_iter;
+
+abi_exception::abi_exception(string const d) : desc(d) {}
+
+
+abi::abi()
+{
+ op_abi_entry const * entry = get_abi();
+ for ( ; entry->name != 0; ++entry)
+ slots[entry->name] = entry->offset;
+
+ slots["little_endian"] = op_little_endian();
+}
+
+
+int abi::need(string const key) const throw (abi_exception)
+{
+ if (slots.find(key) != slots.end())
+ return slots.find(key)->second;
+ else
+ throw abi_exception(string("missing ABI key: ") + key);
+}
+
+
+bool abi::operator==(abi const & other) const
+{
+ return slots == other.slots;
+}
+
+
+ostream & operator<<(ostream & o, abi const & abi)
+{
+ abi_iter i = abi.slots.begin();
+ abi_iter e = abi.slots.end();
+
+ for (; i != e; ++i)
+ o << i->first << " " << i->second << endl;
+
+ return o;
+}
+
+
+istream & operator>>(istream & i, abi & abi)
+{
+ string key;
+ int val;
+ abi.slots.clear();
+
+ while(i >> key >> val)
+ abi.slots[key] = val;
+
+ return i;
+}
diff --git a/libabi/abi.h b/libabi/abi.h
new file mode 100644
index 0000000..8b63e7f
--- /dev/null
+++ b/libabi/abi.h
@@ -0,0 +1,42 @@
+/**
+ * @file abi.h
+ *
+ * Contains internal ABI management class
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ */
+
+#ifndef OPROF_ABI_H
+#define OPROF_ABI_H
+
+#include <string>
+#include <map>
+#include <iosfwd>
+
+struct abi_exception : std::exception {
+ std::string const desc;
+
+ explicit abi_exception(std::string const d);
+
+ ~abi_exception() throw() {}
+};
+
+
+class abi {
+public:
+ abi();
+
+ int need(std::string const key) const throw (abi_exception);
+
+ bool operator==(abi const & other) const;
+ friend std::ostream & operator<<(std::ostream & o, abi const & abi);
+ friend std::istream & operator>>(std::istream & i, abi & abi);
+
+private:
+ std::map<std::string, int> slots;
+};
+
+#endif // OPROF_ABI_H
diff --git a/libabi/op_abi.c b/libabi/op_abi.c
new file mode 100644
index 0000000..283e3ff
--- /dev/null
+++ b/libabi/op_abi.c
@@ -0,0 +1,94 @@
+/**
+ * @file op_abi.c
+ * This file contains a simple C interface to the ABI-describing functionality,
+ * the majority of which is implemented in C++. This is the file which is
+ * intended for use in files outside the /libabi directory.
+ *
+ * @remark Copyright 2002, 2005 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ * @author Philippe Elie
+ */
+
+#include "op_abi.h"
+#include "odb.h"
+#include "op_sample_file.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <assert.h>
+
+static struct op_abi_entry const abi_entries[] = {
+ { "sizeof_double", sizeof(double) },
+ { "sizeof_time_t", sizeof(time_t) },
+ { "sizeof_u8", sizeof(u8) },
+ { "sizeof_u32", sizeof(u32) },
+ { "sizeof_int", sizeof(int) },
+ { "sizeof_unsigned_int", sizeof(unsigned int) },
+ { "sizeof_odb_key_t", sizeof(odb_key_t) },
+ { "sizeof_odb_index_t", sizeof(odb_index_t) },
+ { "sizeof_odb_value_t", sizeof(odb_value_t) },
+ { "sizeof_odb_node_nr_t", sizeof(odb_node_nr_t) },
+ { "sizeof_odb_descr_t", sizeof(odb_descr_t) },
+ { "sizeof_odb_node_t", sizeof(odb_node_t) },
+ { "sizeof_struct_opd_header", sizeof(struct opd_header) },
+
+ { "offsetof_node_key", offsetof(odb_node_t, key) },
+ { "offsetof_node_value", offsetof(odb_node_t, value) },
+ { "offsetof_node_next", offsetof(odb_node_t, next) },
+
+ { "offsetof_descr_size", offsetof(odb_descr_t, size) },
+ { "offsetof_descr_current_size", offsetof(odb_descr_t, current_size) },
+
+ { "offsetof_header_magic", offsetof(struct opd_header, magic) },
+ { "offsetof_header_version", offsetof(struct opd_header, version) },
+ { "offsetof_header_cpu_type", offsetof(struct opd_header, cpu_type) },
+ { "offsetof_header_ctr_event", offsetof(struct opd_header, ctr_event) },
+ { "offsetof_header_ctr_um", offsetof(struct opd_header, ctr_um) },
+ { "offsetof_header_ctr_count", offsetof(struct opd_header, ctr_count) },
+ { "offsetof_header_is_kernel", offsetof(struct opd_header, is_kernel) },
+ { "offsetof_header_cpu_speed", offsetof(struct opd_header, cpu_speed) },
+ { "offsetof_header_mtime", offsetof(struct opd_header, mtime) },
+ { "offsetof_header_cg_to_is_kernel", offsetof(struct opd_header, cg_to_is_kernel), },
+ { "offsetof_header_anon_start", offsetof(struct opd_header, anon_start) },
+ { "offsetof_header_cg_to_anon_start", offsetof(struct opd_header, cg_to_anon_start) },
+
+ { NULL, 0 },
+};
+
+
+struct op_abi_entry const * get_abi(void)
+{
+ return abi_entries;
+}
+
+
+int op_little_endian(void)
+{
+ unsigned int probe = 0xff;
+ size_t sz = sizeof(unsigned int);
+ unsigned char * probe_byte = (unsigned char *)&probe;
+
+ assert(probe_byte[0] == 0xff || probe_byte[sz - 1] == 0xff);
+
+ return probe_byte[0] == 0xff;
+}
+
+
+int op_write_abi_to_file(char const * abi_file)
+{
+ FILE * fp;
+ struct op_abi_entry const * abi_entry;
+
+ if ((fp = fopen(abi_file, "w")) == NULL)
+ return 0;
+
+ for (abi_entry = get_abi() ; abi_entry->name != NULL; ++abi_entry)
+ fprintf(fp, "%s %u\n", abi_entry->name, abi_entry->offset);
+ fprintf(fp, "little_endian %d\n", op_little_endian());
+
+ fclose(fp);
+
+ return 1;
+}
diff --git a/libabic/op_abi.h b/libabi/op_abi.h
index 94ddd20..fbd337b 100644
--- a/libabic/op_abi.h
+++ b/libabi/op_abi.h
@@ -8,15 +8,28 @@
* @remark Read the file COPYING
*
* @author Graydon Hoare
+ * @author Philippe Elie
*/
#ifndef OP_ABI_H
#define OP_ABI_H
+struct op_abi_entry {
+ char const * name;
+ /// offset or size of the named entry
+ int offset;
+};
+
#ifdef __cplusplus
extern "C" {
#endif
+/// return array is terminated by a NULL entry in name field
+struct op_abi_entry const * get_abi(void);
+
+/// return non zero if the abi is little endian
+int op_little_endian(void);
+
/**
* Write current abi to file.
* return 1 on success, 0 on failure
diff --git a/libabi/opimport.cpp b/libabi/opimport.cpp
new file mode 100644
index 0000000..57f74a7
--- /dev/null
+++ b/libabi/opimport.cpp
@@ -0,0 +1,225 @@
+/**
+ * @file opimport.cpp
+ * Import sample files from other ABI
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Graydon Hoare
+ */
+
+#include "abi.h"
+#include "odb.h"
+#include "popt_options.h"
+#include "op_sample_file.h"
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+#include <cassert>
+#include <cstring>
+#include <cstdlib>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <cstdlib>
+#include <cstring>
+
+using namespace std;
+
+namespace {
+ string output_filename;
+ string abi_filename;
+ bool verbose;
+ bool force;
+};
+
+
+popt::option options_array[] = {
+ popt::option(verbose, "verbose", 'V', "verbose output"),
+ popt::option(output_filename, "output", 'o', "output to file", "filename"),
+ popt::option(abi_filename, "abi", 'a', "abi description", "filename"),
+ popt::option(force, "force", 'f', "force conversion, even if identical")
+};
+
+
+struct extractor {
+
+ abi const & theabi;
+
+ unsigned char const * begin;
+ unsigned char const * end;
+ bool little_endian;
+
+ explicit
+ extractor(abi const & a, unsigned char const * src, size_t len)
+ : theabi(a), begin(src), end(src + len) {
+ little_endian = theabi.need(string("little_endian")) == 1;
+ if (verbose) {
+ cerr << "source byte order is: "
+ << string(little_endian ? "little" : "big")
+ << " endian" << endl;
+ }
+ }
+
+ template <typename T>
+ void extract(T & targ, void const * src_,
+ char const * sz, char const * off);
+};
+
+
+template <typename T>
+void extractor::extract(T & targ, void const * src_,
+ char const * sz, char const * off)
+{
+ unsigned char const * src = static_cast<unsigned char const *>(src_)
+ + theabi.need(off);
+ size_t nbytes = theabi.need(sz);
+
+ if (nbytes == 0)
+ return;
+
+ assert(nbytes <= sizeof(T));
+ assert(src >= begin);
+ assert(src + nbytes <= end);
+
+ if (verbose)
+ cerr << hex << "get " << sz << " = " << nbytes
+ << " bytes @ " << off << " = " << (src - begin)
+ << " : ";
+
+ targ = 0;
+ if (little_endian)
+ while(nbytes--)
+ targ = (targ << 8) | src[nbytes];
+ else
+ for(size_t i = 0; i < nbytes; ++i)
+ targ = (targ << 8) | src[i];
+
+ if (verbose)
+ cerr << " = " << targ << endl;
+}
+
+
+void import_from_abi(abi const & abi, void const * srcv,
+ size_t len, odb_t * dest) throw (abi_exception)
+{
+ struct opd_header * head =
+ static_cast<opd_header *>(odb_get_data(dest));
+ unsigned char const * src = static_cast<unsigned char const *>(srcv);
+ unsigned char const * const begin = src;
+ extractor ext(abi, src, len);
+
+ memcpy(head->magic, src + abi.need("offsetof_header_magic"), 4);
+
+ // begin extracting opd header
+ ext.extract(head->version, src, "sizeof_u32", "offsetof_header_version");
+ ext.extract(head->cpu_type, src, "sizeof_u32", "offsetof_header_cpu_type");
+ ext.extract(head->ctr_event, src, "sizeof_u32", "offsetof_header_ctr_event");
+ ext.extract(head->ctr_um, src, "sizeof_u32", "offsetof_header_ctr_um");
+ ext.extract(head->ctr_count, src, "sizeof_u32", "offsetof_header_ctr_count");
+ ext.extract(head->is_kernel, src, "sizeof_u32", "offsetof_header_is_kernel");
+ // "double" extraction is unlikely to work
+ head->cpu_speed = 0.0;
+ ext.extract(head->mtime, src, "sizeof_time_t", "offsetof_header_mtime");
+ ext.extract(head->cg_to_is_kernel, src, "sizeof_u32",
+ "offsetof_header_cg_to_is_kernel");
+ ext.extract(head->anon_start, src, "sizeof_u32",
+ "offsetof_header_anon_start");
+ ext.extract(head->cg_to_anon_start, src, "sizeof_u32",
+ "offsetof_header_cg_to_anon_start");
+ src += abi.need("sizeof_struct_opd_header");
+ // done extracting opd header
+
+ // begin extracting necessary parts of descr
+ odb_node_nr_t node_nr;
+ ext.extract(node_nr, src, "sizeof_odb_node_nr_t", "offsetof_descr_current_size");
+ src += abi.need("sizeof_odb_descr_t");
+ // done extracting descr
+
+ // skip node zero, it is reserved and contains nothing usefull
+ src += abi.need("sizeof_odb_node_t");
+
+ // begin extracting nodes
+ unsigned int step = abi.need("sizeof_odb_node_t");
+ if (verbose)
+ cerr << "extracting " << node_nr << " nodes of " << step << " bytes each " << endl;
+
+ assert(src + (node_nr * step) <= begin + len);
+
+ for (odb_node_nr_t i = 1 ; i < node_nr ; ++i, src += step) {
+ odb_key_t key;
+ odb_value_t val;
+ ext.extract(key, src, "sizeof_odb_key_t", "offsetof_node_key");
+ ext.extract(val, src, "sizeof_odb_value_t", "offsetof_node_value");
+ int rc = odb_add_node(dest, key, val);
+ if (rc != EXIT_SUCCESS) {
+ cerr << strerror(rc) << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+ // done extracting nodes
+}
+
+
+int main(int argc, char const ** argv)
+{
+
+ vector<string> inputs;
+ popt::parse_options(argc, argv, inputs);
+
+ if (inputs.size() != 1) {
+ cerr << "error: must specify exactly 1 input file" << endl;
+ exit(1);
+ }
+
+ abi current_abi, input_abi;
+
+ {
+ ifstream abi_file(abi_filename.c_str());
+ if (!abi_file) {
+ cerr << "error: cannot open abi file "
+ << abi_filename << endl;
+ exit(1);
+ }
+ abi_file >> input_abi;
+ }
+
+ if (!force && current_abi == input_abi) {
+ cerr << "input abi is identical to native. "
+ << "no conversion necessary." << endl;
+ exit(1);
+ }
+
+ int in_fd;
+ struct stat statb;
+ void * in;
+ odb_t dest;
+ int rc;
+
+ assert((in_fd = open(inputs[0].c_str(), O_RDONLY)) > 0);
+ assert(fstat(in_fd, &statb) == 0);
+ assert((in = mmap(0, statb.st_size, PROT_READ,
+ MAP_PRIVATE, in_fd, 0)) != (void *)-1);
+
+ rc = odb_open(&dest, output_filename.c_str(), ODB_RDWR,
+ sizeof(struct opd_header));
+ if (rc) {
+ cerr << "odb_open() fail:\n"
+ << strerror(rc) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try {
+ import_from_abi(input_abi, in, statb.st_size, &dest);
+ } catch (abi_exception & e) {
+ cerr << "caught abi exception: " << e.desc << endl;
+ }
+
+ odb_close(&dest);
+
+ assert(munmap(in, statb.st_size) == 0);
+}
diff --git a/libabic/op_abi.cpp b/libabic/op_abi.cpp
deleted file mode 100644
index 94db9a0..0000000
--- a/libabic/op_abi.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * @file op_abi.cpp
- * This file contains a simple C interface to the ABI-describing functionality,
- * the majority of which is implemented in C++. this is the file which is
- * intended for use in files outside the /libabi directory.
- *
- * @remark Copyright 2002 OProfile authors
- * @remark Read the file COPYING
- *
- * @author Graydon Hoare
- */
-
-#include "op_abi.h"
-#include "odb.h"
-#include "op_sample_file.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#define byte_addr(x) (reinterpret_cast<unsigned char *>(&(x)))
-#define field_offset(s, f) (byte_addr(s.f) - byte_addr(s))
-
-int op_write_abi_to_file(char const * abi_file)
-{
- odb_node_t node;
- odb_descr_t descr;
- struct opd_header header;
-
- FILE* file = fopen(abi_file, "wt");
-
- fprintf(file, "sizeof_double %u\n", sizeof(double));
-
- fprintf(file, "sizeof_time_t %u\n", sizeof(time_t));
- fprintf(file, "sizeof_u8 %u\n", sizeof(u8));
- fprintf(file, "sizeof_u32 %u\n", sizeof(u32));
- fprintf(file, "sizeof_int %u\n", sizeof(int));
- fprintf(file, "sizeof_unsigned_int %u\n", sizeof(unsigned int));
- fprintf(file, "sizeof_odb_key_t %u\n", sizeof(odb_key_t));
- fprintf(file, "sizeof_odb_index_t %u\n", sizeof(odb_index_t));
- fprintf(file, "sizeof_odb_value_t %u\n", sizeof(odb_value_t));
- fprintf(file, "sizeof_odb_node_nr_t %u\n", sizeof(odb_node_nr_t));
- fprintf(file, "sizeof_odb_descr_t %u\n", sizeof(odb_descr_t));
- fprintf(file, "sizeof_odb_node_t %u\n", sizeof(odb_node_t));
- fprintf(file, "sizeof_struct_opd_header %u\n", sizeof(struct opd_header));
-
- fprintf(file, "offsetof_node_key %u\n", field_offset(node, key));
- fprintf(file, "offsetof_node_value %u\n", field_offset(node, value));
- fprintf(file, "offsetof_node_next %u\n", field_offset(node, next));
-
- fprintf(file, "offsetof_descr_size %u\n", field_offset(descr, size));
- fprintf(file, "offsetof_descr_current_size %u\n", field_offset(descr, current_size));
-
- fprintf(file, "offsetof_header_magic %u\n", field_offset(header, magic));
- fprintf(file, "offsetof_header_version %u\n", field_offset(header, version));
- fprintf(file, "offsetof_header_cpu_type %u\n", field_offset(header, cpu_type));
- fprintf(file, "offsetof_header_ctr_event %u\n", field_offset(header, ctr_event));
- fprintf(file, "offsetof_header_ctr_um %u\n", field_offset(header, ctr_um));
- fprintf(file, "offsetof_header_ctr_count %u\n", field_offset(header, ctr_count));
- fprintf(file, "offsetof_header_is_kernel %u\n", field_offset(header, is_kernel));
- fprintf(file, "offsetof_header_cpu_speed %u\n", field_offset(header, cpu_speed));
- fprintf(file, "offsetof_header_mtime %u\n", field_offset(header, mtime));
- fprintf(file, "offsetof_header_cg_to_is_kernel %u\n", field_offset(header, cg_to_is_kernel));
- fprintf(file, "offsetof_header_anon_start %u\n", field_offset(header, anon_start));
- fprintf(file, "offsetof_header_cg_to_anon_start %u\n", field_offset(header, cg_to_anon_start));
-
- // determine endianness
-
- unsigned int probe = 0xff;
- size_t sz = sizeof(unsigned int);
- unsigned char * probe_byte = reinterpret_cast<unsigned char *>(&probe);
-
- assert(probe_byte[0] == 0xff || probe_byte[sz - 1] == 0xff);
-
- if (probe_byte[0] == 0xff)
- fprintf(file, "little_endian 1\n");
- else
- fprintf(file, "little_endian 0\n");
-
- fprintf(file, "\n");
-
- fclose(file);
- return 1;
-}
diff --git a/libdb/db_debug.c b/libdb/db_debug.c
index 8e88dcc..0575570 100644
--- a/libdb/db_debug.c
+++ b/libdb/db_debug.c
@@ -26,9 +26,9 @@ static int check_circular_list(odb_data_t const * data)
odb_index_t index = data->hash_base[pos];
if (index && !do_abort) {
while (index) {
- if (bitmap[index]) {
+ if (bitmap[index])
do_abort = 1;
- }
+
bitmap[index] = 1;
index = data->node_base[index].next;
}
@@ -43,9 +43,9 @@ static int check_circular_list(odb_data_t const * data)
index = data->hash_base[pos];
while (index) {
printf("%d ", index);
- if (bitmap[index]) {
+ if (bitmap[index])
exit(1);
- }
+
bitmap[index] = 1;
index = data->node_base[index].next;
}
@@ -122,13 +122,11 @@ int odb_check_hash(odb_t const * odb)
ret = 1;
}
- if (ret == 0) {
+ if (ret == 0)
ret = check_circular_list(data);
- }
- if (ret == 0) {
+ if (ret == 0)
ret = check_redundant_key(data, max);
- }
return ret;
}
diff --git a/libdb/db_insert.c b/libdb/db_insert.c
index 2c99640..018c294 100644
--- a/libdb/db_insert.c
+++ b/libdb/db_insert.c
@@ -17,26 +17,74 @@
#include "odb.h"
-int odb_insert(odb_t * odb, odb_key_t key, odb_value_t value)
+
+static inline int add_node(odb_data_t * data, odb_key_t key, odb_value_t value)
{
- odb_index_t index;
odb_index_t new_node;
odb_node_t * node;
+ odb_index_t index;
+
+ /* no locking is necessary: iteration interface retrieve data through
+ * the node_base array, we doesn't increase current_size now but it's
+ * done by odb_commit_reservation() so the new slot is visible only
+ * after the increment
+ */
+ if (data->descr->current_size >= data->descr->size) {
+ if (odb_grow_hashtable(data))
+ return EINVAL;
+ }
+ new_node = data->descr->current_size;
+
+ node = &data->node_base[new_node];
+ node->value = value;
+ node->key = key;
+
+ index = odb_do_hash(data, key);
+ node->next = data->hash_base[index];
+ data->hash_base[index] = new_node;
+
+ /* FIXME: we need wrmb() here */
+ odb_commit_reservation(data);
+
+ return 0;
+}
+
+int odb_update_node(odb_t * odb, odb_key_t key)
+{
+ odb_index_t index;
+ odb_node_t * node;
odb_data_t * data;
data = odb->data;
index = data->hash_base[odb_do_hash(data, key)];
while (index) {
- if (index <= 0 || index >= data->descr->current_size) {
- return EINVAL;
- }
node = &data->node_base[index];
if (node->key == key) {
- if (node->value + value >= node->value) {
- node->value += value;
+ if (node->value + 1 != 0) {
+ node->value += 1;
} else {
/* post profile tools must handle overflow */
- node->value = ~(odb_value_t)0;
+ /* FIXME: the tricky way will be just to add
+ * a goto to jump right before the return
+ * add_node(), in this way we no longer can
+ * overflow. It'll work because new node are
+ * linked at the start of the node list for
+ * this bucket so this loop will see first a
+ * non overflowed node if one exist. When we
+ * grow the hashtable the most recently
+ * allocated node for this key will be setup
+ * last, so again it'll be linked at start of
+ * the list. pp tools looke like ok with this
+ * change.
+ *
+ * This change doesn't involve any file format
+ * change but perhaps it's a bit hacky to do
+ * this w/o bumping the sample file format
+ * version. The drawback of this is the added
+ * node are additive not multiplicative.
+ * (multiplicative as if we add more bits to
+ * store a value)
+ */
}
return 0;
}
@@ -44,23 +92,11 @@ int odb_insert(odb_t * odb, odb_key_t key, odb_value_t value)
index = node->next;
}
- /* no locking is necessary: iteration interface retrieve data through
- * the node_base array, odb_hash_add_node() increase current_size but
- * odb_travel just ignore node with a zero key so on setting the key
- * atomically update the node */
- new_node = odb_hash_add_node(odb);
- if (new_node == ODB_NODE_NR_INVALID) {
- return EINVAL;
- }
+ return add_node(data, key, 1);
+}
- node = &data->node_base[new_node];
- node->value = value;
- node->key = key;
- /* we need to recalculate hash code, hash table has perhaps grown */
- index = odb_do_hash(data, key);
- node->next = data->hash_base[index];
- data->hash_base[index] = new_node;
-
- return 0;
+int odb_add_node(odb_t * odb, odb_key_t key, odb_value_t value)
+{
+ return add_node(odb->data, key, value);
}
diff --git a/libdb/db_manage.c b/libdb/db_manage.c
index d9cc1ce..d8a6fcb 100644
--- a/libdb/db_manage.c
+++ b/libdb/db_manage.c
@@ -11,11 +11,14 @@
#define _GNU_SOURCE
#include <stdlib.h>
+#ifndef ANDROID
+#include <sys/fcntl.h>
+#else
#include <fcntl.h>
+#endif
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
@@ -61,65 +64,60 @@ static unsigned int tables_size(odb_data_t const * data, odb_node_nr_t node_nr)
}
-odb_index_t odb_hash_add_node(odb_t * odb)
+int odb_grow_hashtable(odb_data_t * data)
{
- odb_data_t * data = odb->data;
+ unsigned int old_file_size;
+ unsigned int new_file_size;
+ unsigned int pos;
+ void * new_map;
+
+ old_file_size = tables_size(data, data->descr->size);
+ new_file_size = tables_size(data, data->descr->size * 2);
+
+ if (ftruncate(data->fd, new_file_size))
+ return 1;
+
+ new_map = mremap(data->base_memory,
+ old_file_size, new_file_size, MREMAP_MAYMOVE);
+
+ if (new_map == MAP_FAILED)
+ return 1;
- if (data->descr->current_size >= data->descr->size) {
- unsigned int old_file_size;
- unsigned int new_file_size;
- unsigned int pos;
- void * new_map;
-
- old_file_size = tables_size(data, data->descr->size);
- new_file_size = tables_size(data, data->descr->size * 2);
-
- if (ftruncate(data->fd, new_file_size))
- return ODB_NODE_NR_INVALID;
-
- new_map = mremap(data->base_memory,
- old_file_size, new_file_size, MREMAP_MAYMOVE);
-
- if (new_map == MAP_FAILED)
- return ODB_NODE_NR_INVALID;
-
- data->base_memory = new_map;
- data->descr = odb_to_descr(data);
- data->descr->size *= 2;
- data->node_base = odb_to_node_base(data);
- data->hash_base = odb_to_hash_base(data);
- data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
-
- /* rebuild the hash table, node zero is never used. This works
- * because layout of file is node table then hash table,
- * sizeof(node) > sizeof(bucket) and when we grow table we
- * double size ==> old hash table and new hash table can't
- * overlap so on the new hash table is entirely in the new
- * memory area (the grown part) and we know the new hash
- * hash table is zeroed. That's why we don't need to zero init
- * the new table */
- /* OK: the above is not exact
- * if BUCKET_FACTOR < sizeof(bd_node_t) / sizeof(bd_node_nr_t)
- * all things are fine and we don't need to init the hash
- * table because in this case the new hash table is completely
- * inside the new growed part. Avoiding to touch this memory is
- * useful.
- */
+ data->base_memory = new_map;
+ data->descr = odb_to_descr(data);
+ data->descr->size *= 2;
+ data->node_base = odb_to_node_base(data);
+ data->hash_base = odb_to_hash_base(data);
+ data->hash_mask = (data->descr->size * BUCKET_FACTOR) - 1;
+
+ /* rebuild the hash table, node zero is never used. This works
+ * because layout of file is node table then hash table,
+ * sizeof(node) > sizeof(bucket) and when we grow table we
+ * double size ==> old hash table and new hash table can't
+ * overlap so on the new hash table is entirely in the new
+ * memory area (the grown part) and we know the new hash
+ * hash table is zeroed. That's why we don't need to zero init
+ * the new table */
+ /* OK: the above is not exact
+ * if BUCKET_FACTOR < sizeof(bd_node_t) / sizeof(bd_node_nr_t)
+ * all things are fine and we don't need to init the hash
+ * table because in this case the new hash table is completely
+ * inside the new growed part. Avoiding to touch this memory is
+ * useful.
+ */
#if 0
- for (pos = 0 ; pos < data->descr->size*BUCKET_FACTOR ; ++pos) {
- data->hash_base[pos] = 0;
- }
+ for (pos = 0 ; pos < data->descr->size*BUCKET_FACTOR ; ++pos)
+ data->hash_base[pos] = 0;
#endif
- for (pos = 1; pos < data->descr->current_size; ++pos) {
- odb_node_t * node = &data->node_base[pos];
- size_t index = odb_do_hash(data, node->key);
- node->next = data->hash_base[index];
- data->hash_base[index] = pos;
- }
+ for (pos = 1; pos < data->descr->current_size; ++pos) {
+ odb_node_t * node = &data->node_base[pos];
+ size_t index = odb_do_hash(data, node->key);
+ node->next = data->hash_base[index];
+ data->hash_base[index] = pos;
}
- return (odb_index_t)data->descr->current_size++;
+ return 0;
}
diff --git a/libdb/db_stat.c b/libdb/db_stat.c
index ab3ed18..6d29e9a 100644
--- a/libdb/db_stat.c
+++ b/libdb/db_stat.c
@@ -12,12 +12,13 @@
#include <stdio.h>
#include "odb.h"
+#include "op_types.h"
/// hold various statistics data for a db file
struct odb_hash_stat_t {
odb_node_nr_t node_nr; /**< allocated node number */
odb_node_nr_t used_node_nr; /**< in use node number */
- odb_value_t total_count; /**< cumulated samples count */
+ count_type total_count; /**< cumulated samples count */
odb_index_t hash_table_size; /**< hash table entry number */
odb_node_nr_t max_list_length; /**< worst case */
double average_list_length; /**< average case */
@@ -74,7 +75,7 @@ void odb_hash_display_stat(odb_hash_stat_t const * stat)
{
printf("total node number: %d\n", stat->node_nr);
printf("total used node: %d\n", stat->used_node_nr);
- printf("total count: %d\n", stat->total_count);
+ printf("total count: %llu\n", stat->total_count);
printf("hash table size: %d\n", stat->hash_table_size);
printf("greater list length: %d\n", stat->max_list_length);
printf("average non empty list length: %2.4f\n", stat->average_list_length);
diff --git a/libdb/odb.h b/libdb/odb.h
index c88baea..c190b57 100644
--- a/libdb/odb.h
+++ b/libdb/odb.h
@@ -135,14 +135,28 @@ void * odb_get_data(odb_t * odb);
/** issue a msync on the used size of the mmaped file */
void odb_sync(odb_t const * odb);
-/** add a page returning its index. Take care all page pointer can be
- * invalidated by this call !
- * returns the index of the created node on success or
- * ODB_NODE_NR_INVALID on failure, in this case this function do nothing
- * and errno is set by the first libc call failure allowing to retry after
- * cleanup some program resource.
+/**
+ * grow the hashtable in such way current_size is the index of the first free
+ * node. Take care all node pointer can be invalidated by this call.
+ *
+ * Node allocation is done in a two step way 1st) ensure a free node exist
+ * eventually, caller can setup it, 2nd) commit the node allocation with
+ * odb_commit_reservation().
+ * This is done in this way to ensure node setup is visible from another
+ * process like pp tools in an atomic way.
+ *
+ * returns 0 on success, non zero on failure in this case this function do
+ * nothing and errno is set by the first libc call failure allowing to retry
+ * after cleanup some program resource.
*/
-odb_node_nr_t odb_hash_add_node(odb_t * odb);
+int odb_grow_hashtable(odb_data_t * data);
+/**
+ * commit a previously successfull node reservation. This can't fail.
+ */
+static __inline void odb_commit_reservation(odb_data_t * data)
+{
+ ++data->descr->current_size;
+}
/** "immpossible" node number to indicate an error from odb_hash_add_node() */
#define ODB_NODE_NR_INVALID ((odb_node_nr_t)-1)
@@ -158,11 +172,19 @@ void odb_hash_display_stat(odb_hash_stat_t const * stats);
void odb_hash_free_stat(odb_hash_stat_t * stats);
/* db_insert.c */
-/** insert info at key, if key already exist the info is added to the
- * existing samples
+/** update info at key by incrementing its associated value by one,
+ * if the key does not exist a new node is created and the value associated
+ * is set to one.
+ *
+ * returns EXIT_SUCCESS on success, EXIT_FAILURE on failure
+ */
+int odb_update_node(odb_t * odb, odb_key_t key);
+
+/** Add a new node w/o regarding if a node with the same key already exists
+ *
* returns EXIT_SUCCESS on success, EXIT_FAILURE on failure
*/
-int odb_insert(odb_t * odb, odb_key_t key, odb_value_t value);
+int odb_add_node(odb_t * odb, odb_key_t key, odb_value_t value);
/* db_travel.c */
/**
@@ -171,13 +193,11 @@ int odb_insert(odb_t * odb, odb_key_t key, odb_value_t value);
*
* odb_node_nr_t node_nr, pos;
* odb_node_t * node = odb_get_iterator(odb, &node_nr);
- * for ( pos = 0 ; pos < node_nr ; ++pos) {
- * if (node[pos].key) {
- * // do something
- * }
- * }
+ * for ( pos = 0 ; pos < node_nr ; ++pos)
+ * // do something
*
- * note than caller *must* filter nil key.
+ * note than caller does not need to filter nil key as it's a valid key,
+ * The returned range is all valid (i.e. should never contain zero value).
*/
odb_node_t * odb_get_iterator(odb_t const * odb, odb_node_nr_t * nr);
diff --git a/libop/Android.mk b/libop/Android.mk
index 99fd081..8fbd1e6 100644
--- a/libop/Android.mk
+++ b/libop/Android.mk
@@ -3,6 +3,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
op_alloc_counter.c \
+ op_config.c \
op_cpu_type.c \
op_events.c \
op_get_interface.c \
diff --git a/libop/op_alloc_counter.c b/libop/op_alloc_counter.c
index 8d20134..353100a 100644
--- a/libop/op_alloc_counter.c
+++ b/libop/op_alloc_counter.c
@@ -12,6 +12,8 @@
*/
#include <stdlib.h>
+#include <ctype.h>
+#include <dirent.h>
#include "op_events.h"
#include "op_libiberty.h"
@@ -130,7 +132,7 @@ allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth,
counter_arc const * arc = list_entry(pos, counter_arc, next);
if (allocated_mask & (1 << arc->counter))
- return 0;
+ continue;
counter_map[depth] = arc->counter;
@@ -143,6 +145,41 @@ allocate_counter(counter_arc_head const * ctr_arc, int max_depth, int depth,
return 0;
}
+/* determine which directories are counter directories
+ */
+static int perfcounterdir(const struct dirent * entry)
+{
+ return (isdigit(entry->d_name[0]));
+}
+
+
+/**
+ * @param mask pointer where to place bit mask of unavailable counters
+ *
+ * return >= 0 number of counters that are available
+ * < 0 could not determine number of counters
+ *
+ */
+static int op_get_counter_mask(u32 * mask)
+{
+ struct dirent **counterlist;
+ int count, i;
+ /* assume nothing is available */
+ u32 available=0;
+
+ count = scandir("/dev/oprofile", &counterlist, perfcounterdir, alphasort);
+ if (count < 0)
+ /* unable to determine bit mask */
+ return -1;
+ /* convert to bit map (0 where counter exists) */
+ for (i=0; i<count; ++i) {
+ available |= 1 << atoi(counterlist[i]->d_name);
+ free(counterlist[i]);
+ }
+ *mask=~available;
+ free(counterlist);
+ return count;
+}
size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
op_cpu cpu_type)
@@ -150,8 +187,14 @@ size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
counter_arc_head * ctr_arc;
size_t * counter_map;
int nr_counters;
+ u32 unavailable_counters = 0;
- nr_counters = op_get_nr_counters(cpu_type);
+ nr_counters = op_get_counter_mask(&unavailable_counters);
+ /* no counters then probably perfmon managing perfmon hw */
+ if (nr_counters <= 0) {
+ nr_counters = op_get_nr_counters(cpu_type);
+ unavailable_counters = (~0) << nr_counters;
+ }
if (nr_counters < nr_events)
return 0;
@@ -159,7 +202,8 @@ size_t * map_event_to_counter(struct op_event const * pev[], int nr_events,
counter_map = xmalloc(nr_counters * sizeof(size_t));
- if (!allocate_counter(ctr_arc, nr_events, 0, 0, counter_map)) {
+ if (!allocate_counter(ctr_arc, nr_events, 0, unavailable_counters,
+ counter_map)) {
free(counter_map);
counter_map = 0;
}
diff --git a/libop/op_config.c b/libop/op_config.c
new file mode 100644
index 0000000..837242b
--- /dev/null
+++ b/libop/op_config.c
@@ -0,0 +1,77 @@
+/**
+ * @file op_config.c
+ * Oprofile configuration parameters.
+ *
+ * @remark Copyright 2002 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Nathan Tallent
+ * @Modifications Daniel Hansel
+ */
+
+#include "op_config.h"
+#include "op_config_24.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* paths in op_config.h */
+char op_session_dir[PATH_MAX];
+char op_samples_dir[PATH_MAX];
+char op_samples_current_dir[PATH_MAX];
+char op_lock_file[PATH_MAX];
+char op_log_file[PATH_MAX];
+char op_pipe_file[PATH_MAX];
+char op_dump_status[PATH_MAX];
+
+/* paths in op_config_24.h */
+char op_device[PATH_MAX];
+char op_note_device[PATH_MAX];
+char op_hash_device[PATH_MAX];
+
+void
+init_op_config_dirs(char const * session_dir)
+{
+ int session_dir_len;
+
+ assert(session_dir);
+ session_dir_len = strlen(session_dir);
+
+ if (session_dir_len + strlen("/samples/oprofiled.log") > PATH_MAX) {
+ fprintf(stderr, "Session_dir string \"%s\" is too large.\n",
+ session_dir);
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(op_session_dir, session_dir);
+
+ strcpy(op_samples_dir, op_session_dir);
+ strcat(op_samples_dir, "/samples/");
+
+ strcpy(op_samples_current_dir, op_samples_dir);
+ strcat(op_samples_current_dir, "/current/");
+
+ strcpy(op_lock_file, op_session_dir);
+ strcat(op_lock_file, "/lock");
+
+ strcpy(op_pipe_file, op_session_dir);
+ strcat(op_pipe_file, "/opd_pipe");
+
+ strcpy(op_log_file, op_samples_dir);
+ strcat(op_log_file, "oprofiled.log");
+
+ strcpy(op_dump_status, op_session_dir);
+ strcat(op_dump_status, "/complete_dump");
+
+ strcpy(op_device, op_session_dir);
+ strcat(op_device, "/opdev");
+
+ strcpy(op_note_device, op_session_dir);
+ strcat(op_note_device, "/opnotedev");
+
+ strcpy(op_hash_device, op_session_dir);
+ strcat(op_hash_device, "/ophashmapdev");
+}
diff --git a/libop/op_config.h b/libop/op_config.h
index 5e16ede..b384497 100644
--- a/libop/op_config.h
+++ b/libop/op_config.h
@@ -9,30 +9,50 @@
*
* @author John Levon
* @author Philippe Elie
+ * @Modifications Daniel Hansel
*/
#ifndef OP_CONFIG_H
#define OP_CONFIG_H
-/* various paths, duplicated in opcontrol */
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/**
+ * must be called to initialize the paths below.
+ * @param session_dir the non-NULL value of the base session directory
+ */
+void init_op_config_dirs(char const * session_dir);
-#define OP_DRIVER_BASE "/dev.oprofile"
-#define OP_BASE_DIR "/tmp/oprofile/"
-//#define OP_BASE_DIR "/var/lib/oprofile/"
+/*
+ * various paths, corresponding to opcontrol, that should be
+ * initialized by init_op_config_dirs() above.
+ */
+extern char op_session_dir[];
+extern char op_samples_dir[];
+extern char op_samples_current_dir[];
+extern char op_lock_file[];
+extern char op_log_file[];
+extern char op_pipe_file[];
+extern char op_dump_status[];
-#define OP_SAMPLES_DIR OP_BASE_DIR "samples/"
-#define OP_SAMPLES_CURRENT_DIR OP_SAMPLES_DIR "current/"
-#define OP_LOCK_FILE OP_BASE_DIR "lock"
-#define OP_LOG_FILE OP_BASE_DIR "oprofiled.log"
-#define OP_DUMP_STATUS OP_BASE_DIR "complete_dump"
+#define OP_DRIVER_BASE "/dev/oprofile"
+#define OP_DATA_DIR "/data/oprofile"
/* Global directory that stores debug files */
#ifndef DEBUGDIR
-//#define DEBUGDIR "/usr/lib/debug"
-#define DEBUGDIR "/tmp/debug"
+#define DEBUGDIR "/usr/lib/debug"
#endif
#define OPD_MAGIC "DAE\n"
-#define OPD_VERSION 0x10
+#define OPD_VERSION 0x11
+
+#define OP_MIN_CPU_BUF_SIZE 2048
+#define OP_MAX_CPU_BUF_SIZE 131072
+
+#if defined(__cplusplus)
+}
+#endif
#endif /* OP_CONFIG_H */
diff --git a/libop/op_config_24.h b/libop/op_config_24.h
index 8767244..1786fae 100644
--- a/libop/op_config_24.h
+++ b/libop/op_config_24.h
@@ -14,22 +14,24 @@
#define OP_CONFIG_24_H
#define OP_MOUNT "/proc/sys/dev/oprofile/"
-
-#define OP_DEVICE OP_BASE_DIR "opdev"
-#define OP_NOTE_DEVICE OP_BASE_DIR "opnotedev"
-#define OP_HASH_DEVICE OP_BASE_DIR "ophashmapdev"
+
+extern char op_device[];
+extern char op_note_device[];
+extern char op_hash_device[];
/*@{\name module default/min/max settings */
/** 65536 * sizeof(op_sample) */
#define OP_DEFAULT_BUF_SIZE 65536
-/** we don't try to wake-up daemon until it remains more than this free entry
- * in eviction buffer */
+/**
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in eviction buffer
+ */
#define OP_PRE_WATERMARK(buffer_size) \
(((buffer_size) / 8) < OP_MIN_PRE_WATERMARK \
? OP_MIN_PRE_WATERMARK \
: (buffer_size) / 8)
-/* minimal buffer water mark before we try to wakeup daemon */
+/** minimal buffer water mark before we try to wakeup daemon */
#define OP_MIN_PRE_WATERMARK 8192
/** maximum number of entry in samples eviction buffer */
#define OP_MAX_BUF_SIZE 1048576
@@ -38,13 +40,15 @@
/** 16384 * sizeof(op_note) = 273680 bytes default */
#define OP_DEFAULT_NOTE_SIZE 16384
-/** we don't try to wake-up daemon until it remains more than this free entry
- * in note buffer */
+/**
+ * we don't try to wake-up daemon until it remains more than this free entry
+ * in note buffer
+ */
#define OP_PRE_NOTE_WATERMARK(note_size) \
(((note_size) / 32) < OP_MIN_NOTE_PRE_WATERMARK \
? OP_MIN_NOTE_PRE_WATERMARK \
: (note_size) / 32)
-/* minimal note buffer water mark before we try to wakeup daemon */
+/** minimal note buffer water mark before we try to wakeup daemon */
#define OP_MIN_NOTE_PRE_WATERMARK 512
/** maximum number of entry in note buffer */
#define OP_MAX_NOTE_TABLE_SIZE 1048576
@@ -58,8 +62,10 @@
/*@}*/
-/** nr entries in hash map. This is the maximum number of name components
- * allowed. Must be a prime number */
+/**
+ * nr entries in hash map. This is the maximum number of name components
+ * allowed. Must be a prime number
+ */
#define OP_HASH_MAP_NR 4093
/** size of string pool in bytes */
diff --git a/libop/op_cpu_type.c b/libop/op_cpu_type.c
index d7ff4a7..b9d13de 100644
--- a/libop/op_cpu_type.c
+++ b/libop/op_cpu_type.c
@@ -9,14 +9,11 @@
* @author Philippe Elie
*/
-#include "config.h"
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "op_cpu_type.h"
-#include "op_config.h"
struct cpu_descr {
char const * pretty;
@@ -48,8 +45,13 @@ static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = {
{ "ARM/XScale PMU2", "arm/xscale2", CPU_ARM_XSCALE2, 5 },
{ "ppc64 POWER4", "ppc64/power4", CPU_PPC64_POWER4, 8 },
{ "ppc64 POWER5", "ppc64/power5", CPU_PPC64_POWER5, 6 },
+ { "ppc64 POWER5+", "ppc64/power5+", CPU_PPC64_POWER5p, 6 },
{ "ppc64 970", "ppc64/970", CPU_PPC64_970, 8 },
+ { "MIPS 20K", "mips/20K", CPU_MIPS_20K, 1},
{ "MIPS 24K", "mips/24K", CPU_MIPS_24K, 2},
+ { "MIPS 25K", "mips/25K", CPU_MIPS_25K, 2},
+ { "MIPS 34K", "mips/34K", CPU_MIPS_34K, 4},
+ { "MIPS 5K", "mips/5K", CPU_MIPS_5K, 2},
{ "MIPS R10000", "mips/r10000", CPU_MIPS_R10000, 2 },
{ "MIPS R12000", "mips/r12000", CPU_MIPS_R12000, 4 },
{ "QED RM7000", "mips/rm7000", CPU_MIPS_RM7000, 1 },
@@ -58,6 +60,20 @@ static struct cpu_descr const cpu_descrs[MAX_CPU_TYPE] = {
{ "NEC VR5432", "mips/vr5432", CPU_MIPS_VR5432, 2 },
{ "NEC VR5500", "mips/vr5500", CPU_MIPS_VR5500, 2 },
{ "e500", "ppc/e500", CPU_PPC_E500, 4 },
+ { "e500v2", "ppc/e500v2", CPU_PPC_E500_2, 4 },
+ { "Core Solo / Duo", "i386/core", CPU_CORE, 2 },
+ { "PowerPC G4", "ppc/7450", CPU_PPC_7450, 6 },
+ { "Core 2", "i386/core_2", CPU_CORE_2, 2 },
+ { "ppc64 POWER6", "ppc64/power6", CPU_PPC64_POWER6, 4 },
+ { "ppc64 970MP", "ppc64/970MP", CPU_PPC64_970MP, 8 },
+ { "ppc64 Cell Broadband Engine", "ppc64/cell-be", CPU_PPC64_CELL, 8 },
+ { "AMD64 family10", "x86-64/family10", CPU_FAMILY10, 4 },
+ { "ppc64 PA6T", "ppc64/pa6t", CPU_PPC64_PA6T, 6 },
+ { "ARM MPCore", "arm/mpcore", CPU_ARM_MPCORE, 2 },
+ { "ARM V6 PMU", "arm/armv6", CPU_ARM_V6, 3 },
+ { "ppc64 POWER5++", "ppc64/power5++", CPU_PPC64_POWER5pp, 6 },
+ { "e300", "ppc/e300", CPU_PPC_E300, 4 },
+ { "AVR32", "avr32", CPU_AVR32, 3 },
};
static size_t const nr_cpu_descrs = sizeof(cpu_descrs) / sizeof(struct cpu_descr);
@@ -71,7 +87,7 @@ op_cpu op_get_cpu_type(void)
fp = fopen("/proc/sys/dev/oprofile/cpu_type", "r");
if (!fp) {
/* Try 2.6's oprofilefs one instead. */
- fp = fopen(OP_DRIVER_BASE"/cpu_type", "r");
+ fp = fopen("/dev/oprofile/cpu_type", "r");
if (!fp) {
fprintf(stderr, "Unable to open cpu_type file for reading\n");
fprintf(stderr, "Make sure you have done opcontrol --init\n");
diff --git a/libop/op_cpu_type.h b/libop/op_cpu_type.h
index 9def1d7..be95ae2 100644
--- a/libop/op_cpu_type.h
+++ b/libop/op_cpu_type.h
@@ -16,7 +16,9 @@
extern "C" {
#endif
-/** supported cpu type */
+/**
+ * Supported cpu type. Always add new CPU types at the very end.
+ */
typedef enum {
CPU_NO_GOOD = -1, /**< unsupported CPU type */
CPU_PPRO, /**< Pentium Pro */
@@ -41,8 +43,13 @@ typedef enum {
CPU_ARM_XSCALE2, /**< ARM XScale 2 */
CPU_PPC64_POWER4, /**< ppc64 POWER4 family */
CPU_PPC64_POWER5, /**< ppc64 POWER5 family */
+ CPU_PPC64_POWER5p, /**< ppc64 Power5+ family */
CPU_PPC64_970, /**< ppc64 970 family */
+ CPU_MIPS_20K, /**< MIPS 20K */
CPU_MIPS_24K, /**< MIPS 24K */
+ CPU_MIPS_25K, /**< MIPS 25K */
+ CPU_MIPS_34K, /**< MIPS 34K */
+ CPU_MIPS_5K, /**< MIPS 5K */
CPU_MIPS_R10000, /**< MIPS R10000 */
CPU_MIPS_R12000, /**< MIPS R12000 */
CPU_MIPS_RM7000, /**< QED RM7000 */
@@ -51,6 +58,20 @@ typedef enum {
CPU_MIPS_VR5432, /**< NEC VR5432 */
CPU_MIPS_VR5500, /**< MIPS VR5500, VR5532 and VR7701 */
CPU_PPC_E500, /**< e500 */
+ CPU_PPC_E500_2, /**< e500v2 */
+ CPU_CORE, /**< Core Solo / Duo series */
+ CPU_PPC_7450, /**< PowerPC G4 */
+ CPU_CORE_2, /**< Intel Core 2 */
+ CPU_PPC64_POWER6, /**< ppc64 POWER6 family */
+ CPU_PPC64_970MP, /**< ppc64 970MP */
+ CPU_PPC64_CELL, /**< ppc64 Cell Broadband Engine*/
+ CPU_FAMILY10, /**< AMD family 10 */
+ CPU_PPC64_PA6T, /**< ppc64 PA6T */
+ CPU_ARM_MPCORE, /**< ARM MPCore */
+ CPU_ARM_V6, /**< ARM V6 */
+ CPU_PPC64_POWER5pp, /**< ppc64 Power5++ family */
+ CPU_PPC_E300, /**< e300 */
+ CPU_AVR32, /**< AVR32 */
MAX_CPU_TYPE
} op_cpu;
diff --git a/libop/op_events.c b/libop/op_events.c
index 47bae76..b4a10e7 100644
--- a/libop/op_events.c
+++ b/libop/op_events.c
@@ -39,9 +39,8 @@ static void parse_error(char const * context)
static int parse_int(char const * str)
{
int value;
- if (sscanf(str, "%d", &value) != 1) {
+ if (sscanf(str, "%d", &value) != 1)
parse_error("expected decimal value");
- }
return value;
}
@@ -50,9 +49,11 @@ static int parse_int(char const * str)
static int parse_hex(char const * str)
{
int value;
- if (sscanf(str, "%x", &value) != 1) {
+ /* 0x/0X to force the use of hexa notation for field intended to
+ be in hexadecimal */
+ if (sscanf(str, "0x%x", &value) != 1 &&
+ sscanf(str, "0X%x", &value) != 1)
parse_error("expected hexadecimal value");
- }
return value;
}
@@ -61,9 +62,9 @@ static int parse_hex(char const * str)
static u64 parse_long_hex(char const * str)
{
u64 value;
- if (sscanf(str, "%Lx", &value) != 1) {
+ if (sscanf(str, "%Lx", &value) != 1)
parse_error("expected long hexadecimal value");
- }
+
fflush(stderr);
return value;
}
@@ -187,12 +188,11 @@ static void read_unit_masks(char const * file)
um = new_unit_mask();
parse_um(um, line);
} else {
- if (!um) {
+ if (!um)
parse_error("no unit mask name line");
- }
- if (um->num >= MAX_UNIT_MASK) {
+ if (um->num >= MAX_UNIT_MASK)
parse_error("oprofile: maximum unit mask entries exceeded");
- }
+
parse_um_entry(&um->um[um->num], line);
++(um->num);
}
@@ -259,9 +259,8 @@ static int next_token(char const ** cp, char ** name, char ** value)
colon = strchr(colon, ':');
if (!colon) {
- if (*c) {
+ if (*c)
parse_error("next_token(): garbage at end of line");
- }
return 0;
}
@@ -337,6 +336,10 @@ static void read_events(char const * file)
if (seen_name)
parse_error("duplicate name: tag");
seen_name = 1;
+ if (strchr(value, '/') != NULL)
+ parse_error("invalid event name");
+ if (strchr(value, '.') != NULL)
+ parse_error("invalid event name");
event->name = value;
} else if (strcmp(name, "event") == 0) {
if (seen_event)
@@ -385,7 +388,7 @@ next:
static void check_unit_mask(struct op_unit_mask const * um,
char const * cpu_name)
{
- u16 i;
+ u32 i;
if (!um->used) {
fprintf(stderr, "um %s is not used\n", um->name);
@@ -397,10 +400,10 @@ static void check_unit_mask(struct op_unit_mask const * um,
"entry (%s)\n", um->name, cpu_name);
exit(EXIT_FAILURE);
} else if (um->unit_type_mask == utm_bitmask) {
- u16 default_mask = um->default_mask;
- for (i = 0; i < um->num; ++i) {
+ u32 default_mask = um->default_mask;
+ for (i = 0; i < um->num; ++i)
default_mask &= ~um->um[i].value;
- }
+
if (default_mask) {
fprintf(stderr, "um %s default mask is not valid "
"(%s)\n", um->name, cpu_name);
@@ -519,7 +522,7 @@ void op_free_events(void)
}
-static struct op_event * find_event(u8 nr)
+static struct op_event * find_event(u32 nr)
{
struct list_head * pos;
@@ -557,7 +560,7 @@ static FILE * open_event_mapping_file(char const * cpu_name)
/**
* This function is PPC64-specific.
*/
-static char const * get_mapping(u8 nr, FILE * fp)
+static char const * get_mapping(u32 nr, FILE * fp)
{
char * line;
char * name;
@@ -587,14 +590,13 @@ static char const * get_mapping(u8 nr, FILE * fp)
c = line;
while (next_token(&c, &name, &value)) {
if (strcmp(name, "event") == 0) {
- u8 evt;
+ u32 evt;
if (seen_event)
parse_error("duplicate event tag");
seen_event = 1;
evt = parse_hex(value);
- if (evt == nr) {
+ if (evt == nr)
event_found = 1;
- }
free(value);
} else if (strcmp(name, "mmcr0") == 0) {
if (seen_mmcr0)
@@ -639,15 +641,20 @@ next:
}
-char const * find_mapping_for_event(u8 nr, op_cpu cpu_type)
+char const * find_mapping_for_event(u32 nr, op_cpu cpu_type)
{
char const * cpu_name = op_get_cpu_name(cpu_type);
FILE * fp = open_event_mapping_file(cpu_name);
char const * map = NULL;
switch (cpu_type) {
+ case CPU_PPC64_PA6T:
case CPU_PPC64_970:
+ case CPU_PPC64_970MP:
case CPU_PPC64_POWER4:
case CPU_PPC64_POWER5:
+ case CPU_PPC64_POWER5p:
+ case CPU_PPC64_POWER5pp:
+ case CPU_PPC64_POWER6:
if (!fp) {
fprintf(stderr, "oprofile: could not open event mapping file %s\n", filename);
exit(EXIT_FAILURE);
@@ -680,7 +687,7 @@ struct op_event * find_event_by_name(char const * name)
}
-struct op_event * op_find_event(op_cpu cpu_type, u8 nr)
+struct op_event * op_find_event(op_cpu cpu_type, u32 nr)
{
struct op_event * event;
@@ -692,7 +699,7 @@ struct op_event * op_find_event(op_cpu cpu_type, u8 nr)
}
-int op_check_events(int ctr, u8 nr, u16 um, op_cpu cpu_type)
+int op_check_events(int ctr, u32 nr, u32 um, op_cpu cpu_type)
{
int ret = OP_OK_EVENT;
struct op_event * event;
@@ -747,8 +754,11 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
case CPU_PII:
case CPU_PIII:
case CPU_P6_MOBILE:
+ case CPU_CORE:
+ case CPU_CORE_2:
case CPU_ATHLON:
case CPU_HAMMER:
+ case CPU_FAMILY10:
descr->name = "CPU_CLK_UNHALTED";
break;
@@ -781,12 +791,26 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
// we could possibly use the CCNT
case CPU_ARM_XSCALE1:
case CPU_ARM_XSCALE2:
+ case CPU_ARM_MPCORE:
+ case CPU_ARM_V6:
+ case CPU_AVR32:
descr->name = "CPU_CYCLES";
break;
+ case CPU_PPC64_PA6T:
case CPU_PPC64_970:
+ case CPU_PPC64_970MP:
+ case CPU_PPC_7450:
case CPU_PPC64_POWER4:
case CPU_PPC64_POWER5:
+ case CPU_PPC64_POWER6:
+ case CPU_PPC64_POWER5p:
+ case CPU_PPC64_POWER5pp:
+ case CPU_PPC64_CELL:
+ descr->name = "CYCLES";
+ break;
+
+ case CPU_MIPS_20K:
descr->name = "CYCLES";
break;
@@ -794,6 +818,15 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
descr->name = "INSTRUCTIONS";
break;
+ case CPU_MIPS_34K:
+ descr->name = "INSTRUCTIONS";
+ break;
+
+ case CPU_MIPS_5K:
+ case CPU_MIPS_25K:
+ descr->name = "CYCLES";
+ break;
+
case CPU_MIPS_R10000:
case CPU_MIPS_R12000:
descr->name = "INSTRUCTIONS_GRADUATED";
@@ -814,9 +847,11 @@ void op_default_event(op_cpu cpu_type, struct op_default_event_descr * descr)
break;
case CPU_PPC_E500:
+ case CPU_PPC_E500_2:
+ case CPU_PPC_E300:
descr->name = "CPU_CLK";
break;
-
+
// don't use default, if someone add a cpu he wants a compiler
// warning if he forgets to handle it here.
case CPU_TIMER_INT:
diff --git a/libop/op_events.h b/libop/op_events.h
index 1796fb9..f6462fc 100644
--- a/libop/op_events.h
+++ b/libop/op_events.h
@@ -28,8 +28,8 @@ enum unit_mask_type {
utm_bitmask /**< bitmask */
};
-/** up to sixteen allowed unit masks */
-#define MAX_UNIT_MASK 16
+/** up to thirty two allowed unit masks */
+#define MAX_UNIT_MASK 32
/** Describe an unit mask. */
@@ -37,9 +37,9 @@ struct op_unit_mask {
char * name; /**< name of unit mask type */
u32 num; /**< number of possible unit masks */
enum unit_mask_type unit_type_mask;
- u16 default_mask; /**< only the gui use it */
+ u32 default_mask; /**< only the gui use it */
struct op_described_um {
- u16 value;
+ u32 value;
char * desc;
} um[MAX_UNIT_MASK];
struct list_head um_next; /**< next um in list */
@@ -50,7 +50,7 @@ struct op_unit_mask {
/** Describe an event. */
struct op_event {
u32 counter_mask; /**< bitmask of allowed counter */
- u8 val; /**< event number */
+ u32 val; /**< event number */
/** which unit mask if any allowed */
struct op_unit_mask * unit;
char * name; /**< the event name */
@@ -63,7 +63,7 @@ struct op_event {
struct list_head * op_events(op_cpu cpu_type);
/** Find a given event, returns NULL on error */
-struct op_event * op_find_event(op_cpu cpu_type, u8 nr);
+struct op_event * op_find_event(op_cpu cpu_type, u32 nr);
/** Find a given event by name */
struct op_event * find_event_by_name(char const * name);
@@ -72,7 +72,7 @@ struct op_event * find_event_by_name(char const * name);
* Find a mapping for a given event ID for architectures requiring additional information
* from what is held in the events file.
*/
-char const * find_mapping_for_event(u8 val, op_cpu cpu_type);
+char const * find_mapping_for_event(u32 val, op_cpu cpu_type);
/** op_check_events() return code */
@@ -96,7 +96,7 @@ enum op_event_check {
*
* \sa op_cpu, OP_EVENTS_OK
*/
-int op_check_events(int ctr, u8 event, u16 um, op_cpu cpu_type);
+int op_check_events(int ctr, u32 event, u32 um, op_cpu cpu_type);
/**
* free memory used by any call to above function. Need to be called only once
diff --git a/libop/op_get_interface.c b/libop/op_get_interface.c
index b57a79f..bdf72a5 100644
--- a/libop/op_get_interface.c
+++ b/libop/op_get_interface.c
@@ -8,15 +8,12 @@
* @author Will Cohen
*/
-#include "config.h"
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "op_cpu_type.h"
#include "op_file.h"
-#include "op_config.h"
op_interface op_get_interface(void)
{
@@ -27,7 +24,7 @@ op_interface op_get_interface(void)
if (op_file_readable("/proc/sys/dev/oprofile/cpu_type")) {
current_interface = OP_INTERFACE_24;
- } else if (op_file_readable(OP_DRIVER_BASE"/cpu_type")) {
+ } else if (op_file_readable("/dev/oprofile/cpu_type")) {
current_interface = OP_INTERFACE_26;
}
diff --git a/libop/op_mangle.c b/libop/op_mangle.c
index a78d87f..1efe5b1 100644
--- a/libop/op_mangle.c
+++ b/libop/op_mangle.c
@@ -19,12 +19,14 @@
#include "op_sample_file.h"
#include "op_config.h"
-static void append_image(char * dest, int flags, int anon, char const * name)
+static void append_image(char * dest, int flags, int anon, char const * name, char const * anon_name)
{
if ((flags & MANGLE_KERNEL) && !strchr(name, '/')) {
strcat(dest, "{kern}/");
} else if (anon) {
- strcat(dest, "{anon}/");
+ strcat(dest, "{anon:");
+ strcat(dest, anon_name);
+ strcat(dest,"}/");
} else {
strcat(dest, "{root}/");
}
@@ -44,15 +46,19 @@ char * op_mangle_filename(struct mangle_values const * values)
* them), see P:3, FIXME: this is a bit weirds, we prolly need to
* reword pp_interface */
char const * image_name = values->dep_name;
+ char const * anon_name = values->anon_name;
char const * dep_name = values->image_name;
char const * cg_image_name = values->cg_image_name;
- len = strlen(OP_SAMPLES_CURRENT_DIR) + strlen(dep_name) + 1
- + strlen(values->event_name) + 1 + strlen(image_name) + 1;
+ len = strlen(op_samples_current_dir) + strlen(dep_name) + 1
+ + strlen(values->event_name) + 1 + strlen(image_name) + 1;
if (values->flags & MANGLE_CALLGRAPH)
len += strlen(cg_image_name) + 1;
+ if (anon || cg_anon)
+ len += strlen(anon_name);
+
/* provision for tgid, tid, unit_mask, cpu and some {root}, {dep},
* {kern}, {anon} and {cg} marker */
/* FIXME: too ugly */
@@ -60,15 +66,16 @@ char * op_mangle_filename(struct mangle_values const * values)
mangled = xmalloc(len);
- strcpy(mangled, OP_SAMPLES_CURRENT_DIR);
- append_image(mangled, values->flags, 0, image_name);
+ strcpy(mangled, op_samples_current_dir);
+ append_image(mangled, values->flags, 0, image_name, anon_name);
strcat(mangled, "{dep}" "/");
- append_image(mangled, values->flags, anon, dep_name);
+ append_image(mangled, values->flags, anon, dep_name, anon_name);
if (values->flags & MANGLE_CALLGRAPH) {
strcat(mangled, "{cg}" "/");
- append_image(mangled, values->flags, cg_anon, cg_image_name);
+ append_image(mangled, values->flags, cg_anon,
+ cg_image_name, anon_name);
}
strcat(mangled, values->event_name);
diff --git a/libop/op_mangle.h b/libop/op_mangle.h
index 5d02d94..9b600dc 100644
--- a/libop/op_mangle.h
+++ b/libop/op_mangle.h
@@ -37,6 +37,7 @@ struct mangle_values {
int flags;
char const * image_name;
+ char const * anon_name;
char const * dep_name;
char const * cg_image_name;
char const * event_name;
diff --git a/libop/op_sample_file.h b/libop/op_sample_file.h
index c3121c8..4f9f1d0 100644
--- a/libop/op_sample_file.h
+++ b/libop/op_sample_file.h
@@ -14,6 +14,7 @@
#include "op_types.h"
+#include <stdint.h>
#include <time.h>
/* header of the sample files */
@@ -29,6 +30,9 @@ struct opd_header {
double cpu_speed;
time_t mtime;
u32 cg_to_is_kernel;
+ /* spu_profile=1 says sample file contains Cell BE SPU profile data */
+ u32 spu_profile;
+ uint64_t embedded_offset;
u64 anon_start;
u64 cg_to_anon_start;
/* binary compatibility reserve */
diff --git a/libutil/op_file.c b/libutil/op_file.c
index f2658b4..e3e6cb6 100644
--- a/libutil/op_file.c
+++ b/libutil/op_file.c
@@ -11,7 +11,9 @@
#include <sys/stat.h>
#include <unistd.h>
-
+#include <fcntl.h>
+#include <dirent.h>
+#include <fnmatch.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
@@ -71,3 +73,113 @@ int create_path(char const * path)
free(str);
return ret;
}
+
+
+inline static int is_dot_or_dotdot(char const * name)
+{
+ return name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0'));
+}
+
+
+/* If non-null is returned, the caller is responsible for freeing
+ * the memory allocated for the return value. */
+static char * make_pathname_from_dirent(char const * basedir,
+ struct dirent * ent,
+ struct stat * st_buf)
+{
+ int name_len;
+ char * name;
+ name_len = strlen(basedir) + strlen("/") + strlen(ent->d_name) + 1;
+ name = xmalloc(name_len);
+ sprintf(name, "%s/%s", basedir, ent->d_name);
+ if (stat(name, st_buf) != 0) {
+ free(name);
+ name = NULL;
+ }
+ return name;
+}
+
+
+int get_matching_pathnames(void * name_list, get_pathname_callback getpathname,
+ char const * base_dir, char const * filter,
+ enum recursion_type recursion)
+{
+/* The algorithm below depends on recursion type (of which there are 3)
+ * and whether the current dirent matches the filter. There are 6 possible
+ * different behaviors, which is why we define 6 case below in the switch
+ * statement of the algorithm. Actually, when the recursion type is
+ * MATCH_DIR_ONLY_RECURSION, the behavior is the same, whether or not the dir
+ * entry matches the filter. However, the behavior of the recursion types
+ * NO_RECURSION and MATCH_ANY_ENTRY_RECURSION do depend on the dir entry
+ * filter match, so for simplicity, we perform this match for all recursion
+ * types and logically OR the match result with the value of the passed
+ * recursion_type.
+ */
+#define NO_MATCH 0
+#define MATCH 1
+
+ DIR * dir;
+ struct dirent * ent;
+ struct stat stat_buffer;
+ int match;
+ char * name = NULL;
+
+ if (!(dir = opendir(base_dir)))
+ return -1;
+ while ((ent = readdir(dir)) != 0) {
+ if (is_dot_or_dotdot(ent->d_name))
+ continue;
+ if (fnmatch(filter, ent->d_name, 0) == 0)
+ match = 1;
+ else
+ match = 0;
+
+ switch (recursion | match) {
+ case NO_RECURSION + NO_MATCH:
+ case MATCH_ANY_ENTRY_RECURSION + NO_MATCH:
+ // nothing to do but continue the loop
+ break;
+ case NO_RECURSION + MATCH:
+ getpathname(ent->d_name, name_list);
+ break;
+ case MATCH_ANY_ENTRY_RECURSION + MATCH:
+ name = make_pathname_from_dirent(base_dir, ent,
+ &stat_buffer);
+ if (name && S_ISDIR(stat_buffer.st_mode) &&
+ !S_ISLNK(stat_buffer.st_mode)) {
+ get_matching_pathnames(
+ name_list, getpathname,
+ name, filter, recursion);
+ } else {
+ getpathname(name, name_list);
+ }
+ free(name);
+ break;
+ case MATCH_DIR_ONLY_RECURSION + NO_MATCH:
+ case MATCH_DIR_ONLY_RECURSION + MATCH:
+ name = make_pathname_from_dirent(base_dir, ent,
+ &stat_buffer);
+ if (name && S_ISDIR(stat_buffer.st_mode) &&
+ !S_ISLNK(stat_buffer.st_mode)) {
+ /* Check if full directory name contains
+ * match to the filter; if so, add it to
+ * name_list and quit; else, recurse.
+ */
+ if (!fnmatch(filter, name, 0)) {
+ getpathname(name, name_list);
+ } else {
+ get_matching_pathnames(
+ name_list, getpathname,
+ name, filter, recursion);
+ }
+ }
+ free(name);
+ break;
+ }
+ }
+ closedir(dir);
+
+ return 0;
+}
diff --git a/libutil/op_file.h b/libutil/op_file.h
index 2774ffe..d22862c 100644
--- a/libutil/op_file.h
+++ b/libutil/op_file.h
@@ -57,6 +57,51 @@ int create_dir(char const * dir);
*/
int create_path(char const * path);
+/**
+ * Clients of get_matching_pathnames must provide their own implementation
+ * of get_pathname_callback.
+ */
+typedef void (*get_pathname_callback)(char const * pathname, void * name_list);
+
+/* This enum is intended solely for the use of get_matching_pathnames(),
+ * bit 0 is reserved for internal use..*/
+enum recursion_type {
+ NO_RECURSION = 2,
+ MATCH_ANY_ENTRY_RECURSION = 4,
+ MATCH_DIR_ONLY_RECURSION = 8,
+};
+/**
+ * @param name_list where to store result
+ * @param get_pathname_callback client-provided callback function
+ * @param base_dir directory from where lookup starts
+ * @param filter a pathname filter
+ * @param recursion recursion_type -- see above enum and following description:
+ * NO_RECURSION: Find matching files from passed base_dir and call
+ * get_pathname_callback to add entry to name_list to be returned.
+ * MATCH_ANY_ENTRY_RECURSION: Starting at base_dir, for each entry in the
+ * dir that matches the filter: if entry is of type 'dir', recurse;
+ * else call get_pathname_callback to add entry to name_list to be
+ * returned.
+ * MATCH_DIR_ONLY_RECURSION: Starting at base_dir, if an entry in the
+ * dir is of type 'dir' and its complete pathname contains a match to
+ * the filter, call get_pathname_callback to add entry to name_list to
+ * be returned; else recurse.
+ *
+ * Returns 0 on success.
+ *
+ * Return a list of pathnames under base_dir, filtered by filter and optionally
+ * looking in sub-directory. See description above of the recursion_type
+ * parameter for more details.
+ * NOTE: For C clients: Your implementation of the get_pathname_callback
+ * function will probably dynamically allocate storage for elements
+ * added to name_list. If so, remember to free that memory when it's
+ * no longer needed.
+ */
+int get_matching_pathnames(void * name_list, get_pathname_callback,
+ char const * base_dir, char const * filter,
+ enum recursion_type recursion);
+
+
#ifdef __cplusplus
}
#endif
diff --git a/libutil/op_growable_buffer.c b/libutil/op_growable_buffer.c
new file mode 100644
index 0000000..d138f36
--- /dev/null
+++ b/libutil/op_growable_buffer.c
@@ -0,0 +1,46 @@
+/**
+ * @file op_growable_buffer.c
+ * a growable buffer implementation
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#include "op_growable_buffer.h"
+#include "op_libiberty.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+void init_buffer(struct growable_buffer * b)
+{
+ b->max_size = 0;
+ b->size = 0;
+ b->p = NULL;
+}
+
+
+void free_buffer(struct growable_buffer * b)
+{
+ free(b->p);
+}
+
+
+static void grow_buffer(struct growable_buffer * b)
+{
+ size_t new_size = (b->max_size + b->size) * 2;
+ b->p = xrealloc(b->p, new_size);
+ b->max_size = new_size;
+}
+
+
+void add_data(struct growable_buffer * b, void const * data, size_t len)
+{
+ size_t old_size = b->size;
+ b->size += len;
+ if (b->size > b->max_size)
+ grow_buffer(b);
+ memcpy(b->p + old_size, data, len);
+}
diff --git a/libutil/op_growable_buffer.h b/libutil/op_growable_buffer.h
new file mode 100644
index 0000000..491969a
--- /dev/null
+++ b/libutil/op_growable_buffer.h
@@ -0,0 +1,45 @@
+/**
+ * @file op_growable_buffer.h
+ * a growable buffer interface
+ *
+ * @remark Copyright 2007 OProfile authors
+ * @remark Read the file COPYING
+ *
+ * @author Philippe Elie
+ */
+
+#ifndef OP_GROWABLE_BUFFER_H
+#define OP_GROWABLE_BUFFER_H
+
+#include <stddef.h>
+
+struct growable_buffer {
+ void * p;
+ size_t size;
+ size_t max_size;
+};
+
+/**
+ * init_buffer - initialize an empty buffer
+ * @param buffer the buffer to initialize
+ *
+ * init_buffer do not do any allocation, the first allocation will occur
+ * when add_data() with a non zero len param will be called.
+ */
+void init_buffer(struct growable_buffer * buffer);
+
+/**
+ * free_buffer - free the memory allocated for this buffer
+ * @param buffer the buffer to free
+ */
+void free_buffer(struct growable_buffer * buffer);
+
+/**
+ * add_data - add data to this buffer
+ * @param b the buffer where to add data
+ * @param data a pointer to the data to add
+ * @param len number of byte to add to the buffer
+ */
+void add_data(struct growable_buffer * b, void const * data, size_t len);
+
+#endif /* !OP_GROWABLE_BUFFER_H */
diff --git a/libutil/op_libiberty.c b/libutil/op_libiberty.c
index 3f2725a..0cf45d3 100644
--- a/libutil/op_libiberty.c
+++ b/libutil/op_libiberty.c
@@ -11,7 +11,6 @@
*/
#include <string.h>
-#include <stdlib.h>
#include "op_libiberty.h"
@@ -37,27 +36,3 @@ void * xmemdup (void const * input, size_t copy_size, size_t alloc_size)
return output;
}
#endif
-
-#ifndef HAVE_LIBIBERTY_H
-
-void xmalloc_set_program_name(char const * a)
-{
-}
-
-void * xmalloc(size_t s)
-{
- return malloc(s);
-}
-
-void * xrealloc(void *p, size_t s)
-{
- return realloc(p, s);
-}
-
-/* Copy a string into a memory buffer without fail. */
-char * xstrdup(char const * str)
-{
- return strdup(str);
-}
-
-#endif
diff --git a/libutil/op_libiberty.h b/libutil/op_libiberty.h
index 5372a10..ea02a50 100644
--- a/libutil/op_libiberty.h
+++ b/libutil/op_libiberty.h
@@ -34,6 +34,7 @@ extern "C" {
/* some system have a libiberty.a but no libiberty.h so we must provide
* ourself the missing proto */
#ifndef HAVE_LIBIBERTY_H
+
/* Set the program name used by xmalloc. */
void xmalloc_set_program_name(char const *);
@@ -63,6 +64,14 @@ void * xmemdup(void const *, size_t, size_t) OP_ATTRIB_MALLOC;
#endif /* !HAVE_LIBIBERTY_H */
+#ifdef ANDROID
+#define xmalloc(s) malloc(s)
+#define xrealloc(p,s) realloc(p,s)
+#define xstrdup(str) strdup(str)
+#define xmalloc_set_program_name(n)
+#endif
+
+
#ifdef __cplusplus
}
#endif
diff --git a/libutil/op_list.h b/libutil/op_list.h
index 2e93dda..ed0cd8a 100644
--- a/libutil/op_list.h
+++ b/libutil/op_list.h
@@ -117,7 +117,7 @@ static __inline__ void list_del_init(struct list_head * entry)
* list_empty - tests whether a list is empty
* @param head the list to test.
*/
-static __inline__ int list_empty(struct list_head * head)
+static __inline__ int list_empty(struct list_head const * head)
{
return head->next == head;
}
diff --git a/libutil/op_types.h b/libutil/op_types.h
index 612af20..c025b23 100644
--- a/libutil/op_types.h
+++ b/libutil/op_types.h
@@ -27,6 +27,9 @@ typedef int fd_t;
/** generic type for holding addresses */
typedef unsigned long long vma_t;
+/** generic type to hold a sample count in pp tools */
+typedef u64 count_type;
+
#else
#include <linux/types.h>
#endif
diff --git a/opcontrol/Android.mk b/opcontrol/Android.mk
index d238e9e..56211ad 100644
--- a/opcontrol/Android.mk
+++ b/opcontrol/Android.mk
@@ -5,7 +5,7 @@ LOCAL_SRC_FILES:= \
opcontrol.cpp
LOCAL_STATIC_LIBRARIES := \
- libpopt libutil libdb libabic libop
+ libpopt libutil libdb libabi libop
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/.. \
diff --git a/opcontrol/opcontrol.cpp b/opcontrol/opcontrol.cpp
index c0b29c7..50067cc 100644
--- a/opcontrol/opcontrol.cpp
+++ b/opcontrol/opcontrol.cpp
@@ -1,172 +1,275 @@
/*
- * opcontrol/opcontrol.cpp
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-#include "op_config.h"
+/*
+ * Binary implementation of the original opcontrol script due to missing tools
+ * like awk, test, etc.
+ */
-#include <stdlib.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
#include <unistd.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
-#include <assert.h>
-#include <dirent.h>
-#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
-static int usage(const char* name);
-static int echo(const char* str, const char* file);
-static int read_num(const char* file);
+#include "op_config.h"
-static int start_profiler(int argc, char const * argv[]);
-static int stop_profiler();
-static int reset_profiler();
-static int status_profiler();
+#if 0
+#define verbose(fmt...) printf(fmt)
+#else
+#define verbose(fmt...)
+#endif
-int main(int argc, char const * argv[])
-{
- if (argc < 2)
- return usage(argv[0]);
+/* Experiments found that using a small interval may hang the device, and the
+ * more events tracked simultaneously, the longer the interval has to be.
+ */
+int min_count[3] = {150000, 200000, 250000};
+int list_events;
+int show_usage;
+int setup;
+int quick;
+int num_events;
+int start;
+int stop;
+int reset;
- const char* tool = argv[1];
+int selected_events[3];
+int selected_counts[3];
- int ret = 0;
- if (!strcmp("start", tool)) ret = start_profiler(argc-2, argv+2);
- else if (!strcmp("stop", tool)) ret = stop_profiler();
- else if (!strcmp("reset", tool)) ret = reset_profiler();
- else if (!strcmp("status", tool)) ret = status_profiler();
+char kernel_range[512];
+char vmlinux[512];
- return ret ? usage(argv[0]) : 0;
-}
+struct option long_options[] = {
+ {"help", 0, &show_usage, 1},
+ {"list-events", 0, &list_events, 1},
+ {"reset", 0, &reset, 1},
+ {"setup", 0, &setup, 1},
+ {"quick", 0, &quick, 1},
+ {"event", 1, 0, 'e'},
+ {"vmlinux", 1, 0, 'v'},
+ {"kernel-range", 1, 0, 'r'},
+ {"start", 0, &start, 1},
+ {"stop", 0, &stop, 1},
+ {"shutdown", 0, 0, 'h'},
+ {"status", 0, 0, 't'},
+ {0, 0, 0, 0},
+};
-int usage(const char* name)
-{
- printf("usage: %s [start [-e event][-p type][-c depth][-i names]"
- " | stop | reset | status]\n", name);
- return 0;
+struct event_info {
+ int id;
+ const char *name;
+ const char *explanation;
+} event_info[] = {
+ {0x00, "IFU_IFETCH_MISS",
+ "number of instruction fetch misses"},
+ {0x01, "CYCLES_IFU_MEM_STALL",
+ "cycles instruction fetch pipe is stalled"},
+ {0x02, "CYCLES_DATA_STALL",
+ "cycles stall occurs for due to data dependency"},
+ {0x03, "ITLB_MISS",
+ "number of Instruction MicroTLB misses"},
+ {0x04, "DTLB_MISS",
+ "number of Data MicroTLB misses"},
+ {0x05, "BR_INST_EXECUTED",
+ "branch instruction executed w/ or w/o program flow change"},
+ {0x06, "BR_INST_MISS_PRED",
+ "branch mispredicted"},
+ {0x07, "INSN_EXECUTED",
+ "instructions executed"},
+ {0x09, "DCACHE_ACCESS",
+ "data cache access, cacheable locations"},
+ {0x0a, "DCACHE_ACCESS_ALL",
+ "data cache access, all locations"},
+ {0x0b, "DCACHE_MISS",
+ "data cache miss"},
+ {0x0c, "DCACHE_WB",
+ "data cache writeback, 1 event for every half cacheline"},
+ {0x0d, "PC_CHANGE",
+ "number of times the program counter was changed without a mode switch"},
+ {0x0f, "TLB_MISS",
+ "Main TLB miss"},
+ {0x10, "EXP_EXTERNAL",
+ "Explicit external data access"},
+ {0x11, "LSU_STALL",
+ "cycles stalled because Load Store request queue is full"},
+ {0x12, "WRITE_DRAIN",
+ "Times write buffer was drained"},
+ {0xff, "CPU_CYCLES",
+ "clock cycles counter"},
+};
+
+void usage() {
+ printf("\nopcontrol: usage:\n"
+ " --list-events list event types\n"
+ " --help this message\n"
+ " --setup setup directories\n"
+ " --quick setup and select CPU_CYCLES:150000\n"
+ " --status show configuration\n"
+ " --start start data collection\n"
+ " --stop stop data collection\n"
+ " --reset clears out data from current session\n"
+ " --shutdown kill the oprofile daeman\n"
+ " --event=eventspec\n"
+ " Choose an event. May be specified multiple times.\n"
+ " eventspec is in the form of name[:count], where :\n"
+ " name: event name, see \"opcontrol --list-events\"\n"
+ " count: reset counter value\n"
+ " --vmlinux=file vmlinux kernel image\n"
+ " --kernel-range=start,end\n"
+ " kernel range vma address in hexadecimal\n"
+ );
}
+void setup_session_dir() {
+ int fd;
-int start_profiler(int argc, char const * argv[])
-{
- char const* backtrace_depth = "0";
- if (argc&1)
- return -1;
+ fd = open(OP_DATA_DIR, O_RDONLY);
+ if (fd != -1) {
+ system("rm -r "OP_DATA_DIR);
+ close(fd);
+ }
- while (argc>0) {
- if (!strcmp("-c", argv[0]))
- backtrace_depth = argv[1];
- else if (!strcmp("-p", argv[0]))
- ; // type
- else if (!strcmp("-e", argv[0]))
- ; // event
- else if (!strcmp("-i", argv[0]))
- ; // images
- argc-=2;
- argv+=2;
+ if (mkdir(OP_DATA_DIR, 755)) {
+ fprintf(stderr, "Cannot create directory \"%s\": %s\n",
+ OP_DATA_DIR, strerror(errno));
}
+ if (mkdir(OP_DATA_DIR"/samples", 644)) {
+ fprintf(stderr, "Cannot create directory \"%s\": %s\n",
+ OP_DATA_DIR"/samples", strerror(errno));
+ }
+}
- int err;
+int do_setup() {
+ char dir[1024];
- err = echo(backtrace_depth, OP_DRIVER_BASE"/backtrace_depth");
- if (err) {
- printf("couldn't set backtrace depth. backtraces disabled.\n");
- }
+ setup_session_dir();
- err = echo("1", OP_DRIVER_BASE"/enable");
- if (err) {
- printf("couldn't start profiling, is the oprofile driver installed?\n");
+ if (mkdir(OP_DRIVER_BASE, 644)) {
+ fprintf(stderr, "Cannot create directory "OP_DRIVER_BASE": %s\n",
+ strerror(errno));
+ return -1;
+ }
+ if (system("mount -t oprofilefs nodev "OP_DRIVER_BASE)) {
return -1;
}
-
- // XXX: start daemon with all good options ...
-
- mkdir(OP_BASE_DIR, 644);
return 0;
}
-int stop_profiler()
+void do_list_events()
{
- int dump, stop;
- dump = echo("1", OP_DRIVER_BASE"/dump");
- // XXX: should wait for complete_dump
- usleep(250000);
- stop = echo("0", OP_DRIVER_BASE"/enable");
- if (dump || stop) {
- printf("couldn't stop profiling, is the oprofile driver installed?\n");
- return -1;
+ unsigned int i;
+
+ printf("%-20s: %s\n", "name", "meaning");
+ printf("----------------------------------------"
+ "--------------------------------------\n");
+ for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+ printf("%-20s: %s\n", event_info[i].name, event_info[i].explanation);
}
- int num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_received");
- printf("profiler stopped with %u samples received\n", num);
- return 0;
}
-int rm_dir_content(const char* path)
-{
- DIR* d = opendir(path);
- if (d) {
- struct dirent* de;
- while ((de = readdir(d))) {
- if(de->d_name[0] == '.') continue;
- struct stat s;
- char* tmp = (char*)malloc(strlen(path)+strlen(de->d_name)+2);
- if (tmp) {
- sprintf(tmp, "%s/%s", path, de->d_name);
- if (lstat(tmp, &s) == 0) {
- int mode = s.st_mode & S_IFMT;
- if (mode == S_IFDIR) {
- rm_dir_content(tmp);
- rmdir(tmp);
- } else if (mode == S_IFLNK) {
- } else if (mode == S_IFSOCK) {
- } else if (mode == S_IFREG) {
- unlink(tmp);
- }
- }
- free(tmp);
- }
+int find_event_id_from_name(const char *name) {
+ unsigned int i;
+
+ for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+ if (!strcmp(name, event_info[i].name)) {
+ return i;
}
- closedir(d);
}
- return 0;
+ return -1;
}
-int reset_profiler()
-{
- echo("1", OP_DRIVER_BASE"/dump");
- usleep(250000);
- // should erase all samples
- rm_dir_content(OP_BASE_DIR);
- return 0;
+const char * find_event_name_from_id(int id) {
+ unsigned int i;
+
+ for (i = 0; i < sizeof(event_info)/sizeof(struct event_info); i++) {
+ if (event_info[i].id == id) {
+ return event_info[i].name;
+ }
+ }
+ return NULL;
}
+int process_event(const char *event_spec) {
+ char event_name[512];
+ char count_name[512];
+ unsigned int i;
+ int event_id;
+ int count_val;
-int status_profiler()
-{
- int num = read_num(OP_DRIVER_BASE"/enable");
- if (num >= 0) {
- printf("profiler %s\n", num ? "started" : "not started");
- num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_received");
- printf(" %9u samples received\n", num);
- num = read_num(OP_DRIVER_BASE"/stats/cpu0/backtrace_aborted");
- printf(" %9u backtrace aborted\n", num);
- num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_lost_overflow");
- printf(" %9u samples lost overflow\n", num);
- num = read_num(OP_DRIVER_BASE"/backtrace_depth");
- printf(" %9u backtrace_depth\n", num);
- return 0;
- }
- printf("couldn't get profiling status, is the oprofile driver installed?\n");
- return -1;
+ strncpy(event_name, event_spec, 512);
+ count_name[0] = 0;
+
+ /* First, check if the name is followed by ":" */
+ for (i = 0; i < strlen(event_name); i++) {
+ if (event_name[i] == 0) {
+ break;
+ }
+ if (event_name[i] == ':') {
+ strncpy(count_name, event_name+i+1, 512);
+ event_name[i] = 0;
+ break;
+ }
+ }
+ event_id = find_event_id_from_name(event_name);
+ if (event_id == -1) {
+ fprintf(stderr, "Unknown event name: %s\n", event_name);
+ return -1;
+ }
+ /* Use defualt count */
+ if (count_name[0] == 0) {
+ count_val = min_count[0];
+ } else {
+ count_val = atoi(count_name);
+ }
+
+ selected_events[num_events] = event_id;
+ selected_counts[num_events++] = count_val;
+ verbose("event_id is %d\n", event_id);
+ verbose("count_val is %d\n", count_val);
+ return 0;
}
-int echo(const char* str, const char* file)
+int echo_dev(const char* str, int val, const char* file, int counter)
{
- int fd = open(file, O_WRONLY);
- if (fd<0)
+ char fullname[512];
+ char content[128];
+ int fd;
+
+ if (counter >= 0) {
+ snprintf(fullname, 512, OP_DRIVER_BASE"/%d/%s", counter, file);
+ }
+ else {
+ snprintf(fullname, 512, OP_DRIVER_BASE"/%s", file);
+ }
+ fd = open(fullname, O_WRONLY);
+ if (fd<0) {
+ fprintf(stderr, "Cannot open %s: %s\n", fullname, strerror(errno));
return fd;
- write(fd, str, strlen(str));
+ }
+ if (str == 0) {
+ sprintf(content, "%d", val);
+ }
+ else {
+ strncpy(content, str, 128);
+ }
+ verbose("Configure %s (%s)\n", fullname, content);
+ write(fd, content, strlen(content));
close(fd);
return 0;
}
@@ -181,3 +284,258 @@ int read_num(const char* file)
return atoi(buffer);
}
+void do_status()
+{
+ int num;
+ char fullname[512];
+ int i;
+
+ printf("Driver directory: %s\n", OP_DRIVER_BASE);
+ printf("Session directory: %s\n", OP_DATA_DIR);
+ for (i = 0; i < 3; i++) {
+ sprintf(fullname, OP_DRIVER_BASE"/%d/enabled", i);
+ num = read_num(fullname);
+ if (num > 0) {
+ printf("Counter %d:\n", i);
+
+ /* event name */
+ sprintf(fullname, OP_DRIVER_BASE"/%d/event", i);
+ num = read_num(fullname);
+ printf(" name: %s\n", find_event_name_from_id(num));
+
+ /* profile interval */
+ sprintf(fullname, OP_DRIVER_BASE"/%d/count", i);
+ num = read_num(fullname);
+ printf(" count: %d\n", num);
+ }
+ else {
+ printf("Counter %d disabled\n", i);
+ }
+ }
+
+ num = read_num(OP_DATA_DIR"/lock");
+ if (num >= 0) {
+ int fd;
+ /* Still needs to check if this lock is left-over */
+ sprintf(fullname, "/proc/%d", num);
+ fd = open(fullname, O_RDONLY);
+ if (fd == -1) {
+ printf("Session directory is not clean - do \"opcontrol --setup\""
+ " before you continue\n");
+ return;
+ }
+ else {
+ close(fd);
+ printf("oprofiled pid: %d\n", num);
+ num = read_num(OP_DRIVER_BASE"/enable");
+ printf("profiler is%s running\n", num == 0 ? " not" : "");
+ num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_received");
+ printf(" %9u samples received\n", num);
+ num = read_num(OP_DRIVER_BASE"/stats/cpu0/sample_lost_overflow");
+ printf(" %9u samples lost overflow\n", num);
+#if 0
+ /* FIXME - backtrace seems broken */
+ num = read_num(OP_DRIVER_BASE"/stats/cpu0/backtrace_aborted");
+ printf(" %9u backtrace aborted\n", num);
+ num = read_num(OP_DRIVER_BASE"/backtrace_depth");
+ printf(" %9u backtrace_depth\n", num);
+#endif
+ }
+ }
+ else {
+ printf("oprofiled is not running\n");
+ }
+}
+
+void do_reset()
+{
+ int fd;
+
+ fd = open(OP_DATA_DIR"/samples/current", O_RDONLY);
+ if (fd == -1) {
+ return;
+ }
+ close(fd);
+ system("rm -r "OP_DATA_DIR"/samples/current");
+}
+
+int main(int argc, char * const argv[])
+{
+ int option_index;
+ char command[1024];
+
+ /* Initialize default strings */
+ strcpy(vmlinux, "--no-vmlinux");
+ strcpy(kernel_range, "");
+
+ while (1) {
+ int c = getopt_long(argc, argv, "", long_options, &option_index);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 0:
+ break;
+ /* --event */
+ case 'e':
+ if (num_events == 3) {
+ fprintf(stderr, "More than 3 events specified\n");
+ exit(1);
+ }
+ if (process_event(optarg)) {
+ exit(1);
+ }
+ break;
+ /* --vmlinux */
+ case 'v':
+ sprintf(vmlinux, "-k %s", optarg);
+ break;
+ /* --kernel-range */
+ case 'r':
+ sprintf(kernel_range, "-r %s", optarg);
+ break;
+ /* --shutdown */
+ case 'h': {
+ int pid = read_num(OP_DATA_DIR"/lock");
+ if (pid >= 0) {
+ kill(pid, SIGKILL);
+ }
+ setup_session_dir();
+ break;
+ }
+ /* --status */
+ case 't':
+ do_status();
+ break;
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ verbose("list_events = %d\n", list_events);
+ verbose("setup = %d\n", setup);
+
+ if (list_events) {
+ do_list_events();
+ }
+
+ if (quick) {
+ process_event("CPU_CYCLES");
+ setup = 1;
+ }
+
+ if (reset) {
+ do_reset();
+ }
+
+ if (show_usage) {
+ usage();
+ }
+
+ if (setup) {
+ if (do_setup()) {
+ fprintf(stderr, "do_setup failed");
+ exit(1);
+ }
+ }
+
+ if (num_events != 0) {
+ int i;
+
+ strcpy(command, "oprofiled --session-dir="OP_DATA_DIR);
+
+ /* Since counter #3 can only handle CPU_CYCLES, check and shuffle the
+ * order a bit so that the maximal number of events can be profiled
+ * simultaneously
+ */
+ if (num_events == 3) {
+ for (i = 0; i < num_events; i++) {
+ int event_idx = selected_events[i];
+
+ if (event_info[event_idx].id == 0xff) {
+ break;
+ }
+ }
+
+ /* No CPU_CYCLES is found */
+ if (i == 3) {
+ fprintf(stderr, "You can only specify three events if one of "
+ "them is CPU_CYCLES\n");
+ exit(1);
+ }
+ /* Swap CPU_CYCLES to counter #2 (starting from #0)*/
+ else if (i != 2) {
+ int temp;
+
+ temp = selected_events[2];
+ selected_events[2] = selected_events[i];
+ selected_events[i] = temp;
+
+ temp = selected_counts[2];
+ selected_counts[2] = selected_counts[i];
+ selected_counts[i] = temp;
+ }
+ }
+
+
+ /* Configure the counters and enable them */
+ for (i = 0; i < num_events; i++) {
+ int event_idx = selected_events[i];
+ int setup_result = 0;
+
+ if (i == 0) {
+ snprintf(command+strlen(command), 1024 - strlen(command),
+ " --events=");
+ }
+ else {
+ snprintf(command+strlen(command), 1024 - strlen(command),
+ ",");
+ }
+ /* Compose name:id:count:unit_mask:kernel:user, something like
+ * --events=CYCLES_DATA_STALL:2:0:200000:0:1:1,....
+ */
+ snprintf(command+strlen(command), 1024 - strlen(command),
+ "%s:%d:%d:%d:0:1:1",
+ event_info[event_idx].name,
+ event_info[event_idx].id,
+ i,
+ selected_counts[i]);
+
+ setup_result |= echo_dev("1", 0, "user", i);
+ setup_result |= echo_dev("1", 0, "kernel", i);
+ setup_result |= echo_dev("0", 0, "unit_mask", i);
+ setup_result |= echo_dev("1", 0, "enabled", i);
+ setup_result |= echo_dev(NULL, selected_counts[i], "count", i);
+ setup_result |= echo_dev(NULL, event_info[event_idx].id,
+ "event", i);
+ if (setup_result) {
+ fprintf(stderr, "Counter configuration failed for %s\n",
+ event_info[event_idx].name);
+ fprintf(stderr, "Did you do \"opcontrol --setup\" first?\n");
+ exit(1);
+ }
+ }
+
+ /* Disable the unused counters */
+ for (i = num_events; i < 3; i++) {
+ echo_dev("0", 0, "enabled", i);
+ }
+
+ snprintf(command+strlen(command), 1024 - strlen(command), " %s",
+ vmlinux);
+ if (kernel_range[0]) {
+ snprintf(command+strlen(command), 1024 - strlen(command), " %s",
+ kernel_range);
+ }
+ verbose("command: %s\n", command);
+ system(command);
+ }
+
+ if (start) {
+ echo_dev("1", 0, "enable", -1);
+ }
+
+ if (stop) {
+ echo_dev("0", 0, "enable", -1);
+ }
+}
diff --git a/opimport_pull b/opimport_pull
new file mode 100755
index 0000000..7dbac4a
--- /dev/null
+++ b/opimport_pull
@@ -0,0 +1,70 @@
+#!/usr/bin/python2.4 -E
+
+import os
+import re
+import sys
+
+def PrintUsage():
+ print "Usage:" + sys.argv[0] + " dir"
+ print " dir: directory on the host to store profile results"
+
+if (len(sys.argv) != 2):
+ PrintUsage()
+ sys.exit(1)
+
+try:
+ oprofile_event_dir = os.environ['OPROFILE_EVENTS_DIR']
+except:
+ print "OPROFILE_EVENTS_DIR not set. Run \". envsetup.sh\" first"
+ sys.exit(1)
+
+output_dir = sys.argv[1];
+
+try:
+ os.makedirs(output_dir)
+except:
+ if os.path.exists(output_dir):
+ print "Directory already exists:", output_dir
+ else:
+ print "Cannot create", output_dir
+ sys.exit(1)
+
+# get the samples off the phone
+result = os.system("adb pull /data/oprofile/samples " + output_dir + \
+ "/raw_samples > /dev/null 2>&1")
+if result != 0:
+ print "adb pull failure, exiting"
+ sys.exit(1)
+
+# enter the destination directory
+os.chdir(output_dir)
+stream = os.popen("find raw_samples -type f -name \*all")
+
+# now all the sample files are on the host, we need to invoke opimport one at a
+# time to convert the content from the ARM abi to x86 ABI
+
+# break the full filename into:
+# 1: leading dir: "raw_samples"
+# 2: intermediate dirs: "/blah/blah/blah"
+# 3: filename: e.g. "CPU_CYCLES.150000.0.all.all.all"
+pattern = re.compile("(^raw_samples)(.*)/(.*)$")
+for line in stream:
+ match = pattern.search(line)
+ leading_dir = match.group(1)
+ middle_part = match.group(2)
+ file_name = match.group(3)
+
+ dir = "samples" + middle_part
+
+ # if multiple events are collected the directory could have been setup
+ if not os.path.exists(dir):
+ os.makedirs(dir)
+
+ cmd = oprofile_event_dir + "/bin/opimport -a " + oprofile_event_dir + \
+ "/abi/arm_abi -o samples" + middle_part + "/" + file_name + " " + line
+ os.system(cmd)
+
+stream.close()
+
+# short summary of profiling results
+os.system(oprofile_event_dir + "/bin/opreport --session-dir=.")