Skip to content

Commit 74de9f8

Browse files
neildharfacebook-github-bot
authored andcommitted
Track runtime thread through profiler
Summary: Instead of tracking runtimes from the `GlobalProfiler`, track the `SamplingProfiler` instance owned by each runtime. This instance can also track which thread the `SamplingProfiler` instance is associated with. Reviewed By: dulinriley Differential Revision: D25734486 fbshipit-source-id: eb8fca5bee2f0ba0170a870f831cc4f14a0754f7
1 parent adabc92 commit 74de9f8

2 files changed

Lines changed: 47 additions & 39 deletions

File tree

include/hermes/VM/Profiler/SamplingProfilerPosix.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,13 @@ class SamplingProfiler {
129129
/// Lock for profiler operations and access to member fields.
130130
std::mutex profilerLock_;
131131

132-
/// Stores a list of active <thread, runtime> pair.
133-
/// Protected by profilerLock_.
134-
llvh::DenseMap<Runtime *, pthread_t> activeRuntimeThreads_;
132+
/// Profiler instances for all the individual runtimes that are currently
133+
/// registered.
134+
std::unordered_set<SamplingProfiler *> profilers_;
135135

136-
/// Per-thread runtime instance for loom/local profiling.
136+
/// Per-thread profiler instance for loom/local profiling.
137137
/// Limitations: No recursive runtimes in one thread.
138-
ThreadLocal<Runtime> threadLocalRuntime_;
138+
ThreadLocal<SamplingProfiler> threadLocalProfiler_;
139139

140140
/// Whether profiler is enabled or not. Protected by profilerLock_.
141141
bool enabled_{false};
@@ -191,12 +191,13 @@ class SamplingProfiler {
191191
/// \return true if the sampling profiler is enabled, false otherwise.
192192
bool enabled();
193193

194-
/// Register an active \p runtime and current thread with profiler.
194+
/// Register the \p profiler associated with an active runtime.
195195
/// Should only be called from the thread running hermes runtime.
196-
void registerRuntime(Runtime *runtime);
196+
void registerRuntime(SamplingProfiler *profiler);
197197

198-
/// Unregister an active \p runtime and current thread with profiler.
199-
void unregisterRuntime(Runtime *runtime);
198+
/// Unregister the active runtime and current thread associated with
199+
/// \p profiler.
200+
void unregisterRuntime(SamplingProfiler *profiler);
200201

201202
GlobalProfiler();
202203
/// \return the singleton profiler instance.
@@ -222,6 +223,12 @@ class SamplingProfiler {
222223
/// Prellocated map that contains thread names mapping.
223224
ThreadNamesMap threadNames_;
224225

226+
/// Thread that this profiler instance represents. This can currently only be
227+
/// set from the constructor of SamplingProfiler, so we need to construct a
228+
/// new SamplingProfiler every time the runtime is moved to a different
229+
/// thread.
230+
pthread_t currentThread_;
231+
225232
/// Unique GC event extra info strings container.
226233
std::unordered_set<std::string> gcEventExtraInfoSet_;
227234

lib/VM/Profiler/SamplingProfilerPosix.cpp

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,23 @@ std::atomic<SamplingProfiler::GlobalProfiler *>
3939

4040
std::atomic<bool> SamplingProfiler::GlobalProfiler::handlerSyncFlag_{false};
4141

42-
void SamplingProfiler::GlobalProfiler::registerRuntime(Runtime *runtime) {
42+
void SamplingProfiler::GlobalProfiler::registerRuntime(
43+
SamplingProfiler *profiler) {
4344
std::lock_guard<std::mutex> lockGuard(profilerLock_);
44-
45-
// TODO: should we only register runtime when profiler is enabled?
46-
activeRuntimeThreads_[runtime] = pthread_self();
47-
threadLocalRuntime_.set(runtime);
45+
profilers_.insert(profiler);
46+
threadLocalProfiler_.set(profiler);
4847
}
4948

50-
void SamplingProfiler::GlobalProfiler::unregisterRuntime(Runtime *runtime) {
49+
void SamplingProfiler::GlobalProfiler::unregisterRuntime(
50+
SamplingProfiler *profiler) {
5151
std::lock_guard<std::mutex> lockGuard(profilerLock_);
52-
bool succeed = activeRuntimeThreads_.erase(runtime);
52+
bool succeed = profilers_.erase(profiler);
5353
// TODO: should we allow recursive style
5454
// register/register -> unregister/unregister call?
5555
assert(succeed && "How can runtime not registered yet?");
5656
(void)succeed;
5757

58-
threadLocalRuntime_.set(nullptr);
58+
threadLocalProfiler_.set(nullptr);
5959
}
6060

6161
void SamplingProfiler::registerDomain(Domain *domain) {
@@ -117,8 +117,8 @@ void SamplingProfiler::GlobalProfiler::profilingSignalHandler(int signo) {
117117
auto oldErrno = errno;
118118
// Fetch runtime used by this sampling thread.
119119
auto profilerInstance = instance_.load();
120-
Runtime *curThreadRuntime = profilerInstance->threadLocalRuntime_.get();
121-
auto *localProfiler = curThreadRuntime->samplingProfiler_.get();
120+
auto *localProfiler = profilerInstance->threadLocalProfiler_.get();
121+
auto *curThreadRuntime = localProfiler->runtime_;
122122
if (curThreadRuntime == nullptr) {
123123
// Runtime may have unregistered itself before signal.
124124
errno = oldErrno;
@@ -155,9 +155,8 @@ void SamplingProfiler::GlobalProfiler::profilingSignalHandler(int signo) {
155155
}
156156

157157
bool SamplingProfiler::GlobalProfiler::sampleStack() {
158-
for (const auto &entry : activeRuntimeThreads_) {
159-
auto targetThreadId = entry.second;
160-
auto *localProfiler = entry.first->samplingProfiler_.get();
158+
for (SamplingProfiler *localProfiler : profilers_) {
159+
auto targetThreadId = localProfiler->currentThread_;
161160
std::lock_guard<std::mutex> lk(localProfiler->runtimeDataLock_);
162161
// Ensure there are no allocations in the signal handler by keeping ample
163162
// reserved space.
@@ -293,6 +292,10 @@ SamplingProfiler::GlobalProfiler::get() {
293292

294293
SamplingProfiler::GlobalProfiler::GlobalProfiler() {
295294
instance_.store(this);
295+
#if defined(__ANDROID__) && defined(HERMES_FACEBOOK_BUILD)
296+
profilo_api()->register_external_tracer_callback(
297+
TRACER_TYPE_JAVASCRIPT, collectStackForLoom);
298+
#endif
296299
}
297300

298301
bool SamplingProfiler::GlobalProfiler::enabled() {
@@ -307,7 +310,9 @@ bool SamplingProfiler::GlobalProfiler::enabled() {
307310
uint16_t *depth,
308311
uint16_t max_depth) {
309312
auto profilerInstance = GlobalProfiler::instance_.load();
310-
Runtime *curThreadRuntime = profilerInstance->threadLocalRuntime_.get();
313+
SamplingProfiler *localProfiler =
314+
profilerInstance->threadLocalProfiler_.get();
315+
Runtime *curThreadRuntime = localProfiler->runtime_;
311316
if (curThreadRuntime == nullptr) {
312317
// No runtime in this thread.
313318
return StackCollectionRetcode::NO_STACK_FOR_THREAD;
@@ -319,8 +324,8 @@ bool SamplingProfiler::GlobalProfiler::enabled() {
319324
assert(
320325
profilerInstance != nullptr &&
321326
"Why is GlobalProfiler::instance_ not initialized yet?");
322-
sampledStackDepth = curThreadRuntime->samplingProfiler_->walkRuntimeStack(
323-
profilerInstance->sampleStorage_);
327+
sampledStackDepth =
328+
localProfiler->walkRuntimeStack(profilerInstance->sampleStorage_);
324329
} else {
325330
// TODO: log "GC in process" meta event.
326331
sampledStackDepth = 0;
@@ -373,26 +378,22 @@ bool SamplingProfiler::GlobalProfiler::enabled() {
373378
}
374379
#endif
375380

376-
SamplingProfiler::SamplingProfiler(Runtime *runtime) : runtime_{runtime} {
377-
#if defined(__ANDROID__) && defined(HERMES_FACEBOOK_BUILD)
378-
profilo_api()->register_external_tracer_callback(
379-
TRACER_TYPE_JAVASCRIPT, collectStackForLoom);
380-
#endif
381-
382-
GlobalProfiler::get()->registerRuntime(runtime);
381+
SamplingProfiler::SamplingProfiler(Runtime *runtime)
382+
: currentThread_{pthread_self()}, runtime_{runtime} {
383383
threadNames_[oscompat::thread_id()] = oscompat::thread_name();
384+
GlobalProfiler::get()->registerRuntime(this);
384385
}
385386

386387
SamplingProfiler::~SamplingProfiler() {
387-
GlobalProfiler::get()->unregisterRuntime(runtime_);
388+
GlobalProfiler::get()->unregisterRuntime(this);
388389
}
389390

390391
void SamplingProfiler::dumpSampledStackGlobal(llvh::raw_ostream &OS) {
391392
auto globalProfiler = GlobalProfiler::get();
392393
std::lock_guard<std::mutex> lk(globalProfiler->profilerLock_);
393-
if (!globalProfiler->activeRuntimeThreads_.empty()) {
394-
Runtime *rt = globalProfiler->activeRuntimeThreads_.begin()->first;
395-
rt->samplingProfiler_->dumpSampledStack(OS);
394+
if (!globalProfiler->profilers_.empty()) {
395+
auto *localProfiler = *globalProfiler->profilers_.begin();
396+
localProfiler->dumpSampledStack(OS);
396397
}
397398
}
398399

@@ -436,9 +437,9 @@ void SamplingProfiler::dumpSampledStack(llvh::raw_ostream &OS) {
436437
void SamplingProfiler::dumpChromeTraceGlobal(llvh::raw_ostream &OS) {
437438
auto globalProfiler = GlobalProfiler::get();
438439
std::lock_guard<std::mutex> lk(globalProfiler->profilerLock_);
439-
if (!globalProfiler->activeRuntimeThreads_.empty()) {
440-
Runtime *rt = globalProfiler->activeRuntimeThreads_.begin()->first;
441-
rt->samplingProfiler_->dumpChromeTrace(OS);
440+
if (!globalProfiler->profilers_.empty()) {
441+
auto *localProfiler = *globalProfiler->profilers_.begin();
442+
localProfiler->dumpChromeTrace(OS);
442443
}
443444
}
444445

0 commit comments

Comments
 (0)