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