2 +----------------------------------------------------------------------+
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"
28 ///////////////////////////////////////////////////////////////////////////////
31 using Counter
= int64_t;
32 constexpr Counter kInvalidCounter
= -1;
34 Counter
getCurrentCounter() {
36 Timer::GetThreadCPUTimeNanos()
41 Counter start
= kInvalidCounter
;
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();
66 void requestShutdown();
68 void transition(State from
, State to
);
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.
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());
136 m_s
.emplace_back(State::InRequest
);
139 for (auto& count
: m_transitionCounts
) {
144 void RequestWorkloadStats::requestShutdown() {
145 if (m_s
.empty()) return;
147 assertx(m_s
.back() == State::InRequest
);
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
);
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
) {
168 case State::InRequest
:
170 case State::InRequest
:
171 always_assert(false);
173 case State::InInterp
:
182 case State::InInterp
:
184 case State::InInterp
:
186 case State::InRequest
:
199 always_assert(false);
201 case State::InRequest
:
204 case State::InInterp
:
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;
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 ///////////////////////////////////////////////////////////////////////////////