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