2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
18 #include "hphp/runtime/vm/runtime-type-profiler.h"
26 #include "folly/AtomicHashMap.h"
27 #include "folly/dynamic.h"
28 #include "folly/json.h"
30 #include "hphp/runtime/base/type-array.h"
31 #include "hphp/runtime/base/type-string.h"
32 #include "hphp/util/atomic-vector.h"
36 //////////////////////////////////////////////////////////////////////
39 * Holds an atomic count of profiled types.
42 struct ProfileCounter
{
43 std::atomic
<int64_t> m_count
;
45 ProfileCounter() : m_count(0) {}
46 ~ProfileCounter() = default;
47 ProfileCounter
& operator=(const ProfileCounter
& pc
) = delete;
49 ProfileCounter(const ProfileCounter
& pc
) : m_count(pc
.m_count
.load()) {}
50 void inc() { m_count
.fetch_add(1); }
51 int64_t load() { return m_count
.load(); }
55 typedef folly::AtomicHashMap
<const char*,ProfileCounter
,cstr_hash
,eqstr
>
57 typedef AtomicVector
<TypeCounter
*> FuncTypeCounter
;
58 typedef AtomicVector
<FuncTypeCounter
*> RuntimeProfileInfo
;
60 //////////////////////////////////////////////////////////////////////
62 static const int kShowTopN
= 6;
64 static FuncTypeCounter
emptyFuncCounter(1,0);
65 static RuntimeProfileInfo
* allProfileInfo
;
66 static std::atomic
<int> counter(0);
68 //////////////////////////////////////////////////////////////////////
72 void initFuncTypeProfileData(const Func
* func
) {
73 auto myVector
= new FuncTypeCounter(func
->numParams() + 1, 0);
74 for (long i
= 0; i
< func
->numParams() + 1; i
++) {
75 myVector
->exchange(i
, new TypeCounter(200));
77 allProfileInfo
->exchange(func
->getFuncId(), myVector
);
80 const char* getTypeString(const TypedValue
* value
) {
81 if (value
->m_type
== KindOfObject
) {
82 return value
->m_data
.pobj
->o_getClassName().data();
84 if (value
->m_type
== KindOfResource
) {
85 return value
->m_data
.pres
->o_getClassName().data();
87 return getDataTypeString(value
->m_type
).c_str();
90 void logType(const Func
* func
, int32_t paramIndex
, const char* typeString
) {
91 if (paramIndex
> func
->numParams()) {
92 // Don't bother logging types for extra args.
95 if (allProfileInfo
->get(func
->getFuncId()) == &emptyFuncCounter
) {
96 initFuncTypeProfileData(func
);
98 auto it
= allProfileInfo
->get(func
->getFuncId());
99 TypeCounter
* hashmap
= it
->get(paramIndex
);
101 auto result
= hashmap
->insert(typeString
, ProfileCounter());
102 result
.first
->second
.inc();
103 } catch (folly::AtomicHashMapFullError
& e
) {
104 // Fail silently if hashmap is full
108 Array
getTopN(Array
&allTypes
, int n
) {
110 for (int i
= 0; i
< n
; i
++ ) {
113 for (ArrayIter
iter(allTypes
); iter
; ++iter
) {
114 if (iter
.second().toDouble() > max
) {
115 max_key
= iter
.first().toString();
116 max
= iter
.second().toDouble();
120 ret
.set(max_key
, VarNR(max
));
121 allTypes
.remove(max_key
);
129 //////////////////////////////////////////////////////////////////////
131 void initTypeProfileStructure() {
132 allProfileInfo
= new RuntimeProfileInfo(750000, &emptyFuncCounter
);
135 void profileOneArgument(const TypedValue value
, const int32_t paramIndex
,
137 assert(allProfileInfo
!= nullptr);
138 if (!func
|| !func
->fullName()) return;
140 const char* typeString
= getTypeString(&value
);
142 if (func
->fullName()->size() != 0) {
143 logType(func
, paramIndex
+ 1, typeString
);
146 if (paramIndex
== -1) {
150 if (RuntimeOption::EvalRuntimeTypeProfileLoggingFreq
&&
151 counter
.load() % RuntimeOption::EvalRuntimeTypeProfileLoggingFreq
== 0) {
152 writeProfileInformationToDisk();
156 void profileAllArguments(ActRec
* ar
) {
157 for (int i
= 0; i
< ar
->m_func
->numParams(); i
++) {
158 logType(ar
->m_func
, i
+ 1, getTypeString(frame_local(ar
, i
)));
162 Array
getPercentParamInfoArray(const Func
* func
) {
164 auto funcParamMap
= allProfileInfo
->get(func
->getFuncId());
165 for (int i
= 0; i
<= func
->numParams(); i
++) {
167 auto typeCount
= funcParamMap
->get(i
);
168 if (typeCount
== nullptr) {
172 for (auto j
= typeCount
->begin(); j
!= typeCount
->end(); j
++) {
173 total
+= j
->second
.load();
175 for (auto j
= typeCount
->begin(); j
!= typeCount
->end(); j
++) {
176 String key
= String(j
->first
);
177 int64_t count
= j
->second
.load();
178 types
.set(key
, VarNR(double(count
) / total
));
180 Array topTypes
= getTopN(types
, kShowTopN
);
181 ret
.append(VarNR(topTypes
));
186 void writeProfileInformationToDisk() {
187 assert(allProfileInfo
!= nullptr);
188 folly::dynamic all_info
= folly::dynamic::object
;
189 for (auto i
= 0; i
<= Func::nextFuncId(); i
++) {
190 folly::dynamic info
= {};
191 auto funcParamMap
= allProfileInfo
->get(i
);
192 if (funcParamMap
== &emptyFuncCounter
) {
195 for (auto j
= 0; j
<= Func::fromFuncId(i
)->numParams(); j
++) {
196 auto typeCount
= funcParamMap
->get(j
);
197 if (typeCount
== nullptr) {
200 info
.push_back(folly::dynamic::object
);
201 for (auto k
= typeCount
->begin(); k
!= typeCount
->end(); k
++) {
202 folly::dynamic key
= std::string(k
->first
);
203 folly::dynamic value
= k
->second
.load();
204 info
[j
][key
] = value
;
207 const Func
* func
= Func::fromFuncId(i
);
208 all_info
[std::string(func
->fullName()->data())] = info
;
210 std::ofstream logfile
;
211 logfile
.open("/tmp/type-profile.txt", std::fstream::out
| std::fstream::app
);
212 logfile
<< folly::toJson(all_info
).toStdString();