From ca9784626507a17687080d1d765bb97f4cb9071e Mon Sep 17 00:00:00 2001 From: Martin Foo Date: Mon, 21 Sep 2015 17:28:23 +0800 Subject: [PATCH 1/6] Add android support. --- Android.mk | 44 +++++++++++++ android/build_native.sh | 12 ++++ android/jni/Android.mk | 20 ++++++ android/jni/Application.mk | 8 +++ android/jni/speex-jni.cpp | 128 +++++++++++++++++++++++++++++++++++++ android/jni/speex.cbp | 60 +++++++++++++++++ 6 files changed, 272 insertions(+) create mode 100644 Android.mk create mode 100644 android/build_native.sh create mode 100644 android/jni/Android.mk create mode 100644 android/jni/Application.mk create mode 100644 android/jni/speex-jni.cpp create mode 100644 android/jni/speex.cbp diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..7af1739d --- /dev/null +++ b/Android.mk @@ -0,0 +1,44 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libspeex +LOCAL_CFLAGS := -DHAVE_CONFIG_H +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_ARM_MODE := arm + +LOCAL_SRC_FILES := \ + libspeex/bits.c \ + libspeex/cb_search.c \ + libspeex/exc_10_32_table.c \ + libspeex/exc_10_16_table.c \ + libspeex/exc_20_32_table.c \ + libspeex/exc_5_256_table.c \ + libspeex/exc_5_64_table.c \ + libspeex/exc_8_128_table.c \ + libspeex/filters.c \ + libspeex/gain_table.c \ + libspeex/hexc_table.c \ + libspeex/high_lsp_tables.c \ + libspeex/lsp.c \ + libspeex/ltp.c \ + libspeex/speex.c \ + libspeex/stereo.c \ + libspeex/vbr.c \ + libspeex/vq.c \ + libspeex/gain_table_lbr.c \ + libspeex/hexc_10_32_table.c \ + libspeex/lpc.c \ + libspeex/lsp_tables_nb.c \ + libspeex/modes.c \ + libspeex/modes_wb.c \ + libspeex/nb_celp.c \ + libspeex/quant_lsp.c \ + libspeex/sb_celp.c \ + libspeex/speex_callbacks.c \ + libspeex/speex_header.c \ + libspeex/window.c + +include $(BUILD_STATIC_LIBRARY) + diff --git a/android/build_native.sh b/android/build_native.sh new file mode 100644 index 00000000..88e59859 --- /dev/null +++ b/android/build_native.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +cd .. + +./configure --host=arm --enable-arm5e-asm -enable-fixed-point -disable-float-api + +cd android + +# force rebuild libs +ndk-build -B V=1 -j4 + +ls -lR libs diff --git a/android/jni/Android.mk b/android/jni/Android.mk new file mode 100644 index 00000000..afa3a593 --- /dev/null +++ b/android/jni/Android.mk @@ -0,0 +1,20 @@ +LOCAL_PATH := $(call my-dir) + +SPEEX_PATH=$(LOCAL_PATH)/../.. + +include $(CLEAR_VARS) + +LOCAL_MODULE := libspeex-jni + +LOCAL_CFLAGS += -Wall -fvisibility=hidden +LOCAL_C_INCLUDES := $(SPEEX_PATH)/include + +LOCAL_SRC_FILES := speex-jni.cpp +LOCAL_STATIC_LIBRARIES := libspeex + +LOCAL_LDLIBS := -llog +LOCAL_LDFLAGS := -Wl,--as-needed + +include $(BUILD_SHARED_LIBRARY) + +include $(SPEEX_PATH)/Android.mk \ No newline at end of file diff --git a/android/jni/Application.mk b/android/jni/Application.mk new file mode 100644 index 00000000..cf89f6f0 --- /dev/null +++ b/android/jni/Application.mk @@ -0,0 +1,8 @@ +# The ARMv7 is significanly faster due to the use of the hardware FPU +# APP_ABI := armeabi-v7a armeabi x86 mips +APP_ABI := armeabi-v7a +APP_PLATFORM := android-8 + +APP_STL := gnustl_static + +APP_CPPFLAGS += -fexceptions diff --git a/android/jni/speex-jni.cpp b/android/jni/speex-jni.cpp new file mode 100644 index 00000000..c2782e0b --- /dev/null +++ b/android/jni/speex-jni.cpp @@ -0,0 +1,128 @@ +/* + * ComicsReader is an Android application to read comics + * Copyright (C) 2011-2015 Cedric OCHS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include + +#include +#include + +#include + +static int codec_open = 0; + +static int dec_frame_size; +static int enc_frame_size; + +static SpeexBits ebits, dbits; +void *enc_state; +void *dec_state; + +static JavaVM *gJavaVM; + +extern "C" +JNIEXPORT jint JNICALL Java_com_audio_Speex_open + (JNIEnv *env, jobject obj, jint compression) { + int tmp; + + if (codec_open++ != 0) + return (jint)0; + + speex_bits_init(&ebits); + speex_bits_init(&dbits); + + enc_state = speex_encoder_init(&speex_nb_mode); + dec_state = speex_decoder_init(&speex_nb_mode); + tmp = compression; + speex_encoder_ctl(enc_state, SPEEX_SET_QUALITY, &tmp); + speex_encoder_ctl(enc_state, SPEEX_GET_FRAME_SIZE, &enc_frame_size); + speex_decoder_ctl(dec_state, SPEEX_GET_FRAME_SIZE, &dec_frame_size); + + return (jint)0; +} + +extern "C" +JNIEXPORT jint Java_com_audio_Speex_encode + (JNIEnv *env, jobject obj, jshortArray lin, jint offset, jbyteArray encoded, jint size) { + + jshort buffer[enc_frame_size]; + jbyte output_buffer[enc_frame_size]; + int nsamples = (size-1)/enc_frame_size + 1; + int i, tot_bytes = 0; + + if (!codec_open) + return 0; + + speex_bits_reset(&ebits); + + for (i = 0; i < nsamples; i++) { + env->GetShortArrayRegion(lin, offset + i*enc_frame_size, enc_frame_size, buffer); + speex_encode_int(enc_state, buffer, &ebits); + } + //env->GetShortArrayRegion(lin, offset, enc_frame_size, buffer); + //speex_encode_int(enc_state, buffer, &ebits); + + tot_bytes = speex_bits_write(&ebits, (char *)output_buffer, + enc_frame_size); + env->SetByteArrayRegion(encoded, 0, tot_bytes, + output_buffer); + + return (jint)tot_bytes; +} + +extern "C" +JNIEXPORT jint JNICALL Java_com_audio_Speex_decode + (JNIEnv *env, jobject obj, jbyteArray encoded, jshortArray lin, jint size) { + + jbyte buffer[dec_frame_size]; + jshort output_buffer[dec_frame_size]; + jsize encoded_length = size; + + if (!codec_open) + return 0; + + env->GetByteArrayRegion(encoded, 0, encoded_length, buffer); + speex_bits_read_from(&dbits, (char *)buffer, encoded_length); + speex_decode_int(dec_state, &dbits, output_buffer); + env->SetShortArrayRegion(lin, 0, dec_frame_size, + output_buffer); + + return (jint)dec_frame_size; +} + +extern "C" +JNIEXPORT jint JNICALL Java_com_audio_getFrameSize + (JNIEnv *env, jobject obj) { + + if (!codec_open) + return 0; + return (jint)enc_frame_size; + +} + +extern "C" +JNIEXPORT void JNICALL Java_com_audio_Speex_close + (JNIEnv *env, jobject obj) { + + if (--codec_open != 0) + return; + + speex_bits_destroy(&ebits); + speex_bits_destroy(&dbits); + speex_decoder_destroy(dec_state); + speex_encoder_destroy(enc_state); +} diff --git a/android/jni/speex.cbp b/android/jni/speex.cbp new file mode 100644 index 00000000..96534812 --- /dev/null +++ b/android/jni/speex.cbp @@ -0,0 +1,60 @@ + + + + + + From 15dfd10d0b1281d4d940c854890a04cb25ea5abf Mon Sep 17 00:00:00 2001 From: Martin Foo Date: Mon, 21 Sep 2015 17:40:28 +0800 Subject: [PATCH 2/6] Add export CC. --- android/build_native.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/build_native.sh b/android/build_native.sh index 88e59859..daedafaf 100644 --- a/android/build_native.sh +++ b/android/build_native.sh @@ -2,6 +2,8 @@ cd .. +export CC=arm-linux-androideabi-gcc + ./configure --host=arm --enable-arm5e-asm -enable-fixed-point -disable-float-api cd android From 28c87fcaaea0f2d8d2e78a25348b44decd8a0370 Mon Sep 17 00:00:00 2001 From: Martin Foo Date: Mon, 21 Sep 2015 22:40:11 +0800 Subject: [PATCH 3/6] add x86 support. --- android/build_native.sh | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) mode change 100644 => 100755 android/build_native.sh diff --git a/android/build_native.sh b/android/build_native.sh old mode 100644 new mode 100755 index daedafaf..e2474fd2 --- a/android/build_native.sh +++ b/android/build_native.sh @@ -1,14 +1,37 @@ #!/bin/sh +mkdir ../dist + +# for arm cd .. export CC=arm-linux-androideabi-gcc -./configure --host=arm --enable-arm5e-asm -enable-fixed-point -disable-float-api +./configure --host=arm --enable-arm5e-asm --enable-fixed-point --disable-float-api + +cd android + +ndk-build -B V=1 -j4 APP_ABI=armeabi-v7a + +ls -lR libs + +cp ../config.h ../dist/config_arm.h +cp libs/armeabi-v7a/libspeex-jni.so ../dist/libspeex-jni_arm.so +cp obj/local/armeabi-v7a/libspeex.a ../dist/libspeex_arm.a + +# for x86 +cd .. + +export CC=i686-linux-android-gcc + +./configure --host=arm --enable-sse cd android -# force rebuild libs -ndk-build -B V=1 -j4 +ndk-build -B V=1 -j4 APP_ABI=x86 ls -lR libs + +cp ../config.h ../dist/config_x86.h +cp libs/x86/libspeex-jni.so ../dist/libspeex-jni_x86.so +cp obj/local/x86/libspeex.a ../dist/libspeex_x86.a From b966fd14777c277c1bcdbdd10f39be05bd49805a Mon Sep 17 00:00:00 2001 From: Martin Foo Date: Thu, 24 Sep 2015 16:05:51 +0800 Subject: [PATCH 4/6] add codec and profiler --- android/jni/profiler/gmon.h | 148 +++++++++ android/jni/profiler/gmon_out.h | 45 +++ android/jni/profiler/gnu_mcount.S | 28 ++ android/jni/profiler/prof.c | 533 ++++++++++++++++++++++++++++++ android/jni/profiler/prof.h | 22 ++ android/jni/profiler/read_maps.c | 96 ++++++ android/jni/profiler/read_maps.h | 24 ++ 7 files changed, 896 insertions(+) create mode 100644 android/jni/profiler/gmon.h create mode 100644 android/jni/profiler/gmon_out.h create mode 100644 android/jni/profiler/gnu_mcount.S create mode 100644 android/jni/profiler/prof.c create mode 100644 android/jni/profiler/prof.h create mode 100644 android/jni/profiler/read_maps.c create mode 100644 android/jni/profiler/read_maps.h diff --git a/android/jni/profiler/gmon.h b/android/jni/profiler/gmon.h new file mode 100644 index 00000000..7f1c3335 --- /dev/null +++ b/android/jni/profiler/gmon.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 1983, 1991, 1993, 2001 + * 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. + */ +#ifndef gmon_h +#define gmon_h + +/* Size of the 4.4BSD gmon header */ +#define GMON_HDRSIZE_BSD44_32 (4 + 4 + 4 + 4 + 4 + (3 * 4)) +#define GMON_HDRSIZE_BSD44_64 (8 + 8 + 4 + 4 + 4 + (3 * 4)) + +#if 0 /* For documentation purposes only. */ + struct raw_phdr + { + char low_pc[sizeof(void *)]; /* base pc address of sample buffer */ + char high_pc[sizeof(void *)];/* max pc address of sampled buffer */ + char ncnt[4]; /* size of sample buffer (plus this + header) */ + + char version[4]; /* version number */ + char profrate[4]; /* profiling clock rate */ + char spare[3*4]; /* reserved */ + }; +#endif + +#define GMONVERSION 0x00051879 + +/* Size of the old BSD gmon header */ +#define GMON_HDRSIZE_OLDBSD_32 (4 + 4 + 4) + +/* FIXME: Checking host compiler defines here means that we can't + use a cross gprof alpha OSF. */ +#if defined(__alpha__) && defined (__osf__) +#define GMON_HDRSIZE_OLDBSD_64 (8 + 8 + 4 + 4) +#else +#define GMON_HDRSIZE_OLDBSD_64 (8 + 8 + 4) +#endif + +#if 0 /* For documentation purposes only. */ + struct old_raw_phdr + { + char low_pc[sizeof(void *)]; /* base pc address of sample buffer */ + char high_pc[sizeof(void *)];/* max pc address of sampled buffer */ + char ncnt[4]; /* size of sample buffer (plus this + header) */ +#if defined (__alpha__) && defined (__osf__) + /* + * DEC's OSF v3.0 uses 4 bytes of padding to bring the header to + * a size that is a multiple of 8. + */ + char pad[4]; +#endif + }; +#endif + +/* + * Histogram counters are unsigned shorts: + */ +#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 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! + */ +#define HASHFRACTION 1 + +/* + * Percent of text space to allocate for tostructs with a minimum: + */ +#define ARCDENSITY 2 +#define MINARCS 50 + +struct tostruct + { + char *selfpc; + int count; + unsigned short link; + }; + +/* + * A raw arc, with pointers to the calling site and the called site + * and a count. Everything is defined in terms of characters so + * as to get a packed representation (otherwise, different compilers + * might introduce different padding): + */ +#if 0 /* For documentation purposes only. */ + struct raw_arc + { + char from_pc[sizeof(void *)]; + char self_pc[sizeof(void *)]; + char count[sizeof(long)]; + }; +#endif + +/* + * General rounding functions: + */ +#define ROUNDDOWN(x,y) (((x)/(y))*(y)) +#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) + +#endif /* gmon_h */ diff --git a/android/jni/profiler/gmon_out.h b/android/jni/profiler/gmon_out.h new file mode 100644 index 00000000..25dce59d --- /dev/null +++ b/android/jni/profiler/gmon_out.h @@ -0,0 +1,45 @@ +/* gmon_out.h + + Copyright 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU Binutils. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* A gmon.out file consists of a header (defined by gmon_hdr) followed + by a sequence of records. Each record starts with a one-byte tag + identifying the type of records, followed by records specific data. */ +#ifndef gmon_out_h +#define gmon_out_h + +#define GMON_MAGIC "gmon" /* magic cookie */ +#define GMON_VERSION 1 /* version number */ + +/* Raw header as it appears on file (without padding). */ +struct gmon_hdr + { + char cookie[4]; + char version[4]; + char spare[3 * 4]; + }; + +/* Types of records in this file. */ +typedef enum + { + GMON_TAG_TIME_HIST = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2 + } +GMON_Record_Tag; + +#endif /* gmon_out_h */ diff --git a/android/jni/profiler/gnu_mcount.S b/android/jni/profiler/gnu_mcount.S new file mode 100644 index 00000000..e965e99e --- /dev/null +++ b/android/jni/profiler/gnu_mcount.S @@ -0,0 +1,28 @@ +/* + * Part of the android-ndk-profiler library. + * Copyright (C) Richard Quirk + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +.align 2 +.thumb_func +.global __gnu_mcount_nc +.type __gnu_mcount_nc,function + +__gnu_mcount_nc: + push {r0-r3} + push {lr} + ldr r0, [sp, #20] @ r0 = lr pushed by calling routine + mov r1, lr @ address of calling routine + bl profCount + pop {r2} @ this routine's return address + pop {r0, r1} + @ stack contains r2, r3 and lr + ldr r3, [sp , #8] @ r3 = lr pushed by calling routine + str r2, [sp, #8] @ return address now last on the stack + mov lr, r3 @ lr = caller's expected lr + pop {r2, r3} + pop {pc} @ pop caller's expected r2, r3 and return diff --git a/android/jni/profiler/prof.c b/android/jni/profiler/prof.c new file mode 100644 index 00000000..fb30c255 --- /dev/null +++ b/android/jni/profiler/prof.c @@ -0,0 +1,533 @@ +/* adapted from VisualBoyAdvance, which had the following notice.*/ +/* VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. + * Copyright (C) 1999-2003 Forgotten + * Copyright (C) 2004 Forgotten and the VBA development team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or(at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* adapted from gmon.c */ +/*- + * Copyright (c) 1991, 1998 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. [rescinded 22 July 1999] + * 4. 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 /* for __android_log_print, ANDROID_LOG_INFO, etc */ +#include /* for errno */ +#include /* for sigaction, etc */ +#include /* for uint32_t, uint16_t, etc */ +#include /* for FILE */ +#include /* for getenv */ +#include /* for setitimer, etc */ + +#include "gmon.h" +#include "gmon_out.h" +#include "prof.h" +#include "read_maps.h" + +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "PROFILING", __VA_ARGS__) + +#define DEFAULT_GMON_OUT "/sdcard/gmon.out" + +typedef struct { + unsigned short *froms; + struct tostruct *tos; + long tolimit; +} callgraph_t; + +typedef struct { + size_t low_pc; + size_t high_pc; + size_t text_size; +} process_t; + +typedef struct { + uint32_t nb_bins; + char *bins; +} histogram_t; + +static histogram_t hist; +static process_t process; +static callgraph_t cg; +static struct proc_map *s_maps = NULL; +int opt_is_shared_lib = 0; + +static void systemMessage(int a, const char *msg) +{ + LOGI("%d: %s", a, msg); +} + +static void profPut32(char *b, uint32_t v) +{ + b[0] = v & 255; + b[1] = (v >> 8) & 255; + b[2] = (v >> 16) & 255; + b[3] = (v >> 24) & 255; +} + +static void profPut16(char *b, uint16_t v) +{ + b[0] = v & 255; + b[1] = (v >> 8) & 255; +} + +static int profWrite8(FILE *f, uint8_t b) +{ + if (fwrite(&b, 1, 1, f) != 1) { + return 1; + } + return 0; +} + +static int profWrite32(FILE *f, uint32_t v) +{ + char buf[4]; + profPut32(buf, v); + if (fwrite(buf, 1, 4, f) != 4) { + return 1; + } + return 0; +} + +static int profWrite(FILE *f, char *buf, unsigned int n) +{ + if (fwrite(buf, 1, n, f) != n) { + return 1; + } + return 0; +} + +static int get_max_samples_per_sec() +{ + struct itimerval timer; + + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + setitimer(ITIMER_PROF, &timer, 0); + setitimer(ITIMER_PROF, 0, &timer); + return 1000000 / timer.it_interval.tv_usec; +} + +static void histogram_bin_incr(int sig, siginfo_t *info, void *context) +{ + ucontext_t *ucontext = (ucontext_t *) context; + struct sigcontext *mcontext = &ucontext->uc_mcontext; + uint32_t frompcindex = mcontext->arm_pc; + + uint16_t *b = (uint16_t *) hist.bins; + + /* the pc should be divided by HISTFRACTION, but we do */ + /* a right shift with 1 because HISTFRACTION=2 */ + size_t pc = (frompcindex - process.low_pc) >> 1; + + if (pc < hist.nb_bins) { + b[pc]++; + } +} + +static void add_profile_handler(int sample_freq) +{ + struct sigaction action; + /* request info, sigaction called instead of sighandler */ + action.sa_flags = SA_SIGINFO | SA_RESTART; + action.sa_sigaction = histogram_bin_incr; + sigemptyset(&action.sa_mask); + int result = sigaction(SIGPROF, &action, NULL); + if (result != 0) { + /* panic */ + LOGI("add_profile_handler, sigaction failed %d %d", result, + errno); + return; + } + + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1000000 / sample_freq; + timer.it_value = timer.it_interval; + setitimer(ITIMER_PROF, &timer, 0); +} + +static long remove_profile_handler(void) +{ + struct itimerval timer; + struct itimerval oldtimer; + + memset(&timer, 0, sizeof(timer)); + setitimer(ITIMER_PROF, &timer, &oldtimer); + + return oldtimer.it_value.tv_usec; +} + +static int select_frequency() +{ + int max_samples = get_max_samples_per_sec(); + char *freq = getenv("CPUPROFILE_FREQUENCY"); + + if (!freq) { + LOGI("using sample frequency: %d", max_samples); + return max_samples; + } + + int freqval = strtol(freq, 0, 0); + + if (freqval <= 0) { + LOGI("Invalid frequency value: %d, using default: %d", + freqval, max_samples); + return max_samples; + } + + LOGI("Maximum number of samples per second: %d", max_samples); + LOGI("Specified frequency: %d", freqval); + + if (freqval > max_samples) { + LOGI("Specified sample rate is too large, using %d", + max_samples); + return max_samples; + } + + return freqval; +} + +static void process_init() +{ + process.low_pc = s_maps->lo; + process.high_pc = s_maps->hi; + /* + * 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. + */ + process.low_pc = ROUNDDOWN(process.low_pc, HISTFRACTION * sizeof(HISTCOUNTER)); + process.high_pc = ROUNDUP(process.high_pc, HISTFRACTION * sizeof(HISTCOUNTER)); + process.text_size = process.high_pc - process.low_pc; +} + +#define MSG ("No space for profiling buffer(s)\n") + +static int histogram_init() +{ + hist.nb_bins = (process.text_size / HISTFRACTION); + + /* FIXME: check if '2' is the size of short or if it has another meaning */ + hist.bins = calloc(1, sizeof(short) * hist.nb_bins); + if (!hist.bins) { + systemMessage(0, MSG); + return 0; + } + return 1; +} + +static int cg_init() +{ + /* FIXME: what should be the size of 'froms' */ + /* froms = calloc(1, 4 * process.text_size / HASHFRACTION); */ + cg.froms = calloc(1, sizeof(short) * hist.nb_bins); + + if (cg.froms == NULL) { + systemMessage(0, MSG); + free(hist.bins); + return 0; + } + + cg.tolimit = process.text_size * ARCDENSITY / 100; + + if (cg.tolimit < MINARCS) { + cg.tolimit = MINARCS; + } else if (cg.tolimit > 65534) { + cg.tolimit = 65534; + } + + cg.tos = (struct tostruct *) calloc(1, cg.tolimit * sizeof(struct tostruct)); + if (cg.tos == NULL) { + systemMessage(0, MSG); + free(hist.bins); + free(cg.froms); + cg.froms = NULL; + return 0; + } + cg.tos[0].link = 0; + + return 1; +} + +__attribute__((visibility("default"))) +void monstartup(const char *libname) +{ + FILE *self = fopen("/proc/self/maps", "r"); + + if (!self) { + systemMessage(1, "Cannot open memory maps file"); + return; + } + + if (strstr(libname, ".so")) { + LOGI("start profiling shared library %s", libname); + opt_is_shared_lib = 1; + } else { + LOGI("start profiling executable %s", libname); + opt_is_shared_lib = 0; + } + + s_maps = read_maps(self, libname); + + if (s_maps == NULL) { + systemMessage(0, "No maps found"); + return; + } + + process_init(); + + if (!histogram_init()) { + return; + } + + LOGI("Profile %s, pc: 0x%x-0x%x, base: 0x%d", libname, + process.low_pc, process.high_pc, s_maps->base); + + if (!cg_init()) { + return; + } + + int sample_freq = select_frequency(); + add_profile_handler(sample_freq); +} + +static const char *get_gmon_out(void) +{ + char *gmon_out = getenv("CPUPROFILE"); + if (gmon_out && strlen(gmon_out)) + return gmon_out; + return DEFAULT_GMON_OUT; +} + +__attribute__((visibility("default"))) +void moncleanup(void) +{ + FILE *fd; + int fromindex; + int endfrom; + uint32_t frompc; + int toindex; + struct gmon_hdr ghdr; + const char *gmon_name = get_gmon_out(); + + long ival = remove_profile_handler(); + int sample_freq = 1000000 / ival; + + LOGI("Sampling frequency: %d", sample_freq); + LOGI("moncleanup, writing output to %s", gmon_name); + + fd = fopen(gmon_name, "wb"); + if (fd == NULL) { + systemMessage(0, "mcount: gmon.out"); + return; + } + memcpy(&ghdr.cookie[0], GMON_MAGIC, 4); + profPut32((char *) ghdr.version, GMON_VERSION); + if (fwrite(&ghdr, sizeof(ghdr), 1, fd) != 1) { + systemMessage(0, "mcount: gmon.out header"); + fclose(fd); + return; + } + + if (profWrite8(fd, GMON_TAG_TIME_HIST) + || profWrite32(fd, get_real_address(s_maps, (uint32_t) process.low_pc)) + || profWrite32(fd, get_real_address(s_maps, (uint32_t) process.high_pc)) + || profWrite32(fd, hist.nb_bins) + || profWrite32(fd, sample_freq) + || profWrite(fd, "seconds", 15) + || profWrite(fd, "s", 1) + ) { + systemMessage(0, "ERROR writing mcount: gmon.out hist"); + fclose(fd); + return; + } + uint16_t *hist_sample = (uint16_t *) hist.bins; + uint16_t count; + int i; + for (i = 0; i < hist.nb_bins; ++i) { + profPut16((char *) &count, hist_sample[i]); + /* LOGI("bin: %d, value: %d", i, hist_sample[i]); */ + if (fwrite(&count, sizeof(count), 1, fd) != 1) { + systemMessage(0, "ERROR writing file mcount: gmon.out sample"); + fclose(fd); + return; + } + } + endfrom = process.text_size / (HASHFRACTION * sizeof(*cg.froms)); + for (fromindex = 0; fromindex < endfrom; fromindex++) { + if (cg.froms[fromindex] == 0) { + continue; + } + frompc = + process.low_pc + (fromindex * HASHFRACTION * sizeof(*cg.froms)); + frompc = get_real_address(s_maps, frompc); + for (toindex = cg.froms[fromindex]; toindex != 0; + toindex = cg.tos[toindex].link) { + if (profWrite8(fd, GMON_TAG_CG_ARC) + || profWrite32(fd, (uint32_t) frompc) + || profWrite32(fd, + get_real_address(s_maps, + (uint32_t) + cg.tos[toindex]. + selfpc)) + || profWrite32(fd, cg.tos[toindex].count)) { + systemMessage(0, "ERROR writing mcount: arc"); + fclose(fd); + return; + } + } + } + fclose(fd); +} + +void profCount(size_t *frompcindex, char *selfpc) +{ + struct tostruct *top; + struct tostruct *prevtop; + size_t toindex; + /* + * find the return address for mcount, + * and the return address for mcount's caller. + */ + /* selfpc = pc pushed by mcount call. + This identifies the function that was just entered. */ + /*selfpc = (char *) reg[14].I; */ + /* frompcindex = pc in preceding frame. + This identifies the caller of the function just entered. */ + /*frompcindex = (unsigned short *) reg[12].I; */ + /* + * check that we are profiling + * and that we aren't recursively invoked. + */ + + /* + * check that frompcindex is a reasonable pc value. + * for example: signal catchers get called from the stack, + * not from text space. too bad. + */ + + /* frompcindex = (size_t *) ( (size_t) frompcindex - process.low_pc); */ + + size_t frompc_val = (size_t) frompcindex - process.low_pc; + size_t *frompc_ptr = (size_t *) frompc_val; + + if (frompc_val > process.text_size) { + return; + } + + frompc_ptr = (size_t *) &cg.froms[frompc_val / (HASHFRACTION * sizeof(*cg.froms))]; + toindex = *frompc_ptr; + + if (toindex == 0) { + /* + * first time traversing this arc + */ + toindex = ++cg.tos[0].link; + if (toindex >= cg.tolimit) { + goto overflow; + } + *frompc_ptr = (size_t) toindex; + top = &cg.tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = 0; + return; + } + top = &cg.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 = ++cg.tos[0].link; + if (toindex >= cg.tolimit) { + goto overflow; + } + top = &cg.tos[toindex]; + top->selfpc = selfpc; + top->count = 1; + top->link = *frompc_ptr; + *frompc_ptr = (size_t) toindex; + return; + } + /* + * otherwise, check the next arc on the chain. + */ + prevtop = top; + top = &cg.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 = *frompc_ptr; + *frompc_ptr = (size_t) toindex; + return; + } + } + +out: + return; /* normal return restores saved registers */ +overflow: + systemMessage(0, "mcount: tos overflow\n"); + goto out; + +} diff --git a/android/jni/profiler/prof.h b/android/jni/profiler/prof.h new file mode 100644 index 00000000..24486127 --- /dev/null +++ b/android/jni/profiler/prof.h @@ -0,0 +1,22 @@ +/* + * Part of the android-ndk-profiler library. + * Copyright (C) Richard Quirk + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#ifndef prof_h_seen +#define prof_h_seen +#ifdef __cplusplus +extern "C" { +#endif + +void monstartup(const char *libname); +void moncleanup(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/jni/profiler/read_maps.c b/android/jni/profiler/read_maps.c new file mode 100644 index 00000000..598370dd --- /dev/null +++ b/android/jni/profiler/read_maps.c @@ -0,0 +1,96 @@ +/* + * Part of the android-ndk-profiler library. + * Copyright (C) Richard Quirk + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include "read_maps.h" + +#ifdef ANDROID +#include /* for __android_log_print, ANDROID_LOG_INFO, etc */ +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "PROFILING", __VA_ARGS__) +#else +#define LOGI(...) do { printf(__VA_ARGS__) ; printf("\n"); } while (0) +#endif + +static char s_line[256]; +extern int opt_is_shared_lib; + +void free_maps(struct proc_map *s) +{ + struct proc_map *next = s->next; + while (next != NULL) { + struct proc_map *tmp = next; + next = next->next; + free(tmp); + } + free(s); +} + +struct proc_map *read_maps(FILE *fp, const char *lname) +{ + struct proc_map *results = NULL; + struct proc_map *current = NULL; + size_t namelen = strlen(lname); + + while (fgets(s_line, sizeof(s_line), fp) != NULL) { + size_t len = strlen(s_line); + len--; + s_line[len] = 0; + + if (namelen < len + && strcmp(lname, &s_line[len - namelen]) == 0) { + char c[1]; + char perm[4]; + int lo, base, hi; + sscanf(s_line, "%x-%x %4c %x %c", &lo, &hi, perm, &base, c); + + if (results == NULL) { + current = malloc(sizeof(struct proc_map)); + if (!current) { + LOGI("error allocating memory"); + return NULL; + } + current->next = NULL; + results = current; + } else { + current->next = malloc(sizeof(struct proc_map)); + current = current->next; + if (!current) { + LOGI("error allocating memory"); + return NULL; + } + current->next = NULL; + } + + LOGI("process '%s', base = 0x%x, lo = 0x%x, hi = 0x%x", lname, base, lo, hi); + + current->base = base; + current->lo = lo; + current->hi = hi; + } + } + return results; +} + +unsigned int get_real_address(const struct proc_map *maps, + unsigned int fake) +{ + const struct proc_map *mp = maps; + while (mp) { + if (fake >= mp->lo && fake <= mp->hi) { + if (opt_is_shared_lib) { + return fake - mp->lo; + } + return fake; + } + mp = mp->next; + } + return fake; +} diff --git a/android/jni/profiler/read_maps.h b/android/jni/profiler/read_maps.h new file mode 100644 index 00000000..9c78b66d --- /dev/null +++ b/android/jni/profiler/read_maps.h @@ -0,0 +1,24 @@ +/* + * Part of the android-ndk-profiler library. + * Copyright (C) Richard Quirk + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +#ifndef read_maps_h_seen +#define read_maps_h_seen + +struct proc_map { + unsigned int base; + unsigned int lo; + unsigned int hi; + struct proc_map *next; +}; + +struct proc_map *read_maps(FILE *fp, const char *lname); +void free_maps(struct proc_map *s); +unsigned int get_real_address(const struct proc_map *maps, unsigned int fake); + +#endif From d90c62dedc436b53930b4dd2519348e31b35a05c Mon Sep 17 00:00:00 2001 From: Martin Foo Date: Thu, 24 Sep 2015 16:08:03 +0800 Subject: [PATCH 5/6] add codec and profiler --- android/jni/Android.mk | 76 ++++++++++++++++++++++++++++++++++++++++-- src/speexdec.c | 13 ++++++++ src/speexenc.c | 14 ++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/android/jni/Android.mk b/android/jni/Android.mk index afa3a593..7cdca582 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -1,7 +1,21 @@ LOCAL_PATH := $(call my-dir) -SPEEX_PATH=$(LOCAL_PATH)/../.. +SPEEX_PATH=$(realpath $(LOCAL_PATH)/../..) +#$(info $(SPEEX_PATH)) +ifeq ($(NDK_DEBUG),1) + +# for profiler when debug! +include $(CLEAR_VARS) + +LOCAL_CFLAGS := -fvisibility=hidden -DDEBUG +LOCAL_MODULE := android-ndk-profiler +LOCAL_SRC_FILES := profiler/gnu_mcount.S profiler/prof.c profiler/read_maps.c + +include $(BUILD_STATIC_LIBRARY) +endif + +# for speex-jni include $(CLEAR_VARS) LOCAL_MODULE := libspeex-jni @@ -17,4 +31,62 @@ LOCAL_LDFLAGS := -Wl,--as-needed include $(BUILD_SHARED_LIBRARY) -include $(SPEEX_PATH)/Android.mk \ No newline at end of file +# for speexenc +include $(CLEAR_VARS) + +LOCAL_MODULE := speexenc + +LOCAL_CFLAGS += -Wall -fvisibility=hidden + +# add debug info +ifeq ($(NDK_DEBUG),1) +LOCAL_CFLAGS += -pg -fno-omit-frame-pointer -DENABLE_PROFILER +endif + +LOCAL_C_INCLUDES := $(SPEEX_PATH)/include + +LOCAL_SRC_FILES := $(SPEEX_PATH)/src/speexenc.c \ + $(SPEEX_PATH)/src/skeleton.c \ + $(SPEEX_PATH)/src/wav_io.c + +LOCAL_STATIC_LIBRARIES := libspeex +# add profiler static library +ifeq ($(NDK_DEBUG),1) +LOCAL_STATIC_LIBRARIES += libandroid-ndk-profiler +endif + +LOCAL_LDLIBS := -llog +LOCAL_LDFLAGS := -Wl,--as-needed -logg + +include $(BUILD_EXECUTABLE) + +# for speexdec +include $(CLEAR_VARS) + +LOCAL_MODULE := speexdec + +LOCAL_CFLAGS += -Wall -fvisibility=hidden + +# add debug info +ifeq ($(NDK_DEBUG),1) +LOCAL_CFLAGS += -pg -fno-omit-frame-pointer -DENABLE_PROFILER +endif + +LOCAL_C_INCLUDES := $(SPEEX_PATH)/include + +LOCAL_SRC_FILES := $(SPEEX_PATH)/src/speexdec.c \ + $(SPEEX_PATH)/src/skeleton.c \ + $(SPEEX_PATH)/src/wav_io.c + +LOCAL_STATIC_LIBRARIES := libspeex +# add profiler static library +ifeq ($(NDK_DEBUG),1) +LOCAL_STATIC_LIBRARIES += libandroid-ndk-profiler +endif + +LOCAL_LDLIBS := -llog +LOCAL_LDFLAGS := -Wl,--as-needed -logg + +include $(BUILD_EXECUTABLE) + +include $(SPEEX_PATH)/Android.mk diff --git a/src/speexdec.c b/src/speexdec.c index 4721dc1c..c4398db3 100644 --- a/src/speexdec.c +++ b/src/speexdec.c @@ -420,8 +420,16 @@ static void *process_header(ogg_packet *op, spx_int32_t enh_enabled, spx_int32_t return st; } +#ifdef ENABLE_PROFILER +#include "profiler/prof.h" +#endif int main(int argc, char **argv) { +#ifdef ENABLE_PROFILER + /* in the start-up code */ + monstartup("speexdec"); +#endif + int c; int option_index = 0; char *inFile, *outFile; @@ -803,5 +811,10 @@ int main(int argc, char **argv) if (fout != NULL) fclose(fout); +#ifdef ENABLE_PROFILER +/* in the onPause or shutdown code */ +moncleanup(); +#endif + return 0; } diff --git a/src/speexenc.c b/src/speexenc.c index 52733399..2da54fed 100644 --- a/src/speexenc.c +++ b/src/speexenc.c @@ -271,9 +271,17 @@ void usage() printf ("Please report bugs to the mailing list `speex-dev@xiph.org'.\n"); } +#ifdef ENABLE_PROFILER +#include "profiler/prof.h" +#endif int main(int argc, char **argv) { +#ifdef ENABLE_PROFILER +/* in the start-up code */ +monstartup("speexenc"); +#endif + int nb_samples, total_samples=0, nb_encoded; int c; int option_index = 0; @@ -968,6 +976,12 @@ int main(int argc, char **argv) fclose(fin); if (close_out) fclose(fout); + +#ifdef ENABLE_PROFILER +/* in the onPause or shutdown code */ +moncleanup(); +#endif + return 0; } From e6306c4036a270034300a30301d02135a826c4a4 Mon Sep 17 00:00:00 2001 From: fufz Date: Thu, 20 Oct 2016 20:35:58 +0800 Subject: [PATCH 6/6] Update build scripts for android. --- android/build_native.sh | 14 ++++++++------ android/jni/Application.mk | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/android/build_native.sh b/android/build_native.sh index e2474fd2..b2eafc22 100755 --- a/android/build_native.sh +++ b/android/build_native.sh @@ -1,6 +1,8 @@ #!/bin/sh mkdir ../dist +mkdir ../dist/arm +mkdir ../dist/x86 # for arm cd .. @@ -15,9 +17,9 @@ ndk-build -B V=1 -j4 APP_ABI=armeabi-v7a ls -lR libs -cp ../config.h ../dist/config_arm.h -cp libs/armeabi-v7a/libspeex-jni.so ../dist/libspeex-jni_arm.so -cp obj/local/armeabi-v7a/libspeex.a ../dist/libspeex_arm.a +cp ../config.h ../dist/arm/config.h +cp libs/armeabi-v7a/libspeex-jni.so ../dist/arm/libspeex-jni.so +cp obj/local/armeabi-v7a/libspeex.a ../dist/arm/libspeex.a # for x86 cd .. @@ -32,6 +34,6 @@ ndk-build -B V=1 -j4 APP_ABI=x86 ls -lR libs -cp ../config.h ../dist/config_x86.h -cp libs/x86/libspeex-jni.so ../dist/libspeex-jni_x86.so -cp obj/local/x86/libspeex.a ../dist/libspeex_x86.a +cp ../config.h ../dist/x86/config.h +cp libs/armeabi-v7a/libspeex-jni.so ../dist/x86/libspeex-jni.so +cp obj/local/armeabi-v7a/libspeex.a ../dist/x86/libspeex.a diff --git a/android/jni/Application.mk b/android/jni/Application.mk index cf89f6f0..4dbbaa2d 100644 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,7 +1,7 @@ # The ARMv7 is significanly faster due to the use of the hardware FPU # APP_ABI := armeabi-v7a armeabi x86 mips APP_ABI := armeabi-v7a -APP_PLATFORM := android-8 +APP_PLATFORM := android-14 APP_STL := gnustl_static