• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "runtime/profilesaver/profile_dump_info.h"
17 
18 #include <sys/file.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/uio.h>
22 
23 #include <cerrno>
24 #include <climits>
25 #include <cstring>
26 
27 #include "libpandabase/os/failure_retry.h"
28 #include "trace/trace.h"
29 
30 #ifndef PATH_MAX
31 constexpr uint16_t PATH_MAX = 1024;
32 #endif
33 
34 namespace ark {
35 static constexpr size_t K_BITS_PER_BYTE = 8;
36 
37 static constexpr size_t K_LINE_HEADER_SIZE = 3 * sizeof(uint32_t) + sizeof(uint16_t);
38 static constexpr size_t K_METHOD_BYTES = 4;
39 static constexpr size_t K_CLASS_BYTES = 4;
40 
41 const uint8_t ProfileDumpInfo::kProfileMagic[] = {'p', 'r', 'o', 'f', '\0'};  // NOLINT
42 const uint8_t ProfileDumpInfo::kProfileVersion[] = {'0', '1', '\0'};          // NOLINT
43 
44 static constexpr uint16_t K_MAX_FILE_KEY_LENGTH = PATH_MAX;  // NOLINT
45 
WriteBuffer(int fd,const uint8_t * buffer,size_t byteCount)46 static bool WriteBuffer(int fd, const uint8_t *buffer, size_t byteCount)
47 {  // NOLINT
48     while (byteCount > 0) {
49         int bytesWritten = write(fd, buffer, byteCount);  // real place to write
50         if (bytesWritten == -1) {
51             return false;
52         }
53         byteCount -= static_cast<size_t>(bytesWritten);
54         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
55         buffer += static_cast<size_t>(bytesWritten);
56     }
57     return true;
58 }
59 
AddStringToBuffer(PandaVector<uint8_t> * buffer,const PandaString & value)60 static void AddStringToBuffer(PandaVector<uint8_t> *buffer, const PandaString &value)
61 {  // NOLINT
62     buffer->insert(buffer->end(), value.begin(), value.end());
63 }
64 
65 template <typename T>
AddUintToBuffer(PandaVector<uint8_t> * buffer,T value)66 static void AddUintToBuffer(PandaVector<uint8_t> *buffer, T value)
67 {
68     for (size_t i = 0; i < sizeof(T); i++) {
69         buffer->push_back((value >> (i * K_BITS_PER_BYTE)) & 0xff);  // NOLINT
70     }
71 }
72 
73 /*
74  * Tests for EOF by trying to read 1 byte from the descriptor.
75  * Returns:
76  *   0 if the descriptor is at the EOF,
77  *  -1 if there was an IO error
78  *   1 if the descriptor has more content to read
79  */
80 // NOLINTNEXTLINE(readability-identifier-naming)
testEOF(int fd)81 static int testEOF(int fd)
82 {
83     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
84     uint8_t buffer[1];
85     return read(fd, buffer, 1);
86 }
87 
GetFileSizeBytes(const PandaString & filename)88 ssize_t GetFileSizeBytes(const PandaString &filename)
89 {
90     struct stat statBuf {};
91     int rc = stat(filename.c_str(), &statBuf);
92     return (rc == 0) ? statBuf.st_size : -1;
93 }
94 
FillFromFd(int fd,const PandaString & source,PandaString * error)95 ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::SerializerBuffer::FillFromFd(int fd, const PandaString &source,
96                                                                                 PandaString *error)
97 {
98     size_t byteCount = ptrEnd_ - ptrCurrent_;
99     uint8_t *buffer = ptrCurrent_;
100     while (byteCount > 0) {
101         int bytesRead = read(fd, buffer, byteCount);
102         if (bytesRead == 0) {  // NOLINT
103             *error += "Profile EOF reached prematurely for " + source;
104             return PROFILE_LOAD_BAD_DATA;
105         } else if (bytesRead < 0) {  // NOLINT
106             *error += "Profile IO error for " + source + ConvertToString(os::Error(errno).ToString());
107             return PROFILE_LOAD_IO_ERROR;
108         }
109         byteCount -= static_cast<size_t>(bytesRead);
110         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
111         buffer += static_cast<size_t>(bytesRead);
112     }
113     return PROFILE_LOAD_SUCCESS;
114 }
115 
116 template <typename T>
ReadUintAndAdvance()117 T ProfileDumpInfo::SerializerBuffer::ReadUintAndAdvance()
118 {
119     static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
120     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
121     ASSERT(ptrCurrent_ + sizeof(T) <= ptrEnd_);
122     T value = 0;
123     for (size_t i = 0; i < sizeof(T); i++) {
124         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
125         value += ptrCurrent_[i] << (i * K_BITS_PER_BYTE);
126     }
127     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
128     ptrCurrent_ += sizeof(T);
129     return value;
130 }
131 
CompareAndAdvance(const uint8_t * data,size_t dataSize)132 bool ProfileDumpInfo::SerializerBuffer::CompareAndAdvance(const uint8_t *data, size_t dataSize)
133 {
134     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
135     if (ptrCurrent_ + dataSize > ptrEnd_) {
136         return false;
137     }
138     if (memcmp(ptrCurrent_, data, dataSize) == 0) {
139         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
140         ptrCurrent_ += dataSize;
141         return true;
142     }
143     return false;
144 }
145 
MergeWith(const ProfileDumpInfo & other)146 bool ProfileDumpInfo::MergeWith(const ProfileDumpInfo &other)
147 {
148     for (const auto &otherIt : other.dumpInfo_) {
149         auto infoIt = dumpInfo_.find(otherIt.first);
150         if ((infoIt != dumpInfo_.end()) && (infoIt->second.checksum != otherIt.second.checksum)) {
151             LOG(INFO, RUNTIME) << "info_it->second.checksum" << infoIt->second.checksum;
152             LOG(INFO, RUNTIME) << "other_it->second.checksum" << otherIt.second.checksum;
153             LOG(INFO, RUNTIME) << "Checksum mismatch" << otherIt.first;
154             return false;
155         }
156     }
157     LOG(INFO, RUNTIME) << "All checksums match";
158 
159     for (const auto &otherIt : other.dumpInfo_) {
160         const PandaString &otherProfileLocation = otherIt.first;
161         const ProfileLineData &otherProfileData = otherIt.second;
162         auto infoIt = dumpInfo_.find(otherProfileLocation);
163         if (infoIt == dumpInfo_.end()) {
164             auto ret =
165                 dumpInfo_.insert(std::make_pair(otherProfileLocation, ProfileLineData(otherProfileData.checksum)));
166             ASSERT(ret.second);
167             infoIt = ret.first;
168         }
169         infoIt->second.methodWrapperSet.insert(otherProfileData.methodWrapperSet.begin(),
170                                                otherProfileData.methodWrapperSet.end());
171         infoIt->second.classWrapperSet.insert(otherProfileData.classWrapperSet.begin(),
172                                               otherProfileData.classWrapperSet.end());
173     }
174     return true;
175 }
176 
AddMethodsAndClasses(const PandaVector<ExtractedMethod> & methods,const PandaSet<ExtractedResolvedClasses> & resolvedClasses)177 bool ProfileDumpInfo::AddMethodsAndClasses(const PandaVector<ExtractedMethod> &methods,
178                                            const PandaSet<ExtractedResolvedClasses> &resolvedClasses)
179 {
180     for (const ExtractedMethod &method : methods) {
181         if (!AddMethodWrapper(ConvertToString(method.pandaFile->GetFilename()), method.pandaFile->GetHeader()->checksum,
182                               MethodWrapper(method.fileId.GetOffset()))) {
183             return false;
184         }
185     }
186 
187     for (const ExtractedResolvedClasses &classResolved : resolvedClasses) {
188         if (!AddResolvedClasses(classResolved)) {
189             return false;
190         }
191     }
192     return true;
193 }
194 
GetNumberOfMethods() const195 uint64_t ProfileDumpInfo::GetNumberOfMethods() const
196 {
197     uint64_t total = 0;
198     for (const auto &it : dumpInfo_) {
199         total += it.second.methodWrapperSet.size();
200     }
201     return total;
202 }
203 
GetNumberOfResolvedClasses() const204 uint64_t ProfileDumpInfo::GetNumberOfResolvedClasses() const
205 {
206     uint64_t total = 0;
207     for (const auto &it : dumpInfo_) {
208         total += it.second.classWrapperSet.size();
209     }
210     return total;
211 }
212 
ContainsMethod(const ExtractedMethod & methodRef) const213 bool ProfileDumpInfo::ContainsMethod(const ExtractedMethod &methodRef) const
214 {
215     auto infoIt = dumpInfo_.find(ConvertToString(methodRef.pandaFile->GetFilename()));
216     if (infoIt != dumpInfo_.end()) {
217         if (methodRef.pandaFile->GetHeader()->checksum != infoIt->second.checksum) {
218             return false;
219         }
220         const PandaSet<MethodWrapper> &methods = infoIt->second.methodWrapperSet;
221         return methods.find(MethodWrapper(methodRef.fileId.GetOffset())) != methods.end();
222     }
223     return false;
224 }
225 
ContainsClass(const panda_file::File & pandafile,uint32_t classDefIdx) const226 bool ProfileDumpInfo::ContainsClass(const panda_file::File &pandafile, uint32_t classDefIdx) const
227 {
228     auto infoIt = dumpInfo_.find(ConvertToString(pandafile.GetFilename()));
229     if (infoIt != dumpInfo_.end()) {
230         if (pandafile.GetHeader()->checksum != infoIt->second.checksum) {
231             return false;
232         }
233         const PandaSet<ClassWrapper> &classes = infoIt->second.classWrapperSet;
234         return classes.find(ClassWrapper(classDefIdx)) != classes.end();
235     }
236     return false;
237 }
238 
AddMethodWrapper(const PandaString & pandaFileLocation,uint32_t checksum,const ProfileDumpInfo::MethodWrapper & methodToAdd)239 bool ProfileDumpInfo::AddMethodWrapper(const PandaString &pandaFileLocation, uint32_t checksum,
240                                        const ProfileDumpInfo::MethodWrapper &methodToAdd)
241 {
242     ProfileLineData *const data = GetOrAddProfileLineData(pandaFileLocation, checksum);
243     if (data == nullptr) {
244         return false;
245     }
246     data->methodWrapperSet.insert(methodToAdd);
247     return true;
248 }
249 
AddClassWrapper(const PandaString & pandaFileLocation,uint32_t checksum,const ProfileDumpInfo::ClassWrapper & classToAdd)250 bool ProfileDumpInfo::AddClassWrapper(const PandaString &pandaFileLocation, uint32_t checksum,
251                                       const ProfileDumpInfo::ClassWrapper &classToAdd)
252 {
253     ProfileLineData *const data = GetOrAddProfileLineData(pandaFileLocation, checksum);
254     if (data == nullptr) {
255         return false;
256     }
257     data->classWrapperSet.insert(classToAdd);
258     return true;
259 }
260 
AddResolvedClasses(const ExtractedResolvedClasses & classes)261 bool ProfileDumpInfo::AddResolvedClasses(const ExtractedResolvedClasses &classes)
262 {                                                                            // NOLINT(readability-identifier-naming)
263     const PandaString panda_file_location = classes.GetPandaFileLocation();  // NOLINT(readability-identifier-naming)
264     const uint32_t checksum = classes.GetPandaFileChecksum();                // NOLINT(readability-identifier-naming)
265     ProfileLineData *const data = GetOrAddProfileLineData(panda_file_location, checksum);
266     if (data == nullptr) {
267         return false;
268     }
269     for (auto const &i : classes.GetClasses()) {
270         data->classWrapperSet.insert(ClassWrapper(i));
271     }
272     return true;
273 }
274 
GetOrAddProfileLineData(const PandaString & pandaFileLocation,uint32_t checksum)275 ProfileDumpInfo::ProfileLineData *ProfileDumpInfo::GetOrAddProfileLineData(const PandaString &pandaFileLocation,
276                                                                            uint32_t checksum)
277 {
278     auto infoIt = dumpInfo_.find(pandaFileLocation);
279     if (infoIt == dumpInfo_.end()) {
280         auto ret = dumpInfo_.insert(std::make_pair(pandaFileLocation, ProfileLineData(checksum)));
281         ASSERT(ret.second);
282         infoIt = ret.first;
283     }
284     if (infoIt->second.checksum != checksum) {
285         LOG(INFO, RUNTIME) << "Checksum mismatch" << pandaFileLocation;
286         return nullptr;
287     }
288     return &(infoIt->second);
289 }
290 
Save(int fd)291 bool ProfileDumpInfo::Save(int fd)
292 {
293     ASSERT(fd >= 0);
294     trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
295 
296     static constexpr size_t K_MAX_BUFFER_SIZE = 8 * 1024;
297     PandaVector<uint8_t> buffer;  // each element 1 byte
298 
299     WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
300     WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
301     AddUintToBuffer(&buffer, static_cast<uint32_t>(dumpInfo_.size()));
302 
303     for (const auto &it : dumpInfo_) {
304         if (buffer.size() > K_MAX_BUFFER_SIZE) {
305             if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
306                 return false;
307             }
308             buffer.clear();
309         }
310         const PandaString &fileLocation = it.first;
311         const ProfileLineData &fileData = it.second;
312 
313         if (fileLocation.size() >= K_MAX_FILE_KEY_LENGTH) {
314             LOG(INFO, RUNTIME) << "PandaFileKey exceeds allocated limit";
315             return false;
316         }
317 
318         size_t requiredCapacity = buffer.size() + K_LINE_HEADER_SIZE + fileLocation.size() +
319                                   K_METHOD_BYTES * fileData.methodWrapperSet.size() +
320                                   K_CLASS_BYTES * fileData.classWrapperSet.size();
321         buffer.reserve(requiredCapacity);
322 
323         ASSERT(fileLocation.size() <= std::numeric_limits<uint16_t>::max());
324         ASSERT(fileData.methodWrapperSet.size() <= std::numeric_limits<uint32_t>::max());
325         ASSERT(fileData.classWrapperSet.size() <= std::numeric_limits<uint32_t>::max());
326 
327         AddUintToBuffer(&buffer, static_cast<uint16_t>(fileLocation.size()));
328         AddUintToBuffer(&buffer, static_cast<uint32_t>(fileData.methodWrapperSet.size()));
329         AddUintToBuffer(&buffer, static_cast<uint32_t>(fileData.classWrapperSet.size()));
330         AddUintToBuffer(&buffer, fileData.checksum);
331         AddStringToBuffer(&buffer, fileLocation);
332 
333         if (UNLIKELY(fileData.empty())) {
334             LOG(INFO, RUNTIME) << "EMPTY FILE DATA, WERIED!";
335         }
336 
337         for (auto methodIt : fileData.methodWrapperSet) {
338             AddUintToBuffer(&buffer, methodIt.methodId);
339         }
340         for (auto classIt : fileData.classWrapperSet) {
341             AddUintToBuffer(&buffer, classIt.classId);
342         }
343         ASSERT(requiredCapacity == buffer.size());
344     }
345     return WriteBuffer(fd, buffer.data(), buffer.size());
346 }
347 
Load(int fd)348 bool ProfileDumpInfo::Load(int fd)
349 {
350     trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
351     PandaString error;
352     ProfileLoadSatus status = LoadInternal(fd, &error);
353     if (status == PROFILE_LOAD_SUCCESS) {
354         return true;
355     }
356     LOG(INFO, RUNTIME) << "Error when reading profile " << error;
357     return false;
358 }
359 
LoadInternal(int fd,PandaString * error)360 ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::LoadInternal(int fd, PandaString *error)
361 {
362     ASSERT(fd >= 0);
363     trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
364 
365     struct stat statBuffer {};
366     if (fstat(fd, &statBuffer) != 0) {
367         return PROFILE_LOAD_IO_ERROR;
368     }
369 
370     if (statBuffer.st_size == 0) {
371         LOG(INFO, RUNTIME) << "empty file";
372         return PROFILE_LOAD_EMPTYFILE;
373     }
374 
375     uint32_t numberOfLines;
376     ProfileLoadSatus status = ReadProfileHeader(fd, &numberOfLines, error);
377     if (status != PROFILE_LOAD_SUCCESS) {
378         return status;
379     }
380     LOG(INFO, RUNTIME) << "number of profile items = " << numberOfLines;
381 
382     while (numberOfLines > 0) {
383         ProfileLineHeader lineHeader;
384         status = ReadProfileLineHeader(fd, &lineHeader, error);
385         if (status != PROFILE_LOAD_SUCCESS) {
386             return status;
387         }
388 
389         status = ReadProfileLine(fd, lineHeader, error);
390         if (status != PROFILE_LOAD_SUCCESS) {
391             return status;
392         }
393         numberOfLines--;
394     }
395 
396     int result = testEOF(fd);
397     if (result == 0) {
398         return PROFILE_LOAD_SUCCESS;
399     }
400 
401     if (result < 0) {
402         return PROFILE_LOAD_IO_ERROR;
403     }
404 
405     *error = "Unexpected content in the profile file";
406     return PROFILE_LOAD_BAD_DATA;
407 }
408 
ReadProfileHeader(int fd,uint32_t * numberOfLines,PandaString * error)409 ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileHeader(int fd, uint32_t *numberOfLines,
410                                                                      PandaString *error)
411 {
412     const size_t kMagicVersionSize = sizeof(kProfileMagic) + sizeof(kProfileVersion) + sizeof(uint32_t);
413 
414     SerializerBuffer safeBuffer(kMagicVersionSize);
415 
416     ProfileLoadSatus status = safeBuffer.FillFromFd(fd, "ReadProfileHeader", error);
417     if (status != PROFILE_LOAD_SUCCESS) {
418         return status;
419     }
420 
421     if (!safeBuffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
422         *error = "Profile missing magic";
423         return PROFILE_LOAD_VERSION_MISMATCH;
424     }
425     if (!safeBuffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
426         *error = "Profile version mismatch";
427         return PROFILE_LOAD_VERSION_MISMATCH;
428     }
429 
430     *numberOfLines = safeBuffer.ReadUintAndAdvance<uint32_t>();
431     return PROFILE_LOAD_SUCCESS;
432 }
433 
ReadProfileLineHeader(int fd,ProfileLineHeader * lineHeader,PandaString * error)434 ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileLineHeader(int fd, ProfileLineHeader *lineHeader,
435                                                                          PandaString *error)
436 {
437     SerializerBuffer headerBuffer(K_LINE_HEADER_SIZE);
438     ProfileLoadSatus status = headerBuffer.FillFromFd(fd, "ReadProfileLineHeader", error);
439     if (status != PROFILE_LOAD_SUCCESS) {
440         return status;
441     }
442 
443     auto pandaLocationSize = headerBuffer.ReadUintAndAdvance<uint16_t>();  // max chars in location, 4096 = 2 ^ 12
444     lineHeader->methodSetSize = headerBuffer.ReadUintAndAdvance<uint32_t>();
445     lineHeader->classSetSize = headerBuffer.ReadUintAndAdvance<uint32_t>();
446     lineHeader->checksum = headerBuffer.ReadUintAndAdvance<uint32_t>();
447 
448     if (pandaLocationSize == 0 || pandaLocationSize > K_MAX_FILE_KEY_LENGTH) {
449         *error = "PandaFileKey has an invalid size: " + std::to_string(pandaLocationSize);
450         return PROFILE_LOAD_BAD_DATA;
451     }
452 
453     SerializerBuffer locationBuffer(pandaLocationSize);
454     // Read the binary data: location string
455     status = locationBuffer.FillFromFd(fd, "ReadProfileLineHeader", error);
456     if (status != PROFILE_LOAD_SUCCESS) {
457         return status;
458     }
459     lineHeader->pandaFileLocation.assign(reinterpret_cast<char *>(locationBuffer.Get()), pandaLocationSize);
460     return PROFILE_LOAD_SUCCESS;
461 }
462 
ReadProfileLine(int fd,const ProfileLineHeader & lineHeader,PandaString * error)463 ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileLine(int fd, const ProfileLineHeader &lineHeader,
464                                                                    PandaString *error)
465 {
466     static constexpr uint32_t K_MAX_NUMBER_OF_ENTRIES_TO_READ = 8000;  // ~8 kb
467     uint32_t methodsLeftToRead = lineHeader.methodSetSize;
468     uint32_t classesLeftToRead = lineHeader.classSetSize;
469 
470     while ((methodsLeftToRead > 0) || (classesLeftToRead > 0)) {
471         uint32_t methodsToRead = std::min(K_MAX_NUMBER_OF_ENTRIES_TO_READ, methodsLeftToRead);
472         uint32_t maxClassesToRead = K_MAX_NUMBER_OF_ENTRIES_TO_READ - methodsToRead;  // >=0
473         uint32_t classesToRead = std::min(maxClassesToRead, classesLeftToRead);
474 
475         size_t lineSize = K_METHOD_BYTES * methodsToRead + K_CLASS_BYTES * classesToRead;
476         SerializerBuffer lineBuffer(lineSize);
477 
478         ProfileLoadSatus status = lineBuffer.FillFromFd(fd, "ReadProfileLine", error);
479         if (status != PROFILE_LOAD_SUCCESS) {
480             return status;
481         }
482         if (!ProcessLine(lineBuffer, methodsToRead, classesToRead, lineHeader.checksum, lineHeader.pandaFileLocation)) {
483             *error = "Error when reading profile file line";
484             return PROFILE_LOAD_BAD_DATA;
485         }
486 
487         methodsLeftToRead -= methodsToRead;
488         classesLeftToRead -= classesToRead;
489     }
490     return PROFILE_LOAD_SUCCESS;
491 }
492 
493 // NOLINTNEXTLINE(google-runtime-references)
ProcessLine(SerializerBuffer & lineBuffer,uint32_t methodSetSize,uint32_t classSetSize,uint32_t checksum,const PandaString & pandaFileLocation)494 bool ProfileDumpInfo::ProcessLine(SerializerBuffer &lineBuffer, uint32_t methodSetSize, uint32_t classSetSize,
495                                   uint32_t checksum, const PandaString &pandaFileLocation)
496 {
497     for (uint32_t i = 0; i < methodSetSize; i++) {
498         // NB! Read the method info from buffer...
499         auto methodIdx = lineBuffer.ReadUintAndAdvance<uint32_t>();
500         if (!AddMethodWrapper(pandaFileLocation, checksum, MethodWrapper(methodIdx))) {
501             return false;
502         }
503     }
504 
505     for (uint32_t i = 0; i < classSetSize; i++) {
506         auto classDefIdx = lineBuffer.ReadUintAndAdvance<uint32_t>();
507         if (!AddClassWrapper(pandaFileLocation, checksum, ClassWrapper(classDefIdx))) {
508             return false;
509         }
510     }
511     return true;
512 }
513 
Save(const PandaString & filename,ssize_t * bytesWritten,int fd)514 bool ProfileDumpInfo::Save(const PandaString &filename, ssize_t *bytesWritten, int fd)
515 {
516     bool result = Save(fd);
517     if (result) {
518         if (bytesWritten != nullptr) {
519             LOG(INFO, RUNTIME) << "      Profile Saver Bingo! and bytes written = " << bytesWritten;
520             int64_t writedBytes = GetFileSizeBytes(filename);
521             *bytesWritten = writedBytes < 0 ? 0 : static_cast<int64_t>(writedBytes);
522         }
523     } else {
524         LOG(ERROR, RUNTIME) << "Failed to save profile info to " << filename;
525     }
526     return result;
527 }
528 
MergeAndSave(const PandaString & filename,ssize_t * bytesWritten,bool force)529 bool ProfileDumpInfo::MergeAndSave(const PandaString &filename, ssize_t *bytesWritten, bool force)
530 {
531     // NB! we using READWRITE mode to leave the creation job to framework layer.
532     ark::os::unix::file::File myfile = ark::os::file::Open(filename, ark::os::file::Mode::READWRITE);
533     if (!myfile.IsValid()) {
534         LOG(ERROR, RUNTIME) << "Cannot open the profile file" << filename;
535         return false;
536     }
537     ark::os::file::FileHolder fholder(myfile);
538     int fd = myfile.GetFd();
539 
540     LOG(INFO, RUNTIME) << "  Step3.2: starting merging ***";
541     PandaString error;
542     ProfileDumpInfo fileDumpInfo;
543     ProfileLoadSatus status = fileDumpInfo.LoadInternal(fd, &error);
544     if (status == PROFILE_LOAD_SUCCESS || status == PROFILE_LOAD_EMPTYFILE) {
545         bool isMergeWith = MergeWith(fileDumpInfo);
546         if (isMergeWith && dumpInfo_ == fileDumpInfo.dumpInfo_) {
547             if (bytesWritten != nullptr) {
548                 *bytesWritten = 0;
549             }
550             LOG(INFO, RUNTIME) << "  No Saving as no change byte_written = 0";
551             if (status != PROFILE_LOAD_EMPTYFILE) {
552                 return true;
553             }
554         } else if (!isMergeWith) {
555             LOG(INFO, RUNTIME) << "  No Saving as Could not merge previous profile data from file " << filename;
556             if (!force) {
557                 return false;
558             }
559         }
560     } else if (force && ((status == PROFILE_LOAD_VERSION_MISMATCH) || (status == PROFILE_LOAD_BAD_DATA))) {
561         LOG(INFO, RUNTIME) << "  Clearing bad or mismatch version profile data from file " << filename << ": " << error;
562     } else {
563         LOG(INFO, RUNTIME) << "  No Saving as Could not load profile data from file " << filename << ": " << error;
564         return false;
565     }
566 
567     LOG(INFO, RUNTIME) << "  Step3.3: starting Saving ***";
568     LOG(INFO, RUNTIME) << "      clear file data firstly";
569     if (!myfile.ClearData()) {
570         LOG(INFO, RUNTIME) << "Could not clear profile file: " << filename;
571         return false;
572     }
573 
574     return Save(filename, bytesWritten, fd);
575 }
576 
577 }  // namespace ark
578