[PATCH 1/2][bionic] Support profiling for bionic

81 views
Skip to first unread message

Kito Cheng

unread,
Dec 1, 2011, 3:58:37 PM12/1/11
to 0xlab...@googlegroups.com
* Only support arm on current implementation.
* Default profile file place in /data/loca/tmp/$(PROG).gmon.out
- Output file path can change by environment variable PROFDIR.
* Enable profiling by new option LOCAL_PROFILE_MODULE

Change-Id: Ib6326b7c1d63b941eaf1a1b86a2bf4432b70d677
---
libc/arch-arm/include/machine/profile.h | 100 ++++++++++
libc/include/sys/gmon.h | 175 +++++++++++++++++
libc/kernel/arch-arm/asm/ucontext.h | 102 ++++++++++
libc/kernel/arch-x86/asm/ucontext.h | 12 ++
libgmon/Android.mk | 22 ++
libgmon/gmon.c | 325 +++++++++++++++++++++++++++++++
libgmon/mcount.c | 175 +++++++++++++++++
7 files changed, 911 insertions(+), 0 deletions(-)
create mode 100644 libc/arch-arm/include/machine/profile.h
create mode 100644 libc/include/sys/gmon.h
create mode 100644 libc/kernel/arch-arm/asm/ucontext.h
create mode 100644 libc/kernel/arch-x86/asm/ucontext.h
create mode 100644 libgmon/Android.mk
create mode 100644 libgmon/gmon.c
create mode 100644 libgmon/mcount.c

diff --git a/libc/arch-arm/include/machine/profile.h b/libc/arch-arm/include/machine/profile.h
new file mode 100644
index 0000000..6278216
--- /dev/null
+++ b/libc/arch-arm/include/machine/profile.h
@@ -0,0 +1,100 @@
+/* $OpenBSD: profile.h,v 1.2 2011/09/20 22:02:13 miod Exp $ */
+/* $NetBSD: profile.h,v 1.5 2002/03/24 15:49:40 bjh21 Exp $ */
+
+/*
+ * Copyright (c) 2001 Ben Harris
+ * Copyright (c) 1995-1996 Mark Brinicombe
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Brinicombe.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ARM_PROFILE_H_
+#define _ARM_PROFILE_H_
+
+#define _MCOUNT_DECL void _mcount
+
+/*
+ * Cannot implement mcount in C as GCC will trash the ip register when it
+ * pushes a trapframe. Pity we cannot insert assembly before the function
+ * prologue.
+ */
+
+#ifdef __ELF__
+#define MCOUNT_ASM_NAME "__gnu_mcount_nc"
+#ifdef PIC
+#define PLTSYM "(PLT)"
+#endif
+#else
+#define MCOUNT_ASM_NAME "mcount"
+#endif
+
+#ifndef PLTSYM
+#define PLTSYM
+#endif
+
+#define MCOUNT \
+ __asm__(".text"); \
+ __asm__(".align 0"); \
+ __asm__(".type " MCOUNT_ASM_NAME ",%function"); \
+ __asm__(".global " MCOUNT_ASM_NAME); \
+ __asm__(MCOUNT_ASM_NAME ":"); \
+ /* \
+ * Preserve registers that are trashed during mcount \
+ */ \
+ __asm__("stmfd sp!, {r0-r3, lr}"); \
+ /* \
+ * find the return address for mcount, \
+ * and the return address for mcount's caller. \
+ * \
+ * frompcindex = pc pushed by call into self. \
+ */ \
+ __asm__("ldr r0, [sp, #20]"); \
+ /* \
+ * selfpc = pc pushed by mcount call \
+ */ \
+ __asm__("mov r1, lr"); \
+ /* \
+ * Call the real mcount code \
+ */ \
+ __asm__("bl " __STRING(_mcount) PLTSYM); \
+ /* \
+ * Restore registers that were trashed during mcount \
+ */ \
+ __asm__("ldmfd sp!, {r0-r3, pc}");
+
+#ifdef _KERNEL
+#include <arm/cpufunc.h>
+/*
+ * splhigh() and splx() are heavyweight, and call mcount(). Therefore
+ * we disabled interrupts (IRQ, but not FIQ) directly on the CPU.
+ *
+ * We're lucky that the CPSR and 's' both happen to be 'int's.
+ */
+#define MCOUNT_ENTER s = __set_cpsr_c(0x0080, 0x0080); /* kill IRQ */
+#define MCOUNT_EXIT __set_cpsr_c(0xffffffff, s); /* restore old value */
+#endif /* _KERNEL */
+
+#endif /* _ARM_PROFILE_H_ */
diff --git a/libc/include/sys/gmon.h b/libc/include/sys/gmon.h
new file mode 100644
index 0000000..3db3e1b
--- /dev/null
+++ b/libc/include/sys/gmon.h
@@ -0,0 +1,175 @@
+/* $OpenBSD: gmon.h,v 1.4 2003/06/02 23:28:21 millert Exp $ */
+/* $NetBSD: gmon.h,v 1.5 1996/04/09 20:55:30 cgd Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1986, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)gmon.h 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef _SYS_GMON_H_
+#define _SYS_GMON_H_
+
+#include <machine/profile.h>
+#include <sys/types.h>
+/*
+ * Structure prepended to gmon.out profiling data file.
+ */
+struct gmonhdr {
+ u_long lpc; /* base pc address of sample buffer */
+ u_long hpc; /* max pc address of sampled buffer */
+ int ncnt; /* size of sample buffer (plus this header) */
+ int version; /* version number */
+ int profrate; /* profiling clock rate */
+ int spare[3]; /* reserved */
+};
+#define GMONVERSION 0x00051879
+
+/*
+ * histogram counters are unsigned shorts (according to the kernel).
+ */
+#define HISTCOUNTER unsigned short
+
+/*
+ * fraction of text space to allocate for histogram counters here, 1/2
+ */
+#define HISTFRACTION 2
+
+/*
+ * Fraction of text space to allocate for from hash buckets.
+ * The value of HASHFRACTION is based on the minimum number of bytes
+ * of separation between two subroutine call points in the object code.
+ * Given MIN_SUBR_SEPARATION bytes of separation the value of
+ * HASHFRACTION is calculated as:
+ *
+ * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
+ *
+ * For example, on the VAX, the shortest two call sequence is:
+ *
+ * calls $0,(r0)
+ * calls $0,(r0)
+ *
+ * which is separated by only three bytes, thus HASHFRACTION is
+ * calculated as:
+ *
+ * HASHFRACTION = 3 / (2 * 2 - 1) = 1
+ *
+ * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
+ * is less than three, this algorithm will not work!
+ *
+ * In practice, however, call instructions are rarely at a minimal
+ * distance. Hence, we will define HASHFRACTION to be 2 across all
+ * architectures. This saves a reasonable amount of space for
+ * profiling data structures without (in practice) sacrificing
+ * any granularity.
+ */
+#define HASHFRACTION 2
+
+/*
+ * percent of text space to allocate for tostructs with a minimum.
+ */
+#define ARCDENSITY 2
+#define MINARCS 50
+#define MAXARCS ((1 << (8 * sizeof(HISTCOUNTER))) - 2)
+
+struct tostruct {
+ u_long selfpc;
+ long count;
+ u_short link;
+ u_short pad;
+};
+
+/*
+ * a raw arc, with pointers to the calling site and
+ * the called site and a count.
+ */
+struct rawarc {
+ u_long raw_frompc;
+ u_long raw_selfpc;
+ long raw_count;
+};
+
+/*
+ * general rounding functions.
+ */
+#define ROUNDDOWN(x,y) (((x)/(y))*(y))
+#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
+
+/*
+ * The profiling data structures are housed in this structure.
+ */
+struct gmonparam {
+ int state;
+ u_short *kcount;
+ u_long kcountsize;
+ u_short *froms;
+ u_long fromssize;
+ struct tostruct *tos;
+ u_long tossize;
+ long tolimit;
+ u_long lowpc;
+ u_long highpc;
+ u_long textsize;
+ u_long hashfraction;
+};
+extern struct gmonparam _gmonparam;
+
+/*
+ * Possible states of profiling.
+ */
+#define GMON_PROF_ON 0
+#define GMON_PROF_BUSY 1
+#define GMON_PROF_ERROR 2
+#define GMON_PROF_OFF 3
+
+/*
+ * Sysctl definitions for extracting profiling information from the kernel.
+ */
+#define GPROF_STATE 0 /* int: profiling enabling variable */
+#define GPROF_COUNT 1 /* struct: profile tick count buffer */
+#define GPROF_FROMS 2 /* struct: from location hash bucket */
+#define GPROF_TOS 3 /* struct: destination/count structure */
+#define GPROF_GMONPARAM 4 /* struct: profiling parameters (see above) */
+
+#ifndef PROFRATE
+/* Each sample counts as 0.01 seconds by default. */
+#define PROFRATE 100
+#endif
+
+#ifndef PROFDIR
+#define PROFDIR "/data/local/tmp"
+#endif
+
+#ifndef PROFFILE_SUFFIX
+#define PROFFILE_SUFFIX ".gmon.out"
+#endif
+
+void monstartup(u_long lowpc, u_long highpc);
+void _mcleanup(void);
+
+#endif /* !_SYS_GMON_H_ */
diff --git a/libc/kernel/arch-arm/asm/ucontext.h b/libc/kernel/arch-arm/asm/ucontext.h
new file mode 100644
index 0000000..9881af3
--- /dev/null
+++ b/libc/kernel/arch-arm/asm/ucontext.h
@@ -0,0 +1,102 @@
+#ifndef _ASMARM_UCONTEXT_H
+#define _ASMARM_UCONTEXT_H
+
+#include <asm/fpstate.h>
+
+/*
+ * struct sigcontext only has room for the basic registers, but struct
+ * ucontext now has room for all registers which need to be saved and
+ * restored. Coprocessor registers are stored in uc_regspace. Each
+ * coprocessor's saved state should start with a documented 32-bit magic
+ * number, followed by a 32-bit word giving the coproccesor's saved size.
+ * uc_regspace may be expanded if necessary, although this takes some
+ * coordination with glibc.
+ */
+
+struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+ /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */
+ int _unused[32 - (sizeof (sigset_t) / sizeof (int))];
+ /* Last for extensibility. Eight byte aligned because some
+ coprocessors require eight byte alignment. */
+ unsigned long uc_regspace[128] __attribute__((__aligned__(8)));
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Coprocessor save state. The magic values and specific
+ * coprocessor's layouts are part of the userspace ABI. Each one of
+ * these should be a multiple of eight bytes and aligned to eight
+ * bytes, to prevent unpredictable padding in the signal frame.
+ */
+
+#ifdef CONFIG_CRUNCH
+#define CRUNCH_MAGIC 0x5065cf03
+#define CRUNCH_STORAGE_SIZE (CRUNCH_SIZE + 8)
+
+struct crunch_sigframe {
+ unsigned long magic;
+ unsigned long size;
+ struct crunch_state storage;
+} __attribute__((__aligned__(8)));
+#endif
+
+#ifdef CONFIG_IWMMXT
+/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */
+#define IWMMXT_MAGIC 0x12ef842a
+#define IWMMXT_STORAGE_SIZE (IWMMXT_SIZE + 8)
+
+struct iwmmxt_sigframe {
+ unsigned long magic;
+ unsigned long size;
+ struct iwmmxt_struct storage;
+} __attribute__((__aligned__(8)));
+#endif /* CONFIG_IWMMXT */
+
+#ifdef CONFIG_VFP
+#define VFP_MAGIC 0x56465001
+
+struct vfp_sigframe
+{
+ unsigned long magic;
+ unsigned long size;
+ struct user_vfp ufp;
+ struct user_vfp_exc ufp_exc;
+} __attribute__((__aligned__(8)));
+
+/*
+ * 8 byte for magic and size, 264 byte for ufp, 12 bytes for ufp_exc,
+ * 4 bytes padding.
+ */
+#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe)
+
+#endif /* CONFIG_VFP */
+
+/*
+ * Auxiliary signal frame. This saves stuff like FP state.
+ * The layout of this structure is not part of the user ABI,
+ * because the config options aren't. uc_regspace is really
+ * one of these.
+ */
+struct aux_sigframe {
+#ifdef CONFIG_CRUNCH
+ struct crunch_sigframe crunch;
+#endif
+#ifdef CONFIG_IWMMXT
+ struct iwmmxt_sigframe iwmmxt;
+#endif
+#ifdef CONFIG_VFP
+ struct vfp_sigframe vfp;
+#endif
+ /* Something that isn't a valid magic number for any coprocessor. */
+ unsigned long end_magic;
+} __attribute__((__aligned__(8)));
+
+#endif
+
+#endif /* !_ASMARM_UCONTEXT_H */
diff --git a/libc/kernel/arch-x86/asm/ucontext.h b/libc/kernel/arch-x86/asm/ucontext.h
new file mode 100644
index 0000000..b7c29c8
--- /dev/null
+++ b/libc/kernel/arch-x86/asm/ucontext.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_X86_UCONTEXT_H
+#define _ASM_X86_UCONTEXT_H
+
+#define UC_FP_XSTATE 0x1 /* indicates the presence of extended state
+ * information in the memory layout pointed
+ * by the fpstate pointer in the ucontext's
+ * sigcontext struct (uc_mcontext).
+ */
+
+#include <asm-generic/ucontext.h>
+
+#endif /* _ASM_X86_UCONTEXT_H */
diff --git a/libgmon/Android.mk b/libgmon/Android.mk
new file mode 100644
index 0000000..07a1426
--- /dev/null
+++ b/libgmon/Android.mk
@@ -0,0 +1,22 @@
+#
+# libgmon implement the mcount function to enable generate profile
+# by `-pg` option.
+#
+
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH:= $(call my-dir)
+
+#
+# libgmon
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := gmon.c mcount.c
+
+LOCAL_MODULE:= libgmon
+
+include $(BUILD_STATIC_LIBRARY)
+
+endif
diff --git a/libgmon/gmon.c b/libgmon/gmon.c
new file mode 100644
index 0000000..d1043b3
--- /dev/null
+++ b/libgmon/gmon.c
@@ -0,0 +1,325 @@
+/* $OpenBSD: gmon.c,v 1.20 2005/11/20 17:06:06 millert Exp $ */
+/*-
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/gmon.h>
+#include <sys/mman.h>
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+
+struct gmonparam _gmonparam = { .state = GMON_PROF_OFF };
+
+static int s_scale;
+static unsigned short *sample_buf;
+static size_t sample_bufsiz;
+static size_t pc_offset;
+static unsigned int pc_scale;
+
+/* see profil(2) where this is describe (incorrectly) */
+#define SCALE_1_TO_1 0x10000L
+
+#define ERR(s) write(STDERR_FILENO, s, sizeof(s))
+
+static void moncontrol(int);
+static int gmon_profil(unsigned short *buf, size_t bufsiz,
+ size_t offset, unsigned int scale);
+
+void
+monstartup(u_long lowpc, u_long highpc)
+{
+ u_long o;
+ void *addr;
+ struct gmonparam *p = &_gmonparam;
+ if (lowpc > highpc) {
+ ERR("monstartup: Incorrect parameter (lowpc > highpc)\n");
+ return;
+ }
+ /*
+ * round lowpc and highpc to multiples of the density we're using
+ * so the rest of the scaling (here and in gprof) stays in ints.
+ */
+ p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
+ p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
+ p->textsize = p->highpc - p->lowpc;
+ p->kcountsize = p->textsize / HISTFRACTION;
+ p->hashfraction = HASHFRACTION;
+ p->fromssize = p->textsize / p->hashfraction;
+ p->tolimit = p->textsize * ARCDENSITY / 100;
+ if (p->tolimit < MINARCS)
+ p->tolimit = MINARCS;
+ else if (p->tolimit > MAXARCS)
+ p->tolimit = MAXARCS;
+ p->tossize = p->tolimit * sizeof(struct tostruct);
+
+ addr = mmap((void *)0, p->kcountsize, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;
+ p->kcount = addr;
+
+ addr = mmap((void *)0, p->fromssize, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;
+ p->froms = addr;
+
+ addr = mmap((void *)0, p->tossize, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;
+ p->tos = addr;
+ p->tos[0].link = 0;
+
+ o = p->highpc - p->lowpc;
+ if (p->kcountsize < o) {
+#ifndef notdef
+ s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
+#else /* avoid floating point */
+ int quot = o / p->kcountsize;
+
+ if (quot >= 0x10000)
+ s_scale = 1;
+ else if (quot >= 0x100)
+ s_scale = 0x10000 / quot;
+ else if (o >= 0x800000)
+ s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
+ else
+ s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
+#endif
+ } else
+ s_scale = SCALE_1_TO_1;
+
+ moncontrol(1);
+ return;
+
+mapfailed:
+ if (p->kcount != NULL) {
+ munmap(p->kcount, p->kcountsize);
+ p->kcount = NULL;
+ }
+ if (p->froms != NULL) {
+ munmap(p->froms, p->fromssize);
+ p->froms = NULL;
+ }
+ if (p->tos != NULL) {
+ munmap(p->tos, p->tossize);
+ p->tos = NULL;
+ }
+ ERR("monstartup: out of memory\n");
+}
+
+void
+_mcleanup(void)
+{
+ int fd;
+ int fromindex;
+ int endfrom;
+ u_long frompc;
+ int toindex;
+ struct rawarc rawarc;
+ struct gmonparam *p = &_gmonparam;
+ struct gmonhdr gmonhdr, *hdr;
+ char *profdir;
+ char *proffile;
+ char buf[PATH_MAX];
+ extern char *__progname;
+ char *s, *t, *limit;
+#ifdef DEBUG
+ int log, len;
+ char dbuf[200];
+#endif
+
+ if (p->state == GMON_PROF_ERROR)
+ ERR("_mcleanup: tos overflow\n");
+
+ moncontrol(0);
+
+ if ((profdir = getenv("PROFDIR")) != NULL) {
+ /* If PROFDIR contains a null value, no profiling
+ output is produced */
+ if (*profdir == '\0') {
+ return;
+ }
+ } else {
+ profdir = PROFDIR;
+ }
+
+ limit = buf + sizeof buf - 1 - 10 - 1 -
+ strlen(__progname) - 1 - strlen(PROFFILE_SUFFIX) - 1;
+ t = buf;
+ s = profdir;
+ while((*t = *s) != '\0' && t < limit) {
+ t++;
+ s++;
+ }
+ *t++ = '/';
+
+ s = __progname;
+ while ((*t++ = *s++) != '\0')
+ ;
+
+ --t; /* Drop '\0' */
+
+ s = ".gmon.out";
+
+ while ((*t++ = *s++) != '\0')
+ ;
+
+ proffile = buf;
+
+ fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0664);
+ if (fd < 0) {
+ perror( proffile );
+ return;
+ }
+#ifdef DEBUG
+ log = open("/data/local/tmp/gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
+ if (log < 0) {
+ perror("mcount: gmon.log");
+ return;
+ }
+ snprintf(dbuf, sizeof dbuf, "[mcleanup1] kcount 0x%p ssiz %ld\n",
+ p->kcount, p->kcountsize);
+ write(log, dbuf, strlen(dbuf));
+#endif
+ hdr = (struct gmonhdr *)&gmonhdr;
+ bzero(hdr, sizeof(*hdr));
+ hdr->lpc = p->lowpc;
+ hdr->hpc = p->highpc;
+ hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
+ hdr->version = GMONVERSION;
+ hdr->profrate = PROFRATE;
+ write(fd, (char *)hdr, sizeof *hdr);
+ write(fd, p->kcount, p->kcountsize);
+ endfrom = p->fromssize / sizeof(*p->froms);
+ for (fromindex = 0; fromindex < endfrom; fromindex++) {
+ if (p->froms[fromindex] == 0)
+ continue;
+
+ frompc = p->lowpc;
+ frompc += fromindex * p->hashfraction * sizeof(*p->froms);
+ for (toindex = p->froms[fromindex]; toindex != 0;
+ toindex = p->tos[toindex].link) {
+#ifdef DEBUG
+ (void) snprintf(dbuf, sizeof dbuf,
+ "[mcleanup2] frompc 0x%lx selfpc 0x%lx count %ld\n" ,
+ frompc, p->tos[toindex].selfpc,
+ p->tos[toindex].count);
+ write(log, dbuf, strlen(dbuf));
+#endif
+ rawarc.raw_frompc = frompc;
+ rawarc.raw_selfpc = p->tos[toindex].selfpc;
+ rawarc.raw_count = p->tos[toindex].count;
+ write(fd, &rawarc, sizeof rawarc);
+ }
+ }
+ close(fd);
+}
+
+/*
+ * Control profiling
+ * profiling is what mcount checks to see if
+ * all the data structures are ready.
+ */
+void
+moncontrol(int mode)
+{
+ struct gmonparam *p = &_gmonparam;
+ if (mode) {
+ /* start */
+ gmon_profil(p->kcount, p->kcountsize, p->lowpc, s_scale);
+ p->state = GMON_PROF_ON;
+ } else {
+ /* stop */
+ gmon_profil(0, 0, 0, 0);
+ p->state = GMON_PROF_OFF;
+ }
+}
+
+static void
+profil_sampler (int sig __attribute__((unused)),
+ siginfo_t *si __attribute__((unused)),
+ void *context)
+{
+ struct ucontext *uc = (struct ucontext*)context;
+ uintptr_t pc = uc->uc_mcontext.arm_pc;
+ size_t i = ((uintptr_t)pc - (uintptr_t)pc_offset) / 2;
+ i = i / 65536 * pc_scale + i % 65536 * pc_scale / 65536;
+ if (i < sample_bufsiz)
+ ++sample_buf[i];
+}
+
+static int
+gmon_profil(unsigned short *buf, size_t bufsiz,
+ size_t offset, unsigned int scale)
+{
+ struct sigaction sigact;
+ struct itimerval timer;
+ int retval;
+
+ /* Disable profiling if scale is 0 */
+ if (scale == 0) {
+ sigaction (SIGPROF, NULL, NULL);
+ setitimer (ITIMER_PROF, NULL, NULL);
+ return 0;
+ }
+
+ sample_buf = buf;
+ sample_bufsiz = bufsiz / sizeof(unsigned short);
+ pc_offset = offset;
+ pc_scale = scale;
+
+ sigact.sa_sigaction = &profil_sampler;
+ sigact.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigfillset (&sigact.sa_mask);
+
+ if (sigaction (SIGPROF, &sigact, NULL) < 0) {
+ ERR("gmon_profil : error on set sigaction");
+ return -1;
+ }
+
+ timer.it_value.tv_sec = 0;
+ timer.it_value.tv_usec = 1000000 / PROFRATE;
+ timer.it_interval = timer.it_value;
+ if (setitimer (ITIMER_PROF, &timer, NULL) < 0) {
+ ERR("gmon_profil : error on setitimer");
+ return -1;
+ }
+ return 0;
+}
diff --git a/libgmon/mcount.c b/libgmon/mcount.c
new file mode 100644
index 0000000..7c27b9e
--- /dev/null
+++ b/libgmon/mcount.c
@@ -0,0 +1,175 @@
+/* $OpenBSD: mcount.c,v 1.11 2010/05/09 15:56:08 kettenis Exp $ */
+/*-
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/gmon.h>
+
+#include <stdlib.h>
+
+/*
+ * mcount is called on entry to each function compiled with the profiling
+ * switch set. _mcount(), which is declared in a machine-dependent way
+ * with _MCOUNT_DECL, does the actual work and is either inlined into a
+ * C routine or called by an assembly stub. In any case, this magic is
+ * taken care of by the MCOUNT definition in <machine/profile.h>.
+ *
+ * _mcount updates data structures that represent traversals of the
+ * program's call graph edges. frompc and selfpc are the return
+ * address and function address that represents the given call graph edge.
+ *
+ * Note: the original BSD code used the same variable (frompcindex) for
+ * both frompcindex and frompc. Any reasonable, modern compiler will
+ * perform this optimization.
+ */
+_MCOUNT_DECL(u_long frompc, u_long selfpc) __used;
+/* _mcount; may be static, inline, etc */
+_MCOUNT_DECL(u_long frompc, u_long selfpc)
+{
+ unsigned short *frompcindex;
+ struct tostruct *top, *prevtop;
+ struct gmonparam *p;
+ long toindex;
+ p = &_gmonparam;
+ /*
+ * check that we are profiling
+ * and that we aren't recursively invoked.
+ */
+ if (p->state != GMON_PROF_ON)
+ return;
+ p->state = GMON_PROF_BUSY;
+ /*
+ * check that frompcindex is a reasonable pc value.
+ * for example: signal catchers get called from the stack,
+ * not from text space. too bad.
+ */
+ frompc -= p->lowpc;
+ if (frompc > p->textsize)
+ goto done;
+
+#if (HASHFRACTION & (HASHFRACTION - 1)) == 0
+ if (p->hashfraction == HASHFRACTION)
+ frompcindex =
+ &p->froms[frompc / (HASHFRACTION * sizeof(*p->froms))];
+ else
+#endif
+ frompcindex =
+ &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
+ toindex = *frompcindex;
+ if (toindex == 0) {
+ /*
+ * first time traversing this arc
+ */
+ toindex = ++p->tos[0].link;
+ if (toindex >= p->tolimit)
+ /* halt further profiling */
+ goto overflow;
+
+ *frompcindex = toindex;
+ top = &p->tos[toindex];
+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = 0;
+ goto done;
+ }
+ top = &p->tos[toindex];
+ if (top->selfpc == selfpc) {
+ /*
+ * arc at front of chain; usual case.
+ */
+ top->count++;
+ goto done;
+ }
+ /*
+ * have to go looking down chain for it.
+ * top points to what we are looking at,
+ * prevtop points to previous top.
+ * we know it is not at the head of the chain.
+ */
+ for (; /* goto done */; ) {
+ if (top->link == 0) {
+ /*
+ * top is end of the chain and none of the chain
+ * had top->selfpc == selfpc.
+ * so we allocate a new tostruct
+ * and link it to the head of the chain.
+ */
+ toindex = ++p->tos[0].link;
+ if (toindex >= p->tolimit)
+ goto overflow;
+
+ top = &p->tos[toindex];
+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = *frompcindex;
+ *frompcindex = toindex;
+ goto done;
+ }
+ /*
+ * otherwise, check the next arc on the chain.
+ */
+ prevtop = top;
+ top = &p->tos[top->link];
+ if (top->selfpc == selfpc) {
+ /*
+ * there it is.
+ * increment its count
+ * move it to the head of the chain.
+ */
+ top->count++;
+ toindex = prevtop->link;
+ prevtop->link = top->link;
+ top->link = *frompcindex;
+ *frompcindex = toindex;
+ goto done;
+ }
+ }
+done:
+ p->state = GMON_PROF_ON;
+ return;
+overflow:
+ p->state = GMON_PROF_ERROR;
+ return;
+}
+
+/*
+ * Actual definition of mcount function. Defined in <machine/profile.h>,
+ * which is included by <sys/gmon.h>.
+ */
+MCOUNT
+
+extern char _start[];
+extern char etext[];
+
+void __gmon_start__ (void) __attribute__ ((constructor));
+
+void __gmon_start__(){
+ monstartup ((u_long) _start, (u_long) &etext);
+ atexit (&_mcleanup);
+}
--
1.7.7.3

Kito Cheng

unread,
Dec 1, 2011, 3:58:38 PM12/1/11
to 0xlab...@googlegroups.com
* Support profiling shared libraries (include opened by dlopen)!
* Profile file is output to /data/loca/tmp/agmon.out
* Enable profiling by new option LOCAL_ENABLE_APROF

* Only support arm on current implementation
* Profiling file format is *incompatible* with gprof

Change-Id: Iafe425ed32dbb04652cfd66f9c40c470a75207bf
---
libaprof/Android.mk | 98 ++++++++++++++
libaprof/libaprof-aux.c | 20 +++
libaprof/libaprof-static.c | 264 ++++++++++++++++++++++++++++++++++++++
libaprof/libaprof-stubs.c | 7 +
libaprof/libaprof.c | 24 ++++
libaprof/libaprof.h | 73 +++++++++++
libaprof/test/Android.mk | 72 ++++++++++
libaprof/test/libprof-test.c | 7 +
libaprof/test/profiler-test-dl.c | 24 ++++
libaprof/test/profiler-test.c | 20 +++
linker/Android.mk | 13 ++-
linker/linker.c | 16 ++-
linker/linker.h | 20 +++
13 files changed, 653 insertions(+), 5 deletions(-)
create mode 100644 libaprof/Android.mk
create mode 100644 libaprof/libaprof-aux.c
create mode 100644 libaprof/libaprof-static.c
create mode 100644 libaprof/libaprof-stubs.c
create mode 100644 libaprof/libaprof.c
create mode 100644 libaprof/libaprof.h
create mode 100644 libaprof/test/Android.mk
create mode 100644 libaprof/test/libprof-test.c
create mode 100644 libaprof/test/profiler-test-dl.c
create mode 100644 libaprof/test/profiler-test.c

diff --git a/libaprof/Android.mk b/libaprof/Android.mk
new file mode 100644
index 0000000..84f0fab
--- /dev/null
+++ b/libaprof/Android.mk
@@ -0,0 +1,98 @@
+#
+# libaprof implement the mcount function to enable generate profile


+# by `-pg` option.
+#
+
+ifeq ($(TARGET_ARCH),arm)
+
+LOCAL_PATH:= $(call my-dir)
+
+#

+# There are stubs for functions only, they are actually defined
+# in the dynamic linker (aprof.c), and hijacked at runtime.


+#
+
+include $(CLEAR_VARS)
+

+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_SRC_FILES := libaprof-stubs.c
+
+LOCAL_MODULE := libaprof-stubs
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# libgmon with aprof extension
+#
+# The aprof can provide call graph and sample based profiling data
+# for shared libraries, static libraries and executable.


+#
+
+include $(CLEAR_VARS)
+

+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_SRC_FILES := libaprof.c
+
+LOCAL_MODULE:= libaprof
+
+LOCAL_SHARED_LIBRARIES := libaprof-stubs
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# libaprof for mcount


+#
+
+include $(CLEAR_VARS)
+

+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_SRC_FILES := libaprof.c
+
+LOCAL_MODULE:= libaprof
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+#
+# libaprof for static link executable
+#
+# Implment the simplified version for aprof_init, aprof_fini and aprof_mcount.


+#
+
+include $(CLEAR_VARS)
+

+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_SRC_FILES := libaprof-static.c
+
+LOCAL_MODULE:= libaprof-static
+
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# libaprof-aux libraries for register constructor and destructor for aprof
+#
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE_TAGS := eng
+
+LOCAL_SRC_FILES := libaprof-aux.c
+
+LOCAL_MODULE:= libaprof-aux


+
+include $(BUILD_STATIC_LIBRARY)
+
+endif

diff --git a/libaprof/libaprof-aux.c b/libaprof/libaprof-aux.c
new file mode 100644
index 0000000..1f7cdc1
--- /dev/null
+++ b/libaprof/libaprof-aux.c
@@ -0,0 +1,20 @@
+/*
+ * libgmon aux library for register
+ * constructor and destructor for aprof


+ */
+
+#include <stdio.h>
+#include <stdlib.h>

+#include <sys/gmon.h>
+
+extern void aprof_init();
+extern void aprof_fini();
+
+void __libgmon_aprof_ctor() __attribute__((constructor));
+
+void __libgmon_aprof_ctor()
+{
+ printf("__libgmon_aprof_ctor\n");
+ aprof_init();
+ atexit(&aprof_fini);
+}
diff --git a/libaprof/libaprof-static.c b/libaprof/libaprof-static.c
new file mode 100644
index 0000000..a9a88ad
--- /dev/null
+++ b/libaprof/libaprof-static.c
@@ -0,0 +1,264 @@
+/*
+ * libaprof-static are imp


+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/gmon.h>
+#include <sys/mman.h>
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+

+#include <libaprof.h>
+
+extern char etext[];
+extern char *__progname;
+
+static uintptr_t textsize;
+
+static uint32_t bin_size;
+static uint16_t *bins;
+
+static size_t fromssize;
+static uint16_t *froms;
+
+static struct tostruct *tos;
+static size_t tolimit;
+static size_t tossize;
+
+void aprof_init();
+void aprof_fini();
+
+static void aprof_sampler (int sig __attribute__((unused)),
+ siginfo_t *sii __attribute__((unused)),


+ void *context)
+{
+ struct ucontext *uc = (struct ucontext*)context;
+ uintptr_t pc = uc->uc_mcontext.arm_pc;

+ size_t i = (pc / 2) / HASHFRACTION;
+ bins[i]++;
+}
+
+void aprof_init() {


+ struct sigaction sigact;
+ struct itimerval timer;

+ void *addr;
+ size_t tossize;
+ /*
+ * Allocate memory
+ */
+ textsize = (uintptr_t)(&etext[0]);
+ bin_size = textsize / HISTFRACTION;
+ tolimit = textsize * ARCDENSITY / 100;
+ if (tolimit < MINARCS)
+ tolimit = MINARCS;
+ else if (tolimit > MAXARCS)
+ tolimit = MAXARCS;
+ tossize = tolimit * sizeof(struct tostruct);
+
+ addr = mmap((void *)0, bin_size, PROT_READ|PROT_WRITE,


+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;

+ bins = addr;
+
+ fromssize = textsize / HASHFRACTION;
+ addr = mmap((void *)0, fromssize, PROT_READ|PROT_WRITE,


+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;

+ froms = addr;
+
+ addr = mmap((void *)0, tossize, PROT_READ|PROT_WRITE,


+ MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
+ if (addr == MAP_FAILED)
+ goto mapfailed;

+ tos = addr;
+ tos[0].link = 0;
+
+ /*
+ * Setup timer and hander for sample
+ */
+ sigact.sa_sigaction = &aprof_sampler;


+ sigact.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigfillset(&sigact.sa_mask);
+
+ if (sigaction(SIGPROF, &sigact, NULL) < 0) {

+ /* Error */
+ return;


+ }
+
+ timer.it_value.tv_sec = 0;

+ timer.it_value.tv_usec = 1000000 / (PROFRATE*10);


+ timer.it_interval = timer.it_value;
+ if (setitimer(ITIMER_PROF, &timer, NULL) < 0) {

+ /* Error */
+ return;
+ }
+ return;
+
+mapfailed:
+ if (bins != NULL) {
+ munmap(bins, bin_size);
+ bins = NULL;
+ }
+ if (froms != NULL) {
+ munmap(froms, fromssize);
+ froms = NULL;
+ }
+ if (tos != NULL) {
+ munmap(tos, tossize);
+ tos = NULL;
+ }
+}
+
+void aprof_fini() {
+ int fd = open("/data/local/tmp/agmon.out", O_CREAT|O_TRUNC|O_WRONLY, 0664);
+ uint32_t len = strlen(__progname);
+ uint32_t header_type;
+ const char aprof_tag[] = APROF_TAG;
+ uint32_t version = APROF_VERSION;
+ uint32_t pointer_size = sizeof(intptr_t);
+ uint32_t sample_rate = PROFRATE;
+ uint32_t base = 0;
+
+ /* Trun off aprof_sampler */
+ sigaction(SIGPROF, NULL, NULL);
+ setitimer(ITIMER_PROF, NULL, NULL);
+
+ /* Write aprof profiling file header */
+ write(fd, aprof_tag, APROF_TAG_LENGTH);
+ write(fd, &version, sizeof(uint32_t));
+ write(fd, &sample_rate, sizeof(uint32_t));
+ write(fd, &pointer_size, sizeof(uint32_t));
+
+ /* Write histogram records */
+ header_type = APROF_EXECUTABLE_HISTOGRAM_HEADER;
+ write(fd, &header_type, sizeof(uint32_t));
+ write(fd, &len, sizeof(uint32_t));
+ write(fd, __progname, len);
+ write(fd, &base, sizeof(uintptr_t));
+ write(fd, &textsize, sizeof(uintptr_t));
+ write(fd, &bin_size, sizeof(uint32_t));
+ write(fd, bins, bin_size);
+ /* Write call graph records */
+ header_type = APROF_CALL_GRAPH_HEADER;
+ write(fd, &header_type, sizeof(uint32_t));
+ int fromindex, endfrom, toindex;
+ uintptr_t frompc;
+ uint32_t num_edges = 0;
+ /* Go through for count how many edges. */
+ endfrom = fromssize / sizeof(*froms);


+ for (fromindex = 0; fromindex < endfrom; fromindex++) {

+ if (froms[fromindex] == 0) continue;
+ for (toindex = froms[fromindex]; toindex != 0;
+ toindex = tos[toindex].link) {
+ num_edges++;
+ }
+ }
+
+ write(fd, &num_edges, sizeof(uint32_t));
+ endfrom = fromssize / sizeof(*froms);


+ for (fromindex = 0; fromindex < endfrom; fromindex++) {

+ if (froms[fromindex] == 0)
+ continue;
+
+ frompc = fromindex * HASHFRACTION * sizeof(*froms);
+ for (toindex = froms[fromindex]; toindex != 0;
+ toindex = tos[toindex].link) {
+ uintptr_t selfpc = tos[toindex].selfpc;
+ uint32_t count = tos[toindex].count;
+ write(fd, &frompc, sizeof(uintptr_t));
+ write(fd, &selfpc, sizeof(uintptr_t));
+ write(fd, &count, sizeof(uint32_t));
+ }
+ }
+}
+
+void aprof_mcount(uintptr_t frompc, uintptr_t selfpc) {


+ unsigned short *frompcindex;
+ struct tostruct *top, *prevtop;

+ unsigned long toindex;
+ uintptr_t frompc_offset;
+ frompc_offset = frompc;
+ frompcindex =
+ &froms[frompc_offset / (HASHFRACTION * sizeof(*froms))];


+ toindex = *frompcindex;
+ if (toindex == 0) {

+ /* first time traversing this arc */
+ toindex = ++tos[0].link;
+ if (toindex >= tolimit)


+ /* halt further profiling */
+ goto overflow;
+
+ *frompcindex = toindex;

+ top = &tos[toindex];


+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = 0;

+ return;
+ }
+
+ top = &tos[toindex];


+ if (top->selfpc == selfpc) {

+ /* arc at front of chain; usual case. */
+ top->count++;
+ return;
+ }


+
+ /*
+ * have to go looking down chain for it.
+ * top points to what we are looking at,
+ * prevtop points to previous top.
+ * we know it is not at the head of the chain.
+ */
+ for (; /* goto done */; ) {
+ if (top->link == 0) {
+ /*
+ * top is end of the chain and none of the chain
+ * had top->selfpc == selfpc.
+ * so we allocate a new tostruct
+ * and link it to the head of the chain.
+ */

+ toindex = ++tos[0].link;
+ if (toindex >= tolimit)
+ goto overflow;
+
+ top = &tos[toindex];


+ top->selfpc = selfpc;
+ top->count = 1;
+ top->link = *frompcindex;
+ top->link = *frompcindex;
+ *frompcindex = toindex;

+ return;
+ }
+ /* otherwise, check the next arc on the chain. */
+ prevtop = top;
+ top = &tos[top->link];


+ if (top->selfpc == selfpc) {
+ /*
+ * there it is.
+ * increment its count
+ * move it to the head of the chain.
+ */
+ top->count++;
+ toindex = prevtop->link;
+ prevtop->link = top->link;
+ top->link = *frompcindex;
+ *frompcindex = toindex;
+ }

+ }
+ return;
+
+overflow:
+ //ERROR("aprof : overflow");
+ return;
+}
diff --git a/libaprof/libaprof-stubs.c b/libaprof/libaprof-stubs.c
new file mode 100644
index 0000000..43375f7
--- /dev/null
+++ b/libaprof/libaprof-stubs.c
@@ -0,0 +1,7 @@
+/* These are stub functions that are actually defined
+ * in the dynamic linker (aprof.c), and hijacked at runtime.
+ */
+#include <stdint.h>
+void aprof_init(){}
+void aprof_fini(){}
+void aprof_mcount(uintptr_t frompc, uintptr_t selfpc){}
diff --git a/libaprof/libaprof.c b/libaprof/libaprof.c
new file mode 100644
index 0000000..11bbbef
--- /dev/null
+++ b/libaprof/libaprof.c
@@ -0,0 +1,24 @@
+/*
+ * libgmon aux library for register
+ * constructor and destructor for aprof


+ */
+
+#include <stdio.h>

+#include <sys/gmon.h>
+
+void aprof_mcount(uintptr_t frompc, uintptr_t selfpc) __attribute__((weak));
+
+_MCOUNT_DECL(uintptr_t frompc, uintptr_t selfpc) __attribute__((weak));
+
+_MCOUNT_DECL(uintptr_t frompc, uintptr_t selfpc)
+{
+ if (aprof_mcount) {
+ aprof_mcount(frompc, selfpc);
+ }


+}
+
+/*
+ * Actual definition of mcount function. Defined in <machine/profile.h>,
+ * which is included by <sys/gmon.h>.
+ */
+MCOUNT

diff --git a/libaprof/libaprof.h b/libaprof/libaprof.h
new file mode 100644
index 0000000..d937a1b
--- /dev/null
+++ b/libaprof/libaprof.h
@@ -0,0 +1,73 @@
+#ifndef _LIB_APROF_H
+#define _LIB_APROF_H
+
+#include <stdint.h>
+
+#define APROF_TAG "APROF\0\0\0"
+#define APROF_TAG_LENGTH 8
+#define APROF_VERSION 0x1
+
+typedef struct {
+ /* Constant literal "APROF\0\0\0" */
+ char tag[APROF_TAG_LENGTH];
+ /* Indicate the version of aprof file format */
+ uint32_t version;
+ /*
+ * Indicate the sampel rate,
+ * unit is sample times per second.
+ */
+ uint32_t sample_rate;
+ /*
+ * Indicate the pointer size in target,
+ * for future extension.
+ */
+ uint32_t pointer_size;
+} aprof_header;
+
+/*
+ * Histogram records
+ */
+typedef struct {
+ uint32_t img_name_len;
+ char *img_name;
+ /*
+ * Base address for image
+ */
+ uint32_t base;
+ /*
+ * Size of the image size
+ */
+ uint32_t size;
+ /*
+ * bin represent an equal amount of text-space.
+ */
+ uint32_t bin_size;
+ uint16_t *bins;
+} histogram32_header;
+
+/*
+ * Call graph edge
+ */
+typedef struct {
+ uint32_t caller_pc;
+ uint32_t callee_pc;
+ uint32_t count;
+} call_graph32_edge;
+
+/*
+ * Call graph records
+ */
+typedef struct {
+ uint32_t num_edges;
+ call_graph32_edge *edges;
+} call_graph32_header;
+
+/*
+ * Define for header type
+ */
+
+#define APROF_EXECUTABLE_HISTOGRAM_HEADER 0x1
+#define APROF_HISTOGRAM_HEADER 0x2
+#define APROF_CALL_GRAPH_HEADER 0x3
+
+#endif /* _LIB_APROF_H */
diff --git a/libaprof/test/Android.mk b/libaprof/test/Android.mk
new file mode 100644
index 0000000..5a9be8e
--- /dev/null
+++ b/libaprof/test/Android.mk
@@ -0,0 +1,72 @@


+
+LOCAL_PATH:= $(call my-dir)

+#
+# static library for testing profiler
+#
+include $(CLEAR_VARS)
+LOCAL_PRELINK_MODULE := false
+LOCAL_ENABLE_APROF:=true
+LOCAL_SRC_FILES := libprof-test.c
+LOCAL_MODULE := libprof-test
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# shared ibrary for testing profiler
+#
+include $(CLEAR_VARS)
+LOCAL_PRELINK_MODULE := false
+LOCAL_ENABLE_APROF:=true
+LOCAL_SRC_FILES := libprof-test.c
+LOCAL_MODULE := libprof-test
+LOCAL_SHARED_LIBRARIES := libc
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# Test profiler with shared libraries
+#
+include $(CLEAR_VARS)
+LOCAL_ENABLE_APROF:= true
+LOCAL_SRC_FILES := profiler-test.c
+LOCAL_MODULE := profiler-test
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_SHARED_LIBRARIES := libprof-test libc
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
+
+#
+# Test profiler with static link
+#
+include $(CLEAR_VARS)
+LOCAL_ENABLE_APROF:=true
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_SRC_FILES := profiler-test.c
+LOCAL_MODULE := profiler-test-static
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_STATIC_LIBRARIES := libprof-test libc
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
+
+#
+# Test profiler with shared libraries which opened by dlopen
+#
+include $(CLEAR_VARS)
+LOCAL_ENABLE_APROF:= true
+LOCAL_SRC_FILES := profiler-test-dl.c
+LOCAL_MODULE := profiler-test-dl
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_SHARED_LIBRARIES := libc libdl
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
+
+#
+# Test profiling disabled executable link with profiling enabled libraries
+#
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := profiler-test.c
+LOCAL_MODULE := profiler-test-np
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_SHARED_LIBRARIES := libprof-test libc
+LOCAL_MODULE_TAGS := eng
+include $(BUILD_EXECUTABLE)
diff --git a/libaprof/test/libprof-test.c b/libaprof/test/libprof-test.c
new file mode 100644
index 0000000..ed6c8a6
--- /dev/null
+++ b/libaprof/test/libprof-test.c
@@ -0,0 +1,7 @@
+int xfib(int n){
+ if (n<=2) {
+ return 1;
+ } else {
+ return xfib(n-1)+xfib(n-2);
+ }
+}
diff --git a/libaprof/test/profiler-test-dl.c b/libaprof/test/profiler-test-dl.c
new file mode 100644
index 0000000..277d3e8
--- /dev/null
+++ b/libaprof/test/profiler-test-dl.c
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+int (*xfib)(int);
+
+int fib(int n){
+ if (n<=10) {
+ return xfib(n);
+ } else {
+ if (n ==25)
+ printf("fib! %d\n",n);
+ return fib(n-1)+fib(n-2);
+ }
+}
+
+int main(){
+ void *handle = dlopen ("libprof-test.so", RTLD_LAZY);;
+ xfib = dlsym(handle, "xfib");
+ printf("start test profiling !\n");
+ printf("fib(40) =%d\n", fib(40));
+ return 0;
+}
diff --git a/libaprof/test/profiler-test.c b/libaprof/test/profiler-test.c
new file mode 100644
index 0000000..a9ba0a6
--- /dev/null
+++ b/libaprof/test/profiler-test.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int xfib(int n);
+int fib(int n){
+ if (n<=10) {
+ return xfib(n);
+ } else {
+ if (n ==25)
+ printf("fib! %d\n",n);
+ return fib(n-1)+fib(n-2);
+ }
+}
+
+int main(){
+ printf("start test profiling !\n");
+ printf("fib(40) =%d\n", fib(40));
+ return 0;
+}
diff --git a/linker/Android.mk b/linker/Android.mk
index 0cbaf36..fee438b 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -8,7 +8,13 @@ LOCAL_SRC_FILES:= \
rt.c \
dlfcn.c \
debugger.c \
- ba.c
+ ba.c \
+ aprof.c
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ bionic/libaprof
+

ifeq ($(TARGET_ARCH),sh)
# SH-4A series virtual address range from 0x00000000 to 0x7FFFFFFF.
@@ -37,10 +43,15 @@ LOCAL_CFLAGS += -DPRELINK
LOCAL_CFLAGS += -DLINKER_TEXT_BASE=$(LINKER_TEXT_BASE)
LOCAL_CFLAGS += -DLINKER_AREA_SIZE=$(LINKER_AREA_SIZE)

+LOCAL_CFLAGS += -DALLOW_SYMBOLS_FROM_MAIN=1
+
# Set LINKER_DEBUG to either 1 or 0
#
LOCAL_CFLAGS += -DLINKER_DEBUG=0

+# Enable aprof support
+LOCAL_CFLAGS += -DAPROF_SUPPORT
+
# we need to access the Bionic private header <bionic_tls.h>
# in the linker; duplicate the HAVE_ARM_TLS_REGISTER definition
# from the libc build
diff --git a/linker/linker.c b/linker/linker.c
index 4cc33ba..cd0e54a 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -52,7 +52,6 @@

#include "ba.h"

-#define ALLOW_SYMBOLS_FROM_MAIN 1
#define SO_MAX 128

/* Assume average path length of 64 and max 8 paths */
@@ -88,10 +87,15 @@ static int link_image(soinfo *si, unsigned wr_offset);
static int socount = 0;
static soinfo sopool[SO_MAX];
static soinfo *freelist = NULL;
-static soinfo *solist = &libdl_info;
-static soinfo *sonext = &libdl_info;
+
+#ifdef APROF_SUPPORT
+soinfo *solist = &libaprof_info;
+#else
+soinfo *solist = &libdl_info;
+#endif
+soinfo *sonext = &libdl_info;
#if ALLOW_SYMBOLS_FROM_MAIN
-static soinfo *somain; /* main process, always the one after libdl_info */
+soinfo *somain; /* main process, always the one after libdl_info */
#endif


@@ -109,7 +113,11 @@ static struct ba ba_nonprelink = {
static inline int validate_soinfo(soinfo *si)
{
return (si >= sopool && si < sopool + SO_MAX) ||
+#ifdef APROF_SUPPORT
+ si == &libdl_info || si == &libaprof_info;
+#else
si == &libdl_info;
+#endif
}

static char ldpaths_buf[LDPATH_BUFSIZE];
diff --git a/linker/linker.h b/linker/linker.h
index 198f069..b3c65f9 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -29,6 +29,7 @@
#ifndef _LINKER_H_
#define _LINKER_H_

+#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <linux/elf.h>
@@ -160,10 +161,29 @@ struct soinfo

unsigned refcount;
struct link_map linkmap;
+
+#ifdef APROF_SUPPORT
+ uint32_t bin_size;
+ uint16_t *bins;
+
+ uint32_t fromssize;
+ uint16_t *froms;
+ uint32_t tolimit;
+ uint32_t tossize;
+ struct tostruct *tos;
+#endif
};


extern soinfo libdl_info;
+#ifdef APROF_SUPPORT
+extern soinfo libaprof_info;
+#endif
+extern soinfo *solist;
+extern soinfo *sonext;
+#if ALLOW_SYMBOLS_FROM_MAIN
+extern soinfo *somain; /* main process, always the one after libdl_info */
+#endif

/* these must all be powers of two */
#ifdef ARCH_SH
--
1.7.7.3

Reply all
Reply to author
Forward
0 new messages