• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 
16 #ifndef ECMASCRIPT_COMPILER_OHOS_RUNTIME_BUILD_INFO_H
17 #define ECMASCRIPT_COMPILER_OHOS_RUNTIME_BUILD_INFO_H
18 
19 #include <sys/time.h>
20 #include <fcntl.h>
21 #include <mutex>
22 #include <stdio.h>
23 
24 #include "ecmascript/platform/directory.h"
25 #include "ecmascript/platform/file.h"
26 #include "ecmascript/platform/map.h"
27 #include "libpandafile/file.h"
28 #include "llvm/BinaryFormat/ELF.h"
29 #include "ohos_constants.h"
30 #include "utils/json_parser.h"
31 #include "utils/json_builder.h"
32 
33 namespace panda::ecmascript::ohos {
34 #define RUNTIME_INFO_TYPE(V)                                         \
35     V(AOT_CRASH)                                                     \
36     V(OTHERS)                                                        \
37     V(NONE)                                                          \
38     V(JIT)                                                           \
39     V(JS)                                                            \
40     V(AOT_BUILD)                                                     \
41 
42 enum class RuntimeInfoType {
43     AOT_CRASH,
44     JIT,
45     OTHERS,
46     NONE,
47     JS,
48     AOT_BUILD,
49 };
50 
51 class AotRuntimeInfo {
52 public:
53     constexpr static const int USEC_PER_SEC = 1000 * 1000;
54     constexpr static const int NSEC_PER_USEC = 1000;
55     constexpr static const int NT_GNU_BUILD_ID = 3;
56     constexpr static const int CRASH_INFO_SIZE = 3;
57     constexpr static const int MAX_LENGTH = 255;
58     constexpr static const int BUFFER_SIZE = 4096;
59     constexpr static const int TIME_STAMP_SIZE = 21;
60 
61     constexpr static const int RUNTIME_INDEX_BUILDID = 0;
62     constexpr static const int RUNTIME_INDEX_TIMESTAMP = 1;
63     constexpr static const int RUNTIME_INDEX_TYPE = 2;
64 
GetInstance()65     static AotRuntimeInfo &GetInstance()
66     {
67         static AotRuntimeInfo singleAotRuntimeInfo;
68         return singleAotRuntimeInfo;
69     }
70 
BuildCompileRuntimeInfo(RuntimeInfoType type,const std::string & pgoPath)71     void BuildCompileRuntimeInfo(RuntimeInfoType type, const std::string &pgoPath)
72     {
73         std::unique_lock<std::mutex> lock(fileMutex_);
74         static char soBuildId[NAME_MAX] = { '\0' };
75         if (!GetRuntimeBuildId(soBuildId, NAME_MAX) || IsCharEmpty(soBuildId)) {
76             LOG_ECMA(INFO) << "can't get so buildId.";
77             return;
78         }
79         std::string realOutPath;
80         if (!ecmascript::RealPath(pgoPath, realOutPath, false)) {
81             LOG_ECMA(INFO) << "Build compile pgo path fail.";
82             return;
83         }
84         static char lines[MAX_LENGTH][BUFFER_SIZE];
85         for (int i = 0; i < MAX_LENGTH; i++) {
86             memset_s(lines[i], BUFFER_SIZE, '\0', BUFFER_SIZE);
87         }
88         GetRuntimeInfoByPath(lines, realOutPath.c_str(), soBuildId);
89         static char timestamp[TIME_STAMP_SIZE] = { '\0' };
90         if (!GetMicrosecondsTimeStamp(timestamp, TIME_STAMP_SIZE)) {
91             return;
92         }
93 
94         int lineCount = getLength(lines, MAX_LENGTH);
95         if (lineCount < MAX_LENGTH) {
96             if (!BuildRuntimeInfoPart(lines[lineCount], soBuildId, timestamp, type)) {
97                 return;
98             }
99         }
100         SetRuntimeInfo(realOutPath.c_str(), lines, MAX_LENGTH);
101     }
102 
BuildCrashRuntimeInfo(RuntimeInfoType type)103     void BuildCrashRuntimeInfo(RuntimeInfoType type)
104     {
105         std::unique_lock<std::mutex> lock(fileMutex_);
106         static char soBuildId[NAME_MAX] = { '\0' };
107         if (!GetRuntimeBuildId(soBuildId, NAME_MAX) || IsCharEmpty(soBuildId)) {
108             return;
109         }
110         static char lines[MAX_LENGTH][BUFFER_SIZE];
111         for (int i = 0; i < MAX_LENGTH; i++) {
112             memset_s(lines[i], BUFFER_SIZE, '\0', BUFFER_SIZE);
113         }
114         GetCrashRuntimeInfoList(lines);
115         static char timestamp[TIME_STAMP_SIZE] = { '\0' };
116         if (!GetMicrosecondsTimeStamp(timestamp, TIME_STAMP_SIZE)) {
117             return;
118         }
119         int lineCount = getLength(lines, MAX_LENGTH);
120         if (lineCount < MAX_LENGTH) {
121             if (!BuildRuntimeInfoPart(lines[lineCount], soBuildId, timestamp, type)) {
122                 return;
123             }
124         }
125         static char realOutPath[PATH_MAX] = { '\0' };
126         if (!GetCrashSandBoxRealPath(realOutPath, PATH_MAX) || IsCharEmpty(realOutPath)) {
127             return;
128         }
129         SetRuntimeInfo(realOutPath, lines, MAX_LENGTH);
130     }
131 
132     int GetCompileCountByType(RuntimeInfoType type, const std::string &pgoRealPath = "")
133     {
134         std::map<RuntimeInfoType, int> escapeMap = CollectCrashSum(pgoRealPath);
135         if (escapeMap.count(type) == 0) {
136             return 0;
137         }
138         return escapeMap[type];
139     }
140 
141     std::map<RuntimeInfoType, int> CollectCrashSum(const std::string &pgoRealPath = "")
142     {
143         if (IsLoadedMap()) {
144             return escapeMap_;
145         }
146         char (*lines)[BUFFER_SIZE] = new char[MAX_LENGTH][BUFFER_SIZE];
147         if (lines == nullptr) {
148             LOG_ECMA(FATAL) << "lines allocate failed";
149         }
150         for (int i = 0; i < MAX_LENGTH; i++) {
151             memset_s(lines[i], BUFFER_SIZE, '\0', BUFFER_SIZE);
152         }
153         if (IsCharEmpty(pgoRealPath.c_str())) {
154             GetCrashRuntimeInfoList(lines);
155         } else {
156             GetRealPathRuntimeInfoList(lines, pgoRealPath);
157         }
158         char *typeChar = new char[NAME_MAX];
159         for (int i = 0; i < MAX_LENGTH; i++) {
160             if (lines[i][0] != '\0') {
161                 if (strcpy_s(typeChar, NAME_MAX, GetInfoFromBuffer(lines[i], RUNTIME_INDEX_TYPE)) !=0) {
162                     continue;
163                 }
164                 std::string typeStr(typeChar);
165                 escapeMap_[GetRuntimeInfoTypeByStr(typeStr)]++;
166             }
167         }
168         SetLoadedMap(true);
169         delete[] typeChar;
170         delete[] lines;
171         return escapeMap_;
172     }
173 
GetRuntimeInfoTypeStr(const RuntimeInfoType type)174     const char *GetRuntimeInfoTypeStr(const RuntimeInfoType type) const
175     {
176         switch (type) {
177             case RuntimeInfoType::AOT_CRASH:
178                 return "AOT_CRASH";
179             case RuntimeInfoType::JIT:
180                 return "JIT";
181             case RuntimeInfoType::OTHERS:
182                 return "OTHERS";
183             case RuntimeInfoType::NONE:
184                 return "NONE";
185             case RuntimeInfoType::JS:
186                 return "JS";
187             case RuntimeInfoType::AOT_BUILD:
188                 return "AOT_BUILD";
189             default:
190                 return "NONE";
191         }
192     }
193 
GetRuntimeInfoTypeByStr(std::string & type)194     RuntimeInfoType GetRuntimeInfoTypeByStr(std::string &type) const
195     {
196         const std::map<std::string, RuntimeInfoType> strMap = {
197 #define RUNTIME_INFO_TYPE_MAP(name) { #name, RuntimeInfoType::name },
198         RUNTIME_INFO_TYPE(RUNTIME_INFO_TYPE_MAP)
199 #undef RUNTIME_INFO_TYPE_MAP
200         };
201         if (strMap.count(type) > 0) {
202             return strMap.at(type);
203         }
204         return RuntimeInfoType::NONE;
205     }
206 
GetRuntimeBuildId(char * buildId,int length)207     virtual bool GetRuntimeBuildId(char *buildId, int length) const
208     {
209         if (!FileExist(OhosConstants::RUNTIME_SO_PATH)) {
210             return false;
211         }
212         static char realPath[PATH_MAX] = { '\0' };
213         if (!ecmascript::RealPathByChar(OhosConstants::RUNTIME_SO_PATH, realPath, PATH_MAX, false)) {
214             return false;
215         }
216         if (!FileExist(realPath)) {
217             return false;
218         }
219         ecmascript::MemMap fileMap = ecmascript::FileMap(realPath, FILE_RDONLY, PAGE_PROT_READ);
220         if (fileMap.GetOriginAddr() == nullptr) {
221             return false;
222         }
223         ParseELFSectionsForBuildId(fileMap, buildId, length);
224         ecmascript::FileUnMap(fileMap);
225         fileMap.Reset();
226         return true;
227     }
228 
GetMicrosecondsTimeStamp(char * timestamp,size_t length)229     virtual bool GetMicrosecondsTimeStamp(char *timestamp, size_t length) const
230     {
231         time_t current_time;
232         if (time(&current_time) == -1) {
233             return false;
234         }
235         struct tm *local_time = localtime(&current_time);
236         if (local_time == NULL) {
237             return false;
238         }
239         size_t result = strftime(timestamp, length, "%Y-%m-%d %H:%M:%S", local_time);
240         if (result == 0) {
241             return false;
242         }
243         return true;
244     }
245 
GetCrashSandBoxRealPath(char * realOutPath,size_t length)246     virtual bool GetCrashSandBoxRealPath(char *realOutPath, size_t length) const
247     {
248         if (!ecmascript::RealPathByChar(OhosConstants::SANDBOX_ARK_PROFILE_PATH, realOutPath, length, false)) {
249             return false;
250         }
251         if (strcat_s(realOutPath, NAME_MAX, OhosConstants::PATH_SEPARATOR) != 0) {
252             return false;
253         }
254         if (strcat_s(realOutPath, NAME_MAX, OhosConstants::AOT_RUNTIME_INFO_NAME) !=0) {
255             return false;
256         }
257         return true;
258     }
259 protected:
260 
IsCharEmpty(const char * value)261     bool IsCharEmpty(const char *value) const
262     {
263         if (value == NULL || *value == '\0') {
264             return true;
265         }
266         return false;
267     }
268 
BuildRuntimeInfoPart(char * runtimeInfoPart,const char * soBuildId,const char * timestamp,RuntimeInfoType type)269     bool BuildRuntimeInfoPart(char *runtimeInfoPart, const char *soBuildId, const char *timestamp,
270         RuntimeInfoType type) const
271     {
272         if (strcat_s(runtimeInfoPart, NAME_MAX, soBuildId) != 0) {
273             return false;
274         }
275         if (strcat_s(runtimeInfoPart, NAME_MAX, OhosConstants::SPLIT_STR) != 0) {
276             return false;
277         }
278         if (strcat_s(runtimeInfoPart, NAME_MAX, timestamp) != 0) {
279             return false;
280         }
281         if (strcat_s(runtimeInfoPart, NAME_MAX, OhosConstants::SPLIT_STR) != 0) {
282             return false;
283         }
284         if (strcat_s(runtimeInfoPart, NAME_MAX, GetRuntimeInfoTypeStr(type)) != 0) {
285             return false;
286         }
287         return true;
288     }
289 
GetInfoFromBuffer(char * line,int index)290     const char *GetInfoFromBuffer(char *line, int index) const
291     {
292         char *saveptr;
293         char buffer[BUFFER_SIZE] = { '\0' };
294         if (strncpy_s(buffer, BUFFER_SIZE - 1, line, sizeof(buffer) - 1) != 0) {
295             return "";
296         }
297         char *token = strtok_r(buffer, OhosConstants::SPLIT_STR, &saveptr);
298 
299         for (int i = 0; i < index; i++) {
300             token = strtok_r(NULL, OhosConstants::SPLIT_STR, &saveptr);
301         }
302         return token;
303     }
304 
getLength(char lines[][BUFFER_SIZE],int maxInput)305     int getLength(char lines[][BUFFER_SIZE], int maxInput) const
306     {
307         int count = 0;
308         for (int i = 0; i < maxInput; i++) {
309             if (lines[i][0] != '\0') {
310                 count++;
311             }
312         }
313         return count;
314     }
315 
GetCrashRuntimeInfoList(char lines[][BUFFER_SIZE])316     void GetCrashRuntimeInfoList(char lines[][BUFFER_SIZE]) const
317     {
318         static char realOutPath[PATH_MAX] = { '\0' };
319         if (!GetCrashSandBoxRealPath(realOutPath, PATH_MAX)) {
320             return;
321         }
322         if (!FileExist(realOutPath)) {
323             return;
324         }
325         static char soBuildId[NAME_MAX] = { '\0' };
326         if (!GetRuntimeBuildId(soBuildId, NAME_MAX)) {
327             return;
328         }
329         if (IsCharEmpty(soBuildId)) {
330             return;
331         }
332         GetRuntimeInfoByPath(lines, realOutPath, soBuildId);
333         return;
334     }
335 
GetRealPathRuntimeInfoList(char lines[][BUFFER_SIZE],const std::string & pgoRealPath)336     void GetRealPathRuntimeInfoList(char lines[][BUFFER_SIZE], const std::string &pgoRealPath) const
337     {
338         std::string realOutPath;
339         if (!ecmascript::RealPath(pgoRealPath, realOutPath, false)) {
340             return;
341         }
342         if (!FileExist(realOutPath.c_str())) {
343             return;
344         }
345         char soBuildId[NAME_MAX] = { '\0' };
346         if (!GetRuntimeBuildId(soBuildId, NAME_MAX)) {
347             return;
348         }
349         if (IsCharEmpty(soBuildId)) {
350             return;
351         }
352         GetRuntimeInfoByPath(lines, realOutPath.c_str(), soBuildId);
353     }
354 
SetRuntimeInfo(const char * realOutPath,char lines[][BUFFER_SIZE],int length)355     virtual void SetRuntimeInfo(const char *realOutPath, char lines[][BUFFER_SIZE], int length) const
356     {
357         int fd = open(realOutPath,  O_WRONLY | O_CREAT | O_TRUNC, 0666);
358         if (fd == -1) {
359             return;
360         }
361         FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
362         for (int i = 0; i < length && lines[i] != NULL; i++) {
363             if (lines[i][0] != '\0') {
364                 write(fd, lines[i], strlen(lines[i]));
365                 write(fd, "\n", 1);
366             }
367         }
368         Close(reinterpret_cast<fd_t>(fd));
369     }
370 
GetRuntimeInfoByPath(char lines[][BUFFER_SIZE],const char * realOutPath,const char * soBuildId)371     void GetRuntimeInfoByPath(char lines[][BUFFER_SIZE], const char *realOutPath, const char *soBuildId) const
372     {
373         int fd = open(realOutPath, O_RDONLY);
374         if (fd == -1) {
375             return;
376         }
377         FdsanExchangeOwnerTag(reinterpret_cast<fd_t>(fd));
378         char buffer[BUFFER_SIZE] = { '\0' };
379         char *saveptr;
380         char *token;
381         ssize_t bytesRead;
382         int lineCount = 0;
383         while ((bytesRead = read(fd, buffer, BUFFER_SIZE - 1)) > 0) {
384             token = strtok_r(buffer, "\n", &saveptr);
385             while (token != NULL) {
386                 if (strcmp(GetInfoFromBuffer(token, RUNTIME_INDEX_BUILDID), soBuildId) == 0 &&
387                     lineCount < MAX_LENGTH &&
388                     strcpy_s(lines[lineCount], BUFFER_SIZE, token) == 0) {
389                     lineCount++;
390                 }
391                 token = strtok_r(NULL, "\n", &saveptr);
392             }
393         }
394         Close(reinterpret_cast<fd_t>(fd));
395     }
396 
ParseELFSectionsForBuildId(ecmascript::MemMap & fileMap,char * buildId,int length)397     void ParseELFSectionsForBuildId(ecmascript::MemMap &fileMap, char *buildId, int length) const
398     {
399         llvm::ELF::Elf64_Ehdr *ehdr = reinterpret_cast<llvm::ELF::Elf64_Ehdr *>(fileMap.GetOriginAddr());
400         char *addr = reinterpret_cast<char *>(ehdr);
401         llvm::ELF::Elf64_Shdr *shdr = reinterpret_cast<llvm::ELF::Elf64_Shdr *>(addr + ehdr->e_shoff);
402         ASSERT(ehdr->e_shstrndx != static_cast<llvm::ELF::Elf64_Half>(-1));
403         llvm::ELF::Elf64_Shdr strdr = shdr[ehdr->e_shstrndx];
404         int secId = -1;
405         static const char sectionName[] = ".note.gnu.build-id";
406         for (size_t i = 0; i < ehdr->e_shnum; i++) {
407             llvm::ELF::Elf64_Word shName = shdr[i].sh_name;
408             char *curShName = reinterpret_cast<char *>(addr) + shName + strdr.sh_offset;
409             if (strcmp(sectionName, curShName) == 0) {
410                 secId = static_cast<int>(i);
411                 break;
412             }
413         }
414         if (secId == -1) {
415             return;
416         }
417         llvm::ELF::Elf64_Shdr secShdr = shdr[secId];
418         uint64_t buildIdOffset = secShdr.sh_offset;
419         uint64_t buildIdSize = secShdr.sh_size;
420         llvm::ELF::Elf64_Nhdr *nhdr = reinterpret_cast<llvm::ELF::Elf64_Nhdr *>(addr + buildIdOffset);
421         char *curShNameForNhdr = reinterpret_cast<char *>(addr + buildIdOffset + sizeof(*nhdr));
422         if (buildIdSize - sizeof(*nhdr) < nhdr->n_namesz) {
423             return;
424         }
425 
426         static const char gnu[] = "GNU";
427         if (strcmp(curShNameForNhdr, gnu) != 0 || nhdr->n_type != NT_GNU_BUILD_ID) {
428             return;
429         }
430         if ((buildIdSize - sizeof(*nhdr) - AlignValues(nhdr->n_namesz, 4) < nhdr->n_descsz) || nhdr->n_descsz == 0) {
431             return;
432         }
433 
434         char *curShNameValueForNhdr = reinterpret_cast<char *>(addr + buildIdOffset + sizeof(*nhdr) +
435             AlignValues(nhdr->n_namesz, 4));
436         GetReadableBuildId(curShNameValueForNhdr, buildId, length);
437     }
438 
GetReadableBuildId(char * buildIdHex,char * buildId,int length)439     void GetReadableBuildId(char *buildIdHex, char *buildId, int length) const
440     {
441         if (IsCharEmpty(buildIdHex)) {
442             return;
443         }
444         static const char HEXTABLE[] = "0123456789abcdef";
445         static const int HEXLENGTH = 16;
446         static const int HEX_EXPAND_PARAM = 2;
447         const int len = strlen(buildIdHex);
448 
449         for (int i = 0; i < len; i++) {
450             int lowHexExpand = i * HEX_EXPAND_PARAM + 1;
451             if (lowHexExpand >= length) {
452                 break;
453             }
454             unsigned int n = buildIdHex[i];
455             buildId[lowHexExpand - 1] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8
456             buildId[lowHexExpand] = HEXTABLE[n % HEXLENGTH];
457         }
458     }
459 
AlignValues(uint64_t val,uint64_t align)460     uint64_t AlignValues(uint64_t val, uint64_t align) const
461     {
462         return (val + AlignBytes(align)) & AlignMask(align);
463     }
464 
AlignMask(uint64_t align)465     uint64_t AlignMask(uint64_t align) const
466     {
467         return ~(static_cast<uint64_t>((align) - 1));
468     }
469 
AlignBytes(uint64_t align)470     uint64_t AlignBytes(uint64_t align) const
471     {
472         return (align) - 1;
473     }
474 
IsLoadedMap()475     bool IsLoadedMap()
476     {
477         return isLoadedMap_;
478     }
479 
SetLoadedMap(bool value)480     void SetLoadedMap(bool value)
481     {
482         isLoadedMap_ = value;
483     }
484 
485     std::map<RuntimeInfoType, int> escapeMap_;
486     bool isLoadedMap_ = false;
487     std::mutex fileMutex_;
488 };
489 }  // namespace panda::ecmascript
490 #endif  // ECMASCRIPT_COMPILER_OHOS_RUNTIME_BUILD_INFO_H