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