de-dup THREAD_LOCAL macros
[hiphop-php.git] / hphp / runtime / vm / workload-stats.cpp
blob1beef8f9776966603ffea77072069ad014812dad
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/workload-stats.h"
19 #include "hphp/runtime/base/init-fini-node.h"
21 #include "hphp/util/service-data.h"
22 #include "hphp/util/thread-local.h"
23 #include "hphp/util/timer.h"
25 #include <vector>
27 namespace HPHP {
28 ///////////////////////////////////////////////////////////////////////////////
29 namespace {
31 using Counter = int64_t;
32 constexpr Counter kInvalidCounter = -1;
34 Counter getCurrentCounter() {
35 return Counter {
36 Timer::GetThreadCPUTimeNanos()
40 struct DeltaCounter {
41 Counter start = kInvalidCounter;
42 Counter acc;
45 void start(DeltaCounter& dc) {
46 assertx(dc.start == kInvalidCounter);
47 dc.start = getCurrentCounter();
50 void end(DeltaCounter& dc) {
51 dc.acc += getCurrentCounter() - dc.start;
52 assertx(dc.start != kInvalidCounter);
53 if (do_assert) dc.start = kInvalidCounter;
56 ///////////////////////////////////////////////////////////////////////////////
59 // Request workload stats needs external linkage to appease type scanners.
60 struct RequestWorkloadStats final {
61 using State = WorkloadStats::State;
63 static void LoggingInit();
65 void requestInit();
66 void requestShutdown();
68 void transition(State from, State to);
70 void enter(State to);
71 void leave();
73 private:
74 // PHP time is time in a request not spent JITing.
75 // Interp time is time spent interpreting PHP. This discounts time spent
76 // interpreting due to interpOnes. This is because they are in JITed code,
77 // and it doesn't make sense to count them against the JIT being well adapted
78 // to the current workload.
79 DeltaCounter m_php;
80 DeltaCounter m_interp;
82 uint64_t m_transitionCounts[3] = {};
84 std::vector<State> m_s;
86 // Counters initialized at process start.
87 static ServiceData::ExportedTimeSeries* s_interpNanos;
88 static ServiceData::ExportedTimeSeries* s_requestNanos;
89 static ServiceData::ExportedTimeSeries* s_interpVMRatio;
90 static ServiceData::ExportedTimeSeries* s_trans;
91 static ServiceData::ExportedTimeSeries* s_interps;
93 ServiceData::ExportedTimeSeries* RequestWorkloadStats::s_interpNanos;
94 ServiceData::ExportedTimeSeries* RequestWorkloadStats::s_requestNanos;
95 ServiceData::ExportedTimeSeries* RequestWorkloadStats::s_interpVMRatio;
96 ServiceData::ExportedTimeSeries* RequestWorkloadStats::s_trans;
97 ServiceData::ExportedTimeSeries* RequestWorkloadStats::s_interps;
100 // Setup RequestWorkloadStats to have init and request callbacks.
101 THREAD_LOCAL_NO_CHECK(RequestWorkloadStats, s_request_workload_stats);
102 InitFiniNode init(RequestWorkloadStats::LoggingInit,
103 InitFiniNode::When::ProcessInit);
104 InitFiniNode t_init([] () { s_request_workload_stats.getCheck(); },
105 InitFiniNode::When::ThreadInit);
106 InitFiniNode r_init([] () { s_request_workload_stats->requestInit(); },
107 InitFiniNode::When::RequestStart);
108 InitFiniNode r_shutdown([] () { s_request_workload_stats->requestShutdown(); },
109 InitFiniNode::When::RequestFini);
111 void RequestWorkloadStats::LoggingInit() {
112 const std::vector<ServiceData::StatsType> exportTypes{
113 ServiceData::StatsType::AVG,
115 const std::vector<std::chrono::seconds> levels{
116 std::chrono::seconds(60),
117 std::chrono::seconds(0),
120 s_interpNanos = ServiceData::createTimeSeries(
121 "vm.nanos_interp", exportTypes, levels);
122 s_requestNanos = ServiceData::createTimeSeries(
123 "vm.nanos_php", exportTypes, levels);
124 s_interpVMRatio = ServiceData::createTimeSeries(
125 "vm.relative_nanos_interp", exportTypes, levels);
126 s_trans = ServiceData::createTimeSeries(
127 "vm.enter_trans", exportTypes, levels);
128 s_interps = ServiceData::createTimeSeries(
129 "vm.enter_interp", exportTypes, levels);
132 void RequestWorkloadStats::requestInit() {
133 assertx(m_s.empty());
134 m_php.acc = 0;
135 m_interp.acc = 0;
136 m_s.emplace_back(State::InRequest);
137 start(m_php);
139 for (auto& count : m_transitionCounts) {
140 count = 0;
144 void RequestWorkloadStats::requestShutdown() {
145 if (m_s.empty()) return;
146 end(m_php);
147 assertx(m_s.back() == State::InRequest);
148 m_s.pop_back();
149 assertx(m_s.empty());
151 const auto php = m_php.acc;
152 const auto interp = m_interp.acc;
154 s_interpNanos->addValue(interp);
155 s_requestNanos->addValue(php);
157 if (php > 0) {
158 s_interpVMRatio->addValue(interp * 10000 / php);
161 // Log state change counts.
162 s_trans->addValue(m_transitionCounts[State::InTrans]);
163 s_interps->addValue(m_transitionCounts[State::InInterp]);
166 ALWAYS_INLINE void RequestWorkloadStats::transition(State from, State to) {
167 switch (from) {
168 case State::InRequest:
169 switch (to) {
170 case State::InRequest:
171 always_assert(false);
172 break;
173 case State::InInterp:
174 start(m_interp);
175 break;
176 case State::InTrans:
177 end(m_php);
178 break;
180 break;
182 case State::InInterp:
183 switch (to) {
184 case State::InInterp:
185 break;
186 case State::InRequest:
187 end(m_interp);
188 break;
189 case State::InTrans:
190 end(m_interp);
191 end(m_php);
192 break;
194 break;
196 case State::InTrans:
197 switch (to) {
198 case State::InTrans:
199 always_assert(false);
200 break;
201 case State::InRequest:
202 start(m_php);
203 break;
204 case State::InInterp:
205 start(m_php);
206 start(m_interp);
207 break;
209 break;
213 void RequestWorkloadStats::enter(State to) {
214 if (m_s.empty()) return;
215 transition(m_s.back(), to);
216 m_s.emplace_back(to);
217 m_transitionCounts[to]++;
220 void RequestWorkloadStats::leave() {
221 if (m_s.empty()) return;
222 auto s = m_s.back();
223 m_s.pop_back();
224 transition(s, m_s.back());
227 WorkloadStats::WorkloadStats(State guardedState) {
228 s_request_workload_stats->enter(guardedState);
231 WorkloadStats::~WorkloadStats() {
232 s_request_workload_stats->leave();
235 ///////////////////////////////////////////////////////////////////////////////