gecko/tools/profiler/sps_sampler.h

319 lines
11 KiB
C++

/* -*- 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) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Benoit Girard <bgirard@mozilla.com>
*
* 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 ***** */
#include <stdlib.h>
#include <signal.h>
#include "thread_helper.h"
#include "nscore.h"
#include "jsapi.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Util.h"
using mozilla::TimeStamp;
using mozilla::TimeDuration;
extern mozilla::tls::key pkey_stack;
extern mozilla::tls::key pkey_ticker;
extern bool stack_key_initialized;
#ifndef SAMPLE_FUNCTION_NAME
# ifdef __GNUC__
# define SAMPLE_FUNCTION_NAME __FUNCTION__
# elif defined(_MSC_VER)
# define SAMPLE_FUNCTION_NAME __FUNCTION__
# else
# define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
# endif
#endif
#define SAMPLER_INIT() mozilla_sampler_init()
#define SAMPLER_DEINIT() mozilla_sampler_deinit()
#define SAMPLER_START(entries, interval, features, featureCount) mozilla_sampler_start(entries, interval, features, featureCount)
#define SAMPLER_STOP() mozilla_sampler_stop()
#define SAMPLER_IS_ACTIVE() mozilla_sampler_is_active()
#define SAMPLER_RESPONSIVENESS(time) mozilla_sampler_responsiveness(time)
#define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness()
#define SAMPLER_SAVE() mozilla_sampler_save()
#define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile()
#define SAMPLER_GET_PROFILE_DATA(ctx) mozilla_sampler_get_profile_data(ctx)
#define SAMPLER_GET_FEATURES() mozilla_sampler_get_features()
// we want the class and function name but can't easily get that using preprocessor macros
// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
#define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
#define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
#define SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info)
#define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info)
/* we duplicate this code here to avoid header dependencies
* which make it more difficult to include in other places */
#if defined(_M_X64) || defined(__x86_64__)
#define V8_HOST_ARCH_X64 1
#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.
// For now since we don't support stackwalking let's use 1ms since it's fast
// enough.
#define PROFILE_DEFAULT_INTERVAL 1
#else
#define PROFILE_DEFAULT_INTERVAL 1
#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
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
(LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
# if defined(_MSC_VER)
// MSVC2005 has a name collision bug caused when both <intrin.h> and <winnt.h> are included together.
#ifdef _WINNT_
# define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
# define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
# include <intrin.h>
#else
# include <intrin.h>
# define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
# define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
#endif
// Even though MSVC2005 has the intrinsic _ReadWriteBarrier, it fails to link to it when it's
// not explicitly declared.
# pragma intrinsic(_ReadWriteBarrier)
# define STORE_SEQUENCER() _ReadWriteBarrier();
# elif defined(__INTEL_COMPILER)
# define STORE_SEQUENCER() __memory_barrier();
# elif __GNUC__
# define STORE_SEQUENCER() asm volatile("" ::: "memory");
# else
# error "Memory clobber not supported for your compiler."
# endif
#else
# error "Memory clobber not supported for your platform."
#endif
// Returns a handdle to pass on exit. This can check that we are popping the
// correct callstack.
inline void* mozilla_sampler_call_enter(const char *aInfo);
inline void mozilla_sampler_call_exit(void* handle);
inline void mozilla_sampler_add_marker(const char *aInfo);
void mozilla_sampler_start(int aEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount);
void mozilla_sampler_stop();
bool mozilla_sampler_is_active();
void mozilla_sampler_responsiveness(TimeStamp time);
const double* mozilla_sampler_get_responsiveness();
void mozilla_sampler_save();
char* mozilla_sampler_get_profile();
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
const char** mozilla_sampler_get_features();
void mozilla_sampler_init();
namespace mozilla {
class NS_STACK_CLASS SamplerStackFrameRAII {
public:
// we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
SamplerStackFrameRAII(const char *aInfo) {
mHandle = mozilla_sampler_call_enter(aInfo);
}
~SamplerStackFrameRAII() {
mozilla_sampler_call_exit(mHandle);
}
private:
void* mHandle;
};
} //mozilla
// the SamplerStack members are read by signal
// handlers, so the mutation of them needs to be signal-safe.
struct ProfileStack
{
public:
ProfileStack()
: mStackPointer(0)
, mMarkerPointer(0)
, mDroppedStackEntries(0)
, mQueueClearMarker(false)
{ }
void addMarker(const char *aMarker)
{
if (mQueueClearMarker) {
clearMarkers();
}
if (!aMarker) {
return; //discard
}
if (size_t(mMarkerPointer) == mozilla::ArrayLength(mMarkers)) {
return; //array full, silently drop
}
mMarkers[mMarkerPointer] = aMarker;
STORE_SEQUENCER();
mMarkerPointer++;
}
// called within signal. Function must be reentrant
const char* getMarker(int aMarkerId)
{
if (mQueueClearMarker) {
clearMarkers();
}
if (aMarkerId < 0 ||
static_cast<mozilla::sig_safe_t>(aMarkerId) >= mMarkerPointer) {
return NULL;
}
return mMarkers[aMarkerId];
}
// called within signal. Function must be reentrant
void clearMarkers()
{
mMarkerPointer = 0;
mQueueClearMarker = false;
}
void push(const char *aName)
{
if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
mDroppedStackEntries++;
return;
}
// Make sure we increment the pointer after the name has
// been written such that mStack is always consistent.
mStack[mStackPointer] = aName;
// Prevent the optimizer from re-ordering these instructions
STORE_SEQUENCER();
mStackPointer++;
}
void pop()
{
if (mDroppedStackEntries > 0) {
mDroppedStackEntries--;
} else {
mStackPointer--;
}
}
bool isEmpty()
{
return mStackPointer == 0;
}
// Keep a list of active checkpoints
char const * volatile mStack[1024];
// Keep a list of active markers to be applied to the next sample taken
char const * volatile mMarkers[1024];
volatile mozilla::sig_safe_t mStackPointer;
volatile mozilla::sig_safe_t mMarkerPointer;
volatile mozilla::sig_safe_t mDroppedStackEntries;
// We don't want to modify _markers from within the signal so we allow
// it to queue a clear operation.
volatile mozilla::sig_safe_t mQueueClearMarker;
};
inline void* mozilla_sampler_call_enter(const char *aInfo)
{
// check if we've been initialized to avoid calling pthread_getspecific
// with a null pkey_stack which will return undefined results.
if (!stack_key_initialized)
return NULL;
ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
// we can't infer whether 'stack' has been initialized
// based on the value of stack_key_intiailized because
// 'stack' is only intialized when a thread is being
// profiled.
if (!stack) {
return stack;
}
stack->push(aInfo);
// The handle is meant to support future changes
// but for now it is simply use to save a call to
// pthread_getspecific on exit. It also supports the
// case where the sampler is initialized between
// enter and exit.
return stack;
}
inline void mozilla_sampler_call_exit(void *aHandle)
{
if (!aHandle)
return;
ProfileStack *stack = (ProfileStack*)aHandle;
stack->pop();
}
inline void mozilla_sampler_add_marker(const char *aMarker)
{
ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
if (!stack) {
return;
}
stack->addMarker(aMarker);
}