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 +----------------------------------------------------------------------+
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 //////////////////////////////////////////////////////////////////////
73 //////////////////////////////////////////////////////////////////////
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
;
86 auto deserialize(ProfDataDeserializer
&ser
, F
&& f
) -> decltype(f()) {
87 using T
= decltype(f());
88 auto const ptr
= read_raw
<uintptr_t>(ser
);
91 auto& ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
- 1));
94 ITRACE(3, "0x{:08x} => 0x{:08x}\n",
95 ptr
- 1, reinterpret_cast<uintptr_t>(ent
));
99 auto const ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
));
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
)) {
126 template<typename S
, typename T
, typename F
>
127 auto call(F
& f
, S
&, const T
& t
) -> decltype(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
) {
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
{
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
) {
170 read_raw_string(ser
, /* skip = */ !RuntimeOption::EvalJitDesUnitPreload
);
171 // path may be nullptr when JitDesUnitPreload isn't set.
172 if (RuntimeOption::EvalJitDesUnitPreload
) {
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;
188 if (filepath
->size() >= 2 && filepath
->data()[1] == ':') return false;
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
) {
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())) {
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)) {
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
));
245 void write_type(ProfDataSerializer
& ser
, Type t
) {
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
,
321 block
->setProfTransID(read_raw
<TransID
>(ser
));
325 block
->addPredicted(read_typed_location(ser
));
330 block
->addPreCondition(read_guarded_location(ser
));
333 PostConditions postConds
;
336 postConds
.changed
.push_back(read_typed_location(ser
));
340 postConds
.refined
.push_back(read_typed_location(ser
));
342 block
->setPostConds(postConds
);
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
) {
352 write_container(ser
, rd
->blocks(),
353 [&] (const RegionDesc::BlockPtr
& b
) {
354 auto const bid
= b
->id();
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
;
373 [&] { incoming
.insert(read_raw
<RegionDesc::BlockId
>(ser
)); });
374 ret
->incoming(std::move(incoming
));
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
;
392 args
.push_back(read_type(ser
));
394 ret
->setInlineContext(ty
, args
);
398 void write_prof_trans_rec(ProfDataSerializer
& ser
,
399 const ProfTransRec
* ptr
,
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());
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);
443 ret
->profCallers().push_back(read_raw
<TransID
>(ser
));
445 auto const asmSize
= read_raw
<uint32_t>(ser
);
446 ret
->setAsmSize(asmSize
);
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
);
461 if (!ne
->isPersistentTypeAlias()) return false;
462 return write_type_alias(ser
, ne
->getCachedTypeAlias());
465 bool write_type_alias(ProfDataSerializer
& ser
, const TypeAliasReq
* td
) {
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
475 auto const td2
= reinterpret_cast<const char*>(td
) + 2;
476 if (!ser
.serialize(td
)) {
477 return ser
.wasSerialized(td2
);
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());
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))) {
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
:
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
);
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
);
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
;
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
);
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
610 bool read_type_alias_or_class(ProfDataDeserializer
& ser
) {
611 auto const ptr
= read_raw
<uintptr_t>(ser
);
612 if (!ptr
) return false;
616 ITRACE(2, "Class>\n");
618 auto& ent
= ser
.getEnt(reinterpret_cast<Class
*>(ptr
- 1));
620 ent
= read_class_internal(ser
);
622 ITRACE(2, "Class: {}\n", ent
->name());
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();
631 for (auto const& ta
: tas
) {
632 if (ta
.name
== name
) {
633 unit
->defTypeAlias(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());
697 write_profiled_funcs(ser
, pd
);
699 write_raw(ser
, pd
->counterDefault());
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
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
));
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
;
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
));
742 auto write_impl(ProfDataSerializer
& ser
, const T
& out
, bool) ->
743 decltype(std::declval
<T
&>().serialize(ser
),void()) {
748 void write_impl(ProfDataSerializer
& ser
, const T
& out
, int) {
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
,
768 void process(T
& out
, const StringData
* name
) {
769 write_raw(ser
, size
);
770 write_string(ser
, name
);
772 TargetProfile
<T
>::reduce(out
, handle
, size
);
773 if (size
== sizeof(T
)) {
774 write_maybe_serializable(ser
, out
);
776 write_raw(ser
, &out
, size
);
780 template<typename T
> void operator()(const T
&) {}
782 void operator()(const rds::Profile
<T
>& pt
) {
783 if (size
== sizeof(T
)) {
785 process(out
, pt
.name
.get());
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
;
799 void write_target_profiles(ProfDataSerializer
& ser
) {
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{});
810 auto read_impl(ProfDataDeserializer
& ser
, T
& out
, bool) ->
811 decltype(out
.deserialize(ser
),void()) {
812 out
.deserialize(ser
);
816 void read_impl(ProfDataDeserializer
& ser
, T
& out
, int) {
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); }
831 void operator()(rds::Profile
<T
>& pt
) {
832 TargetProfile
<T
> prof(pt
.transId
,
838 if (size
== sizeof(T
)) {
839 read_maybe_serializable(ser
, prof
.value());
841 read_raw(ser
, &prof
.value(), size
);
845 ProfDataDeserializer
& ser
;
847 // The size of the original rds allocation.
851 void read_target_profiles(ProfDataDeserializer
& ser
) {
852 BootStats::Block
timer("DES_read_target_profiles",
853 RuntimeOption::ServerExecutionMode());
855 auto const size
= read_raw
<uint32_t>(ser
);
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
) {
879 spec
.numaNode
= next_numa_node(curr_node
);
880 workers
.emplace_back(
884 ProfileNonVMThread nonVM
;
885 #if USE_JEMALLOC_EXTENT_HOOKS
886 if (auto arena
= next_extra_arena(spec
.numaNode
)) {
887 arena
->bindCurrentThread();
890 hphp_session_init(Treadmill::SessionKind::PreloadRepo
);
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
];
902 // swallow errors silently. persistent things should raise
903 // errors, and we don't really care about merging
904 // non-persistent things.
915 for (auto& worker
: workers
) {
918 for (auto& worker
: workers
) {
923 ////////////////////////////////////////////////////////////////////////////////
926 ProfDataSerializer::ProfDataSerializer(const std::string
& name
, 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);
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);
946 folly::sformat("Failed to open file for write {}, {}", name
,
947 folly::errnoStr(errno
));
949 throw std::runtime_error(msg
);
953 void ProfDataSerializer::finalize() {
955 if (offset
) ::write(fd
, buffer
, offset
);
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
);
965 if (rename(partialFile
.c_str(), fileName
.c_str()) == -1) {
967 folly::sformat("Failed to rename {} to {}, {}",
968 partialFile
, fileName
, folly::errnoStr(errno
));
970 throw std::runtime_error(msg
);
972 FTRACE(1, "Finished serializing all profile data to {}\n", fileName
);
977 ProfDataSerializer::~ProfDataSerializer() {
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.
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() {
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
);
1011 if (ser
.offset
== 0) {
1012 if (::write(ser
.fd
, data
, sz
) != sz
) {
1013 throw std::runtime_error("Failed to write serialized data");
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
;
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");
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
);
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
;
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");
1051 auto const bytes_read
= ::read(ser
.fd
,
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
;
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();
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
];
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
);
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
) {
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();
1143 write_raw(ser
, str
.data(), sz
);
1146 ArrayData
* read_array(ProfDataDeserializer
& 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
);
1155 auto v
= unserialize_from_buffer(
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
) {
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
) {
1189 ITRACE(2, "Class: {}\n", cls
? cls
->name() : staticEmptyString());
1191 ITRACE(2, "Class>\n");
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
) {
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);
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());
1234 write_class(ser
, func
->cls());
1238 Class
* read_class(ProfDataDeserializer
& ser
) {
1239 ITRACE(2, "Class>\n");
1240 auto const ret
= deserialize(
1244 return read_class_internal(ser
);
1248 ITRACE(2, "Class: {}\n", ret
? ret
->name() : staticEmptyString());
1252 void write_func(ProfDataSerializer
& ser
, const Func
* func
) {
1254 ITRACE(2, "Func: {}\n", func
? func
->fullName() : staticEmptyString());
1256 ITRACE(2, "Func>\n");
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()));
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
;
1293 assertx(cls
->getMethod(slot
) == func
);
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(
1316 auto fid
= read_raw
<uint32_t>(ser
);
1317 auto const func
= [&] () -> const Func
* {
1318 if (fid
& 0x80000000) {
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
);
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
) {
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);
1356 ser
.recordFid(fid
, func
->getFuncId());
1357 return const_cast<Func
*>(func
);
1360 ITRACE(2, "Func: {}\n", ret
? ret
->fullName() : staticEmptyString());
1364 void write_clsmeth(ProfDataSerializer
& ser
, ClsMethDataRef clsMeth
) {
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())) {
1374 write_raw(ser
, uintptr_t(-1));
1375 write_class(ser
, clsMeth
->getCls());
1377 if (ser
.serialize(clsMeth
->getFunc())) {
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
) {
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();
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
);
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
) {
1450 ProfDataSerializer
ser(filename
, ProfDataSerializer::FileMode::Append
);
1452 // The profile data collected for the optimized code should be serialized
1458 } catch (std::runtime_error
& err
) {
1459 return folly::sformat("Failed serializeOptProfData: {}", err
.what());
1463 std::string
deserializeProfData(const std::string
& filename
, int numWorkers
) {
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
);
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
);
1492 read_raw(ser
, &tag
[0], size
);
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
);
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
);
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 //////////////////////////////////////////////////////////////////////