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 +----------------------------------------------------------------------+
16 #include "hphp/hhbbc/stats.h"
26 #include <type_traits>
28 #include <boost/variant.hpp>
30 #include <tbb/concurrent_hash_map.h>
32 #include <folly/Conv.h>
33 #include <folly/String.h>
34 #include <folly/Format.h>
35 #include <folly/ScopeGuard.h>
36 #include <folly/portability/Stdlib.h>
38 #include "hphp/runtime/vm/hhbc.h"
40 #include "hphp/util/trace.h"
42 #include "hphp/hhbbc/analyze.h"
43 #include "hphp/hhbbc/func-util.h"
44 #include "hphp/hhbbc/index.h"
45 #include "hphp/hhbbc/interp-internal.h"
46 #include "hphp/hhbbc/interp.h"
47 #include "hphp/hhbbc/context.h"
48 #include "hphp/hhbbc/misc.h"
49 #include "hphp/hhbbc/parallel.h"
50 #include "hphp/hhbbc/representation.h"
52 namespace HPHP::HHBBC
{
54 //////////////////////////////////////////////////////////////////////
58 TRACE_SET_MOD(hhbbc_stats
);
60 //////////////////////////////////////////////////////////////////////
62 #define SPEC_TYPES(X) \
63 X(wh, "wait handle", is_specialized_wait_handle) \
64 X(obj_sub, "sub obj", is_sub_obj) \
65 X(obj_exact, "exact obj", is_exact_obj) \
66 X(obj_isect, "isect obj", is_isect_obj) \
67 X(obj_isect_exact, "isect exact obj", is_isect_exact_obj) \
68 X(cls_sub, "sub class", is_sub_cls) \
69 X(cls_exact, "exact class", is_exact_cls) \
70 X(cls_isect, "isect class", is_isect_cls) \
71 X(cls_isect_exact, "isect exact cls", is_isect_exact_cls) \
72 X(arr_val, "array value", is_specialized_array_like_arrval) \
73 X(arr_packedn, "array packedn", is_specialized_array_like_packedn) \
74 X(arr_packed, "array packed", is_specialized_array_like_packed) \
75 X(arr_mapn, "array mapn", is_specialized_array_like_mapn) \
76 X(arr_map, "array map", is_specialized_array_like_map) \
77 X(str, "string", is_specialized_string) \
78 X(lazy_cls, "lazy class", is_specialized_lazycls) \
79 X(int, "int", is_specialized_int) \
80 X(dbl, "double", is_specialized_double) \
84 std::atomic<uint64_t> sub_##y{}; \
85 std::atomic<uint64_t> eq_##y{};
86 HHBBC_TYPE_PREDEFINED(X
)
89 #define X(y, ...) std::atomic<uint64_t> spec_##y;
94 #define TAG(x,...) 1 +
95 constexpr uint32_t kNumRATTags
= REPO_AUTH_TYPE_TAGS(TAG
) 0 ;
101 std::array
<std::atomic
<uint64_t>,Op_count
> op_counts
{};
102 std::array
<std::atomic
<uint64_t>,kNumRATTags
> ratL_tags
{};
103 std::array
<std::atomic
<uint64_t>,kNumRATTags
> ratStk_tags
{};
104 std::atomic
<uint64_t> ratL_specialized_array
{};
105 std::atomic
<uint64_t> ratStk_specialized_array
{};
106 std::atomic
<uint64_t> totalClasses
{};
107 std::atomic
<uint64_t> totalFunctions
{};
108 std::atomic
<uint64_t> totalMethods
{};
109 std::atomic
<uint64_t> effectfulFuncs
{};
110 std::atomic
<uint64_t> effectFreeFuncs
{};
111 std::atomic
<uint64_t> persistentSPropsPub
{};
112 std::atomic
<uint64_t> persistentSPropsProt
{};
113 std::atomic
<uint64_t> persistentSPropsPriv
{};
114 std::atomic
<uint64_t> totalSProps
{};
116 TypeStat privateProps
;
117 TypeStat publicStatics
;
118 TypeStat privateStatics
;
119 TypeStat iterInitBase
;
124 void type_stat_string(std::string
& ret
,
125 const std::string
& prefix
,
126 const TypeStat
& st
) {
128 if (st.eq_##y.load() > 0) { \
129 folly::format(&ret, " {: <32} {: >12}\n", \
130 folly::sformat("{}_=_{}", prefix, #y ":"), \
133 if (st.sub_##y.load() > 0) { \
134 folly::format(&ret, " {: <32} {: >12}\n", \
135 folly::sformat("{}_<_{}", prefix, #y ":"), \
136 st.sub_##y.load()); \
138 HHBBC_TYPE_PREDEFINED(X
)
143 #define X(y, z, ...) \
144 if (st.spec_##y.load() > 0) { \
145 folly::format(&ret, " {: <32} {: >12}\n", \
146 folly::sformat("{}_specialized {}", prefix, z ":"), \
147 st.spec_##y.load()); \
155 std::string
show(const Stats
& stats
) {
156 auto ret
= std::string
{};
158 for (auto i
= uint32_t{}; i
< stats
.op_counts
.size(); ++i
) {
159 if (stats
.op_counts
[i
].load() == 0) continue;
162 " {: >30}: {: >15}\n",
163 opcodeToName(static_cast<Op
>(i
)),
164 stats
.op_counts
[i
].load()
169 type_stat_string(ret
, "ret", stats
.returns
);
170 type_stat_string(ret
, "priv_prop", stats
.privateProps
);
171 type_stat_string(ret
, "pub_static", stats
.publicStatics
);
172 type_stat_string(ret
, "priv_static", stats
.privateStatics
);
173 type_stat_string(ret
, "iterInit_base", stats
.iterInitBase
);
177 " total_methods: {: >12}\n"
178 " total_funcs: {: >12}\n"
179 " total_classes: {: >12}\n"
181 " effectful_funcs: {: >12}\n"
182 " effect_free_funcs: {: >12}\n"
184 " total_sprops: {: >12}\n"
185 " persistent_sprops_pub: {: >12}\n"
186 " persistent_sprops_prot: {: >12}\n"
187 " persistent_sprops_priv: {: >12}\n",
188 stats
.totalMethods
.load(),
189 stats
.totalFunctions
.load(),
190 stats
.totalClasses
.load(),
191 stats
.effectfulFuncs
.load(),
192 stats
.effectFreeFuncs
.load(),
193 stats
.totalSProps
.load(),
194 stats
.persistentSPropsPub
.load(),
195 stats
.persistentSPropsProt
.load(),
196 stats
.persistentSPropsPriv
.load()
200 using T
= RepoAuthType::Tag
;
201 using U
= std::underlying_type
<T
>::type
;
202 #define TAG(x, ...) \
203 if (stats.ratL_tags[static_cast<U>(T::x)].load() > 0 || \
204 stats.ratStk_tags[static_cast<U>(T::x)].load() > 0) { \
205 folly::format(&ret, \
206 " {: >28}: {: >12}\n" \
207 " {: >28}: {: >12}\n", \
209 stats.ratL_tags[static_cast<U>(T::x)].load(), \
211 stats.ratStk_tags[static_cast<U>(T::x)].load()); \
213 REPO_AUTH_TYPE_TAGS(TAG
)
216 if (stats
.ratL_specialized_array
.load() > 0 ||
217 stats
.ratStk_specialized_array
.load() > 0) {
219 " {: >28}: {: >12}\n"
220 " {: >28}: {: >12}\n",
222 stats
.ratL_specialized_array
.load(),
223 "RATStk_Arr_Special",
224 stats
.ratStk_specialized_array
.load());
230 //////////////////////////////////////////////////////////////////////
232 bool is_sub_obj(const Type
& t
) {
234 is_specialized_obj(t
) &&
235 !is_specialized_wait_handle(t
) &&
238 bool is_exact_obj(const Type
& t
) {
240 is_specialized_obj(t
) &&
241 !is_specialized_wait_handle(t
) &&
242 dobj_of(t
).isExact();
244 bool is_isect_obj(const Type
& t
) {
246 is_specialized_obj(t
) &&
247 !is_specialized_wait_handle(t
) &&
248 dobj_of(t
).isIsect();
250 bool is_isect_exact_obj(const Type
& t
) {
252 is_specialized_obj(t
) &&
253 !is_specialized_wait_handle(t
) &&
254 dobj_of(t
).isIsectAndExact();
257 bool is_sub_cls(const Type
& t
) {
258 return is_specialized_cls(t
) && dcls_of(t
).isSub();
260 bool is_exact_cls(const Type
& t
) {
261 return is_specialized_cls(t
) && dcls_of(t
).isExact();
263 bool is_isect_cls(const Type
& t
) {
264 return is_specialized_cls(t
) && dcls_of(t
).isIsect();
266 bool is_isect_exact_cls(const Type
& t
) {
267 return is_specialized_cls(t
) && dobj_of(t
).isIsectAndExact();
270 void add_type(TypeStat
& stat
, const Type
& t
) {
272 if (t.subtypeOf(B##y)) { \
273 if (B##y == BBottom || !t.is(BBottom)) { \
274 if (t.strictSubtypeOf(B##y)) ++stat.sub_##y; \
275 else ++stat.eq_##y; \
278 HHBBC_TYPE_PREDEFINED(X
)
281 #define X(a, b, c) if (c(t)) ++stat.spec_##a;
286 //////////////////////////////////////////////////////////////////////
288 struct StatsSS
: ISS
{
289 explicit StatsSS(ISS
& env
, Stats
& stats
)
297 //////////////////////////////////////////////////////////////////////
299 template <class OpCode
>
300 bool in(StatsSS
& /*env*/, const OpCode
&) {
304 bool in(StatsSS
& env
, const bc::LIterInit
& op
) {
305 add_type(env
.stats
.iterInitBase
, locAsCell(env
, op
.loc2
));
309 //////////////////////////////////////////////////////////////////////
311 // Run the interpreter. The "bool in(StatsSS&, const bc::XX)" functions
312 // can call the default dispatch on their own or return false to let
313 // the main loop call the default dispatch. Returning true stops
314 // the call to the default interpreter which implies the handler for
315 // that opcode must perform all the right steps wrt the state.
316 void dispatch(StatsSS
& env
, const Bytecode
& op
) {
317 #define O(opcode, ...) \
319 if (!in(env, op.opcode)) default_dispatch(env, op); \
322 switch (op
.op
) { OPCODES
}
327 //////////////////////////////////////////////////////////////////////
329 // Simple stats about opcodes (that don't require full type
330 // information---those cases are only enabled when extendedStats is
332 void collect_simple(Stats
& stats
, const Bytecode
& bc
) {
333 ++stats
.op_counts
[static_cast<uint64_t>(bc
.op
)];
338 rat
= bc
.AssertRATL
.rat
;
340 case Op::AssertRATStk
:
341 rat
= bc
.AssertRATStk
.rat
;
347 using U
= std::underlying_type
<RepoAuthType::Tag
>::type
;
348 auto const tagInt
= static_cast<U
>(rat
.tag());
349 assertx(tagInt
< stats
.ratL_tags
.size());
350 if (bc
.op
== Op::AssertRATL
) {
351 ++stats
.ratL_tags
[tagInt
];
353 ++stats
.ratStk_tags
[tagInt
];
357 if (bc
.op
== Op::AssertRATL
) {
358 ++stats
.ratL_specialized_array
;
360 ++stats
.ratStk_specialized_array
;
365 void collect_func(Stats
& stats
, const Index
& index
, const php::Func
& func
) {
366 if (!func
.cls
) ++stats
.totalFunctions
;
368 auto const [ty
, effectFree
] = index
.lookup_return_type_raw(&func
).first
;
370 ++stats
.effectFreeFuncs
;
372 ++stats
.effectfulFuncs
;
375 add_type(stats
.returns
, ty
);
377 auto const cf
= php::WideFunc::cns(&func
);
378 for (auto const bid
: cf
.blockRange()) {
379 auto const blk
= cf
.blocks()[bid
].get();
380 if (blk
->dead
) continue;
381 for (auto& bc
: blk
->hhbcs
) {
382 collect_simple(stats
, bc
);
386 if (!options
.extendedStats
) return;
388 auto const ctx
= AnalysisContext
{ func
.unit
, cf
, func
.cls
};
389 IndexAdaptor adaptor
{ index
};
390 auto const fa
= analyze_func(adaptor
, ctx
, CollectionOpts
{});
392 Trace::Bump bumper
{Trace::hhbbc
, kStatsBump
};
393 for (auto const bid
: cf
.blockRange()) {
394 auto const blk
= cf
.blocks()[bid
].get();
395 auto state
= fa
.bdata
[bid
].stateIn
;
396 if (!state
.initialized
) continue;
398 CollectedInfo collect
{
399 adaptor
, ctx
, nullptr, CollectionOpts
{}, nullptr, &fa
401 Interp interp
{ adaptor
, ctx
, collect
, bid
, blk
, state
};
402 for (auto& bc
: blk
->hhbcs
) {
403 auto noop
= [] (BlockId
, const State
*) {};
404 ISS env
{ interp
, noop
};
405 StatsSS sss
{ env
, stats
};
407 if (state
.unreachable
) break;
413 void collect_class(Stats
& stats
, const Index
& index
, const php::Class
& cls
) {
414 ++stats
.totalClasses
;
415 stats
.totalMethods
+= cls
.methods
.size();
417 for (auto const& kv
: index
.lookup_private_props(&cls
)) {
418 add_type(stats
.privateProps
, kv
.second
.ty
);
420 for (auto const& kv
: index
.lookup_public_statics(&cls
)) {
421 add_type(stats
.publicStatics
, kv
.second
.ty
);
423 for (auto const& kv
: index
.lookup_private_statics(&cls
)) {
424 add_type(stats
.privateStatics
, kv
.second
.ty
);
427 for (auto& prop
: cls
.properties
) {
428 if (prop
.attrs
& AttrStatic
) {
430 if (prop
.attrs
& AttrPersistent
) {
431 if (prop
.attrs
& AttrPublic
) ++stats
.persistentSPropsPub
;
432 if (prop
.attrs
& AttrProtected
) ++stats
.persistentSPropsProt
;
433 if (prop
.attrs
& AttrPrivate
) ++stats
.persistentSPropsPriv
;
439 //////////////////////////////////////////////////////////////////////
443 //////////////////////////////////////////////////////////////////////
445 StatsHolder::StatsHolder() {
446 if (!Trace::moduleEnabledRelease(Trace::hhbbc_stats
, 1)) return;
450 StatsHolder::~StatsHolder() {
454 StatsHolder
allocate_stats() {
455 return StatsHolder();
458 void collect_stats(const StatsHolder
& stats
,
460 const php::Unit
& unit
) {
462 index
.for_each_unit_class(
464 [&] (const php::Class
& c
) {
465 collect_class(*stats
.stats
, index
, c
);
466 for (auto const& m
: c
.methods
) {
468 collect_func(*stats
.stats
, index
, *m
);
472 index
.for_each_unit_func(
474 [&] (const php::Func
& f
) { collect_func(*stats
.stats
, index
, f
); }
478 void print_stats(const StatsHolder
& stats
) {
481 auto const str
= show(*stats
.stats
);
482 Trace::ftraceRelease("{}", str
);
484 if (!Trace::moduleEnabledRelease(Trace::hhbbc_stats
, 2)) return;
486 auto stats_file
= options
.stats_file
;
488 auto const file
= [&] () -> std::FILE* {
489 if (!stats_file
.empty()) {
490 return fopen(stats_file
.c_str(), "w");
493 char fileBuf
[] = "/tmp/hhbbcXXXXXX";
494 auto const fd
= mkstemp(fileBuf
);
495 stats_file
= fileBuf
;
496 if (fd
== -1) return nullptr;
498 if (auto const fp
= fdopen(fd
, "w")) return fp
;
503 if (file
== nullptr) {
504 std::cerr
<< "couldn't open file for stats: "
505 << folly::errnoStr(errno
) << '\n';
509 SCOPE_EXIT
{ fclose(file
); };
510 std::cout
<< "stats saved to " << stats_file
<< '\n';
511 std::fwrite(str
.c_str(), sizeof(char), str
.size(), file
);
515 //////////////////////////////////////////////////////////////////////