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 GetCompileCountByType(RuntimeInfoType type, const std::string &pgoRealPath = �)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 CollectCrashSum(const std::string &pgoRealPath = �)141 std::map<RuntimeInfoType, int> CollectCrashSum(const std::string &pgoRealPath = "") 142 { 143 if (IsLoadedMap()) { 144 return escapeMap_; 145 } 146 char lines[MAX_LENGTH][BUFFER_SIZE]; 147 for (int i = 0; i < MAX_LENGTH; i++) { 148 memset_s(lines[i], BUFFER_SIZE, '\0', BUFFER_SIZE); 149 } 150 if (IsCharEmpty(pgoRealPath.c_str())) { 151 GetCrashRuntimeInfoList(lines); 152 } else { 153 GetRealPathRuntimeInfoList(lines, pgoRealPath); 154 } 155 char *typeChar = new char[NAME_MAX]; 156 for (int i = 0; i < MAX_LENGTH; i++) { 157 if (lines[i][0] != '\0') { 158 if (strcpy_s(typeChar, NAME_MAX, GetInfoFromBuffer(lines[i], RUNTIME_INDEX_TYPE)) !=0) { 159 continue; 160 } 161 std::string typeStr(typeChar); 162 escapeMap_[GetRuntimeInfoTypeByStr(typeStr)]++; 163 } 164 } 165 SetLoadedMap(true); 166 delete[] typeChar; 167 return escapeMap_; 168 } 169 GetRuntimeInfoTypeStr(const RuntimeInfoType type) const170 const char *GetRuntimeInfoTypeStr(const RuntimeInfoType type) const 171 { 172 switch (type) { 173 case RuntimeInfoType::AOT_CRASH: 174 return "AOT_CRASH"; 175 case RuntimeInfoType::JIT: 176 return "JIT"; 177 case RuntimeInfoType::OTHERS: 178 return "OTHERS"; 179 case RuntimeInfoType::NONE: 180 return "NONE"; 181 case RuntimeInfoType::JS: 182 return "JS"; 183 case RuntimeInfoType::AOT_BUILD: 184 return "AOT_BUILD"; 185 default: 186 return "NONE"; 187 } 188 } 189 GetRuntimeInfoTypeByStr(std::string &type) const190 RuntimeInfoType GetRuntimeInfoTypeByStr(std::string &type) const 191 { 192 const std::map<std::string, RuntimeInfoType> strMap = { 193 #define RUNTIME_INFO_TYPE_MAP(name) { #name, RuntimeInfoType::name }, 194 RUNTIME_INFO_TYPE(RUNTIME_INFO_TYPE_MAP) 195 #undef RUNTIME_INFO_TYPE_MAP 196 }; 197 if (strMap.count(type) > 0) { 198 return strMap.at(type); 199 } 200 return RuntimeInfoType::NONE; 201 } 202 GetRuntimeBuildId(char *buildId, int length) const203 virtual bool GetRuntimeBuildId(char *buildId, int length) const 204 { 205 if (!FileExist(OhosConstants::RUNTIME_SO_PATH)) { 206 return false; 207 } 208 static char realPath[PATH_MAX] = { '\0' }; 209 if (!ecmascript::RealPathByChar(OhosConstants::RUNTIME_SO_PATH, realPath, PATH_MAX, false)) { 210 return false; 211 } 212 if (!FileExist(realPath)) { 213 return false; 214 } 215 ecmascript::MemMap fileMap = ecmascript::FileMap(realPath, FILE_RDONLY, PAGE_PROT_READ); 216 if (fileMap.GetOriginAddr() == nullptr) { 217 return false; 218 } 219 ParseELFSectionsForBuildId(fileMap, buildId, length); 220 ecmascript::FileUnMap(fileMap); 221 fileMap.Reset(); 222 return true; 223 } 224 GetMicrosecondsTimeStamp(char *timestamp, size_t length) const225 virtual bool GetMicrosecondsTimeStamp(char *timestamp, size_t length) const 226 { 227 time_t current_time; 228 if (time(¤t_time) == -1) { 229 return false; 230 } 231 struct tm *local_time = localtime(¤t_time); 232 if (local_time == NULL) { 233 return false; 234 } 235 size_t result = strftime(timestamp, length, "%Y-%m-%d %H:%M:%S", local_time); 236 if (result == 0) { 237 return false; 238 } 239 return true; 240 } 241 GetCrashSandBoxRealPath(char *realOutPath, size_t length) const242 virtual bool GetCrashSandBoxRealPath(char *realOutPath, size_t length) const 243 { 244 if (!ecmascript::RealPathByChar(OhosConstants::SANDBOX_ARK_PROFILE_PATH, realOutPath, length, false)) { 245 return false; 246 } 247 if (strcat_s(realOutPath, NAME_MAX, OhosConstants::PATH_SEPARATOR) != 0) { 248 return false; 249 } 250 if (strcat_s(realOutPath, NAME_MAX, OhosConstants::AOT_RUNTIME_INFO_NAME) !=0) { 251 return false; 252 } 253 return true; 254 } 255 protected: 256 IsCharEmpty(const char *value) const257 bool IsCharEmpty(const char *value) const 258 { 259 if (value == NULL || *value == '\0') { 260 return true; 261 } 262 return false; 263 } 264 BuildRuntimeInfoPart(char *runtimeInfoPart, const char *soBuildId, const char *timestamp, RuntimeInfoType type) const265 bool BuildRuntimeInfoPart(char *runtimeInfoPart, const char *soBuildId, const char *timestamp, 266 RuntimeInfoType type) const 267 { 268 if (strcat_s(runtimeInfoPart, NAME_MAX, soBuildId) != 0) { 269 return false; 270 } 271 if (strcat_s(runtimeInfoPart, NAME_MAX, OhosConstants::SPLIT_STR) != 0) { 272 return false; 273 } 274 if (strcat_s(runtimeInfoPart, NAME_MAX, timestamp) != 0) { 275 return false; 276 } 277 if (strcat_s(runtimeInfoPart, NAME_MAX, OhosConstants::SPLIT_STR) != 0) { 278 return false; 279 } 280 if (strcat_s(runtimeInfoPart, NAME_MAX, GetRuntimeInfoTypeStr(type)) != 0) { 281 return false; 282 } 283 return true; 284 } 285 GetInfoFromBuffer(char *line, int index) const286 const char *GetInfoFromBuffer(char *line, int index) const 287 { 288 char *saveptr; 289 char buffer[BUFFER_SIZE] = { '\0' }; 290 if (strncpy_s(buffer, BUFFER_SIZE - 1, line, sizeof(buffer) - 1) != 0) { 291 return ""; 292 } 293 char *token = strtok_r(buffer, OhosConstants::SPLIT_STR, &saveptr); 294 295 for (int i = 0; i < index; i++) { 296 token = strtok_r(NULL, OhosConstants::SPLIT_STR, &saveptr); 297 } 298 return token; 299 } 300 getLength(char lines[][BUFFER_SIZE], int maxInput) const301 int getLength(char lines[][BUFFER_SIZE], int maxInput) const 302 { 303 int count = 0; 304 for (int i = 0; i < maxInput; i++) { 305 if (lines[i][0] != '\0') { 306 count++; 307 } 308 } 309 return count; 310 } 311 GetCrashRuntimeInfoList(char lines[][BUFFER_SIZE]) const312 void GetCrashRuntimeInfoList(char lines[][BUFFER_SIZE]) const 313 { 314 static char realOutPath[PATH_MAX] = { '\0' }; 315 if (!GetCrashSandBoxRealPath(realOutPath, PATH_MAX)) { 316 return; 317 } 318 if (!FileExist(realOutPath)) { 319 return; 320 } 321 static char soBuildId[NAME_MAX] = { '\0' }; 322 if (!GetRuntimeBuildId(soBuildId, NAME_MAX)) { 323 return; 324 } 325 if (IsCharEmpty(soBuildId)) { 326 return; 327 } 328 GetRuntimeInfoByPath(lines, realOutPath, soBuildId); 329 return; 330 } 331 GetRealPathRuntimeInfoList(char lines[][BUFFER_SIZE], const std::string &pgoRealPath) const332 void GetRealPathRuntimeInfoList(char lines[][BUFFER_SIZE], const std::string &pgoRealPath) const 333 { 334 std::string realOutPath; 335 if (!ecmascript::RealPath(pgoRealPath, realOutPath, false)) { 336 return; 337 } 338 if (!FileExist(realOutPath.c_str())) { 339 return; 340 } 341 char soBuildId[NAME_MAX] = { '\0' }; 342 if (!GetRuntimeBuildId(soBuildId, NAME_MAX)) { 343 return; 344 } 345 if (IsCharEmpty(soBuildId)) { 346 return; 347 } 348 GetRuntimeInfoByPath(lines, realOutPath.c_str(), soBuildId); 349 } 350 SetRuntimeInfo(const char *realOutPath, char lines[][BUFFER_SIZE], int length) const351 virtual void SetRuntimeInfo(const char *realOutPath, char lines[][BUFFER_SIZE], int length) const 352 { 353 int fd = open(realOutPath, O_WRONLY | O_CREAT | O_TRUNC, 0666); 354 if (fd == -1) { 355 return; 356 } 357 for (int i = 0; i < length && lines[i] != NULL; i++) { 358 if (lines[i][0] != '\0') { 359 write(fd, lines[i], strlen(lines[i])); 360 write(fd, "\n", 1); 361 } 362 } 363 close(fd); 364 } 365 GetRuntimeInfoByPath(char lines[][BUFFER_SIZE], const char *realOutPath, const char *soBuildId) const366 void GetRuntimeInfoByPath(char lines[][BUFFER_SIZE], const char *realOutPath, const char *soBuildId) const 367 { 368 int fd = open(realOutPath, O_RDONLY); 369 if (fd == -1) { 370 return; 371 } 372 char buffer[BUFFER_SIZE] = { '\0' }; 373 char *saveptr; 374 char *token; 375 ssize_t bytesRead; 376 int lineCount = 0; 377 while ((bytesRead = read(fd, buffer, BUFFER_SIZE - 1)) > 0) { 378 token = strtok_r(buffer, "\n", &saveptr); 379 while (token != NULL) { 380 if (strcmp(GetInfoFromBuffer(token, RUNTIME_INDEX_BUILDID), soBuildId) == 0 && 381 lineCount < MAX_LENGTH && 382 strcpy_s(lines[lineCount], BUFFER_SIZE, token) == 0) { 383 lineCount++; 384 } 385 token = strtok_r(NULL, "\n", &saveptr); 386 } 387 } 388 close(fd); 389 } 390 ParseELFSectionsForBuildId(ecmascript::MemMap &fileMap, char *buildId, int length) const391 void ParseELFSectionsForBuildId(ecmascript::MemMap &fileMap, char *buildId, int length) const 392 { 393 llvm::ELF::Elf64_Ehdr *ehdr = reinterpret_cast<llvm::ELF::Elf64_Ehdr *>(fileMap.GetOriginAddr()); 394 char *addr = reinterpret_cast<char *>(ehdr); 395 llvm::ELF::Elf64_Shdr *shdr = reinterpret_cast<llvm::ELF::Elf64_Shdr *>(addr + ehdr->e_shoff); 396 ASSERT(ehdr->e_shstrndx != static_cast<llvm::ELF::Elf64_Half>(-1)); 397 llvm::ELF::Elf64_Shdr strdr = shdr[ehdr->e_shstrndx]; 398 int secId = -1; 399 static const char sectionName[] = ".note.gnu.build-id"; 400 for (size_t i = 0; i < ehdr->e_shnum; i++) { 401 llvm::ELF::Elf64_Word shName = shdr[i].sh_name; 402 char *curShName = reinterpret_cast<char *>(addr) + shName + strdr.sh_offset; 403 if (strcmp(sectionName, curShName) == 0) { 404 secId = static_cast<int>(i); 405 break; 406 } 407 } 408 if (secId == -1) { 409 return; 410 } 411 llvm::ELF::Elf64_Shdr secShdr = shdr[secId]; 412 uint64_t buildIdOffset = secShdr.sh_offset; 413 uint64_t buildIdSize = secShdr.sh_size; 414 llvm::ELF::Elf64_Nhdr *nhdr = reinterpret_cast<llvm::ELF::Elf64_Nhdr *>(addr + buildIdOffset); 415 char *curShNameForNhdr = reinterpret_cast<char *>(addr + buildIdOffset + sizeof(*nhdr)); 416 if (buildIdSize - sizeof(*nhdr) < nhdr->n_namesz) { 417 return; 418 } 419 420 static const char gnu[] = "GNU"; 421 if (strcmp(curShNameForNhdr, gnu) != 0 || nhdr->n_type != NT_GNU_BUILD_ID) { 422 return; 423 } 424 if ((buildIdSize - sizeof(*nhdr) - AlignValues(nhdr->n_namesz, 4) < nhdr->n_descsz) || nhdr->n_descsz == 0) { 425 return; 426 } 427 428 char *curShNameValueForNhdr = reinterpret_cast<char *>(addr + buildIdOffset + sizeof(*nhdr) + 429 AlignValues(nhdr->n_namesz, 4)); 430 GetReadableBuildId(curShNameValueForNhdr, buildId, length); 431 } 432 GetReadableBuildId(char *buildIdHex, char *buildId, int length) const433 void GetReadableBuildId(char *buildIdHex, char *buildId, int length) const 434 { 435 if (IsCharEmpty(buildIdHex)) { 436 return; 437 } 438 static const char HEXTABLE[] = "0123456789abcdef"; 439 static const int HEXLENGTH = 16; 440 static const int HEX_EXPAND_PARAM = 2; 441 const int len = strlen(buildIdHex); 442 443 for (int i = 0; i < len; i++) { 444 int lowHexExpand = i * HEX_EXPAND_PARAM + 1; 445 if (lowHexExpand >= length) { 446 break; 447 } 448 unsigned int n = buildIdHex[i]; 449 buildId[lowHexExpand - 1] = HEXTABLE[(n >> 4) % HEXLENGTH]; // 4 : higher 4 bit of uint8 450 buildId[lowHexExpand] = HEXTABLE[n % HEXLENGTH]; 451 } 452 } 453 AlignValues(uint64_t val, uint64_t align) const454 uint64_t AlignValues(uint64_t val, uint64_t align) const 455 { 456 return (val + AlignBytes(align)) & AlignMask(align); 457 } 458 AlignMask(uint64_t align) const459 uint64_t AlignMask(uint64_t align) const 460 { 461 return ~(static_cast<uint64_t>((align) - 1)); 462 } 463 AlignBytes(uint64_t align) const464 uint64_t AlignBytes(uint64_t align) const 465 { 466 return (align) - 1; 467 } 468 IsLoadedMap()469 bool IsLoadedMap() 470 { 471 return isLoadedMap_; 472 } 473 SetLoadedMap(bool value)474 void SetLoadedMap(bool value) 475 { 476 isLoadedMap_ = value; 477 } 478 479 std::map<RuntimeInfoType, int> escapeMap_; 480 bool isLoadedMap_ = false; 481 std::mutex fileMutex_; 482 }; 483 } // namespace panda::ecmascript 484 #endif // ECMASCRIPT_COMPILER_OHOS_RUNTIME_BUILD_INFO_H