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