Bug 1774416 - Allow dynamically fetching uprofiler functions to profile libraries that can't access the Gecko profiler API. r=canaltinova

This is solving a particular problem for us in media land. libxul depends on
libmoz{avcodec,format,util}, but not the opposite. We can't use the profiler
functions (thread registration, markers, etc.) there.

Unfortunately those libraries contain most of the interesting part for a few
codecs, including their own thread pool, so we really want to register those
threads and add markers to their innards so that we can figure out what's going
on and how to use them in the most efficient way.

Another problem is that those libraries are in C, and the markers API is in
C++.

The trick here is that when those libraries are being called into, the functions
in MicroGeckoProfiler.h (that exposes a C API for the profiler) are available in
the address space (it's never too early), so we can fetch them and start using
them, without having libmoz{avcodec,format,util} link to libxul.

This let us write temporary patches over vendored dependencies to diagnose
locally, and/or carry small and easily rebaseable patches to register important
threads that are otherwise invisible to the profiler.

Differential Revision: https://phabricator.services.mozilla.com/D149543
This commit is contained in:
Paul Adenot 2022-11-23 16:39:35 +00:00
Родитель 01a5c02624
Коммит 1d31bafb33
1 изменённых файлов: 93 добавлений и 8 удалений

Просмотреть файл

@ -17,19 +17,26 @@
extern "C" { extern "C" {
#endif #endif
void uprofiler_register_thread(const char* aName, void* aGuessStackTop); #include <mozilla/Types.h>
void uprofiler_unregister_thread(); #include <stdio.h>
void uprofiler_simple_event_marker(const char* name, char phase, int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
#ifdef __cplusplus #ifdef _WIN32
} # include <libloaderapi.h>
#else
# include <dlfcn.h>
#endif #endif
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, char phase, int num_args, const char** arg_names,
const unsigned char* arg_types, const unsigned long long* arg_values);
#ifdef __cplusplus #ifdef __cplusplus
}
struct AutoRegisterProfiler { struct AutoRegisterProfiler {
AutoRegisterProfiler(const char* name, char* stacktop) { AutoRegisterProfiler(const char* name, char* stacktop) {
if (getenv("MOZ_UPROFILER_LOG_THREAD_CREATION")) { if (getenv("MOZ_UPROFILER_LOG_THREAD_CREATION")) {
@ -41,4 +48,82 @@ struct AutoRegisterProfiler {
}; };
#endif // __cplusplus #endif // __cplusplus
void uprofiler_simple_event_marker(const char* name, 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, char phase, int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values);
};
#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, char phase, int num_args,
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values) {
/* no-op */
}
#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) 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
// 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.register_thread = \
UPROFILER_GET_SYM(handle, "uprofiler_register_thread"); \
if (!uprofiler.register_thread) { \
UPROFILER_PRINT_ERROR(uprofiler_unregister_thread); \
uprofiler.register_thread = register_thread_noop; \
} \
uprofiler.unregister_thread = \
UPROFILER_GET_SYM(handle, "uprofiler_unregister_thread"); \
if (!uprofiler.unregister_thread) { \
UPROFILER_PRINT_ERROR(uprofiler_unregister_thread); \
uprofiler.unregister_thread = unregister_thread_noop; \
} \
uprofiler.simple_event_marker = \
UPROFILER_GET_SYM(handle, "uprofiler_simple_event_marker"); \
if (!uprofiler.simple_event_marker) { \
UPROFILER_PRINT_ERROR(uprofiler_simple_event_marker); \
uprofiler.simple_event_marker = simple_event_marker_noop; \
}
#endif // MICRO_GECKO_PROFILER #endif // MICRO_GECKO_PROFILER