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