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