• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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