1 /*
2  * Copyright (c) 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 "rs_profiler_utils.h"
17 
18 #include <chrono>
19 #include <fcntl.h>
20 #include <filesystem>
21 #include <fstream>
22 #include <regex>
23 #include <sstream>
24 #include <string>
25 #ifndef RENDER_PROFILER_APPLICATION
26 #include <dirent.h>
27 #include <sched.h>
28 #include <securec.h>
29 #include <unistd.h>
30 
31 #include "directory_ex.h"
32 #include "rs_profiler_log.h"
33 #else
34 #include "rs_adapt.h"
35 #endif
36 
37 namespace OHOS::Rosen {
38 
39 // Time routines
Now()40 uint64_t Utils::Now()
41 {
42     return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch())
43         .count();
44 }
45 
ToSeconds(uint64_t nano)46 double Utils::ToSeconds(uint64_t nano)
47 {
48     constexpr double nanoToSeconds = 1e-9;
49     return nano * nanoToSeconds;
50 }
51 
ToNanoseconds(double seconds)52 uint64_t Utils::ToNanoseconds(double seconds)
53 {
54     constexpr double secondsToNano = 1e9;
55     return seconds * secondsToNano;
56 }
57 
58 #ifdef RENDER_PROFILER_APPLICATION
59 // Cpu routines
GetCpuId()60 int32_t Utils::GetCpuId()
61 {
62     return 0;
63 }
64 
SetCpuAffinity(uint32_t cpu)65 void Utils::SetCpuAffinity(uint32_t cpu) {}
66 
GetCpuAffinity(uint32_t cpu)67 bool Utils::GetCpuAffinity(uint32_t cpu)
68 {
69     return false; // NOLINT
70 }
71 
72 // Process routines
GetPid()73 pid_t Utils::GetPid()
74 {
75     return _getpid();
76 }
77 #else
78 // Cpu routines
GetCpuId()79 int32_t Utils::GetCpuId()
80 {
81     return sched_getcpu();
82 }
83 
SetCpuAffinity(uint32_t cpu)84 void Utils::SetCpuAffinity(uint32_t cpu)
85 {
86     cpu_set_t set = {};
87     CPU_ZERO(&set);
88     CPU_SET(cpu, &set); // NOLINT
89     sched_setaffinity(getpid(), sizeof(set), &set);
90 }
91 
GetCpuAffinity(uint32_t cpu)92 bool Utils::GetCpuAffinity(uint32_t cpu)
93 {
94     cpu_set_t mask = {};
95     return (sched_getaffinity(0, sizeof(cpu_set_t), &mask) != -1) && CPU_ISSET(cpu, &mask); // NOLINT
96 }
97 
98 // Process routines
GetPid()99 pid_t Utils::GetPid()
100 {
101     return getpid();
102 }
103 #endif
104 
105 // String routines
Split(const std::string& string)106 std::vector<std::string> Utils::Split(const std::string& string)
107 {
108     std::istringstream stream(string);
109     std::vector<std::string> parts { std::istream_iterator<std::string> { stream },
110         std::istream_iterator<std::string> {} };
111     return parts;
112 }
113 
Replace(const std::string& susbtring, std::string& string)114 void Utils::Replace(const std::string& susbtring, std::string& string)
115 {
116     std::string::size_type position = string.find(susbtring);
117     while (position != string.npos) {
118         string.replace(position, 1, "");
119         position = string.find(susbtring);
120     }
121 }
122 
ExtractNumber(const std::string& string)123 std::string Utils::ExtractNumber(const std::string& string)
124 {
125     return std::regex_replace(string, std::regex("[^0-9]*([0-9]+).*"), std::string("$1"));
126 }
127 
ToInt8(const std::string& string)128 int8_t Utils::ToInt8(const std::string& string)
129 {
130     return static_cast<int8_t>(ToInt32(string));
131 }
132 
ToInt16(const std::string& string)133 int16_t Utils::ToInt16(const std::string& string)
134 {
135     return static_cast<int16_t>(ToInt32(string));
136 }
137 
ToInt32(const std::string& string)138 int32_t Utils::ToInt32(const std::string& string)
139 {
140     return static_cast<int32_t>(std::atol(string.data()));
141 }
142 
ToInt64(const std::string& string)143 int64_t Utils::ToInt64(const std::string& string)
144 {
145     return std::atoll(string.data());
146 }
147 
ToUint8(const std::string& string)148 uint8_t Utils::ToUint8(const std::string& string)
149 {
150     return ToUint32(string);
151 }
152 
ToUint16(const std::string& string)153 uint16_t Utils::ToUint16(const std::string& string)
154 {
155     return ToUint32(string);
156 }
157 
ToUint32(const std::string& string)158 uint32_t Utils::ToUint32(const std::string& string)
159 {
160     return ToUint64(string);
161 }
162 
ToUint64(const std::string& string)163 uint64_t Utils::ToUint64(const std::string& string)
164 {
165     constexpr int32_t base = 10;
166     char* end = const_cast<char*>(string.data()) + string.size();
167     return std::strtoull(string.data(), &end, base);
168 }
169 
ToFp32(const std::string& string)170 float Utils::ToFp32(const std::string& string)
171 {
172     return static_cast<float>(ToFp64(string));
173 }
174 
ToFp64(const std::string& string)175 double Utils::ToFp64(const std::string& string)
176 {
177     char* end = const_cast<char*>(string.data()) + string.size();
178     return std::strtod(string.data(), &end);
179 }
180 
ToNumber(const std::string& string, int8_t& number)181 void Utils::ToNumber(const std::string& string, int8_t& number)
182 {
183     number = ToInt8(string);
184 }
185 
ToNumber(const std::string& string, int16_t& number)186 void Utils::ToNumber(const std::string& string, int16_t& number)
187 {
188     number = ToInt16(string);
189 }
190 
ToNumber(const std::string& string, int32_t& number)191 void Utils::ToNumber(const std::string& string, int32_t& number)
192 {
193     number = ToInt32(string);
194 }
195 
ToNumber(const std::string& string, int64_t& number)196 void Utils::ToNumber(const std::string& string, int64_t& number)
197 {
198     number = ToInt64(string);
199 }
200 
ToNumber(const std::string& string, uint8_t& number)201 void Utils::ToNumber(const std::string& string, uint8_t& number)
202 {
203     number = ToUint8(string);
204 }
205 
ToNumber(const std::string& string, uint16_t& number)206 void Utils::ToNumber(const std::string& string, uint16_t& number)
207 {
208     number = ToUint16(string);
209 }
210 
ToNumber(const std::string& string, uint32_t& number)211 void Utils::ToNumber(const std::string& string, uint32_t& number)
212 {
213     number = ToUint32(string);
214 }
215 
ToNumber(const std::string& string, uint64_t& number)216 void Utils::ToNumber(const std::string& string, uint64_t& number)
217 {
218     number = ToUint64(string);
219 }
220 
ToNumber(const std::string& string, float& number)221 void Utils::ToNumber(const std::string& string, float& number)
222 {
223     number = ToFp32(string);
224 }
225 
ToNumber(const std::string& string, double& number)226 void Utils::ToNumber(const std::string& string, double& number)
227 {
228     number = ToFp64(string);
229 }
230 
231 // Memory routines
Move(void* destination, size_t destinationSize, const void* source, size_t size)232 bool Utils::Move(void* destination, size_t destinationSize, const void* source, size_t size)
233 {
234     return memmove_s(destination, destinationSize, source, size) == EOK;
235 }
236 
Set(void* data, size_t size, int32_t value, size_t count)237 bool Utils::Set(void* data, size_t size, int32_t value, size_t count)
238 {
239     return memset_s(data, size, value, count) == EOK;
240 }
241 
242 // File system routines
GetRealPath(const std::string& path)243 std::string Utils::GetRealPath(const std::string& path)
244 {
245     std::string realPath;
246     if (!PathToRealPath(path, realPath)) {
247         HRPE("PathToRealPath fails on %s !", path.data());
248         realPath.clear();
249     }
250     return realPath;
251 }
252 
MakePath(const std::string& directory, const std::string& file)253 std::string Utils::MakePath(const std::string& directory, const std::string& file)
254 {
255     return NormalizePath(directory) + file;
256 }
257 
NormalizePath(const std::string& path)258 std::string Utils::NormalizePath(const std::string& path)
259 {
260     return (path.rfind('/') != path.size() - 1) ? path + "/" : path;
261 }
262 
GetFileName(const std::string& path)263 std::string Utils::GetFileName(const std::string& path)
264 {
265 #ifdef RENDER_PROFILER_APPLICATION
266     return std::filesystem::path(path).filename().string();
267 #else
268     std::string filename;
269     const size_t lastSlashIdx = path.rfind('/');
270     if (std::string::npos != lastSlashIdx) {
271         filename = path.substr(lastSlashIdx + 1);
272     }
273     return filename;
274 #endif
275 }
276 
GetDirectory(const std::string& path)277 std::string Utils::GetDirectory(const std::string& path)
278 {
279 #ifdef RENDER_PROFILER_APPLICATION
280     return std::filesystem::path(path).parent_path().string();
281 #else
282     std::string directory;
283     const size_t lastSlashIdx = path.rfind('/');
284     if (std::string::npos != lastSlashIdx) {
285         directory = path.substr(0, lastSlashIdx);
286     }
287     return directory;
288 #endif
289 }
290 
IsDirectory(const std::string& path)291 bool Utils::IsDirectory(const std::string& path)
292 {
293 #ifdef RENDER_PROFILER_APPLICATION
294     return std::filesystem::is_directory(path);
295 #else
296     struct stat st {};
297     return (stat(path.data(), &st) == 0) && S_ISDIR(st.st_mode);
298 #endif
299 }
300 
IterateDirectory(const std::string& path, std::vector<std::string>& files)301 void Utils::IterateDirectory(const std::string& path, std::vector<std::string>& files)
302 {
303     const std::string realPath = GetRealPath(path);
304     if (realPath.empty()) {
305         return;
306     }
307 
308 #ifdef RENDER_PROFILER_APPLICATION
309     for (auto const& entry : std::filesystem::recursive_directory_iterator(path)) {
310         if (entry.is_directory()) {
311             IterateDirectory(entry.path().string(), files);
312         } else {
313             files.push_back(entry.path().string());
314         }
315     }
316 #else
317     DIR* directory = opendir(realPath.data());
318     if (!directory) {
319         return;
320     }
321 
322     while (struct dirent* entry = readdir(directory)) {
323         const std::string entryName(entry->d_name);
324         if ((entryName == ".") || (entryName == "..")) {
325             continue;
326         }
327         const std::string entryPath = NormalizePath(realPath) + entryName;
328         if (entry->d_type == DT_DIR) {
329             IterateDirectory(entryPath, files);
330         } else {
331             files.push_back(entryPath);
332         }
333     }
334     closedir(directory);
335 #endif
336 }
337 
LoadLine(const std::string& path, std::string& line)338 void Utils::LoadLine(const std::string& path, std::string& line)
339 {
340     line.clear();
341 
342     const std::string realPath = GetRealPath(path);
343     if (realPath.empty()) {
344         return;
345     }
346 
347     std::ifstream file(realPath);
348     if (file) {
349         std::getline(file, line);
350     }
351 }
352 
LoadLines(const std::string& path, std::vector<std::string>& lines)353 void Utils::LoadLines(const std::string& path, std::vector<std::string>& lines)
354 {
355     lines.clear();
356 
357     const std::string realPath = GetRealPath(path);
358     if (realPath.empty()) {
359         return;
360     }
361 
362     std::ifstream file(realPath);
363     if (file) {
364         std::string line;
365         while (std::getline(file, line)) {
366             lines.emplace_back(line);
367         }
368     }
369 }
370 
LoadContent(const std::string& path, std::string& content)371 void Utils::LoadContent(const std::string& path, std::string& content)
372 {
373     content.clear();
374 
375     const std::string realPath = GetRealPath(path);
376     if (realPath.empty()) {
377         return;
378     }
379 
380     std::ifstream file(realPath);
381     if (file) {
382         copy(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), std::back_inserter(content));
383         Replace("\r", content);
384         Replace("\n", content);
385     }
386 }
387 
388 static std::stringstream g_recordInMemory(std::ios::in | std::ios::out | std::ios::binary);
389 static FILE* g_recordInMemoryFile = reinterpret_cast<FILE*>(1);
390 
IsRecordInMemoryFile(const std::string& path)391 static bool IsRecordInMemoryFile(const std::string& path)
392 {
393     return path == "RECORD_IN_MEMORY";
394 }
395 
HasWriteFlag(const std::string& options)396 static bool HasWriteFlag(const std::string& options)
397 {
398     return options.find('w') != std::string::npos;
399 }
400 
HasAppendFlag(const std::string& options)401 static bool HasAppendFlag(const std::string& options)
402 {
403     return options.find('a') != std::string::npos;
404 }
405 
ShouldFileBeCreated(const std::string& options)406 static bool ShouldFileBeCreated(const std::string& options)
407 {
408     return HasWriteFlag(options) || HasAppendFlag(options);
409 }
410 
FileExists(const std::string& path)411 bool Utils::FileExists(const std::string& path)
412 {
413     if (IsRecordInMemoryFile(path)) {
414         return true;
415     }
416 #ifdef RENDER_PROFILER_APPLICATION
417     return std::filesystem::exists(path);
418 #else
419     struct stat st {};
420     return (stat(path.data(), &st) == 0) && S_ISREG(st.st_mode);
421 #endif
422 }
423 
FileDelete(const std::string& path)424 bool Utils::FileDelete(const std::string& path)
425 {
426     if (IsRecordInMemoryFile(path)) {
427         g_recordInMemory = std::stringstream(std::ios::in | std::ios::out | std::ios::binary);
428         return true;
429     }
430 
431     const std::string realPath = GetRealPath(path);
432     if (!FileExists(realPath)) {
433         return false;
434     }
435 
436     return std::filesystem::remove(realPath);
437 }
438 
FileOpen(const std::string& path, const std::string& options)439 FILE* Utils::FileOpen(const std::string& path, const std::string& options)
440 {
441     if (IsRecordInMemoryFile(path)) {
442         g_recordInMemory.seekg(0, std::ios_base::beg);
443         g_recordInMemory.seekp(0, std::ios_base::beg);
444         return g_recordInMemoryFile;
445     }
446 
447     const std::string realPath = GetRealPath(path);
448     if (realPath.empty()) {
449         HRPE("FileOpen: '%s' is invalid!", path.data()); // NOLINT
450         return nullptr;
451     }
452 
453 #ifndef RENDER_PROFILER_APPLICATION
454     if (ShouldFileBeCreated(options) && !FileExists(realPath)) {
455         auto file = open(realPath.data(), O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
456         if (file != -1) {
457             close(file);
458         }
459     }
460 #endif
461 
462     auto file = fopen(realPath.data(), options.data());
463     if (!IsFileValid(file)) {
464         HRPE("FileOpen: Cannot open '%s' with options '%s'!", realPath.data(), options.data()); // NOLINT
465     }
466     return file;
467 }
468 
FileClose(FILE* file)469 void Utils::FileClose(FILE* file)
470 {
471     if (file == g_recordInMemoryFile) {
472         g_recordInMemory.seekg(0, std::ios_base::beg);
473         g_recordInMemory.seekp(0, std::ios_base::beg);
474         return;
475     }
476     if (fflush(file) != 0) {
477         HRPE("File flush failed"); // NOLINT
478     }
479     if (fclose(file) != 0) {
480         HRPE("File close failed"); // NOLINT
481     }
482 }
483 
IsFileValid(FILE* file)484 bool Utils::IsFileValid(FILE* file)
485 {
486     return file != nullptr;
487 }
488 
FileSize(FILE* file)489 size_t Utils::FileSize(FILE* file)
490 {
491     if (file == g_recordInMemoryFile) {
492         const auto position = g_recordInMemory.tellg();
493         g_recordInMemory.seekg(0, std::ios_base::end);
494         const auto size = g_recordInMemory.tellg();
495         if (size == -1) {
496             g_recordInMemory.seekg(0, std::ios_base::beg);
497             return 0;
498         }
499         g_recordInMemory.seekg(position, std::ios_base::beg);
500         return static_cast<size_t>(size);
501     }
502     if (!IsFileValid(file)) {
503         return 0;
504     }
505 
506     const auto position = ftell(file);
507     if (position == -1) {
508         return 0;
509     }
510     FileSeek(file, 0, SEEK_END);
511     const auto size = ftell(file);
512     FileSeek(file, position, SEEK_SET);
513     return static_cast<size_t>(size);
514 }
515 
FileTell(FILE* file)516 size_t Utils::FileTell(FILE* file)
517 {
518     if (file == g_recordInMemoryFile) {
519         return g_recordInMemory.tellg();
520     }
521     if (!IsFileValid(file)) {
522         return 0;
523     }
524 
525     return ftell(file);
526 }
527 
FileSeek(FILE* file, int64_t offset, int origin)528 void Utils::FileSeek(FILE* file, int64_t offset, int origin)
529 {
530     if (file == g_recordInMemoryFile) {
531         if (origin == SEEK_SET) {
532             g_recordInMemory.seekg(offset, std::ios_base::beg);
533             g_recordInMemory.seekp(offset, std::ios_base::beg);
534         } else if (origin == SEEK_CUR) {
535             g_recordInMemory.seekg(offset, std::ios_base::cur);
536             g_recordInMemory.seekp(offset, std::ios_base::cur);
537         } else if (origin == SEEK_END) {
538             g_recordInMemory.seekg(offset, std::ios_base::end);
539             g_recordInMemory.seekp(offset, std::ios_base::end);
540         }
541         return;
542     }
543     if (fseek(file, offset, origin) != 0) {
544         HRPE("Failed fseek in file"); // NOLINT
545     }
546 }
547 
FileRead(FILE* file, void* data, size_t size)548 void Utils::FileRead(FILE* file, void* data, size_t size)
549 {
550     if (!data) {
551         HRPE("FileRead: Data is null"); // NOLINT
552         return;
553     }
554 
555     if (file == g_recordInMemoryFile) {
556         g_recordInMemory.read(reinterpret_cast<char*>(data), size);
557         g_recordInMemory.seekp(g_recordInMemory.tellg());
558         return;
559     }
560     if (fread(data, size, 1, file) < 1) {
561         HRPE("FileRead: Error while reading from file"); // NOLINT
562     }
563 }
564 
FileWrite(FILE* file, const void* data, size_t size)565 void Utils::FileWrite(FILE* file, const void* data, size_t size)
566 {
567     const size_t maxDataSize = 2'000'000'000; // To make sure size is a valid value
568     if (!data || (size == 0) || (size > maxDataSize)) {
569         HRPE("FileWrite: data or size is invalid, size %zu", size); // NOLINT
570         return;
571     }
572 
573     if (file == g_recordInMemoryFile) {
574         g_recordInMemory.write(reinterpret_cast<const char*>(data), size);
575         g_recordInMemory.seekg(g_recordInMemory.tellp());
576         return;
577     }
578     if (fwrite(data, size, 1, file) < 1) {
579         HRPE("FileWrite: Error while writing to file"); // NOLINT
580     }
581 }
582 
583 // deprecated
584 void Utils::FileRead(void* data, size_t size, size_t count, FILE* file)
585 {
586     FileRead(file, data, size * count);
587 }
588 
589 // deprecated
590 void Utils::FileWrite(const void* data, size_t size, size_t count, FILE* file)
591 {
592     FileWrite(file, data, size * count);
593 }
594 
595 } // namespace OHOS::Rosen