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
{ namespace 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(cls_sub, "sub class", is_sub_cls) \
67 X(cls_exact, "exact class", is_exact_cls) \
68 X(arr_val, "array value", is_specialized_array_like_arrval) \
69 X(arr_packedn, "array packedn", is_specialized_array_like_packedn) \
70 X(arr_packed, "array packed", is_specialized_array_like_packed) \
71 X(arr_mapn, "array mapn", is_specialized_array_like_mapn) \
72 X(arr_map, "array map", is_specialized_array_like_map) \
73 X(str, "string", is_specialized_string) \
74 X(lazy_cls, "lazy class", is_specialized_lazycls) \
75 X(int, "int", is_specialized_int) \
76 X(dbl, "double", is_specialized_double) \
77 X(record, "record", is_specialized_record) \
81 std::atomic<uint64_t> sub_##y{}; \
82 std::atomic<uint64_t> eq_##y{};
83 HHBBC_TYPE_PREDEFINED(X
)
86 #define X(y, ...) std::atomic<uint64_t> spec_##y;
92 bool equal(SString s1
, SString s2
) const {
96 size_t hash(SString s
) const {
102 constexpr uint32_t kNumRATTags
= REPO_AUTH_TYPE_TAGS
0 ;
108 std::array
<std::atomic
<uint64_t>,Op_count
> op_counts
{};
109 std::array
<std::atomic
<uint64_t>,kNumRATTags
> ratL_tags
{};
110 std::array
<std::atomic
<uint64_t>,kNumRATTags
> ratStk_tags
{};
111 std::atomic
<uint64_t> ratL_specialized_array
{};
112 std::atomic
<uint64_t> ratStk_specialized_array
{};
113 std::atomic
<uint64_t> totalClasses
{};
114 std::atomic
<uint64_t> totalRecords
{};
115 std::atomic
<uint64_t> totalFunctions
{};
116 std::atomic
<uint64_t> totalMethods
{};
117 std::atomic
<uint64_t> effectfulFuncs
{};
118 std::atomic
<uint64_t> effectFreeFuncs
{};
119 std::atomic
<uint64_t> persistentSPropsPub
{};
120 std::atomic
<uint64_t> persistentSPropsProt
{};
121 std::atomic
<uint64_t> persistentSPropsPriv
{};
122 std::atomic
<uint64_t> totalSProps
{};
124 TypeStat privateProps
;
125 TypeStat publicStatics
;
126 TypeStat privateStatics
;
127 TypeStat iterInitBase
;
132 void type_stat_string(std::string
& ret
,
133 const std::string
& prefix
,
134 const TypeStat
& st
) {
136 if (st.eq_##y.load() > 0) { \
137 folly::format(&ret, " {: <32} {: >12}\n", \
138 folly::sformat("{}_=_{}", prefix, #y ":"), \
141 if (st.sub_##y.load() > 0) { \
142 folly::format(&ret, " {: <32} {: >12}\n", \
143 folly::sformat("{}_<_{}", prefix, #y ":"), \
144 st.sub_##y.load()); \
146 HHBBC_TYPE_PREDEFINED(X
)
151 #define X(y, z, ...) \
152 if (st.spec_##y.load() > 0) { \
153 folly::format(&ret, " {: <32} {: >12}\n", \
154 folly::sformat("{}_specialized {}", prefix, z ":"), \
155 st.spec_##y.load()); \
163 std::string
show(const Stats
& stats
) {
164 auto ret
= std::string
{};
166 for (auto i
= uint32_t{}; i
< stats
.op_counts
.size(); ++i
) {
167 if (stats
.op_counts
[i
].load() == 0) continue;
170 " {: >30}: {: >15}\n",
171 opcodeToName(static_cast<Op
>(i
)),
172 stats
.op_counts
[i
].load()
177 type_stat_string(ret
, "ret", stats
.returns
);
178 type_stat_string(ret
, "priv_prop", stats
.privateProps
);
179 type_stat_string(ret
, "pub_static", stats
.publicStatics
);
180 type_stat_string(ret
, "priv_static", stats
.privateStatics
);
181 type_stat_string(ret
, "iterInit_base", stats
.iterInitBase
);
185 " total_methods: {: >12}\n"
186 " total_funcs: {: >12}\n"
187 " total_classes: {: >12}\n"
188 " total_records: {: >12}\n"
190 " effectful_funcs: {: >12}\n"
191 " effect_free_funcs: {: >12}\n"
193 " total_sprops: {: >12}\n"
194 " persistent_sprops_pub: {: >12}\n"
195 " persistent_sprops_prot: {: >12}\n"
196 " persistent_sprops_priv: {: >12}\n",
197 stats
.totalMethods
.load(),
198 stats
.totalFunctions
.load(),
199 stats
.totalClasses
.load(),
200 stats
.totalRecords
.load(),
201 stats
.effectfulFuncs
.load(),
202 stats
.effectFreeFuncs
.load(),
203 stats
.totalSProps
.load(),
204 stats
.persistentSPropsPub
.load(),
205 stats
.persistentSPropsProt
.load(),
206 stats
.persistentSPropsPriv
.load()
210 using T
= RepoAuthType::Tag
;
211 using U
= std::underlying_type
<T
>::type
;
213 if (stats.ratL_tags[static_cast<U>(T::x)].load() > 0 || \
214 stats.ratStk_tags[static_cast<U>(T::x)].load() > 0) { \
215 folly::format(&ret, \
216 " {: >28}: {: >12}\n" \
217 " {: >28}: {: >12}\n", \
219 stats.ratL_tags[static_cast<U>(T::x)].load(), \
221 stats.ratStk_tags[static_cast<U>(T::x)].load()); \
226 if (stats
.ratL_specialized_array
.load() > 0 ||
227 stats
.ratStk_specialized_array
.load() > 0) {
229 " {: >28}: {: >12}\n"
230 " {: >28}: {: >12}\n",
232 stats
.ratL_specialized_array
.load(),
233 "RATStk_Arr_Special",
234 stats
.ratStk_specialized_array
.load());
240 //////////////////////////////////////////////////////////////////////
242 bool is_sub_obj(const Type
& t
) {
244 is_specialized_obj(t
) &&
245 !is_specialized_wait_handle(t
) &&
246 dobj_of(t
).type
== DObj::Sub
;
248 bool is_exact_obj(const Type
& t
) {
250 is_specialized_obj(t
) &&
251 !is_specialized_wait_handle(t
) &&
252 dobj_of(t
).type
== DObj::Exact
;
255 bool is_sub_cls(const Type
& t
) {
256 return is_specialized_cls(t
) && dcls_of(t
).type
== DCls::Sub
;
258 bool is_exact_cls(const Type
& t
) {
259 return is_specialized_cls(t
) && dcls_of(t
).type
== DCls::Exact
;
262 void add_type(TypeStat
& stat
, const Type
& t
) {
264 if (t.subtypeOf(B##y)) { \
265 if (B##y == BBottom || !t.is(BBottom)) { \
266 if (t.strictSubtypeOf(B##y)) ++stat.sub_##y; \
267 else ++stat.eq_##y; \
270 HHBBC_TYPE_PREDEFINED(X
)
273 #define X(a, b, c) if (c(t)) ++stat.spec_##a;
278 //////////////////////////////////////////////////////////////////////
280 struct StatsSS
: ISS
{
281 explicit StatsSS(ISS
& env
, Stats
& stats
)
289 //////////////////////////////////////////////////////////////////////
291 template <class OpCode
>
292 bool in(StatsSS
& /*env*/, const OpCode
&) {
296 bool in(StatsSS
& env
, const bc::IterInit
& /*op*/) {
297 add_type(env
.stats
.iterInitBase
, topC(env
));
301 //////////////////////////////////////////////////////////////////////
303 // Run the interpreter. The "bool in(StatsSS&, const bc::XX)" functions
304 // can call the default dispatch on their own or return false to let
305 // the main loop call the default dispatch. Returning true stops
306 // the call to the default interpreter which implies the handler for
307 // that opcode must perform all the right steps wrt the state.
308 void dispatch(StatsSS
& env
, const Bytecode
& op
) {
309 #define O(opcode, ...) \
311 if (!in(env, op.opcode)) default_dispatch(env, op); \
314 switch (op
.op
) { OPCODES
}
319 //////////////////////////////////////////////////////////////////////
321 // Simple stats about opcodes (that don't require full type
322 // information---those cases are only enabled when extendedStats is
324 void collect_simple(Stats
& stats
, const Bytecode
& bc
) {
325 ++stats
.op_counts
[static_cast<uint64_t>(bc
.op
)];
330 rat
= bc
.AssertRATL
.rat
;
332 case Op::AssertRATStk
:
333 rat
= bc
.AssertRATStk
.rat
;
339 using U
= std::underlying_type
<RepoAuthType::Tag
>::type
;
340 auto const tagInt
= static_cast<U
>(rat
.tag());
341 assertx(tagInt
< stats
.ratL_tags
.size());
342 if (bc
.op
== Op::AssertRATL
) {
343 ++stats
.ratL_tags
[tagInt
];
345 ++stats
.ratStk_tags
[tagInt
];
348 if (rat
.mayHaveArrData()) {
349 if (rat
.hasArrData()) {
350 if (bc
.op
== Op::AssertRATL
) {
351 ++stats
.ratL_specialized_array
;
353 ++stats
.ratStk_specialized_array
;
359 void collect_func(Stats
& stats
, const Index
& index
, const php::Func
& func
) {
360 if (!func
.cls
) ++stats
.totalFunctions
;
362 if (index
.is_effect_free(&func
)) {
363 ++stats
.effectFreeFuncs
;
365 ++stats
.effectfulFuncs
;
368 auto const ty
= index
.lookup_return_type_raw(&func
).first
;
369 add_type(stats
.returns
, ty
);
371 auto const cf
= php::WideFunc::cns(&func
);
372 for (auto const bid
: cf
.blockRange()) {
373 auto const blk
= cf
.blocks()[bid
].get();
374 if (blk
->dead
) continue;
375 for (auto& bc
: blk
->hhbcs
) {
376 collect_simple(stats
, bc
);
380 if (!options
.extendedStats
) return;
382 auto const ctx
= AnalysisContext
{ func
.unit
, cf
, func
.cls
};
383 auto const fa
= analyze_func(index
, ctx
, CollectionOpts
{});
385 Trace::Bump bumper
{Trace::hhbbc
, kStatsBump
};
386 for (auto const bid
: cf
.blockRange()) {
387 auto const blk
= cf
.blocks()[bid
].get();
388 auto state
= fa
.bdata
[bid
].stateIn
;
389 if (!state
.initialized
) continue;
391 CollectedInfo collect
{
392 index
, ctx
, nullptr, CollectionOpts
{}, &fa
394 Interp interp
{ index
, ctx
, collect
, bid
, blk
, state
};
395 for (auto& bc
: blk
->hhbcs
) {
396 auto noop
= [] (BlockId
, const State
*) {};
397 ISS env
{ interp
, noop
};
398 StatsSS sss
{ env
, stats
};
400 if (state
.unreachable
) break;
406 void collect_record(Stats
& stats
, const php::Record
& rec
) {
407 ++stats
.totalRecords
;
410 void collect_class(Stats
& stats
, const Index
& index
, const php::Class
& cls
) {
411 ++stats
.totalClasses
;
412 stats
.totalMethods
+= cls
.methods
.size();
414 for (auto const& kv
: index
.lookup_private_props(&cls
)) {
415 add_type(stats
.privateProps
, kv
.second
.ty
);
417 for (auto const& kv
: index
.lookup_public_statics(&cls
)) {
418 add_type(stats
.publicStatics
, kv
.second
.ty
);
420 for (auto const& kv
: index
.lookup_private_statics(&cls
)) {
421 add_type(stats
.privateStatics
, kv
.second
.ty
);
424 for (auto& prop
: cls
.properties
) {
425 if (prop
.attrs
& AttrStatic
) {
427 if (prop
.attrs
& AttrPersistent
) {
428 if (prop
.attrs
& AttrPublic
) ++stats
.persistentSPropsPub
;
429 if (prop
.attrs
& AttrProtected
) ++stats
.persistentSPropsProt
;
430 if (prop
.attrs
& AttrPrivate
) ++stats
.persistentSPropsPriv
;
436 void collect_stats(Stats
& stats
,
438 const php::Program
& program
) {
441 [&] (const std::unique_ptr
<php::Unit
>& unit
) {
442 for (auto const& c
: unit
->classes
) {
443 collect_class(stats
, index
, *c
);
444 for (auto const& m
: c
->methods
) {
445 collect_func(stats
, index
, *m
);
448 for (auto const& r
: unit
->records
) {
449 collect_record(stats
, *r
);
451 for (auto const& x
: unit
->funcs
) {
452 collect_func(stats
, index
, *x
);
458 //////////////////////////////////////////////////////////////////////
462 //////////////////////////////////////////////////////////////////////
464 StatsHolder::StatsHolder() {
465 if (!Trace::moduleEnabledRelease(Trace::hhbbc_time
, 1)) return;
469 StatsHolder::~StatsHolder() {
473 StatsHolder
allocate_stats() {
474 return StatsHolder();
477 void collect_stats(const StatsHolder
& stats
,
479 const php::Unit
* unit
) {
481 for (auto& c
: unit
->classes
) {
482 collect_class(*stats
.stats
, index
, *c
);
483 for (auto& m
: c
->methods
) {
484 collect_func(*stats
.stats
, index
, *m
);
487 for (auto& r
: unit
->records
) {
488 collect_record(*stats
.stats
, *r
);
490 for (auto& x
: unit
->funcs
) {
491 collect_func(*stats
.stats
, index
, *x
);
495 void print_stats(const StatsHolder
& stats
) {
498 auto const str
= show(*stats
.stats
);
499 if (Trace::moduleEnabledRelease(Trace::hhbbc_time
, 2)) {
503 auto stats_file
= options
.stats_file
;
505 auto const file
= [&] () -> std::FILE* {
506 if (!stats_file
.empty()) {
507 return fopen(stats_file
.c_str(), "w");
510 char fileBuf
[] = "/tmp/hhbbcXXXXXX";
511 auto const fd
= mkstemp(fileBuf
);
512 stats_file
= fileBuf
;
513 if (fd
== -1) return nullptr;
515 if (auto const fp
= fdopen(fd
, "w")) return fp
;
520 if (file
== nullptr) {
521 std::cerr
<< "couldn't open file for stats: "
522 << folly::errnoStr(errno
) << '\n';
526 SCOPE_EXIT
{ fclose(file
); };
527 std::cout
<< "stats saved to " << stats_file
<< '\n';
528 std::fwrite(str
.c_str(), sizeof(char), str
.size(), file
);
532 //////////////////////////////////////////////////////////////////////