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