forked from mirrors/gecko-dev
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:
parent
01a5c02624
commit
1d31bafb33
1 changed files with 93 additions and 8 deletions
|
|
@ -17,19 +17,26 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
void uprofiler_register_thread(const char* aName, void* aGuessStackTop);
|
||||
void uprofiler_unregister_thread();
|
||||
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);
|
||||
#include <mozilla/Types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#ifdef _WIN32
|
||||
# include <libloaderapi.h>
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
#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
|
||||
}
|
||||
|
||||
struct AutoRegisterProfiler {
|
||||
AutoRegisterProfiler(const char* name, char* stacktop) {
|
||||
if (getenv("MOZ_UPROFILER_LOG_THREAD_CREATION")) {
|
||||
|
|
@ -41,4 +48,82 @@ struct AutoRegisterProfiler {
|
|||
};
|
||||
#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
|
||||
|
|
|
|||
Loading…
Reference in a new issue