/* -*- 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 * * 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 #include #include "thread_helper.h" #include "nscore.h" #include "mozilla/TimeStamp.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_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 SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII only_one_sampleraii_per_scope(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 // 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 and are included together. #ifdef _WINNT_ # define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR # define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR # include #else # include # 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(); 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 Stack { public: Stack() : mStackPointer(0) , mMarkerPointer(0) , mDroppedStackEntries(0) , mQueueClearMarker(false) { } void addMarker(const char *aMarker) { if (mQueueClearMarker) { clearMarkers(); } if (!aMarker) { return; //discard } if (mMarkerPointer == 1024) { 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 >= 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 (mStackPointer >= 1024) { 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; Stack *stack = mozilla::tls::get(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; Stack *stack = (Stack*)aHandle; stack->pop(); } inline void mozilla_sampler_add_marker(const char *aMarker) { Stack *stack = mozilla::tls::get(pkey_stack); if (!stack) { return; } stack->addMarker(aMarker); }