Bug 734302 - Part 1: Enable the Gecko Profiler on native Fennec; r=BenWa,khuey

--HG--
extra : rebase_source : 050443e4850aac78551ed985aa81522d808bcb6b
This commit is contained in:
Ehsan Akhgari 2012-03-02 14:11:47 -05:00
parent ff45f86f4f
commit 5fc5ba4b3e
11 changed files with 303 additions and 36 deletions

View File

@ -8893,6 +8893,42 @@ if test "$CAIRO_FEATURES_H"; then
fi
fi
dnl Build libunwind for Android profiling builds
if test "$OS_TARGET" = "Android" -a "$MOZ_PROFILING"; then
old_ac_configure_arg="$ac_configure_args"
ac_configure_args="--build=${build} --host=${target_alias} --disable-shared --enable-block-signals=no"
if test "$MOZ_DEBUG"; then
ac_configure_args="$ac_configure_args --enable-debug"
fi
if test "$DSO_PIC_CFLAGS"; then
ac_configure_args="$ac_configure_args --with-pic"
fi
ac_configure_args="$ac_configure_args \
CC=\"$CC\" \
CXX=\"$CXX\" \
CPP=\"$CPP\" \
CFLAGS=\"$CFLAGS\" \
CXXFLAGS=\"$CXXFLAGS\" \
CPPFLAGS=\"$CPPFLAGS\" \
LD=\"$LD\" \
LDFLAGS=\"$LDFLAGS\" \
AR=\"$AR\" \
RANLIB=\"$RANLIB\" \
STRIP=\"$STRIP\" \
LIBS=\"$LIBS\""
# Use a separate cache file for libunwind, since it does not use caching.
mkdir -p $_objdir/tools/profiler/libunwind/src
old_cache_file=$cache_file
cache_file=$_objdir/tools/profiler/libunwind/src/config.cache
old_config_files=$CONFIG_FILES
unset CONFIG_FILES
AC_OUTPUT_SUBDIRS(tools/profiler/libunwind/src)
cache_file=$old_cache_file
ac_configure_args="$old_ac_configure_args"
CONFIG_FILES=$old_config_files
fi
# Run freetype configure script
if test "$MOZ_TREE_FREETYPE"; then

View File

@ -84,6 +84,7 @@
<li><a href="about:license#libevent">libevent License</a></li>
<li><a href="about:license#libffi">libffi License</a></li>
<li><a href="about:license#libnestegg">libnestegg License</a></li>
<li><a href="about:license#libunwind">libunwind License</a></li>
<li><a href="about:license#hunspell-lt">Lithuanian Spellchecking Dictionary License</a></li>
<li><a href="about:license#maattachedwindow">MAAttachedWindow License</a></li>
<li><a href="about:license#msstdint">msstdint License</a></li>
@ -1865,6 +1866,35 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
</pre>
<hr>
<h1><a id="libunwind"></a>libunwind License</h1>
<p>This license applies to files in the directory
<span class="path">tools/profiler/libunwind</span>.
</p>
<pre>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
<hr>

View File

@ -57,6 +57,25 @@ LOCAL_INCLUDES += \
-I$(topsrcdir)/ipc/chromium/src \
$(NULL)
ifneq (,$(MOZ_PROFILING))
ifneq (,$(filter Android,$(OS_TARGET)))
LOCAL_INCLUDES += \
-I$(topsrcdir)/tools/profiler/libunwind/src/include \
-I$(DEPTH)/tools/profiler/libunwind/src/include \
$(NULL)
SHARED_LIBRARY_LIBS += \
$(DEPTH)/tools/profiler/libunwind/src/src/.libs/libunwind-arm.$(LIB_SUFFIX) \
$(NULL)
export::
$(call SUBMAKE,,libunwind/src)
distclean::
$(call SUBMAKE,$@,libunwind/src)
endif
endif
MODULE = profiler
MODULE_NAME = nsProfilerModule
LIBRARY_NAME = profiler

View File

@ -65,6 +65,12 @@
#include "nsStackWalk.h"
#endif
#if defined(MOZ_PROFILING) && defined(ANDROID)
#define USE_LIBUNWIND
#include <libunwind.h>
#include "android-signal-defs.h"
#endif
using std::string;
using namespace mozilla;
@ -347,7 +353,14 @@ class TableTicker: public Sampler {
, mPrimaryThreadProfile(aEntrySize, aStack)
, mSaveRequested(false)
{
#if defined(USE_LIBUNWIND) && defined(ANDROID)
// We don't have the Gecko Profiler add-on on Android, but we know that
// libunwind is available, so we can always walk the stacks.
mUseStackWalk = true;
#else
mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
#endif
//XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
@ -377,7 +390,7 @@ class TableTicker: public Sampler {
private:
// Not implemented on platforms which do not support backtracing
void doBacktrace(ThreadProfile &aProfile, Address pc);
static void doBacktrace(ThreadProfile &aProfile, TickSample* aSample);
private:
// This represent the application's main thread (SAMPLER_INIT)
@ -465,7 +478,7 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx)
#ifdef USE_BACKTRACE
void TableTicker::doBacktrace(ThreadProfile &aProfile, Address pc)
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
void *array[100];
int count = backtrace (array, 100);
@ -498,7 +511,7 @@ void StackWalkCallback(void* aPC, void* aClosure)
array->array[array->count++] = aPC;
}
void TableTicker::doBacktrace(ThreadProfile &aProfile, Address fp)
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
#ifndef XP_MACOSX
uintptr_t thread = GetThreadHandle(platform_data());
@ -515,7 +528,7 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, Address fp)
void *stackEnd = reinterpret_cast<void*>(-1);
if (pt)
stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(fp), stackEnd);
nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
#else
nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
#endif
@ -529,6 +542,54 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, Address fp)
}
#endif
#if defined(USE_LIBUNWIND) && defined(ANDROID)
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
void* pc_array[1000];
size_t count = 0;
unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip;
unw_getcontext(&uc);
// Dirty hack: replace the registers with values from the signal handler
// We do this in order to avoid the overhead of walking up to reach the
// signal handler frame, and the possibility that libunwind fails to
// handle it correctly.
unw_tdep_context_t *unw_ctx = reinterpret_cast<unw_tdep_context_t*> (&uc);
mcontext_t& mcontext = reinterpret_cast<ucontext_t*> (aSample->context)->uc_mcontext;
#define REPLACE_REG(num) unw_ctx->regs[num] = mcontext.gregs[R##num]
REPLACE_REG(0);
REPLACE_REG(1);
REPLACE_REG(2);
REPLACE_REG(3);
REPLACE_REG(4);
REPLACE_REG(5);
REPLACE_REG(6);
REPLACE_REG(7);
REPLACE_REG(8);
REPLACE_REG(9);
REPLACE_REG(10);
REPLACE_REG(11);
REPLACE_REG(12);
REPLACE_REG(13);
REPLACE_REG(14);
REPLACE_REG(15);
#undef REPLACE_REG
unw_init_local(&cursor, &uc);
while (count < ArrayLength(pc_array) &&
unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &ip);
pc_array[count++] = reinterpret_cast<void*> (ip);
}
aProfile.addTag(ProfileEntry('s', "(root)", 0));
for (size_t i = count; i > 0; --i) {
aProfile.addTag(ProfileEntry('l', reinterpret_cast<const char*>(pc_array[i - 1])));
}
}
#endif
static
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
@ -591,9 +652,9 @@ void TableTicker::Tick(TickSample* sample)
}
}
#if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
#if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK) || defined(USE_LIBUNWIND)
if (mUseStackWalk) {
doBacktrace(mPrimaryThreadProfile, sample->fp);
doBacktrace(mPrimaryThreadProfile, sample);
} else {
doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
}
@ -637,11 +698,6 @@ string ProfileEntry::TagToString(ThreadProfile *profile)
return tag;
}
#define PROFILE_DEFAULT_ENTRY 100000
#define PROFILE_DEFAULT_INTERVAL 10
#define PROFILE_DEFAULT_FEATURES NULL
#define PROFILE_DEFAULT_FEATURE_COUNT 0
void mozilla_sampler_init()
{
// TODO linux port: Use TLS with ifdefs
@ -655,6 +711,18 @@ void mozilla_sampler_init()
ProfileStack *stack = new ProfileStack();
mozilla::tls::set(pkey_stack, stack);
#if defined(USE_LIBUNWIND) && defined(ANDROID)
// Only try debug_frame and exidx unwinding
putenv("UNW_ARM_UNWIND_METHOD=5");
// Allow the profiler to be started and stopped using signals
OS::RegisterStartStopHandlers();
// On Android, this is too soon in order to start up the
// profiler.
return;
#endif
// We can't open pref so we use an environment variable
// to know if we should trigger the profiler on startup
// NOTE: Default
@ -717,7 +785,7 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
const char** mozilla_sampler_get_features()
{
static const char* features[] = {
#if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
#if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK) || defined(USE_LIBUNWIND))
"stackwalk",
#endif
"jank",

View File

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2012
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// Android runs a fairly new Linux kernel, so signal info is there,
// but the C library doesn't have the structs defined.
struct sigcontext {
uint32_t trap_no;
uint32_t error_code;
uint32_t oldmask;
uint32_t gregs[16];
uint32_t arm_cpsr;
uint32_t fault_address;
};
typedef uint32_t __sigset_t;
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
__sigset_t uc_sigmask;
} ucontext_t;
enum ArmRegisters {R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5,
R6 = 6, R7 = 7, R8 = 8, R9 = 9, R10 = 10,
R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15};

View File

@ -0,0 +1,12 @@
This is a copy of the libunwind source code, tailored for stack walking on ARM
Android.
This code includes patches to libunwind which have not been upstreamed yet.
Please note that this copy of the code is READ-ONLY, and is owned by
Ehsan Akhgari. Modifications to this code without coordinating with the
owner are unacceptable, and will be reverted.
The canonical repository for this source code is https://github.com/ehsan/libunwind.
The information about the upstream repository and revision lives in upstream.info.
In order to update the code, you can run the update.sh script located in
the same directory.

View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
cd `dirname $0`
source upstream.info
hg rm -f src
rm -rf src
git clone "$UPSTREAM_REPO" src
cd src
git checkout "$UPSTREAM_COMMIT"
autoreconf -i
rm -rf .git .gitignore
cd ..
hg add src
echo "libunwind has now been updated. Don't forget to run hg commit!"

View File

@ -0,0 +1,2 @@
UPSTREAM_REPO=git://github.com/ehsan/libunwind.git
UPSTREAM_COMMIT=bf6079087f2901e55d737e517a4225e905913e81

View File

@ -35,6 +35,7 @@
#include <errno.h>
#include <stdarg.h>
#include "platform.h"
#include "sps_sampler.h"
#include <string.h>
#include <stdio.h>
@ -54,28 +55,7 @@ static Sampler* sActiveSampler = NULL;
#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
// Android runs a fairly new Linux kernel, so signal info is there,
// but the C library doesn't have the structs defined.
struct sigcontext {
uint32_t trap_no;
uint32_t error_code;
uint32_t oldmask;
uint32_t gregs[16];
uint32_t arm_cpsr;
uint32_t fault_address;
};
typedef uint32_t __sigset_t;
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
__sigset_t uc_sigmask;
} ucontext_t;
enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
#include "android-signal-defs.h"
#endif
static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
@ -95,7 +75,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
TickSample sample_obj;
TickSample* sample = &sample_obj;
sample->pc = 0;
sample->context = context;
#ifdef ENABLE_SPS_LEAF_DATA
// If profiling, we extract the current pc and sp.
@ -282,3 +262,23 @@ void Sampler::Stop() {
sActiveSampler = NULL;
}
static struct sigaction old_sigstartstop_signal_handler;
const int SIGSTARTSTOP = SIGUSR1;
static void StartStopSignalHandler(int signal, siginfo_t* info, void* context) {
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
}
void OS::RegisterStartStopHandlers()
{
LOG("Registering start/stop signal");
struct sigaction sa;
sa.sa_sigaction = StartStopSignalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGSTARTSTOP, &sa, &old_sigstartstop_signal_handler) != 0) {
LOG("Error installing signal");
}
}

View File

@ -16,7 +16,7 @@
#include <vector>
#define ASSERT(a) MOZ_ASSERT(a)
#ifdef ANDROID
#ifdef defined(__arm__) || defined(__thumb__)
#if defined(__arm__) || defined(__thumb__)
#define ENABLE_SPS_LEAF_DATA
#endif
#define LOG(text) __android_log_print(ANDROID_LOG_ERROR, "profiler", "%s", text);
@ -90,6 +90,10 @@ class OS {
// Please use delete to reclaim the storage for the returned Mutex.
static Mutex* CreateMutex();
// On supported platforms, setup a signal handler which would start
// and stop the profiler.
static void RegisterStartStopHandlers();
private:
static const int msPerSecond = 1000;
@ -160,11 +164,13 @@ class TickSample {
sp(NULL),
fp(NULL),
function(NULL),
context(NULL),
frames_count(0) {}
Address pc; // Instruction pointer.
Address sp; // Stack pointer.
Address fp; // Frame pointer.
Address function; // The last called JS function.
void* context; // The context from the signal handler, if available
static const int kMaxFramesCount = 64;
Address stack[kMaxFramesCount]; // Call stack.
int frames_count; // Number of captured frames.

View File

@ -89,6 +89,20 @@ extern bool stack_key_initialized;
#warning Please add support for your architecture in chromium_types.h
#endif
#define PROFILE_DEFAULT_ENTRY 100000
#ifdef ANDROID
// We use a lower frequency on Android, in order to make things work
// more smoothly on phones. This value can be adjusted later with
// some libunwind optimizations.
// In one sample measurement on Galaxy Nexus, out of about 700 backtraces,
// 60 of them took more than 25ms, and the average and standard deviation
// were 6.17ms and 9.71ms respectively.
#define PROFILE_DEFAULT_INTERVAL 25
#else
#define PROFILE_DEFAULT_INTERVAL 10
#endif
#define PROFILE_DEFAULT_FEATURES NULL
#define PROFILE_DEFAULT_FEATURE_COUNT 0
// STORE_SEQUENCER: Because signals can interrupt our profile modification
// we need to make stores are not re-ordered by the compiler