Bug 703444. Port SPS profiler to Windows. r=jmuizelaar

Some changes and fixes by Felipe Gomes. r=benwa,ehsan
This commit is contained in:
Ehsan Akhgari 2011-12-04 14:09:00 -05:00
parent 1c476ec47b
commit 3c6aabb3df
11 changed files with 420 additions and 50 deletions

View File

@ -51,6 +51,7 @@ include $(DEPTH)/config/autoconf.mk
EXPORTS = \
sampler.h \
sps_sampler.h \
thread_helper.h \
$(NULL)
LOCAL_INCLUDES += \
@ -96,6 +97,17 @@ CPPSRCS += \
$(NULL)
endif
ifeq ($(OS_TARGET),WINNT)
DEFINES += -DMOZ_ENABLE_PROFILER_SPS
CPPSRCS += \
platform-win32.cc \
TableTicker.cpp \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

View File

@ -67,11 +67,14 @@ NS_IMETHODIMP
nsProfiler::GetProfile(char **aProfile)
{
char *profile = SAMPLER_GET_PROFILE();
PRUint32 len = strlen(profile);
char *profileStr = static_cast<char *>
(nsMemory::Clone(profile, len * sizeof(char)));
*aProfile = profileStr;
free(profile);
if (profile) {
PRUint32 len = strlen(profile);
char *profileStr = static_cast<char *>
(nsMemory::Clone(profile, (len + 1) * sizeof(char)));
profileStr[len] = '\0';
*aProfile = profileStr;
free(profile);
}
return NS_OK;
}

View File

@ -86,7 +86,7 @@
#endif
// Redefine the macros for platforms where SPS is supported.
#if defined(ANDROID) || defined(XP_MACOSX)
#if defined(ANDROID) || defined(XP_MACOSX) || defined(XP_WIN)
#include "sps_sampler.h"

View File

@ -36,12 +36,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include <sys/stat.h> // open
#include <fcntl.h> // open
#include <unistd.h>
#include <string>
#include <stdio.h>
#include <semaphore.h>
#include "sps_sampler.h"
#include "platform.h"
#include "nsXULAppAPI.h"
@ -50,8 +46,33 @@
using std::string;
pthread_key_t pkey_stack;
pthread_key_t pkey_ticker;
#ifdef XP_WIN
#include <windows.h>
#define getpid GetCurrentProcessId
#else
#include <unistd.h>
#endif
#ifndef MAXPATHLEN
#ifdef PATH_MAX
#define MAXPATHLEN PATH_MAX
#elif defined(MAX_PATH)
#define MAXPATHLEN MAX_PATH
#elif defined(_MAX_PATH)
#define MAXPATHLEN _MAX_PATH
#elif defined(CCHMAXPATH)
#define MAXPATHLEN CCHMAXPATH
#else
#define MAXPATHLEN 1024
#endif
#endif
#if _MSC_VER
#define snprintf _snprintf
#endif
mozilla::tls::key pkey_stack;
mozilla::tls::key pkey_ticker;
// We need to track whether we've been initialized otherwise
// we end up using pkey_stack without initializing it.
// Because pkey_stack is totally opaque to us we can't reuse
@ -235,15 +256,27 @@ public:
SaveProfileTask() {}
NS_IMETHOD Run() {
TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
char buff[PATH_MAX];
char buff[MAXPATHLEN];
#ifdef ANDROID
#define FOLDER "/sdcard/"
#elif defined(XP_WIN)
#define FOLDER "%TEMP%\\"
#else
#define FOLDER "/tmp/"
#endif
snprintf(buff, PATH_MAX, FOLDER "profile_%i_%i.txt", XRE_GetProcessType(), getpid());
snprintf(buff, MAXPATHLEN, "%sprofile_%i_%i.txt", FOLDER, XRE_GetProcessType(), getpid());
#ifdef XP_WIN
// Expand %TEMP% on Windows
{
char tmp[MAXPATHLEN];
ExpandEnvironmentStringsA(buff, tmp, mozilla::ArrayLength(tmp));
strcpy(buff, tmp);
}
#endif
FILE* stream = ::fopen(buff, "w");
if (stream) {
@ -374,19 +407,15 @@ void ProfileEntry::WriteTag(Profile *profile, FILE *stream)
void mozilla_sampler_init()
{
// TODO linux port: Use TLS with ifdefs
// TODO window port: See bug 683229 comment 15
// profiler uses getspecific because TLS is not supported on android.
// getspecific was picked over nspr because it had less overhead required
// to make the checkpoint function fast.
if (pthread_key_create(&pkey_stack, NULL) ||
pthread_key_create(&pkey_ticker, NULL)) {
if (!mozilla::tls::create(&pkey_stack) ||
!mozilla::tls::create(&pkey_ticker)) {
LOG("Failed to init.");
return;
}
stack_key_initialized = true;
Stack *stack = new Stack();
pthread_setspecific(pkey_stack, stack);
mozilla::tls::set(pkey_stack, stack);
// We can't open pref so we use an environment variable
// to know if we should trigger the profiler on startup
@ -408,7 +437,7 @@ void mozilla_sampler_deinit()
}
void mozilla_sampler_save() {
TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
if (!t) {
return;
}
@ -420,7 +449,7 @@ void mozilla_sampler_save() {
}
char* mozilla_sampler_get_profile() {
TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
if (!t) {
return NULL;
}
@ -436,7 +465,7 @@ char* mozilla_sampler_get_profile() {
// Values are only honored on the first start
void mozilla_sampler_start(int aProfileEntries, int aInterval)
{
Stack *stack = (Stack*)pthread_getspecific(pkey_stack);
Stack *stack = mozilla::tls::get<Stack>(pkey_stack);
if (!stack) {
ASSERT(false);
return;
@ -445,24 +474,24 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval)
mozilla_sampler_stop();
TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack);
pthread_setspecific(pkey_ticker, t);
mozilla::tls::set(pkey_ticker, t);
t->Start();
}
void mozilla_sampler_stop()
{
TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
if (!t) {
return;
}
t->Stop();
pthread_setspecific(pkey_ticker, NULL);
mozilla::tls::set(pkey_ticker, (Stack*)NULL);
}
bool mozilla_sampler_is_active()
{
TableTicker *t = (TableTicker*)pthread_getspecific(pkey_ticker);
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
if (!t) {
return false;
}

View File

@ -246,7 +246,6 @@ static void* SenderEntry(void* arg) {
Sampler::Sampler(int interval, bool profiling)
: interval_(interval),
profiling_(profiling),
synchronous_(profiling),
active_(false) {
data_ = new PlatformData(this);
}

View File

@ -280,7 +280,6 @@ Sampler::Sampler(int interval, bool profiling)
: // isolate_(isolate),
interval_(interval),
profiling_(profiling),
synchronous_(profiling),
active_(false) /*,
samples_taken_(0)*/ {
data_ = new PlatformData;

View File

@ -0,0 +1,194 @@
// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <windows.h>
#include "v8-support.h"
#include "platform.h"
#include <process.h>
class Sampler::PlatformData : public Malloced {
public:
// Get a handle to the calling thread. This is the thread that we are
// going to profile. We need to make a copy of the handle because we are
// going to use it in the sampler thread. Using GetThreadHandle() will
// not work in this case. We're using OpenThread because DuplicateHandle
// for some reason doesn't work in Chrome's sandbox.
PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
THREAD_SUSPEND_RESUME |
THREAD_QUERY_INFORMATION,
false,
GetCurrentThreadId())) {}
~PlatformData() {
if (profiled_thread_ != NULL) {
CloseHandle(profiled_thread_);
profiled_thread_ = NULL;
}
}
HANDLE profiled_thread() { return profiled_thread_; }
private:
HANDLE profiled_thread_;
};
class SamplerThread : public Thread {
public:
SamplerThread(int interval, Sampler* sampler)
: Thread("SamplerThread"),
interval_(interval),
sampler_(sampler) {}
static void StartSampler(Sampler* sampler) {
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval(), sampler);
instance_->Start();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
}
static void StopSampler() {
instance_->Join();
delete instance_;
instance_ = NULL;
}
// Implement Thread::Run().
virtual void Run() {
while (sampler_->IsActive()) {
SampleContext(sampler_);
OS::Sleep(interval_);
}
}
void SampleContext(Sampler* sampler) {
HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
if (profiled_thread == NULL)
return;
// Context used for sampling the register state of the profiled thread.
CONTEXT context;
memset(&context, 0, sizeof(context));
TickSample sample_obj;
TickSample* sample = &sample_obj;
static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
if (SuspendThread(profiled_thread) == kSuspendFailed)
return;
context.ContextFlags = CONTEXT_FULL;
if (GetThreadContext(profiled_thread, &context) != 0) {
#if V8_HOST_ARCH_X64
sample->pc = reinterpret_cast<Address>(context.Rip);
sample->sp = reinterpret_cast<Address>(context.Rsp);
sample->fp = reinterpret_cast<Address>(context.Rbp);
#else
sample->pc = reinterpret_cast<Address>(context.Eip);
sample->sp = reinterpret_cast<Address>(context.Esp);
sample->fp = reinterpret_cast<Address>(context.Ebp);
#endif
sampler->SampleStack(sample);
sampler->Tick(sample);
}
ResumeThread(profiled_thread);
}
Sampler* sampler_;
const int interval_;
// Protects the process wide state below.
static SamplerThread* instance_;
DISALLOW_COPY_AND_ASSIGN(SamplerThread);
};
SamplerThread* SamplerThread::instance_ = NULL;
Sampler::Sampler(int interval, bool profiling)
: interval_(interval),
profiling_(profiling),
active_(false),
data_(new PlatformData) {
}
Sampler::~Sampler() {
ASSERT(!IsActive());
delete data_;
}
void Sampler::Start() {
ASSERT(!IsActive());
SetActive(true);
SamplerThread::StartSampler(this);
}
void Sampler::Stop() {
ASSERT(IsActive());
SetActive(false);
SamplerThread::StopSampler();
}
static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
static unsigned int __stdcall ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
thread->Run();
return 0;
}
class Thread::PlatformData : public Malloced {
public:
explicit PlatformData(HANDLE thread) : thread_(thread) {}
HANDLE thread_;
unsigned thread_id_;
};
// Initialize a Win32 thread object. The thread has an invalid thread
// handle until it is started.
Thread::Thread(const char* name)
: stack_size_(0) {
data_ = new PlatformData(kNoThread);
set_name(name);
}
void Thread::set_name(const char* name) {
strncpy(name_, name, sizeof(name_));
name_[sizeof(name_) - 1] = '\0';
}
// Close our own handle for the thread.
Thread::~Thread() {
if (data_->thread_ != kNoThread) CloseHandle(data_->thread_);
delete data_;
}
// Create a new thread. It is important to use _beginthreadex() instead of
// the Win32 function CreateThread(), because the CreateThread() does not
// initialize thread specific structures in the C runtime library.
void Thread::Start() {
data_->thread_ = reinterpret_cast<HANDLE>(
_beginthreadex(NULL,
static_cast<unsigned>(stack_size_),
ThreadEntry,
this,
0,
&data_->thread_id_));
}
// Wait for thread to terminate.
void Thread::Join() {
if (data_->thread_id_ != GetCurrentThreadId()) {
WaitForSingleObject(data_->thread_, INFINITE);
}
}
void OS::Sleep(int milliseconds) {
::Sleep(milliseconds);
}

View File

@ -20,8 +20,16 @@
#define LOG(text) printf("Profiler: %s\n", text)
#endif
#include <stdint.h>
typedef uint8 byte;
#ifdef _MSC_VER
typedef __int8 byte;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
typedef uint8 byte;
#endif
typedef byte* Address;
class MapEntry {
@ -163,7 +171,6 @@ class OS {
class Thread {
public:
// Create new thread.
explicit Thread(const char* name);
virtual ~Thread();
@ -265,7 +272,6 @@ class Sampler {
const int interval_;
const bool profiling_;
const bool synchronous_;
Atomic32 active_;
PlatformData* data_; // Platform specific data.
};

View File

@ -36,16 +36,16 @@
*
* ***** END LICENSE BLOCK ***** */
#include <pthread.h>
#include <stdlib.h>
#include "thread_helper.h"
#include "nscore.h"
#include "mozilla/TimeStamp.h"
using mozilla::TimeStamp;
using mozilla::TimeDuration;
// TODO Merge into Sampler.h
extern pthread_key_t pkey_stack;
extern mozilla::tls::key pkey_stack;
extern mozilla::tls::key pkey_ticker;
extern bool stack_key_initialized;
#define SAMPLER_INIT() mozilla_sampler_init();
@ -87,7 +87,22 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
# define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
#elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
# define STORE_SEQUENCER() asm volatile("" ::: "memory")
# if defined(_MSC_VER)
// MSVC2005 has a name collision bug caused when both <intrin.h> and <windows.h> are included together.
# define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
# define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
# include <intrin.h>
// 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
@ -181,7 +196,7 @@ public:
// been written such that mStack is always consistent.
mStack[mStackPointer] = aName;
// Prevent the optimizer from re-ordering these instructions
asm("":::"memory");
STORE_SEQUENCER();
mStackPointer++;
}
void pop()
@ -198,15 +213,15 @@ public:
}
// Keep a list of active checkpoints
const char *mStack[1024];
char const * volatile mStack[1024];
// Keep a list of active markers to be applied to the next sample taken
const char *mMarkers[1024];
sig_atomic_t mStackPointer;
sig_atomic_t mMarkerPointer;
sig_atomic_t mDroppedStackEntries;
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.
sig_atomic_t mQueueClearMarker;
volatile mozilla::sig_safe_t mQueueClearMarker;
};
inline void* mozilla_sampler_call_enter(const char *aInfo)
@ -216,7 +231,7 @@ inline void* mozilla_sampler_call_enter(const char *aInfo)
if (!stack_key_initialized)
return NULL;
Stack *stack = (Stack*)pthread_getspecific(pkey_stack);
Stack *stack = mozilla::tls::get<Stack>(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
@ -245,7 +260,7 @@ inline void mozilla_sampler_call_exit(void *aHandle)
inline void mozilla_sampler_add_marker(const char *aMarker)
{
Stack *stack = (Stack*)pthread_getspecific(pkey_stack);
Stack *stack = mozilla::tls::get<Stack>(pkey_stack);
if (!stack) {
return;
}

View File

@ -0,0 +1,109 @@
/* -*- 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):
* Ehsan Akhgari <ehsan@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 ***** */
// Cross-platform lightweight thread local data wrappers
#if defined(XP_WIN)
// This file will get included in any file that wants to add
// a profiler mark. In order to not bring <windows.h> together
// we just include windef.h and winbase.h which are sufficient
// to get the prototypes for the Tls* functions.
# include <windef.h>
# include <winbase.h>
#else
# include <pthread.h>
# include <signal.h>
#endif
namespace mozilla {
#if defined(XP_WIN)
typedef unsigned long sig_safe_t;
#else
typedef sig_atomic_t sig_safe_t;
#endif
namespace tls {
#if defined(XP_WIN)
typedef DWORD key;
template <typename T>
static T* get(key mykey) {
return (T*) TlsGetValue(mykey);
}
template <typename T>
static bool set(key mykey, const T* value) {
return TlsSetValue(mykey, const_cast<T*>(value));
}
static inline bool create(key* mykey) {
key newkey = TlsAlloc();
if (newkey == TLS_OUT_OF_INDEXES) {
return false;
}
*mykey = newkey;
return true;
}
#else
typedef pthread_key_t key;
template <typename T>
static T* get(key mykey) {
return (T*) pthread_getspecific(mykey);
}
template <typename T>
static bool set(key mykey, const T* value) {
return !pthread_setspecific(mykey, value);
}
static bool create(key* mykey) {
return !pthread_key_create(mykey, NULL);
}
#endif
}
}

View File

@ -51,7 +51,11 @@
#warning Please add support for your architecture in chromium_types.h
#endif
typedef int32_t Atomic32;
#ifdef _MSC_VER
typedef __int32 Atomic32;
#else
typedef int32_t Atomic32;
#endif
#if defined(V8_HOST_ARCH_X64) || defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_ARM)
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {