зеркало из https://github.com/mozilla/gecko-dev.git
Bug 779291: Implement SPS stackwalk using the breakpad unwinder. r=bgirard,glandium
bug 850089 - fix SPS with disable-crashreporter. Patch by Ted Mielczarek <ted@mielczarek.org>, r=glandium Bug 850132 - SPS breakpad unwind (bug 779291) breaks Win64 builds with "Unsupported platform". Patch by Makoto Kato <m_kato@ga2.so-net.ne.jp>, r=ted
This commit is contained in:
Родитель
1f5c5c954a
Коммит
fdeb8cf6c2
|
@ -1354,7 +1354,7 @@ XPCJSRuntime::~XPCJSRuntime()
|
|||
}
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
// Tell the profiler that the runtime is gone
|
||||
if (ProfileStack *stack = mozilla_profile_stack())
|
||||
if (PseudoStack *stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(nullptr);
|
||||
#endif
|
||||
|
||||
|
@ -2561,7 +2561,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
if (ProfileStack *stack = mozilla_profile_stack())
|
||||
if (PseudoStack *stack = mozilla_get_pseudo_stack())
|
||||
stack->sampleRuntime(mJSRuntime);
|
||||
#endif
|
||||
JS_SetAccumulateTelemetryCallback(mJSRuntime, AccumulateTelemetryCallback);
|
||||
|
|
|
@ -12,8 +12,10 @@ include $(DEPTH)/config/autoconf.mk
|
|||
MODULE = breakpad_common
|
||||
LIBRARY_NAME = breakpad_common_s
|
||||
ifneq (WINNT,$(OS_TARGET))
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_LIBRARY_NAME = host_breakpad_common_s
|
||||
endif
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/..
|
||||
|
||||
|
@ -34,6 +36,7 @@ CPPSRCS += \
|
|||
dwarf/dwarf2diehandler.cc \
|
||||
dwarf_line_to_module.cc \
|
||||
$(NULL)
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_CPPSRCS = \
|
||||
string_conversion.cc \
|
||||
module.cc \
|
||||
|
@ -47,6 +50,7 @@ HOST_CPPSRCS = \
|
|||
dwarf_line_to_module.cc \
|
||||
$(NULL)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
CPPSRCS += \
|
||||
|
@ -99,8 +103,10 @@ OS_CXXFLAGS += -DHAVE_MACH_O_NLIST_H
|
|||
endif
|
||||
|
||||
ifneq (WINNT,$(OS_TARGET))
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_CSRCS = $(CSRCS)
|
||||
endif
|
||||
endif
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -10,6 +10,8 @@ VPATH = @srcdir@
|
|||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = breakpad_dwarf
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_LIBRARY_NAME = host_breakpad_dwarf_s
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../..
|
||||
|
@ -20,6 +22,7 @@ HOST_CPPSRCS = \
|
|||
dwarf2reader.cc \
|
||||
functioninfo.cc \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
# This code is only compiled for build-time tools,
|
||||
# so enabling RTTI should be fine.
|
||||
|
|
|
@ -11,7 +11,9 @@ include $(DEPTH)/config/autoconf.mk
|
|||
|
||||
MODULE = breakpad_linux_common
|
||||
LIBRARY_NAME = breakpad_linux_common_s
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_LIBRARY_NAME = host_breakpad_linux_common_s
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \
|
||||
|
@ -38,6 +40,7 @@ endif
|
|||
|
||||
DEFINES += -DNO_STABS_SUPPORT
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_CPPSRCS = \
|
||||
dump_symbols.cc \
|
||||
elf_symbols_to_module.cc \
|
||||
|
@ -47,6 +50,7 @@ HOST_CPPSRCS = \
|
|||
linux_libc_support.cc \
|
||||
memory_mapped_file.cc \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -626,7 +626,7 @@ bool LoadSymbols(const string& obj_file,
|
|||
}
|
||||
}
|
||||
|
||||
if (!found_debug_info_section) {
|
||||
if (!found_debug_info_section && symbol_data != ONLY_CFI) {
|
||||
fprintf(stderr, "%s: file contains no debugging information"
|
||||
" (no \".stab\" or \".debug_info\" sections)\n",
|
||||
obj_file.c_str());
|
||||
|
|
|
@ -11,7 +11,9 @@ include $(DEPTH)/config/autoconf.mk
|
|||
|
||||
MODULE = breakpad_sps_common
|
||||
LIBRARY_NAME = breakpad_sps_common_s
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_LIBRARY_NAME = host_breakpad_sps_common_s
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../.. -I$(srcdir)/..
|
||||
|
||||
|
@ -34,10 +36,12 @@ CPPSRCS = \
|
|||
stack_frame_symbolizer.cc \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
HOST_CPPSRCS = \
|
||||
logging.cc \
|
||||
pathname_stripper.cc \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
# need static lib
|
||||
FORCE_STATIC_LIB = 1
|
||||
|
|
|
@ -41,3 +41,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
|||
|
||||
if CONFIG['MOZ_CRASHREPORTER']:
|
||||
PARALLEL_DIRS += ['crashreporter']
|
||||
elif CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
# Profiler requires some crashreporter code,
|
||||
# so build it even if crashreporter is disabled.
|
||||
PARALLEL_DIRS += [
|
||||
'crashreporter/google-breakpad/src/common',
|
||||
'crashreporter/google-breakpad/src/processor',
|
||||
]
|
||||
if CONFIG['OS_ARCH'] == 'Darwin':
|
||||
PARALLEL_DIRS += ['crashreporter/google-breakpad/src/common/mac']
|
||||
elif CONFIG['OS_ARCH'] == 'Linux':
|
||||
PARALLEL_DIRS += ['crashreporter/google-breakpad/src/common/linux']
|
||||
|
|
|
@ -127,15 +127,20 @@ SHARED_LIBRARY_LIBS += $(DEPTH)/widget/xremoteclient/$(LIB_PREFIX)xremote_client
|
|||
LOCAL_INCLUDES += -I$(topsrcdir)/widget/xremoteclient
|
||||
endif
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
SHARED_LIBRARY_LIBS += $(DEPTH)/toolkit/crashreporter/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX)
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX)
|
||||
ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
ifneq (,$(MOZ_CRASHREPORTER)$(MOZ_ENABLE_PROFILER_SPS))
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX)
|
||||
$(NULL)
|
||||
endif
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -145,13 +150,19 @@ SHARED_LIBRARY_LIBS += \
|
|||
$(NULL)
|
||||
endif
|
||||
|
||||
ifdef MOZ_CRASHREPORTER
|
||||
SHARED_LIBRARY_LIBS += $(DEPTH)/toolkit/crashreporter/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX)
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX)
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),Darwin)
|
||||
SHARED_LIBRARY_LIBS += \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/$(LIB_PREFIX)crash_generation_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/mac/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/$(LIB_PREFIX)breakpad_mac_common_s.$(LIB_SUFFIX)
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
|
@ -160,8 +171,6 @@ SHARED_LIBRARY_LIBS += \
|
|||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/$(LIB_PREFIX)minidump_writer_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ EXPORTS += \
|
|||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/mozglue/linker \
|
||||
-I$(topsrcdir)/ipc/chromium/src \
|
||||
-I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src \
|
||||
$(NULL)
|
||||
|
||||
ifneq (,$(filter armeabi,$(ANDROID_CPU_ARCH)))
|
||||
|
@ -45,6 +46,10 @@ CPPSRCS = \
|
|||
nsProfilerFactory.cpp \
|
||||
nsProfiler.cpp \
|
||||
TableTicker.cpp \
|
||||
TableTicker2.cpp \
|
||||
UnwinderThread2.cpp \
|
||||
ProfileEntry2.cpp \
|
||||
local_debug_info_symbolizer.cc \
|
||||
JSObjectBuilder.cpp \
|
||||
JSCustomObjectBuilder.cpp \
|
||||
$(NULL)
|
||||
|
@ -69,6 +74,8 @@ CPPSRCS += \
|
|||
shared-libraries-macos.cc \
|
||||
platform-macos.cc \
|
||||
$(NULL)
|
||||
CMMSRCS += \
|
||||
shim_mac_dump_syms.mm
|
||||
endif
|
||||
|
||||
ifeq ($(OS_TARGET),WINNT)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SPS_PLATFORM_MACROS_H
|
||||
#define SPS_PLATFORM_MACROS_H
|
||||
|
||||
/* Define platform selection macros in a consistent way. Don't add
|
||||
anything else to this file, so it can remain freestanding. The
|
||||
primary factorisation is on (ARCH,OS) pairs ("PLATforms") but ARCH_
|
||||
and OS_ macros are defined too, since they are sometimes
|
||||
convenient. */
|
||||
|
||||
#undef SPS_PLAT_arm_android
|
||||
#undef SPS_PLAT_amd64_linux
|
||||
#undef SPS_PLAT_x86_linux
|
||||
#undef SPS_PLAT_amd64_darwin
|
||||
#undef SPS_PLAT_x86_darwin
|
||||
#undef SPS_PLAT_x86_windows
|
||||
#undef SPS_PLAT_amd64_windows
|
||||
|
||||
#undef SPS_ARCH_arm
|
||||
#undef SPS_ARCH_x86
|
||||
#undef SPS_ARCH_amd64
|
||||
|
||||
#undef SPS_OS_android
|
||||
#undef SPS_OS_linux
|
||||
#undef SPS_OS_darwin
|
||||
#undef SPS_OS_windows
|
||||
|
||||
#if defined(__linux__) && defined(__x86_64__)
|
||||
# define SPS_PLAT_amd64_linux 1
|
||||
# define SPS_ARCH_amd64 1
|
||||
# define SPS_OS_linux 1
|
||||
|
||||
#elif defined(__ANDROID__) && defined(__arm__)
|
||||
# define SPS_PLAT_arm_android 1
|
||||
# define SPS_ARCH_arm 1
|
||||
# define SPS_OS_android 1
|
||||
|
||||
#elif defined(__ANDROID__) && defined(__i386__)
|
||||
# define SPS_PLAT_x86_android 1
|
||||
# define SPS_ARCH_x86 1
|
||||
# define SPS_OS_android 1
|
||||
|
||||
#elif defined(__linux__) && defined(__i386__)
|
||||
# define SPS_PLAT_x86_linux 1
|
||||
# define SPS_ARCH_x86 1
|
||||
# define SPS_OS_linux 1
|
||||
|
||||
#elif defined(__APPLE__) && defined(__x86_64__)
|
||||
# define SPS_PLAT_amd64_darwin 1
|
||||
# define SPS_ARCH_amd64 1
|
||||
# define SPS_OS_darwin 1
|
||||
|
||||
#elif defined(__APPLE__) && defined(__i386__)
|
||||
# define SPS_PLAT_x86_darwin 1
|
||||
# define SPS_ARCH_x86 1
|
||||
# define SPS_OS_darwin 1
|
||||
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
# define SPS_PLAT_x86_windows 1
|
||||
# define SPS_ARCH_x86 1
|
||||
# define SPS_OS_windows 1
|
||||
|
||||
#elif defined(_MSC_VER) && defined(_M_X64)
|
||||
# define SPS_PLAT_amd64_windows 1
|
||||
# define SPS_ARCH_amd64 1
|
||||
# define SPS_OS_windows 1
|
||||
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#endif /* ndef SPS_PLATFORM_MACROS_H */
|
|
@ -0,0 +1,338 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <iostream>
|
||||
#include "sps_sampler.h"
|
||||
#include "platform.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
// JSON
|
||||
#include "JSObjectBuilder.h"
|
||||
|
||||
// Self
|
||||
#include "ProfileEntry2.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// BEGIN ProfileEntry2
|
||||
|
||||
ProfileEntry2::ProfileEntry2()
|
||||
: mTagData(NULL)
|
||||
, mTagName(0)
|
||||
{ }
|
||||
|
||||
// aTagData must not need release (i.e. be a string from the text segment)
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, const char *aTagData)
|
||||
: mTagData(aTagData)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, void *aTagPtr)
|
||||
: mTagPtr(aTagPtr)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, double aTagFloat)
|
||||
: mTagFloat(aTagFloat)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, uintptr_t aTagOffset)
|
||||
: mTagOffset(aTagOffset)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, Address aTagAddress)
|
||||
: mTagAddress(aTagAddress)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, int aTagLine)
|
||||
: mTagLine(aTagLine)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
ProfileEntry2::ProfileEntry2(char aTagName, char aTagChar)
|
||||
: mTagChar(aTagChar)
|
||||
, mTagName(aTagName)
|
||||
{ }
|
||||
|
||||
bool ProfileEntry2::is_ent_hint(char hintChar) {
|
||||
return mTagName == 'h' && mTagChar == hintChar;
|
||||
}
|
||||
|
||||
bool ProfileEntry2::is_ent_hint() {
|
||||
return mTagName == 'h';
|
||||
}
|
||||
|
||||
bool ProfileEntry2::is_ent(char tagChar) {
|
||||
return mTagName == tagChar;
|
||||
}
|
||||
|
||||
void* ProfileEntry2::get_tagPtr() {
|
||||
// No consistency checking. Oh well.
|
||||
return mTagPtr;
|
||||
}
|
||||
|
||||
void ProfileEntry2::log()
|
||||
{
|
||||
// There is no compiler enforced mapping between tag chars
|
||||
// and union variant fields, so the following was derived
|
||||
// by looking through all the use points of TableTicker.cpp.
|
||||
// mTagData (const char*) m,c,s
|
||||
// mTagPtr (void*) d,l,L, S(start-of-stack)
|
||||
// mTagLine (int) n,f
|
||||
// mTagChar (char) h
|
||||
// mTagFloat (double) r,t
|
||||
switch (mTagName) {
|
||||
case 'm': case 'c': case 's':
|
||||
LOGF("%c \"%s\"", mTagName, mTagData); break;
|
||||
case 'd': case 'l': case 'L': case 'S':
|
||||
LOGF("%c %p", mTagName, mTagPtr); break;
|
||||
case 'n': case 'f':
|
||||
LOGF("%c %d", mTagName, mTagLine); break;
|
||||
case 'h':
|
||||
LOGF("%c \'%c\'", mTagName, mTagChar); break;
|
||||
case 'r': case 't':
|
||||
LOGF("%c %f", mTagName, mTagFloat); break;
|
||||
default:
|
||||
LOGF("'%c' unknown_tag", mTagName); break;
|
||||
}
|
||||
}
|
||||
|
||||
// END ProfileEntry2
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// BEGIN ThreadProfile2
|
||||
|
||||
#define PROFILE_MAX_ENTRY 100000
|
||||
#define DYNAMIC_MAX_STRING 512
|
||||
|
||||
ThreadProfile2::ThreadProfile2(int aEntrySize, PseudoStack *aStack)
|
||||
: mWritePos(0)
|
||||
, mLastFlushPos(0)
|
||||
, mReadPos(0)
|
||||
, mEntrySize(aEntrySize)
|
||||
, mPseudoStack(aStack)
|
||||
, mMutex("ThreadProfile2::mMutex")
|
||||
{
|
||||
mEntries = new ProfileEntry2[mEntrySize];
|
||||
}
|
||||
|
||||
ThreadProfile2::~ThreadProfile2()
|
||||
{
|
||||
delete[] mEntries;
|
||||
}
|
||||
|
||||
void ThreadProfile2::addTag(ProfileEntry2 aTag)
|
||||
{
|
||||
// Called from signal, call only reentrant functions
|
||||
mEntries[mWritePos] = aTag;
|
||||
mWritePos = (mWritePos + 1) % mEntrySize;
|
||||
if (mWritePos == mReadPos) {
|
||||
// Keep one slot open
|
||||
mEntries[mReadPos] = ProfileEntry2();
|
||||
mReadPos = (mReadPos + 1) % mEntrySize;
|
||||
}
|
||||
// we also need to move the flush pos to ensure we
|
||||
// do not pass it
|
||||
if (mWritePos == mLastFlushPos) {
|
||||
mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
|
||||
}
|
||||
}
|
||||
|
||||
// flush the new entries
|
||||
void ThreadProfile2::flush()
|
||||
{
|
||||
mLastFlushPos = mWritePos;
|
||||
}
|
||||
|
||||
// discards all of the entries since the last flush()
|
||||
// NOTE: that if mWritePos happens to wrap around past
|
||||
// mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
|
||||
//
|
||||
// r = mReadPos
|
||||
// w = mWritePos
|
||||
// f = mLastFlushPos
|
||||
//
|
||||
// r f w
|
||||
// |-----------------------------|
|
||||
// | abcdefghijklmnopq | -> 'abcdefghijklmnopq'
|
||||
// |-----------------------------|
|
||||
//
|
||||
//
|
||||
// mWritePos and mReadPos have passed mLastFlushPos
|
||||
// f
|
||||
// w r
|
||||
// |-----------------------------|
|
||||
// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz|
|
||||
// |-----------------------------|
|
||||
// w
|
||||
// r
|
||||
// |-----------------------------|
|
||||
// |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> ''
|
||||
// |-----------------------------|
|
||||
//
|
||||
//
|
||||
// mWritePos will end up the same as mReadPos
|
||||
// r
|
||||
// w f
|
||||
// |-----------------------------|
|
||||
// |ABCDEFGHIJKLMklmnopqrstuvwxyz|
|
||||
// |-----------------------------|
|
||||
// r
|
||||
// w
|
||||
// |-----------------------------|
|
||||
// |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> ''
|
||||
// |-----------------------------|
|
||||
//
|
||||
//
|
||||
// mWritePos has moved past mReadPos
|
||||
// w r f
|
||||
// |-----------------------------|
|
||||
// |ABCDEFdefghijklmnopqrstuvwxyz|
|
||||
// |-----------------------------|
|
||||
// r w
|
||||
// |-----------------------------|
|
||||
// |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
|
||||
// |-----------------------------|
|
||||
|
||||
void ThreadProfile2::erase()
|
||||
{
|
||||
mWritePos = mLastFlushPos;
|
||||
}
|
||||
|
||||
char* ThreadProfile2::processDynamicTag(int readPos,
|
||||
int* tagsConsumed, char* tagBuff)
|
||||
{
|
||||
int readAheadPos = (readPos + 1) % mEntrySize;
|
||||
int tagBuffPos = 0;
|
||||
|
||||
// Read the string stored in mTagData until the null character is seen
|
||||
bool seenNullByte = false;
|
||||
while (readAheadPos != mLastFlushPos && !seenNullByte) {
|
||||
(*tagsConsumed)++;
|
||||
ProfileEntry2 readAheadEntry = mEntries[readAheadPos];
|
||||
for (size_t pos = 0; pos < sizeof(void*); pos++) {
|
||||
tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
|
||||
if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
|
||||
seenNullByte = true;
|
||||
break;
|
||||
}
|
||||
tagBuffPos++;
|
||||
}
|
||||
if (!seenNullByte)
|
||||
readAheadPos = (readAheadPos + 1) % mEntrySize;
|
||||
}
|
||||
return tagBuff;
|
||||
}
|
||||
|
||||
JSCustomObject* ThreadProfile2::ToJSObject(JSContext *aCx)
|
||||
{
|
||||
JSObjectBuilder b(aCx);
|
||||
|
||||
JSCustomObject *profile = b.CreateObject();
|
||||
JSCustomArray *samples = b.CreateArray();
|
||||
b.DefineProperty(profile, "samples", samples);
|
||||
|
||||
JSCustomObject *sample = NULL;
|
||||
JSCustomArray *frames = NULL;
|
||||
|
||||
int readPos = mReadPos;
|
||||
while (readPos != mLastFlushPos) {
|
||||
// Number of tag consumed
|
||||
int incBy = 1;
|
||||
ProfileEntry2 entry = mEntries[readPos];
|
||||
|
||||
// Read ahead to the next tag, if it's a 'd' tag process it now
|
||||
const char* tagStringData = entry.mTagData;
|
||||
int readAheadPos = (readPos + 1) % mEntrySize;
|
||||
char tagBuff[DYNAMIC_MAX_STRING];
|
||||
// Make sure the string is always null terminated if it fills up
|
||||
// DYNAMIC_MAX_STRING-2
|
||||
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
|
||||
|
||||
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
|
||||
tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
|
||||
}
|
||||
|
||||
switch (entry.mTagName) {
|
||||
case 's':
|
||||
sample = b.CreateObject();
|
||||
b.DefineProperty(sample, "name", tagStringData);
|
||||
frames = b.CreateArray();
|
||||
b.DefineProperty(sample, "frames", frames);
|
||||
b.ArrayPush(samples, sample);
|
||||
break;
|
||||
case 'r':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "frameNumber", entry.mTagLine);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
{
|
||||
if (sample) {
|
||||
b.DefineProperty(sample, "time", entry.mTagFloat);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
case 'l':
|
||||
{
|
||||
if (sample) {
|
||||
JSCustomObject *frame = b.CreateObject();
|
||||
if (entry.mTagName == 'l') {
|
||||
// Bug 753041
|
||||
// We need a double cast here to tell GCC that we don't want to sign
|
||||
// extend 32-bit addresses starting with 0xFXXXXXX.
|
||||
unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr;
|
||||
snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
|
||||
b.DefineProperty(frame, "location", tagBuff);
|
||||
} else {
|
||||
b.DefineProperty(frame, "location", tagStringData);
|
||||
readAheadPos = (readPos + incBy) % mEntrySize;
|
||||
if (readAheadPos != mLastFlushPos &&
|
||||
mEntries[readAheadPos].mTagName == 'n') {
|
||||
b.DefineProperty(frame, "line",
|
||||
mEntries[readAheadPos].mTagLine);
|
||||
incBy++;
|
||||
}
|
||||
}
|
||||
b.ArrayPush(frames, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
readPos = (readPos + incBy) % mEntrySize;
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
PseudoStack* ThreadProfile2::GetPseudoStack()
|
||||
{
|
||||
return mPseudoStack;
|
||||
}
|
||||
|
||||
mozilla::Mutex* ThreadProfile2::GetMutex()
|
||||
{
|
||||
return &mMutex;
|
||||
}
|
||||
|
||||
// END ThreadProfile2
|
||||
////////////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZ_PROFILE_ENTRY_H
|
||||
#define MOZ_PROFILE_ENTRY_H
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
class ThreadProfile2;
|
||||
|
||||
class ProfileEntry2
|
||||
{
|
||||
public:
|
||||
ProfileEntry2();
|
||||
|
||||
// aTagData must not need release (i.e. be a string from the text segment)
|
||||
ProfileEntry2(char aTagName, const char *aTagData);
|
||||
ProfileEntry2(char aTagName, void *aTagPtr);
|
||||
ProfileEntry2(char aTagName, double aTagFloat);
|
||||
ProfileEntry2(char aTagName, uintptr_t aTagOffset);
|
||||
ProfileEntry2(char aTagName, Address aTagAddress);
|
||||
ProfileEntry2(char aTagName, int aTagLine);
|
||||
ProfileEntry2(char aTagName, char aTagChar);
|
||||
friend std::ostream& operator<<(std::ostream& stream, const ProfileEntry2& entry);
|
||||
bool is_ent_hint(char hintChar);
|
||||
bool is_ent_hint();
|
||||
bool is_ent(char tagName);
|
||||
void* get_tagPtr();
|
||||
void log();
|
||||
|
||||
private:
|
||||
friend class ThreadProfile2;
|
||||
union {
|
||||
const char* mTagData;
|
||||
char mTagChars[sizeof(void*)];
|
||||
void* mTagPtr;
|
||||
double mTagFloat;
|
||||
Address mTagAddress;
|
||||
uintptr_t mTagOffset;
|
||||
int mTagLine;
|
||||
char mTagChar;
|
||||
};
|
||||
char mTagName;
|
||||
};
|
||||
|
||||
|
||||
class ThreadProfile2
|
||||
{
|
||||
public:
|
||||
ThreadProfile2(int aEntrySize, PseudoStack *aStack);
|
||||
~ThreadProfile2();
|
||||
void addTag(ProfileEntry2 aTag);
|
||||
void flush();
|
||||
void erase();
|
||||
char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
|
||||
friend std::ostream& operator<<(std::ostream& stream,
|
||||
const ThreadProfile2& profile);
|
||||
JSCustomObject *ToJSObject(JSContext *aCx);
|
||||
PseudoStack* GetPseudoStack();
|
||||
mozilla::Mutex* GetMutex();
|
||||
private:
|
||||
// Circular buffer 'Keep One Slot Open' implementation
|
||||
// for simplicity
|
||||
ProfileEntry2* mEntries;
|
||||
int mWritePos; // points to the next entry we will write to
|
||||
int mLastFlushPos; // points to the next entry since the last flush()
|
||||
int mReadPos; // points to the next entry we will read to
|
||||
int mEntrySize;
|
||||
PseudoStack* mPseudoStack;
|
||||
mozilla::Mutex mMutex;
|
||||
};
|
||||
|
||||
#endif /* ndef MOZ_PROFILE_ENTRY_H */
|
|
@ -31,6 +31,7 @@
|
|||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "PlatformMacros.h"
|
||||
|
||||
// JS
|
||||
#include "jsdbgapi.h"
|
||||
|
@ -82,13 +83,7 @@ using namespace mozilla;
|
|||
|
||||
static const int DYNAMIC_MAX_STRING = 512;
|
||||
|
||||
mozilla::ThreadLocal<ProfileStack *> tlsStack;
|
||||
mozilla::ThreadLocal<TableTicker *> tlsTicker;
|
||||
// We need to track whether we've been initialized otherwise
|
||||
// we end up using tlsStack without initializing it.
|
||||
// Because tlsStack is totally opaque to us we can't reuse
|
||||
// it as the flag itself.
|
||||
bool stack_key_initialized;
|
||||
static mozilla::ThreadLocal<TableTicker *> tlsTicker;
|
||||
|
||||
TimeStamp sLastTracerEvent;
|
||||
int sFrameNumber = 0;
|
||||
|
@ -155,7 +150,7 @@ typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagSt
|
|||
class ThreadProfile
|
||||
{
|
||||
public:
|
||||
ThreadProfile(int aEntrySize, ProfileStack *aStack)
|
||||
ThreadProfile(int aEntrySize, PseudoStack *aStack)
|
||||
: mWritePos(0)
|
||||
, mLastFlushPos(0)
|
||||
, mReadPos(0)
|
||||
|
@ -416,7 +411,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
ProfileStack* GetStack()
|
||||
PseudoStack* GetStack()
|
||||
{
|
||||
return mStack;
|
||||
}
|
||||
|
@ -428,7 +423,7 @@ private:
|
|||
int mLastFlushPos; // points to the next entry since the last flush()
|
||||
int mReadPos; // points to the next entry we will read to
|
||||
int mEntrySize;
|
||||
ProfileStack *mStack;
|
||||
PseudoStack *mStack;
|
||||
};
|
||||
|
||||
class SaveProfileTask;
|
||||
|
@ -444,7 +439,7 @@ hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature)
|
|||
|
||||
class TableTicker: public Sampler {
|
||||
public:
|
||||
TableTicker(int aInterval, int aEntrySize, ProfileStack *aStack,
|
||||
TableTicker(int aInterval, int aEntrySize, PseudoStack *aStack,
|
||||
const char** aFeatures, uint32_t aFeatureCount)
|
||||
: Sampler(aInterval, true)
|
||||
, mPrimaryThreadProfile(aEntrySize, aStack)
|
||||
|
@ -586,7 +581,7 @@ public:
|
|||
// being thread safe. Bug 750989.
|
||||
if (stream.is_open()) {
|
||||
JSAutoCompartment autoComp(cx, obj);
|
||||
JSObject* profileObj = mozilla_sampler_get_profile_data(cx);
|
||||
JSObject* profileObj = mozilla_sampler_get_profile_data1(cx);
|
||||
jsval val = OBJECT_TO_JSVAL(profileObj);
|
||||
JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
|
||||
stream.close();
|
||||
|
@ -729,7 +724,7 @@ void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
|
|||
|
||||
static
|
||||
void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
|
||||
ProfileStack *stack, void *lastpc)
|
||||
PseudoStack *stack, void *lastpc)
|
||||
{
|
||||
int lineno = -1;
|
||||
|
||||
|
@ -843,7 +838,7 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
|
|||
if (NS_SUCCEEDED(rv)) {
|
||||
aProfile.addTag(ProfileEntry('s', "(root)"));
|
||||
|
||||
ProfileStack* stack = aProfile.GetStack();
|
||||
PseudoStack* stack = aProfile.GetStack();
|
||||
uint32_t pseudoStackPos = 0;
|
||||
|
||||
/* We have two stacks, the native C stack we extracted from unwinding,
|
||||
|
@ -882,7 +877,7 @@ void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
|
|||
#endif
|
||||
|
||||
static
|
||||
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
|
||||
void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
|
||||
{
|
||||
// Sample
|
||||
// 's' tag denotes the start of a sample block
|
||||
|
@ -916,7 +911,7 @@ unsigned int sCurrentEventGeneration = 0;
|
|||
void TableTicker::Tick(TickSample* sample)
|
||||
{
|
||||
// Marker(s) come before the sample
|
||||
ProfileStack* stack = mPrimaryThreadProfile.GetStack();
|
||||
PseudoStack* stack = mPrimaryThreadProfile.GetStack();
|
||||
for (int i = 0; stack->getMarker(i) != NULL; i++) {
|
||||
addDynamicTag(mPrimaryThreadProfile, 'm', stack->getMarker(i));
|
||||
}
|
||||
|
@ -1002,19 +997,62 @@ std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry)
|
|||
return stream;
|
||||
}
|
||||
|
||||
void mozilla_sampler_init()
|
||||
bool sps_version2()
|
||||
{
|
||||
static int version = 0; // Raced on, potentially
|
||||
|
||||
if (version == 0) {
|
||||
bool allow2 = false; // Is v2 allowable on this platform?
|
||||
# if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|
||||
|| defined(SPS_PLAT_x86_linux)
|
||||
allow2 = true;
|
||||
# elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
|
||||
|| defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android) \
|
||||
|| defined(SPS_PLAT_amd64_windows)
|
||||
allow2 = false;
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
|
||||
bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != NULL; // Has v2 been requested?
|
||||
|
||||
bool elfhackd = false;
|
||||
# if defined(USE_ELF_HACK)
|
||||
bool elfhackd = true;
|
||||
# endif
|
||||
|
||||
if (req2 && allow2) {
|
||||
version = 2;
|
||||
LOG("------------------- MOZ_PROFILER_NEW set -------------------");
|
||||
} else if (req2 && !allow2) {
|
||||
version = 1;
|
||||
LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
|
||||
LOG("---------- but is not available on this platform -----------");
|
||||
} else if (req2 && elfhackd) {
|
||||
version = 1;
|
||||
LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
|
||||
LOG("--- but this build was not done with --disable-elf-hack ----");
|
||||
} else {
|
||||
version = 1;
|
||||
LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
|
||||
}
|
||||
}
|
||||
return version == 2;
|
||||
}
|
||||
|
||||
void mozilla_sampler_init1()
|
||||
{
|
||||
if (stack_key_initialized)
|
||||
return;
|
||||
|
||||
if (!tlsStack.init() || !tlsTicker.init()) {
|
||||
if (!tlsPseudoStack.init() || !tlsTicker.init()) {
|
||||
LOG("Failed to init.");
|
||||
return;
|
||||
}
|
||||
stack_key_initialized = true;
|
||||
|
||||
ProfileStack *stack = new ProfileStack();
|
||||
tlsStack.set(stack);
|
||||
PseudoStack *stack = new PseudoStack();
|
||||
tlsPseudoStack.set(stack);
|
||||
|
||||
// Allow the profiler to be started using signals
|
||||
OS::RegisterStartHandler();
|
||||
|
@ -1032,11 +1070,11 @@ void mozilla_sampler_init()
|
|||
, "stackwalk"
|
||||
#endif
|
||||
};
|
||||
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
|
||||
features, sizeof(features)/sizeof(const char*));
|
||||
mozilla_sampler_start1(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
|
||||
features, sizeof(features)/sizeof(const char*));
|
||||
}
|
||||
|
||||
void mozilla_sampler_shutdown()
|
||||
void mozilla_sampler_shutdown1()
|
||||
{
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (t) {
|
||||
|
@ -1051,13 +1089,13 @@ void mozilla_sampler_shutdown()
|
|||
}
|
||||
}
|
||||
|
||||
mozilla_sampler_stop();
|
||||
mozilla_sampler_stop1();
|
||||
// We can't delete the Stack because we can be between a
|
||||
// sampler call_enter/call_exit point.
|
||||
// TODO Need to find a safe time to delete Stack
|
||||
}
|
||||
|
||||
void mozilla_sampler_save()
|
||||
void mozilla_sampler_save1()
|
||||
{
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
|
@ -1070,7 +1108,7 @@ void mozilla_sampler_save()
|
|||
t->HandleSaveRequest();
|
||||
}
|
||||
|
||||
char* mozilla_sampler_get_profile()
|
||||
char* mozilla_sampler_get_profile1()
|
||||
{
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
|
@ -1088,7 +1126,7 @@ char* mozilla_sampler_get_profile()
|
|||
return rtn;
|
||||
}
|
||||
|
||||
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
|
||||
JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx)
|
||||
{
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
|
@ -1099,7 +1137,7 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
|
|||
}
|
||||
|
||||
|
||||
const char** mozilla_sampler_get_features()
|
||||
const char** mozilla_sampler_get_features1()
|
||||
{
|
||||
static const char* features[] = {
|
||||
#if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
|
||||
|
@ -1117,19 +1155,19 @@ const char** mozilla_sampler_get_features()
|
|||
}
|
||||
|
||||
// Values are only honored on the first start
|
||||
void mozilla_sampler_start(int aProfileEntries, int aInterval,
|
||||
const char** aFeatures, uint32_t aFeatureCount)
|
||||
void mozilla_sampler_start1(int aProfileEntries, int aInterval,
|
||||
const char** aFeatures, uint32_t aFeatureCount)
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
mozilla_sampler_init();
|
||||
mozilla_sampler_init1();
|
||||
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
PseudoStack *stack = tlsPseudoStack.get();
|
||||
if (!stack) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla_sampler_stop();
|
||||
mozilla_sampler_stop1();
|
||||
|
||||
TableTicker *t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
|
||||
aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
|
||||
|
@ -1144,10 +1182,10 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval,
|
|||
os->NotifyObservers(nullptr, "profiler-started", nullptr);
|
||||
}
|
||||
|
||||
void mozilla_sampler_stop()
|
||||
void mozilla_sampler_stop1()
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
mozilla_sampler_init();
|
||||
mozilla_sampler_init1();
|
||||
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
|
@ -1159,7 +1197,7 @@ void mozilla_sampler_stop()
|
|||
t->Stop();
|
||||
delete t;
|
||||
tlsTicker.set(NULL);
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
PseudoStack *stack = tlsPseudoStack.get();
|
||||
ASSERT(stack != NULL);
|
||||
|
||||
if (disableJS)
|
||||
|
@ -1170,10 +1208,10 @@ void mozilla_sampler_stop()
|
|||
os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
|
||||
}
|
||||
|
||||
bool mozilla_sampler_is_active()
|
||||
bool mozilla_sampler_is_active1()
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
mozilla_sampler_init();
|
||||
mozilla_sampler_init1();
|
||||
|
||||
TableTicker *t = tlsTicker.get();
|
||||
if (!t) {
|
||||
|
@ -1183,10 +1221,9 @@ bool mozilla_sampler_is_active()
|
|||
return t->IsActive();
|
||||
}
|
||||
|
||||
double sResponsivenessTimes[100];
|
||||
double sCurrResponsiveness = 0.f;
|
||||
unsigned int sResponsivenessLoc = 0;
|
||||
void mozilla_sampler_responsiveness(TimeStamp aTime)
|
||||
static double sResponsivenessTimes[100];
|
||||
static unsigned int sResponsivenessLoc = 0;
|
||||
void mozilla_sampler_responsiveness1(TimeStamp aTime)
|
||||
{
|
||||
if (!sLastTracerEvent.IsNull()) {
|
||||
if (sResponsivenessLoc == 100) {
|
||||
|
@ -1203,17 +1240,17 @@ void mozilla_sampler_responsiveness(TimeStamp aTime)
|
|||
sLastTracerEvent = aTime;
|
||||
}
|
||||
|
||||
const double* mozilla_sampler_get_responsiveness()
|
||||
const double* mozilla_sampler_get_responsiveness1()
|
||||
{
|
||||
return sResponsivenessTimes;
|
||||
}
|
||||
|
||||
void mozilla_sampler_frame_number(int frameNumber)
|
||||
void mozilla_sampler_frame_number1(int frameNumber)
|
||||
{
|
||||
sFrameNumber = frameNumber;
|
||||
}
|
||||
|
||||
void print_callback(const ProfileEntry& entry, const char* tagStringData) {
|
||||
static void print_callback(const ProfileEntry& entry, const char* tagStringData) {
|
||||
switch (entry.mTagName) {
|
||||
case 's':
|
||||
case 'c':
|
||||
|
@ -1221,12 +1258,12 @@ void print_callback(const ProfileEntry& entry, const char* tagStringData) {
|
|||
}
|
||||
}
|
||||
|
||||
void mozilla_sampler_print_location()
|
||||
void mozilla_sampler_print_location1()
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
mozilla_sampler_init();
|
||||
mozilla_sampler_init1();
|
||||
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
PseudoStack *stack = tlsPseudoStack.get();
|
||||
if (!stack) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
|
@ -1241,15 +1278,15 @@ void mozilla_sampler_print_location()
|
|||
threadProfile.IterateTags(print_callback);
|
||||
}
|
||||
|
||||
void mozilla_sampler_lock()
|
||||
void mozilla_sampler_lock1()
|
||||
{
|
||||
mozilla_sampler_stop();
|
||||
mozilla_sampler_stop1();
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
os->NotifyObservers(nullptr, "profiler-locked", nullptr);
|
||||
}
|
||||
|
||||
void mozilla_sampler_unlock()
|
||||
void mozilla_sampler_unlock1()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
||||
if (os)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,60 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZ_UNWINDER_THREAD_2_H
|
||||
#define MOZ_UNWINDER_THREAD_2_H
|
||||
|
||||
#include "sps_sampler.h"
|
||||
#include "ProfileEntry2.h"
|
||||
|
||||
/* Top level exports of UnwinderThread.cpp. */
|
||||
|
||||
// Abstract type. A buffer which is used to transfer information between
|
||||
// the sampled thread(s) and the unwinder thread(s).
|
||||
typedef
|
||||
struct _UnwinderThreadBuffer
|
||||
UnwinderThreadBuffer;
|
||||
|
||||
// RUNS IN SIGHANDLER CONTEXT
|
||||
// Called in the sampled thread (signal) context. Adds a ProfileEntry2
|
||||
// into an UnwinderThreadBuffer that the thread has previously obtained
|
||||
// by a call to utb__acquire_empty_buffer.
|
||||
void utb__addEntry(/*MOD*/UnwinderThreadBuffer* utb,
|
||||
ProfileEntry2 ent);
|
||||
|
||||
// Create the unwinder thread. At the moment there can be only one.
|
||||
void uwt__init();
|
||||
|
||||
// Request the unwinder thread to exit, and wait until it has done so.
|
||||
void uwt__deinit();
|
||||
|
||||
// Registers a sampler thread for profiling. Threads must be registered
|
||||
// before they are allowed to call utb__acquire_empty_buffer or
|
||||
// utb__release_full_buffer.
|
||||
void uwt__register_thread_for_profiling(void* stackTop);
|
||||
|
||||
// RUNS IN SIGHANDLER CONTEXT
|
||||
// Called in the sampled thread (signal) context. Get an empty buffer
|
||||
// into which ProfileEntries can be put. It may return NULL if no
|
||||
// empty buffers can be found, which will be the case if the unwinder
|
||||
// thread(s) have fallen behind for some reason. In this case the
|
||||
// sampled thread must simply give up and return from the signal handler
|
||||
// immediately, else it risks deadlock.
|
||||
UnwinderThreadBuffer* uwt__acquire_empty_buffer();
|
||||
|
||||
// RUNS IN SIGHANDLER CONTEXT
|
||||
// Called in the sampled thread (signal) context. Release a buffer
|
||||
// that the sampled thread has acquired, handing the contents to
|
||||
// the unwinder thread, and, if necessary, passing sufficient
|
||||
// information (stack top chunk, + registers) to also do a native
|
||||
// unwind. If 'ucV' is NULL, no native unwind is done. If non-NULL,
|
||||
// it is assumed to point to a ucontext_t* that holds the initial
|
||||
// register state for the unwind. The results of all of this are
|
||||
// dumped into |aProfile| (by the unwinder thread, not the calling thread).
|
||||
void uwt__release_full_buffer(ThreadProfile2* aProfile,
|
||||
UnwinderThreadBuffer* utb,
|
||||
void* /* ucontext_t*, really */ ucV);
|
||||
|
||||
#endif /* ndef MOZ_UNWINDER_THREAD_2_H */
|
|
@ -0,0 +1,160 @@
|
|||
|
||||
#include "PlatformMacros.h"
|
||||
|
||||
#if !defined(SPS_OS_windows)
|
||||
# include "common/module.h"
|
||||
# include "processor/cfi_frame_info.h"
|
||||
#endif
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/logging.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
|
||||
#if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|
||||
|| defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
|
||||
# include "common/linux/dump_symbols.h"
|
||||
#elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin)
|
||||
# include "shim_mac_dump_syms.h"
|
||||
#elif defined(SPS_OS_windows)
|
||||
/* This is all stubbed out anyway, so don't do anything. */
|
||||
#else
|
||||
# error "Unknown platform"
|
||||
#endif
|
||||
|
||||
#include "platform.h"
|
||||
#include "local_debug_info_symbolizer.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LocalDebugInfoSymbolizer::~LocalDebugInfoSymbolizer() {
|
||||
# if !defined(SPS_OS_windows)
|
||||
for (SymbolMap::iterator it = symbols_.begin();
|
||||
it != symbols_.end();
|
||||
++it) {
|
||||
delete it->second;
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
StackFrameSymbolizer::SymbolizerResult
|
||||
LocalDebugInfoSymbolizer::FillSourceLineInfo(const CodeModules* modules,
|
||||
const SystemInfo* system_info,
|
||||
StackFrame* frame) {
|
||||
if (!modules) {
|
||||
return kError;
|
||||
}
|
||||
const CodeModule* module = modules->GetModuleForAddress(frame->instruction);
|
||||
if (!module) {
|
||||
return kError;
|
||||
}
|
||||
frame->module = module;
|
||||
|
||||
# if !defined(SPS_OS_windows)
|
||||
Module* debug_info_module = NULL;
|
||||
SymbolMap::const_iterator it = symbols_.find(module->code_file());
|
||||
if (it == symbols_.end()) {
|
||||
if (no_symbol_modules_.find(module->code_file()) !=
|
||||
no_symbol_modules_.end()) {
|
||||
return kNoError;
|
||||
}
|
||||
LOG("BPUnw:");
|
||||
LOGF("BPUnw: ReadSymbolData: BEGIN %s", module->code_file().c_str());
|
||||
if (!ReadSymbolData(module->code_file(),
|
||||
debug_dirs_,
|
||||
ONLY_CFI,
|
||||
&debug_info_module)) {
|
||||
BPLOG(ERROR) << "ReadSymbolData failed for " << module->code_file();
|
||||
LOGF("BPUnw: ReadSymbolData: FAIL %s", module->code_file().c_str());
|
||||
if (debug_info_module)
|
||||
delete debug_info_module;
|
||||
no_symbol_modules_.insert(module->code_file());
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
LOGF("BPUnw: ReadSymbolData: SUCCESS %s", module->code_file().c_str());
|
||||
symbols_[module->code_file()] = debug_info_module;
|
||||
} else {
|
||||
debug_info_module = it->second;
|
||||
}
|
||||
|
||||
u_int64_t address = frame->instruction - frame->module->base_address();
|
||||
Module::Function* function =
|
||||
debug_info_module->FindFunctionByAddress(address);
|
||||
if (function) {
|
||||
frame->function_name = function->name;
|
||||
//TODO: line info: function->lines
|
||||
} else {
|
||||
Module::Extern* ex = debug_info_module->FindExternByAddress(address);
|
||||
if (ex) {
|
||||
frame->function_name = ex->name;
|
||||
}
|
||||
}
|
||||
# endif /* !defined(SPS_OS_windows) */
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
|
||||
WindowsFrameInfo* LocalDebugInfoSymbolizer::FindWindowsFrameInfo(
|
||||
const StackFrame* frame) {
|
||||
// Not currently implemented, would require PDBSourceLineWriter to
|
||||
// implement an API to return symbol data.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !defined(SPS_OS_windows)
|
||||
// Taken wholesale from source_line_resolver_base.cc
|
||||
bool ParseCFIRuleSet(const string& rule_set, CFIFrameInfo* frame_info) {
|
||||
CFIFrameInfoParseHandler handler(frame_info);
|
||||
CFIRuleParser parser(&handler);
|
||||
return parser.Parse(rule_set);
|
||||
}
|
||||
|
||||
static void ConvertCFI(const UniqueString* name, const Module::Expr& rule,
|
||||
CFIFrameInfo* frame_info) {
|
||||
if (name == ustr__ZDcfa()) frame_info->SetCFARule(rule);
|
||||
else if (name == ustr__ZDra()) frame_info->SetRARule(rule);
|
||||
else frame_info->SetRegisterRule(name, rule);
|
||||
}
|
||||
|
||||
|
||||
static void ConvertCFI(const Module::RuleMap& rule_map,
|
||||
CFIFrameInfo* frame_info) {
|
||||
for (Module::RuleMap::const_iterator it = rule_map.begin();
|
||||
it != rule_map.end(); ++it) {
|
||||
ConvertCFI(it->first, it->second, frame_info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CFIFrameInfo* LocalDebugInfoSymbolizer::FindCFIFrameInfo(
|
||||
const StackFrame* frame) {
|
||||
#if defined(SPS_OS_windows)
|
||||
return NULL;
|
||||
#else
|
||||
if (!frame || !frame->module) return NULL;
|
||||
|
||||
SymbolMap::const_iterator it = symbols_.find(frame->module->code_file());
|
||||
if (it == symbols_.end()) return NULL;
|
||||
|
||||
Module* module = it->second;
|
||||
u_int64_t address = frame->instruction - frame->module->base_address();
|
||||
Module::StackFrameEntry* entry =
|
||||
module->FindStackFrameEntryByAddress(address);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
//TODO: can we cache this data per-address? does that make sense?
|
||||
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
|
||||
ConvertCFI(entry->initial_rules, rules.get());
|
||||
for (Module::RuleChangeMap::const_iterator delta_it =
|
||||
entry->rule_changes.begin();
|
||||
delta_it != entry->rule_changes.end() && delta_it->first < address;
|
||||
++delta_it) {
|
||||
ConvertCFI(delta_it->second, rules.get());
|
||||
}
|
||||
return rules.release();
|
||||
#endif /* defined(SPS_OS_windows) */
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
|
||||
#define PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
|
||||
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class Module;
|
||||
|
||||
class LocalDebugInfoSymbolizer : public StackFrameSymbolizer {
|
||||
public:
|
||||
using StackFrameSymbolizer::SymbolizerResult;
|
||||
LocalDebugInfoSymbolizer(const std::vector<string>& debug_dirs) :
|
||||
StackFrameSymbolizer(NULL, NULL),
|
||||
debug_dirs_(debug_dirs) {}
|
||||
virtual ~LocalDebugInfoSymbolizer();
|
||||
|
||||
virtual SymbolizerResult FillSourceLineInfo(const CodeModules* modules,
|
||||
const SystemInfo* system_info,
|
||||
StackFrame* stack_frame);
|
||||
|
||||
virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame);
|
||||
|
||||
virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame);
|
||||
|
||||
// Lie to the stackwalker to short-circuit stack-scanning heuristics.
|
||||
virtual bool HasImplementation() { return false; }
|
||||
|
||||
private:
|
||||
typedef std::map<string, Module*> SymbolMap;
|
||||
SymbolMap symbols_;
|
||||
std::vector<string> debug_dirs_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_LOCAL_DEBUG_INFO_SYMBOLIZER_H_
|
|
@ -60,11 +60,11 @@ nsProfiler::Observe(nsISupports *aSubject,
|
|||
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
|
||||
if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
|
||||
mLockedForPrivateBrowsing = true;
|
||||
mozilla_sampler_lock();
|
||||
SAMPLER_LOCK();
|
||||
}
|
||||
} else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
|
||||
mLockedForPrivateBrowsing = false;
|
||||
mozilla_sampler_unlock();
|
||||
SAMPLER_UNLOCK();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -224,7 +224,7 @@ Sampler::~Sampler() {
|
|||
|
||||
|
||||
void Sampler::Start() {
|
||||
LOG("Sampler Started");
|
||||
LOG("Sampler started");
|
||||
if (sActiveSampler != NULL) return;
|
||||
|
||||
// Request profiling signals.
|
||||
|
@ -291,8 +291,8 @@ static struct sigaction old_sigstart_signal_handler;
|
|||
const int SIGSTART = SIGUSR1;
|
||||
|
||||
static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
|
||||
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
|
||||
PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
|
||||
SAMPLER_START(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
|
||||
PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
|
||||
}
|
||||
|
||||
void OS::RegisterStartHandler()
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
#include "platform.h"
|
||||
#include "UnwinderThread2.h" /* uwt__register_thread_for_profiling */
|
||||
|
||||
// this port is based off of v8 svn revision 9837
|
||||
|
||||
|
@ -129,6 +129,16 @@ static void* ThreadEntry(void* arg) {
|
|||
// This is also initialized by the first argument to pthread_create() but we
|
||||
// don't know which thread will run first (the original thread or the new
|
||||
// one) so we initialize it here too.
|
||||
|
||||
// BEGIN temp hack for SPS v1-vs-v2
|
||||
extern bool sps_version2();
|
||||
if (sps_version2()) {
|
||||
// Register this thread for profiling.
|
||||
int aLocal;
|
||||
uwt__register_thread_for_profiling( &aLocal );
|
||||
}
|
||||
// END temp hack for SPS v1-vs-v2
|
||||
|
||||
thread->data()->thread_ = pthread_self();
|
||||
SetThreadName(thread->name());
|
||||
ASSERT(thread->data()->thread_ != kNoThread);
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
#ifndef TOOLS_PLATFORM_H_
|
||||
#define TOOLS_PLATFORM_H_
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#else
|
||||
|
@ -44,11 +47,11 @@
|
|||
#define ENABLE_SPS_LEAF_DATA
|
||||
#define ENABLE_ARM_LR_SAVING
|
||||
#endif
|
||||
#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "profiler", text)
|
||||
#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "profiler", format, __VA_ARGS__)
|
||||
#define LOG(text) __android_log_write(ANDROID_LOG_ERROR, "Profiler", text)
|
||||
#define LOGF(format, ...) __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, __VA_ARGS__)
|
||||
#else
|
||||
#define LOG(text) printf("Profiler: %s\n", text)
|
||||
#define LOGF(format, ...) printf("Profiler: " format "\n", __VA_ARGS__)
|
||||
#define LOG(text) fprintf(stderr, "Profiler: %s\n", text)
|
||||
#define LOGF(format, ...) fprintf(stderr, "Profiler: " format "\n", __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
|
@ -276,3 +279,4 @@ class Sampler {
|
|||
PlatformData* data_; // Platform specific data.
|
||||
};
|
||||
|
||||
#endif /* ndef TOOLS_PLATFORM_H_ */
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
// Read debug info from |obj_file| and park it in a Module, returned
|
||||
// via |module|. Caller owns the Module and is responsible for
|
||||
// deallocating it. Note that |debug_dirs| is ignored.
|
||||
bool ReadSymbolData(const string& obj_file,
|
||||
const std::vector<string> &debug_dirs,
|
||||
SymbolData symbol_data,
|
||||
google_breakpad::Module** module);
|
|
@ -0,0 +1,20 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
#include "common/mac/dump_syms.h"
|
||||
#include "shim_mac_dump_syms.h"
|
||||
|
||||
bool ReadSymbolData(const string& obj_file,
|
||||
const std::vector<string> &debug_dirs,
|
||||
SymbolData symbol_data,
|
||||
google_breakpad::Module** module)
|
||||
{
|
||||
google_breakpad::DumpSymbols ds(symbol_data);
|
||||
|
||||
NSString* obj_file_ns = [NSString stringWithUTF8String:obj_file.c_str()];
|
||||
// TODO: remember to [obj_file_ns release] this at the exit points
|
||||
|
||||
if (!ds.Read(obj_file_ns))
|
||||
return false;
|
||||
|
||||
return ds.ReadSymbolData(module);
|
||||
}
|
|
@ -3,6 +3,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TOOLS_SPS_SAMPLER_H_
|
||||
#define TOOLS_SPS_SAMPLER_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -26,12 +29,11 @@
|
|||
using mozilla::TimeStamp;
|
||||
using mozilla::TimeDuration;
|
||||
|
||||
struct ProfileStack;
|
||||
struct PseudoStack;
|
||||
class TableTicker;
|
||||
class JSCustomObject;
|
||||
|
||||
extern mozilla::ThreadLocal<ProfileStack *> tlsStack;
|
||||
extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
|
||||
extern mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
|
||||
extern bool stack_key_initialized;
|
||||
|
||||
#ifndef SAMPLE_FUNCTION_NAME
|
||||
|
@ -44,18 +46,73 @@ extern bool stack_key_initialized;
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#define SAMPLER_INIT() mozilla_sampler_init()
|
||||
#define SAMPLER_SHUTDOWN() mozilla_sampler_shutdown()
|
||||
#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_FRAME_NUMBER(frameNumber) mozilla_sampler_frame_number(frameNumber)
|
||||
#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()
|
||||
/* Returns true if env var SPS_NEW is set to anything, else false. */
|
||||
extern bool sps_version2();
|
||||
|
||||
#define SAMPLER_INIT() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_init1(); \
|
||||
else mozilla_sampler_init2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_SHUTDOWN() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_shutdown1(); \
|
||||
else mozilla_sampler_shutdown2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_START(entries, interval, features, featureCount) \
|
||||
do { \
|
||||
if (!sps_version2()) \
|
||||
mozilla_sampler_start1(entries, interval, features, featureCount); \
|
||||
else \
|
||||
mozilla_sampler_start2(entries, interval, features, featureCount); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_STOP() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_stop1(); \
|
||||
else mozilla_sampler_stop2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_IS_ACTIVE() \
|
||||
(!sps_version2() ? mozilla_sampler_is_active1() \
|
||||
: mozilla_sampler_is_active2() )
|
||||
|
||||
#define SAMPLER_RESPONSIVENESS(time) \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_responsiveness1(time); \
|
||||
else mozilla_sampler_responsiveness2(time); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_GET_RESPONSIVENESS() \
|
||||
(!sps_version2() ? mozilla_sampler_get_responsiveness1() \
|
||||
: mozilla_sampler_get_responsiveness2() )
|
||||
|
||||
#define SAMPLER_FRAME_NUMBER(frameNumber) \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_frame_number1(frameNumber); \
|
||||
else mozilla_sampler_frame_number2(frameNumber); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_SAVE() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_save1(); \
|
||||
else mozilla_sampler_save2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_GET_PROFILE() \
|
||||
(!sps_version2() ? mozilla_sampler_get_profile1() \
|
||||
: mozilla_sampler_get_profile2() )
|
||||
|
||||
#define SAMPLER_GET_PROFILE_DATA(ctx) \
|
||||
(!sps_version2() ? mozilla_sampler_get_profile_data1(ctx) \
|
||||
: mozilla_sampler_get_profile_data2(ctx) )
|
||||
|
||||
#define SAMPLER_GET_FEATURES() \
|
||||
(!sps_version2() ? mozilla_sampler_get_features1() \
|
||||
: mozilla_sampler_get_features2() )
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -70,7 +127,23 @@ extern bool stack_key_initialized;
|
|||
#define SAMPLE_MAIN_THREAD_LABEL_PRINTF(name_space, info, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__, __VA_ARGS__)
|
||||
#define SAMPLE_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
|
||||
|
||||
#define SAMPLER_PRINT_LOCATION() mozilla_sampler_print_location()
|
||||
#define SAMPLER_PRINT_LOCATION() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_print_location1(); \
|
||||
else mozilla_sampler_print_location2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_LOCK() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_lock1(); \
|
||||
else mozilla_sampler_lock2(); \
|
||||
} while (0)
|
||||
|
||||
#define SAMPLER_UNLOCK() \
|
||||
do { \
|
||||
if (!sps_version2()) mozilla_sampler_unlock1(); \
|
||||
else mozilla_sampler_unlock2(); \
|
||||
} while (0)
|
||||
|
||||
/* we duplicate this code here to avoid header dependencies
|
||||
* which make it more difficult to include in other places */
|
||||
|
@ -165,31 +238,63 @@ LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
|
|||
# error "Memory clobber not supported for your platform."
|
||||
#endif
|
||||
|
||||
// Returns a handdle to pass on exit. This can check that we are popping the
|
||||
// Returns a handle to pass on exit. This can check that we are popping the
|
||||
// correct callstack.
|
||||
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL, bool aCopy = false, uint32_t line = 0);
|
||||
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress = NULL,
|
||||
bool aCopy = false, uint32_t line = 0);
|
||||
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);
|
||||
void mozilla_sampler_frame_number(int frameNumber);
|
||||
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();
|
||||
void mozilla_sampler_shutdown();
|
||||
void mozilla_sampler_print_location();
|
||||
void mozilla_sampler_start1(int aEntries, int aInterval, const char** aFeatures,
|
||||
uint32_t aFeatureCount);
|
||||
void mozilla_sampler_start2(int aEntries, int aInterval, const char** aFeatures,
|
||||
uint32_t aFeatureCount);
|
||||
|
||||
void mozilla_sampler_stop1();
|
||||
void mozilla_sampler_stop2();
|
||||
|
||||
bool mozilla_sampler_is_active1();
|
||||
bool mozilla_sampler_is_active2();
|
||||
|
||||
void mozilla_sampler_responsiveness1(TimeStamp time);
|
||||
void mozilla_sampler_responsiveness2(TimeStamp time);
|
||||
|
||||
void mozilla_sampler_frame_number1(int frameNumber);
|
||||
void mozilla_sampler_frame_number2(int frameNumber);
|
||||
|
||||
const double* mozilla_sampler_get_responsiveness1();
|
||||
const double* mozilla_sampler_get_responsiveness2();
|
||||
|
||||
void mozilla_sampler_save1();
|
||||
void mozilla_sampler_save2();
|
||||
|
||||
char* mozilla_sampler_get_profile1();
|
||||
char* mozilla_sampler_get_profile2();
|
||||
|
||||
JSObject *mozilla_sampler_get_profile_data1(JSContext *aCx);
|
||||
JSObject *mozilla_sampler_get_profile_data2(JSContext *aCx);
|
||||
|
||||
const char** mozilla_sampler_get_features1();
|
||||
const char** mozilla_sampler_get_features2();
|
||||
|
||||
void mozilla_sampler_init1();
|
||||
void mozilla_sampler_init2();
|
||||
|
||||
void mozilla_sampler_shutdown1();
|
||||
void mozilla_sampler_shutdown2();
|
||||
|
||||
void mozilla_sampler_print_location1();
|
||||
void mozilla_sampler_print_location2();
|
||||
|
||||
// Lock the profiler. When locked the profiler is (1) stopped,
|
||||
// (2) profile data is cleared, (3) profiler-locked is fired.
|
||||
// This is used to lock down the profiler during private browsing
|
||||
void mozilla_sampler_lock();
|
||||
void mozilla_sampler_lock1();
|
||||
void mozilla_sampler_lock2();
|
||||
|
||||
// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
|
||||
void mozilla_sampler_unlock();
|
||||
void mozilla_sampler_unlock1();
|
||||
void mozilla_sampler_unlock2();
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -211,7 +316,7 @@ class NS_STACK_CLASS SamplerStackFramePrintfRAII {
|
|||
public:
|
||||
// we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
|
||||
SamplerStackFramePrintfRAII(const char *aDefault, uint32_t line, const char *aFormat, ...) {
|
||||
if (mozilla_sampler_is_active()) {
|
||||
if (SAMPLER_IS_ACTIVE()) {
|
||||
va_list args;
|
||||
va_start(args, aFormat);
|
||||
char buff[SAMPLER_MAX_STRING];
|
||||
|
@ -273,13 +378,14 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// the SamplerStack members are read by signal
|
||||
// the PseudoStack members are read by signal
|
||||
// handlers, so the mutation of them needs to be signal-safe.
|
||||
struct ProfileStack
|
||||
struct PseudoStack
|
||||
{
|
||||
public:
|
||||
ProfileStack()
|
||||
PseudoStack()
|
||||
: mStackPointer(0)
|
||||
, mSignalLock(false)
|
||||
, mMarkerPointer(0)
|
||||
, mQueueClearMarker(false)
|
||||
, mRuntime(NULL)
|
||||
|
@ -420,11 +526,11 @@ public:
|
|||
bool mStartJSSampling;
|
||||
};
|
||||
|
||||
inline ProfileStack* mozilla_profile_stack(void)
|
||||
inline PseudoStack* mozilla_get_pseudo_stack(void)
|
||||
{
|
||||
if (!stack_key_initialized)
|
||||
return NULL;
|
||||
return tlsStack.get();
|
||||
return tlsPseudoStack.get();
|
||||
}
|
||||
|
||||
inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress,
|
||||
|
@ -435,7 +541,7 @@ inline void* mozilla_sampler_call_enter(const char *aInfo, void *aFrameAddress,
|
|||
if (!stack_key_initialized)
|
||||
return NULL;
|
||||
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
PseudoStack *stack = tlsPseudoStack.get();
|
||||
// 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
|
||||
|
@ -458,7 +564,7 @@ inline void mozilla_sampler_call_exit(void *aHandle)
|
|||
if (!aHandle)
|
||||
return;
|
||||
|
||||
ProfileStack *stack = (ProfileStack*)aHandle;
|
||||
PseudoStack *stack = (PseudoStack*)aHandle;
|
||||
stack->pop();
|
||||
}
|
||||
|
||||
|
@ -469,14 +575,15 @@ inline void mozilla_sampler_add_marker(const char *aMarker)
|
|||
|
||||
// Don't insert a marker if we're not profiling to avoid
|
||||
// the heap copy (malloc).
|
||||
if (!mozilla_sampler_is_active()) {
|
||||
if (!SAMPLER_IS_ACTIVE()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileStack *stack = tlsStack.get();
|
||||
PseudoStack *stack = tlsPseudoStack.get();
|
||||
if (!stack) {
|
||||
return;
|
||||
}
|
||||
stack->addMarker(aMarker);
|
||||
}
|
||||
|
||||
#endif /* ndef TOOLS_SPS_SAMPLER_H_ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче