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