* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* bbox_threads.cpp
*
* IDENTIFICATION
* src/gausskernel/cbb/bbox/bbox_threads.cpp
*
* -------------------------------------------------------------------------
*/
#include "bbox_syscall_support.h"
#include "bbox_threads.h"
#include "bbox_print.h"
#include "bbox_atomic.h"
#include "../../src/include/securec.h"
#include "../../src/include/securec_check.h"
static const u32 iSyncSignals[] = {
SIGABRT,
SIGILL,
SIGFPE,
SIGSEGV,
SIGBUS,
SIGXCPU,
SIGXFSZ,
};
struct TASK_ATTACH_INFO {
pid_t pid;
u8 cIsAttached;
};
struct TASK_CHECK_RESUME_ARGS {
struct TASK_ATTACH_INFO* pstTaskInfo;
s32 iThreadCount;
GET_THREAD_TYPE enType;
};
struct BBOX_ListParams {
s32 iResult;
s32 iError;
u8* pAltStackMem;
BBOX_GetAllThreadsCallBack pCallBack;
BBOX_GetAllThreadDone pDoneCallback;
void* pDoneArgs;
GET_THREAD_TYPE enGetType;
va_list ap;
};
u8 g_szAltStackMem[BBOX_ALT_STACKSIZE];
BBOX_ATOMIC_STRU g_isBusy = BBOX_ATOMIC_INIT(0);
* reserved count bytes on current stack, and set 0
*/
void BBOX_ReserveZeroStack(s32 count)
{
char buff[count];
errno_t rc = memset_s(buff, count, 0, count);
securec_check_c(rc, "\0", "\0");
(void)sys_read(-1, buff, count);
}
* clone the current process and runs the specified function
* return 0 if seccess else err code
*/
s32 BBOX_CloneRun(u32 uFlags, s32 (*pFn)(void*), void* pArg, ...)
{
pid_t pid;
if (pArg == NULL || pFn == NULL) {
return -1;
}
pid = sys__clone(pFn, (((char*)(pArg)) - 4096), uFlags | CLONE_UNTRACED, pArg, 0, 0, 0);
return pid;
}
* get count of thread
*/
s32 BBOX_GetTaskNumber(char* szTaskPath)
{
struct kernel_stat stProcSB = {0};
s32 iProc = -1;
if (szTaskPath == NULL) {
bbox_print(PRINT_ERR, "parameter szTaskPath is null.\n");
return -1;
}
iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0);
if (iProc < 0) {
bbox_print(PRINT_ERR, "open file %s failed, errno = %d\n", szTaskPath, errno);
return -1;
}
if (sys_fstat(iProc, &stProcSB)) {
bbox_print(PRINT_ERR, "Get file %s fstat, errno = %d, iProc = %d\n", szTaskPath, errno, iProc);
sys_close(iProc);
return -1;
}
sys_close(iProc);
return stProcSB.st_nlink;
}
* get thread pid
*/
s32 BBOX_GetTaskId(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iSize, char* szTaskPath)
{
s32 iProc = -1;
pid_t pid = 0;
struct linux_dirent* pstEntry = NULL;
char szBuff[PAGE_SIZE];
ssize_t nBytes;
const char* pszPtr = NULL;
s32 iThreadCount = 0;
if (pstTaskInfo == NULL || szTaskPath == NULL) {
bbox_print(PRINT_ERR,
"parameter is invalid, pstTaskInfo or szTaskPath is NULL, iSize = %d \n",
iSize);
return -1;
}
iProc = sys_open(szTaskPath, O_RDONLY | O_DIRECTORY, 0);
if (iProc < 0) {
bbox_print(PRINT_ERR, "open '%s' failed, errno = %d\n", szTaskPath, errno);
return -1;
}
for (; iThreadCount < iSize;) {
nBytes = sys_getdents(iProc, (struct linux_dirent*)szBuff, sizeof(szBuff));
if (nBytes < 0) {
bbox_print(PRINT_ERR, "Get dents '%s' failed, errno = %d\n", szTaskPath, errno);
goto errout;
} else if (0 == nBytes) {
sys_lseek(iProc, 0, SEEK_SET);
break;
}
for (pstEntry = (struct linux_dirent*)szBuff;
(pstEntry < (struct linux_dirent*)&szBuff[nBytes]) && (iThreadCount < iSize);
pstEntry = (struct linux_dirent*)((char*)pstEntry + pstEntry->d_reclen)) {
if (pstEntry->d_ino == 0) {
continue;
}
pszPtr = pstEntry->d_name;
bbox_print(PRINT_DBG, "dir: %s/%s\n", szTaskPath, pszPtr);
if (*pszPtr == '.') {
pszPtr++;
}
if (*pszPtr < '0' || *pszPtr > '9') {
continue;
}
pid = bbox_atoi(pszPtr);
if (!pid || pid == sys_gettid()) {
bbox_print(PRINT_ERR, "pid is invalid , pid = %d\n", pid);
continue;
}
pstTaskInfo[iThreadCount].pid = pid;
pstTaskInfo[iThreadCount].cIsAttached = 0;
iThreadCount++;
}
}
sys_close(iProc);
return iThreadCount;
errout:
sys_close(iProc);
return -1;
}
* a ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
s32 BBOX_PtraceAttachPid(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount, s32 iDoPtraceCheck)
{
u32 i;
pid_t pid;
unsigned long m = 0;
unsigned long n = 0;
if (pstTaskInfo == NULL || iPidCount <= 0) {
return RET_ERR;
}
for (i = 0; i < (u32)iPidCount; i++) {
pid = pstTaskInfo[i].pid;
if (sys_ptrace(PTRACE_ATTACH, pid, (void*)0, (void*)0) < 0) {
bbox_print(PRINT_ERR, "ptrace failed, pid = %d, errno = %d\n", pid, errno);
continue;
}
while (sys_waitpid(pid, (int*)0, __WALL) < 0) {
if (errno != EINTR) {
bbox_print(PRINT_ERR, "wait for %d failed, errno = %d\n", pid, errno);
sys_ptrace(PTRACE_DETACH, pid, 0, 0);
break;
}
}
if (iDoPtraceCheck) {
int ret = 0;
ret = sys_ptrace(PTRACE_PEEKDATA, pid, &m, &n);
if (ret || (m != n)) {
bbox_print(PRINT_ERR,
"ptrace peek data failed, pid = %d, ret = %d,m = %lu, n = %lu, errno = %d\n",
pid, ret, m, n, errno);
sys_ptrace(PTRACE_DETACH, pid, 0, 0);
continue;
}
}
bbox_print(PRINT_LOG, "ptrace attach %d\n", pid);
pstTaskInfo[i].cIsAttached = 1;
}
return RET_OK;
}
* cancel ptrace debug thread
* in : TASK_ATTACH_INFO - thread information
* iPidCount - count of thread information
* iDoPtraceCheck - check if ptrace success
* return : 0 - success
* err code - failed
*/
void BBOX_DetachAllThread(struct TASK_ATTACH_INFO* pstTaskInfo, s32 iPidCount)
{
u32 i;
pid_t pid;
if (pstTaskInfo == NULL || iPidCount <= 0) {
return;
}
for (i = 0; i < (u32)iPidCount; i++) {
pid = pstTaskInfo[i].pid;
if (!pstTaskInfo[i].cIsAttached) {
continue;
}
bbox_print(PRINT_LOG, "ptrace detach %d\n", pid);
sys_sched_yield();
sys_ptrace(PTRACE_DETACH, pid, 0, 0);
sys_kill(pid, SIGCONT);
}
return;
}
* notification function after dump module have got all data, to recover a thread.
*/
void BBOX_CheckResumeThread(void* pArgs)
{
struct TASK_CHECK_RESUME_ARGS* pstTaskCheck = NULL;
if (pArgs == NULL) {
return;
}
pstTaskCheck = (struct TASK_CHECK_RESUME_ARGS*)pArgs;
if (GET_TYPE_SNAP == pstTaskCheck->enType) {
BBOX_DetachAllThread(pstTaskCheck->pstTaskInfo, pstTaskCheck->iThreadCount);
}
}
* ptrace thread and run function.
* in : pstArgs - information of callback function
* iMaxThreadCount - max count of thread
* pszProcSelfTask - /proc/[pid]/task of current tracked thread.
* return 0 if success else err code.
*/
s32 BBOX_PtraceAndRun(struct BBOX_ListParams* pstArgs, s32 iMaxThreadCount, char* pszProcSelfTask)
{
struct TASK_ATTACH_INFO stTaskInfo[iMaxThreadCount];
pid_t thread_pids[iMaxThreadCount];
struct TASK_CHECK_RESUME_ARGS stTaskCheck;
s32 iThreadCount = 0;
s32 iAttachCount = 0;
s32 iRet = 0;
s32 iDoPtraceCheck = 1;
s32 i;
if (pstArgs == NULL || pszProcSelfTask == NULL || iMaxThreadCount <= 0) {
bbox_print(PRINT_ERR,
"Parameter is invald, pstArgs or pszProcSelfTask is NULL, iMaxThreadCount = %d \n",
iMaxThreadCount);
return RET_ERR;
}
errno_t rc = memset_s(stTaskInfo, sizeof(stTaskInfo), 0, sizeof(stTaskInfo));
securec_check_c(rc, "\0", "\0");
rc = memset_s(thread_pids, sizeof(thread_pids), 0, sizeof(thread_pids));
securec_check_c(rc, "\0", "\0");
rc = memset_s(&stTaskCheck, sizeof(stTaskCheck), 0, sizeof(stTaskCheck));
securec_check_c(rc, "\0", "\0");
iThreadCount = BBOX_GetTaskId(stTaskInfo, iMaxThreadCount, pszProcSelfTask);
if (iThreadCount <= 0) {
bbox_print(PRINT_ERR, "Get task id failed.\n");
goto errout;
}
if (GET_TYPE_DUMP != pstArgs->enGetType) {
iDoPtraceCheck = 0;
} else {
iDoPtraceCheck = 1;
}
iRet = BBOX_PtraceAttachPid(stTaskInfo, iThreadCount, iDoPtraceCheck);
if (iRet != RET_OK) {
bbox_print(PRINT_ERR, "Ptrace attache failed.\n");
goto errout;
}
for (i = 0; i < iThreadCount; i++) {
if (!stTaskInfo[i].cIsAttached) {
continue;
}
thread_pids[iAttachCount] = stTaskInfo[i].pid;
iAttachCount++;
}
stTaskCheck.pstTaskInfo = stTaskInfo;
stTaskCheck.enType = pstArgs->enGetType;
stTaskCheck.iThreadCount = iThreadCount;
bbox_print(PRINT_TIP, "Thread count :%d\n", iThreadCount);
bbox_print(PRINT_TIP, "Ptraced thread count :%d\n", iAttachCount);
bbox_print(PRINT_LOG, "Run callback: thread count = %d\n", iThreadCount);
pstArgs->iResult = pstArgs->pCallBack(BBOX_CheckResumeThread, &stTaskCheck, iAttachCount, thread_pids, pstArgs->ap);
pstArgs->iError = errno;
BBOX_DetachAllThread(stTaskInfo, iThreadCount);
return RET_OK;
errout:
BBOX_DetachAllThread(stTaskInfo, iThreadCount);
return RET_ERR;
}
* print log information if export failed.
*/
void BBOX_PrintFailedLog(const char* pFileName)
{
ssize_t iRet = 0;
s32 iWriteSize = 0;
s32 iBboxLogFd = -1;
iBboxLogFd = sys_open(pFileName, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (iBboxLogFd < 0) {
bbox_print(PRINT_ERR, "open failed, errno = %d\n", errno);
return;
}
iWriteSize = bbox_strnlen(g_acBBoxLog, BBOX_LOG_SIZE) + 1;
iWriteSize = (iWriteSize > BBOX_LOG_SIZE) ? BBOX_LOG_SIZE : iWriteSize;
iRet = sys_write(iBboxLogFd, g_acBBoxLog, iWriteSize);
if (iRet < 0) {
bbox_print(PRINT_ERR, "write failed, errno = %d\n", errno);
sys_close(iBboxLogFd);
return;
}
sys_close(iBboxLogFd);
}
* export thread information.
*/
void BBOX_ListThread(struct BBOX_ListParams* pstArgs)
{
pid_t ppid = 0;
s32 iMaker = -1;
s32 iMaxThreadCount = 0;
s32 iRet = 0;
struct kernel_stat stMarkerSB;
char szProcSelfTask[BBOX_PROC_PATH_LEN];
char pszMarkPath[BBOX_PROC_PATH_LEN];
stack_t altstack;
errno_t rc = EOK;
if (pstArgs == NULL) {
bbox_print(PRINT_ERR, "pstArgs is NULL.\n");
return;
}
ppid = sys_getppid();
iMaker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0);
if (iMaker < 0) {
bbox_print(PRINT_ERR, "sys_socket error, errno = %d\n", errno);
goto errout;
}
if (sys_fcntl(iMaker, F_SETFD, FD_CLOEXEC) < 0) {
bbox_print(PRINT_ERR, "sys_fcntl error, errno = %d\n", errno);
goto errout;
}
if (bbox_snprintf(szProcSelfTask, BBOX_PROC_PATH_LEN, "/proc/%d/task", ppid) <= 0) {
bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno);
goto errout;
}
if (bbox_snprintf(pszMarkPath, BBOX_PROC_PATH_LEN, "/proc/%d/fd/%d", ppid, iMaker) <= 0) {
bbox_print(PRINT_ERR, "bbox_snprintf is failed, errno = %d.\n", errno);
goto errout;
}
bbox_print(PRINT_TIP, "Get information for pid %d:\n", ppid);
rc = memset_s(&stMarkerSB, sizeof(stMarkerSB), 0, sizeof(stMarkerSB));
securec_check_c(rc, "\0", "\0");
if (sys_stat(pszMarkPath, &stMarkerSB) < 0) {
bbox_print(PRINT_ERR, "sys_stat error, errno = %d, path = %s\n", errno, pszMarkPath);
goto errout;
}
rc = memset_s(&altstack, sizeof(altstack), 0, sizeof(altstack));
securec_check_c(rc, "\0", "\0");
altstack.ss_sp = pstArgs->pAltStackMem;
altstack.ss_flags = 0;
altstack.ss_size = BBOX_ALT_STACKSIZE;
sys_sigaltstack(&altstack, (const stack_t*)NULL);
iMaxThreadCount = BBOX_GetTaskNumber(szProcSelfTask);
if (iMaxThreadCount <= 0) {
bbox_print(PRINT_ERR, "Get task number failed.\n");
goto errout;
}
iRet = BBOX_PtraceAndRun(pstArgs, iMaxThreadCount, szProcSelfTask);
if (iRet != RET_OK) {
bbox_print(PRINT_ERR, "ptrace task and run failed.\n");
goto errout;
}
bbox_print(PRINT_TIP, "Get information success.\n");
sys_close(iMaker);
if (RET_OK != pstArgs->iResult) {
BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2));
}
if (pstArgs->pDoneCallback != NULL) {
pstArgs->pDoneCallback(pstArgs->pDoneArgs);
}
sys_exit(0);
errout:
if (iMaker > 0) {
sys_close(iMaker);
}
bbox_print(PRINT_ERR, "Get information failed.\n");
pstArgs->iResult = -1;
pstArgs->iError = errno;
BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2));
if (pstArgs->pDoneCallback != NULL) {
pstArgs->pDoneCallback(pstArgs->pDoneArgs);
}
sys_exit(1);
}
* get return value of child process
* in : iClonePid - PID of child process
* pstArgs - parameter
* iCloneErrno - err code
* return 0 if success else failed.
*/
s32 BBOX_GetClonePidResult(pid_t iClonePid, struct BBOX_ListParams* pstArgs, s32 iCloneErrno)
{
s32 iStatus = 0;
s32 iRet = 0;
if (iClonePid < 0) {
bbox_print(PRINT_ERR, "Clone failed, can't create child process, errno = %d.\n", iCloneErrno);
BBOX_PrintFailedLog((char*)(((struct BBOX_ListDirParam*)(pstArgs->pDoneArgs))->pArg2));
if (pstArgs->pDoneCallback != NULL) {
pstArgs->pDoneCallback(pstArgs->pDoneArgs);
}
return RET_ERR;
}
while ((iRet = sys_waitpid(iClonePid, &iStatus, __WALL)) < 0 && errno == EINTR) {
continue;
}
bbox_print(PRINT_LOG, "clone pid %d ret is %x, status = %x\n", iClonePid, iRet, WIFEXITED(iStatus));
if (iRet < 0) {
pstArgs->iError = errno;
pstArgs->iResult = -1;
} else if (WIFEXITED(iStatus)) {
switch (WEXITSTATUS(iStatus)) {
case 0:
break;
case 2:
pstArgs->iError = EFAULT;
pstArgs->iResult = -1;
break;
case 3:
pstArgs->iError = EPERM;
pstArgs->iResult = 1;
break;
default:
pstArgs->iError = ECHILD;
pstArgs->iResult = -1;
break;
}
} else if (!WIFEXITED(iStatus)) {
pstArgs->iError = EFAULT;
pstArgs->iResult = -1;
bbox_print(PRINT_ERR, "WIFEXITED status failed");
} else {
pstArgs->iError = iCloneErrno;
pstArgs->iResult = -1;
bbox_print(PRINT_ERR, "WIFEXITED error, errno = %d\n", iCloneErrno);
}
return RET_OK;
}
* get all threads and run specify function
*/
s32 BBOX_GetAllThreads(
GET_THREAD_TYPE enType, BBOX_GetAllThreadDone pDone, void* pDoneArgs, BBOX_GetAllThreadsCallBack pCallback, ...)
{
struct BBOX_ListParams stArgs;
struct kernel_sigset_t stSigBlocked;
struct kernel_sigset_t stSigOld;
s32 iDumpable = 1;
s32 iSigNo;
s32 iCloneErrno;
pid_t ClonePid;
errno = 0;
if (BBOX_AtomicIncReturn(&g_isBusy) > 1) {
BBOX_AtomicDec(&g_isBusy);
bbox_print(PRINT_ERR, "Dump task is running.\n");
errno = EALREADY;
return -1;
}
if (enType >= GET_TYPE_BUTT || pCallback == NULL) {
bbox_print(PRINT_ERR, "Parameter is invalid, enType = %d, and maybe pCallback is NULL", enType);
BBOX_AtomicDec(&g_isBusy);
return -1;
}
errno_t rc = memset_s(&stArgs, sizeof(stArgs), 0, sizeof(stArgs));
securec_check_c(rc, "\0", "\0");
rc = memset_s(&stSigBlocked, sizeof(stSigBlocked), 0, sizeof(stSigBlocked));
securec_check_c(rc, "\0", "\0");
rc = memset_s(&stSigOld, sizeof(stSigOld), 0, sizeof(stSigOld));
securec_check_c(rc, "\0", "\0");
va_start(stArgs.ap, pCallback);
rc = memset_s(g_szAltStackMem, BBOX_ALT_STACKSIZE, 0, BBOX_ALT_STACKSIZE);
securec_check_c(rc, "\0", "\0");
BBOX_ReserveZeroStack(1024 * 32);
iDumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
if (!iDumpable) {
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
}
stArgs.iResult = -1;
stArgs.iError = 0;
stArgs.pAltStackMem = g_szAltStackMem;
stArgs.pCallBack = pCallback;
stArgs.pDoneCallback = pDone;
stArgs.pDoneArgs = pDoneArgs;
stArgs.enGetType = enType;
sys_sigfillset(&stSigBlocked);
for (iSigNo = 0; iSigNo < (s32)(sizeof(iSyncSignals) / sizeof(*iSyncSignals)); iSigNo++) {
sys_sigdelset(&stSigBlocked, iSyncSignals[iSigNo]);
}
if (sys_sigprocmask(SIG_BLOCK, &stSigBlocked, &stSigOld)) {
stArgs.iError = errno;
stArgs.iResult = -1;
bbox_print(PRINT_ERR, "sys_sigprocmask error, errno = %d\n", errno);
goto errout;
}
if (GET_TYPE_DUMP == enType) {
ClonePid = BBOX_CloneRun(CLONE_VM | CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs);
} else {
ClonePid = BBOX_CloneRun(CLONE_FS | CLONE_FILES, (s32(*)(void*))BBOX_ListThread, &stArgs);
}
iCloneErrno = errno;
sys_sigprocmask(SIG_SETMASK, &stSigOld, &stSigOld);
if (BBOX_GetClonePidResult(ClonePid, &stArgs, iCloneErrno) != RET_OK) {
bbox_print(PRINT_ERR, "BBOX_GetClonePidResult error\n");
}
errout:
BBOX_AtomicDec(&g_isBusy);
if (!iDumpable) {
sys_prctl(PR_SET_DUMPABLE, iDumpable, 0, 0, 0);
}
va_end(stArgs.ap);
errno = stArgs.iError;
return stArgs.iResult;
}