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