2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 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/type-profile.h"
24 #include <tbb/concurrent_hash_map.h>
26 #include "hphp/util/lock.h"
27 #include "hphp/util/logger.h"
28 #include "hphp/util/trace.h"
30 #include "hphp/runtime/base/runtime-option.h"
31 #include "hphp/runtime/base/stats.h"
32 #include "hphp/runtime/base/thread-info.h"
33 #include "hphp/runtime/vm/func.h"
34 #include "hphp/runtime/vm/jit/mc-generator.h"
35 #include "hphp/runtime/vm/jit/write-lease.h"
36 #include "hphp/runtime/vm/treadmill.h"
37 #include "hphp/runtime/vm/jit/relocation.h"
38 #include "hphp/util/atomic-vector.h"
42 TRACE_SET_MOD(typeProfile
);
44 //////////////////////////////////////////////////////////////////////
52 * In cli mode, we only record samples if we're in recording to replay later.
54 * In server mode, we exclude warmup document requests from profiling, then
55 * record samples for EvalJitProfileInterpRequests standard requests.
57 bool __thread profileOn
= false;
58 static bool warmingUp
;
59 static int64_t numRequests
;
60 bool __thread standardRequest
= true;
61 static std::atomic
<bool> singleJitLock
;
62 static std::atomic
<int> singleJitRequests
;
63 static std::atomic
<int> relocateRequests
;
65 void setRelocateRequests(int32_t n
) {
66 relocateRequests
.store(n
);
71 using FuncProfileCounters
= tbb::concurrent_hash_map
<FuncId
,uint32_t>;
72 FuncProfileCounters s_func_counters
;
76 void profileWarmupStart() {
80 void profileWarmupEnd() {
84 typedef std::pair
<const Func
*, uint32_t> FuncHotness
;
85 static bool comp(const FuncHotness
& a
, const FuncHotness
& b
) {
86 return a
.second
> b
.second
;
90 * Set AttrHot on hot functions. Sort all functions by their profile count, and
91 * set AttrHot to the top Eval.HotFuncCount functions.
93 static Mutex syncLock
;
94 static void setHotFuncAttr() {
95 static bool synced
= false;
102 * s_treadmill forces any Funcs that are being destroyed to go through a
103 * treadmill pass, to make sure we won't try to dereference something that's
104 * being pulled out from under us.
106 Func::s_treadmill
= true;
108 Func::s_treadmill
= false;
111 if (RuntimeOption::EvalHotFuncCount
) {
112 std::priority_queue
<FuncHotness
,
113 std::vector
<FuncHotness
>,
114 bool(*)(const FuncHotness
& a
, const FuncHotness
& b
)>
117 Func::getFuncVec().foreach([&](const Func
* f
) {
119 auto const profCounter
= [&]() -> uint32_t {
120 FuncProfileCounters::const_accessor acc
;
121 if (s_func_counters
.find(acc
, f
->getFuncId())) {
126 auto fh
= FuncHotness(f
, profCounter
);
127 if (queue
.size() >= RuntimeOption::EvalHotFuncCount
) {
128 if (!comp(fh
, queue
.top())) return;
134 while (queue
.size()) {
135 auto f
= queue
.top().first
;
137 const_cast<Func
*>(f
)->setAttrs(f
->attrs() | AttrHot
);
141 // We won't need the counters anymore. But there might be requests in flight
142 // that still thought they were profiling, so we need to clear it on the
144 Treadmill::enqueue([&] {
145 FuncProfileCounters
newMap(0);
146 swap(s_func_counters
, newMap
);
152 void profileIncrementFuncCounter(const Func
* f
) {
153 FuncProfileCounters::accessor acc
;
154 auto const value
= FuncProfileCounters::value_type(f
->getFuncId(), 0);
155 s_func_counters
.insert(acc
, value
);
159 int64_t requestCount() {
163 static inline bool doneProfiling() {
164 return (numRequests
>= RuntimeOption::EvalJitProfileInterpRequests
) ||
165 (RuntimeOption::ClientExecutionMode() &&
166 !RuntimeOption::EvalJitProfileRecord
);
169 static inline bool profileThisRequest() {
170 if (warmingUp
) return false;
171 if (doneProfiling()) return false;
172 if (RuntimeOption::ServerExecutionMode()) return true;
173 return RuntimeOption::EvalJitProfileRecord
;
176 void profileRequestStart() {
177 bool p
= profileThisRequest();
178 if (profileOn
&& !p
) {
179 // If we are turning off profiling, set AttrHot on
180 // functions that are "hot".
185 bool okToJit
= !warmingUp
&& !p
;
187 jit::Lease::mayLock(true);
188 if (singleJitRequests
< RuntimeOption::EvalNumSingleJitRequests
) {
190 if (!singleJitLock
.compare_exchange_strong(flag
, true)) {
191 jit::Lease::mayLock(false);
195 if (standardRequest
!= okToJit
) {
196 standardRequest
= okToJit
;
197 if (!ThreadInfo::s_threadInfo
.isNull()) {
198 ThreadInfo::s_threadInfo
->m_reqInjectionData
.updateJit();
202 if (standardRequest
&& relocateRequests
> 0 && !--relocateRequests
) {
203 jit::liveRelocate(true);
207 void profileRequestEnd() {
208 if (warmingUp
) return;
209 numRequests
++; // racy RMW; ok to miss a rare few.
210 if (standardRequest
&&
211 singleJitRequests
< RuntimeOption::EvalNumSingleJitRequests
&&
212 jit::Lease::mayLock(true)) {
213 assert(singleJitLock
);
215 singleJitLock
= false;
216 if (RuntimeOption::ServerExecutionMode()) {
217 Logger::Warning("Finished singleJitRequest %d", singleJitRequests
.load());