/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
// This contains things related to the Gecko profiler, for use in third_party
// code. It is very minimal and is designed to be used by patching over
// upstream code.
// Only use the C ABI and guard C++ code with #ifdefs, don't pull anything from
// Gecko, it must be possible to include the header file into any C++ codebase.
#ifndef MICRO_GECKO_PROFILER
#define MICRO_GECKO_PROFILER
#ifdef __cplusplus
extern "C" {
#endif
#include <mozilla/Types.h>
#include <stdio.h>
#ifdef _WIN32
# include <libloaderapi.h>
#else
# include <dlfcn.h>
#endif
#include "ProfilerNativeStack.h"
#if !
defined(CallerPC)
# define CallerPC() __builtin_extract_return_addr(__builtin_return_address(0))
#endif // !defined(CallerPC)
extern MOZ_EXPORT
void uprofiler_register_thread(
const char* aName,
void* aGuessStackTop);
extern MOZ_EXPORT
void uprofiler_unregister_thread();
extern MOZ_EXPORT
void uprofiler_simple_event_marker(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
extern MOZ_EXPORT
void uprofiler_simple_event_marker_capture_stack(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
extern MOZ_EXPORT
void uprofiler_simple_event_marker_with_stack(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
void* provided_stack);
extern MOZ_EXPORT
bool uprofiler_backtrace_into_buffer(
struct NativeStack* stack,
void* aBuffer);
extern MOZ_EXPORT
void uprofiler_native_backtrace(
const void* top,
struct NativeStack* stack);
extern MOZ_EXPORT
bool uprofiler_is_active();
extern MOZ_EXPORT
bool uprofiler_feature_active(int32_t feature);
extern MOZ_EXPORT
bool uprofiler_get(
struct UprofilerFuncPtrs* aFuncPtrs);
/* NOLINT because we want to stick to C here */
// NOLINTBEGIN(modernize-use-using)
typedef bool (*uprofiler_getter)(
struct UprofilerFuncPtrs* aFuncPtrs);
// NOLINTEND(modernize-use-using)
#ifdef __cplusplus
}
struct AutoRegisterProfiler {
AutoRegisterProfiler(
const char* name,
char* stacktop) {
if (getenv(
"MOZ_UPROFILER_LOG_THREAD_CREATION")) {
printf(
"### UProfiler: new thread: '%s'\n", name);
}
uprofiler_register_thread(name, stacktop);
}
~AutoRegisterProfiler() { uprofiler_unregister_thread(); }
};
#endif // __cplusplus
void uprofiler_simple_event_marker(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
struct UprofilerFuncPtrs {
void (*register_thread)(
const char* aName,
void* aGuessStackTop);
void (*unregister_thread)();
void (*simple_event_marker)(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
void (*simple_event_marker_capture_stack)(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
void (*simple_event_marker_with_stack)(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
void* provided_stack);
bool (*backtrace_into_buffer)(
struct NativeStack* stack,
void* aBuffer);
void (*native_backtrace)(
const void* top,
struct NativeStack* stack);
bool (*is_active)();
bool (*feature_active)(int32_t feature);
};
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored
"-Wunused-function"
static void register_thread_noop(
const char* aName,
void* aGuessStackTop) {
/* no-op */
}
static void unregister_thread_noop() {
/* no-op */ }
static void simple_event_marker_noop(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values) {
/* no-op */
}
static void simple_event_marker_capture_stack_noop(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values) {
/* no-op */
}
static void simple_event_marker_with_stack_noop(
const char* name,
const char category,
char phase,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
void* provided_stack) {
/* no-op */
}
static bool backtrace_into_buffer_noop(
struct NativeStack* stack,
void* aBuffer) {
/* no-op */
return false;
}
static void native_backtrace_noop(
const void* top,
struct NativeStack* stack) {
/* no-op */ }
static bool is_active_noop() {
/* no-op */ return false; }
static bool feature_active_noop(int32_t feature) {
/* no-op */ return false; }
#pragma GCC diagnostic pop
#if defined(_WIN32)
# define UPROFILER_OPENLIB() GetModuleHandle(NULL)
#else
# define UPROFILER_OPENLIB() dlopen(NULL, RTLD_NOW)
#endif
#if defined(_WIN32)
# define UPROFILER_GET_SYM(handle, sym) GetProcAddress(handle, sym)
#else
# define UPROFILER_GET_SYM(handle, sym) (typeof(sym)*)(dlsym(handle,
#sym))
#endif
#if defined(_WIN32)
# define UPROFILER_PRINT_ERROR(func) fprintf(stderr,
"%s error\n",
#func);
#else
# define UPROFILER_PRINT_ERROR(func) \
fprintf(stderr,
"%s error: %s\n",
#func, dlerror());
#endif
#define FETCH(func) \
uprofiler.func = UPROFILER_GET_SYM(handle, uprofiler_
##func); \
if (!uprofiler.func) { \
UPROFILER_PRINT_ERROR(uprofiler_
##func); \
uprofiler.func = func
##_noop; \
}
#define UPROFILER_VISIT() \
FETCH(register_thread) \
FETCH(unregister_thread) \
FETCH(simple_event_marker) \
FETCH(simple_event_marker_capture_stack) \
FETCH(simple_event_marker_with_stack) \
FETCH(backtrace_into_buffer) \
FETCH(native_backtrace) \
FETCH(is_active) \
FETCH(feature_active)
// Assumes that a variable of type UprofilerFuncPtrs, named uprofiler
// is accessible in the scope
#define UPROFILER_GET_FUNCTIONS() \
void* handle = UPROFILER_OPENLIB(); \
if (!handle) { \
UPROFILER_PRINT_ERROR(UPROFILER_OPENLIB); \
uprofiler.register_thread = register_thread_noop; \
uprofiler.unregister_thread = unregister_thread_noop; \
uprofiler.simple_event_marker = simple_event_marker_noop; \
uprofiler.simple_event_marker_capture_stack = \
simple_event_marker_capture_stack_noop; \
uprofiler.simple_event_marker_with_stack = \
simple_event_marker_with_stack_noop; \
uprofiler.backtrace_into_buffer = backtrace_into_buffer_noop; \
uprofiler.native_backtrace = native_backtrace_noop; \
uprofiler.is_active = is_active_noop; \
uprofiler.feature_active = feature_active_noop; \
} \
UPROFILER_VISIT()
#define UPROFILER_GET(var) \
uprofiler_getter var = nullptr; \
void* handle = UPROFILER_OPENLIB(); \
if (!handle) { \
UPROFILER_PRINT_ERROR(UPROFILER_OPENLIB); \
}
else { \
(var) = UPROFILER_GET_SYM(handle, uprofiler_get); \
if (!(var)) { \
UPROFILER_PRINT_ERROR(uprofiler_get); \
} \
}
#endif // MICRO_GECKO_PROFILER