Bug 1634205 - Support Gecko Profiler and Base Profiler on FreeBSD r=mstange

- supports amd64 and arm64 (aarch64)
- uses LUL for stack walking

Differential Revision: https://phabricator.services.mozilla.com/D73162
This commit is contained in:
Greg V 2020-05-06 17:44:19 +00:00
parent 3fa67b082d
commit e65e9412b0
26 changed files with 235 additions and 79 deletions

View File

@ -137,7 +137,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
EXPORTS.mozilla += [
'WindowsVersion.h',
]
elif CONFIG['OS_ARCH'] == 'Linux':
elif CONFIG['OS_ARCH'] == 'Linux' or CONFIG['OS_ARCH'] == 'FreeBSD':
EXPORTS.mozilla += [
'LinuxSignal.h',
]

View File

@ -90,6 +90,16 @@
# define GP_ARCH_amd64 1
# define GP_OS_darwin 1
#elif defined(__FreeBSD__) && defined(__x86_64__)
# define GP_PLAT_amd64_freebsd 1
# define GP_ARCH_amd64 1
# define GP_OS_freebsd 1
#elif defined(__FreeBSD__) && defined(__aarch64__)
# define GP_PLAT_arm64_freebsd 1
# define GP_ARCH_arm64 1
# define GP_OS_freebsd 1
#elif (defined(_MSC_VER) || defined(__MINGW32__)) && \
(defined(_M_IX86) || defined(__i386__))
# define GP_PLAT_x86_windows 1

View File

@ -34,6 +34,9 @@
#include <math.h>
#include <pthread.h>
#if defined(GP_OS_freebsd)
# include <sys/thr.h>
#endif
#include <semaphore.h>
#include <signal.h>
#include <sys/time.h>
@ -75,11 +78,15 @@ namespace baseprofiler {
int profiler_current_process_id() { return getpid(); }
int profiler_current_thread_id() {
#if defined(GP_OS_linux) || defined(GP_OS_android)
// glibc doesn't provide a wrapper for gettid().
#if defined(__linux__) || !defined(__BIONIC__)
return static_cast<int>(static_cast<pid_t>(syscall(SYS_gettid)));
#elif defined(GP_OS_freebsd)
long id;
(void)thr_self(&id);
return static_cast<int>(id);
#else
return static_cast<int>(gettid());
# error "bad platform"
#endif
}
@ -96,27 +103,37 @@ static void PopulateRegsFromContext(Registers& aRegs, ucontext_t* aContext) {
mcontext_t& mcontext = aContext->uc_mcontext;
// Extracting the sample from the context is extremely machine dependent.
#if defined(GP_ARCH_x86)
#if defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
aRegs.mLR = 0;
#elif defined(GP_ARCH_amd64)
#elif defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
aRegs.mLR = 0;
#elif defined(GP_ARCH_arm)
#elif defined(GP_PLAT_amd64_freebsd)
aRegs.mPC = reinterpret_cast<Address>(mcontext.mc_rip);
aRegs.mSP = reinterpret_cast<Address>(mcontext.mc_rsp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.mc_rbp);
aRegs.mLR = 0;
#elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.arm_pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.arm_sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.arm_fp);
aRegs.mLR = reinterpret_cast<Address>(mcontext.arm_lr);
#elif defined(GP_ARCH_arm64)
#elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.regs[29]);
aRegs.mLR = reinterpret_cast<Address>(mcontext.regs[30]);
#elif defined(GP_ARCH_mips64)
#elif defined(GP_PLAT_arm64_freebsd)
aRegs.mPC = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_elr);
aRegs.mSP = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_x[29]);
aRegs.mLR = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_lr);
#elif defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_mips64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[29]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[30]);
@ -130,9 +147,15 @@ static void PopulateRegsFromContext(Registers& aRegs, ucontext_t* aContext) {
# define SYS_tgkill __NR_tgkill
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(SYS_tgkill, tgid, tid, signalno);
}
#endif
#if defined(GP_OS_freebsd)
# define tgkill thr_kill2
#endif
class PlatformData {
public:
@ -457,7 +480,7 @@ void SamplerThread::Stop(PSLockRef aLock) {
// END SamplerThread target specifics
////////////////////////////////////////////////////////////////////////
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
// We use pthread_atfork() to temporarily disable signal delivery during any
// fork() call. Without that, fork() can be repeatedly interrupted by signal

View File

@ -103,11 +103,12 @@
# include "EHABIStackWalk.h"
#endif
// Linux builds use LUL, which uses DWARF info to unwind stacks.
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \
defined(GP_PLAT_arm64_android)
// Linux/BSD builds use LUL, which uses DWARF info to unwind stacks.
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \
defined(GP_PLAT_arm64_android) || defined(GP_PLAT_amd64_freebsd) || \
defined(GP_PLAT_arm64_freebsd)
# define HAVE_NATIVE_UNWIND
# define USE_LUL_STACKWALK
# include "lul/LulMain.h"
@ -141,7 +142,7 @@
# define VALGRIND_MAKE_MEM_DEFINED(_addr, _len) ((void)0)
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include <ucontext.h>
#endif
@ -603,7 +604,7 @@ class ActivePS {
#undef HAS_FEATURE
,
mIsPaused(false)
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
,
mWasPaused(false)
#endif
@ -851,7 +852,7 @@ class ActivePS {
PS_GET_AND_SET(bool, IsPaused)
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
PS_GET_AND_SET(bool, WasPaused)
#endif
@ -1006,7 +1007,7 @@ class ActivePS {
// Is the profiler paused?
bool mIsPaused;
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
// Used to record whether the profiler was paused just before forking. False
// at all times except just before/after forking.
bool mWasPaused;
@ -1159,7 +1160,7 @@ class Registers {
Address mSP; // Stack pointer.
Address mFP; // Frame pointer.
Address mLR; // ARM link register.
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
// This contains all the registers, which means it duplicates the four fields
// above. This is ok.
ucontext_t* mContext; // The context from the signal handler.
@ -1402,6 +1403,10 @@ static void DoLULBacktrace(PSLockRef aLock,
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_RIP]);
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_RSP]);
startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_RBP]);
# elif defined(GP_PLAT_amd64_freebsd)
startRegs.xip = lul::TaggedUWord(mc->mc_rip);
startRegs.xsp = lul::TaggedUWord(mc->mc_rsp);
startRegs.xbp = lul::TaggedUWord(mc->mc_rbp);
# elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
@ -1414,6 +1419,11 @@ static void DoLULBacktrace(PSLockRef aLock,
startRegs.x29 = lul::TaggedUWord(mc->regs[29]);
startRegs.x30 = lul::TaggedUWord(mc->regs[30]);
startRegs.sp = lul::TaggedUWord(mc->sp);
# elif defined(GP_PLAT_arm64_freebsd)
startRegs.pc = lul::TaggedUWord(mc->mc_gpregs.gp_elr);
startRegs.x29 = lul::TaggedUWord(mc->mc_gpregs.gp_x[29]);
startRegs.x30 = lul::TaggedUWord(mc->mc_gpregs.gp_lr);
startRegs.sp = lul::TaggedUWord(mc->mc_gpregs.gp_sp);
# elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
@ -1460,13 +1470,15 @@ static void DoLULBacktrace(PSLockRef aLock,
lul::StackImage stackImg;
{
# if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android)
# if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android) || \
defined(GP_PLAT_amd64_freebsd)
uintptr_t rEDZONE_SIZE = 128;
uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
uintptr_t rEDZONE_SIZE = 0;
uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android)
# elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android) || \
defined(GP_PLAT_arm64_freebsd)
uintptr_t rEDZONE_SIZE = 0;
uintptr_t start = startRegs.sp.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
@ -2021,7 +2033,7 @@ class Sampler {
const TimeStamp& aNow, const Func& aProcessRegs);
private:
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
// Used to restore the SIGPROF handler when ours is removed.
struct sigaction mOldSigprofHandler;
@ -2081,7 +2093,8 @@ class SamplerThread {
// The OS-specific handle for the sampler thread.
#if defined(GP_OS_windows)
HANDLE mThread;
#elif defined(GP_OS_darwin) || defined(GP_OS_linux) || defined(GP_OS_android)
#elif defined(GP_OS_darwin) || defined(GP_OS_linux) || \
defined(GP_OS_android) || defined(GP_OS_freebsd)
pthread_t mThread;
#endif
@ -2295,7 +2308,7 @@ void SamplerThread::Run() {
# include "platform-win32.cpp"
#elif defined(GP_OS_darwin)
# include "platform-macos.cpp"
#elif defined(GP_OS_linux) || defined(GP_OS_android)
#elif defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include "platform-linux-android.cpp"
#else
# error "bad platform"

View File

@ -23,7 +23,9 @@
#include <dlfcn.h>
#include <elf.h>
#include <fcntl.h>
#include <features.h>
#if defined(GP_OS_linux) || defined(GP_OS_android)
# include <features.h>
#endif
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
@ -32,7 +34,7 @@
#if defined(MOZ_LINKER)
# include "AutoObjectMapper.h"
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include <link.h> // dl_phdr_info, ElfW()
#else
# error "Unexpected configuration"
@ -44,6 +46,10 @@ extern "C" MOZ_EXPORT __attribute__((weak)) int dl_iterate_phdr(
void* data);
#endif
#if defined(GP_OS_freebsd) && !defined(ElfW)
# define ElfW(type) Elf_##type
#endif
// ----------------------------------------------------------------------------
// Starting imports from toolkit/crashreporter/google-breakpad/, as needed by
// this file when moved to mozglue.
@ -179,7 +185,8 @@ class MemoryMappedFile {
}
#if defined(__x86_64__) || defined(__aarch64__) || \
(defined(__mips__) && _MIPS_SIM == _ABI64)
(defined(__mips__) && _MIPS_SIM == _ABI64) || \
!(defined(GP_OS_linux) || defined(GP_OS_android))
struct stat st;
if (fstat(fd, &st) == -1 || st.st_size < 0) {
@ -764,6 +771,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
}
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
// Read info from /proc/self/maps. We ignore most of it.
pid_t pid = mozilla::baseprofiler::profiler_current_process_id();
char path[PATH_MAX];
@ -790,12 +798,12 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
continue;
}
#if defined(GP_OS_linux)
# if defined(GP_OS_linux)
// Try to establish the main executable's load address.
if (exeNameLen > 0 && strcmp(modulePath, exeName) == 0) {
exeExeAddr = start;
}
#elif defined(GP_OS_android)
# elif defined(GP_OS_android)
// Use /proc/pid/maps to get the dalvik-jit section since it has no
// associated phdrs.
if (0 == strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) {
@ -807,8 +815,9 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
break;
}
}
#endif
# endif
}
#endif
std::vector<LoadedLibraryInfo> libInfoList;

View File

@ -83,6 +83,14 @@
#endif
#if defined(GP_OS_freebsd)
# ifndef ElfW
# define ElfW(type) Elf_##type
# endif
#endif
namespace lul {
// Traits classes so consumers can write templatized code to deal

View File

@ -1409,8 +1409,9 @@ void LUL::Unwind(/*OUT*/ uintptr_t* aFramePCs,
continue;
}
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android)
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_amd64_freebsd)
// There's no RuleSet for the specified address. On amd64/x86_linux, see if
// it's possible to recover the caller's frame by using the frame pointer.

View File

@ -25,7 +25,7 @@
void read_procmaps(lul::LUL* aLUL) {
MOZ_ASSERT(aLUL->CountMappings() == 0);
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
for (size_t i = 0; i < info.GetSize(); i++) {

View File

@ -32,7 +32,7 @@ if CONFIG['MOZ_GECKO_PROFILER']:
'core/RegisteredThread.cpp',
]
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
if CONFIG['OS_TARGET'] in ('Android', 'Linux', 'FreeBSD'):
if CONFIG['CPU_ARCH'] in ('arm', 'aarch64', 'x86', 'x86_64', 'mips64'):
UNIFIED_SOURCES += [
'lul/AutoObjectMapper.cpp',
@ -47,7 +47,7 @@ if CONFIG['MOZ_GECKO_PROFILER']:
SOURCES += [
'core/shared-libraries-linux.cc',
]
if CONFIG['CPU_ARCH'] == 'arm':
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['OS_TARGET'] != 'FreeBSD':
SOURCES += [
'core/EHABIStackWalk.cpp',
]

View File

@ -795,6 +795,11 @@ bool MFBT_API MozDescribeCodeAddress(void* aPC,
aDetails->library[mozilla::ArrayLength(aDetails->library) - 1] = '\0';
aDetails->loffset = (char*)aPC - (char*)info.dli_fbase;
# if !defined(XP_FREEBSD)
// On FreeBSD, dli_sname is unusably bad, it often returns things like
// 'gtk_xtbin_new' or 'XRE_GetBootstrap' instead of long C++ symbols. Just let
// GetFunction do the lookup directly in the ELF image.
const char* symbol = info.dli_sname;
if (!symbol || symbol[0] == '\0') {
return true;
@ -809,6 +814,8 @@ bool MFBT_API MozDescribeCodeAddress(void* aPC,
}
aDetails->foffset = (char*)aPC - (char*)info.dli_saddr;
# endif
return true;
}

View File

@ -168,7 +168,7 @@ MFBT_API void FramePointerStackWalk(MozWalkStackCallback aCallback,
void* aClosure, void** aBp,
void* aStackEnd);
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_FREEBSD)
MFBT_API void DemangleSymbol(const char* aSymbol, char* aBuffer, int aBufLen);
#endif

View File

@ -57,7 +57,7 @@
#endif
// Map Linux macros to their Apple equivalents.
#if __APPLE__
#if __APPLE__ || __FreeBSD__
#ifndef __LITTLE_ENDIAN
#define __LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
#endif // __LITTLE_ENDIAN

View File

@ -35,6 +35,10 @@
#include "common/linux/linux_libc_support.h"
#include "common/linux/elfutils-inl.h"
#if defined(__FreeBSD__)
# define ElfW(type) Elf_##type
#endif
namespace google_breakpad {
namespace {

View File

@ -42,7 +42,7 @@
#include <sanitizer/msan_interface.h>
#endif
#ifdef __APPLE__
#if defined(__APPLE__) || defined(__FreeBSD__)
#define sys_mmap mmap
#define sys_munmap munmap
#define MAP_ANONYMOUS MAP_ANON

View File

@ -4532,5 +4532,22 @@ struct kernel_statfs {
}
#endif
#elif defined(__FreeBSD__)
#include <unistd.h>
#include <sys/stat.h>
#define sys_readlink readlink
#define sys_open open
#define sys_close close
#define sys_fstat fstat
#define sys_fstat64 fstat
#define kernel_stat stat
#define kernel_stat64 stat
#define sys_mmap mmap
#define sys_munmap munmap
#endif
#endif

View File

@ -73,6 +73,8 @@ def gecko_profiler(target):
return target.cpu in ('aarch64', 'arm', 'x86', 'x86_64')
elif target.kernel == 'Linux':
return target.cpu in ('aarch64', 'arm', 'x86', 'x86_64', 'mips64')
elif target.kernel == 'FreeBSD':
return target.cpu in ('aarch64', 'x86_64')
return target.os in ('OSX', 'WINNT')
@depends(gecko_profiler)
@ -88,7 +90,7 @@ set_define('MOZ_GECKO_PROFILER', gecko_profiler_define)
# (for symbol table dumping).
@depends(gecko_profiler, target)
def gecko_profiler_parse_elf(value, target):
# Currently we only want to build this code on Linux (including Android).
# Currently we only want to build this code on Linux (including Android) and BSD.
# For Android, this is in order to dump symbols from Android system, where
# on other platforms there exist alternatives that don't require bloating
# up our binary size. For Linux more generally, we use this in profile
@ -97,7 +99,7 @@ def gecko_profiler_parse_elf(value, target):
# MozDescribeCodeAddress to call into some Rust crates that parse ELF and
# DWARF data, but build system issues currently prevent Rust from being
# used in mozglue.)
if value and target.kernel == 'Linux':
if value and (target.kernel == 'Linux' or target.kernel == 'FreeBSD'):
return True
set_config('MOZ_GECKO_PROFILER_PARSE_ELF', gecko_profiler_parse_elf)

View File

@ -90,6 +90,16 @@
# define GP_ARCH_amd64 1
# define GP_OS_darwin 1
#elif defined(__FreeBSD__) && defined(__x86_64__)
# define GP_PLAT_amd64_freebsd 1
# define GP_ARCH_amd64 1
# define GP_OS_freebsd 1
#elif defined(__FreeBSD__) && defined(__aarch64__)
# define GP_PLAT_arm64_freebsd 1
# define GP_ARCH_arm64 1
# define GP_OS_freebsd 1
#elif (defined(_MSC_VER) || defined(__MINGW32__)) && \
(defined(_M_IX86) || defined(__i386__))
# define GP_PLAT_x86_windows 1

View File

@ -11,7 +11,7 @@
using namespace mozilla;
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_FREEBSD)
static char* SearchSymbolTable(SymbolTable& aTable, uint32_t aOffset) {
size_t index;
bool exact =
@ -46,7 +46,7 @@ bool ProfilerCodeAddressService::GetFunction(const void* aPc,
nsACString& aResult) {
Entry& entry = GetEntry(aPc);
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_FREEBSD)
// On Linux, most symbols will not be found by the MozDescribeCodeAddress call
// that GetEntry does. So we read the symbol table directly from the ELF
// image.

View File

@ -28,12 +28,15 @@
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
// This file is used for both Linux and Android.
// This file is used for both Linux and Android as well as FreeBSD.
#include <stdio.h>
#include <math.h>
#include <pthread.h>
#if defined(GP_OS_freebsd)
# include <sys/thr.h>
#endif
#include <semaphore.h>
#include <signal.h>
#include <sys/time.h>
@ -63,7 +66,9 @@
#include "mozilla/LinuxSignal.h"
#include "mozilla/PodOperations.h"
#include "mozilla/DebugOnly.h"
#include "common/linux/breakpad_getcontext.h"
#if defined(GP_OS_linux) || defined(GP_OS_android)
# include "common/linux/breakpad_getcontext.h"
#endif
#include <string.h>
#include <list>
@ -73,11 +78,15 @@ using namespace mozilla;
int profiler_current_process_id() { return getpid(); }
int profiler_current_thread_id() {
#if defined(GP_OS_linux) || defined(GP_OS_android)
// glibc doesn't provide a wrapper for gettid().
#if defined(__linux__) || !defined(__BIONIC__)
return static_cast<int>(static_cast<pid_t>(syscall(SYS_gettid)));
#elif defined(GP_OS_freebsd)
long id;
(void)thr_self(&id);
return static_cast<int>(id);
#else
return static_cast<int>(gettid());
# error "bad platform"
#endif
}
@ -88,27 +97,37 @@ static void PopulateRegsFromContext(Registers& aRegs, ucontext_t* aContext) {
mcontext_t& mcontext = aContext->uc_mcontext;
// Extracting the sample from the context is extremely machine dependent.
#if defined(GP_ARCH_x86)
#if defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
aRegs.mLR = 0;
#elif defined(GP_ARCH_amd64)
#elif defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
aRegs.mLR = 0;
#elif defined(GP_ARCH_arm)
#elif defined(GP_PLAT_amd64_freebsd)
aRegs.mPC = reinterpret_cast<Address>(mcontext.mc_rip);
aRegs.mSP = reinterpret_cast<Address>(mcontext.mc_rsp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.mc_rbp);
aRegs.mLR = 0;
#elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.arm_pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.arm_sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.arm_fp);
aRegs.mLR = reinterpret_cast<Address>(mcontext.arm_lr);
#elif defined(GP_ARCH_arm64)
#elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.regs[29]);
aRegs.mLR = reinterpret_cast<Address>(mcontext.regs[30]);
#elif defined(GP_ARCH_mips64)
#elif defined(GP_PLAT_arm64_freebsd)
aRegs.mPC = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_elr);
aRegs.mSP = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_sp);
aRegs.mFP = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_x[29]);
aRegs.mLR = reinterpret_cast<Address>(mcontext.mc_gpregs.gp_lr);
#elif defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_mips64_android)
aRegs.mPC = reinterpret_cast<Address>(mcontext.pc);
aRegs.mSP = reinterpret_cast<Address>(mcontext.gregs[29]);
aRegs.mFP = reinterpret_cast<Address>(mcontext.gregs[30]);
@ -122,9 +141,15 @@ static void PopulateRegsFromContext(Registers& aRegs, ucontext_t* aContext) {
# define SYS_tgkill __NR_tgkill
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(SYS_tgkill, tgid, tid, signalno);
}
#endif
#if defined(GP_OS_freebsd)
# define tgkill thr_kill2
#endif
class PlatformData {
public:
@ -463,7 +488,7 @@ void SamplerThread::Stop(PSLockRef aLock) {
// END SamplerThread target specifics
////////////////////////////////////////////////////////////////////////
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
// We use pthread_atfork() to temporarily disable signal delivery during any
// fork() call. Without that, fork() can be repeatedly interrupted by signal

View File

@ -134,11 +134,12 @@
# include "EHABIStackWalk.h"
#endif
// Linux builds use LUL, which uses DWARF info to unwind stacks.
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \
defined(GP_PLAT_arm64_android)
// Linux/BSD builds use LUL, which uses DWARF info to unwind stacks.
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_mips64_linux) || defined(GP_PLAT_arm64_linux) || \
defined(GP_PLAT_arm64_android) || defined(GP_PLAT_amd64_freebsd) || \
defined(GP_PLAT_arm64_freebsd)
# define HAVE_NATIVE_UNWIND
# define USE_LUL_STACKWALK
# include "lul/LulMain.h"
@ -172,7 +173,7 @@
# define VALGRIND_MAKE_MEM_DEFINED(_addr, _len) ((void)0)
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include <ucontext.h>
#endif
@ -710,7 +711,7 @@ class ActivePS {
? new ProfilerIOInterposeObserver()
: nullptr),
mIsPaused(false)
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
,
mWasPaused(false)
#endif
@ -1074,7 +1075,7 @@ class ActivePS {
PS_GET_AND_SET(bool, IsPaused)
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
PS_GET_AND_SET(bool, WasPaused)
#endif
@ -1283,7 +1284,7 @@ class ActivePS {
// Is the profiler paused?
bool mIsPaused;
#if defined(GP_OS_linux)
#if defined(GP_OS_linux) || defined(GP_OS_freebsd)
// Used to record whether the profiler was paused just before forking. False
// at all times except just before/after forking.
bool mWasPaused;
@ -1454,7 +1455,7 @@ class Registers {
Address mSP; // Stack pointer.
Address mFP; // Frame pointer.
Address mLR; // ARM link register.
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
// This contains all the registers, which means it duplicates the four fields
// above. This is ok.
ucontext_t* mContext; // The context from the signal handler.
@ -1839,6 +1840,10 @@ static void DoLULBacktrace(PSLockRef aLock,
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_RIP]);
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_RSP]);
startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_RBP]);
# elif defined(GP_PLAT_amd64_freebsd)
startRegs.xip = lul::TaggedUWord(mc->mc_rip);
startRegs.xsp = lul::TaggedUWord(mc->mc_rsp);
startRegs.xbp = lul::TaggedUWord(mc->mc_rbp);
# elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
@ -1851,6 +1856,11 @@ static void DoLULBacktrace(PSLockRef aLock,
startRegs.x29 = lul::TaggedUWord(mc->regs[29]);
startRegs.x30 = lul::TaggedUWord(mc->regs[30]);
startRegs.sp = lul::TaggedUWord(mc->sp);
# elif defined(GP_PLAT_arm64_freebsd)
startRegs.pc = lul::TaggedUWord(mc->mc_gpregs.gp_elr);
startRegs.x29 = lul::TaggedUWord(mc->mc_gpregs.gp_x[29]);
startRegs.x30 = lul::TaggedUWord(mc->mc_gpregs.gp_lr);
startRegs.sp = lul::TaggedUWord(mc->mc_gpregs.gp_sp);
# elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
@ -1897,13 +1907,15 @@ static void DoLULBacktrace(PSLockRef aLock,
lul::StackImage stackImg;
{
# if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android)
# if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_amd64_android) || \
defined(GP_PLAT_amd64_freebsd)
uintptr_t rEDZONE_SIZE = 128;
uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
uintptr_t rEDZONE_SIZE = 0;
uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android)
# elif defined(GP_PLAT_arm64_linux) || defined(GP_PLAT_arm64_android) || \
defined(GP_PLAT_arm64_freebsd)
uintptr_t rEDZONE_SIZE = 0;
uintptr_t start = startRegs.sp.Value() - rEDZONE_SIZE;
# elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
@ -2745,7 +2757,7 @@ class Sampler {
const TimeStamp& aNow, const Func& aProcessRegs);
private:
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
// Used to restore the SIGPROF handler when ours is removed.
struct sigaction mOldSigprofHandler;
@ -2854,7 +2866,8 @@ class SamplerThread {
// The OS-specific handle for the sampler thread.
#if defined(GP_OS_windows)
HANDLE mThread;
#elif defined(GP_OS_darwin) || defined(GP_OS_linux) || defined(GP_OS_android)
#elif defined(GP_OS_darwin) || defined(GP_OS_linux) || \
defined(GP_OS_android) || defined(GP_OS_freebsd)
pthread_t mThread;
#endif
@ -3330,7 +3343,7 @@ void SamplerThread::Run() {
# include "platform-win32.cpp"
#elif defined(GP_OS_darwin)
# include "platform-macos.cpp"
#elif defined(GP_OS_linux) || defined(GP_OS_android)
#elif defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include "platform-linux-android.cpp"
#else
# error "bad platform"

View File

@ -25,13 +25,15 @@
#include "common/linux/file_id.h"
#include <algorithm>
#include <dlfcn.h>
#include <features.h>
#if defined(GP_OS_linux) || defined(GP_OS_android)
# include <features.h>
#endif
#include <sys/types.h>
#if defined(MOZ_LINKER)
# include "AutoObjectMapper.h"
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
# include <link.h> // dl_phdr_info
#else
# error "Unexpected configuration"
@ -185,6 +187,7 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
}
#endif
#if defined(GP_OS_linux) || defined(GP_OS_android)
// Read info from /proc/self/maps. We ignore most of it.
pid_t pid = profiler_current_process_id();
char path[PATH_MAX];
@ -211,12 +214,12 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
continue;
}
#if defined(GP_OS_linux)
# if defined(GP_OS_linux)
// Try to establish the main executable's load address.
if (exeNameLen > 0 && strcmp(modulePath, exeName) == 0) {
exeExeAddr = start;
}
#elif defined(GP_OS_android)
# elif defined(GP_OS_android)
// Use /proc/pid/maps to get the dalvik-jit section since it has no
// associated phdrs.
if (0 == strcmp(modulePath, "/dev/ashmem/dalvik-jit-code-cache")) {
@ -228,8 +231,9 @@ SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
break;
}
}
#endif
# endif
}
#endif
nsTArray<LoadedLibraryInfo> libInfoList;

View File

@ -83,6 +83,14 @@
#endif
#if defined(GP_OS_freebsd)
# ifndef ElfW
# define ElfW(type) Elf_##type
# endif
#endif
namespace lul {
// Traits classes so consumers can write templatized code to deal

View File

@ -1405,8 +1405,9 @@ void LUL::Unwind(/*OUT*/ uintptr_t* aFramePCs,
continue;
}
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android)
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) || \
defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) || \
defined(GP_PLAT_amd64_freebsd)
// There's no RuleSet for the specified address. On amd64/x86_linux, see if
// it's possible to recover the caller's frame by using the frame pointer.
@ -1517,7 +1518,8 @@ void LUL::Unwind(/*OUT*/ uintptr_t* aFramePCs,
}
}
#endif // defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux) ||
// defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android)
// defined(GP_PLAT_amd64_android) || defined(GP_PLAT_x86_android) ||
// defined(GP_PLAT_amd64_freebsd)
// We failed to recover a frame either using CFI or FP chasing, and we
// have no other ways to recover the frame. So we have to give up.

View File

@ -24,7 +24,7 @@
void read_procmaps(lul::LUL* aLUL) {
MOZ_ASSERT(aLUL->CountMappings() == 0);
#if defined(GP_OS_linux) || defined(GP_OS_android)
#if defined(GP_OS_linux) || defined(GP_OS_android) || defined(GP_OS_freebsd)
SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
for (size_t i = 0; i < info.GetSize(); i++) {

View File

@ -59,7 +59,7 @@ if CONFIG['MOZ_GECKO_PROFILER']:
'gecko/nsProfiler.cpp',
]
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
if CONFIG['OS_TARGET'] in ('Android', 'Linux', 'FreeBSD'):
if CONFIG['CPU_ARCH'] in ('arm', 'aarch64', 'x86', 'x86_64', 'mips64'):
UNIFIED_SOURCES += [
'lul/AutoObjectMapper.cpp',
@ -83,7 +83,7 @@ if CONFIG['MOZ_GECKO_PROFILER']:
]
if not CONFIG['HAVE_GETCONTEXT']:
SOURCES += ['/toolkit/crashreporter/google-breakpad/src/common/linux/breakpad_getcontext.S']
if CONFIG['CPU_ARCH'] == 'arm':
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['OS_TARGET'] != 'FreeBSD':
SOURCES += [
'core/EHABIStackWalk.cpp',
]

View File

@ -39,7 +39,7 @@ class ProfilerCodeAddressService : public mozilla::CodeAddressService<> {
bool GetFunction(const void* aPc, nsACString& aResult);
private:
#ifdef XP_LINUX
#if defined(XP_LINUX) || defined(XP_FREEBSD)
// Map of library names (owned by mLibraryStrings) to SymbolTables filled
// in by profiler_get_symbol_table.
mozilla::HashMap<const char*, mozilla::SymbolTable,