• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&sectionHeader, 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> &sectionInfos)
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 &section : 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, &sectionHeader)) {
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> &sectionInfos,
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