1 /**
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "pgo_file_builder.h"
17 #include "compiler/aot/aot_manager.h"
18 #include "include/mem/panda_string.h"
19 #include "zlib.h"
20 #include "utils/expected.h"
21 #include "utils/logger.h"
22
23 #include <iostream>
24 #include <iomanip>
25
26 namespace ark::pgo {
27 // NOLINTNEXTLINE(readability-magic-numbers)
28 static_assert(sizeof(PgoHeader) == 24, "PgoHeader is 24 bytes");
29 // NOLINTNEXTLINE(readability-magic-numbers)
30 static_assert(sizeof(PandaFilesSectionHeader) == 8, "PandaFiles section header is 8 bytes");
31 // NOLINTNEXTLINE(readability-magic-numbers)
32 static_assert(sizeof(PandaFileInfoHeader) == 8,
33 "Header of each item of PandaFileInfo in PandaFiles section is 8 bytes");
34 // NOLINTNEXTLINE(readability-magic-numbers)
35 static_assert(sizeof(SectionsInfoSectionHeader) == 8, "Header of SectionsInfo section is 8 bytes");
36 // NOLINTNEXTLINE(readability-magic-numbers)
37 static_assert(sizeof(SectionInfo) == 20, "Each item of section info in SectionsInfo section is 20 bytes");
38 // NOLINTNEXTLINE(readability-magic-numbers)
39 static_assert(sizeof(MethodsHeader) == 8, "Header of Methods section is 8 bytes");
40 // NOLINTNEXTLINE(readability-magic-numbers)
41 static_assert(sizeof(MethodDataHeader) == 16, "Header of one method data sub-section is 16 bytes");
42 // NOLINTNEXTLINE(readability-magic-numbers)
43 static_assert(sizeof(AotProfileDataHeader) == 12, "Header of one profile data sub-section is 12 bytes");
44
WritePandaFilesSection(std::ofstream & fd,PandaMap<int32_t,std::string_view> & pandaFileMap)45 uint32_t AotPgoFile::WritePandaFilesSection(std::ofstream &fd, PandaMap<int32_t, std::string_view> &pandaFileMap)
46 {
47 PandaFilesSectionHeader sectionHeader = {static_cast<uint32_t>(pandaFileMap.size()),
48 static_cast<uint32_t>(sizeof(PandaFilesSectionHeader))};
49
50 for (auto &fileInfo : pandaFileMap) {
51 sectionHeader.sectionSize += sizeof(PandaFileInfoHeader) + fileInfo.second.size() + 1;
52 }
53 AotPgoFile::Buffer buffer(sectionHeader.sectionSize);
54
55 uint32_t currPos = 0;
56 if (buffer.CopyToBuffer(§ionHeader, sizeof(sectionHeader), currPos) == 0) {
57 return 0;
58 }
59 currPos += sizeof(sectionHeader);
60
61 for (auto &fileInfo : pandaFileMap) {
62 PandaFileInfoHeader fileInfoHeader = {FileType::BOOT, static_cast<uint32_t>(fileInfo.second.size() + 1)};
63 if (buffer.CopyToBuffer(&fileInfoHeader, sizeof(fileInfoHeader), currPos) == 0) {
64 return 0;
65 }
66 currPos += sizeof(fileInfoHeader);
67 if (buffer.CopyToBuffer(fileInfo.second.data(), fileInfoHeader.fileNameLen, currPos) == 0) {
68 return 0;
69 }
70 currPos += fileInfoHeader.fileNameLen;
71 }
72
73 buffer.WriteBuffer(fd);
74 return sectionHeader.sectionSize;
75 }
76
WriteSectionInfosSection(std::ofstream & fd,PandaVector<SectionInfo> & sectionInfos)77 uint32_t AotPgoFile::WriteSectionInfosSection(std::ofstream &fd, PandaVector<SectionInfo> §ionInfos)
78 {
79 SectionsInfoSectionHeader header = {static_cast<uint32_t>(sectionInfos.size()),
80 static_cast<uint32_t>(sizeof(SectionsInfoSectionHeader))};
81 header.sectionSize += sectionInfos.size() * sizeof(SectionInfo);
82
83 AotPgoFile::Buffer buffer(header.sectionSize);
84
85 auto currPos = 0;
86 if (buffer.CopyToBuffer(&header, sizeof(SectionsInfoSectionHeader), currPos) == 0) {
87 return 0;
88 }
89 currPos += sizeof(SectionsInfoSectionHeader);
90
91 for (auto &secInfo : sectionInfos) {
92 if (buffer.CopyToBuffer(&secInfo, sizeof(SectionInfo), currPos) == 0) {
93 return 0;
94 }
95 currPos += sizeof(SectionInfo);
96 }
97
98 buffer.WriteBuffer(fd);
99 return header.sectionSize;
100 }
101
WriteAllMethodsSections(std::ofstream & fd,FileToMethodsMap & methods)102 uint32_t AotPgoFile::WriteAllMethodsSections(std::ofstream &fd, FileToMethodsMap &methods)
103 {
104 uint32_t totalSize = 0;
105 for (auto §ion : methods) {
106 uint32_t checkSum = adler32(0L, Z_NULL, 0);
107 auto sectionSize = WriteMethodsSection(fd, section.first, &checkSum, section.second);
108 if (sectionSize == 0) {
109 continue;
110 }
111
112 SectionInfo sectionInfo = {checkSum, 0, sectionSize, SectionType::METHODS, section.first};
113 sectionInfos_.push_back(sectionInfo);
114
115 totalSize += sectionSize;
116 }
117 return totalSize;
118 }
119
GetMaxMethodSectionSize(AotProfilingData::MethodsMap & methods)120 uint32_t AotPgoFile::GetMaxMethodSectionSize(AotProfilingData::MethodsMap &methods)
121 {
122 uint32_t size = 0;
123 for (auto &[methodIdx, methodProfData] : methods) {
124 bool empty = true;
125 auto inlineCaches = methodProfData.GetInlineCaches();
126 auto branches = methodProfData.GetBranchData();
127 auto throws = methodProfData.GetThrowData();
128 if (!inlineCaches.empty()) {
129 size += sizeof(AotProfileDataHeader) + inlineCaches.SizeBytes();
130 empty = false;
131 }
132 if (!branches.empty()) {
133 size += sizeof(AotProfileDataHeader) + branches.SizeBytes();
134 empty = false;
135 }
136 if (!throws.empty()) {
137 size += sizeof(AotProfileDataHeader) + throws.SizeBytes();
138 empty = false;
139 }
140 if (!empty) {
141 size += sizeof(MethodDataHeader);
142 }
143 }
144 if (size > 0) {
145 size += sizeof(MethodsHeader);
146 }
147 return size;
148 }
149
GetMethodSectionProf(AotProfilingData::MethodsMap & methods)150 uint32_t AotPgoFile::GetMethodSectionProf(AotProfilingData::MethodsMap &methods)
151 {
152 uint32_t savedType = 0;
153 for (const auto &[methodIdx, methodProfData] : methods) {
154 auto inlineCaches = methodProfData.GetInlineCaches();
155 auto branches = methodProfData.GetBranchData();
156 auto throws = methodProfData.GetThrowData();
157 if (!inlineCaches.empty()) {
158 savedType |= ProfileType::VCALL;
159 }
160 if (!branches.empty()) {
161 savedType |= ProfileType::BRANCH;
162 }
163 if (!throws.empty()) {
164 savedType |= ProfileType::THROW;
165 }
166 }
167 return savedType;
168 }
169
WriteMethodsSection(std::ofstream & fd,int32_t pandaFileIdx,uint32_t * checkSum,AotProfilingData::MethodsMap & methods)170 uint32_t AotPgoFile::WriteMethodsSection(std::ofstream &fd, int32_t pandaFileIdx, uint32_t *checkSum,
171 AotProfilingData::MethodsMap &methods)
172 {
173 MethodsHeader methodsSectionHeader = {static_cast<uint32_t>(methods.size()), pandaFileIdx};
174
175 uint32_t sectionSize = GetMaxMethodSectionSize(methods);
176 Buffer buffer(sectionSize);
177
178 uint32_t writtenBytes = 0;
179 uint32_t currPos = 0;
180 currPos += sizeof(MethodsHeader);
181
182 for (auto &[methodIdx, methodProfData] : methods) {
183 writtenBytes += WriteMethodSubSection(currPos, &buffer, methodIdx, methodProfData);
184 }
185
186 if (writtenBytes == 0) {
187 return 0;
188 }
189
190 if (buffer.CopyToBuffer(&methodsSectionHeader, sizeof(MethodsHeader), 0) == 0) {
191 return 0;
192 }
193
194 buffer.WriteBuffer(fd);
195 writtenBytes += sizeof(MethodsHeader);
196
197 *checkSum = adler32(*checkSum, buffer.GetBuffer().data(), writtenBytes);
198
199 return writtenBytes;
200 }
201
WriteMethodSubSection(uint32_t & currPos,Buffer * buffer,uint32_t methodIdx,AotProfilingData::AotMethodProfilingData & methodProfData)202 uint32_t AotPgoFile::WriteMethodSubSection(uint32_t &currPos, Buffer *buffer, uint32_t methodIdx,
203 AotProfilingData::AotMethodProfilingData &methodProfData)
204 {
205 MethodDataHeader methodHeader = {methodIdx, methodProfData.GetClassIdx(), ProfileType::NO,
206 sizeof(MethodDataHeader)};
207
208 auto currMethodHeaderPos = currPos;
209 currPos += sizeof(MethodDataHeader);
210 uint32_t writtenBytes = 0;
211
212 auto inlineCaches = methodProfData.GetInlineCaches();
213 if (!inlineCaches.empty()) {
214 methodHeader.savedType |= ProfileType::VCALL;
215 auto icSize = WriteInlineCachesToStream(currPos, buffer, inlineCaches);
216 methodHeader.chunkSize += icSize;
217 writtenBytes += icSize;
218 currPos += icSize;
219 }
220
221 auto branches = methodProfData.GetBranchData();
222 if (!branches.empty()) {
223 methodHeader.savedType |= ProfileType::BRANCH;
224 auto brSize = WriteBranchDataToStream(currPos, buffer, branches);
225 methodHeader.chunkSize += brSize;
226 writtenBytes += brSize;
227 currPos += brSize;
228 }
229
230 auto throws = methodProfData.GetThrowData();
231 if (!throws.empty()) {
232 methodHeader.savedType |= ProfileType::THROW;
233 auto thSize = WriteThrowDataToStream(currPos, buffer, throws);
234 methodHeader.chunkSize += thSize;
235 writtenBytes += thSize;
236 currPos += thSize;
237 }
238
239 if (methodHeader.chunkSize > sizeof(MethodDataHeader)) {
240 if (buffer->CopyToBuffer(&methodHeader, sizeof(MethodDataHeader), currMethodHeaderPos) == 0) {
241 return 0;
242 }
243 writtenBytes += sizeof(MethodDataHeader);
244 } else {
245 currPos -= sizeof(MethodDataHeader);
246 }
247 return writtenBytes;
248 }
249
WriteInlineCachesToStream(uint32_t streamBegin,Buffer * buffer,Span<AotProfilingData::AotCallSiteInlineCache> inlineCaches)250 uint32_t AotPgoFile::WriteInlineCachesToStream(uint32_t streamBegin, Buffer *buffer,
251 Span<AotProfilingData::AotCallSiteInlineCache> inlineCaches)
252 {
253 AotProfileDataHeader icHeader = {ProfileType::VCALL, static_cast<uint32_t>(inlineCaches.size()),
254 static_cast<uint32_t>(sizeof(AotProfileDataHeader) + inlineCaches.SizeBytes())};
255 uint32_t writtenBytes = 0;
256 auto currPos = streamBegin;
257
258 if (buffer->CopyToBuffer(&icHeader, sizeof(AotProfileDataHeader), currPos) == 0) {
259 return 0;
260 }
261 currPos += sizeof(AotProfileDataHeader);
262 writtenBytes += sizeof(AotProfileDataHeader);
263
264 if (buffer->CopyToBuffer(inlineCaches.data(), inlineCaches.SizeBytes(), currPos) == 0) {
265 return 0;
266 }
267 writtenBytes += inlineCaches.SizeBytes();
268 return writtenBytes;
269 }
270
WriteBranchDataToStream(uint32_t streamBegin,Buffer * buffer,Span<AotProfilingData::AotBranchData> branches)271 uint32_t AotPgoFile::WriteBranchDataToStream(uint32_t streamBegin, Buffer *buffer,
272 Span<AotProfilingData::AotBranchData> branches)
273 {
274 AotProfileDataHeader brHeader = {ProfileType::BRANCH, static_cast<uint32_t>(branches.size()),
275 static_cast<uint32_t>(sizeof(AotProfileDataHeader) + branches.SizeBytes())};
276 uint32_t writtenBytes = 0;
277 auto currPos = streamBegin;
278
279 if (buffer->CopyToBuffer(&brHeader, sizeof(AotProfileDataHeader), currPos) == 0) {
280 return 0;
281 }
282 currPos += sizeof(AotProfileDataHeader);
283 writtenBytes += sizeof(AotProfileDataHeader);
284
285 if (buffer->CopyToBuffer(branches.data(), branches.SizeBytes(), currPos) == 0) {
286 return 0;
287 }
288 writtenBytes += branches.SizeBytes();
289 return writtenBytes;
290 }
291
WriteThrowDataToStream(uint32_t streamBegin,Buffer * buffer,Span<AotProfilingData::AotThrowData> throws)292 uint32_t AotPgoFile::WriteThrowDataToStream(uint32_t streamBegin, Buffer *buffer,
293 Span<AotProfilingData::AotThrowData> throws)
294 {
295 AotProfileDataHeader thHeader = {ProfileType::THROW, static_cast<uint32_t>(throws.size()),
296 static_cast<uint32_t>(sizeof(AotProfileDataHeader) + throws.SizeBytes())};
297 uint32_t writtenBytes = 0;
298 auto currPos = streamBegin;
299
300 if (buffer->CopyToBuffer(&thHeader, sizeof(AotProfileDataHeader), currPos) == 0) {
301 return 0;
302 }
303 currPos += sizeof(AotProfileDataHeader);
304 writtenBytes += sizeof(AotProfileDataHeader);
305
306 if (buffer->CopyToBuffer(throws.data(), throws.SizeBytes(), currPos) == 0) {
307 return 0;
308 }
309 writtenBytes += throws.SizeBytes();
310 return writtenBytes;
311 }
312
GetSectionNumbers(FileToMethodsMap & methods)313 uint32_t AotPgoFile::GetSectionNumbers(FileToMethodsMap &methods)
314 {
315 uint32_t count = 0;
316 for (auto &method : methods) {
317 if (GetMaxMethodSectionSize(method.second) > 0) {
318 count++;
319 }
320 }
321 return count;
322 }
323
GetSavedTypes(FileToMethodsMap & allMethodsMap)324 uint32_t AotPgoFile::GetSavedTypes(FileToMethodsMap &allMethodsMap)
325 {
326 uint32_t savedType = 0;
327 for (auto &methodMap : allMethodsMap) {
328 savedType |= GetMethodSectionProf(methodMap.second);
329 if (savedType == PROFILE_TYPE) {
330 break;
331 }
332 }
333 return savedType;
334 }
335
336 // CC-OFFNXT(G.FUN.01-CPP) Decreasing the number of arguments will decrease the clarity of the code.
WriteFileHeader(std::ofstream & fd,const std::array<char,MAGIC_SIZE> & magic,const std::array<char,VERSION_SIZE> & version,uint32_t versionPType,uint32_t savedPType,const PandaString & classCtxStr)337 uint32_t AotPgoFile::WriteFileHeader(std::ofstream &fd, const std::array<char, MAGIC_SIZE> &magic,
338 const std::array<char, VERSION_SIZE> &version, uint32_t versionPType,
339 uint32_t savedPType, const PandaString &classCtxStr)
340 {
341 uint32_t cha = classCtxStr.size() + 1;
342 uint32_t headerSize = sizeof(PgoHeader) + cha;
343 PgoHeader header = {magic, version, versionPType, savedPType, headerSize, cha};
344 Buffer buffer(headerSize);
345
346 if (buffer.CopyToBuffer(&header, sizeof(PgoHeader), 0) == 0) {
347 return 0;
348 }
349
350 if (buffer.CopyToBuffer(classCtxStr.c_str(), cha, sizeof(PgoHeader)) == 0) {
351 return 0;
352 }
353
354 buffer.WriteBuffer(fd);
355 return header.headerSize;
356 }
357
358 // CC-OFFNXT(G.PRE.06) code generation
359 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
360 #define CheckAndAddBytes(fd, filePath, checkBytes, writtenBytes) \
361 { \
362 if ((checkBytes) == 0) { \
363 (fd).close(); \
364 if (remove((filePath).data()) == -1) { \
365 LOG(ERROR, RUNTIME) << "Failed to remove file: " << (filePath); \
366 } \
367 /* CC-OFFNXT(G.PRE.05) function gen */ \
368 return 0; \
369 } \
370 (writtenBytes) += (checkBytes); \
371 }
372
Save(const PandaString & fileName,AotProfilingData * profObject,const PandaString & classCtxStr)373 uint32_t AotPgoFile::Save(const PandaString &fileName, AotProfilingData *profObject, const PandaString &classCtxStr)
374 {
375 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
376 std::ofstream fd(fileName.data(), std::ios::binary | std::ios::out);
377 if (!fd.is_open()) {
378 return 0;
379 }
380 uint32_t writtenBytes = 0;
381
382 auto savedProf = GetSavedTypes(profObject->GetAllMethods());
383 auto headerBytes = WriteFileHeader(fd, MAGIC, VERSION, PROFILE_TYPE, savedProf, classCtxStr);
384 CheckAndAddBytes(fd, fileName, headerBytes, writtenBytes);
385
386 auto pandaFilesBytes = WritePandaFilesSection(fd, profObject->GetPandaFileMapReverse());
387 CheckAndAddBytes(fd, fileName, pandaFilesBytes, writtenBytes);
388
389 uint32_t offset = writtenBytes;
390 auto sectionNum = GetSectionNumbers(profObject->GetAllMethods());
391 auto sectionInfosSize = GetSectionInfosSectionSize(sectionNum);
392
393 fd.seekp(sectionInfosSize, std::ios::cur);
394
395 auto methodsBytes = WriteAllMethodsSections(fd, profObject->GetAllMethods());
396 CheckAndAddBytes(fd, fileName, methodsBytes, writtenBytes);
397
398 fd.seekp(offset, std::ios::beg);
399 auto sectionInfoBytes = WriteSectionInfosSection(fd, sectionInfos_);
400 CheckAndAddBytes(fd, fileName, sectionInfoBytes, writtenBytes);
401
402 if (chmod(fileName.data(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
403 return 0;
404 }
405
406 return writtenBytes;
407 }
408 #undef CheckAndAddBytes
409
ReadBytes(std::ifstream & inputFile,char * dst,size_t n,uint32_t * checkSum)410 std::ifstream &AotPgoFile::ReadBytes(std::ifstream &inputFile, char *dst, size_t n, uint32_t *checkSum)
411 {
412 if (dst != nullptr) {
413 inputFile.read(dst, n);
414 if (checkSum != nullptr) {
415 *checkSum = adler32(*checkSum, reinterpret_cast<unsigned char *>(dst), n);
416 }
417 } else {
418 char ch;
419 for (size_t i = 0; i < n; i++) {
420 if (!ReadBytes(inputFile, &ch, 1, checkSum)) {
421 break;
422 }
423 }
424 }
425 return inputFile;
426 }
427
428 using UnexpectedS = Unexpected<PandaString>;
429
ReadPandaFilesSection(std::ifstream & inputFile)430 Expected<PandaVector<PandaString>, PandaString> AotPgoFile::ReadPandaFilesSection(std::ifstream &inputFile)
431 {
432 PandaFilesSectionHeader sectionHeader {};
433 if (!Read(inputFile, §ionHeader)) {
434 return UnexpectedS("Cannot read panda files section header");
435 }
436
437 PandaVector<PandaString> pandaFiles(sectionHeader.numberOfFiles);
438 for (auto &pandaFile : pandaFiles) {
439 PandaFileInfoHeader fileInfoHeader {};
440 if (!Read(inputFile, &fileInfoHeader)) {
441 return UnexpectedS("Cannot read panda file info header");
442 }
443
444 pandaFile.resize(fileInfoHeader.fileNameLen - 1, '\0');
445 if (!Read(inputFile, pandaFile.data(), fileInfoHeader.fileNameLen - 1) ||
446 // fileNameLen is written with the 0-terminator
447 !ReadBytes(inputFile, nullptr, 1)) {
448 return UnexpectedS("Cannot read panda file info name");
449 }
450 }
451
452 return pandaFiles;
453 }
454
ReadMethodsSection(std::ifstream & inputFile,AotProfilingData & data)455 Expected<uint32_t, PandaString> AotPgoFile::ReadMethodsSection(std::ifstream &inputFile, AotProfilingData &data)
456 {
457 using namespace std::string_literals;
458 uint32_t checkSum = adler32(0L, Z_NULL, 0);
459 MethodsHeader methodsSectionHeader {};
460 if (!Read(inputFile, &methodsSectionHeader, 1, &checkSum)) {
461 return UnexpectedS("Couldn't read profile methods section header");
462 }
463
464 AotProfilingData::MethodsMap methodsMap;
465
466 for (uint32_t i = 0; i < methodsSectionHeader.numberOfMethods; i++) {
467 auto methodProfDataOrError = ReadMethodSubSection(inputFile, checkSum);
468 if (!methodProfDataOrError) {
469 return Unexpected(methodProfDataOrError.Error());
470 }
471 data.AddMethod(methodsSectionHeader.pandaFileIdx, methodProfDataOrError->GetMethodIdx(),
472 std::move(*methodProfDataOrError));
473 }
474
475 return checkSum;
476 }
477
ReadMethodSubSection(std::ifstream & inputFile,uint32_t & checkSum)478 Expected<AotProfilingData::AotMethodProfilingData, PandaString> AotPgoFile::ReadMethodSubSection(
479 std::ifstream &inputFile, uint32_t &checkSum)
480 {
481 using namespace std::string_literals;
482 MethodDataHeader methodHeader {};
483 if (!Read(inputFile, &methodHeader, 1, &checkSum)) {
484 return UnexpectedS("Couldn't read profile method data header");
485 }
486 PandaVector<AotProfilingData::AotCallSiteInlineCache> inlineCaches;
487 PandaVector<AotProfilingData::AotBranchData> branchData;
488 PandaVector<AotProfilingData::AotThrowData> throwData;
489
490 size_t readBytes = sizeof(MethodDataHeader);
491 while (readBytes < methodHeader.chunkSize) {
492 AotProfileDataHeader header {};
493 if (!Read(inputFile, &header, 1, &checkSum)) {
494 return UnexpectedS("Couldn't read profile data header");
495 }
496
497 Expected<size_t, PandaString> errorOrBytesRead;
498 if ((methodHeader.savedType & ProfileType::VCALL) != 0 && header.profType == ProfileType::VCALL) {
499 errorOrBytesRead = ReadProfileData(inputFile, header, inlineCaches, checkSum);
500 } else if ((methodHeader.savedType & ProfileType::BRANCH) != 0 && header.profType == ProfileType::BRANCH) {
501 errorOrBytesRead = ReadProfileData(inputFile, header, branchData, checkSum);
502 } else if ((methodHeader.savedType & ProfileType::THROW) != 0 && header.profType == ProfileType::THROW) {
503 errorOrBytesRead = ReadProfileData(inputFile, header, throwData, checkSum);
504 } else {
505 errorOrBytesRead = 0;
506 }
507
508 if (!errorOrBytesRead) {
509 return Unexpected(errorOrBytesRead.Error());
510 }
511
512 if (sizeof(AotProfileDataHeader) + *errorOrBytesRead < header.chunkSize) {
513 ReadBytes(inputFile, nullptr, header.chunkSize - sizeof(AotProfileDataHeader) - *errorOrBytesRead,
514 &checkSum);
515 }
516
517 readBytes += header.chunkSize;
518 }
519
520 if (readBytes != methodHeader.chunkSize) {
521 return UnexpectedS("Inconsistent method data size in profile");
522 }
523
524 return AotProfilingData::AotMethodProfilingData(methodHeader.methodIdx, methodHeader.classIdx,
525 std::move(inlineCaches), std::move(branchData),
526 std::move(throwData));
527 }
528
529 template <typename T>
ReadProfileData(std::ifstream & inputFile,const AotProfileDataHeader & header,PandaVector<T> & data,uint32_t & checkSum)530 Expected<size_t, PandaString> AotPgoFile::ReadProfileData(std::ifstream &inputFile, const AotProfileDataHeader &header,
531 PandaVector<T> &data, uint32_t &checkSum)
532 {
533 using namespace std::string_literals;
534 size_t oldSize = data.size();
535 data.resize(oldSize + header.numberOfRecords);
536 size_t bytesToRead = sizeof(AotProfilingData::AotCallSiteInlineCache) * header.numberOfRecords;
537 if (!Read(inputFile, &data[oldSize], header.numberOfRecords, &checkSum)) {
538 return UnexpectedS("Couldn't read method profile data");
539 }
540 return bytesToRead;
541 }
542
543 template <typename Range>
PrintBytesToSS(PandaStringStream & ss,const Range & rng)544 static PandaStringStream &PrintBytesToSS(PandaStringStream &ss, const Range &rng)
545 {
546 ss << std::setfill('0');
547 for (auto &b : rng) {
548 ss << std::hex << std::setw(2U) << static_cast<int>(b);
549 }
550 return ss;
551 }
552
ReadFileHeader(std::ifstream & inputFile)553 Expected<std::pair<PgoHeader, PandaString>, PandaString> AotPgoFile::ReadFileHeader(std::ifstream &inputFile)
554 {
555 using namespace std::string_literals;
556 PgoHeader header {};
557 if (!Read(inputFile, &header)) {
558 return UnexpectedS("Couldn't read profile header");
559 }
560 if (header.magic != MAGIC) {
561 PandaStringStream ss("Wrong profile header magic: expected ");
562 PrintBytesToSS(ss, MAGIC) << ", got ";
563 PrintBytesToSS(ss, header.magic);
564 return Unexpected(std::move(ss).str());
565 }
566 if (header.version != VERSION) {
567 PandaStringStream ss("Unsupported profile version: expected");
568 PrintBytesToSS(ss, VERSION) << ", got ";
569 PrintBytesToSS(ss, header.version);
570 return Unexpected(std::move(ss).str());
571 }
572 if ((header.versionProfileType & ~PROFILE_TYPE) != 0) {
573 // CC-OFFNXT(G.NAM.03-CPP) project code style
574 constexpr size_t MASK_SIZE = sizeof(PROFILE_TYPE) * CHAR_BIT;
575 PandaStringStream ss("Unsupported profile type: ");
576 ss << std::bitset<MASK_SIZE>(header.versionProfileType) << ", expected submask of "
577 << std::bitset<MASK_SIZE>(PROFILE_TYPE);
578 return Unexpected(std::move(ss).str());
579 }
580 // Class context string is written with the 0-terminator
581 auto classCtxStrSize = header.withCha;
582 PandaString classCtxStr(classCtxStrSize - 1, '\0');
583 if (!Read(inputFile, classCtxStr.data(), classCtxStrSize - 1) || !ReadBytes(inputFile, nullptr, 1)) {
584 return UnexpectedS("Cannot read profile class context");
585 }
586
587 return std::make_pair(header, classCtxStr);
588 }
589
ReadSectionInfos(std::ifstream & inputFile)590 Expected<PandaVector<SectionInfo>, PandaString> AotPgoFile::ReadSectionInfos(std::ifstream &inputFile)
591 {
592 using namespace std::string_literals;
593 SectionsInfoSectionHeader header {};
594 if (!Read(inputFile, &header)) {
595 return UnexpectedS("Couldn't read profile section information header");
596 }
597
598 PandaVector<SectionInfo> sectionInfos(header.sectionNumber);
599 if (!Read(inputFile, sectionInfos.data(), sectionInfos.size())) {
600 return UnexpectedS("Couldn't read profile section information");
601 }
602
603 return sectionInfos;
604 }
605
ReadAllMethodsSections(std::ifstream & inputFile,PandaVector<SectionInfo> & sectionInfos,AotProfilingData & data)606 Expected<size_t, PandaString> AotPgoFile::ReadAllMethodsSections(std::ifstream &inputFile,
607 PandaVector<SectionInfo> §ionInfos,
608 AotProfilingData &data)
609 {
610 using namespace std::string_literals;
611 size_t nSections = 0;
612 for (auto &info : sectionInfos) {
613 if (info.sectionType != SectionType::METHODS) {
614 inputFile.seekg(info.zippedSize != 0 ? info.zippedSize : info.unzippedSize, std::ios_base::cur);
615 continue;
616 }
617 if (info.zippedSize != 0) {
618 return UnexpectedS("Zipped profile method sections are unsupported");
619 }
620 auto checkSumOrError = ReadMethodsSection(inputFile, data);
621 if (!checkSumOrError) {
622 return Unexpected(checkSumOrError.Error());
623 }
624 if (info.checkSum != *checkSumOrError) {
625 return UnexpectedS("Check sum mismatch for method profile data");
626 }
627 nSections++;
628 }
629
630 return nSections;
631 }
632
Load(const PandaString & fileName,PandaString & classCtxStr,PandaVector<PandaString> & pandaFiles)633 Expected<AotProfilingData, PandaString> AotPgoFile::Load(const PandaString &fileName, PandaString &classCtxStr,
634 PandaVector<PandaString> &pandaFiles)
635 {
636 using namespace std::string_literals;
637 AotProfilingData data;
638
639 std::ifstream inputFile(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
640 if (!inputFile.is_open()) {
641 return UnexpectedS("Cannot open file");
642 }
643
644 {
645 auto headerOrError = ReadFileHeader(inputFile);
646 if (!headerOrError) {
647 return Unexpected(headerOrError.Error());
648 }
649 classCtxStr = std::move(headerOrError->second);
650 }
651
652 {
653 auto pandaFilesOrError = ReadPandaFilesSection(inputFile);
654 if (!pandaFilesOrError) {
655 return Unexpected(pandaFilesOrError.Error());
656 }
657 pandaFiles = std::move(*pandaFilesOrError);
658 }
659 data.AddPandaFiles(pandaFiles);
660
661 auto sectionInfosOrError = ReadSectionInfos(inputFile);
662 if (!sectionInfosOrError) {
663 return Unexpected(sectionInfosOrError.Error());
664 }
665
666 auto nOrError = ReadAllMethodsSections(inputFile, *sectionInfosOrError, data);
667 if (!nOrError) {
668 return Unexpected(nOrError.Error());
669 }
670
671 return data;
672 }
673 } // namespace ark::pgo
674