michael@0: // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions michael@0: // are met: michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above copyright michael@0: // notice, this list of conditions and the following disclaimer in michael@0: // the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google, Inc. nor the names of its contributors michael@0: // may be used to endorse or promote products derived from this michael@0: // software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS michael@0: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE michael@0: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, michael@0: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS michael@0: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED michael@0: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, michael@0: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT michael@0: // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@0: // SUCH DAMAGE. michael@0: michael@0: #ifndef TOOLS_PLATFORM_H_ michael@0: #define TOOLS_PLATFORM_H_ michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: #else michael@0: #define __android_log_print(a, ...) michael@0: #endif michael@0: michael@0: #ifdef XP_UNIX michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "MainThreadUtils.h" michael@0: #include "PlatformMacros.h" michael@0: #include "v8-support.h" michael@0: #include michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #endif michael@0: michael@0: #define ASSERT(a) MOZ_ASSERT(a) michael@0: michael@0: bool moz_profiler_verbose(); michael@0: michael@0: #ifdef ANDROID michael@0: # if defined(__arm__) || defined(__thumb__) michael@0: # define ENABLE_SPS_LEAF_DATA michael@0: # define ENABLE_ARM_LR_SAVING michael@0: # endif michael@0: # define LOG(text) \ michael@0: do { if (moz_profiler_verbose()) \ michael@0: __android_log_write(ANDROID_LOG_ERROR, "Profiler", text); \ michael@0: } while (0) michael@0: # define LOGF(format, ...) \ michael@0: do { if (moz_profiler_verbose()) \ michael@0: __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, \ michael@0: __VA_ARGS__); \ michael@0: } while (0) michael@0: michael@0: #else michael@0: # define LOG(text) \ michael@0: do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: %s\n", text); \ michael@0: } while (0) michael@0: # define LOGF(format, ...) \ michael@0: do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: " format \ michael@0: "\n", __VA_ARGS__); \ michael@0: } while (0) michael@0: michael@0: #endif michael@0: michael@0: #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX) michael@0: #define ENABLE_SPS_LEAF_DATA michael@0: #endif michael@0: michael@0: extern mozilla::TimeStamp sStartTime; michael@0: michael@0: typedef uint8_t* Address; michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: // Mutex michael@0: // michael@0: // Mutexes are used for serializing access to non-reentrant sections of code. michael@0: // The implementations of mutex should allow for nested/recursive locking. michael@0: michael@0: class Mutex { michael@0: public: michael@0: virtual ~Mutex() {} michael@0: michael@0: // Locks the given mutex. If the mutex is currently unlocked, it becomes michael@0: // locked and owned by the calling thread, and immediately. If the mutex michael@0: // is already locked by another thread, suspends the calling thread until michael@0: // the mutex is unlocked. michael@0: virtual int Lock() = 0; michael@0: michael@0: // Unlocks the given mutex. The mutex is assumed to be locked and owned by michael@0: // the calling thread on entrance. michael@0: virtual int Unlock() = 0; michael@0: michael@0: // Tries to lock the given mutex. Returns whether the mutex was michael@0: // successfully locked. michael@0: virtual bool TryLock() = 0; michael@0: }; michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: // OS michael@0: // michael@0: // This class has static methods for the different platform specific michael@0: // functions. Add methods here to cope with differences between the michael@0: // supported platforms. michael@0: michael@0: class OS { michael@0: public: michael@0: michael@0: // Sleep for a number of milliseconds. michael@0: static void Sleep(const int milliseconds); michael@0: michael@0: // Sleep for a number of microseconds. michael@0: static void SleepMicro(const int microseconds); michael@0: michael@0: // Called on startup to initialize platform specific things michael@0: static void Startup(); michael@0: michael@0: private: michael@0: static const int msPerSecond = 1000; michael@0: michael@0: }; michael@0: michael@0: michael@0: michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: // Thread michael@0: // michael@0: // Thread objects are used for creating and running threads. When the start() michael@0: // method is called the new thread starts running the run() method in the new michael@0: // thread. The Thread object should not be deallocated before the thread has michael@0: // terminated. michael@0: michael@0: class Thread { michael@0: public: michael@0: // Create new thread. michael@0: explicit Thread(const char* name); michael@0: virtual ~Thread(); michael@0: michael@0: // Start new thread by calling the Run() method in the new thread. michael@0: void Start(); michael@0: michael@0: void Join(); michael@0: michael@0: inline const char* name() const { michael@0: return name_; michael@0: } michael@0: michael@0: // Abstract method for run handler. michael@0: virtual void Run() = 0; michael@0: michael@0: // The thread name length is limited to 16 based on Linux's implementation of michael@0: // prctl(). michael@0: static const int kMaxThreadNameLength = 16; michael@0: michael@0: #ifdef XP_WIN michael@0: HANDLE thread_; michael@0: typedef DWORD tid_t; michael@0: tid_t thread_id_; michael@0: #else michael@0: typedef ::pid_t tid_t; michael@0: #endif michael@0: #if defined(XP_MACOSX) michael@0: pthread_t thread_; michael@0: #endif michael@0: michael@0: static tid_t GetCurrentId(); michael@0: michael@0: private: michael@0: void set_name(const char *name); michael@0: michael@0: char name_[kMaxThreadNameLength]; michael@0: int stack_size_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(Thread); michael@0: }; michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: // HAVE_NATIVE_UNWIND michael@0: // michael@0: // Pseudo backtraces are available on all platforms. Native michael@0: // backtraces are available only on selected platforms. Breakpad is michael@0: // the only supported native unwinder. HAVE_NATIVE_UNWIND is set at michael@0: // build time to indicate whether native unwinding is possible on this michael@0: // platform. The actual unwind mode currently in use is stored in michael@0: // sUnwindMode. michael@0: michael@0: #undef HAVE_NATIVE_UNWIND michael@0: #if defined(MOZ_PROFILING) \ michael@0: && (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ michael@0: || defined(SPS_PLAT_x86_linux) \ michael@0: || defined(SPS_OS_windows) \ michael@0: || defined(SPS_OS_darwin)) michael@0: # define HAVE_NATIVE_UNWIND michael@0: #endif michael@0: michael@0: /* Some values extracted at startup from environment variables, that michael@0: control the behaviour of the breakpad unwinder. */ michael@0: extern const char* PROFILER_MODE; michael@0: extern const char* PROFILER_INTERVAL; michael@0: extern const char* PROFILER_ENTRIES; michael@0: extern const char* PROFILER_STACK; michael@0: extern const char* PROFILER_FEATURES; michael@0: michael@0: void read_profiler_env_vars(); michael@0: void profiler_usage(); michael@0: michael@0: // Helper methods to expose modifying profiler behavior michael@0: bool set_profiler_mode(const char*); michael@0: bool set_profiler_interval(const char*); michael@0: bool set_profiler_entries(const char*); michael@0: bool set_profiler_scan(const char*); michael@0: bool is_native_unwinding_avail(); michael@0: michael@0: typedef enum { UnwINVALID, UnwNATIVE, UnwPSEUDO, UnwCOMBINED } UnwMode; michael@0: extern UnwMode sUnwindMode; /* what mode? */ michael@0: extern int sUnwindInterval; /* in milliseconds */ michael@0: extern int sUnwindStackScan; /* max # of dubious frames allowed */ michael@0: michael@0: extern int sProfileEntries; /* how many entries do we store? */ michael@0: michael@0: void set_tls_stack_top(void* stackTop); michael@0: michael@0: // ---------------------------------------------------------------------------- michael@0: // Sampler michael@0: // michael@0: // A sampler periodically samples the state of the VM and optionally michael@0: // (if used for profiling) the program counter and stack pointer for michael@0: // the thread that created it. michael@0: michael@0: class PseudoStack; michael@0: class ThreadProfile; michael@0: michael@0: // TickSample captures the information collected for each sample. michael@0: class TickSample { michael@0: public: michael@0: TickSample() michael@0: : michael@0: pc(NULL), michael@0: sp(NULL), michael@0: fp(NULL), michael@0: #ifdef ENABLE_ARM_LR_SAVING michael@0: lr(NULL), michael@0: #endif michael@0: context(NULL), michael@0: isSamplingCurrentThread(false) {} michael@0: michael@0: void PopulateContext(void* aContext); michael@0: michael@0: Address pc; // Instruction pointer. michael@0: Address sp; // Stack pointer. michael@0: Address fp; // Frame pointer. michael@0: #ifdef ENABLE_ARM_LR_SAVING michael@0: Address lr; // ARM link register michael@0: #endif michael@0: void* context; // The context from the signal handler, if available. On michael@0: // Win32 this may contain the windows thread context. michael@0: bool isSamplingCurrentThread; michael@0: ThreadProfile* threadProfile; michael@0: mozilla::TimeStamp timestamp; michael@0: }; michael@0: michael@0: class ThreadInfo; michael@0: class PlatformData; michael@0: class TableTicker; michael@0: class SyncProfile; michael@0: class Sampler { michael@0: public: michael@0: // Initialize sampler. michael@0: explicit Sampler(double interval, bool profiling, int entrySize); michael@0: virtual ~Sampler(); michael@0: michael@0: double interval() const { return interval_; } michael@0: michael@0: // This method is called for each sampling period with the current michael@0: // program counter. michael@0: virtual void Tick(TickSample* sample) = 0; michael@0: michael@0: // Immediately captures the calling thread's call stack and returns it. michael@0: virtual SyncProfile* GetBacktrace() = 0; michael@0: michael@0: // Request a save from a signal handler michael@0: virtual void RequestSave() = 0; michael@0: // Process any outstanding request outside a signal handler. michael@0: virtual void HandleSaveRequest() = 0; michael@0: michael@0: // Start and stop sampler. michael@0: void Start(); michael@0: void Stop(); michael@0: michael@0: // Is the sampler used for profiling? michael@0: bool IsProfiling() const { return profiling_; } michael@0: michael@0: // Whether the sampler is running (that is, consumes resources). michael@0: bool IsActive() const { return active_; } michael@0: michael@0: // Low overhead way to stop the sampler from ticking michael@0: bool IsPaused() const { return paused_; } michael@0: void SetPaused(bool value) { NoBarrier_Store(&paused_, value); } michael@0: michael@0: virtual bool ProfileThreads() const = 0; michael@0: michael@0: int EntrySize() { return entrySize_; } michael@0: michael@0: // We can't new/delete the type safely without defining it michael@0: // (-Wdelete-incomplete). Use these Alloc/Free functions instead. michael@0: static PlatformData* AllocPlatformData(int aThreadId); michael@0: static void FreePlatformData(PlatformData*); michael@0: michael@0: // If we move the backtracing code into the platform files we won't michael@0: // need to have these hacks michael@0: #ifdef XP_WIN michael@0: // xxxehsan sucky hack :( michael@0: static uintptr_t GetThreadHandle(PlatformData*); michael@0: #endif michael@0: #ifdef XP_MACOSX michael@0: static pthread_t GetProfiledThread(PlatformData*); michael@0: #endif michael@0: michael@0: static std::vector GetRegisteredThreads() { michael@0: return *sRegisteredThreads; michael@0: } michael@0: michael@0: static bool RegisterCurrentThread(const char* aName, michael@0: PseudoStack* aPseudoStack, michael@0: bool aIsMainThread, void* stackTop); michael@0: static void UnregisterCurrentThread(); michael@0: michael@0: static void Startup(); michael@0: // Should only be called on shutdown michael@0: static void Shutdown(); michael@0: michael@0: static TableTicker* GetActiveSampler() { return sActiveSampler; } michael@0: static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; } michael@0: michael@0: static mozilla::Mutex* sRegisteredThreadsMutex; michael@0: michael@0: static bool CanNotifyObservers() { michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: // Android ANR reporter uses the profiler off the main thread michael@0: return NS_IsMainThread(); michael@0: #else michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: protected: michael@0: static std::vector* sRegisteredThreads; michael@0: static TableTicker* sActiveSampler; michael@0: michael@0: private: michael@0: void SetActive(bool value) { NoBarrier_Store(&active_, value); } michael@0: michael@0: const double interval_; michael@0: const bool profiling_; michael@0: Atomic32 paused_; michael@0: Atomic32 active_; michael@0: const int entrySize_; michael@0: michael@0: // Refactor me! michael@0: #if defined(SPS_OS_linux) || defined(SPS_OS_android) michael@0: bool signal_handler_installed_; michael@0: struct sigaction old_sigprof_signal_handler_; michael@0: struct sigaction old_sigsave_signal_handler_; michael@0: bool signal_sender_launched_; michael@0: pthread_t signal_sender_thread_; michael@0: #endif michael@0: }; michael@0: michael@0: class ThreadInfo { michael@0: public: michael@0: ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop) michael@0: : mName(strdup(aName)) michael@0: , mThreadId(aThreadId) michael@0: , mIsMainThread(aIsMainThread) michael@0: , mPseudoStack(aPseudoStack) michael@0: , mPlatformData(Sampler::AllocPlatformData(aThreadId)) michael@0: , mProfile(NULL) michael@0: , mStackTop(aStackTop) {} michael@0: michael@0: virtual ~ThreadInfo(); michael@0: michael@0: const char* Name() const { return mName; } michael@0: int ThreadId() const { return mThreadId; } michael@0: michael@0: bool IsMainThread() const { return mIsMainThread; } michael@0: PseudoStack* Stack() const { return mPseudoStack; } michael@0: michael@0: void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; } michael@0: ThreadProfile* Profile() const { return mProfile; } michael@0: michael@0: PlatformData* GetPlatformData() const { return mPlatformData; } michael@0: void* StackTop() const { return mStackTop; } michael@0: private: michael@0: char* mName; michael@0: int mThreadId; michael@0: const bool mIsMainThread; michael@0: PseudoStack* mPseudoStack; michael@0: PlatformData* mPlatformData; michael@0: ThreadProfile* mProfile; michael@0: void* const mStackTop; michael@0: }; michael@0: michael@0: #endif /* ndef TOOLS_PLATFORM_H_ */