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"
27 #include "hphp/runtime/ext/std/ext_std_closure.h"
29 #include "hphp/runtime/vm/class.h"
30 #include "hphp/runtime/vm/func.h"
31 #include "hphp/runtime/vm/instance-bits.h"
32 #include "hphp/runtime/vm/jit/array-kind-profile.h"
33 #include "hphp/runtime/vm/jit/array-offset-profile.h"
34 #include "hphp/runtime/vm/jit/call-target-profile.h"
35 #include "hphp/runtime/vm/jit/cls-cns-profile.h"
36 #include "hphp/runtime/vm/jit/containers.h"
37 #include "hphp/runtime/vm/jit/decref-profile.h"
38 #include "hphp/runtime/vm/jit/incref-profile.h"
39 #include "hphp/runtime/vm/jit/inlining-decider.h"
40 #include "hphp/runtime/vm/jit/meth-profile.h"
41 #include "hphp/runtime/vm/jit/prof-data.h"
42 #include "hphp/runtime/vm/jit/release-vv-profile.h"
43 #include "hphp/runtime/vm/jit/switch-profile.h"
44 #include "hphp/runtime/vm/jit/trans-cfg.h"
45 #include "hphp/runtime/vm/jit/type-profile.h"
46 #include "hphp/runtime/vm/named-entity-defs.h"
47 #include "hphp/runtime/vm/named-entity.h"
48 #include "hphp/runtime/vm/repo-global-data.h"
49 #include "hphp/runtime/vm/repo.h"
50 #include "hphp/runtime/vm/treadmill.h"
51 #include "hphp/runtime/vm/type-profile.h"
52 #include "hphp/runtime/vm/unit.h"
54 #include "hphp/util/boot-stats.h"
55 #include "hphp/util/build-info.h"
56 #include "hphp/util/managed-arena.h"
57 #include "hphp/util/numa.h"
58 #include "hphp/util/process.h"
60 #include <folly/portability/Unistd.h>
61 #include <folly/String.h>
63 namespace HPHP
{ namespace jit
{
64 //////////////////////////////////////////////////////////////////////
67 //////////////////////////////////////////////////////////////////////
71 StaticString
s_invoke("__invoke");
72 StaticString
s_86ctor("86ctor");
73 StaticString
s_86pinit("86pinit");
74 StaticString
s_86sinit("86sinit");
75 StaticString
s_86linit("86linit");
77 constexpr uint32_t k86pinitSlot
= 0x80000000u
;
78 constexpr uint32_t k86sinitSlot
= 0x80000001u
;
79 constexpr uint32_t k86linitSlot
= 0x80000002u
;
82 auto deserialize(ProfDataDeserializer
&ser
, F
&& f
) -> decltype(f()) {
83 using T
= decltype(f());
84 auto const ptr
= read_raw
<uintptr_t>(ser
);
87 auto& ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
- 1));
90 ITRACE(3, "0x{:08x} => 0x{:08x}\n",
91 ptr
- 1, reinterpret_cast<uintptr_t>(ent
));
95 auto const ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
));
100 void write_serialized_ptr(ProfDataSerializer
& ser
, const void* p
) {
101 auto const ptr_as_int
= reinterpret_cast<uintptr_t>(p
);
102 assertx(!(ptr_as_int
& 1));
103 write_raw(ser
, ptr_as_int
| 1);
106 void write_raw_ptr(ProfDataSerializer
& ser
, const void* p
) {
107 auto const ptr_as_int
= reinterpret_cast<uintptr_t>(p
);
108 assertx(!(ptr_as_int
& 1));
109 write_raw(ser
, ptr_as_int
);
113 * Helper functions so that the function passed to write_container can
114 * take a serializer as a parameter, or skip it (eg if its a lambda
115 * which is already capturing the serializer).
117 template<typename S
, typename T
, typename F
>
118 auto call(F
& f
, S
& ser
, const T
& t
) -> decltype(f(ser
, t
)) {
122 template<typename S
, typename T
, typename F
>
123 auto call(F
& f
, S
&, const T
& t
) -> decltype(f(t
)) {
127 template<typename C
, typename F
>
128 void write_container(ProfDataSerializer
& ser
, const C
& cont
, F f
) {
129 write_raw(ser
, safe_cast
<uint32_t>(cont
.size()));
130 for (auto const &elm
: cont
) {
136 void read_container(ProfDataDeserializer
& ser
, F f
) {
137 auto sz
= read_raw
<uint32_t>(ser
);
138 while (sz
--) { f(); }
141 void write_srckey(ProfDataSerializer
& ser
, SrcKey sk
) {
142 ITRACE(2, "SrcKey>\n");
143 if (ser
.serialize(sk
.func())) {
145 write_raw(ser
, uintptr_t(-1));
146 write_func(ser
, sk
.func());
148 write_raw(ser
, sk
.toAtomicInt());
149 ITRACE(2, "SrcKey: {}\n", show(sk
));
152 SrcKey
read_srckey(ProfDataDeserializer
& ser
) {
153 ITRACE(2, "SrcKey>\n");
154 auto orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
155 if (orig
== uintptr_t(-1)) {
158 orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
160 auto const id
= SrcKey::fromAtomicInt(orig
).funcID();
161 assertx(uint32_t(orig
) == id
);
162 auto const sk
= SrcKey::fromAtomicInt(orig
- id
+ ser
.getFid(id
));
163 ITRACE(2, "SrcKey: {}\n", show(sk
));
167 void write_type(ProfDataSerializer
& ser
, Type t
) {
171 Type
read_type(ProfDataDeserializer
& ser
) {
172 return Type::deserialize(ser
);
175 void write_typed_location(ProfDataSerializer
& ser
,
176 const RegionDesc::TypedLocation
& loc
) {
177 write_raw(ser
, loc
.location
);
178 write_type(ser
, loc
.type
);
181 void write_guarded_location(ProfDataSerializer
& ser
,
182 const RegionDesc::GuardedLocation
& loc
) {
183 write_raw(ser
, loc
.location
);
184 write_type(ser
, loc
.type
);
185 write_raw(ser
, loc
.category
);
188 RegionDesc::TypedLocation
read_typed_location(ProfDataDeserializer
& ser
) {
189 auto const location
= read_raw
<Location
>(ser
);
190 auto const type
= read_type(ser
);
191 return { location
, type
};
194 RegionDesc::GuardedLocation
read_guarded_location(ProfDataDeserializer
& ser
) {
195 auto const location
= read_raw
<Location
>(ser
);
196 auto const type
= read_type(ser
);
197 auto const cat
= read_raw
<DataTypeCategory
>(ser
);
198 return { location
, type
, cat
};
201 void write_global_array_map(ProfDataSerializer
& ser
) {
202 write_container(ser
, globalArrayTypeTable(),
203 write_raw
<const RepoAuthType::Array
*>);
206 void read_global_array_map(ProfDataDeserializer
& ser
) {
207 BootStats::Block
timer("DES_read_global_array_map",
208 RuntimeOption::ServerExecutionMode());
209 auto sz DEBUG_ONLY
= read_raw
<uint32_t>(ser
);
210 assertx(sz
== globalArrayTypeTable().size());
211 for (auto arr
: globalArrayTypeTable()) {
212 auto const orig
= read_raw
<const RepoAuthType::Array
*>(ser
);
213 ser
.recordRat(orig
, arr
);
217 void write_region_block(ProfDataSerializer
& ser
,
218 const RegionDesc::BlockPtr
& block
) {
219 write_raw(ser
, block
->id());
220 write_srckey(ser
, block
->start());
221 write_raw(ser
, block
->length());
222 write_raw(ser
, block
->initialSpOffset());
223 write_raw(ser
, block
->profTransID());
224 write_container(ser
, block
->typePredictions(), write_typed_location
);
225 write_container(ser
, block
->typePreConditions(), write_guarded_location
);
226 write_container(ser
, block
->knownFuncs(),
227 [] (ProfDataSerializer
& s
,
228 std::pair
<SrcKey
, const Func
*> knownFunc
) {
229 write_srckey(s
, knownFunc
.first
);
230 write_func(s
, knownFunc
.second
);
232 write_container(ser
, block
->postConds().changed
, write_typed_location
);
233 write_container(ser
, block
->postConds().refined
, write_typed_location
);
236 RegionDesc::BlockPtr
read_region_block(ProfDataDeserializer
& ser
) {
237 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
238 auto const start
= read_srckey(ser
);
239 auto const length
= read_raw
<int>(ser
);
240 auto const initialSpOffset
= read_raw
<FPInvOffset
>(ser
);
242 auto const block
= std::make_shared
<RegionDesc::Block
>(id
,
250 block
->setProfTransID(read_raw
<TransID
>(ser
));
254 block
->addPredicted(read_typed_location(ser
));
259 block
->addPreCondition(read_guarded_location(ser
));
264 auto const sk
= read_srckey(ser
);
265 auto const func
= read_func(ser
);
266 block
->setKnownFunc(sk
, func
);
269 PostConditions postConds
;
272 postConds
.changed
.push_back(read_typed_location(ser
));
276 postConds
.refined
.push_back(read_typed_location(ser
));
278 block
->setPostConds(postConds
);
282 void write_region_desc(ProfDataSerializer
& ser
, const RegionDesc
* rd
) {
283 write_container(ser
, rd
->blocks(), write_region_block
);
284 write_container(ser
, findPredTrans(*rd
, profData()),
285 [&] (RegionDesc::BlockId id
) {
288 write_container(ser
, rd
->blocks(),
289 [&] (const RegionDesc::BlockPtr
& b
) {
290 auto const bid
= b
->id();
292 assertx(rd
->succs(bid
).empty());
293 assertx(rd
->preds(bid
).empty());
294 assertx(rd
->merged(bid
).empty());
295 auto const pr
= rd
->prevRetrans(bid
);
296 write_raw(ser
, pr
? pr
.value() : kInvalidTransID
);
297 auto const nr
= rd
->nextRetrans(bid
);
298 write_raw(ser
, nr
? nr
.value() : kInvalidTransID
);
300 write_type(ser
, rd
->inlineCtxType());
301 write_container(ser
, rd
->inlineInputTypes(), write_type
);
304 RegionDescPtr
read_region_desc(ProfDataDeserializer
& ser
) {
305 auto ret
= std::make_shared
<RegionDesc
>();
306 read_container(ser
, [&] { ret
->addBlock(read_region_block(ser
)); });
307 RegionDesc::BlockIdSet incoming
;
309 [&] { incoming
.insert(read_raw
<RegionDesc::BlockId
>(ser
)); });
310 ret
->incoming(std::move(incoming
));
314 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
315 auto const pr
= read_raw
<RegionDesc::BlockId
>(ser
);
316 auto const nr
= read_raw
<RegionDesc::BlockId
>(ser
);
317 if (pr
!= kInvalidTransID
) {
318 ret
->setNextRetrans(pr
, id
);
320 if (nr
!= kInvalidTransID
) {
321 ret
->setNextRetrans(id
, nr
);
324 auto const ty
= read_type(ser
);
325 std::vector
<Type
> args
;
328 args
.push_back(read_type(ser
));
330 ret
->setInlineContext(ty
, args
);
334 void write_prof_trans_rec(ProfDataSerializer
& ser
,
335 const ProfTransRec
* ptr
,
337 if (!ptr
) return write_raw(ser
, TransKind
{});
338 write_raw(ser
, ptr
->kind());
339 write_srckey(ser
, ptr
->srcKey());
340 if (ptr
->kind() == TransKind::Profile
) {
341 write_raw(ser
, ptr
->lastBcOff());
342 write_region_desc(ser
, ptr
->region().get());
344 write_raw(ser
, ptr
->prologueArgs());
346 auto lock
= ptr
->lockCallerList();
347 std::vector
<TransID
> callers
;
348 auto addCaller
= [&] (TCA caller
) {
349 if (!tc::isProfileCodeAddress(caller
)) return;
350 auto const callerTransId
= pd
->jmpTransID(caller
);
351 assertx(callerTransId
!= kInvalidTransID
);
352 callers
.push_back(callerTransId
);
354 for (auto const caller
: ptr
->mainCallers()) addCaller(caller
);
355 for (auto const caller
: ptr
->guardCallers()) addCaller(caller
);
356 write_container(ser
, callers
, write_raw
<TransID
>);
360 std::unique_ptr
<ProfTransRec
> read_prof_trans_rec(ProfDataDeserializer
& ser
) {
361 auto const kind
= read_raw
<TransKind
>(ser
);
362 static_assert(TransKind::Profile
!= TransKind
{} &&
363 TransKind::ProfPrologue
!= TransKind
{},
364 "Profile and ProfPrologue must not be zero");
365 if (kind
== TransKind
{}) return nullptr;
366 auto const sk
= read_srckey(ser
);
367 if (kind
== TransKind::Profile
) {
368 auto const lastBcOff
= read_raw
<Offset
>(ser
);
369 auto const region
= read_region_desc(ser
);
370 return std::make_unique
<ProfTransRec
>(lastBcOff
, sk
, region
);
373 auto ret
= std::make_unique
<ProfTransRec
>(sk
, read_raw
<int>(ser
));
377 ret
->profCallers().push_back(read_raw
<TransID
>(ser
));
383 bool write_type_alias(ProfDataSerializer
& ser
, const TypeAliasReq
* td
);
385 bool write_type_alias_or_class(ProfDataSerializer
& ser
, const NamedEntity
* ne
) {
386 if (!ne
) return false;
387 if (auto const cls
= ne
->clsList()) {
388 if (!(cls
->attrs() & AttrUnique
)) return false;
389 if (!cls
->wasSerialized()) write_class(ser
, cls
);
392 if (!ne
->isPersistentTypeAlias()) return false;
393 return write_type_alias(ser
, ne
->getCachedTypeAlias());
396 bool write_type_alias(ProfDataSerializer
& ser
, const TypeAliasReq
* td
) {
398 ITRACE(2, "TypeAlias: {}\n", td
->name
);
400 ITRACE(2, "TypeAlias>\n");
401 // we're writing out Class* and TypeAliasReq* intermingled here. Set
402 // bit 1 for TypeAliasReq's so we can distinguish when reading back
403 // in. We also need to keep track of both whether we already tried
404 // serialize this one, and whether it was successful. Use td2 for
406 auto const td2
= reinterpret_cast<const char*>(td
) + 2;
407 if (!ser
.serialize(td
)) {
408 return ser
.wasSerialized(td2
);
412 auto const unit
= td
->unit
;
413 auto const name
= td
->name
;
414 auto const tas
= unit
->typeAliases();
415 for (auto const& ta
: tas
) {
416 if (ta
.name
== name
) {
417 switch (get_ts_kind(ta
.typeStructure
.get())) {
418 case TypeStructure::Kind::T_unresolved
: {
419 auto const clsname
= get_ts_classname(ta
.typeStructure
.get());
420 if (!write_type_alias_or_class(ser
,
421 NamedEntity::get(clsname
, false))) {
426 case TypeStructure::Kind::T_void
:
427 case TypeStructure::Kind::T_int
:
428 case TypeStructure::Kind::T_bool
:
429 case TypeStructure::Kind::T_float
:
430 case TypeStructure::Kind::T_string
:
431 case TypeStructure::Kind::T_resource
:
432 case TypeStructure::Kind::T_num
:
433 case TypeStructure::Kind::T_arraykey
:
434 case TypeStructure::Kind::T_noreturn
:
435 case TypeStructure::Kind::T_tuple
:
436 case TypeStructure::Kind::T_array
:
437 case TypeStructure::Kind::T_darray
:
438 case TypeStructure::Kind::T_varray
:
439 case TypeStructure::Kind::T_varray_or_darray
:
440 case TypeStructure::Kind::T_dict
:
441 case TypeStructure::Kind::T_vec
:
442 case TypeStructure::Kind::T_keyset
:
443 case TypeStructure::Kind::T_vec_or_dict
:
444 case TypeStructure::Kind::T_arraylike
:
445 case TypeStructure::Kind::T_nonnull
:
448 case TypeStructure::Kind::T_fun
:
449 case TypeStructure::Kind::T_typevar
:
450 case TypeStructure::Kind::T_shape
:
451 case TypeStructure::Kind::T_typeaccess
:
452 case TypeStructure::Kind::T_xhp
:
453 case TypeStructure::Kind::T_mixed
:
454 case TypeStructure::Kind::T_reifiedtype
:
457 case TypeStructure::Kind::T_class
:
458 case TypeStructure::Kind::T_interface
:
459 case TypeStructure::Kind::T_trait
:
460 case TypeStructure::Kind::T_enum
:
461 // these types don't occur in unresolved TypeStructures
462 always_assert(false);
465 if (!ser
.serialize(td2
)) always_assert(false);
466 write_serialized_ptr(ser
, td2
);
467 write_unit(ser
, td
->unit
);
468 write_string(ser
, td
->name
);
475 Class
* read_class_internal(ProfDataDeserializer
& ser
) {
476 auto const id
= read_raw
<decltype(std::declval
<PreClass
*>()->id())>(ser
);
477 auto const unit
= read_unit(ser
);
481 auto const dep
= read_class(ser
);
482 auto const ne
= dep
->preClass()->namedEntity();
483 // if it's not persistent, make sure that dep
484 // is the active class for this NamedEntity
485 assertx(ne
->m_cachedClass
.bound());
486 if (ne
->m_cachedClass
.isNormal()) {
487 ne
->setCachedClass(dep
);
489 auto const depName
= read_string(ser
);
490 if (!dep
->name()->isame(depName
)) {
491 // this dependent was referred to via a
492 // class_alias, so we need to make sure
493 // *that* points to the class too
494 auto const aliasNe
= NamedEntity::get(depName
);
495 aliasNe
->m_cachedClass
.bind(rds::Mode::Normal
);
496 if (aliasNe
->m_cachedClass
.isNormal()) {
497 aliasNe
->m_cachedClass
.markUninit();
499 aliasNe
->setCachedClass(dep
);
503 auto const preClass
= unit
->lookupPreClassId(id
);
504 folly::Optional
<TypeAliasReq
> enumBaseReq
;
507 preClass
->enumBaseTy().namedEntity()->m_cachedTypeAlias
.markUninit();
510 if (preClass
->attrs() & AttrEnum
&&
511 preClass
->enumBaseTy().isObject()) {
512 auto const dt
= read_raw
<DataType
>(ser
);
513 auto const ne
= preClass
->enumBaseTy().namedEntity();
514 if (!ne
->m_cachedTypeAlias
.bound() ||
515 !ne
->m_cachedTypeAlias
.isInit()) {
516 enumBaseReq
.emplace();
517 enumBaseReq
->type
= dt
== KindOfInt64
?
518 AnnotType::Int
: AnnotType::String
;
519 enumBaseReq
->name
= preClass
->enumBaseTy().typeName();
520 ne
->m_cachedTypeAlias
.bind(rds::Mode::Normal
);
521 ne
->m_cachedTypeAlias
.initWith(*enumBaseReq
);
524 auto const ne
= preClass
->namedEntity();
525 // If it's not persistent, make sure its NamedEntity is
526 // unbound, ready for DefClass
527 if (ne
->m_cachedClass
.bound() &&
528 ne
->m_cachedClass
.isNormal()) {
529 ne
->m_cachedClass
.markUninit();
532 auto const cls
= Unit::defClass(preClass
, true);
533 if (cls
->pinitVec().size()) cls
->initPropHandle();
534 if (cls
->numStaticProperties()) cls
->initSPropHandles();
536 if (cls
->parent() == c_Closure::classof()) {
537 auto const ctx
= read_class(ser
);
538 if (ctx
!= cls
) return cls
->rescope(ctx
);
544 * This reads in TypeAliases and Classes that are used for code gen,
545 * but otherwise aren't needed for profiling. We just need them to be
546 * loaded into the NamedEntity table, so this function just returns
547 * whether or not to continue (the end of the list is marked by a
550 bool read_type_alias_or_class(ProfDataDeserializer
& ser
) {
551 auto const ptr
= read_raw
<uintptr_t>(ser
);
552 if (!ptr
) return false;
556 ITRACE(2, "Class>\n");
558 auto& ent
= ser
.getEnt(reinterpret_cast<Class
*>(ptr
- 1));
560 ent
= read_class_internal(ser
);
562 ITRACE(2, "Class: {}\n", ent
->name());
566 auto const unit
= read_unit(ser
);
567 auto const name
= read_string(ser
);
568 ITRACE(2, "TypeAlias: {}\n", name
);
569 auto const tas
= unit
->typeAliases();
571 for (auto const& ta
: tas
) {
572 if (ta
.name
== name
) {
573 unit
->defTypeAlias(id
);
578 always_assert(false);
581 void write_profiled_funcs(ProfDataSerializer
& ser
, ProfData
* pd
) {
582 auto const maxFuncId
= pd
->maxProfilingFuncId();
584 for (FuncId fid
= 0; fid
<= maxFuncId
; fid
++) {
585 if (!Func::isFuncIdValid(fid
) || !pd
->profiling(fid
)) continue;
586 write_func(ser
, Func::fromFuncId(fid
));
588 write_raw(ser
, uintptr_t{});
591 void read_profiled_funcs(ProfDataDeserializer
& ser
, ProfData
* pd
) {
592 while (auto const func
= read_func(ser
)) {
593 pd
->setProfiling(func
->getFuncId());
597 void write_classes_and_type_aliases(ProfDataSerializer
& ser
, ProfData
* pd
) {
598 auto const maxFuncId
= pd
->maxProfilingFuncId();
600 // in an attempt to get a sensible order for these, start with the
601 // ones referenced by params and return constraints.
602 for (FuncId fid
= 0; fid
<= maxFuncId
; fid
++) {
603 if (!Func::isFuncIdValid(fid
) || !pd
->profiling(fid
)) continue;
604 auto const func
= Func::fromFuncId(fid
);
605 for (auto const& p
: func
->params()) {
606 write_type_alias_or_class(ser
, p
.typeConstraint
.namedEntity());
610 // Now just iterate and write anything that remains
611 NamedEntity::foreach_name(
612 [&] (NamedEntity
& ne
) {
613 write_type_alias_or_class(ser
, &ne
);
616 write_raw(ser
, uintptr_t{});
619 void read_classes_and_type_aliases(ProfDataDeserializer
& ser
) {
620 BootStats::Block
timer("DES_read_classes_and_type_aliases",
621 RuntimeOption::ServerExecutionMode());
622 while (read_type_alias_or_class(ser
)) {
623 // nothing to do. this was just to make sure everything is loaded
624 // into the NamedEntity table
628 void write_prof_data(ProfDataSerializer
& ser
, ProfData
* pd
) {
629 write_profiled_funcs(ser
, pd
);
631 write_raw(ser
, pd
->counterDefault());
633 [&] (const ProfTransRec
* ptr
) {
634 auto const transID
= ptr
->isProfile() ?
635 ptr
->region()->entry()->profTransID() :
636 pd
->proflogueTransId(ptr
->func(), ptr
->prologueArgs());
637 write_raw(ser
, transID
);
638 write_prof_trans_rec(ser
, ptr
, pd
);
639 // forEachTransRec already grabs a read lock, and we're not
640 // going to add a *new* counter here (so we don't need a write
642 write_raw(ser
, *pd
->transCounterAddrNoLock(transID
));
645 write_raw(ser
, kInvalidTransID
);
648 void read_prof_data(ProfDataDeserializer
& ser
, ProfData
* pd
) {
649 BootStats::Block
timer("DES_read_prof_data",
650 RuntimeOption::ServerExecutionMode());
651 read_profiled_funcs(ser
, pd
);
653 pd
->resetCounters(read_raw
<int64_t>(ser
));
655 auto const transID
= read_raw
<TransID
>(ser
);
656 if (transID
== kInvalidTransID
) break;
657 pd
->addProfTrans(transID
, read_prof_trans_rec(ser
));
658 *pd
->transCounterAddr(transID
) = read_raw
<int64_t>(ser
);
663 auto write_impl(ProfDataSerializer
& ser
, const T
& out
, bool) ->
664 decltype(std::declval
<T
&>().serialize(ser
),void()) {
669 void write_impl(ProfDataSerializer
& ser
, const T
& out
, int) {
674 void write_maybe_serializable(ProfDataSerializer
& ser
, const T
& out
) {
675 write_impl(ser
, out
, false);
678 struct TargetProfileVisitor
: boost::static_visitor
<void> {
679 TargetProfileVisitor(ProfDataSerializer
& ser
,
680 const rds::Symbol
& sym
,
689 void process(T
& out
, const StringData
* name
) {
690 write_raw(ser
, size
);
691 write_string(ser
, name
);
693 TargetProfile
<T
>::reduce(out
, handle
, size
);
694 if (size
== sizeof(T
)) {
695 write_maybe_serializable(ser
, out
);
697 write_raw(ser
, &out
, size
);
701 template<typename T
> void operator()(const T
&) {}
703 void operator()(const rds::Profile
<T
>& pt
) {
704 if (size
== sizeof(T
)) {
706 process(out
, pt
.name
.get());
708 auto const mem
= calloc(1, size
);
709 SCOPE_EXIT
{ free(mem
); };
710 process(*reinterpret_cast<T
*>(mem
), pt
.name
.get());
714 ProfDataSerializer
& ser
;
715 const rds::Symbol
& sym
;
720 void write_target_profiles(ProfDataSerializer
& ser
) {
722 [&] (const rds::Symbol
& symbol
, rds::Handle handle
, uint32_t size
) {
723 TargetProfileVisitor
tv(ser
, symbol
, handle
, size
);
724 boost::apply_visitor(tv
, symbol
);
727 write_raw(ser
, uint32_t{});
731 auto read_impl(ProfDataDeserializer
& ser
, T
& out
, bool) ->
732 decltype(out
.deserialize(ser
),void()) {
733 out
.deserialize(ser
);
737 void read_impl(ProfDataDeserializer
& ser
, T
& out
, int) {
742 void read_maybe_serializable(ProfDataDeserializer
& ser
, T
& out
) {
743 read_impl(ser
, out
, false);
746 struct SymbolFixup
: boost::static_visitor
<void> {
747 SymbolFixup(ProfDataDeserializer
& ser
, StringData
* name
, uint32_t size
) :
748 ser
{ser
}, name
{name
}, size
{size
} {}
750 template<typename T
> void operator()(T
&) { always_assert(false); }
752 void operator()(rds::Profile
<T
>& pt
) {
753 TargetProfile
<T
> prof(pt
.transId
,
759 if (size
== sizeof(T
)) {
760 read_maybe_serializable(ser
, prof
.value());
762 read_raw(ser
, &prof
.value(), size
);
766 ProfDataDeserializer
& ser
;
768 // The size of the original rds allocation.
772 void read_target_profiles(ProfDataDeserializer
& ser
) {
773 BootStats::Block
timer("DES_read_target_profiles",
774 RuntimeOption::ServerExecutionMode());
776 auto const size
= read_raw
<uint32_t>(ser
);
778 auto const name
= read_string(ser
);
779 auto sym
= read_raw
<rds::Symbol
>(ser
);
780 auto sf
= SymbolFixup
{ser
, name
, size
};
781 boost::apply_visitor(sf
, sym
);
785 void merge_loaded_units(int numWorkers
) {
786 BootStats::Block
timer("DES_merge_loaded_units",
787 RuntimeOption::ServerExecutionMode());
788 auto units
= loadedUnitsRepoAuth();
790 std::vector
<std::thread
> workers
;
791 // Compute a batch size that causes each thread to process approximately 16
792 // batches. Even if the batches are somewhat imbalanced in what they contain,
793 // the straggler workers are very unlikey to take more than 10% longer than
794 // the first worker to finish.
795 auto const batchSize
{std::max(units
.size() / numWorkers
/ 16, size_t(1))};
796 std::atomic
<size_t> index
{0};
797 UNUSED
std::atomic_int curr_node
{0};
798 for (auto worker
= 0; worker
< numWorkers
; ++worker
) {
799 workers
.push_back(std::thread([&] {
800 ProfileNonVMThread nonVM
;
801 #if USE_JEMALLOC_EXTENT_HOOKS
802 auto const numaNode
= next_numa_node(curr_node
);
805 s_numaNode
= numaNode
;
806 numa_sched_setaffinity(0, node_to_cpu_mask
[numaNode
]);
809 if (auto arena
= next_extra_arena(numaNode
)) {
810 arena
->bindCurrentThread();
815 hphp_session_init(Treadmill::SessionKind::PreloadRepo
);
818 auto begin
= index
.fetch_add(batchSize
);
819 auto end
= std::min(begin
+ batchSize
, units
.size());
820 if (begin
>= end
) break;
821 auto unitCount
= end
- begin
;
822 for (auto i
= size_t{0}; i
< unitCount
; ++i
) {
823 auto const unit
= units
[begin
+ i
];
827 // swallow errors silently. persistent things should raise
828 // errors, and we don't really care about merging
829 // non-persistent things.
839 for (auto& worker
: workers
) {
844 ////////////////////////////////////////////////////////////////////////////////
847 ProfDataSerializer::ProfDataSerializer(const std::string
& name
)
849 // Delete old profile data to avoid confusion. This should've happened from
850 // outside the process, but in case it didn't happen, try to do it here.
851 unlink(name
.c_str());
853 std::string partialFile
= name
+ ".part";
854 fd
= open(partialFile
.c_str(),
855 O_CLOEXEC
| O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
858 folly::sformat("Failed to open file for write {}, {}", name
,
859 folly::errnoStr(errno
));
861 throw std::runtime_error(msg
);
865 void ProfDataSerializer::finalize() {
867 if (offset
) ::write(fd
, buffer
, offset
);
871 std::string partialFile
= fileName
+ ".part";
872 if (rename(partialFile
.c_str(), fileName
.c_str()) == -1) {
874 folly::sformat("Failed to rename {} to {}, {}",
875 partialFile
, fileName
, folly::errnoStr(errno
));
877 throw std::runtime_error(msg
);
879 FTRACE(1, "Finished serializing profile data to " + fileName
);
883 ProfDataSerializer::~ProfDataSerializer() {
885 // We didn't finalize(), maybe because an exception was thrown while writing
886 // the data. The file is likely corrupt or incomplete, so discard it.
889 std::string partialFile
= fileName
+ ".part";
890 unlink(partialFile
.c_str());
894 std::string
ProfDataDeserializer::s_buildHost
;
895 int64_t ProfDataDeserializer::s_buildTime
{0};
897 ProfDataDeserializer::ProfDataDeserializer(const std::string
& name
) {
898 fd
= open(name
.c_str(), O_CLOEXEC
| O_RDONLY
);
899 if (fd
== -1) throw std::runtime_error("Failed to open: " + name
);
902 ProfDataDeserializer::~ProfDataDeserializer() {
907 bool ProfDataDeserializer::done() {
909 return offset
== buffer_size
&& ::read(fd
, &byte
, 1) == 0;
912 void write_raw(ProfDataSerializer
& ser
, const void* data
, size_t sz
) {
913 if (ser
.offset
+ sz
<= ProfDataSerializer::buffer_size
) {
914 memcpy(ser
.buffer
+ ser
.offset
, data
, sz
);
918 if (ser
.offset
== 0) {
919 if (::write(ser
.fd
, data
, sz
) != sz
) {
920 throw std::runtime_error("Failed to write serialized data");
924 if (auto const delta
= ProfDataSerializer::buffer_size
- ser
.offset
) {
925 memcpy(ser
.buffer
+ ser
.offset
, data
, delta
);
926 data
= static_cast<const char*>(data
) + delta
;
928 ser
.offset
= ProfDataSerializer::buffer_size
;
930 assertx(ser
.offset
== ProfDataSerializer::buffer_size
);
931 if (::write(ser
.fd
, ser
.buffer
, ser
.offset
) != ser
.offset
) {
932 throw std::runtime_error("Failed to write serialized data");
935 write_raw(ser
, data
, sz
);
938 void read_raw(ProfDataDeserializer
& ser
, void* data
, size_t sz
) {
939 if (ser
.offset
+ sz
<= ProfDataDeserializer::buffer_size
) {
940 memcpy(data
, ser
.buffer
+ ser
.offset
, sz
);
944 if (auto const delta
= ProfDataDeserializer::buffer_size
- ser
.offset
) {
945 memcpy(data
, ser
.buffer
+ ser
.offset
, delta
);
946 data
= static_cast<char*>(data
) + delta
;
948 ser
.offset
= ProfDataDeserializer::buffer_size
;
950 if (sz
>= ProfDataDeserializer::buffer_size
) {
951 auto const bytes_read
= ::read(ser
.fd
, data
, sz
);
952 if (bytes_read
< 0 || bytes_read
< sz
) {
953 throw std::runtime_error("Failed to read serialized data");
958 auto const bytes_read
= ::read(ser
.fd
,
960 ProfDataDeserializer::buffer_size
);
961 if (bytes_read
< 0 || bytes_read
< sz
) {
962 throw std::runtime_error("Failed to read serialized data");
964 ser
.offset
= ProfDataDeserializer::buffer_size
- bytes_read
;
966 memmove(ser
.buffer
+ ser
.offset
, ser
.buffer
, bytes_read
);
968 return read_raw(ser
, data
, sz
);
971 StringData
*& ProfDataDeserializer::getEnt(const StringData
* p
) {
972 return stringMap
[uintptr_t(p
)];
975 ArrayData
*& ProfDataDeserializer::getEnt(const ArrayData
* p
) {
976 return arrayMap
[uintptr_t(p
)];
979 Unit
*& ProfDataDeserializer::getEnt(const Unit
* p
) {
980 return unitMap
[uintptr_t(p
)];
983 Func
*& ProfDataDeserializer::getEnt(const Func
* p
) {
984 return funcMap
[uintptr_t(p
)];
987 Class
*& ProfDataDeserializer::getEnt(const Class
* p
) {
988 return classMap
[uintptr_t(p
)];
991 const RepoAuthType::Array
*&
992 ProfDataDeserializer::getEnt(const RepoAuthType::Array
* p
) {
993 return ratMap
[uintptr_t(p
)];
996 bool ProfDataSerializer::serialize(const Unit
* unit
) {
997 return unit
->serialize();
1000 bool ProfDataSerializer::serialize(const Func
* func
) {
1001 return func
->serialize();
1004 bool ProfDataSerializer::serialize(const Class
* cls
) {
1005 return cls
->serialize();
1008 void write_string(ProfDataSerializer
& ser
, const StringData
* str
) {
1009 if (!ser
.serialize(str
)) return write_raw(ser
, str
);
1010 write_serialized_ptr(ser
, str
);
1011 uint32_t sz
= str
->size();
1013 write_raw(ser
, str
->data(), sz
);
1016 StringData
* read_string(ProfDataDeserializer
& ser
) {
1019 [&] () -> StringData
* {
1020 auto const sz
= read_raw
<uint32_t>(ser
);
1021 String s
{sz
, ReserveStringMode
{}};
1022 auto const range
= s
.bufferSlice();
1023 read_raw(ser
, range
.begin(), sz
);
1025 return makeStaticString(s
);
1030 void write_array(ProfDataSerializer
& ser
, const ArrayData
* arr
) {
1031 if (!ser
.serialize(arr
)) return write_raw(ser
, arr
);
1032 write_serialized_ptr(ser
, arr
);
1033 auto const str
= internal_serialize(VarNR(const_cast<ArrayData
*>(arr
)));
1034 uint32_t sz
= str
.size();
1036 write_raw(ser
, str
.data(), sz
);
1039 ArrayData
* read_array(ProfDataDeserializer
& ser
) {
1042 [&] () -> ArrayData
* {
1043 auto const sz
= read_raw
<uint32_t>(ser
);
1044 String s
{sz
, ReserveStringMode
{}};
1045 auto const range
= s
.bufferSlice();
1046 read_raw(ser
, range
.begin(), sz
);
1048 auto v
= unserialize_from_buffer(
1051 VariableUnserializer::Type::Internal
1053 return ArrayData::GetScalarArray(std::move(v
));
1058 void write_unit(ProfDataSerializer
& ser
, const Unit
* unit
) {
1059 if (!ser
.serialize(unit
)) return write_raw(ser
, unit
);
1060 ITRACE(2, "Unit: {}\n", unit
->filepath());
1061 write_serialized_ptr(ser
, unit
);
1062 write_string(ser
, unit
->filepath());
1065 Unit
* read_unit(ProfDataDeserializer
& ser
) {
1069 auto const filepath
= read_string(ser
);
1070 ITRACE(2, "Unit: {}\n", filepath
);
1071 auto& nativeFuncs
= Native::s_noNativeFuncs
;
1072 if (filepath
->data()[0] == '/' && filepath
->data()[1] == ':') {
1073 return lookupSyslibUnit(filepath
, nativeFuncs
);
1075 return lookupUnit(filepath
, "", nullptr, nativeFuncs
);
1080 template<typename C1
, typename C2
, typename F
>
1081 void visit_deps(const C1
& c1
, const C2
& c2
, F
& f
) {
1082 auto it
= c2
.begin();
1083 auto const DEBUG_ONLY end
= c2
.end();
1084 for (auto const& dep
: c1
) {
1086 f(dep
.get(), *it
++);
1090 void write_class(ProfDataSerializer
& ser
, const Class
* cls
) {
1092 ITRACE(2, "Class: {}\n", cls
? cls
->name() : staticEmptyString());
1094 ITRACE(2, "Class>\n");
1097 if (!cls
|| !ser
.serialize(cls
)) return write_raw(ser
, cls
);
1099 write_serialized_ptr(ser
, cls
);
1100 write_raw(ser
, cls
->preClass()->id());
1101 write_unit(ser
, cls
->preClass()->unit());
1103 jit::vector
<std::pair
<const Class
*, const StringData
*>> dependents
;
1104 auto record_dep
= [&] (const Class
* dep
, const StringData
* depName
) {
1106 if (!dep
->wasSerialized() ||
1107 !classHasPersistentRDS(dep
) ||
1108 !dep
->name()->isame(depName
)) {
1109 dependents
.emplace_back(dep
, depName
);
1112 record_dep(cls
->parent(), cls
->preClass()->parent());
1114 visit_deps(cls
->declInterfaces(), cls
->preClass()->interfaces(), record_dep
);
1116 if (cls
->preClass()->attrs() & AttrNoExpandTrait
) {
1117 for (auto const tName
: cls
->preClass()->usedTraits()) {
1118 auto const trait
= Unit::lookupUniqueClassInContext(tName
, nullptr);
1120 record_dep(trait
, tName
);
1123 visit_deps(cls
->usedTraitClasses(),
1124 cls
->preClass()->usedTraits(),
1128 write_container(ser
, dependents
,
1129 [&] (const std::pair
<const Class
*, const StringData
*> &dep
) {
1130 write_class(ser
, dep
.first
);
1131 write_string(ser
, dep
.second
);
1134 if (cls
->attrs() & AttrEnum
&&
1135 cls
->preClass()->enumBaseTy().isObject()) {
1136 if (cls
->enumBaseTy()) {
1137 write_raw(ser
, *cls
->enumBaseTy());
1139 write_raw(ser
, KindOfUninit
);
1143 if (cls
->parent() == c_Closure::classof()) {
1144 auto const func
= cls
->lookupMethod(s_invoke
.get());
1146 write_class(ser
, func
->cls());
1150 Class
* read_class(ProfDataDeserializer
& ser
) {
1151 ITRACE(2, "Class>\n");
1152 auto const ret
= deserialize(
1156 return read_class_internal(ser
);
1160 ITRACE(2, "Class: {}\n", ret
? ret
->name() : staticEmptyString());
1164 void write_func(ProfDataSerializer
& ser
, const Func
* func
) {
1166 ITRACE(2, "Func: {}\n", func
? func
->fullName() : staticEmptyString());
1168 ITRACE(2, "Func>\n");
1171 if (!func
|| !ser
.serialize(func
)) return write_raw(ser
, func
);
1173 write_serialized_ptr(ser
, func
);
1174 uint32_t fid
= func
->getFuncId();
1175 assertx(!(fid
& 0x80000000));
1176 if (func
== SystemLib::s_nullCtor
||
1177 (!func
->isMethod() && !func
->isPseudoMain() && func
->isBuiltin())) {
1178 if (func
== SystemLib::s_nullCtor
) {
1179 assertx(func
->name()->isame(s_86ctor
.get()));
1182 write_raw(ser
, fid
);
1183 return write_string(ser
, func
->name());
1185 write_raw(ser
, fid
);
1186 if (func
->isPseudoMain()) {
1187 const uint32_t zero
= 0;
1188 write_raw(ser
, zero
);
1189 write_unit(ser
, func
->unit());
1190 return write_class(ser
, func
->cls());
1193 if (func
->isMethod()) {
1194 auto const* cls
= func
->implCls();
1195 auto const nslot
= [&] () -> uint32_t {
1196 const uint32_t slot
= func
->methodSlot();
1197 if (cls
->getMethod(slot
) != func
) {
1198 if (func
->name() == s_86pinit
.get()) return k86pinitSlot
;
1199 if (func
->name() == s_86sinit
.get()) return k86sinitSlot
;
1200 if (func
->name() == s_86linit
.get()) return k86linitSlot
;
1201 cls
= getOwningClassForFunc(func
);
1202 assertx(cls
->getMethod(slot
) == func
);
1206 assertx(nslot
& 0x80000000);
1207 write_raw(ser
, nslot
);
1208 return write_class(ser
, cls
);
1211 // Ideally we'd write the func's index in its Unit; but we may not
1212 // have that after Unit::initial_merge
1213 const uint32_t off
= func
->base();
1214 assertx(off
&& !(off
& 0x80000000));
1215 write_raw(ser
, off
);
1216 write_unit(ser
, func
->unit());
1219 Func
* read_func(ProfDataDeserializer
& ser
) {
1220 ITRACE(2, "Func>\n");
1221 auto const ret
= deserialize(
1225 auto fid
= read_raw
<uint32_t>(ser
);
1226 auto const func
= [&] () -> const Func
* {
1227 if (fid
& 0x80000000) {
1229 auto const name
= read_string(ser
);
1230 if (name
->isame(s_86ctor
.get())) return SystemLib::s_nullCtor
;
1231 return Unit::lookupFunc(name
);
1233 auto const id
= read_raw
<uint32_t>(ser
);
1235 auto const unit
= read_unit(ser
);
1236 return unit
->getMain(read_class(ser
));
1238 if (id
& 0x80000000) {
1239 auto const cls
= read_class(ser
);
1240 if (id
== k86pinitSlot
) return cls
->get86pinit();
1241 if (id
== k86sinitSlot
) return cls
->get86sinit();
1242 if (id
== k86linitSlot
) return cls
->get86linit();
1243 const Slot slot
= ~id
;
1244 return cls
->getMethod(slot
);
1246 auto const unit
= read_unit(ser
);
1247 for (auto const f
: unit
->funcs()) {
1248 if (f
->base() == id
) {
1250 auto const handle
= f
->funcHandle();
1251 if (!rds::isPersistentHandle(handle
) &&
1252 (!rds::isHandleInit(handle
, rds::NormalTag
{}) ||
1253 rds::handleToRef
<LowPtr
<Func
>,
1254 rds::Mode::Normal
>(handle
).get() != f
)) {
1255 rds::uninitHandle(handle
);
1256 Unit::defFunc(f
, false);
1263 ser
.recordFid(fid
, func
->getFuncId());
1264 return const_cast<Func
*>(func
);
1267 ITRACE(2, "Func: {}\n", ret
? ret
->fullName() : staticEmptyString());
1271 std::string
serializeProfData(const std::string
& filename
) {
1273 ProfDataSerializer ser
{filename
};
1275 write_raw(ser
, Repo::get().global().Signature
);
1276 auto schema
= repoSchemaId();
1277 write_raw(ser
, schema
.size());
1278 write_raw(ser
, schema
.begin(), schema
.size());
1280 auto host
= Process::GetHostName();
1281 write_raw(ser
, host
.size());
1282 write_raw(ser
, &host
[0], host
.size());
1283 write_raw(ser
, TimeStamp::Current());
1285 Func::s_treadmill
= true;
1287 hphp_session_init(Treadmill::SessionKind::ProfData
);
1288 requestInitProfData();
1291 requestExitProfData();
1292 hphp_context_exit();
1293 hphp_session_exit();
1295 Func::s_treadmill
= false;
1298 InstanceBits::init();
1299 InstanceBits::serialize(ser
);
1300 write_global_array_map(ser
);
1302 auto const pd
= profData();
1303 write_prof_data(ser
, pd
);
1305 write_target_profiles(ser
);
1307 // We've written everything directly referenced by the profile
1308 // data, but jitted code might still use Classes and TypeAliasReqs
1309 // that haven't been otherwise mentioned (eg VerifyParamType,
1310 // InstanceOfD etc).
1311 write_classes_and_type_aliases(ser
, pd
);
1316 } catch (std::runtime_error
& err
) {
1317 return folly::sformat("Failed to serialize profile data: {}", err
.what());
1321 std::string
deserializeProfData(const std::string
& filename
, int numWorkers
) {
1323 ProfDataDeserializer ser
{filename
};
1325 auto signature
= read_raw
<decltype(Repo::get().global().Signature
)>(ser
);
1326 if (signature
!= Repo::get().global().Signature
) {
1327 throw std::runtime_error("Mismatched repo-schema");
1329 auto size
= read_raw
<size_t>(ser
);
1331 schema
.resize(size
);
1332 read_raw(ser
, &schema
[0], size
);
1333 if (schema
!= repoSchemaId()) {
1334 throw std::runtime_error("Mismatched repo-schema");
1337 size
= read_raw
<size_t>(ser
);
1338 ProfDataDeserializer::s_buildHost
.resize(size
);
1339 read_raw(ser
, &ProfDataDeserializer::s_buildHost
[0], size
);
1342 read_raw(ser
, buildTime
);
1343 auto const currTime
= TimeStamp::Current();
1344 if (buildTime
<= currTime
- 3600 * RuntimeOption::ProfDataTTLHours
) {
1345 throw std::runtime_error(
1346 "Stale profile data (check Eval.ProfDataTTLHours)");
1347 } else if (buildTime
>= currTime
) {
1348 throw std::runtime_error("profile data dumped in the future?");
1350 ProfDataDeserializer::s_buildTime
= buildTime
;
1352 InstanceBits::deserialize(ser
);
1353 read_global_array_map(ser
);
1355 ProfData::Session pds
;
1356 auto const pd
= profData();
1357 read_prof_data(ser
, pd
);
1358 pd
->setDeserialized();
1360 read_target_profiles(ser
);
1362 read_classes_and_type_aliases(ser
);
1364 always_assert(ser
.done());
1366 // During deserialization we didn't merge the loaded units because
1367 // we wanted to pick and choose the hot Funcs and Classes. But we
1368 // need to merge them before we start serving traffic to ensure we
1369 // don't have inconsistentcies (eg a persistent memoized Func
1370 // wrapper might have been merged, while its implementation was
1371 // not; since the implementation has an internal name, there won't
1372 // be an autoload entry for it, so unless something else causes
1373 // the unit to be loaded the implementation might never get pulled
1374 // in (resulting in fatals when the wrapper tries to call it).
1375 merge_loaded_units(numWorkers
);
1378 } catch (std::runtime_error
& err
) {
1379 return folly::sformat("Failed to deserialize profile data {}: {}",
1380 filename
, err
.what());
1384 //////////////////////////////////////////////////////////////////////