diff options
Diffstat (limited to 'src/VBox/Runtime/r3/solaris')
-rw-r--r-- | src/VBox/Runtime/r3/solaris/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp | 107 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp | 102 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp | 2365 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/coredumper-solaris.h | 167 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp | 565 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp | 324 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/mp-solaris.cpp | 469 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp | 71 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp | 172 | ||||
-rw-r--r-- | src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp | 94 |
11 files changed, 4436 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/solaris/Makefile.kup b/src/VBox/Runtime/r3/solaris/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/Makefile.kup diff --git a/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp new file mode 100644 index 00000000..ade80801 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/RTSystemQueryDmiString-solaris.cpp @@ -0,0 +1,107 @@ +/* $Id: RTSystemQueryDmiString-solaris.cpp $ */ +/** @file + * IPRT - RTSystemQueryDmiString, solaris ring-3. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/string.h> + +#include <smbios.h> +#include <errno.h> + + +RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf) +{ + AssertPtrReturn(pszBuf, VERR_INVALID_POINTER); + AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER); + *pszBuf = '\0'; + AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER); + + int rc = VERR_NOT_SUPPORTED; + int err = 0; + smbios_hdl_t *pSMB = smbios_open(NULL /* default fd */, SMB_VERSION, 0 /* flags */, &err); + if (pSMB) + { + smbios_system_t hSMBSys; + id_t hSMBId = smbios_info_system(pSMB, &hSMBSys); + if (hSMBId != SMB_ERR) + { + /* Don't need the common bits for the product UUID. */ + if (enmString == RTSYSDMISTR_PRODUCT_UUID) + { + static char const s_szHex[17] = "0123456789ABCDEF"; + char szData[64]; + char *pszData = szData; + unsigned cchUuid = RT_MIN(hSMBSys.smbs_uuidlen, sizeof(szData) - 1); + for (unsigned i = 0; i < cchUuid; i++) + { + *pszData++ = s_szHex[hSMBSys.smbs_uuid[i] >> 4]; + *pszData++ = s_szHex[hSMBSys.smbs_uuid[i] & 0xf]; + if (i == 3 || i == 5 || i == 7 || i == 9) + *pszData++ = '-'; + } + *pszData = '\0'; + rc = RTStrCopy(pszBuf, cbBuf, szData); + smbios_close(pSMB); + return rc; + } + + smbios_info_t hSMBInfo; + id_t hSMBInfoId = smbios_info_common(pSMB, hSMBId, &hSMBInfo); + if (hSMBInfoId != SMB_ERR) + { + switch (enmString) + { + case RTSYSDMISTR_PRODUCT_NAME: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_product); break; + case RTSYSDMISTR_PRODUCT_VERSION: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_version); break; + case RTSYSDMISTR_PRODUCT_SERIAL: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_serial); break; + case RTSYSDMISTR_MANUFACTURER: rc = RTStrCopy(pszBuf, cbBuf, hSMBInfo.smbi_manufacturer); break; + + default: /* make gcc happy */ + rc = VERR_NOT_SUPPORTED; + } + smbios_close(pSMB); + return rc; + } + } + + /* smbios_* error path. */ + err = smbios_errno(pSMB); + smbios_close(pSMB); + } + + /* Do some error conversion. */ + if (err == EPERM || err == EACCES) + rc = VERR_ACCESS_DENIED; + return rc; +} + diff --git a/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp b/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp new file mode 100644 index 00000000..15495433 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/RTSystemShutdown-solaris.cpp @@ -0,0 +1,102 @@ +/* $Id: RTSystemShutdown-solaris.cpp $ */ +/** @file + * IPRT - RTSystemShutdown, linux implementation. + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/env.h> +#include <iprt/err.h> +#include <iprt/process.h> +#include <iprt/string.h> + + +RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg) +{ + AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER); + + /* + * Assemble the argument vector. + */ + int iArg = 0; + const char *apszArgs[8]; + + RT_BZERO(apszArgs, sizeof(apszArgs)); + + apszArgs[iArg++] = "/usr/sbin/shutdown"; + apszArgs[iArg++] = "-y"; /* Pre-answer confirmation question. */ + apszArgs[iArg++] = "-i"; /* Change to the following state. */ + switch (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) + { + case RTSYSTEM_SHUTDOWN_HALT: + apszArgs[iArg++] = "0"; + break; + case RTSYSTEM_SHUTDOWN_REBOOT: + apszArgs[iArg++] = "6"; + break; + case RTSYSTEM_SHUTDOWN_POWER_OFF: + case RTSYSTEM_SHUTDOWN_POWER_OFF_HALT: + apszArgs[iArg++] = "5"; + break; + } + + apszArgs[iArg++] = "-g"; /* Grace period. */ + + char szWhen[80]; + if (cMsDelay < 500) + strcpy(szWhen, "0"); + else + RTStrPrintf(szWhen, sizeof(szWhen), "%u", (unsigned)((cMsDelay + 499) / 1000)); + apszArgs[iArg++] = szWhen; + + apszArgs[iArg++] = pszLogMsg; + + + /* + * Start the shutdown process and wait for it to complete. + */ + RTPROCESS hProc; + int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, 0 /*fFlags*/, &hProc); + if (RT_FAILURE(rc)) + return rc; + + RTPROCSTATUS ProcStatus; + rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &ProcStatus); + if (RT_SUCCESS(rc)) + { + if ( ProcStatus.enmReason != RTPROCEXITREASON_NORMAL + || ProcStatus.iStatus != 0) + rc = VERR_SYS_SHUTDOWN_FAILED; + } + + return rc; +} + diff --git a/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp b/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp new file mode 100644 index 00000000..c0d09738 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/coredumper-solaris.cpp @@ -0,0 +1,2365 @@ +/* $Id: coredumper-solaris.cpp $ */ +/** @file + * IPRT - Custom Core Dumper, Solaris. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DEFAULT +#include <iprt/coredumper.h> + +#include <iprt/asm.h> +#include <iprt/dir.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/param.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/string.h> +#include <iprt/thread.h> +#include "coredumper-solaris.h" + +#ifdef RT_OS_SOLARIS +# include <syslog.h> +# include <signal.h> +# include <stdlib.h> +# include <unistd.h> +# include <errno.h> +# include <zone.h> +# include <sys/proc.h> +# include <sys/sysmacros.h> +# include <sys/systeminfo.h> +# include <sys/mman.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +# include <ucontext.h> +#endif /* RT_OS_SOLARIS */ + +#include <iprt/formats/elf.h> +#include <iprt/formats/elf64.h> + + +/********************************************************************************************************************************* +* Globals * +*********************************************************************************************************************************/ +static RTNATIVETHREAD volatile g_CoreDumpThread = NIL_RTNATIVETHREAD; +static bool volatile g_fCoreDumpSignalSetup = false; +static uint32_t volatile g_fCoreDumpFlags = 0; +static char g_szCoreDumpDir[PATH_MAX] = { 0 }; +static char g_szCoreDumpFile[PATH_MAX] = { 0 }; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define CORELOG_NAME "CoreDumper: " +#define CORELOG(a) Log(a) +#define CORELOGRELSYS(a) \ + do { \ + rtCoreDumperSysLogWrapper a; \ + } while (0) + + +/** + * ELFNOTEHDR: ELF NOTE header. + */ +typedef struct ELFNOTEHDR +{ + Elf64_Nhdr Hdr; /* Header of NOTE section */ + char achName[8]; /* Name of NOTE section */ +} ELFNOTEHDR; +typedef ELFNOTEHDR *PELFNOTEHDR; + +/** + * Wrapper function to write IPRT format style string to the syslog. + * + * @param pszFormat Format string + */ +static void rtCoreDumperSysLogWrapper(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + char szBuf[1024]; + RTStrPrintfV(szBuf, sizeof(szBuf), pszFormat, va); + va_end(va); + syslog(LOG_ERR, "%s", szBuf); +} + + +/** + * Determines endianness of the system. Just for completeness. + * + * @return Will return false if system is little endian, true otherwise. + */ +static bool IsBigEndian() +{ + const int i = 1; + char *p = (char *)&i; + if (p[0] == 1) + return false; + return true; +} + + +/** + * Reads from a file making sure an interruption doesn't cause a failure. + * + * @param fd Handle to the file to read. + * @param pv Where to store the read data. + * @param cbToRead Size of data to read. + * + * @return IPRT status code. + */ +static int ReadFileNoIntr(int fd, void *pv, size_t cbToRead) +{ + for (;;) + { + ssize_t cbRead = read(fd, pv, cbToRead); + if (cbRead < 0) + { + if (errno == EINTR) + continue; + return RTErrConvertFromErrno(errno); + } + if ((size_t)cbRead == cbToRead) + return VINF_SUCCESS; + if ((size_t)cbRead > cbToRead) + return VERR_INTERNAL_ERROR_3; + if (cbRead == 0) + return VERR_EOF; + pv = (uint8_t *)pv + cbRead; + cbToRead -= cbRead; + } +} + + +/** + * Writes to a file making sure an interruption doesn't cause a failure. + * + * @param fd Handle to the file to write to. + * @param pv Pointer to what to write. + * @param cbToWrite Size of data to write. + * + * @return IPRT status code. + */ +static int WriteFileNoIntr(int fd, const void *pv, size_t cbToWrite) +{ + for (;;) + { + ssize_t cbWritten = write(fd, pv, cbToWrite); + if (cbWritten < 0) + { + if (errno == EINTR) + continue; + return RTErrConvertFromErrno(errno); + } + if ((size_t)cbWritten == cbToWrite) + return VINF_SUCCESS; + if ((size_t)cbWritten > cbToWrite) + return VERR_INTERNAL_ERROR_2; + pv = (uint8_t const *)pv + cbWritten; + cbToWrite -= cbWritten; + } +} + + +/** + * Read from a given offset in the process' address space. + * + * @param pSolProc Pointer to the solaris process. + * @param off The offset to read from. + * @param pvBuf Where to read the data into. + * @param cbToRead Number of bytes to read. + * + * @return VINF_SUCCESS, if all the given bytes was read in, otherwise VERR_READ_ERROR. + */ +static ssize_t ProcReadAddrSpace(PRTSOLCOREPROCESS pSolProc, RTFOFF off, void *pvBuf, size_t cbToRead) +{ + for (;;) + { + ssize_t cbRead = pread(pSolProc->fdAs, pvBuf, cbToRead, off); + if (cbRead < 0) + { + if (errno == EINTR) + continue; + return RTErrConvertFromErrno(errno); + } + if ((size_t)cbRead == cbToRead) + return VINF_SUCCESS; + if ((size_t)cbRead > cbToRead) + return VERR_INTERNAL_ERROR_4; + if (cbRead == 0) + return VERR_EOF; + + pvBuf = (uint8_t *)pvBuf + cbRead; + cbToRead -= cbRead; + off += cbRead; + } +} + + +/** + * Determines if the current process' architecture is suitable for dumping core. + * + * @param pSolProc Pointer to the solaris process. + * + * @return true if the architecture matches the current one. + */ +static inline bool IsProcessArchNative(PRTSOLCOREPROCESS pSolProc) +{ + psinfo_t *pProcInfo = (psinfo_t *)pSolProc->pvProcInfo; + return pProcInfo->pr_dmodel == PR_MODEL_NATIVE; +} + + +/** + * Helper function to get the size_t compatible file size from a file + * descriptor. + * + * @return The file size (in bytes). + * @param fd The file descriptor. + */ +static size_t GetFileSizeByFd(int fd) +{ + struct stat st; + if (fstat(fd, &st) == 0) + { + if (st.st_size <= 0) + return 0; + size_t cbFile = (size_t)st.st_size; + return (off_t)cbFile == st.st_size ? cbFile : ~(size_t)0; + } + + CORELOGRELSYS((CORELOG_NAME "GetFileSizeByFd: fstat failed rc=%Rrc\n", RTErrConvertFromErrno(errno))); + return 0; +} + + +/** + * Helper function to get the size_t compatible size of a file given its path. + * + * @return The file size (in bytes). + * @param pszPath Pointer to the full path of the file. + */ +static size_t GetFileSizeByName(const char *pszPath) +{ + int fd = open(pszPath, O_RDONLY); + if (fd < 0) + { + CORELOGRELSYS((CORELOG_NAME "GetFileSizeByName: failed to open %s rc=%Rrc\n", pszPath, RTErrConvertFromErrno(errno))); + return 0; + } + + size_t cb = GetFileSizeByFd(fd); + close(fd); + return cb; +} + + +/** + * Pre-compute and pre-allocate sufficient memory for dumping core. + * This is meant to be called once, as a single-large anonymously + * mapped memory area which will be used during the core dumping routines. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int AllocMemoryArea(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore->pvCore == NULL, VERR_ALREADY_EXISTS); + + static struct + { + const char *pszFilePath; /* Proc based path */ + size_t cbHeader; /* Size of header */ + size_t cbEntry; /* Size of each entry in file */ + size_t cbAccounting; /* Size of each accounting entry per entry */ + } const s_aPreAllocTable[] = + { + { "/proc/%d/psinfo", 0, 0, 0 }, + { "/proc/%d/map", 0, sizeof(prmap_t), sizeof(RTSOLCOREMAPINFO) }, + { "/proc/%d/auxv", 0, 0, 0 }, + { "/proc/%d/lpsinfo", sizeof(prheader_t), sizeof(lwpsinfo_t), sizeof(RTSOLCORETHREADINFO) }, + { "/proc/%d/lstatus", 0, 0, 0 }, + { "/proc/%d/ldt", 0, 0, 0 }, + { "/proc/%d/cred", sizeof(prcred_t), sizeof(gid_t), 0 }, + { "/proc/%d/priv", sizeof(prpriv_t), sizeof(priv_chunk_t), 0 }, + }; + + size_t cb = 0; + for (unsigned i = 0; i < RT_ELEMENTS(s_aPreAllocTable); i++) + { + char szPath[PATH_MAX]; + RTStrPrintf(szPath, sizeof(szPath), s_aPreAllocTable[i].pszFilePath, (int)pSolCore->SolProc.Process); + size_t cbFile = GetFileSizeByName(szPath); + cb += cbFile; + if ( cbFile > 0 + && s_aPreAllocTable[i].cbEntry > 0) + { + cb += ((cbFile - s_aPreAllocTable[i].cbHeader) / s_aPreAllocTable[i].cbEntry) + * (s_aPreAllocTable[i].cbAccounting > 0 ? s_aPreAllocTable[i].cbAccounting : 1); + cb += s_aPreAllocTable[i].cbHeader; + } + } + + /* + * Make room for our own mapping accountant entry which will also be included in the core. + */ + cb += sizeof(RTSOLCOREMAPINFO); + + /* + * Allocate the required space, plus some extra room. + */ + cb += _128K; + void *pv = mmap(NULL, cb, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1 /* fd */, 0 /* offset */); + if (pv != MAP_FAILED) + { + CORELOG((CORELOG_NAME "AllocMemoryArea: memory area of %u bytes allocated.\n", cb)); + pSolCore->pvCore = pv; + pSolCore->pvFree = pv; + pSolCore->cbCore = cb; + return VINF_SUCCESS; + } + CORELOGRELSYS((CORELOG_NAME "AllocMemoryArea: failed cb=%u\n", cb)); + return VERR_NO_MEMORY; +} + + +/** + * Free memory area used by the core object. + * + * @param pSolCore Pointer to the core object. + */ +static void FreeMemoryArea(PRTSOLCORE pSolCore) +{ + AssertReturnVoid(pSolCore); + AssertReturnVoid(pSolCore->pvCore); + AssertReturnVoid(pSolCore->cbCore > 0); + + munmap(pSolCore->pvCore, pSolCore->cbCore); + CORELOG((CORELOG_NAME "FreeMemoryArea: memory area of %u bytes freed.\n", pSolCore->cbCore)); + + pSolCore->pvCore = NULL; + pSolCore->pvFree= NULL; + pSolCore->cbCore = 0; +} + + +/** + * Get a chunk from the area of allocated memory. + * + * @param pSolCore Pointer to the core object. + * @param cb Size of requested chunk. + * + * @return Pointer to allocated memory, or NULL on failure. + */ +static void *GetMemoryChunk(PRTSOLCORE pSolCore, size_t cb) +{ + AssertReturn(pSolCore, NULL); + AssertReturn(pSolCore->pvCore, NULL); + AssertReturn(pSolCore->pvFree, NULL); + + size_t cbAllocated = (char *)pSolCore->pvFree - (char *)pSolCore->pvCore; + if (cbAllocated < pSolCore->cbCore) + { + char *pb = (char *)pSolCore->pvFree; + pSolCore->pvFree = pb + cb; + return pb; + } + + return NULL; +} + + +/** + * Reads the proc file's content into a newly allocated buffer. + * + * @param pSolCore Pointer to the core object. + * @param pszProcFileName Only the name of the file to read from + * (/proc/\<pid\> will be prepended) + * @param ppv Where to store the allocated buffer. + * @param pcb Where to store size of the buffer. + * + * @return IPRT status code. If the proc file is 0 bytes, VINF_SUCCESS is + * returned with pointed to values of @c ppv, @c pcb set to NULL and 0 + * respectively. + */ +static int ProcReadFileInto(PRTSOLCORE pSolCore, const char *pszProcFileName, void **ppv, size_t *pcb) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + char szPath[PATH_MAX]; + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/%s", (int)pSolCore->SolProc.Process, pszProcFileName); + int rc = VINF_SUCCESS; + int fd = open(szPath, O_RDONLY); + if (fd >= 0) + { + *pcb = GetFileSizeByFd(fd); + if (*pcb > 0) + { + *ppv = GetMemoryChunk(pSolCore, *pcb); + if (*ppv) + rc = ReadFileNoIntr(fd, *ppv, *pcb); + else + rc = VERR_NO_MEMORY; + } + else + { + *pcb = 0; + *ppv = NULL; + rc = VINF_SUCCESS; + } + close(fd); + } + else + { + rc = RTErrConvertFromErrno(fd); + CORELOGRELSYS((CORELOG_NAME "ProcReadFileInto: failed to open %s. rc=%Rrc\n", szPath, rc)); + } + return rc; +} + + +/** + * Read process information (format psinfo_t) from /proc. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int ProcReadInfo(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + return ProcReadFileInto(pSolCore, "psinfo", &pSolProc->pvProcInfo, &pSolProc->cbProcInfo); +} + + +/** + * Read process status (format pstatus_t) from /proc. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int ProcReadStatus(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + + char szPath[PATH_MAX]; + int rc = VINF_SUCCESS; + + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/status", (int)pSolProc->Process); + int fd = open(szPath, O_RDONLY); + if (fd >= 0) + { + size_t cbProcStatus = sizeof(pstatus_t); + AssertCompile(sizeof(pstatus_t) == sizeof(pSolProc->ProcStatus)); + rc = ReadFileNoIntr(fd, &pSolProc->ProcStatus, cbProcStatus); + close(fd); + } + else + { + rc = RTErrConvertFromErrno(fd); + CORELOGRELSYS((CORELOG_NAME "ProcReadStatus: failed to open %s. rc=%Rrc\n", szPath, rc)); + } + return rc; +} + + +/** + * Read process credential information (format prcred_t + array of guid_t) + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadCred(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + return ProcReadFileInto(pSolCore, "cred", &pSolProc->pvCred, &pSolProc->cbCred); +} + + +/** + * Read process privilege information (format prpriv_t + array of priv_chunk_t) + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadPriv(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + int rc = ProcReadFileInto(pSolCore, "priv", (void **)&pSolProc->pPriv, &pSolProc->cbPriv); + if (RT_FAILURE(rc)) + return rc; + pSolProc->pcPrivImpl = getprivimplinfo(); + if (!pSolProc->pcPrivImpl) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadPriv: getprivimplinfo returned NULL.\n")); + return VERR_INVALID_STATE; + } + return rc; +} + + +/** + * Read process LDT information (format array of struct ssd) from /proc. + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadLdt(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + return ProcReadFileInto(pSolCore, "ldt", &pSolProc->pvLdt, &pSolProc->cbLdt); +} + + +/** + * Read process auxiliary vectors (format auxv_t) for the process. + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadAuxVecs(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + char szPath[PATH_MAX]; + int rc = VINF_SUCCESS; + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/auxv", (int)pSolProc->Process); + int fd = open(szPath, O_RDONLY); + if (fd < 0) + { + rc = RTErrConvertFromErrno(fd); + CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: failed to open %s rc=%Rrc\n", szPath, rc)); + return rc; + } + + size_t cbAuxFile = GetFileSizeByFd(fd); + if (cbAuxFile >= sizeof(auxv_t)) + { + pSolProc->pAuxVecs = (auxv_t*)GetMemoryChunk(pSolCore, cbAuxFile + sizeof(auxv_t)); + if (pSolProc->pAuxVecs) + { + rc = ReadFileNoIntr(fd, pSolProc->pAuxVecs, cbAuxFile); + if (RT_SUCCESS(rc)) + { + /* Terminate list of vectors */ + pSolProc->cAuxVecs = cbAuxFile / sizeof(auxv_t); + CORELOG((CORELOG_NAME "ProcReadAuxVecs: cbAuxFile=%u auxv_t size %d cAuxVecs=%u\n", cbAuxFile, sizeof(auxv_t), + pSolProc->cAuxVecs)); + if (pSolProc->cAuxVecs > 0) + { + pSolProc->pAuxVecs[pSolProc->cAuxVecs].a_type = AT_NULL; + pSolProc->pAuxVecs[pSolProc->cAuxVecs].a_un.a_val = 0L; + close(fd); + return VINF_SUCCESS; + } + + CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: Invalid vector count %u\n", pSolProc->cAuxVecs)); + rc = VERR_READ_ERROR; + } + else + CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: ReadFileNoIntr failed. rc=%Rrc cbAuxFile=%u\n", rc, cbAuxFile)); + + pSolProc->pAuxVecs = NULL; + pSolProc->cAuxVecs = 0; + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: no memory for %u bytes\n", cbAuxFile + sizeof(auxv_t))); + rc = VERR_NO_MEMORY; + } + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadAuxVecs: aux file too small %u, expecting %u or more\n", cbAuxFile, sizeof(auxv_t))); + rc = VERR_READ_ERROR; + } + + close(fd); + return rc; +} + + +/* + * Find an element in the process' auxiliary vector. + */ +static long GetAuxVal(PRTSOLCOREPROCESS pSolProc, int Type) +{ + AssertReturn(pSolProc, -1); + if (pSolProc->pAuxVecs) + { + auxv_t *pAuxVec = pSolProc->pAuxVecs; + for (; pAuxVec->a_type != AT_NULL; pAuxVec++) + { + if (pAuxVec->a_type == Type) + return pAuxVec->a_un.a_val; + } + } + return -1; +} + + +/** + * Read the process mappings (format prmap_t array). + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadMappings(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + char szPath[PATH_MAX]; + int rc = VINF_SUCCESS; + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/map", (int)pSolProc->Process); + int fdMap = open(szPath, O_RDONLY); + if (fdMap < 0) + { + rc = RTErrConvertFromErrno(errno); + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: failed to open %s. rc=%Rrc\n", szPath, rc)); + return rc; + } + + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/as", (int)pSolProc->Process); + pSolProc->fdAs = open(szPath, O_RDONLY); + if (pSolProc->fdAs >= 0) + { + /* + * Allocate and read all the prmap_t objects from proc. + */ + size_t cbMapFile = GetFileSizeByFd(fdMap); + if (cbMapFile >= sizeof(prmap_t)) + { + prmap_t *pMap = (prmap_t*)GetMemoryChunk(pSolCore, cbMapFile); + if (pMap) + { + rc = ReadFileNoIntr(fdMap, pMap, cbMapFile); + if (RT_SUCCESS(rc)) + { + pSolProc->cMappings = cbMapFile / sizeof(prmap_t); + if (pSolProc->cMappings > 0) + { + /* + * Allocate for each prmap_t object, a corresponding RTSOLCOREMAPINFO object. + */ + pSolProc->pMapInfoHead = (PRTSOLCOREMAPINFO)GetMemoryChunk(pSolCore, + pSolProc->cMappings * sizeof(RTSOLCOREMAPINFO)); + if (pSolProc->pMapInfoHead) + { + /* + * Associate the prmap_t with the mapping info object. + */ + /*Assert(pSolProc->pMapInfoHead == NULL); - does not make sense */ + PRTSOLCOREMAPINFO pCur = pSolProc->pMapInfoHead; + PRTSOLCOREMAPINFO pPrev = NULL; + for (uint64_t i = 0; i < pSolProc->cMappings; i++, pMap++, pCur++) + { + memcpy(&pCur->pMap, pMap, sizeof(pCur->pMap)); + if (pPrev) + pPrev->pNext = pCur; + + pCur->fError = 0; + + /* + * Make sure we can read the mapping, otherwise mark them to be skipped. + */ + char achBuf[PAGE_SIZE]; + uint64_t k = 0; + while (k < pCur->pMap.pr_size) + { + size_t cb = RT_MIN(sizeof(achBuf), pCur->pMap.pr_size - k); + int rc2 = ProcReadAddrSpace(pSolProc, pCur->pMap.pr_vaddr + k, &achBuf, cb); + if (RT_FAILURE(rc2)) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: skipping mapping. vaddr=%#x rc=%Rrc\n", + pCur->pMap.pr_vaddr, rc2)); + + /* + * Instead of storing the actual mapping data which we failed to read, the core + * will contain an errno in place. So we adjust the prmap_t's size field too + * so the program header offsets match. + */ + pCur->pMap.pr_size = RT_ALIGN_Z(sizeof(int), 8); + pCur->fError = errno; + if (pCur->fError == 0) /* huh!? somehow errno got reset? fake one! EFAULT is nice. */ + pCur->fError = EFAULT; + break; + } + k += cb; + } + + pPrev = pCur; + } + if (pPrev) + pPrev->pNext = NULL; + + close(fdMap); + close(pSolProc->fdAs); + pSolProc->fdAs = -1; + CORELOG((CORELOG_NAME "ProcReadMappings: successfully read in %u mappings\n", pSolProc->cMappings)); + return VINF_SUCCESS; + } + + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: GetMemoryChunk failed %u\n", + pSolProc->cMappings * sizeof(RTSOLCOREMAPINFO))); + rc = VERR_NO_MEMORY; + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: Invalid mapping count %u\n", pSolProc->cMappings)); + rc = VERR_READ_ERROR; + } + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: FileReadNoIntr failed. rc=%Rrc cbMapFile=%u\n", rc, + cbMapFile)); + } + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: GetMemoryChunk failed. cbMapFile=%u\n", cbMapFile)); + rc = VERR_NO_MEMORY; + } + } + + close(pSolProc->fdAs); + pSolProc->fdAs = -1; + } + else + CORELOGRELSYS((CORELOG_NAME "ProcReadMappings: failed to open %s. rc=%Rrc\n", szPath, rc)); + + close(fdMap); + return rc; +} + + +/** + * Reads the thread information for all threads in the process. + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * + * @remarks Should not be called before successful call to @see AllocMemoryArea() + */ +static int ProcReadThreads(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + AssertReturn(pSolProc->pCurThreadCtx, VERR_NO_DATA); + + /* + * Read the information for threads. + * Format: prheader_t + array of lwpsinfo_t's. + */ + size_t cbInfoHdrAndData; + void *pvInfoHdr = NULL; + int rc = ProcReadFileInto(pSolCore, "lpsinfo", &pvInfoHdr, &cbInfoHdrAndData); + if (RT_SUCCESS(rc)) + { + /* + * Read the status of threads. + * Format: prheader_t + array of lwpstatus_t's. + */ + void *pvStatusHdr = NULL; + size_t cbStatusHdrAndData; + rc = ProcReadFileInto(pSolCore, "lstatus", &pvStatusHdr, &cbStatusHdrAndData); + if (RT_SUCCESS(rc)) + { + prheader_t *pInfoHdr = (prheader_t *)pvInfoHdr; + prheader_t *pStatusHdr = (prheader_t *)pvStatusHdr; + lwpstatus_t *pStatus = (lwpstatus_t *)((uintptr_t)pStatusHdr + sizeof(prheader_t)); + lwpsinfo_t *pInfo = (lwpsinfo_t *)((uintptr_t)pInfoHdr + sizeof(prheader_t)); + uint64_t cStatus = pStatusHdr->pr_nent; + uint64_t cInfo = pInfoHdr->pr_nent; + + CORELOG((CORELOG_NAME "ProcReadThreads: read info(%u) status(%u), threads:cInfo=%u cStatus=%u\n", cbInfoHdrAndData, + cbStatusHdrAndData, cInfo, cStatus)); + + /* + * Minor sanity size check (remember sizeof lwpstatus_t & lwpsinfo_t is <= size in file per entry). + */ + if ( (cbStatusHdrAndData - sizeof(prheader_t)) % pStatusHdr->pr_entsize == 0 + && (cbInfoHdrAndData - sizeof(prheader_t)) % pInfoHdr->pr_entsize == 0) + { + /* + * Make sure we have a matching lstatus entry for an lpsinfo entry unless + * it is a zombie thread, in which case we will not have a matching lstatus entry. + */ + for (; cInfo != 0; cInfo--) + { + if (pInfo->pr_sname != 'Z') /* zombie */ + { + if ( cStatus == 0 + || pStatus->pr_lwpid != pInfo->pr_lwpid) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: cStatus = %u pStatuslwpid=%d infolwpid=%d\n", cStatus, + pStatus->pr_lwpid, pInfo->pr_lwpid)); + rc = VERR_INVALID_STATE; + break; + } + pStatus = (lwpstatus_t *)((uintptr_t)pStatus + pStatusHdr->pr_entsize); + cStatus--; + } + pInfo = (lwpsinfo_t *)((uintptr_t)pInfo + pInfoHdr->pr_entsize); + } + + if (RT_SUCCESS(rc)) + { + /* + * There can still be more lwpsinfo_t's than lwpstatus_t's, build the + * lists accordingly. + */ + pStatus = (lwpstatus_t *)((uintptr_t)pStatusHdr + sizeof(prheader_t)); + pInfo = (lwpsinfo_t *)((uintptr_t)pInfoHdr + sizeof(prheader_t)); + cInfo = pInfoHdr->pr_nent; + cStatus = pInfoHdr->pr_nent; + + size_t cbThreadInfo = RT_MAX(cStatus, cInfo) * sizeof(RTSOLCORETHREADINFO); + pSolProc->pThreadInfoHead = (PRTSOLCORETHREADINFO)GetMemoryChunk(pSolCore, cbThreadInfo); + if (pSolProc->pThreadInfoHead) + { + PRTSOLCORETHREADINFO pCur = pSolProc->pThreadInfoHead; + PRTSOLCORETHREADINFO pPrev = NULL; + for (uint64_t i = 0; i < cInfo; i++, pCur++) + { + pCur->Info = *pInfo; + if ( pInfo->pr_sname != 'Z' + && pInfo->pr_lwpid == pStatus->pr_lwpid) + { + /* + * Adjust the context of the dumping thread to reflect the context + * when the core dump got initiated before whatever signal caused it. + */ + if ( pStatus /* noid droid */ + && pStatus->pr_lwpid == (id_t)pSolProc->hCurThread) + { + AssertCompile(sizeof(pStatus->pr_reg) == sizeof(pSolProc->pCurThreadCtx->uc_mcontext.gregs)); + AssertCompile(sizeof(pStatus->pr_fpreg) == sizeof(pSolProc->pCurThreadCtx->uc_mcontext.fpregs)); + memcpy(&pStatus->pr_reg, &pSolProc->pCurThreadCtx->uc_mcontext.gregs, sizeof(pStatus->pr_reg)); + memcpy(&pStatus->pr_fpreg, &pSolProc->pCurThreadCtx->uc_mcontext.fpregs, sizeof(pStatus->pr_fpreg)); + + AssertCompile(sizeof(pStatus->pr_lwphold) == sizeof(pSolProc->pCurThreadCtx->uc_sigmask)); + memcpy(&pStatus->pr_lwphold, &pSolProc->pCurThreadCtx->uc_sigmask, sizeof(pStatus->pr_lwphold)); + pStatus->pr_ustack = (uintptr_t)&pSolProc->pCurThreadCtx->uc_stack; + + CORELOG((CORELOG_NAME "ProcReadThreads: patched dumper thread with pre-dump time context.\n")); + } + + pCur->pStatus = pStatus; + pStatus = (lwpstatus_t *)((uintptr_t)pStatus + pStatusHdr->pr_entsize); + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: missing status for lwp %d\n", pInfo->pr_lwpid)); + pCur->pStatus = NULL; + } + + if (pPrev) + pPrev->pNext = pCur; + pPrev = pCur; + pInfo = (lwpsinfo_t *)((uintptr_t)pInfo + pInfoHdr->pr_entsize); + } + if (pPrev) + pPrev->pNext = NULL; + + CORELOG((CORELOG_NAME "ProcReadThreads: successfully read %u threads.\n", cInfo)); + pSolProc->cThreads = cInfo; + return VINF_SUCCESS; + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: GetMemoryChunk failed for %u bytes\n", cbThreadInfo)); + rc = VERR_NO_MEMORY; + } + } + else + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: Invalid state information for threads. rc=%Rrc\n", rc)); + } + else + { + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: huh!? cbStatusHdrAndData=%u prheader_t=%u entsize=%u\n", + cbStatusHdrAndData, sizeof(prheader_t), pStatusHdr->pr_entsize)); + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: huh!? cbInfoHdrAndData=%u entsize=%u\n", cbInfoHdrAndData, + pStatusHdr->pr_entsize)); + rc = VERR_INVALID_STATE; + } + } + else + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: ReadFileNoIntr failed for \"lpsinfo\" rc=%Rrc\n", rc)); + } + else + CORELOGRELSYS((CORELOG_NAME "ProcReadThreads: ReadFileNoIntr failed for \"lstatus\" rc=%Rrc\n", rc)); + return rc; +} + + +/** + * Reads miscellaneous information that is collected as part of a core file. + * This may include platform name, zone name and other OS-specific information. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int ProcReadMiscInfo(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + +#ifdef RT_OS_SOLARIS + /* + * Read the platform name, uname string and zone name. + */ + int rc = sysinfo(SI_PLATFORM, pSolProc->szPlatform, sizeof(pSolProc->szPlatform)); + if (rc == -1) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: sysinfo failed. rc=%d errno=%d\n", rc, errno)); + return VERR_GENERAL_FAILURE; + } + pSolProc->szPlatform[sizeof(pSolProc->szPlatform) - 1] = '\0'; + + rc = uname(&pSolProc->UtsName); + if (rc == -1) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: uname failed. rc=%d errno=%d\n", rc, errno)); + return VERR_GENERAL_FAILURE; + } + + /* + * See comment in GetOldProcessInfo() for why we need to verify the offset here. + * It's not perfect, but it should be fine unless they really mess up the structure + * layout in the future. See @bugref{8479}. + */ + size_t const offZoneId = RT_UOFFSETOF(psinfo_t, pr_zoneid); + if (pSolProc->cbProcInfo < offZoneId) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: psinfo size mismatch. cbProcInfo=%u expected >= %u\n", + pSolProc->cbProcInfo, offZoneId)); + return VERR_GENERAL_FAILURE; + } + + psinfo_t *pProcInfo = (psinfo_t *)pSolProc->pvProcInfo; + rc = getzonenamebyid(pProcInfo->pr_zoneid, pSolProc->szZoneName, sizeof(pSolProc->szZoneName)); + if (rc < 0) + { + CORELOGRELSYS((CORELOG_NAME "ProcReadMiscInfo: getzonenamebyid failed. rc=%d errno=%d zoneid=%d\n", rc, errno, + pProcInfo->pr_zoneid)); + return VERR_GENERAL_FAILURE; + } + pSolProc->szZoneName[sizeof(pSolProc->szZoneName) - 1] = '\0'; + rc = VINF_SUCCESS; + +#else +# error Port Me! +#endif + return rc; +} + + +/** + * On Solaris use the old-style procfs interfaces but the core file still should have this + * info. for backward and GDB compatibility, hence the need for this ugly function. + * + * @returns IPRT status code. + * + * @param pSolCore Pointer to the core object. + * @param pInfo Pointer to the old prpsinfo_t structure to update. + */ +static int GetOldProcessInfo(PRTSOLCORE pSolCore, prpsinfo_t *pInfo) +{ + AssertReturn(pSolCore, VERR_INVALID_PARAMETER); + AssertReturn(pInfo, VERR_INVALID_PARAMETER); + + /* + * The psinfo_t and the size of /proc/<pid>/psinfo varies both within the same Solaris system + * and across Solaris major versions. However, manual dumping of the structure and offsets shows + * that they changed the size of lwpsinfo_t and the size of the lwpsinfo_t::pr_filler. + * + * The proc psinfo file will be what gets dumped ultimately in the core file but we still need + * to read the fields to translate to the older process info structure here. + * + * See @bugref{8479}. + */ + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + + size_t offLwp = RT_UOFFSETOF(psinfo_t, pr_lwp); + /* last member we care about in lwpsinfo_t is pr_bindpset which is also present on ancient Solaris version we use for + building the additions. Should be safe enough as we don't need/access members upto or beyond that point anyway. */ + size_t offLastOnProc = RT_UOFFSETOF(lwpsinfo_t, pr_bindpset); + if (pSolProc->cbProcInfo >= offLwp + offLastOnProc) + { + psinfo_t *pSrc = (psinfo_t *)pSolProc->pvProcInfo; + memset(pInfo, 0, sizeof(prpsinfo_t)); + pInfo->pr_state = pSrc->pr_lwp.pr_state; + pInfo->pr_zomb = (pInfo->pr_state == SZOMB); + RTStrCopy(pInfo->pr_clname, sizeof(pInfo->pr_clname), pSrc->pr_lwp.pr_clname); + RTStrCopy(pInfo->pr_fname, sizeof(pInfo->pr_fname), pSrc->pr_fname); + memcpy(&pInfo->pr_psargs, &pSrc->pr_psargs, sizeof(pInfo->pr_psargs)); + pInfo->pr_nice = pSrc->pr_lwp.pr_nice; + pInfo->pr_flag = pSrc->pr_lwp.pr_flag; + pInfo->pr_uid = pSrc->pr_uid; + pInfo->pr_gid = pSrc->pr_gid; + pInfo->pr_pid = pSrc->pr_pid; + pInfo->pr_ppid = pSrc->pr_ppid; + pInfo->pr_pgrp = pSrc->pr_pgid; + pInfo->pr_sid = pSrc->pr_sid; + pInfo->pr_addr = (caddr_t)pSrc->pr_addr; + pInfo->pr_size = pSrc->pr_size; + pInfo->pr_rssize = pSrc->pr_rssize; + pInfo->pr_wchan = (caddr_t)pSrc->pr_lwp.pr_wchan; + pInfo->pr_start = pSrc->pr_start; + pInfo->pr_time = pSrc->pr_time; + pInfo->pr_pri = pSrc->pr_lwp.pr_pri; + pInfo->pr_oldpri = pSrc->pr_lwp.pr_oldpri; + pInfo->pr_cpu = pSrc->pr_lwp.pr_cpu; + pInfo->pr_ottydev = cmpdev(pSrc->pr_ttydev); + pInfo->pr_lttydev = pSrc->pr_ttydev; + pInfo->pr_syscall = pSrc->pr_lwp.pr_syscall; + pInfo->pr_ctime = pSrc->pr_ctime; + pInfo->pr_bysize = pSrc->pr_size * PAGESIZE; + pInfo->pr_byrssize = pSrc->pr_rssize * PAGESIZE; + pInfo->pr_argc = pSrc->pr_argc; + pInfo->pr_argv = (char **)pSrc->pr_argv; + pInfo->pr_envp = (char **)pSrc->pr_envp; + pInfo->pr_wstat = pSrc->pr_wstat; + pInfo->pr_pctcpu = pSrc->pr_pctcpu; + pInfo->pr_pctmem = pSrc->pr_pctmem; + pInfo->pr_euid = pSrc->pr_euid; + pInfo->pr_egid = pSrc->pr_egid; + pInfo->pr_aslwpid = 0; + pInfo->pr_dmodel = pSrc->pr_dmodel; + + return VINF_SUCCESS; + } + + CORELOGRELSYS((CORELOG_NAME "GetOldProcessInfo: Size/offset mismatch. offLwp=%u offLastOnProc=%u cbProcInfo=%u\n", + offLwp, offLastOnProc, pSolProc->cbProcInfo)); + return VERR_MISMATCH; +} + + +/** + * On Solaris use the old-style procfs interfaces but the core file still should have this + * info. for backward and GDB compatibility, hence the need for this ugly function. + * + * @param pSolCore Pointer to the core object. + * @param pInfo Pointer to the thread info. + * @param pStatus Pointer to the thread status. + * @param pDst Pointer to the old-style status structure to update. + * + */ +static void GetOldProcessStatus(PRTSOLCORE pSolCore, lwpsinfo_t *pInfo, lwpstatus_t *pStatus, prstatus_t *pDst) +{ + AssertReturnVoid(pSolCore); + AssertReturnVoid(pInfo); + AssertReturnVoid(pStatus); + AssertReturnVoid(pDst); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + memset(pDst, 0, sizeof(prstatus_t)); + if (pStatus->pr_flags & PR_STOPPED) + pDst->pr_flags = 0x0001; + if (pStatus->pr_flags & PR_ISTOP) + pDst->pr_flags = 0x0002; + if (pStatus->pr_flags & PR_DSTOP) + pDst->pr_flags = 0x0004; + if (pStatus->pr_flags & PR_ASLEEP) + pDst->pr_flags = 0x0008; + if (pStatus->pr_flags & PR_FORK) + pDst->pr_flags = 0x0010; + if (pStatus->pr_flags & PR_RLC) + pDst->pr_flags = 0x0020; + /* PR_PTRACE is never set */ + if (pStatus->pr_flags & PR_PCINVAL) + pDst->pr_flags = 0x0080; + if (pStatus->pr_flags & PR_ISSYS) + pDst->pr_flags = 0x0100; + if (pStatus->pr_flags & PR_STEP) + pDst->pr_flags = 0x0200; + if (pStatus->pr_flags & PR_KLC) + pDst->pr_flags = 0x0400; + if (pStatus->pr_flags & PR_ASYNC) + pDst->pr_flags = 0x0800; + if (pStatus->pr_flags & PR_PTRACE) + pDst->pr_flags = 0x1000; + if (pStatus->pr_flags & PR_MSACCT) + pDst->pr_flags = 0x2000; + if (pStatus->pr_flags & PR_BPTADJ) + pDst->pr_flags = 0x4000; + if (pStatus->pr_flags & PR_ASLWP) + pDst->pr_flags = 0x8000; + + pDst->pr_who = pStatus->pr_lwpid; + pDst->pr_why = pStatus->pr_why; + pDst->pr_what = pStatus->pr_what; + pDst->pr_info = pStatus->pr_info; + pDst->pr_cursig = pStatus->pr_cursig; + pDst->pr_sighold = pStatus->pr_lwphold; + pDst->pr_altstack = pStatus->pr_altstack; + pDst->pr_action = pStatus->pr_action; + pDst->pr_syscall = pStatus->pr_syscall; + pDst->pr_nsysarg = pStatus->pr_nsysarg; + pDst->pr_lwppend = pStatus->pr_lwppend; + pDst->pr_oldcontext = (ucontext_t *)pStatus->pr_oldcontext; + memcpy(pDst->pr_reg, pStatus->pr_reg, sizeof(pDst->pr_reg)); + memcpy(pDst->pr_sysarg, pStatus->pr_sysarg, sizeof(pDst->pr_sysarg)); + RTStrCopy(pDst->pr_clname, sizeof(pDst->pr_clname), pStatus->pr_clname); + + pDst->pr_nlwp = pSolProc->ProcStatus.pr_nlwp; + pDst->pr_sigpend = pSolProc->ProcStatus.pr_sigpend; + pDst->pr_pid = pSolProc->ProcStatus.pr_pid; + pDst->pr_ppid = pSolProc->ProcStatus.pr_ppid; + pDst->pr_pgrp = pSolProc->ProcStatus.pr_pgid; + pDst->pr_sid = pSolProc->ProcStatus.pr_sid; + pDst->pr_utime = pSolProc->ProcStatus.pr_utime; + pDst->pr_stime = pSolProc->ProcStatus.pr_stime; + pDst->pr_cutime = pSolProc->ProcStatus.pr_cutime; + pDst->pr_cstime = pSolProc->ProcStatus.pr_cstime; + pDst->pr_brkbase = (caddr_t)pSolProc->ProcStatus.pr_brkbase; + pDst->pr_brksize = pSolProc->ProcStatus.pr_brksize; + pDst->pr_stkbase = (caddr_t)pSolProc->ProcStatus.pr_stkbase; + pDst->pr_stksize = pSolProc->ProcStatus.pr_stksize; + + pDst->pr_processor = (short)pInfo->pr_onpro; + pDst->pr_bind = (short)pInfo->pr_bindpro; + pDst->pr_instr = pStatus->pr_instr; +} + + +/** + * Callback for rtCoreDumperForEachThread to suspend a thread. + * + * @param pSolCore Pointer to the core object. + * @param pvThreadInfo Opaque pointer to thread information. + * + * @return IPRT status code. + */ +static int suspendThread(PRTSOLCORE pSolCore, void *pvThreadInfo) +{ + AssertPtrReturn(pvThreadInfo, VERR_INVALID_POINTER); + NOREF(pSolCore); + + lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)pvThreadInfo; + CORELOG((CORELOG_NAME ":suspendThread %d\n", (lwpid_t)pThreadInfo->pr_lwpid)); + if ((lwpid_t)pThreadInfo->pr_lwpid != pSolCore->SolProc.hCurThread) + _lwp_suspend(pThreadInfo->pr_lwpid); + return VINF_SUCCESS; +} + + +/** + * Callback for rtCoreDumperForEachThread to resume a thread. + * + * @param pSolCore Pointer to the core object. + * @param pvThreadInfo Opaque pointer to thread information. + * + * @return IPRT status code. + */ +static int resumeThread(PRTSOLCORE pSolCore, void *pvThreadInfo) +{ + AssertPtrReturn(pvThreadInfo, VERR_INVALID_POINTER); + NOREF(pSolCore); + + lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)pvThreadInfo; + CORELOG((CORELOG_NAME ":resumeThread %d\n", (lwpid_t)pThreadInfo->pr_lwpid)); + if ((lwpid_t)pThreadInfo->pr_lwpid != (lwpid_t)pSolCore->SolProc.hCurThread) + _lwp_continue(pThreadInfo->pr_lwpid); + return VINF_SUCCESS; +} + + +/** + * Calls a thread worker function for all threads in the process as described by /proc + * + * @param pSolCore Pointer to the core object. + * @param pcThreads Number of threads read. + * @param pfnWorker Callback function for each thread. + * + * @return IPRT status code. + */ +static int rtCoreDumperForEachThread(PRTSOLCORE pSolCore, uint64_t *pcThreads, PFNRTSOLCORETHREADWORKER pfnWorker) +{ + AssertPtrReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + + /* + * Read the information for threads. + * Format: prheader_t + array of lwpsinfo_t's. + */ + char szLpsInfoPath[PATH_MAX]; + RTStrPrintf(szLpsInfoPath, sizeof(szLpsInfoPath), "/proc/%d/lpsinfo", (int)pSolProc->Process); + + int rc = VINF_SUCCESS; + int fd = open(szLpsInfoPath, O_RDONLY); + if (fd >= 0) + { + size_t cbInfoHdrAndData = GetFileSizeByFd(fd); + void *pvInfoHdr = mmap(NULL, cbInfoHdrAndData, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, + -1 /* fd */, 0 /* offset */); + if (pvInfoHdr != MAP_FAILED) + { + rc = ReadFileNoIntr(fd, pvInfoHdr, cbInfoHdrAndData); + if (RT_SUCCESS(rc)) + { + prheader_t *pHeader = (prheader_t *)pvInfoHdr; + lwpsinfo_t *pThreadInfo = (lwpsinfo_t *)((uintptr_t)pvInfoHdr + sizeof(prheader_t)); + for (long i = 0; i < pHeader->pr_nent; i++) + { + pfnWorker(pSolCore, pThreadInfo); + pThreadInfo = (lwpsinfo_t *)((uintptr_t)pThreadInfo + pHeader->pr_entsize); + } + if (pcThreads) + *pcThreads = pHeader->pr_nent; + } + + munmap(pvInfoHdr, cbInfoHdrAndData); + } + else + rc = VERR_NO_MEMORY; + close(fd); + } + else + rc = RTErrConvertFromErrno(rc); + + return rc; +} + + +/** + * Resume all threads of this process. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code.. + */ +static int rtCoreDumperResumeThreads(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + uint64_t cThreads; + return rtCoreDumperForEachThread(pSolCore, &cThreads, resumeThread); +} + + +/** + * Stop all running threads of this process except the current one. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int rtCoreDumperSuspendThreads(PRTSOLCORE pSolCore) +{ + AssertPtrReturn(pSolCore, VERR_INVALID_POINTER); + + /* + * This function tries to ensures while we suspend threads, no newly spawned threads + * or a combination of spawning and terminating threads can cause any threads to be left running. + * The assumption here is that threads can only increase not decrease across iterations. + */ + uint16_t cTries = 0; + uint64_t aThreads[4]; + RT_ZERO(aThreads); + int rc = VERR_GENERAL_FAILURE; + void *pv = NULL; + size_t cb = 0; + for (cTries = 0; cTries < RT_ELEMENTS(aThreads); cTries++) + { + rc = rtCoreDumperForEachThread(pSolCore, &aThreads[cTries], suspendThread); + if (RT_FAILURE(rc)) + break; + } + if ( RT_SUCCESS(rc) + && aThreads[cTries - 1] != aThreads[cTries - 2]) + { + CORELOGRELSYS((CORELOG_NAME "rtCoreDumperSuspendThreads: possible thread bomb!?\n")); + rc = VERR_TIMEOUT; + } + return rc; +} + + +/** + * Returns size of an ELF NOTE header given the size of data the NOTE section will contain. + * + * @param cb Size of the data. + * + * @return Size of data actually used for NOTE header and section. + */ +static inline size_t ElfNoteHeaderSize(size_t cb) +{ + return sizeof(ELFNOTEHDR) + RT_ALIGN_Z(cb, 4); +} + + +/** + * Write an ELF NOTE header into the core file. + * + * @param pSolCore Pointer to the core object. + * @param Type Type of this NOTE section. + * @param pcv Opaque pointer to the data, if NULL only computes size. + * @param cb Size of the data. + * + * @return IPRT status code. + */ +static int ElfWriteNoteHeader(PRTSOLCORE pSolCore, uint_t Type, const void *pcv, size_t cb) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + AssertReturn(pcv, VERR_INVALID_POINTER); + AssertReturn(cb > 0, VERR_NO_DATA); + AssertReturn(pSolCore->pfnWriter, VERR_WRITE_ERROR); + AssertReturn(pSolCore->fdCoreFile >= 0, VERR_INVALID_HANDLE); + + int rc = VERR_GENERAL_FAILURE; +#ifdef RT_OS_SOLARIS + ELFNOTEHDR ElfNoteHdr; + RT_ZERO(ElfNoteHdr); + ElfNoteHdr.achName[0] = 'C'; + ElfNoteHdr.achName[1] = 'O'; + ElfNoteHdr.achName[2] = 'R'; + ElfNoteHdr.achName[3] = 'E'; + + /* + * This is a known violation of the 64-bit ELF spec., see xTracker @bugref{5211} + * for the historic reasons as to the padding and 'namesz' anomalies. + */ + static const char s_achPad[3] = { 0, 0, 0 }; + size_t cbAlign = RT_ALIGN_Z(cb, 4); + ElfNoteHdr.Hdr.n_namesz = 5; + ElfNoteHdr.Hdr.n_type = Type; + ElfNoteHdr.Hdr.n_descsz = cbAlign; + + /* + * Write note header and description. + */ + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ElfNoteHdr, sizeof(ElfNoteHdr)); + if (RT_SUCCESS(rc)) + { + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, pcv, cb); + if (RT_SUCCESS(rc)) + { + if (cbAlign > cb) + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, s_achPad, cbAlign - cb); + } + } + + if (RT_FAILURE(rc)) + CORELOGRELSYS((CORELOG_NAME "ElfWriteNote: pfnWriter failed. Type=%d rc=%Rrc\n", Type, rc)); +#else +#error Port Me! +#endif + return rc; +} + + +/** + * Computes the size of NOTE section for the given core type. + * Solaris has two types of program header information (new and old). + * + * @param pSolCore Pointer to the core object. + * @param enmType Type of core file information required. + * + * @return Size of NOTE section. + */ +static size_t ElfNoteSectionSize(PRTSOLCORE pSolCore, RTSOLCORETYPE enmType) +{ + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + size_t cb = 0; + switch (enmType) + { + case enmOldEra: + { + cb += ElfNoteHeaderSize(sizeof(prpsinfo_t)); + cb += ElfNoteHeaderSize(pSolProc->cAuxVecs * sizeof(auxv_t)); + cb += ElfNoteHeaderSize(strlen(pSolProc->szPlatform)); + + PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead; + while (pThreadInfo) + { + if (pThreadInfo->pStatus) + { + cb += ElfNoteHeaderSize(sizeof(prstatus_t)); + cb += ElfNoteHeaderSize(sizeof(prfpregset_t)); + } + pThreadInfo = pThreadInfo->pNext; + } + + break; + } + + case enmNewEra: + { + cb += ElfNoteHeaderSize(sizeof(psinfo_t)); + cb += ElfNoteHeaderSize(sizeof(pstatus_t)); + cb += ElfNoteHeaderSize(pSolProc->cAuxVecs * sizeof(auxv_t)); + cb += ElfNoteHeaderSize(strlen(pSolProc->szPlatform) + 1); + cb += ElfNoteHeaderSize(sizeof(struct utsname)); + cb += ElfNoteHeaderSize(sizeof(core_content_t)); + cb += ElfNoteHeaderSize(pSolProc->cbCred); + + if (pSolProc->pPriv) + cb += ElfNoteHeaderSize(PRIV_PRPRIV_SIZE(pSolProc->pPriv)); /* Ought to be same as cbPriv!? */ + + if (pSolProc->pcPrivImpl) + cb += ElfNoteHeaderSize(PRIV_IMPL_INFO_SIZE(pSolProc->pcPrivImpl)); + + cb += ElfNoteHeaderSize(strlen(pSolProc->szZoneName) + 1); + if (pSolProc->cbLdt > 0) + cb += ElfNoteHeaderSize(pSolProc->cbLdt); + + PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead; + while (pThreadInfo) + { + cb += ElfNoteHeaderSize(sizeof(lwpsinfo_t)); + if (pThreadInfo->pStatus) + cb += ElfNoteHeaderSize(sizeof(lwpstatus_t)); + + pThreadInfo = pThreadInfo->pNext; + } + + break; + } + + default: + { + CORELOGRELSYS((CORELOG_NAME "ElfNoteSectionSize: Unknown segment era %d\n", enmType)); + break; + } + } + + return cb; +} + + +/** + * Write the note section for the given era into the core file. + * Solaris has two types of program header information (new and old). + * + * @param pSolCore Pointer to the core object. + * @param enmType Type of core file information required. + * + * @return IPRT status code. + */ +static int ElfWriteNoteSection(PRTSOLCORE pSolCore, RTSOLCORETYPE enmType) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + int rc = VERR_GENERAL_FAILURE; + +#ifdef RT_OS_SOLARIS + typedef int (*PFNELFWRITENOTEHDR)(PRTSOLCORE pSolCore, uint_t, const void *pcv, size_t cb); + typedef struct ELFWRITENOTE + { + const char *pszType; + uint_t Type; + const void *pcv; + size_t cb; + } ELFWRITENOTE; + + switch (enmType) + { + case enmOldEra: + { + ELFWRITENOTE aElfNotes[] = + { + { "NT_PRPSINFO", NT_PRPSINFO, &pSolProc->ProcInfoOld, sizeof(prpsinfo_t) }, + { "NT_AUXV", NT_AUXV, pSolProc->pAuxVecs, pSolProc->cAuxVecs * sizeof(auxv_t) }, + { "NT_PLATFORM", NT_PLATFORM, pSolProc->szPlatform, strlen(pSolProc->szPlatform) + 1 } + }; + + for (unsigned i = 0; i < RT_ELEMENTS(aElfNotes); i++) + { + rc = ElfWriteNoteHeader(pSolCore, aElfNotes[i].Type, aElfNotes[i].pcv, aElfNotes[i].cb); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader failed for %s. rc=%Rrc\n", + aElfNotes[i].pszType, rc)); + break; + } + } + + /* + * Write old-style thread info., they contain nothing about zombies, + * so we just skip if there is no status information for them. + */ + PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead; + for (; pThreadInfo; pThreadInfo = pThreadInfo->pNext) + { + if (!pThreadInfo->pStatus) + continue; + + prstatus_t OldProcessStatus; + GetOldProcessStatus(pSolCore, &pThreadInfo->Info, pThreadInfo->pStatus, &OldProcessStatus); + rc = ElfWriteNoteHeader(pSolCore, NT_PRSTATUS, &OldProcessStatus, sizeof(prstatus_t)); + if (RT_SUCCESS(rc)) + { + rc = ElfWriteNoteHeader(pSolCore, NT_PRFPREG, &pThreadInfo->pStatus->pr_fpreg, sizeof(prfpregset_t)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteSegment: ElfWriteNote failed for NT_PRFPREF. rc=%Rrc\n", rc)); + break; + } + } + else + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteSegment: ElfWriteNote failed for NT_PRSTATUS. rc=%Rrc\n", rc)); + break; + } + } + break; + } + + case enmNewEra: + { + ELFWRITENOTE aElfNotes[] = + { + { "NT_PSINFO", NT_PSINFO, pSolProc->pvProcInfo, pSolProc->cbProcInfo }, + { "NT_PSTATUS", NT_PSTATUS, &pSolProc->ProcStatus, sizeof(pstatus_t) }, + { "NT_AUXV", NT_AUXV, pSolProc->pAuxVecs, pSolProc->cAuxVecs * sizeof(auxv_t) }, + { "NT_PLATFORM", NT_PLATFORM, pSolProc->szPlatform, strlen(pSolProc->szPlatform) + 1 }, + { "NT_UTSNAME", NT_UTSNAME, &pSolProc->UtsName, sizeof(struct utsname) }, + { "NT_CONTENT", NT_CONTENT, &pSolProc->CoreContent, sizeof(core_content_t) }, + { "NT_PRCRED", NT_PRCRED, pSolProc->pvCred, pSolProc->cbCred }, + { "NT_PRPRIV", NT_PRPRIV, pSolProc->pPriv, PRIV_PRPRIV_SIZE(pSolProc->pPriv) }, + { "NT_PRPRIVINFO", NT_PRPRIVINFO, pSolProc->pcPrivImpl, PRIV_IMPL_INFO_SIZE(pSolProc->pcPrivImpl) }, + { "NT_ZONENAME", NT_ZONENAME, pSolProc->szZoneName, strlen(pSolProc->szZoneName) + 1 } + }; + + for (unsigned i = 0; i < RT_ELEMENTS(aElfNotes); i++) + { + rc = ElfWriteNoteHeader(pSolCore, aElfNotes[i].Type, aElfNotes[i].pcv, aElfNotes[i].cb); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader failed for %s. rc=%Rrc\n", + aElfNotes[i].pszType, rc)); + break; + } + } + + /* + * Write new-style thread info., missing lwpstatus_t indicates it's a zombie thread + * we only dump the lwpsinfo_t in that case. + */ + PRTSOLCORETHREADINFO pThreadInfo = pSolProc->pThreadInfoHead; + for (; pThreadInfo; pThreadInfo = pThreadInfo->pNext) + { + rc = ElfWriteNoteHeader(pSolCore, NT_LWPSINFO, &pThreadInfo->Info, sizeof(lwpsinfo_t)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader for NT_LWPSINFO failed. rc=%Rrc\n", rc)); + break; + } + + if (pThreadInfo->pStatus) + { + rc = ElfWriteNoteHeader(pSolCore, NT_LWPSTATUS, pThreadInfo->pStatus, sizeof(lwpstatus_t)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: ElfWriteNoteHeader for NT_LWPSTATUS failed. rc=%Rrc\n", + rc)); + break; + } + } + } + break; + } + + default: + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteNoteSection: Invalid type %d\n", enmType)); + rc = VERR_GENERAL_FAILURE; + break; + } + } +#else +# error Port Me! +#endif + return rc; +} + + +/** + * Write mappings into the core file. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int ElfWriteMappings(PRTSOLCORE pSolCore) +{ + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + + int rc = VERR_GENERAL_FAILURE; + PRTSOLCOREMAPINFO pMapInfo = pSolProc->pMapInfoHead; + while (pMapInfo) + { + if (!pMapInfo->fError) + { + uint64_t k = 0; + char achBuf[PAGE_SIZE]; + while (k < pMapInfo->pMap.pr_size) + { + size_t cb = RT_MIN(sizeof(achBuf), pMapInfo->pMap.pr_size - k); + int rc2 = ProcReadAddrSpace(pSolProc, pMapInfo->pMap.pr_vaddr + k, &achBuf, cb); + if (RT_FAILURE(rc2)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: Failed to read mapping, can't recover. Bye. rc=%Rrc\n", rc)); + return VERR_INVALID_STATE; + } + + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, achBuf, sizeof(achBuf)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: pfnWriter failed. rc=%Rrc\n", rc)); + return rc; + } + k += cb; + } + } + else + { + char achBuf[RT_ALIGN_Z(sizeof(int), 8)]; + RT_ZERO(achBuf); + memcpy(achBuf, &pMapInfo->fError, sizeof(pMapInfo->fError)); + if (sizeof(achBuf) != pMapInfo->pMap.pr_size) + CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: Huh!? something is wrong!\n")); + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &achBuf, sizeof(achBuf)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteMappings: pfnWriter(2) failed. rc=%Rrc\n", rc)); + return rc; + } + } + + pMapInfo = pMapInfo->pNext; + } + + return VINF_SUCCESS; +} + + +/** + * Write program headers for all mappings into the core file. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int ElfWriteMappingHeaders(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + Elf_Phdr ProgHdr; + RT_ZERO(ProgHdr); + ProgHdr.p_type = PT_LOAD; + + int rc = VERR_GENERAL_FAILURE; + PRTSOLCOREMAPINFO pMapInfo = pSolProc->pMapInfoHead; + while (pMapInfo) + { + ProgHdr.p_vaddr = pMapInfo->pMap.pr_vaddr; /* Virtual address of this mapping in the process address space */ + ProgHdr.p_offset = pSolCore->offWrite; /* Where this mapping is located in the core file */ + ProgHdr.p_memsz = pMapInfo->pMap.pr_size; /* Size of the memory image of the mapping */ + ProgHdr.p_filesz = pMapInfo->pMap.pr_size; /* Size of the file image of the mapping */ + + ProgHdr.p_flags = 0; /* Reset fields in a loop when needed! */ + if (pMapInfo->pMap.pr_mflags & MA_READ) + ProgHdr.p_flags |= PF_R; + if (pMapInfo->pMap.pr_mflags & MA_WRITE) + ProgHdr.p_flags |= PF_W; + if (pMapInfo->pMap.pr_mflags & MA_EXEC) + ProgHdr.p_flags |= PF_X; + + if (pMapInfo->fError) + ProgHdr.p_flags |= PF_SUNW_FAILURE; + + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "ElfWriteMappingHeaders: pfnWriter failed. rc=%Rrc\n", rc)); + return rc; + } + + pSolCore->offWrite += ProgHdr.p_filesz; + pMapInfo = pMapInfo->pNext; + } + return rc; +} + +/** + * Inner worker for rtCoreDumperWriteCore, which purpose is to + * squash cleanup gotos. + */ +static int rtCoreDumperWriteCoreDoIt(PRTSOLCORE pSolCore, PFNRTCOREWRITER pfnWriter, + PRTSOLCOREPROCESS pSolProc) +{ + pSolCore->offWrite = 0; + uint32_t cProgHdrs = pSolProc->cMappings + 2; /* two PT_NOTE program headers (old, new style) */ + + /* + * Write the ELF header. + */ + Elf_Ehdr ElfHdr; + RT_ZERO(ElfHdr); + ElfHdr.e_ident[EI_MAG0] = ELFMAG0; + ElfHdr.e_ident[EI_MAG1] = ELFMAG1; + ElfHdr.e_ident[EI_MAG2] = ELFMAG2; + ElfHdr.e_ident[EI_MAG3] = ELFMAG3; + ElfHdr.e_ident[EI_DATA] = IsBigEndian() ? ELFDATA2MSB : ELFDATA2LSB; + ElfHdr.e_type = ET_CORE; + ElfHdr.e_version = EV_CURRENT; +#ifdef RT_ARCH_AMD64 + ElfHdr.e_machine = EM_AMD64; + ElfHdr.e_ident[EI_CLASS] = ELFCLASS64; +#else + ElfHdr.e_machine = EM_386; + ElfHdr.e_ident[EI_CLASS] = ELFCLASS32; +#endif + if (cProgHdrs >= PN_XNUM) + ElfHdr.e_phnum = PN_XNUM; + else + ElfHdr.e_phnum = cProgHdrs; + ElfHdr.e_ehsize = sizeof(ElfHdr); + ElfHdr.e_phoff = sizeof(ElfHdr); + ElfHdr.e_phentsize = sizeof(Elf_Phdr); + ElfHdr.e_shentsize = sizeof(Elf_Shdr); + int rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ElfHdr, sizeof(ElfHdr)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing ELF header. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Setup program header. + */ + Elf_Phdr ProgHdr; + RT_ZERO(ProgHdr); + ProgHdr.p_type = PT_NOTE; + ProgHdr.p_flags = PF_R; + + /* + * Write old-style NOTE program header. + */ + pSolCore->offWrite += sizeof(ElfHdr) + cProgHdrs * sizeof(ProgHdr); + ProgHdr.p_offset = pSolCore->offWrite; + ProgHdr.p_filesz = ElfNoteSectionSize(pSolCore, enmOldEra); + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing old-style ELF program Header. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Write new-style NOTE program header. + */ + pSolCore->offWrite += ProgHdr.p_filesz; + ProgHdr.p_offset = pSolCore->offWrite; + ProgHdr.p_filesz = ElfNoteSectionSize(pSolCore, enmNewEra); + rc = pSolCore->pfnWriter(pSolCore->fdCoreFile, &ProgHdr, sizeof(ProgHdr)); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: pfnWriter failed writing new-style ELF program header. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Write program headers per mapping. + */ + pSolCore->offWrite += ProgHdr.p_filesz; + rc = ElfWriteMappingHeaders(pSolCore); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "Write: ElfWriteMappings failed. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Write old-style note section. + */ + rc = ElfWriteNoteSection(pSolCore, enmOldEra); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteNoteSection old-style failed. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Write new-style section. + */ + rc = ElfWriteNoteSection(pSolCore, enmNewEra); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteNoteSection new-style failed. rc=%Rrc\n", rc)); + return rc; + } + + /* + * Write all mappings. + */ + rc = ElfWriteMappings(pSolCore); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "WriteCore: ElfWriteMappings failed. rc=%Rrc\n", rc)); + return rc; + } + + return rc; +} + + +/** + * Write a prepared core file using a user-passed in writer function, requires all threads + * to be in suspended state (i.e. called after CreateCore). + * + * @return IPRT status code. + * @param pSolCore Pointer to the core object. + * @param pfnWriter Pointer to the writer function to override default writer (NULL uses default). + * + * @remarks Resumes all suspended threads, unless it's an invalid core. This + * function must be called only -after- rtCoreDumperCreateCore(). + */ +static int rtCoreDumperWriteCore(PRTSOLCORE pSolCore, PFNRTCOREWRITER pfnWriter) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + + if (!pSolCore->fIsValid) + return VERR_INVALID_STATE; + + if (pfnWriter) + pSolCore->pfnWriter = pfnWriter; + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + char szPath[PATH_MAX]; + int rc; + + /* + * Open the process address space file. + */ + RTStrPrintf(szPath, sizeof(szPath), "/proc/%d/as", (int)pSolProc->Process); + int fd = open(szPath, O_RDONLY); + if (fd >= 0) + { + pSolProc->fdAs = fd; + + /* + * Create the core file. + */ + fd = open(pSolCore->szCorePath, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR); + if (fd >= 0) + { + pSolCore->fdCoreFile = fd; + + /* + * Do the actual writing. + */ + rc = rtCoreDumperWriteCoreDoIt(pSolCore, pfnWriter, pSolProc); + + close(pSolCore->fdCoreFile); + pSolCore->fdCoreFile = -1; + } + else + { + rc = RTErrConvertFromErrno(fd); + CORELOGRELSYS((CORELOG_NAME "WriteCore: failed to open %s. rc=%Rrc\n", pSolCore->szCorePath, rc)); + } + close(pSolProc->fdAs); + pSolProc->fdAs = -1; + } + else + { + rc = RTErrConvertFromErrno(fd); + CORELOGRELSYS((CORELOG_NAME "WriteCore: Failed to open address space, %s. rc=%Rrc\n", szPath, rc)); + } + + rtCoreDumperResumeThreads(pSolCore); + return rc; +} + + +/** + * Takes a process snapshot into a passed-in core object. It has the side-effect of halting + * all threads which can lead to things like spurious wakeups of threads (if and when threads + * are ultimately resumed en-masse) already suspended while calling this function. + * + * @return IPRT status code. + * @param pSolCore Pointer to a core object. + * @param pContext Pointer to the caller context thread. + * @param pszCoreFilePath Path to the core file. If NULL is passed, the global + * path specified in RTCoreDumperSetup() would be used. + * + * @remarks Halts all threads. + */ +static int rtCoreDumperCreateCore(PRTSOLCORE pSolCore, ucontext_t *pContext, const char *pszCoreFilePath) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + AssertReturn(pContext, VERR_INVALID_POINTER); + + /* + * Initialize core structures. + */ + memset(pSolCore, 0, sizeof(RTSOLCORE)); + pSolCore->pfnReader = &ReadFileNoIntr; + pSolCore->pfnWriter = &WriteFileNoIntr; + pSolCore->fIsValid = false; + pSolCore->fdCoreFile = -1; + + PRTSOLCOREPROCESS pSolProc = &pSolCore->SolProc; + pSolProc->Process = RTProcSelf(); + pSolProc->hCurThread = _lwp_self(); /* thr_self() */ + pSolProc->fdAs = -1; + pSolProc->pCurThreadCtx = pContext; + pSolProc->CoreContent = CC_CONTENT_DEFAULT; + + RTProcGetExecutablePath(pSolProc->szExecPath, sizeof(pSolProc->szExecPath)); /* this gets full path not just name */ + pSolProc->pszExecName = RTPathFilename(pSolProc->szExecPath); + + /* + * If a path has been specified, use it. Otherwise use the global path. + */ + if (!pszCoreFilePath) + { + /* + * If no output directory is specified, use current directory. + */ + if (g_szCoreDumpDir[0] == '\0') + g_szCoreDumpDir[0] = '.'; + + if (g_szCoreDumpFile[0] == '\0') + { + /* We cannot call RTPathAbs*() as they call getcwd() which calls malloc. */ + RTStrPrintf(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), "%s/core.vb.%s.%d", + g_szCoreDumpDir, pSolProc->pszExecName, (int)pSolProc->Process); + } + else + RTStrPrintf(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), "%s/core.vb.%s", g_szCoreDumpDir, g_szCoreDumpFile); + } + else + RTStrCopy(pSolCore->szCorePath, sizeof(pSolCore->szCorePath), pszCoreFilePath); + + CORELOG((CORELOG_NAME "CreateCore: Taking Core %s from Thread %d\n", pSolCore->szCorePath, (int)pSolProc->hCurThread)); + + /* + * Quiesce the process. + */ + int rc = rtCoreDumperSuspendThreads(pSolCore); + if (RT_SUCCESS(rc)) + { + rc = AllocMemoryArea(pSolCore); + if (RT_SUCCESS(rc)) + { + rc = ProcReadInfo(pSolCore); + if (RT_SUCCESS(rc)) + { + rc = GetOldProcessInfo(pSolCore, &pSolProc->ProcInfoOld); + if (RT_SUCCESS(rc)) + { + if (IsProcessArchNative(pSolProc)) + { + /* + * Read process status, information such as number of active LWPs will be + * invalid since we just quiesced the process. + */ + rc = ProcReadStatus(pSolCore); + if (RT_SUCCESS(rc)) + { + struct COREACCUMULATOR + { + const char *pszName; + PFNRTSOLCOREACCUMULATOR pfnAcc; + bool fOptional; + } aAccumulators[] = + { + { "ProcReadLdt", &ProcReadLdt, false }, + { "ProcReadCred", &ProcReadCred, false }, + { "ProcReadPriv", &ProcReadPriv, false }, + { "ProcReadAuxVecs", &ProcReadAuxVecs, false }, + { "ProcReadMappings", &ProcReadMappings, false }, + { "ProcReadThreads", &ProcReadThreads, false }, + { "ProcReadMiscInfo", &ProcReadMiscInfo, false } + }; + + for (unsigned i = 0; i < RT_ELEMENTS(aAccumulators); i++) + { + rc = aAccumulators[i].pfnAcc(pSolCore); + if (RT_FAILURE(rc)) + { + CORELOGRELSYS((CORELOG_NAME "CreateCore: %s failed. rc=%Rrc\n", aAccumulators[i].pszName, rc)); + if (!aAccumulators[i].fOptional) + break; + } + } + + if (RT_SUCCESS(rc)) + { + pSolCore->fIsValid = true; + return VINF_SUCCESS; + } + + FreeMemoryArea(pSolCore); + } + else + CORELOGRELSYS((CORELOG_NAME "CreateCore: ProcReadStatus failed. rc=%Rrc\n", rc)); + } + else + { + CORELOGRELSYS((CORELOG_NAME "CreateCore: IsProcessArchNative failed.\n")); + rc = VERR_BAD_EXE_FORMAT; + } + } + else + CORELOGRELSYS((CORELOG_NAME "CreateCore: GetOldProcessInfo failed. rc=%Rrc\n", rc)); + } + else + CORELOGRELSYS((CORELOG_NAME "CreateCore: ProcReadInfo failed. rc=%Rrc\n", rc)); + } + else + CORELOGRELSYS((CORELOG_NAME "CreateCore: AllocMemoryArea failed. rc=%Rrc\n", rc)); + + /* + * Resume threads on failure. + */ + rtCoreDumperResumeThreads(pSolCore); + } + else + CORELOG((CORELOG_NAME "CreateCore: SuspendAllThreads failed. Thread bomb!?! rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * Destroy an existing core object. + * + * @param pSolCore Pointer to the core object. + * + * @return IPRT status code. + */ +static int rtCoreDumperDestroyCore(PRTSOLCORE pSolCore) +{ + AssertReturn(pSolCore, VERR_INVALID_POINTER); + if (!pSolCore->fIsValid) + return VERR_INVALID_STATE; + + FreeMemoryArea(pSolCore); + pSolCore->fIsValid = false; + return VINF_SUCCESS; +} + + +/** + * Takes a core dump. + * + * @param pContext The context of the caller. + * @param pszOutputFile Path of the core file. If NULL is passed, the + * global path passed in RTCoreDumperSetup will + * be used. + * @returns IPRT status code. + */ +static int rtCoreDumperTakeDump(ucontext_t *pContext, const char *pszOutputFile) +{ + if (!pContext) + { + CORELOGRELSYS((CORELOG_NAME "TakeDump: Missing context.\n")); + return VERR_INVALID_POINTER; + } + + /* + * Take a snapshot, then dump core to disk, all threads except this one are halted + * from before taking the snapshot until writing the core is completely finished. + * Any errors would resume all threads if they were halted. + */ + RTSOLCORE SolCore; + RT_ZERO(SolCore); + int rc = rtCoreDumperCreateCore(&SolCore, pContext, pszOutputFile); + if (RT_SUCCESS(rc)) + { + rc = rtCoreDumperWriteCore(&SolCore, &WriteFileNoIntr); + if (RT_SUCCESS(rc)) + CORELOGRELSYS((CORELOG_NAME "Core dumped in %s\n", SolCore.szCorePath)); + else + CORELOGRELSYS((CORELOG_NAME "TakeDump: WriteCore failed. szCorePath=%s rc=%Rrc\n", SolCore.szCorePath, rc)); + + rtCoreDumperDestroyCore(&SolCore); + } + else + CORELOGRELSYS((CORELOG_NAME "TakeDump: CreateCore failed. rc=%Rrc\n", rc)); + + return rc; +} + + +/** + * The signal handler that will be invoked to take core dumps. + * + * @param Sig The signal that invoked us. + * @param pSigInfo The signal information. + * @param pvArg Opaque pointer to the caller context structure, + * this cannot be NULL. + */ +static void rtCoreDumperSignalHandler(int Sig, siginfo_t *pSigInfo, void *pvArg) +{ + CORELOG((CORELOG_NAME "SignalHandler Sig=%d pvArg=%p\n", Sig, pvArg)); + + RTNATIVETHREAD hCurNativeThread = RTThreadNativeSelf(); + int rc = VERR_GENERAL_FAILURE; + bool fCallSystemDump = false; + bool fRc; + ASMAtomicCmpXchgHandle(&g_CoreDumpThread, hCurNativeThread, NIL_RTNATIVETHREAD, fRc); + if (fRc) + { + rc = rtCoreDumperTakeDump((ucontext_t *)pvArg, NULL /* Use Global Core filepath */); + ASMAtomicWriteHandle(&g_CoreDumpThread, NIL_RTNATIVETHREAD); + + if (RT_FAILURE(rc)) + CORELOGRELSYS((CORELOG_NAME "TakeDump failed! rc=%Rrc\n", rc)); + } + else if (Sig == SIGSEGV || Sig == SIGBUS || Sig == SIGTRAP) + { + /* + * Core dumping is already in progress and we've somehow ended up being + * signalled again. + */ + rc = VERR_INTERNAL_ERROR; + + /* + * If our dumper has crashed. No point in waiting, trigger the system one. + * Wait only when the dumping thread is not the one generating this signal. + */ + RTNATIVETHREAD hNativeDumperThread; + ASMAtomicReadHandle(&g_CoreDumpThread, &hNativeDumperThread); + if (hNativeDumperThread == RTThreadNativeSelf()) + { + CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dumper (thread %u) crashed Sig=%d. Triggering system dump\n", + RTThreadSelf(), Sig)); + fCallSystemDump = true; + } + else + { + /* + * Some other thread in the process is triggering a crash, wait a while + * to let our core dumper finish, on timeout trigger system dump. + */ + CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dump already in progress! Waiting a while for completion Sig=%d.\n", + Sig)); + int64_t iTimeout = 16000; /* timeout (ms) */ + for (;;) + { + ASMAtomicReadHandle(&g_CoreDumpThread, &hNativeDumperThread); + if (hNativeDumperThread == NIL_RTNATIVETHREAD) + break; + RTThreadSleep(200); + iTimeout -= 200; + if (iTimeout <= 0) + break; + } + if (iTimeout <= 0) + { + fCallSystemDump = true; + CORELOGRELSYS((CORELOG_NAME "SignalHandler: Core dumper seems to be stuck. Signalling new signal %d\n", Sig)); + } + } + } + + if (Sig == SIGSEGV || Sig == SIGBUS || Sig == SIGTRAP) + { + /* + * Reset signal handlers, we're not a live core we will be blown away + * one way or another. + */ + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGTRAP, SIG_DFL); + + /* + * Hard terminate the process if this is not a live dump without invoking + * the system core dumping behaviour. + */ + if (RT_SUCCESS(rc)) + raise(SIGKILL); + + /* + * Something went wrong, fall back to the system core dumper. + */ + if (fCallSystemDump) + abort(); + } +} + + +RTDECL(int) RTCoreDumperTakeDump(const char *pszOutputFile, bool fLiveCore) +{ + ucontext_t Context; + int rc = getcontext(&Context); + if (!rc) + { + /* + * Block SIGSEGV and co. while we write the core. + */ + sigset_t SigSet, OldSigSet; + sigemptyset(&SigSet); + sigaddset(&SigSet, SIGSEGV); + sigaddset(&SigSet, SIGBUS); + sigaddset(&SigSet, SIGTRAP); + sigaddset(&SigSet, SIGUSR2); + pthread_sigmask(SIG_BLOCK, &SigSet, &OldSigSet); + rc = rtCoreDumperTakeDump(&Context, pszOutputFile); + if (RT_FAILURE(rc)) + CORELOGRELSYS(("RTCoreDumperTakeDump: rtCoreDumperTakeDump failed rc=%Rrc\n", rc)); + + if (!fLiveCore) + { + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGTRAP, SIG_DFL); + if (RT_SUCCESS(rc)) + raise(SIGKILL); + else + abort(); + } + pthread_sigmask(SIG_SETMASK, &OldSigSet, NULL); + } + else + { + CORELOGRELSYS(("RTCoreDumperTakeDump: getcontext failed rc=%d.\n", rc)); + rc = VERR_INVALID_CONTEXT; + } + + return rc; +} + + +RTDECL(int) RTCoreDumperSetup(const char *pszOutputDir, uint32_t fFlags) +{ + /* + * Validate flags. + */ + AssertReturn(fFlags, VERR_INVALID_PARAMETER); + AssertReturn(!(fFlags & ~( RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP + | RTCOREDUMPER_FLAGS_LIVE_CORE)), + VERR_INVALID_PARAMETER); + + + /* + * Setup/change the core dump directory if specified. + */ + RT_ZERO(g_szCoreDumpDir); + if (pszOutputDir) + { + if (!RTDirExists(pszOutputDir)) + return VERR_NOT_A_DIRECTORY; + RTStrCopy(g_szCoreDumpDir, sizeof(g_szCoreDumpDir), pszOutputDir); + } + + /* + * Install core dump signal handler only if the flags changed or if it's the first time. + */ + if ( ASMAtomicReadBool(&g_fCoreDumpSignalSetup) == false + || ASMAtomicReadU32(&g_fCoreDumpFlags) != fFlags) + { + struct sigaction sigAct; + RT_ZERO(sigAct); + sigAct.sa_sigaction = &rtCoreDumperSignalHandler; + + if ( (fFlags & RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP) + && !(g_fCoreDumpFlags & RTCOREDUMPER_FLAGS_REPLACE_SYSTEM_DUMP)) + { + sigemptyset(&sigAct.sa_mask); + sigAct.sa_flags = SA_RESTART | SA_SIGINFO | SA_NODEFER; + sigaction(SIGSEGV, &sigAct, NULL); + sigaction(SIGBUS, &sigAct, NULL); + sigaction(SIGTRAP, &sigAct, NULL); + } + + if ( fFlags & RTCOREDUMPER_FLAGS_LIVE_CORE + && !(g_fCoreDumpFlags & RTCOREDUMPER_FLAGS_LIVE_CORE)) + { + sigfillset(&sigAct.sa_mask); /* Block all signals while in it's signal handler */ + sigAct.sa_flags = SA_RESTART | SA_SIGINFO; + sigaction(SIGUSR2, &sigAct, NULL); + } + + ASMAtomicWriteU32(&g_fCoreDumpFlags, fFlags); + ASMAtomicWriteBool(&g_fCoreDumpSignalSetup, true); + } + + return VINF_SUCCESS; +} + + +RTDECL(int) RTCoreDumperDisable(void) +{ + /* + * Remove core dump signal handler & reset variables. + */ + if (ASMAtomicReadBool(&g_fCoreDumpSignalSetup) == true) + { + signal(SIGSEGV, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGTRAP, SIG_DFL); + signal(SIGUSR2, SIG_DFL); + ASMAtomicWriteBool(&g_fCoreDumpSignalSetup, false); + } + + RT_ZERO(g_szCoreDumpDir); + RT_ZERO(g_szCoreDumpFile); + ASMAtomicWriteU32(&g_fCoreDumpFlags, 0); + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r3/solaris/coredumper-solaris.h b/src/VBox/Runtime/r3/solaris/coredumper-solaris.h new file mode 100644 index 00000000..67cd03ba --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/coredumper-solaris.h @@ -0,0 +1,167 @@ +/* $Id: coredumper-solaris.h $ */ +/** @file + * IPRT - Custom Core Dumper, Solaris. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h +#define IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> + +#ifdef RT_OS_SOLARIS +# if defined(RT_ARCH_X86) && _FILE_OFFSET_BITS==64 +/* + * Solaris' procfs cannot be used with large file environment in 32-bit. + */ +# undef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 32 +# include <procfs.h> +# include <sys/procfs.h> +# include <sys/old_procfs.h> +# undef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +#else +# include <procfs.h> +# include <sys/procfs.h> +# include <sys/old_procfs.h> +#endif +# include <limits.h> +# include <thread.h> +# include <sys/auxv.h> +# include <sys/lwp.h> +# include <sys/zone.h> +# include <sys/utsname.h> + +#ifdef RT_ARCH_AMD64 +# define _ELF64 +# undef _ELF32_COMPAT +#endif +# include <sys/corectl.h> +#endif + + +#ifdef RT_OS_SOLARIS +/** + * Memory mapping descriptor employed by the solaris core dumper. + */ +typedef struct RTSOLCOREMAPINFO +{ + prmap_t pMap; /**< Proc description of this mapping */ + int fError; /**< Any error reading this mapping (errno) */ + struct RTSOLCOREMAPINFO *pNext; /**< Pointer to the next mapping */ +} RTSOLCOREMAPINFO; +/** Pointer to a solaris memory mapping descriptor. */ +typedef RTSOLCOREMAPINFO *PRTSOLCOREMAPINFO; + +/** + * Whether this is an old or new style solaris core. + */ +typedef enum RTSOLCORETYPE +{ + enmOldEra = 0x01d, /**< old */ + enmNewEra = 0x5c1f1 /**< sci-fi */ +} RTSOLCORETYPE; + +/** + * Per-Thread information employed by the solaris core dumper. + */ +typedef struct RTSOLCORETHREADINFO +{ + lwpsinfo_t Info; /**< Proc description of this thread */ + lwpstatus_t *pStatus; /**< Proc description of this thread's status (can be NULL, zombie lwp) */ + struct RTSOLCORETHREADINFO *pNext; /**< Pointer to the next thread */ +} RTSOLCORETHREADINFO; +typedef RTSOLCORETHREADINFO *PRTSOLCORETHREADINFO; +#endif + + +/** + * Current (also the core target) process information. + */ +typedef struct RTSOLCOREPROCESS +{ + RTPROCESS Process; /**< The pid of the process */ + char szExecPath[PATH_MAX]; /**< Path of the executable */ + char *pszExecName; /**< Name of the executable file */ +#ifdef RT_OS_SOLARIS + void *pvProcInfo; /**< Process info. */ + size_t cbProcInfo; /**< Size of the process info. */ + prpsinfo_t ProcInfoOld; /**< Process info. Older version (for GDB compat.) */ + pstatus_t ProcStatus; /**< Process status info. */ + thread_t hCurThread; /**< The current thread */ + ucontext_t *pCurThreadCtx; /**< Context info. of current thread before starting to dump */ + int fdAs; /**< proc/pid/as file handle */ + auxv_t *pAuxVecs; /**< Aux vector of process */ + int cAuxVecs; /**< Number of aux vector entries */ + PRTSOLCOREMAPINFO pMapInfoHead; /**< Pointer to the head of list of mappings */ + uint32_t cMappings; /**< Number of mappings (count of pMapInfoHead list) */ + PRTSOLCORETHREADINFO pThreadInfoHead; /**< Pointer to the head of list of threads */ + uint64_t cThreads; /**< Number of threads (count of pThreadInfoHead list) */ + char szPlatform[SYS_NMLN]; /**< Platform name */ + char szZoneName[ZONENAME_MAX]; /**< Zone name */ + struct utsname UtsName; /**< UTS name */ + void *pvCred; /**< Process credential info. */ + size_t cbCred; /**< Size of process credential info. */ + void *pvLdt; /**< Process LDT info. */ + size_t cbLdt; /**< Size of the LDT info. */ + prpriv_t *pPriv; /**< Process privilege info. */ + size_t cbPriv; /**< Size of process privilege info. */ + const priv_impl_info_t *pcPrivImpl; /**< Process privilege implementation info. (opaque handle) */ + core_content_t CoreContent; /**< What information goes in the core */ +#else +# error Port Me! +#endif + +} RTSOLCOREPROCESS; +typedef RTSOLCOREPROCESS *PRTSOLCOREPROCESS; + +typedef int (*PFNRTCOREREADER)(int fdFile, void *pv, size_t cb); +typedef int (*PFNRTCOREWRITER)(int fdhFile, const void *pcv, size_t cb); + +/** + * The solaris core file object. + */ +typedef struct RTSOLCORE +{ + char szCorePath[PATH_MAX]; /**< Path of the core file */ + RTSOLCOREPROCESS SolProc; /**< Current process information */ + void *pvCore; /**< Pointer to memory area during dumping */ + size_t cbCore; /**< Size of memory area during dumping */ + void *pvFree; /**< Pointer to base of free range in preallocated memory area */ + bool fIsValid; /**< Whether core information has been fully collected */ + PFNRTCOREREADER pfnReader; /**< Reader function */ + PFNRTCOREWRITER pfnWriter; /**< Writer function */ + int fdCoreFile; /**< Core file (used only while writing the core) */ + RTFOFF offWrite; /**< Segment/section offset (used only while writing the core) */ +} RTSOLCORE; +typedef RTSOLCORE *PRTSOLCORE; + +typedef int (*PFNRTSOLCOREACCUMULATOR)(PRTSOLCORE pSolCore); +typedef int (*PFNRTSOLCORETHREADWORKER)(PRTSOLCORE pSolCore, void *pvThreadInfo); + +#endif /* !IPRT_INCLUDED_SRC_r3_solaris_coredumper_solaris_h */ + diff --git a/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp b/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp new file mode 100644 index 00000000..940b7a0c --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/fileaio-solaris.cpp @@ -0,0 +1,565 @@ +/* $Id: fileaio-solaris.cpp $ */ +/** @file + * IPRT - File async I/O, native implementation for the Solaris host platform. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_FILE +#include <iprt/asm.h> +#include <iprt/file.h> +#include <iprt/mem.h> +#include <iprt/assert.h> +#include <iprt/string.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include "internal/fileaio.h" + +#include <port.h> +#include <aio.h> +#include <errno.h> +#include <unistd.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Async I/O completion context state. + */ +typedef struct RTFILEAIOCTXINTERNAL +{ + /** Handle to the port. */ + int iPort; + /** Current number of requests active on this context. */ + volatile int32_t cRequests; + /** Flags given during creation. */ + uint32_t fFlags; + /** Magic value (RTFILEAIOCTX_MAGIC). */ + uint32_t u32Magic; +} RTFILEAIOCTXINTERNAL; +/** Pointer to an internal context structure. */ +typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL; + +/** + * Async I/O request state. + */ +typedef struct RTFILEAIOREQINTERNAL +{ + /** The aio control block. Must be the FIRST + * element. */ + struct aiocb AioCB; + /** Current state the request is in. */ + RTFILEAIOREQSTATE enmState; + /** Flag whether this is a flush request. */ + bool fFlush; + /** Port notifier object to associate a request to a port. */ + port_notify_t PortNotifier; + /** Opaque user data. */ + void *pvUser; + /** Completion context we are assigned to. */ + PRTFILEAIOCTXINTERNAL pCtxInt; + /** Magic value (RTFILEAIOREQ_MAGIC). */ + uint32_t u32Magic; +} RTFILEAIOREQINTERNAL; +/** Pointer to an internal request structure. */ +typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The max number of events to get in one call. */ +#define AIO_MAXIMUM_REQUESTS_PER_CONTEXT 64 +/** Id for the wakeup event. */ +#define AIO_CONTEXT_WAKEUP_EVENT 1 + +RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits) +{ + int rcBSD = 0; + AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER); + + /* No limits known. */ + pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS; + pAioLimits->cbBufferAlignment = 0; + + return VINF_SUCCESS; +} + +RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq) +{ + AssertPtrReturn(phReq, VERR_INVALID_POINTER); + + PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL)); + if (RT_UNLIKELY(!pReqInt)) + return VERR_NO_MEMORY; + + /* Ininitialize static parts. */ + pReqInt->AioCB.aio_sigevent.sigev_notify = SIGEV_PORT; + pReqInt->AioCB.aio_sigevent.sigev_value.sival_ptr = &pReqInt->PortNotifier; + pReqInt->PortNotifier.portnfy_user = pReqInt; + pReqInt->pCtxInt = NULL; + pReqInt->u32Magic = RTFILEAIOREQ_MAGIC; + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + + *phReq = (RTFILEAIOREQ)pReqInt; + + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq) +{ + /* + * Validate the handle and ignore nil. + */ + if (hReq == NIL_RTFILEAIOREQ) + return VINF_SUCCESS; + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + + /* + * Trash the magic and free it. + */ + ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC); + RTMemFree(pReqInt); + return VINF_SUCCESS; +} + +/** + * Worker setting up the request. + */ +DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile, + unsigned uTransferDirection, + RTFOFF off, void *pvBuf, size_t cbTransfer, + void *pvUser) +{ + /* + * Validate the input. + */ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + Assert(hFile != NIL_RTFILE); + AssertPtr(pvBuf); + Assert(off >= 0); + Assert(cbTransfer > 0); + + pReqInt->AioCB.aio_lio_opcode = uTransferDirection; + pReqInt->AioCB.aio_fildes = RTFileToNative(hFile); + pReqInt->AioCB.aio_offset = off; + pReqInt->AioCB.aio_nbytes = cbTransfer; + pReqInt->AioCB.aio_buf = pvBuf; + pReqInt->fFlush = false; + pReqInt->pvUser = pvUser; + pReqInt->pCtxInt = NULL; + RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED); + + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off, + void *pvBuf, size_t cbRead, void *pvUser) +{ + return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_READ, + off, pvBuf, cbRead, pvUser); +} + +RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off, + void const *pvBuf, size_t cbWrite, void *pvUser) +{ + return rtFileAioReqPrepareTransfer(hReq, hFile, LIO_WRITE, + off, (void *)pvBuf, cbWrite, pvUser); +} + +RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser) +{ + PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)hReq; + + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + Assert(hFile != NIL_RTFILE); + + pReqInt->fFlush = true; + pReqInt->AioCB.aio_fildes = RTFileToNative(hFile); + pReqInt->AioCB.aio_offset = 0; + pReqInt->AioCB.aio_nbytes = 0; + pReqInt->AioCB.aio_buf = NULL; + pReqInt->pvUser = pvUser; + RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED); + + return VINF_SUCCESS; +} + +RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL); + + return pReqInt->pvUser; +} + +RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED); + + int rcSolaris = aio_cancel(pReqInt->AioCB.aio_fildes, &pReqInt->AioCB); + + if (rcSolaris == AIO_CANCELED) + { + /* + * Decrement request count because the request will never arrive at the + * completion port. + */ + AssertMsg(VALID_PTR(pReqInt->pCtxInt), + ("Invalid state. Request was canceled but wasn't submitted\n")); + + ASMAtomicDecS32(&pReqInt->pCtxInt->cRequests); + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + return VINF_SUCCESS; + } + else if (rcSolaris == AIO_ALLDONE) + return VERR_FILE_AIO_COMPLETED; + else if (rcSolaris == AIO_NOTCANCELED) + return VERR_FILE_AIO_IN_PROGRESS; + else + return RTErrConvertFromErrno(errno); +} + +RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered) +{ + PRTFILEAIOREQINTERNAL pReqInt = hReq; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS); + RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED); + AssertPtrNull(pcbTransfered); + + int rcSol = aio_error(&pReqInt->AioCB); + Assert(rcSol != EINPROGRESS); /* Handled by our own state handling. */ + + if (rcSol == 0) + { + if (pcbTransfered) + *pcbTransfered = aio_return(&pReqInt->AioCB); + return VINF_SUCCESS; + } + + /* An error occurred. */ + return RTErrConvertFromErrno(rcSol); +} + +RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, + uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt; + AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER); + AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER); + + pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL)); + if (RT_UNLIKELY(!pCtxInt)) + return VERR_NO_MEMORY; + + /* Init the event handle. */ + pCtxInt->iPort = port_create(); + if (RT_LIKELY(pCtxInt->iPort > 0)) + { + pCtxInt->fFlags = fFlags; + pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC; + *phAioCtx = (RTFILEAIOCTX)pCtxInt; + } + else + { + RTMemFree(pCtxInt); + rc = RTErrConvertFromErrno(errno); + } + + return rc; +} + +RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx) +{ + /* Validate the handle and ignore nil. */ + if (hAioCtx == NIL_RTFILEAIOCTX) + return VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + + /* Cannot destroy a busy context. */ + if (RT_UNLIKELY(pCtxInt->cRequests)) + return VERR_FILE_AIO_BUSY; + + close(pCtxInt->iPort); + ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD); + RTMemFree(pCtxInt); + + return VINF_SUCCESS; +} + +RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx) +{ + return RTFILEAIO_UNLIMITED_REQS; +} + +RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile) +{ + return VINF_SUCCESS; +} + +RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs) +{ + /* + * Parameter validation. + */ + int rc = VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER); + AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); + size_t i = cReqs; + + do + { + int rcSol = 0; + size_t cReqsSubmit = 0; + PRTFILEAIOREQINTERNAL pReqInt; + + while(i-- > 0) + { + pReqInt = pahReqs[i]; + if (RTFILEAIOREQ_IS_NOT_VALID(pReqInt)) + { + /* Undo everything and stop submitting. */ + for (size_t iUndo = 0; iUndo < i; iUndo++) + { + pReqInt = pahReqs[iUndo]; + RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED); + pReqInt->pCtxInt = NULL; + } + rc = VERR_INVALID_HANDLE; + break; + } + + pReqInt->PortNotifier.portnfy_port = pCtxInt->iPort; + pReqInt->pCtxInt = pCtxInt; + RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED); + + if (pReqInt->fFlush) + break; + + cReqsSubmit++; + } + + if (cReqsSubmit) + { + rcSol = lio_listio(LIO_NOWAIT, (struct aiocb **)pahReqs, cReqsSubmit, NULL); + if (RT_UNLIKELY(rcSol < 0)) + { + if (rcSol == EAGAIN) + rc = VERR_FILE_AIO_INSUFFICIENT_RESSOURCES; + else + rc = RTErrConvertFromErrno(errno); + + /* Check which requests got actually submitted and which not. */ + for (i = 0; i < cReqs; i++) + { + pReqInt = pahReqs[i]; + rcSol = aio_error(&pReqInt->AioCB); + if (rcSol == EINVAL) + { + /* Was not submitted. */ + RTFILEAIOREQ_SET_STATE(pReqInt, PREPARED); + pReqInt->pCtxInt = NULL; + } + else if (rcSol != EINPROGRESS) + { + /* The request encountered an error. */ + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + } + } + break; + } + + ASMAtomicAddS32(&pCtxInt->cRequests, cReqsSubmit); + cReqs -= cReqsSubmit; + pahReqs += cReqsSubmit; + } + + if (cReqs) + { + pReqInt = pahReqs[0]; + RTFILEAIOREQ_VALID_RETURN(pReqInt); + + /* + * If there are still requests left we have a flush request. + * lio_listio does not work with this requests so + * we have to use aio_fsync directly. + */ + rcSol = aio_fsync(O_SYNC, &pReqInt->AioCB); + if (RT_UNLIKELY(rcSol < 0)) + { + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + rc = RTErrConvertFromErrno(errno); + break; + } + + ASMAtomicIncS32(&pCtxInt->cRequests); + cReqs--; + pahReqs++; + } + } while (cReqs); + + return rc; +} + +RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies, + PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs) +{ + int rc = VINF_SUCCESS; + int cRequestsCompleted = 0; + + /* + * Validate the parameters, making sure to always set pcReqs. + */ + AssertPtrReturn(pcReqs, VERR_INVALID_POINTER); + *pcReqs = 0; /* always set */ + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + AssertPtrReturn(pahReqs, VERR_INVALID_POINTER); + AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER); + AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE); + + if ( RT_UNLIKELY(ASMAtomicReadS32(&pCtxInt->cRequests) == 0) + && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS)) + return VERR_FILE_AIO_NO_REQUEST; + + /* + * Convert the timeout if specified. + */ + struct timespec *pTimeout = NULL; + struct timespec Timeout = {0,0}; + uint64_t StartNanoTS = 0; + if (cMillies != RT_INDEFINITE_WAIT) + { + Timeout.tv_sec = cMillies / 1000; + Timeout.tv_nsec = cMillies % 1000 * 1000000; + pTimeout = &Timeout; + StartNanoTS = RTTimeNanoTS(); + } + + /* Wait for at least one. */ + if (!cMinReqs) + cMinReqs = 1; + + while ( cMinReqs + && RT_SUCCESS_NP(rc)) + { + port_event_t aPortEvents[AIO_MAXIMUM_REQUESTS_PER_CONTEXT]; + uint_t cRequests = cMinReqs; + int cRequestsToWait = RT_MIN(cReqs, AIO_MAXIMUM_REQUESTS_PER_CONTEXT); + int rcSol; + uint64_t StartTime; + + rcSol = port_getn(pCtxInt->iPort, &aPortEvents[0], cRequestsToWait, &cRequests, pTimeout); + + if (RT_UNLIKELY(rcSol < 0)) + rc = RTErrConvertFromErrno(errno); + + /* Process received events. */ + for (uint_t i = 0; i < cRequests; i++) + { + if (aPortEvents[i].portev_source == PORT_SOURCE_ALERT) + { + Assert(aPortEvents[i].portev_events == AIO_CONTEXT_WAKEUP_EVENT); + rc = VERR_INTERRUPTED; /* We've got interrupted. */ + /* Reset the port. */ + port_alert(pCtxInt->iPort, PORT_ALERT_SET, 0, NULL); + } + else + { + PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)aPortEvents[i].portev_user; + AssertPtr(pReqInt); + Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC); + + /* A request has finished. */ + pahReqs[cRequestsCompleted++] = pReqInt; + + /* Mark the request as finished. */ + RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED); + } + } + + /* + * Done Yet? If not advance and try again. + */ + if (cRequests >= cMinReqs) + break; + cMinReqs -= cRequests; + cReqs -= cRequests; + + if (cMillies != RT_INDEFINITE_WAIT) + { + uint64_t NanoTS = RTTimeNanoTS(); + uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000; + + /* The syscall supposedly updates it, but we're paranoid. :-) */ + if (cMilliesElapsed < cMillies) + { + Timeout.tv_sec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) / 1000; + Timeout.tv_nsec = (cMillies - (RTMSINTERVAL)cMilliesElapsed) % 1000 * 1000000; + } + else + { + Timeout.tv_sec = 0; + Timeout.tv_nsec = 0; + } + } + } + + /* + * Update the context state and set the return value. + */ + *pcReqs = cRequestsCompleted; + ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted); + + return rc; +} + +RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx) +{ + int rc = VINF_SUCCESS; + PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx; + RTFILEAIOCTX_VALID_RETURN(pCtxInt); + + rc = port_alert(pCtxInt->iPort, PORT_ALERT_UPDATE, AIO_CONTEXT_WAKEUP_EVENT, NULL); + if (RT_UNLIKELY((rc < 0) && (errno != EBUSY))) + return RTErrConvertFromErrno(errno); + + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp b/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp new file mode 100644 index 00000000..545165fb --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/krnlmod-solaris.cpp @@ -0,0 +1,324 @@ +/* $Id: krnlmod-solaris.cpp $ */ +/** @file + * IPRT - Kernel module, Linux. + */ + +/* + * Copyright (C) 2017-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_SYSTEM +#include <iprt/krnlmod.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/types.h> + +#include <iprt/stream.h> + +#include <sys/modctl.h> +#include <errno.h> + +/** + * Internal kernel information record state. + */ +typedef struct RTKRNLMODINFOINT +{ + /** Reference counter. */ + volatile uint32_t cRefs; + /** Load address of the kernel module. */ + RTR0UINTPTR uLoadAddr; + /** Size of the kernel module. */ + size_t cbKrnlMod; + /** Size of the name in characters including the zero terminator. */ + size_t cchName; + /** Module name - variable in size. */ + char achName[1]; +} RTKRNLMODINFOINT; +/** Pointer to the internal kernel module information record. */ +typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT; +/** Pointer to a const internal kernel module information record. */ +typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT; + + + +/** + * Destroy the given kernel module information record. + * + * @returns nothing. + * @param pThis The record to destroy. + */ +static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis) +{ + RTMemFree(pThis); +} + + +/** + * Creates a new kernel module information record for the given module. + * + * @returns IPRT status code. + * @param pModInfo The Solaris kernel module information. + * @param phKrnlModInfo Where to store the handle to the kernel module information record + * on success. + */ +static int rtKrnlModSolInfoCreate(struct modinfo *pModInfo, PRTKRNLMODINFO phKrnlModInfo) +{ + int rc = VINF_SUCCESS; + size_t cchName = strlen(&pModInfo->mi_name[0]) + 1; + PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achName[cchName])); + if (RT_LIKELY(pThis)) + { + memcpy(&pThis->achName[0], &pModInfo->mi_name[0], cchName); + pThis->cchName = cchName; + pThis->cRefs = 1; + pThis->cbKrnlMod = pModInfo->mi_size; + pThis->uLoadAddr = (RTR0UINTPTR)pModInfo->mi_base; + + *phKrnlModInfo = pThis; + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER); + + RTKRNLMODINFO hKrnlModInfo = NIL_RTKRNLMODINFO; + int rc = RTKrnlModLoadedQueryInfo(pszName, &hKrnlModInfo); + if (RT_SUCCESS(rc)) + { + *pfLoaded = true; + RTKrnlModInfoRelease(hKrnlModInfo); + } + else if (rc == VERR_NOT_FOUND) + { + *pfLoaded = false; + rc = VINF_SUCCESS; + } + + return rc; +} + + +RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER); + + int rc = VERR_NOT_FOUND; + int iId = -1; + struct modinfo ModInfo; + + ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT; + ModInfo.mi_id = iId; + ModInfo.mi_nextid = iId; + do + { + int rcSol = modctl(MODINFO, iId, &ModInfo); + if (rcSol < 0) + { + rc = RTErrConvertFromErrno(errno); + break; + } + + if (ModInfo.mi_id != -1) + { + ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */ + if (!RTStrCmp(pszName, &ModInfo.mi_name[0])) + { + rc = rtKrnlModSolInfoCreate(&ModInfo, phKrnlModInfo); + break; + } + } + + iId = ModInfo.mi_id; + } while (iId != -1); + + return rc; +} + + +RTDECL(uint32_t) RTKrnlModLoadedGetCount(void) +{ + uint32_t cKmodsLoaded = 0; + int iId = -1; + struct modinfo ModInfo; + + ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT; + ModInfo.mi_id = iId; + ModInfo.mi_nextid = iId; + do + { + int rcSol = modctl(MODINFO, iId, &ModInfo); + if (rcSol < 0) + break; + + cKmodsLoaded++; + + iId = ModInfo.mi_id; + } while (iId != -1); + + return cKmodsLoaded; +} + + +RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax, + uint32_t *pcEntries) +{ + AssertReturn(VALID_PTR(pahKrnlModInfo) || cEntriesMax == 0, VERR_INVALID_PARAMETER); + + uint32_t cKmodsLoaded = RTKrnlModLoadedGetCount(); + if (cEntriesMax < cKmodsLoaded) + { + if (*pcEntries) + *pcEntries = cKmodsLoaded; + return VERR_BUFFER_OVERFLOW; + } + + int rc = VINF_SUCCESS; + int iId = -1; + unsigned idxKrnlModInfo = 0; + struct modinfo ModInfo; + + ModInfo.mi_info = MI_INFO_ALL | MI_INFO_CNT; + ModInfo.mi_id = iId; + ModInfo.mi_nextid = iId; + do + { + int rcSol = modctl(MODINFO, iId, &ModInfo); + if (rcSol < 0) + { + rc = RTErrConvertFromErrno(errno); + if (rc == VERR_INVALID_PARAMETER && idxKrnlModInfo > 0) + rc = VINF_SUCCESS; + break; + } + + ModInfo.mi_name[MODMAXNAMELEN - 1] = '\0'; /* Paranoia. */ + rc = rtKrnlModSolInfoCreate(&ModInfo, &pahKrnlModInfo[idxKrnlModInfo]); + if (RT_SUCCESS(rc)) + idxKrnlModInfo++; + + iId = ModInfo.mi_id; + } while (iId != -1); + + if (RT_FAILURE(rc)) + { + /* Rollback */ + while (idxKrnlModInfo-- > 0) + RTKrnlModInfoRelease(pahKrnlModInfo[idxKrnlModInfo]); + } + else if (pcEntries) + *pcEntries = idxKrnlModInfo; + + return rc; +} + + +RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + return cRefs; +} + + +RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + if (!pThis) + return 0; + AssertPtrReturn(pThis, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); + AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis)); + if (cRefs == 0) + rtKrnlModInfoDestroy(pThis); + return cRefs; +} + + +RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return 0; +} + + +RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, NULL); + + return &pThis->achName[0]; +} + + +RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, NULL); + + return NULL; +} + + +RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return pThis->cbKrnlMod; +} + + +RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo) +{ + PRTKRNLMODINFOINT pThis = hKrnlModInfo; + AssertPtrReturn(pThis, 0); + + return pThis->uLoadAddr; +} + + +RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx, + PRTKRNLMODINFO phKrnlModInfoRef) +{ + RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef); + return VERR_NOT_IMPLEMENTED; +} diff --git a/src/VBox/Runtime/r3/solaris/mp-solaris.cpp b/src/VBox/Runtime/r3/solaris/mp-solaris.cpp new file mode 100644 index 00000000..cc944b2d --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/mp-solaris.cpp @@ -0,0 +1,469 @@ +/* $Id: mp-solaris.cpp $ */ +/** @file + * IPRT - Multiprocessor, Solaris. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_DEFAULT +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <kstat.h> +#include <sys/processor.h> + +#include <iprt/mp.h> +#include <iprt/cpuset.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/log.h> +#include <iprt/once.h> +#include <iprt/string.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Initialization serializing (rtMpSolarisOnce). */ +static RTONCE g_MpSolarisOnce = RTONCE_INITIALIZER; +/** Critical section serializing access to kstat. */ +static RTCRITSECT g_MpSolarisCritSect; +/** The kstat handle. */ +static kstat_ctl_t *g_pKsCtl; +/** Array pointing to the cpu_info instances. */ +static kstat_t **g_papCpuInfo; +/** The number of entries in g_papCpuInfo */ +static RTCPUID g_capCpuInfo; +/** Array of core ids. */ +static uint64_t *g_pu64CoreIds; +/** Number of entries in g_pu64CoreIds. */ +static size_t g_cu64CoreIds; +/** Number of cores in the system. */ +static size_t g_cCores; + + +/** + * Helper for getting the core ID for a given CPU/strand/hyperthread. + * + * @returns The core ID. + * @param idCpu The CPU ID instance. + */ +static inline uint64_t rtMpSolarisGetCoreId(RTCPUID idCpu) +{ + kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"core_id"); + Assert(pStat->data_type == KSTAT_DATA_LONG); + Assert(pStat->value.l >= 0); + AssertCompile(sizeof(uint64_t) >= sizeof(long)); /* Paranoia. */ + return (uint64_t)pStat->value.l; +} + + +/** + * Populates 'g_pu64CoreIds' array with unique core identifiers in the system. + * + * @returns VBox status code. + */ +static int rtMpSolarisGetCoreIds(void) +{ + for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++) + { + /* + * It is possible that the number of cores don't match the maximum number + * of cores possible on the system. Hence check if we have a valid cpu_info + * object. We don't want to break out if it's NULL, the array may be sparse + * in theory, see @bugref{8469}. + */ + if (g_papCpuInfo[idCpu]) + { + if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1) + { + /* Strands/Hyperthreads share the same core ID. */ + uint64_t u64CoreId = rtMpSolarisGetCoreId(idCpu); + bool fAddedCore = false; + for (RTCPUID i = 0; i < g_cCores; i++) + { + if (g_pu64CoreIds[i] == u64CoreId) + { + fAddedCore = true; + break; + } + } + + if (!fAddedCore) + { + g_pu64CoreIds[g_cCores] = u64CoreId; + ++g_cCores; + } + } + else + return VERR_INTERNAL_ERROR_2; + } + } + + return VINF_SUCCESS; +} + + +/** + * Run once function that initializes the kstats we need here. + * + * @returns IPRT status code. + * @param pvUser Unused. + */ +static DECLCALLBACK(int) rtMpSolarisOnce(void *pvUser) +{ + int rc = VINF_SUCCESS; + NOREF(pvUser); + + /* + * Open kstat and find the cpu_info entries for each of the CPUs. + */ + g_pKsCtl = kstat_open(); + if (g_pKsCtl) + { + g_capCpuInfo = RTMpGetCount(); + if (RT_LIKELY(g_capCpuInfo > 0)) + { + g_papCpuInfo = (kstat_t **)RTMemAllocZ(g_capCpuInfo * sizeof(kstat_t *)); + if (g_papCpuInfo) + { + g_cu64CoreIds = g_capCpuInfo; + g_pu64CoreIds = (uint64_t *)RTMemAllocZ(g_cu64CoreIds * sizeof(uint64_t)); + if (g_pu64CoreIds) + { + rc = RTCritSectInit(&g_MpSolarisCritSect); + if (RT_SUCCESS(rc)) + { + RTCPUID i = 0; + for (kstat_t *pKsp = g_pKsCtl->kc_chain; pKsp != NULL; pKsp = pKsp->ks_next) + { + if (!RTStrCmp(pKsp->ks_module, "cpu_info")) + { + AssertBreak(i < g_capCpuInfo); + g_papCpuInfo[i++] = pKsp; + /** @todo ks_instance == cpu_id (/usr/src/uts/common/os/cpu.c)? Check this and fix it ASAP. */ + } + } + + rc = rtMpSolarisGetCoreIds(); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + else + Log(("rtMpSolarisGetCoreIds failed. rc=%Rrc\n", rc)); + } + + RTMemFree(g_pu64CoreIds); + g_pu64CoreIds = NULL; + } + else + rc = VERR_NO_MEMORY; + + /* bail out, we failed. */ + RTMemFree(g_papCpuInfo); + g_papCpuInfo = NULL; + } + else + rc = VERR_NO_MEMORY; + } + else + rc = VERR_CPU_IPE_1; + kstat_close(g_pKsCtl); + g_pKsCtl = NULL; + } + else + { + rc = RTErrConvertFromErrno(errno); + if (RT_SUCCESS(rc)) + rc = VERR_INTERNAL_ERROR; + Log(("kstat_open() -> %d (%Rrc)\n", errno, rc)); + } + + return rc; +} + + +/** + * RTOnceEx() cleanup function. + * + * @param pvUser Unused. + * @param fLazyCleanUpOk Whether lazy cleanup is okay or not. + */ +static DECLCALLBACK(void) rtMpSolarisCleanUp(void *pvUser, bool fLazyCleanUpOk) +{ + if (g_pKsCtl) + kstat_close(g_pKsCtl); + RTMemFree(g_pu64CoreIds); + RTMemFree(g_papCpuInfo); +} + + +/** + * Worker for RTMpGetCurFrequency and RTMpGetMaxFrequency. + * + * @returns The desired frequency on success, 0 on failure. + * + * @param idCpu The CPU ID. + * @param pszStatName The cpu_info stat name. + */ +static uint64_t rtMpSolarisGetFrequency(RTCPUID idCpu, const char *pszStatName) +{ + uint64_t u64 = 0; + int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */); + if (RT_SUCCESS(rc)) + { + if ( idCpu < g_capCpuInfo + && g_papCpuInfo[idCpu]) + { + rc = RTCritSectEnter(&g_MpSolarisCritSect); + AssertRC(rc); + if (RT_SUCCESS(rc)) + { + if (kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0) != -1) + { + /* Solaris really need to fix their APIs. Explicitly cast for now. */ + kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char*)pszStatName); + if (pStat) + { + Assert(pStat->data_type == KSTAT_DATA_UINT64 || pStat->data_type == KSTAT_DATA_LONG); + switch (pStat->data_type) + { + case KSTAT_DATA_UINT64: u64 = pStat->value.ui64; break; /* current_clock_Hz */ + case KSTAT_DATA_INT32: u64 = pStat->value.i32; break; /* clock_MHz */ + + /* just in case... */ + case KSTAT_DATA_UINT32: u64 = pStat->value.ui32; break; + case KSTAT_DATA_INT64: u64 = pStat->value.i64; break; + default: + AssertMsgFailed(("%d\n", pStat->data_type)); + break; + } + } + else + Log(("kstat_data_lookup(%s) -> %d\n", pszStatName, errno)); + } + else + Log(("kstat_read() -> %d\n", errno)); + RTCritSectLeave(&g_MpSolarisCritSect); + } + } + else + Log(("invalid idCpu: %d (g_capCpuInfo=%d)\n", (int)idCpu, (int)g_capCpuInfo)); + } + + return u64; +} + + +RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu) +{ + return rtMpSolarisGetFrequency(idCpu, "current_clock_Hz") / 1000000; +} + + +RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu) +{ + return rtMpSolarisGetFrequency(idCpu, "clock_MHz"); +} + + +#if defined(RT_ARCH_SPARC) || defined(RT_ARCH_SPARC64) +RTDECL(RTCPUID) RTMpCpuId(void) +{ + /** @todo implement RTMpCpuId on solaris/r3! */ + return NIL_RTCPUID; +} +#endif + + +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) +{ + return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1; +} + + +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) +{ + return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID; +} + + +RTDECL(RTCPUID) RTMpGetMaxCpuId(void) +{ + return RTMpGetCount() - 1; +} + + +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + return idCpu != NIL_RTCPUID + && idCpu < (RTCPUID)RTMpGetCount(); +} + + +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) +{ + int iStatus = p_online(idCpu, P_STATUS); + return iStatus == P_ONLINE + || iStatus == P_NOINTR; +} + + +RTDECL(bool) RTMpIsCpuPresent(RTCPUID idCpu) +{ + int iStatus = p_online(idCpu, P_STATUS); + return iStatus != -1; +} + + +RTDECL(RTCPUID) RTMpGetCount(void) +{ + /* + * Solaris has sysconf. + */ + int cCpus = sysconf(_SC_NPROCESSORS_MAX); + if (cCpus < 0) + cCpus = sysconf(_SC_NPROCESSORS_CONF); + return cCpus; +} + + +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet) +{ + RTCpuSetEmpty(pSet); + int idCpu = RTMpGetCount(); + while (idCpu-- > 0) + RTCpuSetAdd(pSet, idCpu); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetOnlineCount(void) +{ + /* + * Solaris has sysconf. + */ + return sysconf(_SC_NPROCESSORS_ONLN); +} + + +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) +{ + RTCpuSetEmpty(pSet); + RTCPUID cCpus = RTMpGetCount(); + for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++) + if (RTMpIsCpuOnline(idCpu)) + RTCpuSetAdd(pSet, idCpu); + return pSet; +} + + +RTDECL(PRTCPUSET) RTMpGetPresentSet(PRTCPUSET pSet) +{ +#ifdef RT_STRICT + RTCPUID cCpusPresent = 0; +#endif + RTCpuSetEmpty(pSet); + RTCPUID cCpus = RTMpGetCount(); + for (RTCPUID idCpu = 0; idCpu < cCpus; idCpu++) + if (RTMpIsCpuPresent(idCpu)) + { + RTCpuSetAdd(pSet, idCpu); +#ifdef RT_STRICT + cCpusPresent++; +#endif + } + Assert(cCpusPresent == RTMpGetPresentCount()); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetPresentCount(void) +{ + /* + * Solaris has sysconf. + */ + return sysconf(_SC_NPROCESSORS_CONF); +} + + +RTDECL(RTCPUID) RTMpGetPresentCoreCount(void) +{ + return RTMpGetCoreCount(); +} + + +RTDECL(RTCPUID) RTMpGetCoreCount(void) +{ + int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */); + if (RT_SUCCESS(rc)) + return g_cCores; + + return 0; +} + + +RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void) +{ + RTCPUID uOnlineCores = 0; + int rc = RTOnceEx(&g_MpSolarisOnce, rtMpSolarisOnce, rtMpSolarisCleanUp, NULL /* pvUser */); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectEnter(&g_MpSolarisCritSect); + AssertRC(rc); + + /* + * For each core in the system, count how many are currently online. + */ + for (RTCPUID j = 0; j < g_cCores; j++) + { + uint64_t u64CoreId = g_pu64CoreIds[j]; + for (RTCPUID idCpu = 0; idCpu < g_capCpuInfo; idCpu++) + { + rc = kstat_read(g_pKsCtl, g_papCpuInfo[idCpu], 0); + AssertReturn(rc != -1, 0 /* rc */); + uint64_t u64ThreadCoreId = rtMpSolarisGetCoreId(idCpu); + if (u64ThreadCoreId == u64CoreId) + { + kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(g_papCpuInfo[idCpu], (char *)"state"); + Assert(pStat->data_type == KSTAT_DATA_CHAR); + if( !RTStrNCmp(pStat->value.c, PS_ONLINE, sizeof(PS_ONLINE) - 1) + || !RTStrNCmp(pStat->value.c, PS_NOINTR, sizeof(PS_NOINTR) - 1)) + { + uOnlineCores++; + break; /* Move to the next core. We have at least 1 hyperthread online in the current core. */ + } + } + } + } + + RTCritSectLeave(&g_MpSolarisCritSect); + } + + return uOnlineCores; +} + diff --git a/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp b/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp new file mode 100644 index 00000000..3ffa0d21 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/rtProcInitExePath-solaris.cpp @@ -0,0 +1,71 @@ +/* $Id: rtProcInitExePath-solaris.cpp $ */ +/** @file + * IPRT - rtProcInitName, Solaris. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP RTLOGGROUP_PROCESS +#include <unistd.h> +#include <errno.h> + +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/path.h> +#include "internal/process.h" +#include "internal/path.h" + + +DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath) +{ + /* + * Read the /proc/<pid>/path/a.out link, convert to native and return it. + */ + char szProcFile[80]; + RTStrPrintf(szProcFile, sizeof(szProcFile), "/proc/%ld/path/a.out", (long)getpid()); + int cchLink = readlink(szProcFile, pszPath, cchPath - 1); + if (cchLink > 0 && (size_t)cchLink <= cchPath - 1) + { + pszPath[cchLink] = '\0'; + + char const *pszTmp; + int rc = rtPathFromNative(&pszTmp, pszPath, NULL); + AssertMsgRCReturn(rc, ("rc=%Rrc pszLink=\"%s\"\nhex: %.*Rhxs\n", rc, pszPath, cchLink, pszPath), rc); + if (pszTmp != pszPath) + { + rc = RTStrCopy(pszPath, cchPath, pszTmp); + rtPathFreeIprt(pszTmp, pszPath); + } + return rc; + } + + int err = errno; + int rc = RTErrConvertFromErrno(err); + AssertMsgFailed(("rc=%Rrc err=%d cchLink=%d\n", rc, err, cchLink)); + return rc; +} + diff --git a/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp b/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp new file mode 100644 index 00000000..db1368d5 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/systemmem-solaris.cpp @@ -0,0 +1,172 @@ +/* $Id: systemmem-solaris.cpp $ */ +/** @file + * IPRT - RTSystemQueryTotalRam, Solaris ring-3. + */ + +/* + * Copyright (C) 2012-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/system.h> +#include "internal/iprt.h" + +#include <iprt/errcore.h> +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/once.h> +#include <iprt/param.h> + +#include <errno.h> +#include <kstat.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Initialize globals once. */ +static RTONCE g_rtSysMemSolInitOnce = RTONCE_INITIALIZER; +/** Critical section serializing access to g_pKStatCtl and the other handles. */ +static RTCRITSECT g_rtSysMemSolCritSect; +/** The kstate control handle. */ +static kstat_ctl_t *g_pKStatCtl = NULL; +/** The unix.system_pages handle. */ +static kstat_t *g_pUnixSysPages = NULL; +/** The zfs.archstats handle. */ +static kstat_t *g_pZfsArcStats = NULL; + + +/** @callback_method_impl{FNRTONCE} */ +static DECLCALLBACK(int) rtSysMemSolInit(void *pvUser) +{ + int rc = RTCritSectInit(&g_rtSysMemSolCritSect); + if (RT_SUCCESS(rc)) + { + g_pKStatCtl = kstat_open(); + if (g_pKStatCtl) + { + g_pUnixSysPages = kstat_lookup(g_pKStatCtl, (char *)"unix", 0, (char *)"system_pages"); + if (g_pUnixSysPages) + { + g_pZfsArcStats = kstat_lookup(g_pKStatCtl, (char *)"zfs", 0, (char *)"arcstats"); /* allow NULL */ + return VINF_SUCCESS; + } + + rc = RTErrConvertFromErrno(errno); + kstat_close(g_pKStatCtl); + g_pKStatCtl = NULL; + } + else + rc = RTErrConvertFromErrno(errno); + } + return rc; +} + + +/** @callback_method_impl{FNRTONCECLEANUP} */ +static DECLCALLBACK(void) rtSysMemSolCleanUp(void *pvUser, bool fLazyCleanUpOk) +{ + RTCritSectDelete(&g_rtSysMemSolCritSect); + + kstat_close(g_pKStatCtl); + g_pKStatCtl = NULL; + g_pUnixSysPages = NULL; + g_pZfsArcStats = NULL; + + NOREF(pvUser); NOREF(fLazyCleanUpOk); +} + + + +RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + int rc = RTOnceEx(&g_rtSysMemSolInitOnce, rtSysMemSolInit, rtSysMemSolCleanUp, NULL); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectEnter(&g_rtSysMemSolCritSect); + if (RT_SUCCESS(rc)) + { + if (kstat_read(g_pKStatCtl, g_pUnixSysPages, NULL) != -1) + { + kstat_named_t *pData = (kstat_named_t *)kstat_data_lookup(g_pUnixSysPages, (char *)"physmem"); + if (pData) + *pcb = (uint64_t)pData->value.ul * PAGE_SIZE; + else + rc = RTErrConvertFromErrno(errno); + } + else + rc = RTErrConvertFromErrno(errno); + RTCritSectLeave(&g_rtSysMemSolCritSect); + } + } + return rc; +} + + +RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb) +{ + AssertPtrReturn(pcb, VERR_INVALID_POINTER); + + int rc = RTOnceEx(&g_rtSysMemSolInitOnce, rtSysMemSolInit, rtSysMemSolCleanUp, NULL); + if (RT_SUCCESS(rc)) + { + rc = RTCritSectEnter(&g_rtSysMemSolCritSect); + if (RT_SUCCESS(rc)) + { + if (kstat_read(g_pKStatCtl, g_pUnixSysPages, NULL) != -1) + { + kstat_named_t *pData = (kstat_named_t *)kstat_data_lookup(g_pUnixSysPages, (char *)"freemem"); + if (pData) + { + *pcb = (uint64_t)pData->value.ul * PAGE_SIZE; + + /* + * Adjust for ZFS greedyness if possible. + * (c_min is the target minimum size of the cache, it is not + * an absolute minimum.) + */ + if ( g_pZfsArcStats + && kstat_read(g_pKStatCtl, g_pZfsArcStats, NULL) != -1) + { + kstat_named_t *pCurSize = (kstat_named_t *)kstat_data_lookup(g_pZfsArcStats, (char *)"size"); + kstat_named_t *pMinSize = (kstat_named_t *)kstat_data_lookup(g_pZfsArcStats, (char *)"c_min"); + if ( pCurSize + && pMinSize + && pCurSize->value.ul > pMinSize->value.ul) + { + *pcb += pCurSize->value.ul - pMinSize->value.ul; + } + } + } + else + rc = RTErrConvertFromErrno(errno); + } + + RTCritSectLeave(&g_rtSysMemSolCritSect); + } + } + return rc; +} + diff --git a/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp b/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp new file mode 100644 index 00000000..f75dab84 --- /dev/null +++ b/src/VBox/Runtime/r3/solaris/thread-affinity-solaris.cpp @@ -0,0 +1,94 @@ +/* $Id: thread-affinity-solaris.cpp $ */ +/** @file + * IPRT - Thread Affinity, Solaris ring-3 implementation. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/thread.h> +#include "internal/iprt.h" + +#include <iprt/assert.h> +#include <iprt/cpuset.h> +#include <iprt/err.h> +#include <iprt/mp.h> + +#include <sys/types.h> +#include <sys/processor.h> +#include <sys/procset.h> +#include <unistd.h> +#include <errno.h> + + +/* Note! The current implementation can only bind to a single CPU. */ + + +RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet) +{ + int rc; + if (pCpuSet == NULL) + rc = processor_bind(P_LWPID, P_MYID, PBIND_NONE, NULL); + else + { + RTCPUSET PresentSet; + int cCpusInSet = RTCpuSetCount(pCpuSet); + if (cCpusInSet == 1) + { + unsigned iCpu = 0; + while ( iCpu < RTCPUSET_MAX_CPUS + && !RTCpuSetIsMemberByIndex(pCpuSet, iCpu)) + iCpu++; + rc = processor_bind(P_LWPID, P_MYID, iCpu, NULL); + } + else if ( cCpusInSet == RTCPUSET_MAX_CPUS + || RTCpuSetIsEqual(pCpuSet, RTMpGetPresentSet(&PresentSet))) + rc = processor_bind(P_LWPID, P_MYID, PBIND_NONE, NULL); + else + return VERR_NOT_SUPPORTED; + } + if (!rc) + return VINF_SUCCESS; + return RTErrConvertFromErrno(errno); +} + + +RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet) +{ + processorid_t iOldCpu; + int rc = processor_bind(P_LWPID, P_MYID, PBIND_QUERY, &iOldCpu); + if (rc) + return RTErrConvertFromErrno(errno); + if (iOldCpu == PBIND_NONE) + RTMpGetPresentSet(pCpuSet); + else + { + RTCpuSetEmpty(pCpuSet); + if (RTCpuSetAdd(pCpuSet, iOldCpu) != 0) + return VERR_INTERNAL_ERROR_5; + } + return VINF_SUCCESS; +} + |