1800b99b8Sopenharmony_ci/* 2800b99b8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd. 3800b99b8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4800b99b8Sopenharmony_ci * you may not use this file except in compliance with the License. 5800b99b8Sopenharmony_ci * You may obtain a copy of the License at 6800b99b8Sopenharmony_ci * 7800b99b8Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8800b99b8Sopenharmony_ci * 9800b99b8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10800b99b8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11800b99b8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12800b99b8Sopenharmony_ci * See the License for the specific language governing permissions and 13800b99b8Sopenharmony_ci * limitations under the License. 14800b99b8Sopenharmony_ci */ 15800b99b8Sopenharmony_ci#ifndef _GNU_SOURCE 16800b99b8Sopenharmony_ci#define _GNU_SOURCE 1 17800b99b8Sopenharmony_ci#endif 18800b99b8Sopenharmony_ci 19800b99b8Sopenharmony_ci#include "dfx_dumprequest.h" 20800b99b8Sopenharmony_ci 21800b99b8Sopenharmony_ci#include <fcntl.h> 22800b99b8Sopenharmony_ci#include <poll.h> 23800b99b8Sopenharmony_ci#include <pthread.h> 24800b99b8Sopenharmony_ci#include <sched.h> 25800b99b8Sopenharmony_ci#include <signal.h> 26800b99b8Sopenharmony_ci#include <sigchain.h> 27800b99b8Sopenharmony_ci#include <stdint.h> 28800b99b8Sopenharmony_ci#include <stdio.h> 29800b99b8Sopenharmony_ci#include <sys/capability.h> 30800b99b8Sopenharmony_ci#include <sys/mman.h> 31800b99b8Sopenharmony_ci#include <sys/prctl.h> 32800b99b8Sopenharmony_ci#include <sys/syscall.h> 33800b99b8Sopenharmony_ci#include <sys/time.h> 34800b99b8Sopenharmony_ci#include <sys/types.h> 35800b99b8Sopenharmony_ci#include <sys/uio.h> 36800b99b8Sopenharmony_ci#include <sys/wait.h> 37800b99b8Sopenharmony_ci#include <time.h> 38800b99b8Sopenharmony_ci#include <unistd.h> 39800b99b8Sopenharmony_ci#include "dfx_define.h" 40800b99b8Sopenharmony_ci#include "dfx_dump_request.h" 41800b99b8Sopenharmony_ci#include "dfx_signalhandler_exception.h" 42800b99b8Sopenharmony_ci#include "errno.h" 43800b99b8Sopenharmony_ci#include "linux/capability.h" 44800b99b8Sopenharmony_ci#include "stdbool.h" 45800b99b8Sopenharmony_ci#include "string.h" 46800b99b8Sopenharmony_ci#ifndef DFX_SIGNAL_LIBC 47800b99b8Sopenharmony_ci#include <securec.h> 48800b99b8Sopenharmony_ci#include "dfx_cutil.h" 49800b99b8Sopenharmony_ci#include "dfx_log.h" 50800b99b8Sopenharmony_ci#else 51800b99b8Sopenharmony_ci#include "musl_cutil.h" 52800b99b8Sopenharmony_ci#include "musl_log.h" 53800b99b8Sopenharmony_ci#endif 54800b99b8Sopenharmony_ci 55800b99b8Sopenharmony_ci#include "info/fatal_message.h" 56800b99b8Sopenharmony_ci 57800b99b8Sopenharmony_ci#ifdef LOG_DOMAIN 58800b99b8Sopenharmony_ci#undef LOG_DOMAIN 59800b99b8Sopenharmony_ci#define LOG_DOMAIN 0xD002D11 60800b99b8Sopenharmony_ci#endif 61800b99b8Sopenharmony_ci 62800b99b8Sopenharmony_ci#ifdef LOG_TAG 63800b99b8Sopenharmony_ci#undef LOG_TAG 64800b99b8Sopenharmony_ci#define LOG_TAG "DfxSignalHandler" 65800b99b8Sopenharmony_ci#endif 66800b99b8Sopenharmony_ci 67800b99b8Sopenharmony_ci#ifndef F_SETPIPE_SZ 68800b99b8Sopenharmony_ci#define F_SETPIPE_SZ 1031 69800b99b8Sopenharmony_ci#endif 70800b99b8Sopenharmony_ci 71800b99b8Sopenharmony_ci#define NUMBER_SIXTYFOUR 64 72800b99b8Sopenharmony_ci#define INHERITABLE_OFFSET 32 73800b99b8Sopenharmony_ci 74800b99b8Sopenharmony_cistatic struct ProcessDumpRequest *g_request = NULL; 75800b99b8Sopenharmony_cistatic void *g_reservedChildStack = NULL; 76800b99b8Sopenharmony_ci 77800b99b8Sopenharmony_cienum PIPE_FD_TYPE { 78800b99b8Sopenharmony_ci WRITE_TO_DUMP, 79800b99b8Sopenharmony_ci READ_FROM_DUMP_TO_MAIN, 80800b99b8Sopenharmony_ci READ_FORM_DUMP_TO_VIRTUAL, 81800b99b8Sopenharmony_ci PIPE_MAX, 82800b99b8Sopenharmony_ci}; 83800b99b8Sopenharmony_ci 84800b99b8Sopenharmony_cistatic int g_pipeFds[PIPE_MAX][2] = { 85800b99b8Sopenharmony_ci {-1, -1}, 86800b99b8Sopenharmony_ci {-1, -1}, 87800b99b8Sopenharmony_ci {-1, -1} 88800b99b8Sopenharmony_ci}; 89800b99b8Sopenharmony_ci 90800b99b8Sopenharmony_cistatic const int SIGNALHANDLER_TIMEOUT = 10000; // 10000 us 91800b99b8Sopenharmony_cistatic const int ALARM_TIME_S = 10; 92800b99b8Sopenharmony_cienum DumpPreparationStage { 93800b99b8Sopenharmony_ci CREATE_PIPE_FAIL = 1, 94800b99b8Sopenharmony_ci SET_PIPE_LEN_FAIL, 95800b99b8Sopenharmony_ci WRITE_PIPE_FAIL, 96800b99b8Sopenharmony_ci INHERIT_CAP_FAIL, 97800b99b8Sopenharmony_ci EXEC_FAIL, 98800b99b8Sopenharmony_ci}; 99800b99b8Sopenharmony_ci 100800b99b8Sopenharmony_cistatic const char* GetCrashDescription(const int32_t errCode) 101800b99b8Sopenharmony_ci{ 102800b99b8Sopenharmony_ci size_t i; 103800b99b8Sopenharmony_ci 104800b99b8Sopenharmony_ci for (i = 0; i < sizeof(g_crashExceptionMap) / sizeof(g_crashExceptionMap[0]); i++) { 105800b99b8Sopenharmony_ci if (errCode == g_crashExceptionMap[i].errCode) { 106800b99b8Sopenharmony_ci return g_crashExceptionMap[i].str; 107800b99b8Sopenharmony_ci } 108800b99b8Sopenharmony_ci } 109800b99b8Sopenharmony_ci return g_crashExceptionMap[i - 1].str; /* the end of map is "unknown reason" */ 110800b99b8Sopenharmony_ci} 111800b99b8Sopenharmony_ci 112800b99b8Sopenharmony_cistatic void FillCrashExceptionAndReport(const int err) 113800b99b8Sopenharmony_ci{ 114800b99b8Sopenharmony_ci struct CrashDumpException exception; 115800b99b8Sopenharmony_ci (void)memset_s(&exception, sizeof(struct CrashDumpException), 0, sizeof(struct CrashDumpException)); 116800b99b8Sopenharmony_ci exception.pid = g_request->pid; 117800b99b8Sopenharmony_ci exception.uid = (int32_t)(g_request->uid); 118800b99b8Sopenharmony_ci exception.error = err; 119800b99b8Sopenharmony_ci exception.time = (int64_t)(GetTimeMilliseconds()); 120800b99b8Sopenharmony_ci if (strncpy_s(exception.message, sizeof(exception.message), GetCrashDescription(err), 121800b99b8Sopenharmony_ci sizeof(exception.message) - 1) != 0) { 122800b99b8Sopenharmony_ci DFXLOGE("strcpy exception message fail"); 123800b99b8Sopenharmony_ci return; 124800b99b8Sopenharmony_ci } 125800b99b8Sopenharmony_ci ReportException(exception); 126800b99b8Sopenharmony_ci} 127800b99b8Sopenharmony_ci 128800b99b8Sopenharmony_cistatic int32_t InheritCapabilities(void) 129800b99b8Sopenharmony_ci{ 130800b99b8Sopenharmony_ci struct __user_cap_header_struct capHeader; 131800b99b8Sopenharmony_ci (void)memset_s(&capHeader, sizeof(capHeader), 0, sizeof(capHeader)); 132800b99b8Sopenharmony_ci 133800b99b8Sopenharmony_ci capHeader.version = _LINUX_CAPABILITY_VERSION_3; 134800b99b8Sopenharmony_ci capHeader.pid = 0; 135800b99b8Sopenharmony_ci struct __user_cap_data_struct capData[2]; 136800b99b8Sopenharmony_ci if (capget(&capHeader, &capData[0]) == -1) { 137800b99b8Sopenharmony_ci DFXLOGE("Failed to get origin cap data"); 138800b99b8Sopenharmony_ci return -1; 139800b99b8Sopenharmony_ci } 140800b99b8Sopenharmony_ci 141800b99b8Sopenharmony_ci capData[0].inheritable = capData[0].permitted; 142800b99b8Sopenharmony_ci capData[1].inheritable = capData[1].permitted; 143800b99b8Sopenharmony_ci if (capset(&capHeader, &capData[0]) == -1) { 144800b99b8Sopenharmony_ci DFXLOGE("Failed to set cap data, errno(%{public}d)", errno); 145800b99b8Sopenharmony_ci return -1; 146800b99b8Sopenharmony_ci } 147800b99b8Sopenharmony_ci 148800b99b8Sopenharmony_ci uint64_t ambCap = capData[0].inheritable; 149800b99b8Sopenharmony_ci ambCap = ambCap | (((uint64_t)capData[1].inheritable) << INHERITABLE_OFFSET); 150800b99b8Sopenharmony_ci for (size_t i = 0; i < NUMBER_SIXTYFOUR; i++) { 151800b99b8Sopenharmony_ci if (ambCap & ((uint64_t)1)) { 152800b99b8Sopenharmony_ci if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) { 153800b99b8Sopenharmony_ci DFXLOGE("Failed to change the ambient capability set, errno(%{public}d)", errno); 154800b99b8Sopenharmony_ci } 155800b99b8Sopenharmony_ci } 156800b99b8Sopenharmony_ci ambCap = ambCap >> 1; 157800b99b8Sopenharmony_ci } 158800b99b8Sopenharmony_ci return 0; 159800b99b8Sopenharmony_ci} 160800b99b8Sopenharmony_ci 161800b99b8Sopenharmony_cistatic const int SIGCHAIN_DUMP_SIGNAL_LIST[] = { 162800b99b8Sopenharmony_ci SIGDUMP, SIGLEAK_STACK 163800b99b8Sopenharmony_ci}; 164800b99b8Sopenharmony_ci 165800b99b8Sopenharmony_cistatic const int SIGCHAIN_CRASH_SIGNAL_LIST[] = { 166800b99b8Sopenharmony_ci SIGILL, SIGABRT, SIGBUS, SIGFPE, 167800b99b8Sopenharmony_ci SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP 168800b99b8Sopenharmony_ci}; 169800b99b8Sopenharmony_ci 170800b99b8Sopenharmony_cistatic void SetInterestedSignalMasks(int how) 171800b99b8Sopenharmony_ci{ 172800b99b8Sopenharmony_ci sigset_t set; 173800b99b8Sopenharmony_ci sigemptyset(&set); 174800b99b8Sopenharmony_ci for (size_t i = 0; i < sizeof(SIGCHAIN_DUMP_SIGNAL_LIST) / sizeof(SIGCHAIN_DUMP_SIGNAL_LIST[0]); i++) { 175800b99b8Sopenharmony_ci sigaddset(&set, SIGCHAIN_DUMP_SIGNAL_LIST[i]); 176800b99b8Sopenharmony_ci } 177800b99b8Sopenharmony_ci for (size_t i = 0; i < sizeof(SIGCHAIN_CRASH_SIGNAL_LIST) / sizeof(SIGCHAIN_CRASH_SIGNAL_LIST[0]); i++) { 178800b99b8Sopenharmony_ci sigaddset(&set, SIGCHAIN_CRASH_SIGNAL_LIST[i]); 179800b99b8Sopenharmony_ci } 180800b99b8Sopenharmony_ci sigprocmask(how, &set, NULL); 181800b99b8Sopenharmony_ci} 182800b99b8Sopenharmony_ci 183800b99b8Sopenharmony_cistatic void CloseFds(void) 184800b99b8Sopenharmony_ci{ 185800b99b8Sopenharmony_ci const int closeFdCount = 1024; 186800b99b8Sopenharmony_ci for (int i = 0; i < closeFdCount; i++) { 187800b99b8Sopenharmony_ci syscall(SYS_close, i); 188800b99b8Sopenharmony_ci } 189800b99b8Sopenharmony_ci} 190800b99b8Sopenharmony_ci 191800b99b8Sopenharmony_cistatic void DFX_SetUpEnvironment(void) 192800b99b8Sopenharmony_ci{ 193800b99b8Sopenharmony_ci // clear stdout and stderr 194800b99b8Sopenharmony_ci int devNull = OHOS_TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); 195800b99b8Sopenharmony_ci if (devNull < 0) { 196800b99b8Sopenharmony_ci DFXLOGE("Failed to open dev/null."); 197800b99b8Sopenharmony_ci return; 198800b99b8Sopenharmony_ci } 199800b99b8Sopenharmony_ci 200800b99b8Sopenharmony_ci OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDOUT_FILENO)); 201800b99b8Sopenharmony_ci OHOS_TEMP_FAILURE_RETRY(dup2(devNull, STDERR_FILENO)); 202800b99b8Sopenharmony_ci syscall(SYS_close, devNull); 203800b99b8Sopenharmony_ci SetInterestedSignalMasks(SIG_BLOCK); 204800b99b8Sopenharmony_ci} 205800b99b8Sopenharmony_ci 206800b99b8Sopenharmony_cistatic void DFX_SetUpSigAlarmAction(void) 207800b99b8Sopenharmony_ci{ 208800b99b8Sopenharmony_ci if (signal(SIGALRM, SIG_DFL) == SIG_ERR) { 209800b99b8Sopenharmony_ci DFXLOGW("Default signal alarm error!"); 210800b99b8Sopenharmony_ci } 211800b99b8Sopenharmony_ci sigset_t set; 212800b99b8Sopenharmony_ci sigemptyset(&set); 213800b99b8Sopenharmony_ci sigaddset(&set, SIGALRM); 214800b99b8Sopenharmony_ci sigprocmask(SIG_UNBLOCK, &set, NULL); 215800b99b8Sopenharmony_ci} 216800b99b8Sopenharmony_ci 217800b99b8Sopenharmony_cistatic int DFX_ExecDump(void) 218800b99b8Sopenharmony_ci{ 219800b99b8Sopenharmony_ci DFX_SetUpEnvironment(); 220800b99b8Sopenharmony_ci DFX_SetUpSigAlarmAction(); 221800b99b8Sopenharmony_ci alarm(ALARM_TIME_S); 222800b99b8Sopenharmony_ci int pipefd[2] = {-1, -1}; 223800b99b8Sopenharmony_ci // create pipe for passing request to processdump 224800b99b8Sopenharmony_ci if (g_request->dumpMode == SPLIT_MODE) { 225800b99b8Sopenharmony_ci if (syscall(SYS_pipe2, pipefd, 0) != 0) { 226800b99b8Sopenharmony_ci DFXLOGE("Failed to create pipe for transfering context, errno(%{public}d)", errno); 227800b99b8Sopenharmony_ci return CREATE_PIPE_FAIL; 228800b99b8Sopenharmony_ci } 229800b99b8Sopenharmony_ci } else { 230800b99b8Sopenharmony_ci pipefd[0] = g_pipeFds[WRITE_TO_DUMP][0]; 231800b99b8Sopenharmony_ci pipefd[1] = g_pipeFds[WRITE_TO_DUMP][1]; 232800b99b8Sopenharmony_ci } 233800b99b8Sopenharmony_ci 234800b99b8Sopenharmony_ci ssize_t writeLen = (long)(sizeof(struct ProcessDumpRequest)); 235800b99b8Sopenharmony_ci if (fcntl(pipefd[1], F_SETPIPE_SZ, writeLen) < writeLen) { 236800b99b8Sopenharmony_ci DFXLOGE("Failed to set pipe buffer size, errno(%{public}d).", errno); 237800b99b8Sopenharmony_ci return SET_PIPE_LEN_FAIL; 238800b99b8Sopenharmony_ci } 239800b99b8Sopenharmony_ci 240800b99b8Sopenharmony_ci struct iovec iovs[1] = { 241800b99b8Sopenharmony_ci { 242800b99b8Sopenharmony_ci .iov_base = g_request, 243800b99b8Sopenharmony_ci .iov_len = sizeof(struct ProcessDumpRequest) 244800b99b8Sopenharmony_ci }, 245800b99b8Sopenharmony_ci }; 246800b99b8Sopenharmony_ci if (OHOS_TEMP_FAILURE_RETRY(writev(pipefd[1], iovs, 1)) != writeLen) { 247800b99b8Sopenharmony_ci DFXLOGE("Failed to write pipe, errno(%{public}d)", errno); 248800b99b8Sopenharmony_ci return WRITE_PIPE_FAIL; 249800b99b8Sopenharmony_ci } 250800b99b8Sopenharmony_ci OHOS_TEMP_FAILURE_RETRY(dup2(pipefd[0], STDIN_FILENO)); 251800b99b8Sopenharmony_ci if (pipefd[0] != STDIN_FILENO) { 252800b99b8Sopenharmony_ci syscall(SYS_close, pipefd[0]); 253800b99b8Sopenharmony_ci } 254800b99b8Sopenharmony_ci syscall(SYS_close, pipefd[1]); 255800b99b8Sopenharmony_ci 256800b99b8Sopenharmony_ci if (InheritCapabilities() != 0) { 257800b99b8Sopenharmony_ci DFXLOGE("Failed to inherit Capabilities from parent."); 258800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_EINHERITCAP); 259800b99b8Sopenharmony_ci return INHERIT_CAP_FAIL; 260800b99b8Sopenharmony_ci } 261800b99b8Sopenharmony_ci DFXLOGW("execl processdump."); 262800b99b8Sopenharmony_ci#ifdef DFX_LOG_HILOG_BASE 263800b99b8Sopenharmony_ci execl("/system/bin/processdump", "processdump", "-signalhandler", NULL); 264800b99b8Sopenharmony_ci#else 265800b99b8Sopenharmony_ci execl("/bin/processdump", "processdump", "-signalhandler", NULL); 266800b99b8Sopenharmony_ci#endif 267800b99b8Sopenharmony_ci DFXLOGE("Failed to execl processdump, errno(%{public}d)", errno); 268800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_EEXECL); 269800b99b8Sopenharmony_ci return errno; 270800b99b8Sopenharmony_ci} 271800b99b8Sopenharmony_ci 272800b99b8Sopenharmony_cistatic pid_t ForkBySyscall(void) 273800b99b8Sopenharmony_ci{ 274800b99b8Sopenharmony_ci#ifdef SYS_fork 275800b99b8Sopenharmony_ci return syscall(SYS_fork); 276800b99b8Sopenharmony_ci#else 277800b99b8Sopenharmony_ci return syscall(SYS_clone, SIGCHLD, 0); 278800b99b8Sopenharmony_ci#endif 279800b99b8Sopenharmony_ci} 280800b99b8Sopenharmony_ci 281800b99b8Sopenharmony_cistatic bool SetDumpState(void) 282800b99b8Sopenharmony_ci{ 283800b99b8Sopenharmony_ci if (prctl(PR_SET_DUMPABLE, 1) != 0) { 284800b99b8Sopenharmony_ci DFXLOGE("Failed to set dumpable, errno(%{public}d).", errno); 285800b99b8Sopenharmony_ci return false; 286800b99b8Sopenharmony_ci } 287800b99b8Sopenharmony_ci 288800b99b8Sopenharmony_ci if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) { 289800b99b8Sopenharmony_ci if (errno != EINVAL) { 290800b99b8Sopenharmony_ci DFXLOGE("Failed to set ptracer, errno(%{public}d).", errno); 291800b99b8Sopenharmony_ci return false; 292800b99b8Sopenharmony_ci } 293800b99b8Sopenharmony_ci } 294800b99b8Sopenharmony_ci return true; 295800b99b8Sopenharmony_ci} 296800b99b8Sopenharmony_ci 297800b99b8Sopenharmony_cistatic void RestoreDumpState(int prevState, bool isTracerStatusModified) 298800b99b8Sopenharmony_ci{ 299800b99b8Sopenharmony_ci prctl(PR_SET_DUMPABLE, prevState); 300800b99b8Sopenharmony_ci if (isTracerStatusModified == true) { 301800b99b8Sopenharmony_ci prctl(PR_SET_PTRACER, 0); 302800b99b8Sopenharmony_ci } 303800b99b8Sopenharmony_ci} 304800b99b8Sopenharmony_ci 305800b99b8Sopenharmony_cistatic void SetSelfThreadParam(const char* name, int priority) 306800b99b8Sopenharmony_ci{ 307800b99b8Sopenharmony_ci pthread_setname_np(pthread_self(), name); 308800b99b8Sopenharmony_ci struct sched_param schedParam; 309800b99b8Sopenharmony_ci schedParam.sched_priority = priority; 310800b99b8Sopenharmony_ci pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedParam); 311800b99b8Sopenharmony_ci} 312800b99b8Sopenharmony_ci 313800b99b8Sopenharmony_cistatic bool WaitProcessExit(int childPid, const char* name) 314800b99b8Sopenharmony_ci{ 315800b99b8Sopenharmony_ci int ret = -1; 316800b99b8Sopenharmony_ci int status = 0; 317800b99b8Sopenharmony_ci int startTime = (int)time(NULL); 318800b99b8Sopenharmony_ci bool isSuccess = false; 319800b99b8Sopenharmony_ci DFXLOGI("(%{public}ld) wait %{public}s(%{public}d) exit.", syscall(SYS_gettid), name, childPid); 320800b99b8Sopenharmony_ci do { 321800b99b8Sopenharmony_ci errno = 0; 322800b99b8Sopenharmony_ci ret = waitpid(childPid, &status, WNOHANG); 323800b99b8Sopenharmony_ci if (ret < 0) { 324800b99b8Sopenharmony_ci DFXLOGE("Failed to wait child process terminated, errno(%{public}d)", errno); 325800b99b8Sopenharmony_ci return isSuccess; 326800b99b8Sopenharmony_ci } 327800b99b8Sopenharmony_ci 328800b99b8Sopenharmony_ci if (ret == childPid) { 329800b99b8Sopenharmony_ci isSuccess = true; 330800b99b8Sopenharmony_ci break; 331800b99b8Sopenharmony_ci } 332800b99b8Sopenharmony_ci 333800b99b8Sopenharmony_ci if ((int)time(NULL) - startTime > PROCESSDUMP_TIMEOUT) { 334800b99b8Sopenharmony_ci DFXLOGI("(%{public}ld) wait for (%{public}d) timeout", syscall(SYS_gettid), childPid); 335800b99b8Sopenharmony_ci isSuccess = false; 336800b99b8Sopenharmony_ci break; 337800b99b8Sopenharmony_ci } 338800b99b8Sopenharmony_ci usleep(SIGNALHANDLER_TIMEOUT); // sleep 10ms 339800b99b8Sopenharmony_ci } while (1); 340800b99b8Sopenharmony_ci 341800b99b8Sopenharmony_ci DFXLOGI("(%{public}ld) wait for %{public}s(%{public}d) return with ret(%{public}d), status(%{public}d)", 342800b99b8Sopenharmony_ci syscall(SYS_gettid), name, childPid, ret, status); 343800b99b8Sopenharmony_ci if (WIFEXITED(status)) { 344800b99b8Sopenharmony_ci int exitCode = WEXITSTATUS(status); 345800b99b8Sopenharmony_ci DFXLOGI("wait %{public}s(%{public}d) exit code: %{public}d", name, childPid, exitCode); 346800b99b8Sopenharmony_ci } else if (WIFSIGNALED(status)) { 347800b99b8Sopenharmony_ci int sigNum = WTERMSIG(status); 348800b99b8Sopenharmony_ci DFXLOGI("wait %{public}s(%{public}d) exit with sig: %{public}d", name, childPid, sigNum); 349800b99b8Sopenharmony_ci } 350800b99b8Sopenharmony_ci return isSuccess; 351800b99b8Sopenharmony_ci} 352800b99b8Sopenharmony_ci 353800b99b8Sopenharmony_cistatic void Exit(int flag) 354800b99b8Sopenharmony_ci{ 355800b99b8Sopenharmony_ci _exit(flag); // Avoid crashes that occur when directly using the _exit() 356800b99b8Sopenharmony_ci} 357800b99b8Sopenharmony_ci 358800b99b8Sopenharmony_cistatic int ForkAndExecProcessDump(void) 359800b99b8Sopenharmony_ci{ 360800b99b8Sopenharmony_ci int childPid = -1; 361800b99b8Sopenharmony_ci SetSelfThreadParam("dump_tmp_thread", 0); 362800b99b8Sopenharmony_ci 363800b99b8Sopenharmony_ci // set privilege for dump ourself 364800b99b8Sopenharmony_ci int prevDumpableStatus = prctl(PR_GET_DUMPABLE); 365800b99b8Sopenharmony_ci bool isTracerStatusModified = SetDumpState(); 366800b99b8Sopenharmony_ci if (!isTracerStatusModified) { 367800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_ESETSTATE); 368800b99b8Sopenharmony_ci goto out; 369800b99b8Sopenharmony_ci } 370800b99b8Sopenharmony_ci 371800b99b8Sopenharmony_ci // fork a child process that could ptrace us 372800b99b8Sopenharmony_ci childPid = ForkBySyscall(); 373800b99b8Sopenharmony_ci if (childPid == 0) { 374800b99b8Sopenharmony_ci g_request->dumpMode = SPLIT_MODE; 375800b99b8Sopenharmony_ci DFXLOGI("The exec processdump pid(%{public}ld).", syscall(SYS_getpid)); 376800b99b8Sopenharmony_ci Exit(DFX_ExecDump()); 377800b99b8Sopenharmony_ci } else if (childPid < 0) { 378800b99b8Sopenharmony_ci DFXLOGE("[%{public}d]: Failed to fork child process, errno(%{public}d).", __LINE__, errno); 379800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_EFORK); 380800b99b8Sopenharmony_ci goto out; 381800b99b8Sopenharmony_ci } 382800b99b8Sopenharmony_ci WaitProcessExit(childPid, "processdump"); 383800b99b8Sopenharmony_ciout: 384800b99b8Sopenharmony_ci RestoreDumpState(prevDumpableStatus, isTracerStatusModified); 385800b99b8Sopenharmony_ci return 0; 386800b99b8Sopenharmony_ci} 387800b99b8Sopenharmony_ci 388800b99b8Sopenharmony_cistatic int CloneAndDoProcessDump(void* arg) 389800b99b8Sopenharmony_ci{ 390800b99b8Sopenharmony_ci (void)arg; 391800b99b8Sopenharmony_ci DFXLOGI("The clone thread(%{public}ld).", syscall(SYS_gettid)); 392800b99b8Sopenharmony_ci g_request->recycleTid = syscall(SYS_gettid); 393800b99b8Sopenharmony_ci return ForkAndExecProcessDump(); 394800b99b8Sopenharmony_ci} 395800b99b8Sopenharmony_ci 396800b99b8Sopenharmony_cistatic bool StartProcessdump(void) 397800b99b8Sopenharmony_ci{ 398800b99b8Sopenharmony_ci uint64_t startTime = GetAbsTimeMilliSeconds(); 399800b99b8Sopenharmony_ci pid_t pid = ForkBySyscall(); 400800b99b8Sopenharmony_ci if (pid < 0) { 401800b99b8Sopenharmony_ci DFXLOGE("Failed to fork dummy processdump(%{public}d)", errno); 402800b99b8Sopenharmony_ci return false; 403800b99b8Sopenharmony_ci } else if (pid == 0) { 404800b99b8Sopenharmony_ci pid_t processDumpPid = ForkBySyscall(); 405800b99b8Sopenharmony_ci if (processDumpPid < 0) { 406800b99b8Sopenharmony_ci DFXLOGE("Failed to fork processdump(%{public}d)", errno); 407800b99b8Sopenharmony_ci _exit(0); 408800b99b8Sopenharmony_ci } else if (processDumpPid > 0) { 409800b99b8Sopenharmony_ci _exit(0); 410800b99b8Sopenharmony_ci } else { 411800b99b8Sopenharmony_ci uint64_t endTime; 412800b99b8Sopenharmony_ci int tid; 413800b99b8Sopenharmony_ci ParseSiValue(&g_request->siginfo, &endTime, &tid); 414800b99b8Sopenharmony_ci uint64_t curTime = GetAbsTimeMilliSeconds(); 415800b99b8Sopenharmony_ci DFXLOGW("start processdump, fork spend time %{public}" PRIu64 "ms", curTime - startTime); 416800b99b8Sopenharmony_ci if (endTime != 0) { 417800b99b8Sopenharmony_ci DFXLOGW("dump remain %{public}" PRId64 "ms", endTime - curTime); 418800b99b8Sopenharmony_ci } 419800b99b8Sopenharmony_ci if (endTime == 0 || endTime > curTime) { 420800b99b8Sopenharmony_ci DFX_ExecDump(); 421800b99b8Sopenharmony_ci } else { 422800b99b8Sopenharmony_ci DFXLOGW("current has spend all time, not execl processdump"); 423800b99b8Sopenharmony_ci } 424800b99b8Sopenharmony_ci _exit(0); 425800b99b8Sopenharmony_ci } 426800b99b8Sopenharmony_ci } 427800b99b8Sopenharmony_ci 428800b99b8Sopenharmony_ci if (waitpid(pid, NULL, 0) <= 0) { 429800b99b8Sopenharmony_ci DFXLOGE("failed to wait dummy processdump(%{public}d)", errno); 430800b99b8Sopenharmony_ci } 431800b99b8Sopenharmony_ci return true; 432800b99b8Sopenharmony_ci} 433800b99b8Sopenharmony_ci 434800b99b8Sopenharmony_cistatic bool StartVMProcessUnwind(void) 435800b99b8Sopenharmony_ci{ 436800b99b8Sopenharmony_ci uint32_t startTime = GetAbsTimeMilliSeconds(); 437800b99b8Sopenharmony_ci pid_t pid = ForkBySyscall(); 438800b99b8Sopenharmony_ci if (pid < 0) { 439800b99b8Sopenharmony_ci DFXLOGE("Failed to fork vm process(%{public}d)", errno); 440800b99b8Sopenharmony_ci return false; 441800b99b8Sopenharmony_ci } else if (pid == 0) { 442800b99b8Sopenharmony_ci pid_t vmPid = ForkBySyscall(); 443800b99b8Sopenharmony_ci if (vmPid == 0) { 444800b99b8Sopenharmony_ci DFXLOGW("start vm process, fork spend time %{public}" PRIu64 "ms", GetAbsTimeMilliSeconds() - startTime); 445800b99b8Sopenharmony_ci syscall(SYS_close, g_pipeFds[WRITE_TO_DUMP][0]); 446800b99b8Sopenharmony_ci pid_t pids[PID_MAX] = {0}; 447800b99b8Sopenharmony_ci pids[REAL_PROCESS_PID] = GetRealPid(); 448800b99b8Sopenharmony_ci pids[VIRTUAL_PROCESS_PID] = syscall(SYS_getpid); 449800b99b8Sopenharmony_ci OHOS_TEMP_FAILURE_RETRY(write(g_pipeFds[WRITE_TO_DUMP][1], pids, sizeof(pids))); 450800b99b8Sopenharmony_ci syscall(SYS_close, g_pipeFds[WRITE_TO_DUMP][1]); 451800b99b8Sopenharmony_ci 452800b99b8Sopenharmony_ci uint32_t finishUnwind = OPE_FAIL; 453800b99b8Sopenharmony_ci syscall(SYS_close, g_pipeFds[READ_FORM_DUMP_TO_VIRTUAL][1]); 454800b99b8Sopenharmony_ci OHOS_TEMP_FAILURE_RETRY(read(g_pipeFds[READ_FORM_DUMP_TO_VIRTUAL][0], &finishUnwind, sizeof(finishUnwind))); 455800b99b8Sopenharmony_ci syscall(SYS_close, g_pipeFds[READ_FORM_DUMP_TO_VIRTUAL][0]); 456800b99b8Sopenharmony_ci DFXLOGI("processdump unwind finish, exit vm pid = %{public}d", pids[VIRTUAL_PROCESS_PID]); 457800b99b8Sopenharmony_ci _exit(0); 458800b99b8Sopenharmony_ci } else { 459800b99b8Sopenharmony_ci DFXLOGI("exit dummy vm process"); 460800b99b8Sopenharmony_ci _exit(0); 461800b99b8Sopenharmony_ci } 462800b99b8Sopenharmony_ci } 463800b99b8Sopenharmony_ci 464800b99b8Sopenharmony_ci if (waitpid(pid, NULL, 0) <= 0) { 465800b99b8Sopenharmony_ci DFXLOGE("failed to wait dummy vm process(%{public}d)", errno); 466800b99b8Sopenharmony_ci } 467800b99b8Sopenharmony_ci return true; 468800b99b8Sopenharmony_ci} 469800b99b8Sopenharmony_ci 470800b99b8Sopenharmony_cistatic void CleanFd(int *pipeFd) 471800b99b8Sopenharmony_ci{ 472800b99b8Sopenharmony_ci if (*pipeFd != -1) { 473800b99b8Sopenharmony_ci syscall(SYS_close, *pipeFd); 474800b99b8Sopenharmony_ci *pipeFd = -1; 475800b99b8Sopenharmony_ci } 476800b99b8Sopenharmony_ci} 477800b99b8Sopenharmony_ci 478800b99b8Sopenharmony_cistatic void CleanPipe(void) 479800b99b8Sopenharmony_ci{ 480800b99b8Sopenharmony_ci for (size_t i = 0; i < PIPE_MAX; i++) { 481800b99b8Sopenharmony_ci CleanFd(&g_pipeFds[i][0]); 482800b99b8Sopenharmony_ci CleanFd(&g_pipeFds[i][1]); 483800b99b8Sopenharmony_ci } 484800b99b8Sopenharmony_ci} 485800b99b8Sopenharmony_ci 486800b99b8Sopenharmony_cistatic bool InitPipe(void) 487800b99b8Sopenharmony_ci{ 488800b99b8Sopenharmony_ci for (int i = 0; i < PIPE_MAX; i++) { 489800b99b8Sopenharmony_ci if (syscall(SYS_pipe2, g_pipeFds[i], 0) == -1) { 490800b99b8Sopenharmony_ci DFXLOGE("create pipe fail, errno(%{public}d)", errno); 491800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_ECREATEPIPE); 492800b99b8Sopenharmony_ci CleanPipe(); 493800b99b8Sopenharmony_ci return false; 494800b99b8Sopenharmony_ci } 495800b99b8Sopenharmony_ci } 496800b99b8Sopenharmony_ci 497800b99b8Sopenharmony_ci g_request->pmPipeFd[0] = g_pipeFds[READ_FROM_DUMP_TO_MAIN][0]; 498800b99b8Sopenharmony_ci g_request->pmPipeFd[1] = g_pipeFds[READ_FROM_DUMP_TO_MAIN][1]; 499800b99b8Sopenharmony_ci g_request->vmPipeFd[0] = g_pipeFds[READ_FORM_DUMP_TO_VIRTUAL][0]; 500800b99b8Sopenharmony_ci g_request->vmPipeFd[1] = g_pipeFds[READ_FORM_DUMP_TO_VIRTUAL][1]; 501800b99b8Sopenharmony_ci return true; 502800b99b8Sopenharmony_ci} 503800b99b8Sopenharmony_ci 504800b99b8Sopenharmony_cistatic bool ReadPipeTimeout(int fd, uint64_t timeout, uint32_t* value) 505800b99b8Sopenharmony_ci{ 506800b99b8Sopenharmony_ci if (fd < 0 || value == NULL) { 507800b99b8Sopenharmony_ci return false; 508800b99b8Sopenharmony_ci } 509800b99b8Sopenharmony_ci struct pollfd pfds[1]; 510800b99b8Sopenharmony_ci pfds[0].fd = fd; 511800b99b8Sopenharmony_ci pfds[0].events = POLLIN; 512800b99b8Sopenharmony_ci 513800b99b8Sopenharmony_ci uint64_t startTime = GetTimeMilliseconds(); 514800b99b8Sopenharmony_ci uint64_t endTime = startTime + timeout; 515800b99b8Sopenharmony_ci int pollRet = -1; 516800b99b8Sopenharmony_ci do { 517800b99b8Sopenharmony_ci pollRet = poll(pfds, 1, timeout); 518800b99b8Sopenharmony_ci if ((pollRet > 0) && (pfds[0].revents && POLLIN)) { 519800b99b8Sopenharmony_ci if (OHOS_TEMP_FAILURE_RETRY(read(fd, value, sizeof(uint32_t))) == 520800b99b8Sopenharmony_ci (long int)(sizeof(uint32_t))) { 521800b99b8Sopenharmony_ci return true; 522800b99b8Sopenharmony_ci } 523800b99b8Sopenharmony_ci } 524800b99b8Sopenharmony_ci 525800b99b8Sopenharmony_ci uint64_t now = GetTimeMilliseconds(); 526800b99b8Sopenharmony_ci if (now >= endTime || now < startTime) { 527800b99b8Sopenharmony_ci break; 528800b99b8Sopenharmony_ci } else { 529800b99b8Sopenharmony_ci timeout = endTime - now; 530800b99b8Sopenharmony_ci } 531800b99b8Sopenharmony_ci } while (pollRet < 0 && errno == EINTR); 532800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_EREADPIPE); 533800b99b8Sopenharmony_ci DFXLOGE("read pipe failed , errno(%{public}d)", errno); 534800b99b8Sopenharmony_ci return false; 535800b99b8Sopenharmony_ci} 536800b99b8Sopenharmony_ci 537800b99b8Sopenharmony_cistatic bool ReadProcessDumpGetRegsMsg(void) 538800b99b8Sopenharmony_ci{ 539800b99b8Sopenharmony_ci CleanFd(&g_pipeFds[READ_FROM_DUMP_TO_MAIN][1]); 540800b99b8Sopenharmony_ci 541800b99b8Sopenharmony_ci DFXLOGI("start wait processdump read registers"); 542800b99b8Sopenharmony_ci const uint64_t readRegsTimeout = 5000; // 5s 543800b99b8Sopenharmony_ci uint32_t isFinishGetRegs = OPE_FAIL; 544800b99b8Sopenharmony_ci if (ReadPipeTimeout(g_pipeFds[READ_FROM_DUMP_TO_MAIN][0], readRegsTimeout, &isFinishGetRegs)) { 545800b99b8Sopenharmony_ci if (isFinishGetRegs == OPE_SUCCESS) { 546800b99b8Sopenharmony_ci DFXLOGI("processdump have get all registers ."); 547800b99b8Sopenharmony_ci return true; 548800b99b8Sopenharmony_ci } 549800b99b8Sopenharmony_ci } 550800b99b8Sopenharmony_ci 551800b99b8Sopenharmony_ci return false; 552800b99b8Sopenharmony_ci} 553800b99b8Sopenharmony_ci 554800b99b8Sopenharmony_cistatic void ReadUnwindFinishMsg(int sig) 555800b99b8Sopenharmony_ci{ 556800b99b8Sopenharmony_ci if (sig == SIGDUMP) { 557800b99b8Sopenharmony_ci return; 558800b99b8Sopenharmony_ci } 559800b99b8Sopenharmony_ci 560800b99b8Sopenharmony_ci DFXLOGI("start wait processdump unwind"); 561800b99b8Sopenharmony_ci const uint64_t unwindTimeout = 10000; // 10s 562800b99b8Sopenharmony_ci uint32_t isExitAfterUnwind = OPE_CONTINUE; 563800b99b8Sopenharmony_ci if (ReadPipeTimeout(g_pipeFds[READ_FROM_DUMP_TO_MAIN][0], unwindTimeout, &isExitAfterUnwind)) { 564800b99b8Sopenharmony_ci DFXLOGI("processdump unwind finish"); 565800b99b8Sopenharmony_ci } else { 566800b99b8Sopenharmony_ci DFXLOGE("wait processdump unwind finish timeout"); 567800b99b8Sopenharmony_ci } 568800b99b8Sopenharmony_ci} 569800b99b8Sopenharmony_ci 570800b99b8Sopenharmony_cistatic int ProcessDump(int sig) 571800b99b8Sopenharmony_ci{ 572800b99b8Sopenharmony_ci int prevDumpableStatus = prctl(PR_GET_DUMPABLE); 573800b99b8Sopenharmony_ci bool isTracerStatusModified = SetDumpState(); 574800b99b8Sopenharmony_ci 575800b99b8Sopenharmony_ci if (!InitPipe()) { 576800b99b8Sopenharmony_ci return -1; 577800b99b8Sopenharmony_ci } 578800b99b8Sopenharmony_ci g_request->dumpMode = FUSION_MODE; 579800b99b8Sopenharmony_ci 580800b99b8Sopenharmony_ci do { 581800b99b8Sopenharmony_ci uint64_t endTime; 582800b99b8Sopenharmony_ci int tid; 583800b99b8Sopenharmony_ci ParseSiValue(&g_request->siginfo, &endTime, &tid); 584800b99b8Sopenharmony_ci if (endTime != 0 && endTime <= GetAbsTimeMilliSeconds()) { 585800b99b8Sopenharmony_ci DFXLOGW("enter processdump has coat all time, just exit"); 586800b99b8Sopenharmony_ci break; 587800b99b8Sopenharmony_ci } 588800b99b8Sopenharmony_ci if (!StartProcessdump()) { 589800b99b8Sopenharmony_ci DFXLOGE("start processdump fail"); 590800b99b8Sopenharmony_ci break; 591800b99b8Sopenharmony_ci } 592800b99b8Sopenharmony_ci 593800b99b8Sopenharmony_ci if (!ReadProcessDumpGetRegsMsg()) { 594800b99b8Sopenharmony_ci break; 595800b99b8Sopenharmony_ci } 596800b99b8Sopenharmony_ci 597800b99b8Sopenharmony_ci if (!StartVMProcessUnwind()) { 598800b99b8Sopenharmony_ci DFXLOGE("start vm process unwind fail"); 599800b99b8Sopenharmony_ci break; 600800b99b8Sopenharmony_ci } 601800b99b8Sopenharmony_ci ReadUnwindFinishMsg(sig); 602800b99b8Sopenharmony_ci } while (false); 603800b99b8Sopenharmony_ci 604800b99b8Sopenharmony_ci CleanPipe(); 605800b99b8Sopenharmony_ci RestoreDumpState(prevDumpableStatus, isTracerStatusModified); 606800b99b8Sopenharmony_ci return 0; 607800b99b8Sopenharmony_ci} 608800b99b8Sopenharmony_ci 609800b99b8Sopenharmony_cistatic void ForkAndDoProcessDump(int sig) 610800b99b8Sopenharmony_ci{ 611800b99b8Sopenharmony_ci int prevDumpableStatus = prctl(PR_GET_DUMPABLE); 612800b99b8Sopenharmony_ci bool isTracerStatusModified = SetDumpState(); 613800b99b8Sopenharmony_ci int childPid = ForkBySyscall(); 614800b99b8Sopenharmony_ci if (childPid == 0) { 615800b99b8Sopenharmony_ci CloseFds(); 616800b99b8Sopenharmony_ci g_request->vmNsPid = syscall(SYS_getpid); 617800b99b8Sopenharmony_ci g_request->vmPid = GetRealPid(); 618800b99b8Sopenharmony_ci DFXLOGI("The vm pid(%{public}d:%{public}d).", g_request->vmPid, g_request->vmNsPid); 619800b99b8Sopenharmony_ci DFX_SetUpSigAlarmAction(); 620800b99b8Sopenharmony_ci alarm(ALARM_TIME_S); 621800b99b8Sopenharmony_ci _exit(ForkAndExecProcessDump()); 622800b99b8Sopenharmony_ci } else if (childPid < 0) { 623800b99b8Sopenharmony_ci DFXLOGE("[%{public}d]: Failed to fork child process, errno(%{public}d).", __LINE__, errno); 624800b99b8Sopenharmony_ci RestoreDumpState(prevDumpableStatus, isTracerStatusModified); 625800b99b8Sopenharmony_ci ForkAndExecProcessDump(); 626800b99b8Sopenharmony_ci return; 627800b99b8Sopenharmony_ci } 628800b99b8Sopenharmony_ci 629800b99b8Sopenharmony_ci DFXLOGI("Start wait for VmProcess(%{public}d) exit.", childPid); 630800b99b8Sopenharmony_ci errno = 0; 631800b99b8Sopenharmony_ci if (!WaitProcessExit(childPid, "VmProcess") && 632800b99b8Sopenharmony_ci sig != SIGDUMP && 633800b99b8Sopenharmony_ci sig != SIGLEAK_STACK) { 634800b99b8Sopenharmony_ci DFXLOGI("Wait VmProcess(%{public}d) exit timeout in handling critical signal.", childPid); 635800b99b8Sopenharmony_ci FillCrashExceptionAndReport(CRASH_SIGNAL_EWAITEXIT); 636800b99b8Sopenharmony_ci // do not left vm process 637800b99b8Sopenharmony_ci kill(childPid, SIGKILL); 638800b99b8Sopenharmony_ci } 639800b99b8Sopenharmony_ci 640800b99b8Sopenharmony_ci RestoreDumpState(prevDumpableStatus, isTracerStatusModified); 641800b99b8Sopenharmony_ci} 642800b99b8Sopenharmony_ci 643800b99b8Sopenharmony_ciint DfxDumpRequest(int sig, struct ProcessDumpRequest *request, void *reservedChildStack) 644800b99b8Sopenharmony_ci{ 645800b99b8Sopenharmony_ci int ret = 0; 646800b99b8Sopenharmony_ci if (request == NULL || reservedChildStack == NULL) { 647800b99b8Sopenharmony_ci DFXLOGE("Failed to DumpRequest because of error parameters!"); 648800b99b8Sopenharmony_ci return ret; 649800b99b8Sopenharmony_ci } 650800b99b8Sopenharmony_ci g_request = request; 651800b99b8Sopenharmony_ci g_reservedChildStack = reservedChildStack; 652800b99b8Sopenharmony_ci if (ProcessDump(sig) == 0) { 653800b99b8Sopenharmony_ci ret = sig == SIGDUMP || sig == SIGLEAK_STACK; 654800b99b8Sopenharmony_ci return ret; 655800b99b8Sopenharmony_ci } 656800b99b8Sopenharmony_ci 657800b99b8Sopenharmony_ci if (sig != SIGDUMP) { 658800b99b8Sopenharmony_ci ret = sig == SIGLEAK_STACK ? true : false; 659800b99b8Sopenharmony_ci ForkAndDoProcessDump(sig); 660800b99b8Sopenharmony_ci } else { 661800b99b8Sopenharmony_ci ret = true; 662800b99b8Sopenharmony_ci int recycleTid = clone(CloneAndDoProcessDump, reservedChildStack,\ 663800b99b8Sopenharmony_ci CLONE_THREAD | CLONE_SIGHAND | CLONE_VM, NULL); 664800b99b8Sopenharmony_ci if (recycleTid == -1) { 665800b99b8Sopenharmony_ci DFXLOGE("Failed to clone thread for recycle dump process, errno(%{public}d)", errno); 666800b99b8Sopenharmony_ci } 667800b99b8Sopenharmony_ci } 668800b99b8Sopenharmony_ci return ret; 669800b99b8Sopenharmony_ci} 670