Don't use c++ streams for jit serialization
[hiphop-php.git] / hphp / runtime / vm / jit / prof-data-serialize.cpp
blob608d25d0672474d9b71b952f6be3fe70c0479ff0
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/vm/jit/prof-data-serialize.h"
19 #include "hphp/runtime/base/builtin-functions.h"
20 #include "hphp/runtime/base/program-functions.h"
21 #include "hphp/runtime/base/string-data.h"
22 #include "hphp/runtime/base/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 //////////////////////////////////////////////////////////////////////
51 namespace {
52 //////////////////////////////////////////////////////////////////////
54 TRACE_SET_MOD(hhbc);
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;
64 template<typename F>
65 auto deserialize(ProfDataDeserializer&ser, F&& f) -> decltype(f()) {
66 using T = decltype(f());
67 auto const ptr = read_raw<uintptr_t>(ser);
68 if (!ptr) return T{};
69 if (ptr & 1) {
70 auto& ent = ser.getEnt(reinterpret_cast<T>(ptr - 1));
71 assertx(!ent);
72 ent = f();
73 ITRACE(3, "0x{:08x} => 0x{:08x}\n",
74 ptr - 1, reinterpret_cast<uintptr_t>(ent));
75 assertx(ent);
76 return ent;
78 auto const ent = ser.getEnt(reinterpret_cast<T>(ptr));
79 assertx(ent);
80 return ent;
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)) {
102 return f(ser, t);
105 template<typename S, typename T, typename F>
106 auto call(F& f, S&, const T& t) -> decltype(f(t)) {
107 return 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) {
114 call(f, ser, elm);
118 template<typename F>
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())) {
127 Trace::Indent _i;
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)) {
139 Trace::Indent _i;
140 read_func(ser);
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));
147 return 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;
159 read_container(ser,
160 [&] { ret.mask.push_back(read_raw<bool>(ser)); });
161 read_container(ser,
162 [&] { ret.vals.push_back(read_raw<bool>(ser)); });
163 ret.arSpOffset = read_raw<decltype(ret.arSpOffset)>(ser);
164 return ret;
167 void write_type(ProfDataSerializer& ser, Type t) {
168 t.serialize(ser);
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,
247 start.func(),
248 start.resumeMode(),
249 start.hasThis(),
250 start.offset(),
251 length,
252 initialSpOffset);
254 block->setProfTransID(read_raw<TransID>(ser));
256 read_container(ser,
257 [&] {
258 block->addPredicted(read_typed_location(ser));
261 read_container(ser,
262 [&] {
263 block->addPreCondition(read_guarded_location(ser));
266 read_container(ser,
267 [&] {
268 auto const sk = read_srckey(ser);
269 auto const byRef = read_raw<bool>(ser);
270 block->setParamByRef(sk, byRef);
273 read_container(ser,
274 [&] {
275 block->addReffinessPred(read_reffiness_pred(ser));
278 read_container(ser,
279 [&] {
280 auto const sk = read_srckey(ser);
281 auto const func = read_func(ser);
282 block->setKnownFunc(sk, func);
285 PostConditions postConds;
286 read_container(ser,
287 [&] {
288 postConds.changed.push_back(read_typed_location(ser));
290 read_container(ser,
291 [&] {
292 postConds.refined.push_back(read_typed_location(ser));
294 block->setPostConds(postConds);
295 return block;
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) {
302 write_raw(ser, id);
304 write_container(ser, rd->blocks(),
305 [&] (const RegionDesc::BlockPtr& b) {
306 auto const bid = b->id();
307 write_raw(ser, bid);
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;
324 read_container(ser,
325 [&] { incoming.insert(read_raw<RegionDesc::BlockId>(ser)); });
326 ret->incoming(std::move(incoming));
328 read_container(ser,
329 [&] {
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;
342 read_container(ser,
343 [&] {
344 args.push_back(read_type(ser));
346 ret->setInlineContext(ty, args);
347 return ret;
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());
357 } else {
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());
400 pd->forEachTransRec(
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
409 // lock).
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));
420 while (true) {
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);
428 template<typename T>
429 auto write_impl(ProfDataSerializer& ser, const T& out, bool) ->
430 decltype(std::declval<T&>().serialize(ser),void()) {
431 out.serialize(ser);
434 template<typename T>
435 void write_impl(ProfDataSerializer& ser, const T& out, int) {
436 write_raw(ser, out);
439 template<typename T>
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,
447 rds::Handle handle,
448 uint32_t size) :
449 ser{ser},
450 sym{sym},
451 handle{handle},
452 size{size} {}
454 template<typename T>
455 void process(T& out, const StringData* name) {
456 write_raw(ser, size);
457 write_string(ser, name);
458 write_raw(ser, sym);
459 TargetProfile<T>::reduce(out, handle, size);
460 if (size == sizeof(T)) {
461 write_maybe_serializable(ser, out);
462 } else {
463 write_raw(ser, &out, size);
467 template<typename T> void operator()(const T&) {}
468 template<typename T>
469 void operator()(const rds::Profile<T>& pt) {
470 if (size == sizeof(T)) {
471 T out{};
472 process(out, pt.name.get());
473 } else {
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;
482 rds::Handle handle;
483 uint32_t size;
486 void write_target_profiles(ProfDataSerializer& ser) {
487 rds::visitSymbols(
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{});
496 template<typename T>
497 auto read_impl(ProfDataDeserializer& ser, T& out, bool) ->
498 decltype(out.deserialize(ser),void()) {
499 out.deserialize(ser);
502 template<typename T>
503 void read_impl(ProfDataDeserializer& ser, T& out, int) {
504 read_raw(ser, out);
507 template<typename T>
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); }
517 template<typename T>
518 void operator()(rds::Profile<T>& pt) {
519 TargetProfile<T> prof(pt.transId,
520 TransKind::Profile,
521 pt.bcOff,
522 name,
523 size - sizeof(T));
525 if (size == sizeof(T)) {
526 read_maybe_serializable(ser, prof.value());
527 } else {
528 read_raw(ser, &prof.value(), size);
532 ProfDataDeserializer& ser;
533 StringData* name;
534 // The size of the original rds allocation.
535 uint32_t size;
538 void read_target_profiles(ProfDataDeserializer& ser) {
539 while (true) {
540 auto const size = read_raw<uint32_t>(ser);
541 if (!size) break;
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() {
558 assertx(fd != -1);
559 if (offset) ::write(fd, buffer, offset);
560 close(fd);
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() {
569 assertx(fd != -1);
570 close(fd);
573 bool ProfDataDeserializer::done() {
574 char byte;
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);
581 ser.offset += sz;
582 return;
584 if (ser.offset == 0) {
585 if (::write(ser.fd, data, sz) != sz) {
586 throw std::runtime_error("Failed to write serialized data");
588 return;
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;
593 sz -= 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");
600 ser.offset = 0;
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);
607 ser.offset += sz;
608 return;
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;
613 sz -= 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");
621 return;
624 auto const bytes_read = ::read(ser.fd,
625 ser.buffer,
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;
631 if (ser.offset) {
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();
678 write_raw(ser, sz);
679 write_raw(ser, str->data(), sz);
682 StringData* read_string(ProfDataDeserializer& ser) {
683 return deserialize(
684 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);
690 s.setSize(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();
701 write_raw(ser, sz);
702 write_raw(ser, str.data(), sz);
705 ArrayData* read_array(ProfDataDeserializer& ser) {
706 return deserialize(
707 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);
713 s.setSize(sz);
714 auto v = unserialize_from_buffer(
715 s.data(),
716 s.size(),
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) {
732 return deserialize(
733 ser,
734 [&] () -> Unit* {
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) {
750 assertx(it != end);
751 f(dep.get(), *it++);
755 void write_class(ProfDataSerializer& ser, const Class* cls) {
756 SCOPE_EXIT {
757 ITRACE(2, "Class: {}\n", cls ? cls->name() : staticEmptyString());
759 ITRACE(2, "Class>\n");
760 Trace::Indent _;
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) {
770 if (!dep) return;
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);
784 assertx(trait);
785 record_dep(trait, tName);
787 } else {
788 visit_deps(cls->usedTraitClasses(),
789 cls->preClass()->usedTraits(),
790 record_dep);
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());
801 assertx(func);
802 write_class(ser, func->cls());
806 Class* read_class(ProfDataDeserializer& ser) {
807 ITRACE(2, "Class>\n");
808 auto const ret = deserialize(
809 ser,
810 [&] () -> Class* {
811 Trace::Indent _;
812 auto const id = read_raw<decltype(std::declval<PreClass*>()->id())>(ser);
813 auto const unit = read_unit(ser);
815 read_container(ser,
816 [&] {
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);
856 return cls;
860 ITRACE(2, "Class: {}\n", ret ? ret->name() : staticEmptyString());
861 return ret;
864 void write_func(ProfDataSerializer& ser, const Func* func) {
865 SCOPE_EXIT {
866 ITRACE(2, "Func: {}\n", func ? func->fullName() : staticEmptyString());
868 ITRACE(2, "Func>\n");
869 Trace::Indent _;
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()));
881 fid = ~fid;
882 write_raw(ser, fid);
883 return write_string(ser, func->name());
885 write_raw(ser, fid);
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);
903 return ~slot;
904 }();
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));
914 write_raw(ser, off);
915 write_unit(ser, func->unit());
918 Func* read_func(ProfDataDeserializer& ser) {
919 ITRACE(2, "Func>\n");
920 auto const ret = deserialize(
921 ser,
922 [&] () -> Func* {
923 Trace::Indent _;
924 auto fid = read_raw<uint32_t>(ser);
925 auto const func = [&] () -> const Func* {
926 if (fid & 0x80000000) {
927 fid = ~fid;
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);
933 if (!id) {
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) {
947 Unit::bindFunc(f);
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);
956 return f;
959 not_reached();
960 }();
961 ser.recordFid(fid, func->getFuncId());
962 return const_cast<Func*>(func);
965 ITRACE(2, "Func: {}\n", ret ? ret->fullName() : staticEmptyString());
966 return ret;
969 bool serializeProfData(const std::string& filename) {
970 try {
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;
979 hphp_thread_init();
980 hphp_session_init();
981 requestInitProfData();
983 SCOPE_EXIT {
984 requestExitProfData();
985 hphp_context_exit();
986 hphp_session_exit();
987 hphp_thread_exit();
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);
998 return true;
999 } catch (std::runtime_error& err) {
1000 FTRACE(1, "serializeProfData - Failed: {}\n", err.what());
1001 return false;
1005 bool deserializeProfData(const std::string& filename) {
1006 try {
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);
1014 std::string schema;
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());
1031 return true;
1032 } catch (std::runtime_error& err) {
1033 FTRACE(1, "deserializeProfData - Failed: {}\n", err.what());
1034 return false;
1038 //////////////////////////////////////////////////////////////////////