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/repo.h"
41 #include "hphp/runtime/vm/repo-global-data.h"
42 #include "hphp/runtime/vm/unit.h"
44 #include "hphp/util/build-info.h"
46 #include <folly/portability/Unistd.h>
48 namespace HPHP
{ namespace jit
{
49 //////////////////////////////////////////////////////////////////////
52 //////////////////////////////////////////////////////////////////////
56 StaticString
s_invoke("__invoke");
57 StaticString
s_86ctor("86ctor");
58 StaticString
s_86pinit("86pinit");
59 StaticString
s_86sinit("86sinit");
61 constexpr uint32_t k86pinitSlot
= 0x80000000u
;
62 constexpr uint32_t k86sinitSlot
= 0x80000001u
;
65 auto deserialize(ProfDataDeserializer
&ser
, F
&& f
) -> decltype(f()) {
66 using T
= decltype(f());
67 auto const ptr
= read_raw
<uintptr_t>(ser
);
70 auto& ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
- 1));
73 ITRACE(3, "0x{:08x} => 0x{:08x}\n",
74 ptr
- 1, reinterpret_cast<uintptr_t>(ent
));
78 auto const ent
= ser
.getEnt(reinterpret_cast<T
>(ptr
));
83 void write_serialized_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
| 1);
89 void write_raw_ptr(ProfDataSerializer
& ser
, const void* p
) {
90 auto const ptr_as_int
= reinterpret_cast<uintptr_t>(p
);
91 assertx(!(ptr_as_int
& 1));
92 write_raw(ser
, ptr_as_int
);
96 * Helper functions so that the function passed to write_container can
97 * take a serializer as a parameter, or skip it (eg if its a lambda
98 * which is already capturing the serializer).
100 template<typename S
, typename T
, typename F
>
101 auto call(F
& f
, S
& ser
, const T
& t
) -> decltype(f(ser
, t
)) {
105 template<typename S
, typename T
, typename F
>
106 auto call(F
& f
, S
&, const T
& t
) -> decltype(f(t
)) {
110 template<typename C
, typename F
>
111 void write_container(ProfDataSerializer
& ser
, const C
& cont
, F f
) {
112 write_raw(ser
, safe_cast
<uint32_t>(cont
.size()));
113 for (auto const &elm
: cont
) {
119 void read_container(ProfDataDeserializer
& ser
, F f
) {
120 auto sz
= read_raw
<uint32_t>(ser
);
121 while (sz
--) { f(); }
124 void write_srckey(ProfDataSerializer
& ser
, SrcKey sk
) {
125 ITRACE(2, "SrcKey>\n");
126 if (ser
.serialize(sk
.func())) {
128 write_raw(ser
, uintptr_t(-1));
129 write_func(ser
, sk
.func());
131 write_raw(ser
, sk
.toAtomicInt());
132 ITRACE(2, "SrcKey: {}\n", show(sk
));
135 SrcKey
read_srckey(ProfDataDeserializer
& ser
) {
136 ITRACE(2, "SrcKey>\n");
137 auto orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
138 if (orig
== uintptr_t(-1)) {
141 orig
= read_raw
<SrcKey::AtomicInt
>(ser
);
143 auto const id
= SrcKey::fromAtomicInt(orig
).funcID();
144 assertx(uint32_t(orig
) == id
);
145 auto const sk
= SrcKey::fromAtomicInt(orig
- id
+ ser
.getFid(id
));
146 ITRACE(2, "SrcKey: {}\n", show(sk
));
150 void write_reffiness_pred(ProfDataSerializer
& ser
,
151 const RegionDesc::ReffinessPred
& pred
) {
152 write_container(ser
, pred
.mask
, write_raw
<bool>);
153 write_container(ser
, pred
.vals
, write_raw
<bool>);
154 write_raw(ser
, pred
.arSpOffset
);
157 RegionDesc::ReffinessPred
read_reffiness_pred(ProfDataDeserializer
& ser
) {
158 RegionDesc::ReffinessPred ret
;
160 [&] { ret
.mask
.push_back(read_raw
<bool>(ser
)); });
162 [&] { ret
.vals
.push_back(read_raw
<bool>(ser
)); });
163 ret
.arSpOffset
= read_raw
<decltype(ret
.arSpOffset
)>(ser
);
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 auto sz DEBUG_ONLY
= read_raw
<uint32_t>(ser
);
208 assertx(sz
== globalArrayTypeTable().size());
209 for (auto arr
: globalArrayTypeTable()) {
210 auto const orig
= read_raw
<const RepoAuthType::Array
*>(ser
);
211 ser
.recordRat(orig
, arr
);
215 void write_region_block(ProfDataSerializer
& ser
,
216 const RegionDesc::BlockPtr
& block
) {
217 write_raw(ser
, block
->id());
218 write_srckey(ser
, block
->start());
219 write_raw(ser
, block
->length());
220 write_raw(ser
, block
->initialSpOffset());
221 write_raw(ser
, block
->profTransID());
222 write_container(ser
, block
->typePredictions(), write_typed_location
);
223 write_container(ser
, block
->typePreConditions(), write_guarded_location
);
224 write_container(ser
, block
->paramByRefs(),
225 [] (ProfDataSerializer
& s
, std::pair
<SrcKey
, bool> byRef
) {
226 write_srckey(s
, byRef
.first
);
227 write_raw(s
, byRef
.second
);
229 write_container(ser
, block
->reffinessPreds(), write_reffiness_pred
);
230 write_container(ser
, block
->knownFuncs(),
231 [] (ProfDataSerializer
& s
,
232 std::pair
<SrcKey
, const Func
*> knownFunc
) {
233 write_srckey(s
, knownFunc
.first
);
234 write_func(s
, knownFunc
.second
);
236 write_container(ser
, block
->postConds().changed
, write_typed_location
);
237 write_container(ser
, block
->postConds().refined
, write_typed_location
);
240 RegionDesc::BlockPtr
read_region_block(ProfDataDeserializer
& ser
) {
241 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
242 auto const start
= read_srckey(ser
);
243 auto const length
= read_raw
<int>(ser
);
244 auto const initialSpOffset
= read_raw
<FPInvOffset
>(ser
);
246 auto const block
= std::make_shared
<RegionDesc::Block
>(id
,
254 block
->setProfTransID(read_raw
<TransID
>(ser
));
258 block
->addPredicted(read_typed_location(ser
));
263 block
->addPreCondition(read_guarded_location(ser
));
268 auto const sk
= read_srckey(ser
);
269 auto const byRef
= read_raw
<bool>(ser
);
270 block
->setParamByRef(sk
, byRef
);
275 block
->addReffinessPred(read_reffiness_pred(ser
));
280 auto const sk
= read_srckey(ser
);
281 auto const func
= read_func(ser
);
282 block
->setKnownFunc(sk
, func
);
285 PostConditions postConds
;
288 postConds
.changed
.push_back(read_typed_location(ser
));
292 postConds
.refined
.push_back(read_typed_location(ser
));
294 block
->setPostConds(postConds
);
298 void write_region_desc(ProfDataSerializer
& ser
, const RegionDesc
* rd
) {
299 write_container(ser
, rd
->blocks(), write_region_block
);
300 write_container(ser
, findPredTrans(*rd
, profData()),
301 [&] (RegionDesc::BlockId id
) {
304 write_container(ser
, rd
->blocks(),
305 [&] (const RegionDesc::BlockPtr
& b
) {
306 auto const bid
= b
->id();
308 assertx(rd
->succs(bid
).empty());
309 assertx(rd
->preds(bid
).empty());
310 assertx(rd
->merged(bid
).empty());
311 auto const pr
= rd
->prevRetrans(bid
);
312 write_raw(ser
, pr
? pr
.value() : kInvalidTransID
);
313 auto const nr
= rd
->nextRetrans(bid
);
314 write_raw(ser
, nr
? nr
.value() : kInvalidTransID
);
316 write_type(ser
, rd
->inlineCtxType());
317 write_container(ser
, rd
->inlineInputTypes(), write_type
);
320 RegionDescPtr
read_region_desc(ProfDataDeserializer
& ser
) {
321 auto ret
= std::make_shared
<RegionDesc
>();
322 read_container(ser
, [&] { ret
->addBlock(read_region_block(ser
)); });
323 RegionDesc::BlockIdSet incoming
;
325 [&] { incoming
.insert(read_raw
<RegionDesc::BlockId
>(ser
)); });
326 ret
->incoming(std::move(incoming
));
330 auto const id
= read_raw
<RegionDesc::BlockId
>(ser
);
331 auto const pr
= read_raw
<RegionDesc::BlockId
>(ser
);
332 auto const nr
= read_raw
<RegionDesc::BlockId
>(ser
);
333 if (pr
!= kInvalidTransID
) {
334 ret
->setNextRetrans(pr
, id
);
336 if (nr
!= kInvalidTransID
) {
337 ret
->setNextRetrans(id
, nr
);
340 auto const ty
= read_type(ser
);
341 std::vector
<Type
> args
;
344 args
.push_back(read_type(ser
));
346 ret
->setInlineContext(ty
, args
);
350 void write_prof_trans_rec(ProfDataSerializer
& ser
, const ProfTransRec
* ptr
) {
351 if (!ptr
) return write_raw(ser
, TransKind
{});
352 write_raw(ser
, ptr
->kind());
353 write_srckey(ser
, ptr
->srcKey());
354 if (ptr
->kind() == TransKind::Profile
) {
355 write_raw(ser
, ptr
->lastBcOff());
356 write_region_desc(ser
, ptr
->region().get());
358 // No need to preserve callers; there won't be any when we use this data.
359 write_raw(ser
, ptr
->prologueArgs());
363 std::unique_ptr
<ProfTransRec
> read_prof_trans_rec(ProfDataDeserializer
& ser
) {
364 auto const kind
= read_raw
<TransKind
>(ser
);
365 static_assert(TransKind::Profile
!= TransKind
{} &&
366 TransKind::ProfPrologue
!= TransKind
{},
367 "Profile and ProfPrologue must not be zero");
368 if (kind
== TransKind
{}) return nullptr;
369 auto const sk
= read_srckey(ser
);
370 if (kind
== TransKind::Profile
) {
371 auto const lastBcOff
= read_raw
<Offset
>(ser
);
372 auto const region
= read_region_desc(ser
);
373 return std::make_unique
<ProfTransRec
>(lastBcOff
, sk
, region
);
376 return std::make_unique
<ProfTransRec
>(sk
, read_raw
<int>(ser
));
379 void write_profiled_funcs(ProfDataSerializer
& ser
, ProfData
* pd
) {
380 auto const maxFuncId
= pd
->maxProfilingFuncId();
382 for (FuncId fid
= 0; fid
<= maxFuncId
; fid
++) {
383 if (!Func::isFuncIdValid(fid
) || !pd
->profiling(fid
)) continue;
384 write_func(ser
, Func::fromFuncId(fid
));
387 write_raw(ser
, uintptr_t{});
390 void read_profiled_funcs(ProfDataDeserializer
& ser
, ProfData
* pd
) {
391 while (auto const func
= read_func(ser
)) {
392 pd
->setProfiling(func
->getFuncId());
396 void write_prof_data(ProfDataSerializer
& ser
, ProfData
* pd
) {
397 write_profiled_funcs(ser
, pd
);
399 write_raw(ser
, pd
->counterDefault());
401 [&] (const ProfTransRec
* ptr
) {
402 auto const transID
= ptr
->isProfile() ?
403 ptr
->region()->entry()->profTransID() :
404 pd
->proflogueTransId(ptr
->func(), ptr
->prologueArgs());
405 write_raw(ser
, transID
);
406 write_prof_trans_rec(ser
, ptr
);
407 // forEachTransRec already grabs a read lock, and we're not
408 // going to add a *new* counter here (so we don't need a write
410 write_raw(ser
, *pd
->transCounterAddrNoLock(transID
));
413 write_raw(ser
, kInvalidTransID
);
416 void read_prof_data(ProfDataDeserializer
& ser
, ProfData
* pd
) {
417 read_profiled_funcs(ser
, pd
);
419 pd
->resetCounters(read_raw
<int64_t>(ser
));
421 auto const transID
= read_raw
<TransID
>(ser
);
422 if (transID
== kInvalidTransID
) break;
423 pd
->addProfTrans(transID
, read_prof_trans_rec(ser
));
424 *pd
->transCounterAddr(transID
) = read_raw
<int64_t>(ser
);
429 auto write_impl(ProfDataSerializer
& ser
, const T
& out
, bool) ->
430 decltype(std::declval
<T
&>().serialize(ser
),void()) {
435 void write_impl(ProfDataSerializer
& ser
, const T
& out
, int) {
440 void write_maybe_serializable(ProfDataSerializer
& ser
, const T
& out
) {
441 write_impl(ser
, out
, false);
444 struct TargetProfileVisitor
: boost::static_visitor
<void> {
445 TargetProfileVisitor(ProfDataSerializer
& ser
,
446 const rds::Symbol
& sym
,
455 void process(T
& out
, const StringData
* name
) {
456 write_raw(ser
, size
);
457 write_string(ser
, name
);
459 TargetProfile
<T
>::reduce(out
, handle
, size
);
460 if (size
== sizeof(T
)) {
461 write_maybe_serializable(ser
, out
);
463 write_raw(ser
, &out
, size
);
467 template<typename T
> void operator()(const T
&) {}
469 void operator()(const rds::Profile
<T
>& pt
) {
470 if (size
== sizeof(T
)) {
472 process(out
, pt
.name
.get());
474 auto const mem
= calloc(1, size
);
475 SCOPE_EXIT
{ free(mem
); };
476 process(*reinterpret_cast<T
*>(mem
), pt
.name
.get());
480 ProfDataSerializer
& ser
;
481 const rds::Symbol
& sym
;
486 void write_target_profiles(ProfDataSerializer
& ser
) {
488 [&] (const rds::Symbol
& symbol
, rds::Handle handle
, uint32_t size
) {
489 TargetProfileVisitor
tv(ser
, symbol
, handle
, size
);
490 boost::apply_visitor(tv
, symbol
);
493 write_raw(ser
, uint32_t{});
497 auto read_impl(ProfDataDeserializer
& ser
, T
& out
, bool) ->
498 decltype(out
.deserialize(ser
),void()) {
499 out
.deserialize(ser
);
503 void read_impl(ProfDataDeserializer
& ser
, T
& out
, int) {
508 void read_maybe_serializable(ProfDataDeserializer
& ser
, T
& out
) {
509 read_impl(ser
, out
, false);
512 struct SymbolFixup
: boost::static_visitor
<void> {
513 SymbolFixup(ProfDataDeserializer
& ser
, StringData
* name
, uint32_t size
) :
514 ser
{ser
}, name
{name
}, size
{size
} {}
516 template<typename T
> void operator()(T
&) { always_assert(false); }
518 void operator()(rds::Profile
<T
>& pt
) {
519 TargetProfile
<T
> prof(pt
.transId
,
525 if (size
== sizeof(T
)) {
526 read_maybe_serializable(ser
, prof
.value());
528 read_raw(ser
, &prof
.value(), size
);
532 ProfDataDeserializer
& ser
;
534 // The size of the original rds allocation.
538 void read_target_profiles(ProfDataDeserializer
& ser
) {
540 auto const size
= read_raw
<uint32_t>(ser
);
542 auto const name
= read_string(ser
);
543 auto sym
= read_raw
<rds::Symbol
>(ser
);
544 auto sf
= SymbolFixup
{ser
, name
, size
};
545 boost::apply_visitor(sf
, sym
);
549 ////////////////////////////////////////////////////////////////////////////////
552 ProfDataSerializer::ProfDataSerializer(const std::string
& name
) {
553 fd
= open(name
.c_str(), O_CLOEXEC
| O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
554 if (fd
== -1) throw std::runtime_error("Failed to open: " + name
);
557 ProfDataSerializer::~ProfDataSerializer() {
559 if (offset
) ::write(fd
, buffer
, offset
);
563 ProfDataDeserializer::ProfDataDeserializer(const std::string
& name
) {
564 fd
= open(name
.c_str(), O_CLOEXEC
| O_RDONLY
);
565 if (fd
== -1) throw std::runtime_error("Failed to open: " + name
);
568 ProfDataDeserializer::~ProfDataDeserializer() {
573 bool ProfDataDeserializer::done() {
575 return offset
== buffer_size
&& ::read(fd
, &byte
, 1) == 0;
578 void write_raw(ProfDataSerializer
& ser
, const void* data
, size_t sz
) {
579 if (ser
.offset
+ sz
<= ProfDataSerializer::buffer_size
) {
580 memcpy(ser
.buffer
+ ser
.offset
, data
, sz
);
584 if (ser
.offset
== 0) {
585 if (::write(ser
.fd
, data
, sz
) != sz
) {
586 throw std::runtime_error("Failed to write serialized data");
590 if (auto const delta
= ProfDataSerializer::buffer_size
- ser
.offset
) {
591 memcpy(ser
.buffer
+ ser
.offset
, data
, delta
);
592 data
= static_cast<const char*>(data
) + delta
;
594 ser
.offset
= ProfDataSerializer::buffer_size
;
596 assertx(ser
.offset
== ProfDataSerializer::buffer_size
);
597 if (::write(ser
.fd
, ser
.buffer
, ser
.offset
) != ser
.offset
) {
598 throw std::runtime_error("Failed to write serialized data");
601 write_raw(ser
, data
, sz
);
604 void read_raw(ProfDataDeserializer
& ser
, void* data
, size_t sz
) {
605 if (ser
.offset
+ sz
<= ProfDataDeserializer::buffer_size
) {
606 memcpy(data
, ser
.buffer
+ ser
.offset
, sz
);
610 if (auto const delta
= ProfDataDeserializer::buffer_size
- ser
.offset
) {
611 memcpy(data
, ser
.buffer
+ ser
.offset
, delta
);
612 data
= static_cast<char*>(data
) + delta
;
614 ser
.offset
= ProfDataDeserializer::buffer_size
;
616 if (sz
>= ProfDataDeserializer::buffer_size
) {
617 auto const bytes_read
= ::read(ser
.fd
, data
, sz
);
618 if (bytes_read
< 0 || bytes_read
< sz
) {
619 throw std::runtime_error("Failed to read serialized data");
624 auto const bytes_read
= ::read(ser
.fd
,
626 ProfDataDeserializer::buffer_size
);
627 if (bytes_read
< 0 || bytes_read
< sz
) {
628 throw std::runtime_error("Failed to read serialized data");
630 ser
.offset
= ProfDataDeserializer::buffer_size
- bytes_read
;
632 memmove(ser
.buffer
+ ser
.offset
, ser
.buffer
, bytes_read
);
634 return read_raw(ser
, data
, sz
);
637 StringData
*& ProfDataDeserializer::getEnt(const StringData
* p
) {
638 return stringMap
[uintptr_t(p
)];
641 ArrayData
*& ProfDataDeserializer::getEnt(const ArrayData
* p
) {
642 return arrayMap
[uintptr_t(p
)];
645 Unit
*& ProfDataDeserializer::getEnt(const Unit
* p
) {
646 return unitMap
[uintptr_t(p
)];
649 Func
*& ProfDataDeserializer::getEnt(const Func
* p
) {
650 return funcMap
[uintptr_t(p
)];
653 Class
*& ProfDataDeserializer::getEnt(const Class
* p
) {
654 return classMap
[uintptr_t(p
)];
657 const RepoAuthType::Array
*&
658 ProfDataDeserializer::getEnt(const RepoAuthType::Array
* p
) {
659 return ratMap
[uintptr_t(p
)];
662 bool ProfDataSerializer::serialize(const Unit
* unit
) {
663 return unit
->serialize();
666 bool ProfDataSerializer::serialize(const Func
* func
) {
667 return func
->serialize();
670 bool ProfDataSerializer::serialize(const Class
* cls
) {
671 return cls
->serialize();
674 void write_string(ProfDataSerializer
& ser
, const StringData
* str
) {
675 if (!ser
.serialize(str
)) return write_raw(ser
, str
);
676 write_serialized_ptr(ser
, str
);
677 uint32_t sz
= str
->size();
679 write_raw(ser
, str
->data(), sz
);
682 StringData
* read_string(ProfDataDeserializer
& ser
) {
685 [&] () -> StringData
* {
686 auto const sz
= read_raw
<uint32_t>(ser
);
687 String s
{sz
, ReserveStringMode
{}};
688 auto const range
= s
.bufferSlice();
689 read_raw(ser
, range
.begin(), sz
);
691 return makeStaticString(s
);
696 void write_array(ProfDataSerializer
& ser
, const ArrayData
* arr
) {
697 if (!ser
.serialize(arr
)) return write_raw(ser
, arr
);
698 write_serialized_ptr(ser
, arr
);
699 auto const str
= internal_serialize(VarNR(const_cast<ArrayData
*>(arr
)));
700 uint32_t sz
= str
.size();
702 write_raw(ser
, str
.data(), sz
);
705 ArrayData
* read_array(ProfDataDeserializer
& ser
) {
708 [&] () -> ArrayData
* {
709 auto const sz
= read_raw
<uint32_t>(ser
);
710 String s
{sz
, ReserveStringMode
{}};
711 auto const range
= s
.bufferSlice();
712 read_raw(ser
, range
.begin(), sz
);
714 auto v
= unserialize_from_buffer(
717 VariableUnserializer::Type::Internal
719 return ArrayData::GetScalarArray(std::move(v
));
724 void write_unit(ProfDataSerializer
& ser
, const Unit
* unit
) {
725 if (!ser
.serialize(unit
)) return write_raw(ser
, unit
);
726 ITRACE(2, "Unit: {}\n", unit
->filepath());
727 write_serialized_ptr(ser
, unit
);
728 write_string(ser
, unit
->filepath());
731 Unit
* read_unit(ProfDataDeserializer
& ser
) {
735 auto const filepath
= read_string(ser
);
736 ITRACE(2, "Unit: {}\n", filepath
);
737 if (filepath
->data()[0] == '/' && filepath
->data()[1] == ':') {
738 return lookupSyslibUnit(filepath
);
740 return lookupUnit(filepath
, "", nullptr);
745 template<typename C1
, typename C2
, typename F
>
746 void visit_deps(const C1
& c1
, const C2
& c2
, F
& f
) {
747 auto it
= c2
.begin();
748 auto const DEBUG_ONLY end
= c2
.end();
749 for (auto const& dep
: c1
) {
755 void write_class(ProfDataSerializer
& ser
, const Class
* cls
) {
757 ITRACE(2, "Class: {}\n", cls
? cls
->name() : staticEmptyString());
759 ITRACE(2, "Class>\n");
762 if (!cls
|| !ser
.serialize(cls
)) return write_raw(ser
, cls
);
764 write_serialized_ptr(ser
, cls
);
765 write_raw(ser
, cls
->preClass()->id());
766 write_unit(ser
, cls
->preClass()->unit());
768 jit::vector
<std::pair
<const Class
*, const StringData
*>> dependents
;
769 auto record_dep
= [&] (const Class
* dep
, const StringData
* depName
) {
771 if (!dep
->wasSerialized() ||
772 !classHasPersistentRDS(dep
) ||
773 !dep
->name()->isame(depName
)) {
774 dependents
.emplace_back(dep
, depName
);
777 record_dep(cls
->parent(), cls
->preClass()->parent());
779 visit_deps(cls
->declInterfaces(), cls
->preClass()->interfaces(), record_dep
);
781 if (cls
->preClass()->attrs() & AttrNoExpandTrait
) {
782 for (auto const tName
: cls
->preClass()->usedTraits()) {
783 auto const trait
= Unit::lookupUniqueClassInContext(tName
, nullptr);
785 record_dep(trait
, tName
);
788 visit_deps(cls
->usedTraitClasses(),
789 cls
->preClass()->usedTraits(),
793 write_container(ser
, dependents
,
794 [&] (const std::pair
<const Class
*, const StringData
*> &dep
) {
795 write_class(ser
, dep
.first
);
796 write_string(ser
, dep
.second
);
799 if (cls
->parent() == c_Closure::classof()) {
800 auto const func
= cls
->lookupMethod(s_invoke
.get());
802 write_class(ser
, func
->cls());
806 Class
* read_class(ProfDataDeserializer
& ser
) {
807 ITRACE(2, "Class>\n");
808 auto const ret
= deserialize(
812 auto const id
= read_raw
<decltype(std::declval
<PreClass
*>()->id())>(ser
);
813 auto const unit
= read_unit(ser
);
817 auto const dep
= read_class(ser
);
818 auto const ne
= dep
->preClass()->namedEntity();
819 // if its not persistent, make sure that dep
820 // is the active class for this NamedEntity
821 assertx(ne
->m_cachedClass
.bound());
822 if (ne
->m_cachedClass
.isNormal()) {
823 ne
->setCachedClass(dep
);
825 auto const depName
= read_string(ser
);
826 if (!dep
->name()->isame(depName
)) {
827 // this dependent was referred to via a
828 // class_alias, so we need to make sure
829 // *that* points to the class too
830 auto const aliasNe
= NamedEntity::get(depName
);
831 aliasNe
->m_cachedClass
.bind(rds::Mode::Normal
);
832 if (aliasNe
->m_cachedClass
.isNormal()) {
833 aliasNe
->m_cachedClass
.markUninit();
835 aliasNe
->setCachedClass(dep
);
839 auto const preClass
= unit
->lookupPreClassId(id
);
840 auto const ne
= preClass
->namedEntity();
841 // If its not persistent, make sure its NamedEntity is
842 // unbound, ready for DefClass
843 if (ne
->m_cachedClass
.bound() &&
844 ne
->m_cachedClass
.isNormal()) {
845 ne
->m_cachedClass
.markUninit();
848 auto const cls
= Unit::defClass(preClass
, true);
849 if (cls
->pinitVec().size()) cls
->initPropHandle();
850 if (cls
->numStaticProperties()) cls
->initSPropHandles();
852 if (cls
->parent() == c_Closure::classof()) {
853 auto const ctx
= read_class(ser
);
854 if (ctx
!= cls
) return cls
->rescope(ctx
);
860 ITRACE(2, "Class: {}\n", ret
? ret
->name() : staticEmptyString());
864 void write_func(ProfDataSerializer
& ser
, const Func
* func
) {
866 ITRACE(2, "Func: {}\n", func
? func
->fullName() : staticEmptyString());
868 ITRACE(2, "Func>\n");
871 if (!func
|| !ser
.serialize(func
)) return write_raw(ser
, func
);
873 write_serialized_ptr(ser
, func
);
874 uint32_t fid
= func
->getFuncId();
875 assertx(!(fid
& 0x80000000));
876 if (func
== SystemLib::s_nullCtor
||
877 (!func
->isMethod() && !func
->isPseudoMain() && func
->isBuiltin())) {
878 if (func
== SystemLib::s_nullCtor
) {
879 assertx(func
->name()->isame(s_86ctor
.get()));
883 return write_string(ser
, func
->name());
886 if (func
->isPseudoMain()) {
887 const uint32_t zero
= 0;
888 write_raw(ser
, zero
);
889 write_unit(ser
, func
->unit());
890 return write_class(ser
, func
->cls());
893 if (func
->isMethod()) {
894 auto const* cls
= func
->implCls();
895 auto const nslot
= [&] () -> uint32_t {
896 const uint32_t slot
= func
->methodSlot();
897 if (cls
->getMethod(slot
) != func
) {
898 if (func
->name() == s_86pinit
.get()) return k86pinitSlot
;
899 if (func
->name() == s_86sinit
.get()) return k86sinitSlot
;
900 cls
= getOwningClassForFunc(func
);
901 assertx(cls
->getMethod(slot
) == func
);
905 assertx(nslot
& 0x80000000);
906 write_raw(ser
, nslot
);
907 return write_class(ser
, cls
);
910 // Ideally we'd write the func's index in its Unit; but we may not
911 // have that after Unit::initial_merge
912 const uint32_t off
= func
->base();
913 assertx(off
&& !(off
& 0x80000000));
915 write_unit(ser
, func
->unit());
918 Func
* read_func(ProfDataDeserializer
& ser
) {
919 ITRACE(2, "Func>\n");
920 auto const ret
= deserialize(
924 auto fid
= read_raw
<uint32_t>(ser
);
925 auto const func
= [&] () -> const Func
* {
926 if (fid
& 0x80000000) {
928 auto const name
= read_string(ser
);
929 if (name
->isame(s_86ctor
.get())) return SystemLib::s_nullCtor
;
930 return Unit::lookupFunc(name
);
932 auto const id
= read_raw
<uint32_t>(ser
);
934 auto const unit
= read_unit(ser
);
935 return unit
->getMain(read_class(ser
));
937 if (id
& 0x80000000) {
938 auto const cls
= read_class(ser
);
939 if (id
== k86pinitSlot
) return cls
->get86pinit();
940 if (id
== k86sinitSlot
) return cls
->get86sinit();
941 const Slot slot
= ~id
;
942 return cls
->getMethod(slot
);
944 auto const unit
= read_unit(ser
);
945 for (auto const f
: unit
->funcs()) {
946 if (f
->base() == id
) {
948 auto const handle
= f
->funcHandle();
949 if (!rds::isPersistentHandle(handle
) &&
950 (!rds::isHandleInit(handle
, rds::NormalTag
{}) ||
951 rds::handleToRef
<LowPtr
<Func
>,
952 rds::Mode::Normal
>(handle
).get() != f
)) {
953 rds::uninitHandle(handle
);
954 Unit::defFunc(f
, false);
961 ser
.recordFid(fid
, func
->getFuncId());
962 return const_cast<Func
*>(func
);
965 ITRACE(2, "Func: {}\n", ret
? ret
->fullName() : staticEmptyString());
969 bool serializeProfData(const std::string
& filename
) {
971 ProfDataSerializer ser
{filename
};
973 write_raw(ser
, Repo::get().global().Signature
);
974 auto schema
= repoSchemaId();
975 write_raw(ser
, schema
.size());
976 write_raw(ser
, schema
.begin(), schema
.size());
978 Func::s_treadmill
= true;
981 requestInitProfData();
984 requestExitProfData();
988 Func::s_treadmill
= false;
991 write_global_array_map(ser
);
993 auto const pd
= profData();
994 write_prof_data(ser
, pd
);
996 write_target_profiles(ser
);
999 } catch (std::runtime_error
& err
) {
1000 FTRACE(1, "serializeProfData - Failed: {}\n", err
.what());
1005 bool deserializeProfData(const std::string
& filename
) {
1007 ProfDataDeserializer ser
{filename
};
1009 auto signature
= read_raw
<decltype(Repo::get().global().Signature
)>(ser
);
1010 if (signature
!= Repo::get().global().Signature
) {
1011 throw std::runtime_error("Mismatched repo-schema");
1013 auto size
= read_raw
<size_t>(ser
);
1015 schema
.resize(size
);
1016 read_raw(ser
, &schema
[0], size
);
1017 if (schema
!= repoSchemaId()) {
1018 throw std::runtime_error("Mismatched repo-schema");
1021 read_global_array_map(ser
);
1023 ProfData::Session pds
;
1024 auto const pd
= profData();
1025 read_prof_data(ser
, pd
);
1026 pd
->setDeserialized();
1028 read_target_profiles(ser
);
1030 always_assert(ser
.done());
1032 } catch (std::runtime_error
& err
) {
1033 FTRACE(1, "deserializeProfData - Failed: {}\n", err
.what());
1038 //////////////////////////////////////////////////////////////////////