Deshim VirtualExecutor in folly
[hiphop-php.git] / hphp / hhbbc / stats.cpp
blobcc21e694407ba57a58da0bad9433d23703b87c62
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
18 #include <atomic>
19 #include <array>
20 #include <string>
21 #include <cinttypes>
22 #include <cstdlib>
23 #include <iostream>
24 #include <memory>
25 #include <tuple>
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 //////////////////////////////////////////////////////////////////////
56 namespace {
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) \
82 struct TypeStat {
83 #define X(y, ...) \
84 std::atomic<uint64_t> sub_##y{}; \
85 std::atomic<uint64_t> eq_##y{};
86 HHBBC_TYPE_PREDEFINED(X)
87 #undef X
89 #define X(y, ...) std::atomic<uint64_t> spec_##y;
90 SPEC_TYPES(X)
91 #undef X
94 #define TAG(x,...) 1 +
95 constexpr uint32_t kNumRATTags = REPO_AUTH_TYPE_TAGS(TAG) 0 ;
96 #undef TAG
100 struct Stats {
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{};
115 TypeStat returns;
116 TypeStat privateProps;
117 TypeStat publicStatics;
118 TypeStat privateStatics;
119 TypeStat iterInitBase;
122 namespace {
124 void type_stat_string(std::string& ret,
125 const std::string& prefix,
126 const TypeStat& st) {
127 #define X(y, ...) \
128 if (st.eq_##y.load() > 0) { \
129 folly::format(&ret, " {: <32} {: >12}\n", \
130 folly::sformat("{}_=_{}", prefix, #y ":"), \
131 st.eq_##y.load()); \
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)
139 #undef X
141 ret += "\n";
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()); \
149 SPEC_TYPES(X)
150 #undef X
152 ret += "\n";
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;
160 folly::format(
161 &ret,
162 " {: >30}: {: >15}\n",
163 opcodeToName(static_cast<Op>(i)),
164 stats.op_counts[i].load()
167 ret += "\n";
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);
175 folly::format(
176 &ret,
177 " total_methods: {: >12}\n"
178 " total_funcs: {: >12}\n"
179 " total_classes: {: >12}\n"
180 "\n"
181 " effectful_funcs: {: >12}\n"
182 " effect_free_funcs: {: >12}\n"
183 "\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()
199 ret += "\n";
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", \
208 "RATL_" #x, \
209 stats.ratL_tags[static_cast<U>(T::x)].load(), \
210 "RATStk_" #x, \
211 stats.ratStk_tags[static_cast<U>(T::x)].load()); \
213 REPO_AUTH_TYPE_TAGS(TAG)
214 #undef TAG
216 if (stats.ratL_specialized_array.load() > 0 ||
217 stats.ratStk_specialized_array.load() > 0) {
218 folly::format(&ret,
219 " {: >28}: {: >12}\n"
220 " {: >28}: {: >12}\n",
221 "RATL_Arr_Special",
222 stats.ratL_specialized_array.load(),
223 "RATStk_Arr_Special",
224 stats.ratStk_specialized_array.load());
227 return ret;
230 //////////////////////////////////////////////////////////////////////
232 bool is_sub_obj(const Type& t) {
233 return
234 is_specialized_obj(t) &&
235 !is_specialized_wait_handle(t) &&
236 dobj_of(t).isSub();
238 bool is_exact_obj(const Type& t) {
239 return
240 is_specialized_obj(t) &&
241 !is_specialized_wait_handle(t) &&
242 dobj_of(t).isExact();
244 bool is_isect_obj(const Type& t) {
245 return
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) {
251 return
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) {
271 #define X(y, ...) \
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)
279 #undef X
281 #define X(a, b, c) if (c(t)) ++stat.spec_##a;
282 SPEC_TYPES(X)
283 #undef X
286 //////////////////////////////////////////////////////////////////////
288 struct StatsSS : ISS {
289 explicit StatsSS(ISS& env, Stats& stats)
290 : ISS(env)
291 , stats(stats)
294 Stats& stats;
297 //////////////////////////////////////////////////////////////////////
299 template <class OpCode>
300 bool in(StatsSS& /*env*/, const OpCode&) {
301 return false;
304 bool in(StatsSS& env, const bc::LIterInit& op) {
305 add_type(env.stats.iterInitBase, locAsCell(env, op.loc2));
306 return false;
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, ...) \
318 case Op::opcode: \
319 if (!in(env, op.opcode)) default_dispatch(env, op); \
320 return;
322 switch (op.op) { OPCODES }
323 #undef O
324 not_reached();
327 //////////////////////////////////////////////////////////////////////
329 // Simple stats about opcodes (that don't require full type
330 // information---those cases are only enabled when extendedStats is
331 // on).
332 void collect_simple(Stats& stats, const Bytecode& bc) {
333 ++stats.op_counts[static_cast<uint64_t>(bc.op)];
335 RepoAuthType rat;
336 switch (bc.op) {
337 case Op::AssertRATL:
338 rat = bc.AssertRATL.rat;
339 break;
340 case Op::AssertRATStk:
341 rat = bc.AssertRATStk.rat;
342 break;
343 default:
344 return;
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];
352 } else {
353 ++stats.ratStk_tags[tagInt];
356 if (rat.array()) {
357 if (bc.op == Op::AssertRATL) {
358 ++stats.ratL_specialized_array;
359 } else {
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;
369 if (effectFree) {
370 ++stats.effectFreeFuncs;
371 } else {
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 };
406 dispatch(sss, bc);
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) {
429 ++stats.totalSProps;
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;
447 stats = new Stats{};
450 StatsHolder::~StatsHolder() {
451 delete stats;
454 StatsHolder allocate_stats() {
455 return StatsHolder();
458 void collect_stats(const StatsHolder& stats,
459 const Index& index,
460 const php::Unit& unit) {
461 if (!stats) return;
462 index.for_each_unit_class(
463 unit,
464 [&] (const php::Class& c) {
465 collect_class(*stats.stats, index, c);
466 for (auto const& m : c.methods) {
467 if (!m) continue;
468 collect_func(*stats.stats, index, *m);
472 index.for_each_unit_func(
473 unit,
474 [&] (const php::Func& f) { collect_func(*stats.stats, index, f); }
478 void print_stats(const StatsHolder& stats) {
479 if (!stats) return;
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;
499 close(fd);
500 return nullptr;
501 }();
503 if (file == nullptr) {
504 std::cerr << "couldn't open file for stats: "
505 << folly::errnoStr(errno) << '\n';
506 return;
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);
512 std::fflush(file);
515 //////////////////////////////////////////////////////////////////////