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