Coverage Report

Created: 2025-01-28 06:38

/src/hermes/lib/VM/Profiler/SamplingProfilerSampler.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#ifndef HERMES_VM_PROFILER_GLOBALPROFILER_H
9
#define HERMES_VM_PROFILER_GLOBALPROFILER_H
10
11
#include "hermes/VM/Profiler/SamplingProfiler.h"
12
13
#if HERMESVM_SAMPLING_PROFILER_AVAILABLE
14
15
#include <condition_variable>
16
#include <mutex>
17
#include <thread>
18
#include <unordered_set>
19
20
namespace hermes {
21
namespace vm {
22
namespace sampling_profiler {
23
/// Sampler manages the SamplingProfiler's sampling thread, and abstracts
24
/// away platform-specific code for suspending the VM thread and performing the
25
/// JS stack walk.
26
///
27
/// Each suported platform (e.g., Posix), must have its own "flavor" of the
28
/// Sampler (e.g., SamplerPosix) which must implement the
29
/// Sampler::platform<<*>> methods.
30
///
31
/// Usually, the platform-agnostic Sampler invokes the platform-specific
32
/// ones. For example, Sampler::enable() performs some checks, then call
33
/// Sampler::platformEnable() for completing the initialization.
34
///
35
/// Sampling happens on a separate thread whose lifetime is managed by the
36
/// Sampler's platform-specific code. The sampling thread runs the
37
/// Sampler::timerLoop function, which will periodically traverse the
38
/// list of registered runtimes, and perform the stack walk, which is
39
/// accomplished as follows.
40
///
41
/// Sampler::timerLoop invokes Sampler::sampleStacks, which will
42
/// invoke Sampler::sampleStack for each registered runtime.
43
///
44
/// Sampler::sampleStack invoke the platform-specific
45
/// Sampler::suspendVMAndWalkStack method. This hook should be
46
/// implemented on every supported platform, and it is responsible for
47
/// suspending VM execution. With the stopped VM, the platform-specific code
48
/// calls back into platform-agnostic code (Sampler::walkRuntimeStack) so
49
/// stack walking can continue.
50
///
51
/// When Sampler::walkRuntimeStack runs the VM is suspended, but this
52
/// suspension can happen when the VM is in the middle of, e.g., memory
53
/// allocation, or while it is holding some lock. Thus,
54
/// Sampler::walkRuntimeStack should not acquire locks, or even allocate
55
/// memory. It should perform stack walking quickly and expeditiously. All
56
/// buffers used for stack walking are pre-allocated before calling into this
57
/// function.
58
///
59
/// Finally, when Sampler::walkRuntimeStack returns to
60
/// Sampler::suspendVMAndWalkStack, VM execution should be resumed.
61
struct Sampler {
62
  virtual ~Sampler();
63
64
  /// Lock for profiler operations and access to member fields.
65
  std::mutex profilerLock_;
66
67
  /// Profiler instances for all the individual runtimes that are currently
68
  /// registered.
69
  std::unordered_set<SamplingProfiler *> profilers_;
70
71
  /// Whether profiler is enabled or not. Protected by profilerLock_.
72
  bool enabled_{false};
73
74
  /// Threading: load/store of sampledStackDepth_ and sampleStorage_
75
  /// are protected by samplingDoneSem_.
76
  /// Actual sampled stack depth in sampleStorage_.
77
  uint32_t sampledStackDepth_{0};
78
  /// Preallocated stack frames storage for signal handler(because
79
  /// allocating memory in signal handler is not allowed)
80
  /// This storage does not need to be protected by lock because accessing to
81
  /// it is serialized by samplingDoneSem_.
82
  SamplingProfiler::StackTrace sampleStorage_{SamplingProfiler::kMaxStackDepth};
83
84
  /// This thread starts in timerLoop_, and samples the stacks of registered
85
  /// runtimes periodically. It is created in \p enable() and joined in
86
  /// \p disable().
87
  std::thread timerThread_;
88
89
  /// This condition variable can be used to wait for a change in the enabled
90
  /// member variable.
91
  std::condition_variable enabledCondVar_;
92
93
  /// Main routine to take a sample of runtime stack.
94
  /// \return false for failure which timer loop thread should stop.
95
  bool sampleStacks();
96
97
  /// Timer loop thread main routine.
98
  void timerLoop(double meanHzFreq);
99
100
  /// Implementation of SamplingProfiler::enable/disable.
101
  bool enable(double meanHzFreq);
102
  bool disable();
103
104
  /// \return true if the sampling profiler is enabled, false otherwise.
105
  bool enabled();
106
107
  /// Register the \p profiler associated with an active runtime.
108
  /// Should only be called from the thread running the hermes runtime
109
  /// associated with \p profiler.
110
  void registerRuntime(SamplingProfiler *profiler);
111
112
  /// Unregister the active runtime and current thread associated with
113
  /// \p profiler.
114
  void unregisterRuntime(SamplingProfiler *profiler);
115
116
  /// \return the singleton profiler instance.
117
  static Sampler *get();
118
119
 protected:
120
0
  Sampler() = default;
121
122
  void walkRuntimeStack(SamplingProfiler *profiler);
123
124
 private:
125
  /// Sample stack for a profiler.
126
  bool sampleStack(SamplingProfiler *localProfiler);
127
128
  // Platform-specific hooks.
129
130
  /// Platform-specific hook invoked while enabling the sampling profiler. This
131
  /// hook is invoked prior to creating the sampling thread, and with
132
  /// this->profilerLock_ lock held. It must \return true if the sampling
133
  /// profiler can be enabled, and false otherwise.
134
  bool platformEnable();
135
136
  /// Platform-specific hook invoked while disabling the sampling profiler.
137
  /// This hook is invoked prior to terminating the sampling thread, and with
138
  /// this->profilerLock_ lock held. It must \return true if the sampling
139
  /// profiler can be disabled, and false otherwise.
140
  bool platformDisable();
141
142
  /// Platform-specific hook invoked after \p profiler is registered for
143
  /// sampling profiling. It is invoked with this->profilerLock_ lock held.
144
  void platformRegisterRuntime(SamplingProfiler *profiler);
145
146
  /// Platform-specific hook invoked after \p profiler is removed from the list
147
  /// of known profilers -- i.e., profiling should stop on \p profiler. It is
148
  /// invoked with this->profilerLock_ lock held.
149
  void platformUnregisterRuntime(SamplingProfiler *profiler);
150
151
  /// Platform-specific hook invoked after sampleStack() collects the current
152
  /// stack of \p localProfiler. This is invoked with the
153
  /// \p localProfiler->runtimeDataLock_ lock held outside of the VM thread.
154
  void platformPostSampleStack(SamplingProfiler *localProfiler);
155
156
  /// Platform-specific hook invoked to suspend the VM thread and perform stack
157
  /// sampling. Note that this method is invoked with both this->profilerLock_
158
  /// and \p profiler->runtimeDataLock_ locks held.
159
  bool platformSuspendVMAndWalkStack(SamplingProfiler *profiler);
160
};
161
} // namespace sampling_profiler
162
} // namespace vm
163
} // namespace hermes
164
165
#endif // HERMESVM_SAMPLING_PROFILER_AVAILABLE
166
167
#endif // HERMES_VM_PROFILER_GLOBALPROFILER_H