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(¤t_time) == -1) { 233 return false; 234 } 235 struct tm *local_time = localtime(¤t_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