option to preload units in parallel during jit deserialization
[hiphop-php.git] / hphp / runtime / vm / jit / prof-data-serialize.cpp
blobe8366f1b8940e877d46b5fb193c1a9f1467cb938
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/prof-data-serialize.h"
19 #include "hphp/runtime/base/builtin-functions.h"
20 #include "hphp/runtime/base/program-functions.h"
21 #include "hphp/runtime/base/string-data.h"
22 #include "hphp/runtime/base/timestamp.h"
23 #include "hphp/runtime/base/type-structure-helpers-defs.h"
24 #include "hphp/runtime/base/unit-cache.h"
25 #include "hphp/runtime/base/variable-serializer.h"
26 #include "hphp/runtime/base/vm-worker.h"
28 #include "hphp/runtime/ext/std/ext_std_closure.h"
30 #include "hphp/runtime/vm/class.h"
31 #include "hphp/runtime/vm/func.h"
32 #include "hphp/runtime/vm/instance-bits.h"
33 #include "hphp/runtime/vm/jit/array-access-profile.h"
34 #include "hphp/runtime/vm/jit/array-iter-profile.h"
35 #include "hphp/runtime/vm/jit/array-kind-profile.h"
36 #include "hphp/runtime/vm/jit/call-target-profile.h"
37 #include "hphp/runtime/vm/jit/cls-cns-profile.h"
38 #include "hphp/runtime/vm/jit/containers.h"
39 #include "hphp/runtime/vm/jit/decref-profile.h"
40 #include "hphp/runtime/vm/jit/incref-profile.h"
41 #include "hphp/runtime/vm/jit/inlining-decider.h"
42 #include "hphp/runtime/vm/jit/meth-profile.h"
43 #include "hphp/runtime/vm/jit/prof-data.h"
44 #include "hphp/runtime/vm/jit/release-vv-profile.h"
45 #include "hphp/runtime/vm/jit/switch-profile.h"
46 #include "hphp/runtime/vm/jit/trans-cfg.h"
47 #include "hphp/runtime/vm/jit/type-profile.h"
48 #include "hphp/runtime/vm/named-entity-defs.h"
49 #include "hphp/runtime/vm/named-entity.h"
50 #include "hphp/runtime/vm/property-profile.h"
51 #include "hphp/runtime/vm/repo-global-data.h"
52 #include "hphp/runtime/vm/repo.h"
53 #include "hphp/runtime/vm/treadmill.h"
54 #include "hphp/runtime/vm/type-profile.h"
55 #include "hphp/runtime/vm/unit.h"
57 #include "hphp/util/boot-stats.h"
58 #include "hphp/util/build-info.h"
59 #include "hphp/util/job-queue.h"
60 #include "hphp/util/logger.h"
61 #include "hphp/util/managed-arena.h"
62 #include "hphp/util/numa.h"
63 #include "hphp/util/process.h"
64 #include "hphp/util/service-data.h"
66 #include <folly/portability/Unistd.h>
67 #include <folly/String.h>
69 namespace HPHP { namespace jit {
70 //////////////////////////////////////////////////////////////////////
72 namespace {
73 //////////////////////////////////////////////////////////////////////
75 TRACE_SET_MOD(hhbc);
77 StaticString s_invoke("__invoke");
79 constexpr uint32_t kMagic = 0x4d564848;
81 constexpr uint32_t k86pinitSlot = 0x80000000u;
82 constexpr uint32_t k86sinitSlot = 0x80000001u;
83 constexpr uint32_t k86linitSlot = 0x80000002u;
85 template<typename F>
86 auto deserialize(ProfDataDeserializer&ser, F&& f) -> decltype(f()) {
87 using T = decltype(f());
88 auto const ptr = read_raw<uintptr_t>(ser);
89 if (!ptr) return T{};
90 if (ptr & 1) {
91 auto& ent = ser.getEnt(reinterpret_cast<T>(ptr - 1));
92 assertx(!ent);
93 ent = f();
94 ITRACE(3, "0x{:08x} => 0x{:08x}\n",
95 ptr - 1, reinterpret_cast<uintptr_t>(ent));
96 assertx(ent);
97 return ent;
99 auto const ent = ser.getEnt(reinterpret_cast<T>(ptr));
100 assertx(ent);
101 return ent;
104 void write_serialized_ptr(ProfDataSerializer& ser, const void* p) {
105 auto const ptr_as_int = reinterpret_cast<uintptr_t>(p);
106 assertx(!(ptr_as_int & 1));
107 write_raw(ser, ptr_as_int | 1);
110 void write_raw_ptr(ProfDataSerializer& ser, const void* p) {
111 auto const ptr_as_int = reinterpret_cast<uintptr_t>(p);
112 assertx(!(ptr_as_int & 1));
113 write_raw(ser, ptr_as_int);
117 * Helper functions so that the function passed to write_container can
118 * take a serializer as a parameter, or skip it (eg if its a lambda
119 * which is already capturing the serializer).
121 template<typename S, typename T, typename F>
122 auto call(F& f, S& ser, const T& t) -> decltype(f(ser, t)) {
123 return f(ser, t);
126 template<typename S, typename T, typename F>
127 auto call(F& f, S&, const T& t) -> decltype(f(t)) {
128 return f(t);
131 template<typename C, typename F>
132 void write_container(ProfDataSerializer& ser, const C& cont, F f) {
133 write_raw(ser, safe_cast<uint32_t>(cont.size()));
134 for (auto const &elm : cont) {
135 call(f, ser, elm);
139 template<typename F>
140 void read_container(ProfDataDeserializer& ser, F f) {
141 auto sz = read_raw<uint32_t>(ser);
142 while (sz--) { f(); }
145 void write_unit_preload(ProfDataSerializer& ser, Unit* unit) {
146 write_raw_string(ser, unit->filepath());
149 struct UnitPreloader : JobQueueWorker<StringData*, void*> {
150 virtual void onThreadEnter() override {
151 rl_typeProfileLocals->nonVMThread = true;
152 hphp_session_init(Treadmill::SessionKind::PreloadRepo);
154 virtual void onThreadExit() override {
155 hphp_context_exit();
156 hphp_session_exit();
158 virtual void doJob(StringData* path) override {
159 auto& nativeFuncs = Native::s_noNativeFuncs;
160 DEBUG_ONLY auto unit = lookupUnit(path, "", nullptr, nativeFuncs, false);
161 FTRACE(2, "Preloaded unit with path {}\n", path->data());
162 assertx(unit->filepath() == path); // both static
165 using UnitPreloadDispatcher = JobQueueDispatcher<UnitPreloader>;
166 UnitPreloadDispatcher* s_preload_dispatcher;
168 void read_unit_preload(ProfDataDeserializer& ser) {
169 auto const path =
170 read_raw_string(ser, /* skip = */ !RuntimeOption::EvalJitDesUnitPreload);
171 // path may be nullptr when JitDesUnitPreload isn't set.
172 if (RuntimeOption::EvalJitDesUnitPreload) {
173 assertx(path);
174 s_preload_dispatcher->enqueue(path);
178 void write_units_preload(ProfDataSerializer& ser) {
179 auto const maxFuncId = profData()->maxProfilingFuncId();
180 std::vector<Unit*> units;
181 hphp_fast_set<Unit*, pointer_hash<Unit>> seen;
182 auto const check_unit =
183 [] (Unit* unit) -> bool {
184 if (!unit) return false;
185 auto const filepath = unit->filepath();
186 if (filepath->empty()) return false;
187 // skip systemlib
188 if (filepath->size() >= 2 && filepath->data()[1] == ':') return false;
189 return true;
191 for (FuncId fid = 0; fid <= maxFuncId; fid++) {
192 if (!Func::isFuncIdValid(fid) || !profData()->profiling(fid)) continue;
193 auto const u = Func::fromFuncId(fid)->unit();
194 if (!check_unit(u)) continue;
195 if (seen.insert(u).second) units.push_back(u);
197 auto all_loaded = loadedUnitsRepoAuth();
198 for (auto u : all_loaded) {
199 if (!check_unit(u)) continue;
200 if (seen.insert(u).second) units.push_back(u);
202 write_container(ser, units, write_unit_preload);
205 void read_units_preload(ProfDataDeserializer& ser) {
206 BootStats::Block timer("DES_read_units_preload",
207 RuntimeOption::ServerExecutionMode());
208 if (RuntimeOption::EvalJitDesUnitPreload) {
209 auto const threads =
210 std::max(RuntimeOption::EvalJitWorkerThreadsForSerdes, 1);
211 s_preload_dispatcher = new UnitPreloadDispatcher(
212 threads, threads, 0, false, nullptr
214 s_preload_dispatcher->start();
216 read_container(ser, [&] { read_unit_preload(ser); });
219 void write_srckey(ProfDataSerializer& ser, SrcKey sk) {
220 ITRACE(2, "SrcKey>\n");
221 if (ser.serialize(sk.func())) {
222 Trace::Indent _i;
223 write_raw(ser, uintptr_t(-1));
224 write_func(ser, sk.func());
226 write_raw(ser, sk.toAtomicInt());
227 ITRACE(2, "SrcKey: {}\n", show(sk));
230 SrcKey read_srckey(ProfDataDeserializer& ser) {
231 ITRACE(2, "SrcKey>\n");
232 auto orig = read_raw<SrcKey::AtomicInt>(ser);
233 if (orig == uintptr_t(-1)) {
234 Trace::Indent _i;
235 read_func(ser);
236 orig = read_raw<SrcKey::AtomicInt>(ser);
238 auto const id = SrcKey::fromAtomicInt(orig).funcID();
239 assertx(uint32_t(orig) == id);
240 auto const sk = SrcKey::fromAtomicInt(orig - id + ser.getFid(id));
241 ITRACE(2, "SrcKey: {}\n", show(sk));
242 return sk;
245 void write_type(ProfDataSerializer& ser, Type t) {
246 t.serialize(ser);
249 Type read_type(ProfDataDeserializer& ser) {
250 return Type::deserialize(ser);
253 void write_typed_location(ProfDataSerializer& ser,
254 const RegionDesc::TypedLocation& loc) {
255 write_raw(ser, loc.location);
256 write_type(ser, loc.type);
259 void write_guarded_location(ProfDataSerializer& ser,
260 const RegionDesc::GuardedLocation& loc) {
261 write_raw(ser, loc.location);
262 write_type(ser, loc.type);
263 write_raw(ser, loc.category);
266 RegionDesc::TypedLocation read_typed_location(ProfDataDeserializer& ser) {
267 auto const location = read_raw<Location>(ser);
268 auto const type = read_type(ser);
269 return { location, type };
272 RegionDesc::GuardedLocation read_guarded_location(ProfDataDeserializer& ser) {
273 auto const location = read_raw<Location>(ser);
274 auto const type = read_type(ser);
275 auto const cat = read_raw<DataTypeCategory>(ser);
276 return { location, type, cat };
279 void write_global_array_map(ProfDataSerializer& ser) {
280 write_container(ser, globalArrayTypeTable(),
281 write_raw<const RepoAuthType::Array*>);
284 void read_global_array_map(ProfDataDeserializer& ser) {
285 BootStats::Block timer("DES_read_global_array_map",
286 RuntimeOption::ServerExecutionMode());
287 auto sz DEBUG_ONLY = read_raw<uint32_t>(ser);
288 assertx(sz == globalArrayTypeTable().size());
289 for (auto arr : globalArrayTypeTable()) {
290 auto const orig = read_raw<const RepoAuthType::Array*>(ser);
291 ser.recordRat(orig, arr);
295 void write_region_block(ProfDataSerializer& ser,
296 const RegionDesc::BlockPtr& block) {
297 write_raw(ser, block->id());
298 write_srckey(ser, block->start());
299 write_raw(ser, block->length());
300 write_raw(ser, block->initialSpOffset());
301 write_raw(ser, block->profTransID());
302 write_container(ser, block->typePredictions(), write_typed_location);
303 write_container(ser, block->typePreConditions(), write_guarded_location);
304 write_container(ser, block->postConds().changed, write_typed_location);
305 write_container(ser, block->postConds().refined, write_typed_location);
308 RegionDesc::BlockPtr read_region_block(ProfDataDeserializer& ser) {
309 auto const id = read_raw<RegionDesc::BlockId>(ser);
310 auto const start = read_srckey(ser);
311 auto const length = read_raw<int>(ser);
312 auto const initialSpOffset = read_raw<FPInvOffset>(ser);
314 auto const block = std::make_shared<RegionDesc::Block>(id,
315 start.func(),
316 start.resumeMode(),
317 start.offset(),
318 length,
319 initialSpOffset);
321 block->setProfTransID(read_raw<TransID>(ser));
323 read_container(ser,
324 [&] {
325 block->addPredicted(read_typed_location(ser));
328 read_container(ser,
329 [&] {
330 block->addPreCondition(read_guarded_location(ser));
333 PostConditions postConds;
334 read_container(ser,
335 [&] {
336 postConds.changed.push_back(read_typed_location(ser));
338 read_container(ser,
339 [&] {
340 postConds.refined.push_back(read_typed_location(ser));
342 block->setPostConds(postConds);
343 return block;
346 void write_region_desc(ProfDataSerializer& ser, const RegionDesc* rd) {
347 write_container(ser, rd->blocks(), write_region_block);
348 write_container(ser, findPredTrans(*rd, profData()),
349 [&] (RegionDesc::BlockId id) {
350 write_raw(ser, id);
352 write_container(ser, rd->blocks(),
353 [&] (const RegionDesc::BlockPtr& b) {
354 auto const bid = b->id();
355 write_raw(ser, bid);
356 assertx(rd->succs(bid).empty());
357 assertx(rd->preds(bid).empty());
358 assertx(rd->merged(bid).empty());
359 auto const pr = rd->prevRetrans(bid);
360 write_raw(ser, pr ? pr.value() : kInvalidTransID);
361 auto const nr = rd->nextRetrans(bid);
362 write_raw(ser, nr ? nr.value() : kInvalidTransID);
364 write_type(ser, rd->inlineCtxType());
365 write_container(ser, rd->inlineInputTypes(), write_type);
368 RegionDescPtr read_region_desc(ProfDataDeserializer& ser) {
369 auto ret = std::make_shared<RegionDesc>();
370 read_container(ser, [&] { ret->addBlock(read_region_block(ser)); });
371 RegionDesc::BlockIdSet incoming;
372 read_container(ser,
373 [&] { incoming.insert(read_raw<RegionDesc::BlockId>(ser)); });
374 ret->incoming(std::move(incoming));
376 read_container(ser,
377 [&] {
378 auto const id = read_raw<RegionDesc::BlockId>(ser);
379 auto const pr = read_raw<RegionDesc::BlockId>(ser);
380 auto const nr = read_raw<RegionDesc::BlockId>(ser);
381 if (pr != kInvalidTransID) {
382 ret->setNextRetrans(pr, id);
384 if (nr != kInvalidTransID) {
385 ret->setNextRetrans(id, nr);
388 auto const ty = read_type(ser);
389 std::vector<Type> args;
390 read_container(ser,
391 [&] {
392 args.push_back(read_type(ser));
394 ret->setInlineContext(ty, args);
395 return ret;
398 void write_prof_trans_rec(ProfDataSerializer& ser,
399 const ProfTransRec* ptr,
400 ProfData* pd) {
401 if (!ptr) return write_raw(ser, TransKind{});
402 write_raw(ser, ptr->kind());
403 write_srckey(ser, ptr->srcKey());
404 if (ptr->kind() == TransKind::Profile) {
405 write_raw(ser, ptr->lastBcOff());
406 write_region_desc(ser, ptr->region().get());
407 write_raw(ser, ptr->asmSize());
408 } else {
409 write_raw(ser, ptr->prologueArgs());
411 auto lock = ptr->lockCallerList();
412 std::vector<TransID> callers;
413 auto addCaller = [&] (TCA caller) {
414 if (!tc::isProfileCodeAddress(caller)) return;
415 auto const callerTransId = pd->jmpTransID(caller);
416 assertx(callerTransId != kInvalidTransID);
417 callers.push_back(callerTransId);
419 for (auto const caller : ptr->mainCallers()) addCaller(caller);
420 write_container(ser, callers, write_raw<TransID>);
421 write_raw(ser, ptr->asmSize());
425 std::unique_ptr<ProfTransRec> read_prof_trans_rec(ProfDataDeserializer& ser) {
426 auto const kind = read_raw<TransKind>(ser);
427 static_assert(TransKind::Profile != TransKind{} &&
428 TransKind::ProfPrologue != TransKind{},
429 "Profile and ProfPrologue must not be zero");
430 if (kind == TransKind{}) return nullptr;
431 auto const sk = read_srckey(ser);
432 if (kind == TransKind::Profile) {
433 auto const lastBcOff = read_raw<Offset>(ser);
434 auto const region = read_region_desc(ser);
435 auto const asmSize = read_raw<uint32_t>(ser);
436 return std::make_unique<ProfTransRec>(lastBcOff, sk, region, asmSize);
439 auto ret = std::make_unique<ProfTransRec>(sk, read_raw<int>(ser), 0);
441 read_container(ser,
442 [&] {
443 ret->profCallers().push_back(read_raw<TransID>(ser));
445 auto const asmSize = read_raw<uint32_t>(ser);
446 ret->setAsmSize(asmSize);
447 return ret;
450 bool write_type_alias(ProfDataSerializer&, const TypeAliasReq*);
452 bool write_type_alias_or_class(ProfDataSerializer& ser, const NamedEntity* ne) {
453 if (!ne) return false;
454 if (auto const cls = ne->clsList()) {
455 if (!(cls->attrs() & AttrUnique)) return false;
456 auto const filepath = cls->preClass()->unit()->filepath();
457 if (!filepath || filepath->empty()) return false;
458 if (!cls->wasSerialized()) write_class(ser, cls);
459 return true;
461 if (!ne->isPersistentTypeAlias()) return false;
462 return write_type_alias(ser, ne->getCachedTypeAlias());
465 bool write_type_alias(ProfDataSerializer& ser, const TypeAliasReq* td) {
466 SCOPE_EXIT {
467 ITRACE(2, "TypeAlias: {}\n", td->name);
469 ITRACE(2, "TypeAlias>\n");
470 // we're writing out Class* and TypeAliasReq* intermingled here. Set
471 // bit 1 for TypeAliasReq's so we can distinguish when reading back
472 // in. We also need to keep track of both whether we already tried
473 // serialize this one, and whether it was successful. Use td2 for
474 // that too.
475 auto const td2 = reinterpret_cast<const char*>(td) + 2;
476 if (!ser.serialize(td)) {
477 return ser.wasSerialized(td2);
479 Trace::Indent _;
481 auto const unit = td->unit;
482 auto const name = td->name;
483 auto const tas = unit->typeAliases();
484 for (auto const& ta : tas) {
485 if (ta.name == name) {
486 auto const kind = get_ts_kind(ta.typeStructure.get());
487 switch (kind) {
488 case TypeStructure::Kind::T_unresolved:
489 case TypeStructure::Kind::T_xhp:
491 auto const clsname = get_ts_classname(ta.typeStructure.get());
492 if (!write_type_alias_or_class(ser,
493 NamedEntity::get(clsname, false))) {
494 return false;
496 break;
498 case TypeStructure::Kind::T_null:
499 case TypeStructure::Kind::T_void:
500 case TypeStructure::Kind::T_int:
501 case TypeStructure::Kind::T_bool:
502 case TypeStructure::Kind::T_float:
503 case TypeStructure::Kind::T_string:
504 case TypeStructure::Kind::T_resource:
505 case TypeStructure::Kind::T_num:
506 case TypeStructure::Kind::T_arraykey:
507 case TypeStructure::Kind::T_nothing:
508 case TypeStructure::Kind::T_noreturn:
509 case TypeStructure::Kind::T_tuple:
510 case TypeStructure::Kind::T_fun:
511 case TypeStructure::Kind::T_array:
512 case TypeStructure::Kind::T_typevar:
513 case TypeStructure::Kind::T_shape:
514 case TypeStructure::Kind::T_darray:
515 case TypeStructure::Kind::T_varray:
516 case TypeStructure::Kind::T_varray_or_darray:
517 case TypeStructure::Kind::T_dict:
518 case TypeStructure::Kind::T_vec:
519 case TypeStructure::Kind::T_keyset:
520 case TypeStructure::Kind::T_vec_or_dict:
521 case TypeStructure::Kind::T_arraylike:
522 case TypeStructure::Kind::T_nonnull:
523 case TypeStructure::Kind::T_dynamic:
524 case TypeStructure::Kind::T_typeaccess:
525 case TypeStructure::Kind::T_mixed:
526 break;
528 case TypeStructure::Kind::T_reifiedtype:
529 case TypeStructure::Kind::T_class:
530 case TypeStructure::Kind::T_interface:
531 case TypeStructure::Kind::T_trait:
532 case TypeStructure::Kind::T_enum:
533 // these types don't occur in unresolved TypeStructures
534 always_assert(false);
537 if (!ser.serialize(td2)) always_assert(false);
538 write_serialized_ptr(ser, td2);
539 write_unit(ser, td->unit);
540 write_string(ser, td->name);
541 return true;
544 return false;
547 Class* read_class_internal(ProfDataDeserializer& ser) {
548 auto const id = read_raw<decltype(std::declval<PreClass*>()->id())>(ser);
549 auto const unit = read_unit(ser);
551 read_container(ser,
552 [&] {
553 auto const dep = read_class(ser);
554 auto const ne = dep->preClass()->namedEntity();
555 // if it's not persistent, make sure that dep
556 // is the active class for this NamedEntity
557 assertx(ne->m_cachedClass.bound());
558 if (ne->m_cachedClass.isNormal()) {
559 ne->setCachedClass(dep);
563 auto const preClass = unit->lookupPreClassId(id);
564 folly::Optional<TypeAliasReq> enumBaseReq;
565 SCOPE_EXIT {
566 if (enumBaseReq) {
567 preClass->enumBaseTy().namedEntity()->m_cachedTypeAlias.markUninit();
570 if (preClass->attrs() & AttrEnum &&
571 preClass->enumBaseTy().isObject()) {
572 auto const dt = read_raw<DataType>(ser);
573 auto const ne = preClass->enumBaseTy().namedEntity();
574 if (!ne->m_cachedTypeAlias.bound() ||
575 !ne->m_cachedTypeAlias.isInit()) {
576 enumBaseReq.emplace();
577 enumBaseReq->type = dt == KindOfInt64 ?
578 AnnotType::Int : AnnotType::String;
579 enumBaseReq->name = preClass->enumBaseTy().typeName();
580 ne->m_cachedTypeAlias.bind(rds::Mode::Normal);
581 ne->m_cachedTypeAlias.initWith(*enumBaseReq);
584 auto const ne = preClass->namedEntity();
585 // If it's not persistent, make sure its NamedEntity is
586 // unbound, ready for DefClass
587 if (ne->m_cachedClass.bound() &&
588 ne->m_cachedClass.isNormal()) {
589 ne->m_cachedClass.markUninit();
592 auto const cls = Unit::defClass(preClass, true);
593 if (cls->pinitVec().size()) cls->initPropHandle();
594 if (cls->numStaticProperties()) cls->initSPropHandles();
596 if (cls->parent() == c_Closure::classof()) {
597 auto const ctx = read_class(ser);
598 if (ctx != cls) return cls->rescope(ctx);
600 return cls;
604 * This reads in TypeAliases and Classes that are used for code gen,
605 * but otherwise aren't needed for profiling. We just need them to be
606 * loaded into the NamedEntity table, so this function just returns
607 * whether or not to continue (the end of the list is marked by a
608 * null pointer).
610 bool read_type_alias_or_class(ProfDataDeserializer& ser) {
611 auto const ptr = read_raw<uintptr_t>(ser);
612 if (!ptr) return false;
613 assertx(ptr & 1);
615 if (!(ptr & 2)) {
616 ITRACE(2, "Class>\n");
617 Trace::Indent _;
618 auto& ent = ser.getEnt(reinterpret_cast<Class*>(ptr - 1));
619 assertx(!ent);
620 ent = read_class_internal(ser);
621 assertx(ent);
622 ITRACE(2, "Class: {}\n", ent->name());
623 return true;
626 auto const unit = read_unit(ser);
627 auto const name = read_string(ser);
628 ITRACE(2, "TypeAlias: {}\n", name);
629 auto const tas = unit->typeAliases();
630 Id id = 0;
631 for (auto const& ta : tas) {
632 if (ta.name == name) {
633 unit->defTypeAlias(id);
634 return true;
636 ++id;
638 always_assert(false);
641 void write_profiled_funcs(ProfDataSerializer& ser, ProfData* pd) {
642 auto const maxFuncId = pd->maxProfilingFuncId();
644 for (FuncId fid = 0; fid <= maxFuncId; fid++) {
645 if (!Func::isFuncIdValid(fid) || !pd->profiling(fid)) continue;
646 write_func(ser, Func::fromFuncId(fid));
648 write_raw(ser, uintptr_t{});
651 void read_profiled_funcs(ProfDataDeserializer& ser, ProfData* pd) {
652 while (auto const func = read_func(ser)) {
653 pd->setProfiling(func->getFuncId());
657 void write_classes_and_type_aliases(ProfDataSerializer& ser, ProfData* pd) {
658 auto const maxFuncId = pd->maxProfilingFuncId();
660 // in an attempt to get a sensible order for these, start with the
661 // ones referenced by params and return constraints.
662 for (FuncId fid = 0; fid <= maxFuncId; fid++) {
663 if (!Func::isFuncIdValid(fid) || !pd->profiling(fid)) continue;
664 auto const func = Func::fromFuncId(fid);
665 for (auto const& p : func->params()) {
666 write_type_alias_or_class(ser, p.typeConstraint.namedEntity());
670 // Now just iterate and write anything that remains
671 NamedEntity::foreach_name(
672 [&] (NamedEntity& ne) {
673 write_type_alias_or_class(ser, &ne);
676 write_raw(ser, uintptr_t{});
679 void read_classes_and_type_aliases(ProfDataDeserializer& ser) {
680 BootStats::Block timer("DES_read_classes_and_type_aliases",
681 RuntimeOption::ServerExecutionMode());
682 while (read_type_alias_or_class(ser)) {
683 // nothing to do. this was just to make sure everything is loaded
684 // into the NamedEntity table
688 void write_prof_data(ProfDataSerializer& ser, ProfData* pd) {
689 // Write the profiled metadata to output
690 std::string metaFile = ser.filename() + ".meta";
691 if (auto out = fopen(metaFile.c_str(), "w")) {
692 fprintf(out, "profFuncCnt=%ld\n", pd->profilingFuncs());
693 fprintf(out, "profBCSize=%ld\n", pd->profilingBCSize());
694 fclose(out);
697 write_profiled_funcs(ser, pd);
699 write_raw(ser, pd->counterDefault());
700 pd->forEachTransRec(
701 [&] (const ProfTransRec* ptr) {
702 auto const transID = ptr->isProfile() ?
703 ptr->region()->entry()->profTransID() :
704 pd->proflogueTransId(ptr->func(), ptr->prologueArgs());
705 write_raw(ser, transID);
706 write_prof_trans_rec(ser, ptr, pd);
707 // forEachTransRec already grabs a read lock, and we're not
708 // going to add a *new* counter here (so we don't need a write
709 // lock).
710 write_raw(ser, *pd->transCounterAddrNoLock(transID));
713 write_raw(ser, kInvalidTransID);
714 write_raw<uint64_t>(ser, pd->baseProfCount());
715 write_container(ser, pd->sortedFuncs(), write_raw<FuncId>);
718 void read_prof_data(ProfDataDeserializer& ser, ProfData* pd) {
719 BootStats::Block timer("DES_read_prof_data",
720 RuntimeOption::ServerExecutionMode());
721 read_profiled_funcs(ser, pd);
723 pd->resetCounters(read_raw<int64_t>(ser));
724 while (true) {
725 auto const transID = read_raw<TransID>(ser);
726 if (transID == kInvalidTransID) break;
727 pd->addProfTrans(transID, read_prof_trans_rec(ser));
728 *pd->transCounterAddr(transID) = read_raw<int64_t>(ser);
730 pd->setBaseProfCount(read_raw<uint64_t>(ser));
731 auto const sz = read_raw<uint32_t>(ser);
732 std::vector<FuncId> order;
733 order.reserve(sz);
734 for (auto i = sz; i > 0; --i) {
735 auto const origId = read_raw<FuncId>(ser);
736 order.push_back(ser.getFid(origId));
738 pd->setFuncOrder(std::move(order));
741 template<typename T>
742 auto write_impl(ProfDataSerializer& ser, const T& out, bool) ->
743 decltype(std::declval<T&>().serialize(ser),void()) {
744 out.serialize(ser);
747 template<typename T>
748 void write_impl(ProfDataSerializer& ser, const T& out, int) {
749 write_raw(ser, out);
752 template<typename T>
753 void write_maybe_serializable(ProfDataSerializer& ser, const T& out) {
754 write_impl(ser, out, false);
757 struct TargetProfileVisitor : boost::static_visitor<void> {
758 TargetProfileVisitor(ProfDataSerializer& ser,
759 const rds::Symbol& sym,
760 rds::Handle handle,
761 uint32_t size) :
762 ser{ser},
763 sym{sym},
764 handle{handle},
765 size{size} {}
767 template<typename T>
768 void process(T& out, const StringData* name) {
769 write_raw(ser, size);
770 write_string(ser, name);
771 write_raw(ser, sym);
772 TargetProfile<T>::reduce(out, handle, size);
773 if (size == sizeof(T)) {
774 write_maybe_serializable(ser, out);
775 } else {
776 write_raw(ser, &out, size);
780 template<typename T> void operator()(const T&) {}
781 template<typename T>
782 void operator()(const rds::Profile<T>& pt) {
783 if (size == sizeof(T)) {
784 T out{};
785 process(out, pt.name.get());
786 } else {
787 auto const mem = calloc(1, size);
788 SCOPE_EXIT { free(mem); };
789 process(*reinterpret_cast<T*>(mem), pt.name.get());
793 ProfDataSerializer& ser;
794 const rds::Symbol& sym;
795 rds::Handle handle;
796 uint32_t size;
799 void write_target_profiles(ProfDataSerializer& ser) {
800 rds::visitSymbols(
801 [&] (const rds::Symbol& symbol, rds::Handle handle, uint32_t size) {
802 TargetProfileVisitor tv(ser, symbol, handle, size);
803 boost::apply_visitor(tv, symbol);
806 write_raw(ser, uint32_t{});
809 template<typename T>
810 auto read_impl(ProfDataDeserializer& ser, T& out, bool) ->
811 decltype(out.deserialize(ser),void()) {
812 out.deserialize(ser);
815 template<typename T>
816 void read_impl(ProfDataDeserializer& ser, T& out, int) {
817 read_raw(ser, out);
820 template<typename T>
821 void read_maybe_serializable(ProfDataDeserializer& ser, T& out) {
822 read_impl(ser, out, false);
825 struct SymbolFixup : boost::static_visitor<void> {
826 SymbolFixup(ProfDataDeserializer& ser, StringData* name, uint32_t size) :
827 ser{ser}, name{name}, size{size} {}
829 template<typename T> void operator()(T&) { always_assert(false); }
830 template<typename T>
831 void operator()(rds::Profile<T>& pt) {
832 TargetProfile<T> prof(pt.transId,
833 TransKind::Profile,
834 pt.bcOff,
835 name,
836 size - sizeof(T));
838 if (size == sizeof(T)) {
839 read_maybe_serializable(ser, prof.value());
840 } else {
841 read_raw(ser, &prof.value(), size);
845 ProfDataDeserializer& ser;
846 StringData* name;
847 // The size of the original rds allocation.
848 uint32_t size;
851 void read_target_profiles(ProfDataDeserializer& ser) {
852 BootStats::Block timer("DES_read_target_profiles",
853 RuntimeOption::ServerExecutionMode());
854 while (true) {
855 auto const size = read_raw<uint32_t>(ser);
856 if (!size) break;
857 auto const name = read_string(ser);
858 auto sym = read_raw<rds::Symbol>(ser);
859 auto sf = SymbolFixup{ser, name, size};
860 boost::apply_visitor(sf, sym);
864 void merge_loaded_units(int numWorkers) {
865 BootStats::Block timer("DES_merge_loaded_units",
866 RuntimeOption::ServerExecutionMode());
867 auto units = loadedUnitsRepoAuth();
869 std::vector<VMWorker> workers;
870 // Compute a batch size that causes each thread to process approximately 16
871 // batches. Even if the batches are somewhat imbalanced in what they contain,
872 // the straggler workers are very unlikey to take more than 10% longer than
873 // the first worker to finish.
874 auto const batchSize{std::max(units.size() / numWorkers / 16, size_t(1))};
875 std::atomic<size_t> index{0};
876 std::atomic<uint32_t> curr_node{0};
877 for (auto worker = 0; worker < numWorkers; ++worker) {
878 WorkerSpec spec;
879 spec.numaNode = next_numa_node(curr_node);
880 workers.emplace_back(
881 VMWorker(
882 spec,
883 [&] {
884 ProfileNonVMThread nonVM;
885 #if USE_JEMALLOC_EXTENT_HOOKS
886 if (auto arena = next_extra_arena(spec.numaNode)) {
887 arena->bindCurrentThread();
889 #endif
890 hphp_session_init(Treadmill::SessionKind::PreloadRepo);
892 while (true) {
893 auto begin = index.fetch_add(batchSize);
894 auto end = std::min(begin + batchSize, units.size());
895 if (begin >= end) break;
896 auto unitCount = end - begin;
897 for (auto i = size_t{0}; i < unitCount; ++i) {
898 auto const unit = units[begin + i];
899 try {
900 unit->merge();
901 } catch (...) {
902 // swallow errors silently. persistent things should raise
903 // errors, and we don't really care about merging
904 // non-persistent things.
909 hphp_context_exit();
910 hphp_session_exit();
915 for (auto& worker : workers) {
916 worker.start();
918 for (auto& worker : workers) {
919 worker.waitForEnd();
923 ////////////////////////////////////////////////////////////////////////////////
926 ProfDataSerializer::ProfDataSerializer(const std::string& name, FileMode mode)
927 : fileName(name)
928 , fileMode(mode) {
930 std::string partialFile = name + ".part";
932 if (fileMode == FileMode::Append) {
933 fd = open(partialFile.c_str(),
934 O_CLOEXEC | O_APPEND | O_WRONLY, 0644);
935 } else {
936 // Delete old profile data to avoid confusion. This should've happened from
937 // outside the process, but in case it didn't happen, try to do it here.
938 unlink(name.c_str());
940 fd = open(partialFile.c_str(),
941 O_CLOEXEC | O_CREAT | O_TRUNC | O_WRONLY, 0644);
944 if (fd == -1) {
945 auto const msg =
946 folly::sformat("Failed to open file for write {}, {}", name,
947 folly::errnoStr(errno));
948 Logger::Error(msg);
949 throw std::runtime_error(msg);
953 void ProfDataSerializer::finalize() {
954 assertx(fd != -1);
955 if (offset) ::write(fd, buffer, offset);
956 offset = 0;
957 close(fd);
958 fd = -1;
959 std::string partialFile = fileName + ".part";
960 if (fileMode == FileMode::Create && serializeOptProfEnabled()) {
961 // Don't rename the file to it's final name yet as we're still going to
962 // append the profile data collected for the optimized code to it.
963 FTRACE(1, "Finished serializing base profile data to {}\n", partialFile);
964 } else {
965 if (rename(partialFile.c_str(), fileName.c_str()) == -1) {
966 auto const msg =
967 folly::sformat("Failed to rename {} to {}, {}",
968 partialFile, fileName, folly::errnoStr(errno));
969 Logger::Error(msg);
970 throw std::runtime_error(msg);
971 } else {
972 FTRACE(1, "Finished serializing all profile data to {}\n", fileName);
977 ProfDataSerializer::~ProfDataSerializer() {
978 if (fd != -1) {
979 // We didn't finalize(), maybe because an exception was thrown while writing
980 // the data. The file is likely corrupt or incomplete, so discard it.
981 ftruncate(fd, 0);
982 close(fd);
983 std::string partialFile = fileName + ".part";
984 unlink(partialFile.c_str());
988 ProfDataDeserializer::ProfDataDeserializer(const std::string& name) {
989 fd = open(name.c_str(), O_CLOEXEC | O_RDONLY);
990 if (fd == -1) throw std::runtime_error("Failed to open: " + name);
993 ProfDataDeserializer::~ProfDataDeserializer() {
994 assertx(fd != -1);
995 close(fd);
998 bool ProfDataDeserializer::done() {
999 auto const pos = lseek(fd, 0, SEEK_CUR);
1000 auto const end = lseek(fd, 0, SEEK_END);
1001 lseek(fd, pos, SEEK_SET); // go back to original position
1002 return offset == buffer_size && pos == end;
1005 void write_raw(ProfDataSerializer& ser, const void* data, size_t sz) {
1006 if (ser.offset + sz <= ProfDataSerializer::buffer_size) {
1007 memcpy(ser.buffer + ser.offset, data, sz);
1008 ser.offset += sz;
1009 return;
1011 if (ser.offset == 0) {
1012 if (::write(ser.fd, data, sz) != sz) {
1013 throw std::runtime_error("Failed to write serialized data");
1015 return;
1017 if (auto const delta = ProfDataSerializer::buffer_size - ser.offset) {
1018 memcpy(ser.buffer + ser.offset, data, delta);
1019 data = static_cast<const char*>(data) + delta;
1020 sz -= delta;
1021 ser.offset = ProfDataSerializer::buffer_size;
1023 assertx(ser.offset == ProfDataSerializer::buffer_size);
1024 if (::write(ser.fd, ser.buffer, ser.offset) != ser.offset) {
1025 throw std::runtime_error("Failed to write serialized data");
1027 ser.offset = 0;
1028 write_raw(ser, data, sz);
1031 void read_raw(ProfDataDeserializer& ser, void* data, size_t sz) {
1032 if (ser.offset + sz <= ProfDataDeserializer::buffer_size) {
1033 memcpy(data, ser.buffer + ser.offset, sz);
1034 ser.offset += sz;
1035 return;
1037 if (auto const delta = ProfDataDeserializer::buffer_size - ser.offset) {
1038 memcpy(data, ser.buffer + ser.offset, delta);
1039 data = static_cast<char*>(data) + delta;
1040 sz -= delta;
1041 ser.offset = ProfDataDeserializer::buffer_size;
1043 if (sz >= ProfDataDeserializer::buffer_size) {
1044 auto const bytes_read = ::read(ser.fd, data, sz);
1045 if (bytes_read < 0 || bytes_read < sz) {
1046 throw std::runtime_error("Failed to read serialized data");
1048 return;
1051 auto const bytes_read = ::read(ser.fd,
1052 ser.buffer,
1053 ProfDataDeserializer::buffer_size);
1054 if (bytes_read < 0 || bytes_read < sz) {
1055 throw std::runtime_error("Failed to read serialized data");
1057 ser.offset = ProfDataDeserializer::buffer_size - bytes_read;
1058 if (ser.offset) {
1059 memmove(ser.buffer + ser.offset, ser.buffer, bytes_read);
1061 return read_raw(ser, data, sz);
1064 StringData*& ProfDataDeserializer::getEnt(const StringData* p) {
1065 return stringMap[uintptr_t(p)];
1068 ArrayData*& ProfDataDeserializer::getEnt(const ArrayData* p) {
1069 return arrayMap[uintptr_t(p)];
1072 Unit*& ProfDataDeserializer::getEnt(const Unit* p) {
1073 return unitMap[uintptr_t(p)];
1076 Func*& ProfDataDeserializer::getEnt(const Func* p) {
1077 return funcMap[uintptr_t(p)];
1080 Class*& ProfDataDeserializer::getEnt(const Class* p) {
1081 return classMap[uintptr_t(p)];
1084 const RepoAuthType::Array*&
1085 ProfDataDeserializer::getEnt(const RepoAuthType::Array* p) {
1086 return ratMap[uintptr_t(p)];
1089 bool ProfDataSerializer::serialize(const Unit* unit) {
1090 return unit->serialize();
1093 bool ProfDataSerializer::serialize(const Func* func) {
1094 return func->serialize();
1097 bool ProfDataSerializer::serialize(const Class* cls) {
1098 return cls->serialize();
1101 void write_raw_string(ProfDataSerializer& ser, const StringData* str) {
1102 uint32_t sz = str->size();
1103 write_raw(ser, sz);
1104 write_raw(ser, str->data(), sz);
1107 StringData* read_raw_string(ProfDataDeserializer& ser,
1108 bool skip /* = false */) {
1109 auto const sz = read_raw<uint32_t>(ser);
1110 constexpr uint32_t kMaxStringLen = 2 << 20;
1111 if (sz > kMaxStringLen) {
1112 throw std::runtime_error("string too long, likely corrupt");
1114 constexpr uint32_t kBufLen = 8192;
1115 char buffer[kBufLen];
1116 char* ptr = buffer;
1117 if (sz > kBufLen) ptr = (char*)malloc(sz);
1118 SCOPE_EXIT { if (ptr != buffer) free(ptr); };
1119 read_raw(ser, ptr, sz);
1120 if (!skip) return makeStaticString(ptr, sz);
1121 return nullptr;
1124 void write_string(ProfDataSerializer& ser, const StringData* str) {
1125 if (!ser.serialize(str)) return write_raw(ser, str);
1126 write_serialized_ptr(ser, str);
1127 write_raw_string(ser, str);
1130 StringData* read_string(ProfDataDeserializer& ser) {
1131 return deserialize(
1132 ser,
1133 [&] { return read_raw_string(ser); }
1137 void write_array(ProfDataSerializer& ser, const ArrayData* arr) {
1138 if (!ser.serialize(arr)) return write_raw(ser, arr);
1139 write_serialized_ptr(ser, arr);
1140 auto const str = internal_serialize(VarNR(const_cast<ArrayData*>(arr)));
1141 uint32_t sz = str.size();
1142 write_raw(ser, sz);
1143 write_raw(ser, str.data(), sz);
1146 ArrayData* read_array(ProfDataDeserializer& ser) {
1147 return deserialize(
1148 ser,
1149 [&] () -> ArrayData* {
1150 auto const sz = read_raw<uint32_t>(ser);
1151 String s{sz, ReserveStringMode{}};
1152 auto const range = s.bufferSlice();
1153 read_raw(ser, range.begin(), sz);
1154 s.setSize(sz);
1155 auto v = unserialize_from_buffer(
1156 s.data(),
1157 s.size(),
1158 VariableUnserializer::Type::Internal
1160 return ArrayData::GetScalarArray(std::move(v));
1165 void write_unit(ProfDataSerializer& ser, const Unit* unit) {
1166 if (!ser.serialize(unit)) return write_raw(ser, unit);
1167 ITRACE(2, "Unit: {}\n", unit->filepath());
1168 write_serialized_ptr(ser, unit);
1169 write_string(ser, unit->filepath());
1172 Unit* read_unit(ProfDataDeserializer& ser) {
1173 return deserialize(
1174 ser,
1175 [&] () -> Unit* {
1176 auto const filepath = read_string(ser);
1177 ITRACE(2, "Unit: {}\n", filepath);
1178 auto& nativeFuncs = Native::s_noNativeFuncs;
1179 if (filepath->data()[0] == '/' && filepath->data()[1] == ':') {
1180 return lookupSyslibUnit(filepath, nativeFuncs);
1182 return lookupUnit(filepath, "", nullptr, nativeFuncs, false);
1187 void write_class(ProfDataSerializer& ser, const Class* cls) {
1188 SCOPE_EXIT {
1189 ITRACE(2, "Class: {}\n", cls ? cls->name() : staticEmptyString());
1191 ITRACE(2, "Class>\n");
1192 Trace::Indent _;
1194 if (!cls || !ser.serialize(cls)) return write_raw(ser, cls);
1196 write_serialized_ptr(ser, cls);
1197 write_raw(ser, cls->preClass()->id());
1198 write_unit(ser, cls->preClass()->unit());
1200 jit::vector<const Class*> dependents;
1201 auto record_dep = [&] (const Class* dep) {
1202 if (!dep) return;
1203 if (!dep->wasSerialized() || !classHasPersistentRDS(dep)) {
1204 dependents.emplace_back(dep);
1207 record_dep(cls->parent());
1208 for (auto const& iface : cls->declInterfaces()) {
1209 record_dep(iface.get());
1212 if (cls->preClass()->attrs() & AttrNoExpandTrait) {
1213 for (auto const tName : cls->preClass()->usedTraits()) {
1214 auto const trait = Unit::lookupUniqueClassInContext(tName, nullptr);
1215 assertx(trait);
1216 record_dep(trait);
1218 } else {
1219 for (auto const& trait : cls->usedTraitClasses()) {
1220 record_dep(trait.get());
1224 write_container(ser, dependents, write_class);
1226 if (cls->attrs() & AttrEnum &&
1227 cls->preClass()->enumBaseTy().isObject()) {
1228 write_raw(ser, cls->enumBaseTy().value_or(KindOfUninit));
1231 if (cls->parent() == c_Closure::classof()) {
1232 auto const func = cls->lookupMethod(s_invoke.get());
1233 assertx(func);
1234 write_class(ser, func->cls());
1238 Class* read_class(ProfDataDeserializer& ser) {
1239 ITRACE(2, "Class>\n");
1240 auto const ret = deserialize(
1241 ser,
1242 [&] () -> Class* {
1243 Trace::Indent _;
1244 return read_class_internal(ser);
1248 ITRACE(2, "Class: {}\n", ret ? ret->name() : staticEmptyString());
1249 return ret;
1252 void write_func(ProfDataSerializer& ser, const Func* func) {
1253 SCOPE_EXIT {
1254 ITRACE(2, "Func: {}\n", func ? func->fullName() : staticEmptyString());
1256 ITRACE(2, "Func>\n");
1257 Trace::Indent _;
1259 if (!func || !ser.serialize(func)) return write_raw(ser, func);
1261 write_serialized_ptr(ser, func);
1262 uint32_t fid = func->getFuncId();
1263 assertx(!(fid & 0x80000000));
1264 if (func == SystemLib::s_nullCtor ||
1265 (!func->isMethod() && !func->isPseudoMain() && func->isBuiltin() &&
1266 !func->isMethCaller())) {
1267 if (func == SystemLib::s_nullCtor) {
1268 assertx(func->name()->isame(s_86ctor.get()));
1270 fid = ~fid;
1271 write_raw(ser, fid);
1272 return write_string(ser, func->name());
1274 write_raw(ser, fid);
1275 if (func->isPseudoMain()) {
1276 const uint32_t zero = 0;
1277 auto const isStatic = func->isStatic();
1278 write_raw(ser, zero);
1279 write_unit(ser, func->unit());
1280 write_raw(ser, isStatic);
1281 return write_class(ser, func->cls());
1284 if (func->isMethod()) {
1285 auto const* cls = func->implCls();
1286 auto const nslot = [&] () -> uint32_t {
1287 const uint32_t slot = func->methodSlot();
1288 if (cls->getMethod(slot) != func) {
1289 if (func->name() == s_86pinit.get()) return k86pinitSlot;
1290 if (func->name() == s_86sinit.get()) return k86sinitSlot;
1291 if (func->name() == s_86linit.get()) return k86linitSlot;
1292 cls = func->cls();
1293 assertx(cls->getMethod(slot) == func);
1295 return ~slot;
1296 }();
1297 assertx(nslot & 0x80000000);
1298 write_raw(ser, nslot);
1299 return write_class(ser, cls);
1302 // Ideally we'd write the func's index in its Unit; but we may not
1303 // have that after Unit::initial_merge
1304 const uint32_t off = func->base();
1305 assertx(off && !(off & 0x80000000));
1306 write_raw(ser, off);
1307 write_unit(ser, func->unit());
1310 Func* read_func(ProfDataDeserializer& ser) {
1311 ITRACE(2, "Func>\n");
1312 auto const ret = deserialize(
1313 ser,
1314 [&] () -> Func* {
1315 Trace::Indent _;
1316 auto fid = read_raw<uint32_t>(ser);
1317 auto const func = [&] () -> const Func* {
1318 if (fid & 0x80000000) {
1319 fid = ~fid;
1320 auto const name = read_string(ser);
1321 if (name->isame(s_86ctor.get())) return SystemLib::s_nullCtor;
1322 return Unit::lookupFunc(name);
1324 auto const id = read_raw<uint32_t>(ser);
1325 if (!id) {
1326 bool isStatic = false;
1327 auto const unit = read_unit(ser);
1328 read_raw(ser, isStatic);
1329 return unit->getMain(read_class(ser), !isStatic);
1331 if (id & 0x80000000) {
1332 auto const cls = read_class(ser);
1333 if (id == k86pinitSlot) return cls->get86pinit();
1334 if (id == k86sinitSlot) return cls->get86sinit();
1335 if (id == k86linitSlot) return cls->get86linit();
1336 const Slot slot = ~id;
1337 return cls->getMethod(slot);
1339 auto const unit = read_unit(ser);
1340 for (auto const f : unit->funcs()) {
1341 if (f->base() == id) {
1342 Unit::bindFunc(f);
1343 auto const handle = f->funcHandle();
1344 if (!rds::isPersistentHandle(handle) &&
1345 (!rds::isHandleInit(handle, rds::NormalTag{}) ||
1346 rds::handleToRef<LowPtr<Func>,
1347 rds::Mode::Normal>(handle).get() != f)) {
1348 rds::uninitHandle(handle);
1349 Unit::defFunc(f, false);
1351 return f;
1354 not_reached();
1355 }();
1356 ser.recordFid(fid, func->getFuncId());
1357 return const_cast<Func*>(func);
1360 ITRACE(2, "Func: {}\n", ret ? ret->fullName() : staticEmptyString());
1361 return ret;
1364 void write_clsmeth(ProfDataSerializer& ser, ClsMethDataRef clsMeth) {
1365 SCOPE_EXIT {
1366 ITRACE(2, "ClsMeth: {}, {}\n",
1367 clsMeth->getCls() ? clsMeth->getCls()->name() : staticEmptyString(),
1368 clsMeth->getFunc() ? clsMeth->getFunc()->fullName() : staticEmptyString()
1371 ITRACE(2, "ClsMeth>\n");
1372 if (ser.serialize(clsMeth->getCls())) {
1373 Trace::Indent _i;
1374 write_raw(ser, uintptr_t(-1));
1375 write_class(ser, clsMeth->getCls());
1377 if (ser.serialize(clsMeth->getFunc())) {
1378 Trace::Indent _i;
1379 write_raw(ser, uintptr_t(-1));
1380 write_func(ser, clsMeth->getFunc());
1384 ClsMethDataRef read_clsmeth(ProfDataDeserializer& ser) {
1385 ITRACE(2, "ClsMeth>\n");
1386 auto const cls = read_class(ser);
1387 auto const func = read_func(ser);
1388 ITRACE(2, "ClsMeth: {}, {}\n",
1389 cls ? cls->name() : staticEmptyString(),
1390 func ? func->fullName() : staticEmptyString());
1391 return ClsMethDataRef::create(cls, func);
1394 std::string serializeProfData(const std::string& filename) {
1395 try {
1396 ProfDataSerializer ser{filename, ProfDataSerializer::FileMode::Create};
1398 write_raw(ser, kMagic);
1399 write_raw(ser, Repo::get().global().Signature);
1400 auto schema = repoSchemaId();
1401 write_raw(ser, schema.size());
1402 write_raw(ser, schema.begin(), schema.size());
1404 auto host = Process::GetHostName();
1405 write_raw(ser, host.size());
1406 write_raw(ser, &host[0], host.size());
1407 auto& tag = RuntimeOption::ProfDataTag;
1408 write_raw(ser, tag.size());
1409 write_raw(ser, &tag[0], tag.size());
1410 write_raw(ser, TimeStamp::Current());
1412 Func::s_treadmill = true;
1413 hphp_session_init(Treadmill::SessionKind::ProfData);
1414 requestInitProfData();
1416 SCOPE_EXIT {
1417 requestExitProfData();
1418 hphp_context_exit();
1419 hphp_session_exit();
1420 Func::s_treadmill = false;
1423 write_units_preload(ser);
1424 PropertyProfile::serialize(ser);
1425 InstanceBits::init();
1426 InstanceBits::serialize(ser);
1427 write_global_array_map(ser);
1429 auto const pd = profData();
1430 write_prof_data(ser, pd);
1432 write_target_profiles(ser);
1434 // We've written everything directly referenced by the profile
1435 // data, but jitted code might still use Classes and TypeAliasReqs
1436 // that haven't been otherwise mentioned (eg VerifyParamType,
1437 // InstanceOfD etc).
1438 write_classes_and_type_aliases(ser, pd);
1440 ser.finalize();
1442 return "";
1443 } catch (std::runtime_error& err) {
1444 return folly::sformat("Failed to serialize profile data: {}", err.what());
1448 std::string serializeOptProfData(const std::string& filename) {
1449 try {
1450 ProfDataSerializer ser(filename, ProfDataSerializer::FileMode::Append);
1452 // The profile data collected for the optimized code should be serialized
1453 // here.
1455 ser.finalize();
1457 return "";
1458 } catch (std::runtime_error& err) {
1459 return folly::sformat("Failed serializeOptProfData: {}", err.what());
1463 std::string deserializeProfData(const std::string& filename, int numWorkers) {
1464 try {
1465 ProfData::setTriedDeserialization();
1467 ProfDataDeserializer ser{filename};
1469 if (read_raw<decltype(kMagic)>(ser) != kMagic) {
1470 throw std::runtime_error("Not a profile-data dump");
1472 auto signature = read_raw<decltype(Repo::get().global().Signature)>(ser);
1473 if (signature != Repo::get().global().Signature) {
1474 throw std::runtime_error("Mismatched repo-schema");
1476 auto size = read_raw<size_t>(ser);
1477 std::string schema;
1478 schema.resize(size);
1479 read_raw(ser, &schema[0], size);
1480 if (schema != repoSchemaId()) {
1481 throw std::runtime_error("Mismatched repo-schema");
1484 size = read_raw<size_t>(ser);
1485 std::string buildHost;
1486 buildHost.resize(size);
1487 read_raw(ser, &buildHost[0], size);
1489 size = read_raw<size_t>(ser);
1490 std::string tag;
1491 tag.resize(size);
1492 read_raw(ser, &tag[0], size);
1494 int64_t buildTime;
1495 read_raw(ser, buildTime);
1496 auto const currTime = TimeStamp::Current();
1497 if (buildTime <= currTime - 3600 * RuntimeOption::ProfDataTTLHours) {
1498 throw std::runtime_error(
1499 "Stale profile data (check Eval.ProfDataTTLHours)");
1500 } else if (buildTime > currTime) {
1501 throw std::runtime_error(
1502 folly::sformat("profile data build timestame: {}, currTime: {}",
1503 buildTime, currTime).c_str());
1506 read_units_preload(ser);
1507 PropertyProfile::deserialize(ser);
1508 InstanceBits::deserialize(ser);
1509 read_global_array_map(ser);
1511 ProfData::Session pds;
1512 auto const pd = profData();
1513 read_prof_data(ser, pd);
1514 pd->setDeserialized(buildHost, tag, buildTime);
1516 read_target_profiles(ser);
1518 read_classes_and_type_aliases(ser);
1520 if (!ser.done()) {
1521 // We have profile data for the optimized code, so deserialize it too.
1524 if (s_preload_dispatcher) {
1525 BootStats::Block timer("DES_wait_for_units_preload",
1526 RuntimeOption::ServerExecutionMode());
1527 s_preload_dispatcher->waitEmpty(true);
1528 delete s_preload_dispatcher;
1529 s_preload_dispatcher = nullptr;
1531 always_assert(ser.done());
1533 // During deserialization we didn't merge the loaded units because
1534 // we wanted to pick and choose the hot Funcs and Classes. But we
1535 // need to merge them before we start serving traffic to ensure we
1536 // don't have inconsistentcies (eg a persistent memoized Func
1537 // wrapper might have been merged, while its implementation was
1538 // not; since the implementation has an internal name, there won't
1539 // be an autoload entry for it, so unless something else causes
1540 // the unit to be loaded the implementation might never get pulled
1541 // in (resulting in fatals when the wrapper tries to call it).
1542 merge_loaded_units(numWorkers);
1544 return "";
1545 } catch (std::runtime_error& err) {
1546 return folly::sformat("Failed to deserialize profile data {}: {}",
1547 filename, err.what());
1551 bool serializeOptProfEnabled() {
1552 return RuntimeOption::EvalJitSerializeOptProfSeconds > 0 ||
1553 RuntimeOption::EvalJitSerializeOptProfRequests > 0;
1556 //////////////////////////////////////////////////////////////////////