diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:47 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:04:47 -0800 |
commit | 7984f7ab3e13cda0c3b04ffeb2608f232e57f93a (patch) | |
tree | 480cc437dec853127d8a2f82dcd5a2f6de124a39 | |
parent | 48ae5fc270ea3bbb965b4bd07cb1691a5c115642 (diff) | |
download | oprofile-7984f7ab3e13cda0c3b04ffeb2608f232e57f93a.tar.gz |
Code drop from //branches/cupcake/...@124589
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=.") |