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/unit-cache.h"
23 #include "hphp/runtime/base/variable-serializer.h"
25 #include "hphp/runtime/ext/std/ext_std_closure.h"
27 #include "hphp/runtime/vm/class.h"
28 #include "hphp/runtime/vm/func.h"
29 #include "hphp/runtime/vm/jit/array-kind-profile.h"
30 #include "hphp/runtime/vm/jit/array-offset-profile.h"
31 #include "hphp/runtime/vm/jit/cls-cns-profile.h"
32 #include "hphp/runtime/vm/jit/meth-profile.h"
33 #include "hphp/runtime/vm/jit/profile-refcount.h"
34 #include "hphp/runtime/vm/jit/release-vv-profile.h"
35 #include "hphp/runtime/vm/jit/switch-profile.h"
36 #include "hphp/runtime/vm/jit/type-profile.h"
37 #include "hphp/runtime/vm/jit/containers.h"
38 #include "hphp/runtime/vm/jit/prof-data.h"
39 #include "hphp/runtime/vm/jit/trans-cfg.h"
40 #include "hphp/runtime/vm/unit.h"
44 namespace HPHP
{ namespace jit
{
45 //////////////////////////////////////////////////////////////////////
48 //////////////////////////////////////////////////////////////////////
52 StaticString
s_invoke("__invoke");
53 StaticString
s_86ctor("86ctor");
54 StaticString
s_86pinit("86pinit");
55 StaticString
s_86sinit("86sinit");
57 constexpr uint32_t k86pinitSlot
= 0x80000000u
;
58 constexpr uint32_t k86sinitSlot
= 0x80000001u
;
61 auto deserialize(ProfDataDeserializer
&ser
, F
&& f
) -> decltype(f()) {
62 using T
= decltype(f());
63 auto const ptr
= read_raw
<uintptr_t>(ser
);
66 auto& ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
- 1));
72 auto const ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
));
77 void write_serialized_ptr(ProfDataSerializer
& ser
, const void* p
) {
78 auto const ptr_as_int
= reinterpret_cast<uintptr_t>(p
);
79 assertx(!(ptr_as_int
& 1));
80 write_raw(ser
, ptr_as_int
| 1);
83 void write_raw_ptr(ProfDataSerializer
& ser
, const void* p
) {
84 auto const ptr_as_int
= reinterpret_cast<uintptr_t>(p
);
85 assertx(!(ptr_as_int
& 1));
86 write_raw(ser
, ptr_as_int
);
90 * Helper functions so that the function passed to write_container can
91 * take a serializer as a parameter, or skip it (eg if its a lambda
92 * which is already capturing the serializer).
94 template<typename S
, typename T
, typename F
>
95 auto call(F
& f
, S
& ser
, const T
& t
) -> decltype(f(ser
, t
)) {
99 template<typename S
, typename T
, typename F
>
100 auto call(F
& f
, S
&, const T
& t
) -> decltype(f(t
)) {
104 template<typename C
, typename F
>
105 void write_container(ProfDataSerializer
& ser
, const C
& cont
, F f
) {
106 write_raw(ser
, safe_cast
<uint32_t>(cont
.size()));
107 for (auto const &elm
: cont
) {
113 void read_container(ProfDataDeserializer
& ser
, F f
) {
114 auto sz
= read_raw
<uint32_t>(ser
);
115 while (sz
--) { f(); }
118 void write_srckey(ProfDataSerializer
& ser
, SrcKey sk
) {
119 ITRACE(2, "SrcKey>\n");
120 if (ser
.serialize(sk
.func())) {
122 write_raw(ser
, uintptr_t(-1));
123 write_func(ser
, sk
.func());
125 write_raw(ser
, sk
.toAtomicInt());
126 ITRACE(2, "SrcKey: {}\n", show(sk
));
129 SrcKey
read_srckey(ProfDataDeserializer
& ser
) {
130 ITRACE(2, "SrcKey>\n");
131 auto orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
132 if (orig
== uintptr_t(-1)) {
135 orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
137 auto const id
= SrcKey::fromAtomicInt(orig
).funcID();
138 assertx(uint32_t(orig
) == id
);
139 auto const sk
= SrcKey::fromAtomicInt(orig
- id
+ ser
.getFid(id
));
140 ITRACE(2, "SrcKey: {}\n", show(sk
));
144 void write_reffiness_pred(ProfDataSerializer
& ser
,
145 const RegionDesc::ReffinessPred
& pred
) {
146 write_container(ser
, pred
.mask
, write_raw
<bool>);
147 write_container(ser
, pred
.vals
, write_raw
<bool>);
148 write_raw(ser
, pred
.arSpOffset
);
151 RegionDesc::ReffinessPred
read_reffiness_pred(ProfDataDeserializer
& ser
) {
152 RegionDesc::ReffinessPred ret
;
154 [&] { ret
.mask
.push_back(read_raw
<bool>(ser
)); });
156 [&] { ret
.vals
.push_back(read_raw
<bool>(ser
)); });
157 ret
.arSpOffset
= read_raw
<decltype(ret
.arSpOffset
)>(ser
);
161 void write_type(ProfDataSerializer
& ser
, Type t
) {
165 Type
read_type(ProfDataDeserializer
& ser
) {
166 return Type::deserialize(ser
);
169 void write_typed_location(ProfDataSerializer
& ser
,
170 const RegionDesc::TypedLocation
& loc
) {
171 write_raw(ser
, loc
.location
);
172 write_type(ser
, loc
.type
);
175 void write_guarded_location(ProfDataSerializer
& ser
,
176 const RegionDesc::GuardedLocation
& loc
) {
177 write_raw(ser
, loc
.location
);
178 write_type(ser
, loc
.type
);
179 write_raw(ser
, loc
.category
);
182 RegionDesc::TypedLocation
read_typed_location(ProfDataDeserializer
& ser
) {
183 auto const location
= read_raw
<Location
>(ser
);
184 auto const type
= read_type(ser
);
185 return { location
, type
};
188 RegionDesc::GuardedLocation
read_guarded_location(ProfDataDeserializer
& ser
) {
189 auto const location
= read_raw
<Location
>(ser
);
190 auto const type
= read_type(ser
);
191 auto const cat
= read_raw
<DataTypeCategory
>(ser
);
192 return { location
, type
, cat
};
195 void write_global_array_map(ProfDataSerializer
& ser
) {
196 write_container(ser
, globalArrayTypeTable(),
197 write_raw
<const RepoAuthType::Array
*>);
200 void read_global_array_map(ProfDataDeserializer
& ser
) {
201 auto sz DEBUG_ONLY
= read_raw
<uint32_t>(ser
);
202 assertx(sz
== globalArrayTypeTable().size());
203 for (auto arr
: globalArrayTypeTable()) {
204 auto const orig
= read_raw
<const RepoAuthType::Array
*>(ser
);
205 ser
.recordRat(orig
, arr
);
209 void write_region_block(ProfDataSerializer
& ser
,
210 const RegionDesc::BlockPtr
& block
) {
211 write_raw(ser
, block
->id());
212 write_srckey(ser
, block
->start());
213 write_raw(ser
, block
->length());
214 write_raw(ser
, block
->initialSpOffset());
215 write_raw(ser
, block
->profTransID());
216 write_container(ser
, block
->typePredictions(), write_typed_location
);
217 write_container(ser
, block
->typePreConditions(), write_guarded_location
);
218 write_container(ser
, block
->paramByRefs(),
219 [] (ProfDataSerializer
& s
, std::pair
<SrcKey
, bool> byRef
) {
220 write_srckey(s
, byRef
.first
);
221 write_raw(s
, byRef
.second
);
223 write_container(ser
, block
->reffinessPreds(), write_reffiness_pred
);
224 write_container(ser
, block
->knownFuncs(),
225 [] (ProfDataSerializer
& s
,
226 std::pair
<SrcKey
, const Func
*> knownFunc
) {
227 write_srckey(s
, knownFunc
.first
);
228 write_func(s
, knownFunc
.second
);
230 write_container(ser
, block
->postConds().changed
, write_typed_location
);
231 write_container(ser
, block
->postConds().refined
, write_typed_location
);
234 RegionDesc::BlockPtr
read_region_block(ProfDataDeserializer
& ser
) {
235 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
236 auto const start
= read_srckey(ser
);
237 auto const length
= read_raw
<int>(ser
);
238 auto const initialSpOffset
= read_raw
<FPInvOffset
>(ser
);
240 auto const block
= std::make_shared
<RegionDesc::Block
>(id
,
248 block
->setProfTransID(read_raw
<TransID
>(ser
));
252 block
->addPredicted(read_typed_location(ser
));
257 block
->addPreCondition(read_guarded_location(ser
));
262 auto const sk
= read_srckey(ser
);
263 auto const byRef
= read_raw
<bool>(ser
);
264 block
->setParamByRef(sk
, byRef
);
269 block
->addReffinessPred(read_reffiness_pred(ser
));
274 auto const sk
= read_srckey(ser
);
275 auto const func
= read_func(ser
);
276 block
->setKnownFunc(sk
, func
);
279 PostConditions postConds
;
282 postConds
.changed
.push_back(read_typed_location(ser
));
286 postConds
.refined
.push_back(read_typed_location(ser
));
288 block
->setPostConds(postConds
);
292 void write_region_desc(ProfDataSerializer
& ser
, const RegionDesc
* rd
) {
293 write_container(ser
, rd
->blocks(), write_region_block
);
294 write_container(ser
, findPredTrans(rd
->entry()->id(), profData()),
295 [&] (RegionDesc::BlockId id
) {
298 write_container(ser
, rd
->blocks(),
299 [&] (const RegionDesc::BlockPtr
& b
) {
300 auto const bid
= b
->id();
302 assertx(rd
->succs(bid
).empty());
303 assertx(rd
->preds(bid
).empty());
304 assertx(rd
->merged(bid
).empty());
305 auto const pr
= rd
->prevRetrans(bid
);
306 write_raw(ser
, pr
? pr
.value() : kInvalidTransID
);
307 auto const nr
= rd
->nextRetrans(bid
);
308 write_raw(ser
, nr
? nr
.value() : kInvalidTransID
);
310 write_type(ser
, rd
->inlineCtxType());
311 write_container(ser
, rd
->inlineInputTypes(), write_type
);
314 RegionDescPtr
read_region_desc(ProfDataDeserializer
& ser
) {
315 auto ret
= std::make_shared
<RegionDesc
>();
316 read_container(ser
, [&] { ret
->addBlock(read_region_block(ser
)); });
317 RegionDesc::BlockIdSet incoming
;
319 [&] { incoming
.insert(read_raw
<RegionDesc::BlockId
>(ser
)); });
320 ret
->incoming(std::move(incoming
));
324 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
325 auto const pr
= read_raw
<RegionDesc::BlockId
>(ser
);
326 auto const nr
= read_raw
<RegionDesc::BlockId
>(ser
);
327 if (pr
!= kInvalidTransID
) {
328 ret
->setNextRetrans(pr
, id
);
330 if (nr
!= kInvalidTransID
) {
331 ret
->setNextRetrans(id
, nr
);
334 auto const ty
= read_type(ser
);
335 std::vector
<Type
> args
;
338 args
.push_back(read_type(ser
));
340 ret
->setInlineContext(ty
, args
);
344 void write_prof_trans_rec(ProfDataSerializer
& ser
, const ProfTransRec
* ptr
) {
345 if (!ptr
) return write_raw(ser
, TransKind
{});
346 write_raw(ser
, ptr
->kind());
347 write_srckey(ser
, ptr
->srcKey());
348 if (ptr
->kind() == TransKind::Profile
) {
349 write_raw(ser
, ptr
->lastBcOff());
350 write_region_desc(ser
, ptr
->region().get());
352 // No need to preserve callers; there won't be any when we use this data.
353 write_raw(ser
, ptr
->prologueArgs());
357 std::unique_ptr
<ProfTransRec
> read_prof_trans_rec(ProfDataDeserializer
& ser
) {
358 auto const kind
= read_raw
<TransKind
>(ser
);
359 static_assert(TransKind::Profile
!= TransKind
{} &&
360 TransKind::ProfPrologue
!= TransKind
{},
361 "Profile and ProfPrologue must not be zero");
362 if (kind
== TransKind
{}) return nullptr;
363 auto const sk
= read_srckey(ser
);
364 if (kind
== TransKind::Profile
) {
365 auto const lastBcOff
= read_raw
<Offset
>(ser
);
366 auto const region
= read_region_desc(ser
);
367 return std::make_unique
<ProfTransRec
>(lastBcOff
, sk
, region
);
370 return std::make_unique
<ProfTransRec
>(sk
, read_raw
<int>(ser
));
373 void write_profiled_funcs(ProfDataSerializer
& ser
, ProfData
* pd
) {
374 auto const maxFuncId
= pd
->maxProfilingFuncId();
376 for (FuncId fid
= 0; fid
<= maxFuncId
; fid
++) {
377 if (!Func::isFuncIdValid(fid
) || !pd
->profiling(fid
)) continue;
378 write_func(ser
, Func::fromFuncId(fid
));
381 write_raw(ser
, uintptr_t{});
384 void read_profiled_funcs(ProfDataDeserializer
& ser
, ProfData
* pd
) {
385 while (auto const func
= read_func(ser
)) {
386 pd
->setProfiling(func
->getFuncId());
390 void write_prof_data(ProfDataSerializer
& ser
, ProfData
* pd
) {
391 write_profiled_funcs(ser
, pd
);
393 write_raw(ser
, pd
->counterDefault());
395 [&] (const ProfTransRec
* ptr
) {
396 auto const transID
= ptr
->isProfile() ?
397 ptr
->region()->entry()->profTransID() :
398 pd
->proflogueTransId(ptr
->func(), ptr
->prologueArgs());
399 write_raw(ser
, transID
);
400 write_prof_trans_rec(ser
, ptr
);
401 // forEachTransRec already grabs a read lock, and we're not
402 // going to add a *new* counter here (so we don't need a write
404 write_raw(ser
, *pd
->transCounterAddrNoLock(transID
));
407 write_raw(ser
, kInvalidTransID
);
410 void read_prof_data(ProfDataDeserializer
& ser
, ProfData
* pd
) {
411 read_profiled_funcs(ser
, pd
);
413 pd
->resetCounters(read_raw
<int64_t>(ser
));
415 auto const transID
= read_raw
<TransID
>(ser
);
416 if (transID
== kInvalidTransID
) break;
417 pd
->addProfTrans(transID
, read_prof_trans_rec(ser
));
418 *pd
->transCounterAddr(transID
) = read_raw
<int64_t>(ser
);
423 auto write_impl(ProfDataSerializer
& ser
, const T
& out
, bool) ->
424 decltype(std::declval
<T
&>().serialize(ser
),void()) {
429 void write_impl(ProfDataSerializer
& ser
, const T
& out
, int) {
434 void write_maybe_serializable(ProfDataSerializer
& ser
, const T
& out
) {
435 write_impl(ser
, out
, false);
438 struct TargetProfileVisitor
: boost::static_visitor
<void> {
439 TargetProfileVisitor(ProfDataSerializer
& ser
,
440 const rds::Symbol
& sym
,
449 void process(T
& out
, const StringData
* name
) {
450 write_raw(ser
, size
);
451 write_string(ser
, name
);
453 TargetProfile
<T
>::reduce(out
, handle
, size
);
454 if (size
== sizeof(T
)) {
455 write_maybe_serializable(ser
, out
);
457 write_raw(ser
, &out
, size
);
461 template<typename T
> void operator()(const T
&) {}
463 void operator()(const rds::Profile
<T
>& pt
) {
464 if (size
== sizeof(T
)) {
466 process(out
, pt
.name
.get());
468 auto const mem
= calloc(1, size
);
469 SCOPE_EXIT
{ free(mem
); };
470 process(*reinterpret_cast<T
*>(mem
), pt
.name
.get());
474 ProfDataSerializer
& ser
;
475 const rds::Symbol
& sym
;
480 void write_target_profiles(ProfDataSerializer
& ser
) {
482 [&] (const rds::Symbol
& symbol
, rds::Handle handle
, uint32_t size
) {
483 TargetProfileVisitor
tv(ser
, symbol
, handle
, size
);
484 boost::apply_visitor(tv
, symbol
);
487 write_raw(ser
, uint32_t{});
491 auto read_impl(ProfDataDeserializer
& ser
, T
& out
, bool) ->
492 decltype(out
.deserialize(ser
),void()) {
493 out
.deserialize(ser
);
497 void read_impl(ProfDataDeserializer
& ser
, T
& out
, int) {
502 void read_maybe_serializable(ProfDataDeserializer
& ser
, T
& out
) {
503 read_impl(ser
, out
, false);
506 struct SymbolFixup
: boost::static_visitor
<void> {
507 SymbolFixup(ProfDataDeserializer
& ser
, StringData
* name
, uint32_t size
) :
508 ser
{ser
}, name
{name
}, size
{size
} {}
510 template<typename T
> void operator()(T
&) { always_assert(false); }
512 void operator()(rds::Profile
<T
>& pt
) {
513 TargetProfile
<T
> prof(pt
.transId
,
519 if (size
== sizeof(T
)) {
520 read_maybe_serializable(ser
, prof
.value());
522 read_raw(ser
, &prof
.value(), size
);
526 ProfDataDeserializer
& ser
;
528 // The size of the original rds allocation.
532 void read_target_profiles(ProfDataDeserializer
& ser
) {
534 auto const size
= read_raw
<uint32_t>(ser
);
536 auto const name
= read_string(ser
);
537 auto sym
= read_raw
<rds::Symbol
>(ser
);
538 auto sf
= SymbolFixup
{ser
, name
, size
};
539 boost::apply_visitor(sf
, sym
);
543 ////////////////////////////////////////////////////////////////////////////////
546 ProfDataSerializer::ProfDataSerializer(const std::string
& name
) : m_ofs(name
) {
547 if (!m_ofs
.good()) throw std::runtime_error("Failed to open: " + name
);
550 ProfDataDeserializer::ProfDataDeserializer(const std::string
& name
)
552 if (!m_ifs
.good()) throw std::runtime_error("Failed to open: " + name
);
555 void write_raw(ProfDataSerializer
& ser
, const void* data
, size_t sz
) {
556 if (!ser
.m_ofs
.write(static_cast<const char*>(data
), sz
).good()) {
557 throw std::runtime_error("Failed to write serialized data");
561 void read_raw(ProfDataDeserializer
& ser
, void* data
, size_t sz
) {
562 if (!ser
.m_ifs
.read(static_cast<char*>(data
), sz
).good()) {
563 throw std::runtime_error("Failed to read serialized data");
567 StringData
*& ProfDataDeserializer::getEnt(const StringData
* p
) {
568 return stringMap
[uintptr_t(p
)];
571 ArrayData
*& ProfDataDeserializer::getEnt(const ArrayData
* p
) {
572 return arrayMap
[uintptr_t(p
)];
575 Unit
*& ProfDataDeserializer::getEnt(const Unit
* p
) {
576 return unitMap
[uintptr_t(p
)];
579 Func
*& ProfDataDeserializer::getEnt(const Func
* p
) {
580 return funcMap
[uintptr_t(p
)];
583 Class
*& ProfDataDeserializer::getEnt(const Class
* p
) {
584 return classMap
[uintptr_t(p
)];
587 const RepoAuthType::Array
*&
588 ProfDataDeserializer::getEnt(const RepoAuthType::Array
* p
) {
589 return ratMap
[uintptr_t(p
)];
592 bool ProfDataSerializer::serialize(const Unit
* unit
) {
593 return unit
->serialize();
596 bool ProfDataSerializer::serialize(const Func
* func
) {
597 return func
->serialize();
600 bool ProfDataSerializer::serialize(const Class
* cls
) {
601 return cls
->serialize();
604 void write_string(ProfDataSerializer
& ser
, const StringData
* str
) {
605 if (!ser
.serialize(str
)) return write_raw(ser
, str
);
606 write_serialized_ptr(ser
, str
);
607 uint32_t sz
= str
->size();
609 write_raw(ser
, str
->data(), sz
);
612 StringData
* read_string(ProfDataDeserializer
& ser
) {
615 [&] () -> StringData
* {
616 auto const sz
= read_raw
<uint32_t>(ser
);
617 String s
{sz
, ReserveStringMode
{}};
618 auto const range
= s
.bufferSlice();
619 read_raw(ser
, range
.begin(), sz
);
621 return makeStaticString(s
);
626 void write_array(ProfDataSerializer
& ser
, const ArrayData
* arr
) {
627 if (!ser
.serialize(arr
)) return write_raw(ser
, arr
);
628 write_serialized_ptr(ser
, arr
);
629 auto const str
= internal_serialize(VarNR(const_cast<ArrayData
*>(arr
)));
630 uint32_t sz
= str
.size();
632 write_raw(ser
, str
.data(), sz
);
635 ArrayData
* read_array(ProfDataDeserializer
& ser
) {
638 [&] () -> ArrayData
* {
639 auto const sz
= read_raw
<uint32_t>(ser
);
640 String s
{sz
, ReserveStringMode
{}};
641 auto const range
= s
.bufferSlice();
642 read_raw(ser
, range
.begin(), sz
);
644 auto v
= unserialize_from_buffer(
647 VariableUnserializer::Type::Internal
649 return ArrayData::GetScalarArray(std::move(v
));
654 void write_unit(ProfDataSerializer
& ser
, const Unit
* unit
) {
655 if (!ser
.serialize(unit
)) return write_raw(ser
, unit
);
656 ITRACE(2, "Unit: {}\n", unit
->filepath());
657 write_serialized_ptr(ser
, unit
);
658 write_string(ser
, unit
->filepath());
661 Unit
* read_unit(ProfDataDeserializer
& ser
) {
665 auto const filepath
= read_string(ser
);
666 ITRACE(2, "Unit: {}\n", filepath
);
667 return lookupUnit(filepath
, "", nullptr);
672 template<typename C1
, typename C2
, typename F
>
673 void visit_deps(const C1
& c1
, const C2
& c2
, F
& f
) {
674 auto it
= c2
.begin();
675 auto const DEBUG_ONLY end
= c2
.end();
676 for (auto const& dep
: c1
) {
682 void write_class(ProfDataSerializer
& ser
, const Class
* cls
) {
684 ITRACE(2, "Class: {}\n", cls
? cls
->name() : staticEmptyString());
686 ITRACE(2, "Class>\n");
689 if (!cls
|| !ser
.serialize(cls
)) return write_raw(ser
, cls
);
691 write_serialized_ptr(ser
, cls
);
692 write_raw(ser
, cls
->preClass()->id());
693 write_unit(ser
, cls
->preClass()->unit());
695 jit::vector
<std::pair
<const Class
*, const StringData
*>> dependents
;
696 auto record_dep
= [&] (const Class
* dep
, const StringData
* depName
) {
698 if (!dep
->wasSerialized() ||
699 !classHasPersistentRDS(dep
) ||
700 !dep
->name()->isame(depName
)) {
701 dependents
.emplace_back(dep
, depName
);
704 record_dep(cls
->parent(), cls
->preClass()->parent());
706 visit_deps(cls
->declInterfaces(), cls
->preClass()->interfaces(), record_dep
);
708 if (cls
->preClass()->attrs() & AttrNoExpandTrait
) {
709 for (auto const tName
: cls
->preClass()->usedTraits()) {
710 auto const trait
= Unit::lookupUniqueClassInContext(tName
, nullptr);
712 record_dep(trait
, tName
);
715 visit_deps(cls
->usedTraitClasses(),
716 cls
->preClass()->usedTraits(),
720 write_container(ser
, dependents
,
721 [&] (const std::pair
<const Class
*, const StringData
*> &dep
) {
722 write_class(ser
, dep
.first
);
723 write_string(ser
, dep
.second
);
726 if (cls
->parent() == c_Closure::classof()) {
727 auto const func
= cls
->lookupMethod(s_invoke
.get());
729 write_class(ser
, func
->cls());
733 Class
* read_class(ProfDataDeserializer
& ser
) {
734 ITRACE(2, "Class>\n");
735 auto const ret
= deserialize(
739 auto const id
= read_raw
<decltype(std::declval
<PreClass
*>()->id())>(ser
);
740 auto const unit
= read_unit(ser
);
744 auto const dep
= read_class(ser
);
745 auto const ne
= dep
->preClass()->namedEntity();
746 // if its not persistent, make sure that dep
747 // is the active class for this NamedEntity
748 assertx(ne
->m_cachedClass
.bound());
749 if (ne
->m_cachedClass
.isNormal()) {
750 ne
->setCachedClass(dep
);
752 auto const depName
= read_string(ser
);
753 if (!dep
->name()->isame(depName
)) {
754 // this dependent was referred to via a
755 // class_alias, so we need to make sure
756 // *that* points to the class too
757 auto const aliasNe
= NamedEntity::get(depName
);
758 aliasNe
->m_cachedClass
.bind();
759 if (aliasNe
->m_cachedClass
.isNormal()) {
760 aliasNe
->m_cachedClass
.markUninit();
762 aliasNe
->setCachedClass(dep
);
766 auto const preClass
= unit
->lookupPreClassId(id
);
767 auto const ne
= preClass
->namedEntity();
768 // If its not persistent, make sure its NamedEntity is
769 // unbound, ready for DefClass
770 if (ne
->m_cachedClass
.bound() &&
771 ne
->m_cachedClass
.isNormal()) {
772 ne
->m_cachedClass
.markUninit();
775 auto const cls
= Unit::defClass(preClass
, true);
776 if (cls
->pinitVec().size()) cls
->initPropHandle();
777 if (cls
->numStaticProperties()) cls
->initSPropHandles();
779 if (cls
->parent() == c_Closure::classof()) {
780 auto const ctx
= read_class(ser
);
781 if (ctx
!= cls
) return cls
->rescope(ctx
);
787 ITRACE(2, "Class: {}\n", ret
? ret
->name() : staticEmptyString());
791 void write_func(ProfDataSerializer
& ser
, const Func
* func
) {
793 ITRACE(2, "Func: {}\n", func
? func
->fullName() : staticEmptyString());
795 ITRACE(2, "Func>\n");
798 if (!func
|| !ser
.serialize(func
)) return write_raw(ser
, func
);
800 write_serialized_ptr(ser
, func
);
801 uint32_t fid
= func
->getFuncId();
802 assertx(!(fid
& 0x80000000));
803 if (func
== SystemLib::s_nullCtor
||
804 (!func
->isMethod() && !func
->isPseudoMain() && func
->isBuiltin())) {
805 if (func
== SystemLib::s_nullCtor
) {
806 assertx(func
->name()->isame(s_86ctor
.get()));
810 return write_string(ser
, func
->name());
813 if (func
->isPseudoMain()) {
814 const uint32_t zero
= 0;
815 write_raw(ser
, zero
);
816 write_unit(ser
, func
->unit());
817 return write_class(ser
, func
->cls());
820 if (func
->isMethod()) {
821 auto const* cls
= func
->implCls();
822 auto const nslot
= [&] () -> uint32_t {
823 const uint32_t slot
= func
->methodSlot();
824 if (cls
->getMethod(slot
) != func
) {
825 if (func
->name() == s_86pinit
.get()) return k86pinitSlot
;
826 if (func
->name() == s_86sinit
.get()) return k86sinitSlot
;
827 cls
= getOwningClassForFunc(func
);
828 assertx(cls
->getMethod(slot
) == func
);
832 assertx(nslot
& 0x80000000);
833 write_raw(ser
, nslot
);
834 return write_class(ser
, cls
);
837 // Ideally we'd write the func's index in its Unit; but we may not
838 // have that after Unit::initial_merge
839 const uint32_t off
= func
->base();
840 assertx(off
&& !(off
& 0x80000000));
842 write_unit(ser
, func
->unit());
845 Func
* read_func(ProfDataDeserializer
& ser
) {
846 ITRACE(2, "Func>\n");
847 auto const ret
= deserialize(
851 auto fid
= read_raw
<uint32_t>(ser
);
852 auto const func
= [&] () -> const Func
* {
853 if (fid
& 0x80000000) {
855 auto const name
= read_string(ser
);
856 if (name
->isame(s_86ctor
.get())) return SystemLib::s_nullCtor
;
857 return Unit::lookupFunc(name
);
859 auto const id
= read_raw
<uint32_t>(ser
);
861 auto const unit
= read_unit(ser
);
862 return unit
->getMain(read_class(ser
));
864 if (id
& 0x80000000) {
865 auto const cls
= read_class(ser
);
866 if (id
== k86pinitSlot
) return cls
->get86pinit();
867 if (id
== k86sinitSlot
) return cls
->get86sinit();
868 const Slot slot
= ~id
;
869 return cls
->getMethod(slot
);
871 auto const unit
= read_unit(ser
);
872 for (auto const f
: unit
->funcs()) {
873 if (f
->base() == id
) {
875 if (!rds::isPersistentHandle(f
->funcHandle()) &&
876 (!rds::isHandleInit(f
->funcHandle(), rds::NormalTag
{}) ||
877 rds::handleToRef
<LowPtr
<Func
>>(f
->funcHandle()).get() != f
)) {
878 rds::uninitHandle(f
->funcHandle());
879 Unit::defFunc(f
, false);
886 ser
.recordFid(fid
, func
->getFuncId());
887 return const_cast<Func
*>(func
);
890 ITRACE(2, "Func: {}\n", ret
? ret
->fullName() : staticEmptyString());
894 bool serializeProfData(const std::string
& filename
) {
896 ProfDataSerializer ser
{filename
};
898 Func::s_treadmill
= true;
901 requestInitProfData();
904 requestExitProfData();
908 Func::s_treadmill
= false;
911 write_global_array_map(ser
);
913 auto const pd
= profData();
914 write_prof_data(ser
, pd
);
916 write_target_profiles(ser
);
919 } catch (std::runtime_error
& err
) {
920 FTRACE(1, "serializeProfData - Failed: {}\n", err
.what());
925 bool deserializeProfData(const std::string
& filename
) {
927 ProfDataDeserializer ser
{filename
};
929 read_global_array_map(ser
);
931 ProfData::Session pds
;
932 auto const pd
= profData();
933 read_prof_data(ser
, pd
);
934 pd
->setDeserialized();
936 read_target_profiles(ser
);
939 } catch (std::runtime_error
& err
) {
940 FTRACE(1, "deserializeProfData - Failed: {}\n", err
.what());
945 //////////////////////////////////////////////////////////////////////