Bug 734302 - Part 1: Enable the Gecko Profiler on native Fennec; r=BenWa,khuey
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 02 Mar 2012 14:11:47 -0500
changeset 93280 1f4dfa861e1025b258f603dc712bf981e34bf4e9
parent 93279 2e3738aa6670ca66333b6b94b9fd2b16da717e30
child 93281 c66090bcddbe99779a8557b80ce764dbbe678722
push idunknown
push userunknown
push dateunknown
reviewersBenWa, khuey
bugs734302
milestone14.0a1
Bug 734302 - Part 1: Enable the Gecko Profiler on native Fennec; r=BenWa,khuey
configure.in
toolkit/content/license.html
tools/profiler/Makefile.in
tools/profiler/TableTicker.cpp
tools/profiler/android-signal-defs.h
tools/profiler/libunwind/README.mozilla
tools/profiler/libunwind/update.sh
tools/profiler/libunwind/upstream.info
tools/profiler/platform-linux.cc
tools/profiler/platform.h
tools/profiler/sps_sampler.h
--- a/configure.in
+++ b/configure.in
@@ -8888,16 +8888,52 @@ if test "$CAIRO_FEATURES_H"; then
   if cmp -s $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig; then
     echo "$CAIRO_FEATURES_H is unchanged"
     mv -f "$CAIRO_FEATURES_H".orig "$CAIRO_FEATURES_H" 2> /dev/null
   else
     rm -f "$CAIRO_FEATURES_H".orig 2> /dev/null
   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
    export CFLAGS="$CFLAGS -std=c99"
    export CPPFLAGS="$CPPFLAGS"
    export CXXFLAGS="$CXXFLAGS"
    export LDFLAGS="$LDFLAGS"
    export CONFIG_FILES="unix-cc.mk:unix-cc.in unix-def.mk:unix-def.in freetype-config freetype2.pc:freetype2.in"
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -79,16 +79,17 @@
       <li><a href="about:license#icu">ICU License</a></li>
       <li><a href="about:license#jpnic">Japan Network Information Center License</a></li>
       <li><a href="about:license#jemalloc">jemalloc License</a></li>
       <li><a href="about:license#jquery">jQuery License</a></li>
       <li><a href="about:license#libcubeb">libcubeb License</a></li>
       <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>
       <li><a href="about:license#openvision">OpenVision License</a></li>
       <li><a href="about:license#qcms">qcms License</a></li>
       <li><a href="about:license#xdg">Red Hat xdg_user_dir_lookup License</a></li>
       <li><a href="about:license#hunspell-ru">Russian Spellchecking Dictionary License</a></li>
       <li><a href="about:license#skia">Skia License</a></li>
@@ -1863,16 +1864,45 @@ WITH REGARD TO THIS SOFTWARE INCLUDING A
 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 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>
     
     <h1><a id="hunspell-lt"></a>Lithuanian Spellchecking Dictionary License</h1>
 
     <p>This license applies to certain files in the directory
       <span class="path">l10n/lt/extensions/spellcheck/hunspell/</span>. (This
       code only ships in some localized versions of this product.)</p>
 
 <pre>
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -52,16 +52,35 @@ EXPORTS += \
   thread_helper.h \
   shared-libraries.h \
   $(NULL)
 
 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
 EXPORT_LIBRARY  = 1
 LIBXUL_LIBRARY  = 1
 IS_COMPONENT    = 1
 
 CPPSRCS		= \
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -60,16 +60,22 @@
 
 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
  #define USE_NS_STACKWALK
 #endif
 #ifdef USE_NS_STACKWALK
  #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;
 
 #ifdef XP_WIN
  #include <windows.h>
  #define getpid GetCurrentProcessId
 #else
  #include <unistd.h>
@@ -342,17 +348,24 @@ hasFeature(const char** aFeatures, uint3
 class TableTicker: public Sampler {
  public:
   TableTicker(int aInterval, int aEntrySize, ProfileStack *aStack,
               const char** aFeatures, uint32_t aFeatureCount)
     : Sampler(aInterval, true)
     , 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"));
   }
 
   ~TableTicker() { if (IsActive()) Stop(); }
 
   virtual void SampleStack(TickSample* sample) {}
@@ -372,17 +385,17 @@ class TableTicker: public Sampler {
   {
     return &mPrimaryThreadProfile;
   }
 
   JSObject *ToJSObject(JSContext *aCx);
 
 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)
   ThreadProfile mPrimaryThreadProfile;
   bool mSaveRequested;
   bool mUseStackWalk;
   bool mJankOnly;
 };
@@ -460,17 +473,17 @@ JSObject* TableTicker::ToJSObject(JSCont
   JSObject* threadSamples = GetPrimaryThreadProfile()->ToJSObject(aCx);
   b.ArrayPush(threads, threadSamples);
 
   return profile;
 }
 
 
 #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);
 
   aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
   for (int i = 0; i < count; i++) {
     if( (intptr_t)array[i] == -1 ) break;
@@ -493,47 +506,95 @@ void StackWalkCallback(void* aPC, void* 
   PCArray* array = static_cast<PCArray*>(aClosure);
   if (array->count >= array->size) {
     // too many frames, ignore
     return;
   }
   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());
   MOZ_ASSERT(thread);
 #endif
   void* pc_array[1000];
   PCArray array = {
     pc_array,
     mozilla::ArrayLength(pc_array),
     0
   };
 #ifdef XP_MACOSX
   pthread_t pt = GetProfiledThread(platform_data());
   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
   if (NS_SUCCEEDED(rv)) {
     aProfile.addTag(ProfileEntry('s', "(root)", 0));
 
     for (size_t i = array.count; i > 0; --i) {
       aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
     }
   }
 }
 #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)
 {
   // Sample
   // 's' tag denotes the start of a sample block
   // followed by 0 or more 'c' tags.
   for (int i = 0; i < aStack->mStackPointer; i++) {
     if (i == 0) {
@@ -586,19 +647,19 @@ void TableTicker::Tick(TickSample* sampl
     if (!sLastTracerEvent.IsNull()) {
       TimeDuration delta = sample->timestamp - sLastTracerEvent;
       if (delta.ToMilliseconds() > 100.0) {
           recordSample = true;
       }
     }
   }
 
-#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);
   }
 #else
   doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
 #endif
 
   if (recordSample)
@@ -632,34 +693,41 @@ string ProfileEntry::TagToString(ThreadP
     unsigned long pc = (unsigned long)mLeafAddress;
     snprintf(tagBuff, 1024, "l-%llu\n", pc);
     tag += string(tagBuff);
   }
 #endif
   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
   if (!mozilla::tls::create(&pkey_stack) ||
       !mozilla::tls::create(&pkey_ticker)) {
     LOG("Failed to init.");
     return;
   }
   stack_key_initialized = true;
 
   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
   const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
   if (!val || !*val) {
     return;
   }
 
@@ -712,17 +780,17 @@ JSObject *mozilla_sampler_get_profile_da
 
   return t->ToJSObject(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",
     NULL
   };
 
   return features;
 }
new file mode 100644
--- /dev/null
+++ b/tools/profiler/android-signal-defs.h
@@ -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};
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/libunwind/README.mozilla
@@ -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.
new file mode 100755
--- /dev/null
+++ b/tools/profiler/libunwind/update.sh
@@ -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!"
new file mode 100644
--- /dev/null
+++ b/tools/profiler/libunwind/upstream.info
@@ -0,0 +1,2 @@
+UPSTREAM_REPO=git://github.com/ehsan/libunwind.git
+UPSTREAM_COMMIT=bf6079087f2901e55d737e517a4225e905913e81
--- a/tools/profiler/platform-linux.cc
+++ b/tools/profiler/platform-linux.cc
@@ -30,16 +30,17 @@
 #include <unistd.h>     // sysconf
 #ifdef __GLIBC__
 #include <execinfo.h>   // backtrace, backtrace_symbols
 #endif  // def __GLIBC__
 #include <strings.h>    // index
 #include <errno.h>
 #include <stdarg.h>
 #include "platform.h"
+#include "sps_sampler.h"
 
 #include <string.h>
 #include <stdio.h>
 
 #define SIGNAL_SAVE_PROFILE SIGUSR2
 
 #if defined(__GLIBC__)
 // glibc doesn't implement gettid(2).
@@ -49,38 +50,17 @@ pid_t gettid()
   return (pid_t) syscall(SYS_gettid);
 }
 #endif
 
 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) {
   sActiveSampler->RequestSave();
 }
 
 #ifdef ANDROID
 #define V8_HOST_ARCH_ARM 1
@@ -90,17 +70,17 @@ static void ProfilerSaveSignalHandler(in
 #define V8_HOST_ARCH_X64 1
 #endif
 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
   if (!sActiveSampler)
     return;
 
   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.
   if (sActiveSampler->IsProfiling()) {
     // Extracting the sample from the context is extremely machine dependent.
     ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
     mcontext_t& mcontext = ucontext->uc_mcontext;
 #if V8_HOST_ARCH_IA32
@@ -277,8 +257,28 @@ void Sampler::Stop() {
     sigaction(SIGPROF, &data_->old_sigprof_signal_handler_, 0);
     data_->signal_handler_installed_ = false;
   }
 
   // This sampler is no longer the active sampler.
   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");
+  }
+}
+
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -11,17 +11,17 @@
 #include "mozilla/StandardInteger.h"
 #include "mozilla/Util.h"
 #include "mozilla/unused.h"
 #include "mozilla/TimeStamp.h"
 #include "v8-support.h"
 #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);
 #else
 #define LOG(text) printf("Profiler: %s\n", text)
 #endif
 
 typedef uint8_t* Address;
@@ -85,16 +85,20 @@ class OS {
 
   // Sleep for a number of milliseconds.
   static void Sleep(const int milliseconds);
 
   // Factory method for creating platform dependent Mutex.
   // 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;
 
 };
 
 
 
 
@@ -155,21 +159,23 @@ class Thread {
 class TickSample {
  public:
   TickSample()
       :
         pc(NULL),
         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.
   mozilla::TimeStamp timestamp;
 };
 
 class Sampler {
  public:
--- a/tools/profiler/sps_sampler.h
+++ b/tools/profiler/sps_sampler.h
@@ -84,16 +84,30 @@ extern bool stack_key_initialized;
 #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
 #define V8_HOST_ARCH_IA32 1
 #elif defined(__ARMEL__)
 #define V8_HOST_ARCH_ARM 1
 #else
 #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
 //                  or hardware to make sure the profile is consistent at
 //                  every point the signal can fire.
 #ifdef V8_HOST_ARCH_ARM
 // TODO Is there something cheaper that will prevent
 //      memory stores from being reordered