1 /*
2 * Copyright (c) 2021-2022 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 #include "utilities.h"
16 #include <zlib.h>
17 #if is_mingw
18 #include <io.h>
19 #endif
20
21 namespace OHOS {
22 namespace Developtools {
23 namespace HiPerf {
HoldStringView(std::string_view view)24 const char *MemoryHold::HoldStringView(std::string_view view)
25 {
26 if (view.size() == 0) {
27 return "";
28 }
29 try {
30 // for null end
31 char *p = new char[view.size() + 1];
32 if (p == nullptr) {
33 return "";
34 }
35 p[view.size()] = '\0';
36 std::copy(view.data(), view.data() + view.size(), p);
37 holder_.emplace_back(p);
38 return p;
39 } catch (...) {
40 return "";
41 }
42 return "";
43 }
44
CanonicalizeSpecPath(const char * src)45 std::string CanonicalizeSpecPath(const char* src)
46 {
47 if (src == nullptr || strlen(src) >= PATH_MAX) {
48 fprintf(stderr, "Error: CanonicalizeSpecPath %s failed", src);
49 return "";
50 }
51 char resolvedPath[PATH_MAX] = { 0 };
52 #if defined(_WIN32)
53 if (!_fullpath(resolvedPath, src, PATH_MAX)) {
54 fprintf(stderr, "Error: _fullpath %s failed", src);
55 return "";
56 }
57 #else
58 if (access(src, F_OK) == 0) {
59 if (realpath(src, resolvedPath) == nullptr) {
60 fprintf(stderr, "Error: realpath %s failed", src);
61 return "";
62 }
63 } else {
64 std::string fileName(src);
65 if (fileName.find("..") == std::string::npos) {
66 if (sprintf_s(resolvedPath, PATH_MAX, "%s", src) == -1) {
67 fprintf(stderr, "Error: sprintf_s %s failed", src);
68 return "";
69 }
70 } else {
71 fprintf(stderr, "Error: find .. %s failed", src);
72 return "";
73 }
74 }
75 #endif
76 std::string res(resolvedPath);
77 return res;
78 }
79
RoundUp(uint32_t x,const int align)80 uint32_t RoundUp(uint32_t x, const int align)
81 {
82 return (((x) + (align)-1) / (align)) * (align);
83 }
84
StringReplace(std::string source,const std::string & from,const std::string & to)85 std::string StringReplace(std::string source, const std::string &from, const std::string &to)
86 {
87 size_t pos = 0;
88 std::string result;
89 // find
90 while ((pos = source.find(from)) != std::string::npos) {
91 // replace
92 result.append(source.substr(0, pos) + to);
93 source.erase(0, pos + from.length());
94 }
95 // add last token
96 result.append(source);
97 return result;
98 }
99
SubStringCount(const std::string & source,const std::string & sub)100 size_t SubStringCount(const std::string &source, const std::string &sub)
101 {
102 size_t count(0);
103 size_t pos(0);
104 if (sub.empty()) {
105 return source.size();
106 }
107 while ((pos = source.find(sub, pos)) != std::string::npos) {
108 pos += sub.size();
109 count++;
110 }
111 return count;
112 }
113
StringSplit(std::string source,std::string split)114 std::vector<std::string> StringSplit(std::string source, std::string split)
115 {
116 std::vector<std::string> result;
117
118 // find
119 if (!split.empty()) {
120 size_t pos = 0;
121 while ((pos = source.find(split)) != std::string::npos) {
122 // split
123 std::string token = source.substr(0, pos);
124 if (!token.empty()) {
125 result.push_back(token);
126 }
127 source.erase(0, pos + split.length());
128 }
129 }
130 // add last token
131 if (!source.empty()) {
132 result.push_back(source);
133 }
134 return result;
135 }
StdoutRecord(const std::string & tempFile,const std::string & mode)136 StdoutRecord::StdoutRecord(const std::string &tempFile, const std::string &mode)
137 {
138 if (!tempFile.empty()) {
139 std::string resolvedPath = CanonicalizeSpecPath(tempFile.c_str());
140 recordFile_ = fopen(resolvedPath.c_str(), mode.c_str());
141 if (recordFile_ == nullptr) {
142 HLOGE("tmpfile create failed '%s' with mode '%s'", tempFile.c_str(), mode.c_str());
143 } else {
144 // auto start it
145 Start();
146 }
147 }
148 }
Start()149 bool StdoutRecord::Start()
150 {
151 content_ = EMPTY_STRING;
152 fflush(stdout);
153
154 // we will save output here
155 if (recordFile_ == nullptr) {
156 recordFile_ = std::tmpfile();
157 }
158 if (recordFile_ == nullptr) {
159 // try second way
160 std::string fileName = "temp.stdout";
161 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
162 recordFile_ = fopen(resolvedPath.c_str(), "w+");
163 if (recordFile_ == nullptr) {
164 HLOGF("tmpfile create failed '%s'", fileName.c_str());
165 return false;
166 }
167 }
168
169 // we save the stdout
170 stdoutFile_ = OHOS::UniqueFd(dup(STDOUT_FILENO));
171 if (stdoutFile_ == -1) {
172 HLOGF("std dup failed");
173 return false;
174 }
175
176 // setup temp file as stdout
177 if (dup2(fileno(recordFile_), STDOUT_FILENO) != -1) {
178 stop_ = false;
179 return true;
180 } else {
181 HLOGF("std dup2 failed");
182 return false;
183 }
184 }
185
Stop()186 std::string StdoutRecord::Stop()
187 {
188 if (stop_)
189 return content_;
190 fflush(stdout);
191 // restore fd
192 dup2(stdoutFile_, STDOUT_FILENO);
193
194 // return file content
195 if (recordFile_ != nullptr) {
196 const long fileLength = lseek(fileno(recordFile_), 0, SEEK_END);
197 content_.resize(fileLength);
198 lseek(fileno(recordFile_), 0, SEEK_SET);
199 const long len = read(fileno(recordFile_), content_.data(), fileLength);
200 std::fclose(recordFile_);
201 recordFile_ = nullptr;
202 if (len < 0) {
203 HLOGE("tmp file read failed (try read %ld)", fileLength);
204 } else if (len < fileLength) {
205 HLOGE("not all the data is read, lost %ld/%ld bytes", fileLength - len, fileLength);
206 }
207 } else {
208 HLOGE("recordFile_ is nullptr");
209 }
210 stop_ = true;
211 return content_;
212 }
213
IsDigits(const std::string & str)214 bool IsDigits(const std::string &str)
215 {
216 if (str.empty()) {
217 return false;
218 } else {
219 return std::all_of(str.begin(), str.end(), ::isdigit);
220 }
221 }
222
IsHexDigits(const std::string & str)223 bool IsHexDigits(const std::string &str)
224 {
225 if (str.empty()) {
226 return false;
227 }
228 const std::string prefix {"0x"};
229 std::string effectStr {str};
230 if (prefix.compare(0, prefix.size(), effectStr.substr(0, prefix.size())) == 0) {
231 effectStr = effectStr.substr(prefix.size(), effectStr.size() - prefix.size());
232 }
233 if (effectStr.empty()) {
234 return false;
235 }
236 std::size_t start {0};
237 for (; start < effectStr.size(); ++start) {
238 if (effectStr[start] == '0') {
239 continue;
240 }
241 break;
242 }
243 if (start == effectStr.size()) {
244 effectStr = "0";
245 }
246 return std::all_of(effectStr.begin(), effectStr.end(), ::isxdigit);
247 }
248
IsDir(const std::string & path)249 bool IsDir(const std::string &path)
250 {
251 struct stat st;
252 if (stat(path.c_str(), &st) == 0) {
253 return S_ISDIR(st.st_mode);
254 }
255 return false;
256 }
257
IsPath(const std::string & fileName)258 bool IsPath(const std::string &fileName)
259 {
260 HLOG_ASSERT(!fileName.empty());
261 if (fileName[0] == PATH_SEPARATOR) {
262 return true;
263 }
264 const int prefixPathLen = 2;
265 if (fileName.substr(0, prefixPathLen) == "./") {
266 return true;
267 }
268 return false;
269 }
270
PlatformPathConvert(const std::string & path)271 std::string PlatformPathConvert(const std::string &path)
272 {
273 #if is_mingw
274 return StringReplace(path, "/", "\\");
275 #else
276 return path;
277 #endif
278 }
279
ReadFileToString(const std::string & fileName)280 std::string ReadFileToString(const std::string &fileName)
281 {
282 std::ifstream inputString(fileName, std::ios::in);
283 if (!inputString or !inputString.is_open()) {
284 return EMPTY_STRING;
285 }
286 std::istreambuf_iterator<char> firstIt = {inputString};
287 std::istreambuf_iterator<char> lastIt = {};
288
289 std::string content(firstIt, lastIt);
290 return content;
291 }
292
ReadFileToString(const std::string & fileName,std::string & fileData,size_t fileSize)293 bool ReadFileToString(const std::string &fileName, std::string &fileData, size_t fileSize)
294 {
295 fileData.clear();
296 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
297 OHOS::UniqueFd fd(open(resolvedPath.c_str(), O_RDONLY | O_BINARY));
298 if (fileSize == 0) {
299 struct stat fileStat;
300 if (fstat(fd.Get(), &fileStat) != -1 && fileStat.st_size > 0) {
301 fileData.reserve(fileStat.st_size);
302 }
303 } else {
304 fileData.reserve(fileSize);
305 }
306
307 char buf[BUFSIZ] __attribute__((__uninitialized__));
308 ssize_t readSize;
309 while ((readSize = read(fd.Get(), &buf[0], sizeof(buf))) > 0) {
310 fileData.append(buf, readSize);
311 }
312 return (readSize == 0) ? true : false;
313 }
314
WriteStringToFile(const std::string & fileName,const std::string & value)315 bool WriteStringToFile(const std::string &fileName, const std::string &value)
316 {
317 std::ofstream output(fileName, std::ios::out);
318 if (!output) {
319 return false;
320 }
321 output << value;
322
323 return output.good();
324 }
325
IsRoot()326 bool IsRoot()
327 {
328 #if is_linux || is_ohos
329 static bool isRoot = (getuid() == 0);
330 return isRoot;
331 #else
332 return true;
333 #endif
334 }
335
PowerOfTwo(uint64_t n)336 bool PowerOfTwo(uint64_t n)
337 {
338 return n && (!(n & (n - 1)));
339 }
340
ReadIntFromProcFile(const std::string & path,int & value)341 bool ReadIntFromProcFile(const std::string &path, int &value)
342 {
343 std::string s = ReadFileToString(path);
344 if (s.empty()) {
345 return false;
346 }
347 value = std::stoi(s);
348 return true;
349 }
350
WriteIntToProcFile(const std::string & path,int value)351 bool WriteIntToProcFile(const std::string &path, int value)
352 {
353 std::string s = std::to_string(value);
354
355 return WriteStringToFile(path, s);
356 }
357
358 // compress specified dataFile into gzip file
CompressFile(const std::string & dataFile,const std::string & destFile)359 bool CompressFile(const std::string &dataFile, const std::string &destFile)
360 {
361 std::string resolvedPath = CanonicalizeSpecPath(dataFile.c_str());
362 FILE *fp = fopen(resolvedPath.c_str(), "rb");
363 if (fp == nullptr) {
364 HLOGE("Fail to open data file %s", dataFile.c_str());
365 perror("Fail to fopen(rb)");
366 return false;
367 }
368
369 std::unique_ptr<gzFile_s, decltype(&gzclose)> fgz(gzopen(destFile.c_str(), "wb"), gzclose);
370 if (fgz == nullptr) {
371 HLOGE("Fail to call gzopen(%s)", destFile.c_str());
372 fclose(fp);
373 return false;
374 }
375
376 std::vector<char> buf(COMPRESS_READ_BUF_SIZE);
377 size_t len = 0;
378 while ((len = fread(buf.data(), sizeof(uint8_t), buf.size(), fp))) {
379 if (gzwrite(fgz.get(), buf.data(), len) == 0) {
380 HLOGE("Fail to call gzwrite for %zu bytes", len);
381 fclose(fp);
382 return false;
383 }
384 }
385 if (!feof(fp)) {
386 if (ferror(fp) != 0) {
387 HLOGE("ferror return err");
388 fclose(fp);
389 return false;
390 }
391 }
392 if (fclose(fp) < 0) {
393 return false;
394 }
395 return true;
396 }
397
398 // uncompress specified gzip file into dataFile
UncompressFile(const std::string & gzipFile,const std::string & dataFile)399 bool UncompressFile(const std::string &gzipFile, const std::string &dataFile)
400 {
401 std::string resolvedPath = CanonicalizeSpecPath(dataFile.c_str());
402 FILE *fp = fopen(resolvedPath.c_str(), "wb");
403 if (fp == nullptr) {
404 HLOGE("Fail to open data file %s", dataFile.c_str());
405 perror("Fail to fopen(rb)");
406 return false;
407 }
408 std::unique_ptr<gzFile_s, decltype(&gzclose)> fgz(gzopen(gzipFile.c_str(), "rb"), gzclose);
409 if (fgz == nullptr) {
410 HLOGE("Fail to call gzopen(%s)", gzipFile.c_str());
411 fclose(fp);
412 return false;
413 }
414
415 std::vector<char> buf(COMPRESS_READ_BUF_SIZE);
416 z_size_t len = 0;
417 while ((len = gzfread(buf.data(), sizeof(uint8_t), buf.size(), fgz.get()))) {
418 if (len != fwrite(buf.data(), sizeof(uint8_t), len, fp)) {
419 HLOGE("Fail to call fwrite for %zu bytes", len);
420 fclose(fp);
421 return false;
422 }
423 }
424 if (!gzeof(fgz.get())) {
425 int rc = 0;
426 const char *err = gzerror(fgz.get(), &rc);
427 if (rc != Z_OK) {
428 HLOGE("gzfread return %d:%s", rc, err);
429 fclose(fp);
430 return false;
431 }
432 }
433 if (fclose(fp) < 0) {
434 return false;
435 }
436 return true;
437 }
438
StringTrim(std::string & string)439 std::string &StringTrim(std::string &string)
440 {
441 if (!string.empty()) {
442 string.erase(0, string.find_first_not_of(" "));
443 string.erase(string.find_last_not_of(" ") + 1);
444 }
445 return string;
446 }
447
GetEntriesInDir(const std::string & basePath)448 std::vector<std::string> GetEntriesInDir(const std::string &basePath)
449 {
450 std::vector<std::string> result;
451 DIR *dir = opendir(basePath.c_str());
452 if (dir == nullptr) {
453 return result;
454 }
455 dirent *entry;
456 while ((entry = readdir(dir)) != nullptr) {
457 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
458 continue;
459 }
460 result.push_back(entry->d_name);
461 }
462 closedir(dir);
463 return result;
464 }
465
GetSubDirs(const std::string & basePath)466 std::vector<std::string> GetSubDirs(const std::string &basePath)
467 {
468 std::vector<std::string> entries = GetEntriesInDir(basePath);
469 std::vector<std::string> result = {};
470 for (std::size_t index = 0; index < entries.size(); ++index) {
471 if (IsDir(basePath + "/" + entries[index])) {
472 result.push_back(std::move(entries[index]));
473 }
474 }
475 return result;
476 }
477
IsSameCommand(const std::string & cmdLine,const std::string & cmdName)478 bool IsSameCommand(const std::string &cmdLine, const std::string &cmdName)
479 {
480 std::vector<std::string> cmdpaths = StringSplit(cmdLine, "/");
481 if (!cmdpaths.empty()) {
482 if (strcmp(cmdpaths.back().c_str(), cmdName.c_str()) == 0) {
483 return true;
484 }
485 }
486 return false;
487 }
488
GetSubthreadIDs(const pid_t pid)489 std::vector<pid_t> GetSubthreadIDs(const pid_t pid)
490 {
491 std::string path {"/proc/"};
492 path += std::to_string(pid);
493 path += "/task/";
494 auto tids = GetSubDirs(path);
495 std::vector<pid_t> res {};
496 for (auto tidStr : tids) {
497 pid_t tid = static_cast<pid_t>(std::stoul(tidStr, nullptr));
498 if (tid == pid) {
499 continue;
500 }
501 res.push_back(tid);
502 }
503 return res;
504 }
505
StringStartsWith(const std::string & string,const std::string & with)506 bool StringStartsWith(const std::string &string, const std::string &with)
507 {
508 return string.find(with) == 0;
509 }
510
StringEndsWith(const std::string & string,const std::string & with)511 bool StringEndsWith(const std::string &string, const std::string &with)
512 {
513 if (string.empty()) {
514 // empty string only end with empty string
515 if (with.empty()) {
516 return true;
517 } else {
518 return false;
519 }
520 }
521 return string.rfind(with) == (string.length() - with.length());
522 }
523
HexDump(const void * buf,size_t size,size_t maxSize)524 void HexDump(const void *buf, size_t size, size_t maxSize)
525 {
526 const unsigned char *byteBuf = static_cast<const unsigned char *>(buf);
527 const size_t dumpByteEachLine = 8;
528 size_t outputBytes = 0;
529 if (!maxSize) {
530 outputBytes = size;
531 } else {
532 outputBytes = std::min(size, maxSize);
533 }
534
535 for (size_t i = 0; i <= outputBytes; i += dumpByteEachLine) {
536 HLOGM(" %02zu: %s ", i, BufferToHexString(byteBuf, dumpByteEachLine).c_str());
537 byteBuf += dumpByteEachLine;
538 }
539 }
540
BufferToHexString(const std::vector<unsigned char> & vec)541 std::string BufferToHexString(const std::vector<unsigned char> &vec)
542 {
543 return BufferToHexString(vec.data(), vec.size());
544 }
545
BufferToHexString(const unsigned char buf[],size_t size)546 std::string BufferToHexString(const unsigned char buf[], size_t size)
547 {
548 std::stringstream ss;
549 ss << size << ":";
550 for (size_t i = 0; i < size; i++) {
551 ss << " 0x" << std::setfill('0') << std::setw(BYTE_PRINT_WIDTH) << std::hex
552 << (unsigned short)buf[i];
553 }
554 return ss.str();
555 }
556 } // namespace HiPerf
557 } // namespace Developtools
558 } // namespace OHOS
559
560 // this will also used for libunwind head (out of namespace)
561 #if is_mingw
562 using namespace OHOS::Developtools::HiPerf;
GetLastErrorString()563 std::string GetLastErrorString()
564 {
565 LPVOID lpMsgBuf;
566 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
567 FORMAT_MESSAGE_IGNORE_INSERTS,
568 NULL, GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, NULL);
569 std::string error((LPTSTR)lpMsgBuf);
570 LocalFree(lpMsgBuf);
571 return error;
572 }
573
mmap(void * addr,size_t length,int prot,int flags,int fd,size_t offset)574 void *mmap(void *addr, size_t length, int prot, int flags, int fd, size_t offset)
575 {
576 HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
577 if (FileHandle == INVALID_HANDLE_VALUE) {
578 return MMAP_FAILED;
579 }
580
581 HLOGV("fd is %d", fd);
582
583 HANDLE FileMappingHandle = ::CreateFileMappingW(FileHandle, 0, PAGE_READONLY, 0, 0, 0);
584 if (FileMappingHandle == nullptr) {
585 HLOGE("CreateFileMappingW %zu Failed with %ld:%s", length, GetLastError(),
586 GetLastErrorString().c_str());
587 return MMAP_FAILED;
588 }
589
590 void *mapAddr = ::MapViewOfFile(FileMappingHandle, FILE_MAP_READ, 0, 0, 0);
591 if (mapAddr == nullptr) {
592 HLOGE("MapViewOfFile %zu Failed with %ld:%s", length, GetLastError(),
593 GetLastErrorString().c_str());
594 return MMAP_FAILED;
595 }
596
597 // Close all the handles except for the view. It will keep the other handles
598 // alive.
599 ::CloseHandle(FileMappingHandle);
600 return mapAddr;
601 }
602
munmap(void * addr,size_t)603 int munmap(void *addr, size_t)
604 {
605 /*
606 On success, munmap() returns 0. On failure, it returns -1, and
607 errno is set to indicate the error (probably to EINVAL).
608
609 UnmapViewOfFile function (memoryapi.h)
610
611 If the function succeeds, the return value is nonzero.
612 If the function fails, the return value is zero. To get extended error information, call
613 GetLastError.
614 */
615 return !UnmapViewOfFile(addr);
616 }
617 #endif
618