* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* gstrace_infra.cpp
*
*
* IDENTIFICATION
* src/lib/gstrace/common/gstrace_infra.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <errno.h>
#include "port.h"
#include "securec.h"
#include "securec_check.h"
#include "gstrace/funcs.comps.h"
#include "gstrace/gstrace_infra_int.h"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define isDigital(_ch) (((_ch) >= '0') && ((_ch) <= '9'))
#define GS_TRC_CFG_MASK_OPTION (1 << 0)
#define GS_TRC_MASK_MAX_LEN 8192
#define GS_TRC_WAIT_IN_US 1000
#define TRC_BUF_SHARED_MEM_NAME "/gstrace_trace_buffer"
#define TRC_CFG_SHARED_MEM_NAME "/gstrace_trace_cfg"
#define TRC_SHARED_MEM_NAME_MAX 256
#define COMPONENT_IDX(traceId) (((traceId)&GS_TRC_COMP_MASK) >> GS_TRC_COMP_SHIFT)
#define FUNCTION_IDX(traceId) (((traceId)&GS_TRC_FUNC_MASK) >> GS_TRC_FUNC_SHIFT)
#define isTraceEnabled(pTrcCxt) ((pTrcCxt)->pTrcCfg != NULL && (pTrcCxt)->pTrcCfg->bEnabled)
#define FILL_TRACE_HADER(slotAddress, seq, numSlot) \
do { \
\
trace_slot_head* pHdr = (trace_slot_head*)(slotAddress); \
\
pHdr->hdr_sequence = seq; \
pHdr->hdr_magic_number = SLOT_AREAD_HEADER_MAGIC_NO; \
pHdr->num_slots_in_area = numSlot; \
} while (0)
#define FILL_TRACE_RECORD(pBuf, _type, _probe, _rec_id, data_len) \
do { \
trace_record* pRec = (trace_record*)(pBuf); \
struct timeval tp; \
errno_t rc = memset_s(&tp, sizeof(tp), 0, sizeof(tp)); \
securec_check(rc, "\0", "\0"); \
\
gettimeofday(&tp, NULL); \
pRec->timestamp = tp.tv_sec * USECS_PER_SEC + tp.tv_usec; \
pRec->pid = getpid(); \
pRec->tid = syscall(__NR_gettid); \
pRec->type = _type; \
pRec->probe = _probe; \
pRec->rec_id = _rec_id; \
\
\
pRec->user_data_len = (data_len) + sizeof(trace_data_fmt) + sizeof(size_t); \
} while (0)
#define FILL_TRACE_PROBEDATA(pBuf, totalSize, fmt_type, data_len, pData) \
do { \
if ((totalSize) >= (int)(sizeof(trace_data_fmt) + sizeof(size_t) + (data_len))) { \
int ret; \
char* pDataInBuf = (char*)(pBuf); \
\
ret = memcpy_s(pDataInBuf, totalSize, &(fmt_type), sizeof(trace_data_fmt)); \
securec_check(ret, "\0", "\0"); \
pDataInBuf += sizeof(trace_data_fmt); \
totalSize -= sizeof(trace_data_fmt); \
\
ret = memcpy_s(pDataInBuf, totalSize, &(data_len), sizeof(size_t)); \
securec_check(ret, "\0", "\0"); \
pDataInBuf += sizeof(size_t); \
totalSize -= sizeof(size_t); \
\
ret = memcpy_s(pDataInBuf, totalSize, pData, data_len); \
securec_check(ret, "\0", "\0"); \
} \
} while (0)
static __thread int* gtCurTryCounter = NULL;
static trace_context* getTraceContext()
{
static trace_context TRC_GLOBALS;
return &TRC_GLOBALS;
}
static pthread_mutex_t* getTraceFileMutex()
{
static pthread_mutex_t fileMutex;
return &fileMutex;
}
static uint64_t roundToNearestPowerOfTwo(uint64_t initialSize)
{
uint64_t roundedSize = 0;
if (initialSize > 0) {
roundedSize = 1;
while (initialSize > 1) {
roundedSize <<= 1;
initialSize >>= 1;
}
}
return roundedSize;
}
static void addValToBitMap(uint8_t* pBitMap, uint32_t value)
{
pBitMap[value / GS_TRC_NUM_BITS] |= (1 << (value % GS_TRC_NUM_BITS));
}
bool isNumeric(const char* str)
{
size_t i = 0;
while (str[i] != '\0') {
if (0 == isDigital(str[i])) {
return false;
}
i++;
}
return true;
}
static bool parseFuncMask(char* str_func, trace_mask* trc_mask)
{
* The specified components need doing parse only when funcs option
* is "ALL" or at least one func is given by its ID (not trace id)
* way in which component information is lacking.
*/
bool bComps = false;
char* tok = NULL;
if (0 == strcmp(str_func, "ALL")) {
bComps = true;
for (uint32_t v = 0; v < GS_TRC_FUNC_MAX; v++) {
addValToBitMap(trc_mask->func_bitmap, v);
}
} else {
char* outer_ptr = NULL;
tok = strtok_s(str_func, ",", &outer_ptr);
while (tok != NULL) {
uint32_t func, comp;
if (!isNumeric(tok)) {
func = getFunctionIdxByName(tok, &comp);
if (comp > 0) {
addValToBitMap(trc_mask->comp_bitmap, comp);
}
} else {
func = atoi(tok);
bComps = true;
}
if (func > 0 && func < GS_TRC_FUNC_MAX) {
addValToBitMap(trc_mask->func_bitmap, func);
}
tok = strtok_s(NULL, ",", &outer_ptr);
}
}
return bComps;
}
static void parseCompMask(char* str_comp, trace_mask* trc_mask)
{
char* tok = NULL;
if (0 == strcmp(str_comp, "ALL")) {
for (uint32_t v = 0; v < GS_TRC_COMP_MAX; v++) {
addValToBitMap(trc_mask->comp_bitmap, v);
}
} else {
char* outer_ptr = NULL;
tok = strtok_s(str_comp, ",", &outer_ptr);
while (tok != NULL) {
uint32_t comp;
if (!isNumeric(tok)) {
comp = getCompIdxByName(tok);
} else {
comp = atoi(tok);
}
if (comp > 0 && comp < GS_TRC_COMP_MAX) {
addValToBitMap(trc_mask->comp_bitmap, comp);
}
tok = strtok_s(NULL, ",", &outer_ptr);
}
}
}
static void parseMask(const char* mask, trace_mask* trc_mask)
{
char str_mask[GS_TRC_MASK_MAX_LEN + 1] = {'\0'};
char* str_comp = NULL;
char* str_func = NULL;
char* outer_ptr = NULL;
int ret = snprintf_s(str_mask, GS_TRC_MASK_MAX_LEN + 1, GS_TRC_MASK_MAX_LEN, "%s", mask);
securec_check_ss_c(ret, "\0", "\0");
* tokenize the mask string and populate our bitmaps.
*
* mask is of form comp.func, for example, 1.7,12 means
* trace func calls of component 1 and functions 7 and 12
*/
str_comp = strtok_s(str_mask, ".", &outer_ptr);
str_func = strtok_s(NULL, ".", &outer_ptr);
if (str_func != NULL) {
bool bComps = parseFuncMask(str_func, trc_mask);
if (str_comp != NULL && bComps) {
parseCompMask(str_comp, trc_mask);
}
}
}
static void getSharedMemName(char* name, size_t len_name, const char* prefix, int pid)
{
int ret = -1;
if (len_name >= 1) {
ret = snprintf_s(name, len_name, (len_name - 1), "%s_%d", prefix, pid);
}
securec_check_ss_c(ret, "\0", "\0");
}
* Attach to either the trace config shared memory buffer or the trace
* buffer based on the Option passed, and then map the memory into the
* process address space.
* This function will NOT create the shared memory if it does not exist.
*/
static trace_msg_code attachTraceSharedMemLow(void** pTrcMem, const char* sMemName, uint64_t size)
{
int fd;
if (size == 0) {
return TRACE_BUFFER_SIZE_ERR;
}
fd = shm_open(sMemName, O_RDWR, S_IRWXU);
if (fd == -1) {
return TRACE_OPEN_SHARE_MEMORY_ERR;
}
*pTrcMem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if (*pTrcMem == MAP_FAILED) {
return TRACE_MMAP_ERR;
}
return TRACE_OK;
}
static trace_msg_code detachTraceSharedMemLow(trace_infra* pTrcMem)
{
if (pTrcMem != NULL && munmap(pTrcMem, pTrcMem->total_size) == -1) {
return TRACE_MUNMAP_ERR;
}
return TRACE_OK;
}
static trace_msg_code attachTraceBufferSharedMem(int key)
{
trace_msg_code rc;
void* ptr = NULL;
char sBufMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
uint64_t bufferSize;
trace_context* pTrcCxt = getTraceContext();
bufferSize = pTrcCxt->pTrcCfg->size;
getSharedMemName(sBufMemName, sizeof(sBufMemName), TRC_BUF_SHARED_MEM_NAME, key);
rc = attachTraceSharedMemLow(&ptr, sBufMemName, sizeof(trace_infra) + bufferSize);
if (rc == TRACE_OK) {
pTrcCxt->pTrcInfra = (trace_infra*)ptr;
}
return rc;
}
static trace_msg_code attachTraceCfgSharedMem(int key)
{
char sCfgMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
trace_context* pTrcCxt = getTraceContext();
trace_msg_code rc;
void* ptr = NULL;
getSharedMemName(sCfgMemName, sizeof(sCfgMemName), TRC_CFG_SHARED_MEM_NAME, key);
rc = attachTraceSharedMemLow(&ptr, sCfgMemName, sizeof(trace_config));
if (rc == TRACE_OK) {
pTrcCxt->pTrcCfg = (trace_config*)ptr;
}
return rc;
}
static void attachTraceBufferIfEnabled()
{
trace_context* pTrcCxt = getTraceContext();
if (pTrcCxt->pTrcInfra == NULL) {
if (attachTraceBufferSharedMem(pTrcCxt->pTrcCfg->key) != TRACE_OK) {
printf("Failed to attach to trace buffer shared mem.\n");
}
}
}
static void detachTraceBufferIfDisabled()
{
trace_context* pTrcCxt = getTraceContext();
(void)detachTraceSharedMemLow(pTrcCxt->pTrcInfra);
pTrcCxt->pTrcInfra = NULL;
pTrcCxt->pTrcCfg->status = TRACE_STATUS_END_STOP;
pTrcCxt->pTrcCfg->bEnabled = false;
}
static bool checkProcess(int port)
{
int ret;
bool procexist = false;
char checkcmd[MAX_PATH_LEN] = {0};
char buf[MAX_PATH_LEN];
FILE* fp = NULL;
ret = snprintf_s(checkcmd, MAX_PATH_LEN, MAX_PATH_LEN - 1, "lsof -i:%d", port);
securec_check_ss_c(ret, "\0", "\0");
fp = popen(checkcmd, "r");
if (fp == NULL) {
printf("popen failed. could not query database process.\n");
return procexist;
}
while (fgets(buf, sizeof(buf), fp) != NULL) {
procexist = true;
break;
}
pclose(fp);
return procexist;
}
void gstrace_destroy(int code, void *arg)
{
trace_context* pTrcCxt = getTraceContext();
if (pTrcCxt->pTrcCfg != NULL) {
char sMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
int key = pTrcCxt->pTrcCfg->key;
pTrcCxt->pTrcCfg->status = TRACE_STATUS_BEGIN_STOP;
while (pTrcCxt->pTrcCfg->status_counter != 0) {
usleep(GS_TRC_WAIT_IN_US);
}
detachTraceBufferIfDisabled();
pTrcCxt->pTrcCfg->key = -1;
pTrcCxt->pTrcCfg->pid = 0;
getSharedMemName(sMemName, sizeof(sMemName), TRC_BUF_SHARED_MEM_NAME, key);
(void)shm_unlink(sMemName);
getSharedMemName(sMemName, sizeof(sMemName), TRC_CFG_SHARED_MEM_NAME, key);
(void)shm_unlink(sMemName);
}
}
static int try_reuse_configshm(const char* sCfgMemName, int identifer)
{
int fdCfg = shm_open(sCfgMemName, O_RDWR | O_EXCL, S_IRWXU);
if (fdCfg == -1) {
return -1;
}
struct stat stat;
if (fstat(fdCfg, &stat) == 0 && stat.st_size == sizeof(trace_config)) {
trace_config* pTrcCfg = (trace_config*)mmap(
NULL, sizeof(trace_config), PROT_READ | PROT_WRITE, MAP_SHARED, fdCfg, 0);
if (pTrcCfg->trc_cfg_magic_no == GS_TRC_CFG_MAGIC_N &&
pTrcCfg->key == (uint32_t)identifer && kill(pTrcCfg->pid, 0) != 0 && errno == ESRCH) {
(void)munmap(pTrcCfg, sizeof(trace_config));
return fdCfg;
}
(void)munmap(pTrcCfg, sizeof(trace_config));
}
close(fdCfg);
return -1;
}
int gstrace_init(int key)
{
trace_context* pTrcCxt = getTraceContext();
if (pTrcCxt->pTrcCfg != NULL) {
return TRACE_COMMON_ERROR;
}
char sCfgMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
void* pTrcCfgAddress = NULL;
const char* hash_str = HASH_TRACE_CONFIG;
getSharedMemName(sCfgMemName, sizeof(sCfgMemName), TRC_CFG_SHARED_MEM_NAME, key);
int fdCfg = shm_open(sCfgMemName, O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
if (fdCfg == -1 && errno == EEXIST) {
fdCfg = try_reuse_configshm(sCfgMemName, key);
}
if (fdCfg == -1) {
return TRACE_COMMON_ERROR;
}
if (ftruncate(fdCfg, sizeof(trace_config)) == -1) {
close(fdCfg);
return TRACE_COMMON_ERROR;
}
pTrcCfgAddress = mmap(NULL, sizeof(trace_config), PROT_READ | PROT_WRITE, MAP_SHARED, fdCfg, 0);
close(fdCfg);
if (pTrcCfgAddress == MAP_FAILED) {
return TRACE_COMMON_ERROR;
}
pTrcCxt->pTrcCfg = (trace_config*)pTrcCfgAddress;
int ret = memset_s(pTrcCxt->pTrcCfg, sizeof(trace_config), 0, sizeof(trace_config));
securec_check(ret, "\0", "\0");
pTrcCxt->pTrcCfg->trc_cfg_magic_no = GS_TRC_CFG_MAGIC_N;
pTrcCxt->pTrcCfg->key = key;
pTrcCxt->pTrcCfg->pid = getpid();
pTrcCxt->pTrcCfg->bEnabled = false;
pTrcCxt->pTrcCfg->version = TRACE_VERSION;
ret = memcpy_s(
pTrcCxt->pTrcCfg->hash_trace_config_file, LENGTH_TRACE_CONFIG_HASH, hash_str, LENGTH_TRACE_CONFIG_HASH);
securec_check(ret, "\0", "\0");
(void)on_exit(gstrace_destroy, NULL);
return TRACE_OK;
}
static trace_msg_code createAndAttachTraceBuffer(int key, uint64_t bufferSize)
{
trace_context* pTrcCxt = getTraceContext();
char sBufMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
int ret;
getSharedMemName(sBufMemName, sizeof(sBufMemName), TRC_BUF_SHARED_MEM_NAME, key);
int fdTrc = shm_open(sBufMemName, O_RDWR | O_CREAT, S_IRWXU);
if (fdTrc == -1) {
return TRACE_OPEN_SHARE_MEMORY_ERR;
}
ret = ftruncate(fdTrc, bufferSize + sizeof(trace_infra));
if (ret == -1) {
close(fdTrc);
return TRACE_TRUNCATE_ERR;
}
void* pTrcBufAddress = mmap(NULL, bufferSize + sizeof(trace_infra), PROT_READ | PROT_WRITE, MAP_SHARED, fdTrc, 0);
close(fdTrc);
if (pTrcBufAddress == MAP_FAILED) {
return TRACE_MMAP_ERR;
}
pTrcCxt->pTrcInfra = (trace_infra*)pTrcBufAddress;
ret = memset_s(pTrcCxt->pTrcInfra, sizeof(trace_infra), 0, sizeof(trace_infra));
securec_check(ret, "\0", "\0");
pTrcCxt->pTrcInfra->g_Counter = 0;
pTrcCxt->pTrcInfra->g_slot_count = 0;
pTrcCxt->pTrcInfra->total_size = bufferSize + sizeof(trace_infra);
pTrcCxt->pTrcInfra->num_slots = bufferSize / SLOT_SIZE;
pTrcCxt->pTrcInfra->slot_index_mask = (bufferSize / SLOT_SIZE) - 1;
return TRACE_OK;
}
* init shared memory buffers
* This function will now create two
* shared memory buffers, one for the trace
* config and one for the trace infra.
*/
trace_msg_code gstrace_start(int key, const char* mask, uint64_t bufferSize, const char* trcFile)
{
trace_msg_code ret;
bool bTrcToFile = (trcFile != NULL);
trace_context* pTrcCxt = getTraceContext();
if (!bTrcToFile && bufferSize <= 0) {
return TRACE_BUFFER_SIZE_ERR;
}
bufferSize = gsAlign(((bufferSize > MIN_BUF_SIZE) ? bufferSize : MIN_BUF_SIZE), SLOT_SIZE);
bufferSize = bTrcToFile ? MIN_BUF_SIZE : roundToNearestPowerOfTwo(bufferSize);
if (!checkProcess(key)) {
return TRACE_PROCESS_NOT_EXIST;
}
if (attachTraceCfgSharedMem(key) != TRACE_OK) {
return TRACE_ATTACH_CFG_SHARE_MEMORY_ERR;
}
if (pTrcCxt->pTrcCfg->bEnabled) {
return TRACE_ALREADY_START;
}
if (mask != NULL) {
trace_mask st_mask = {0};
parseMask(mask, &st_mask);
errno_t rcs = memcpy_s(&pTrcCxt->pTrcCfg->gs_trc_mask, sizeof(trace_mask), &st_mask, sizeof(trace_mask));
securec_check(rcs, "\0", "\0");
pTrcCxt->pTrcCfg->options |= GS_TRC_CFG_MASK_OPTION;
}
ret = createAndAttachTraceBuffer(key, bufferSize);
if (ret == TRACE_OK) {
pTrcCxt->pTrcCfg->size = bufferSize;
pTrcCxt->pTrcCfg->bTrcToFile = bTrcToFile;
if (bTrcToFile) {
int rc = snprintf_s(pTrcCxt->pTrcCfg->filePath, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s", trcFile);
securec_check_ss_c(rc, "\0", "\0");
ret = TRACE_OK;
}
pTrcCxt->pTrcCfg->status = TRACE_STATUS_RECORDING;
pTrcCxt->pTrcCfg->bEnabled = true;
printf("Shared memory buffer has been initialized.\n");
}
return ret;
}
trace_msg_code gstrace_stop(int key)
{
char sBufMemName[TRC_SHARED_MEM_NAME_MAX] = {0};
trace_context* pTrcCxt = getTraceContext();
if (!checkProcess(key)) {
return TRACE_PROCESS_NOT_EXIST;
}
if (attachTraceCfgSharedMem(key) != TRACE_OK) {
return TRACE_ATTACH_CFG_SHARE_MEMORY_ERR;
}
if (!pTrcCxt->pTrcCfg->bEnabled || pTrcCxt->pTrcCfg->status != TRACE_STATUS_RECORDING) {
return TRACE_ALREADY_STOP;
}
pTrcCxt->pTrcCfg->status = TRACE_STATUS_PREPARE_STOP;
getSharedMemName(sBufMemName, sizeof(sBufMemName), TRC_BUF_SHARED_MEM_NAME, key);
while (1) {
if (!pTrcCxt->pTrcCfg->bEnabled) {
break;
} else if (kill(pTrcCxt->pTrcCfg->pid, 0) != 0 && errno == ESRCH) {
* case when kernel stopped in advance.
* Attention: it is unsafty for concurrency among INIT/START/STOP operations now.
*/
pTrcCxt->pTrcInfra = NULL;
pTrcCxt->pTrcCfg->status = TRACE_STATUS_END_STOP;
pTrcCxt->pTrcCfg->bEnabled = false;
break;
}
usleep(GS_TRC_WAIT_IN_US);
}
if (shm_unlink(sBufMemName) == -1) {
perror("shm_unlink");
return TRACE_UNLINK_SHARE_MEMORY_ERR;
}
pTrcCxt->pTrcCfg->size = 0;
pTrcCxt->pTrcCfg->options = 0;
printf("Successfully deleted shared mem and cleared trace configuration.\n");
return TRACE_OK;
}
static bool isTraceIdRequired(const uint32_t rec_id)
{
trace_context* pTrcCxt = getTraceContext();
trace_config* cfg = pTrcCxt->pTrcCfg;
if (cfg->options & GS_TRC_CFG_MASK_OPTION) {
uint32_t comp_idx, func_idx, nword, nbits;
comp_idx = COMPONENT_IDX(rec_id);
nword = comp_idx / GS_TRC_NUM_BITS;
nbits = comp_idx % GS_TRC_NUM_BITS;
if (cfg->gs_trc_mask.comp_bitmap[nword] & (1 << nbits)) {
return true;
}
func_idx = FUNCTION_IDX(rec_id);
nword = func_idx / GS_TRC_NUM_BITS;
nbits = func_idx % GS_TRC_NUM_BITS;
if (cfg->gs_trc_mask.func_bitmap[nword] & (1 << nbits)) {
return true;
}
return false;
}
return true;
}
static uint32_t gsTrcCalcNeededNumOfSlots(size_t len)
{
if (len > 0) {
size_t tSize = len;
tSize += sizeof(trace_slot_head) + sizeof(trace_slot_tail);
return gsAlign(tSize, SLOT_SIZE) / SLOT_SIZE;
} else {
return 0;
}
}
* if pData is a string, it must be null ermainted and the data_len
* must include the null character.
*/
static void do_memory_trace(const trace_type type, const uint32_t probe, const uint32_t rec_id,
const trace_data_fmt fmt_type, const char* pData, size_t data_len)
{
uint64_t sequence, slotIndex;
trace_context* pTrcCxt = getTraceContext();
sequence = __sync_fetch_and_add(&pTrcCxt->pTrcInfra->g_Counter, 1);
uint32_t numSlotsNeeded = (pData == NULL) ? 1 : 1 + gsTrcCalcNeededNumOfSlots(data_len);
if (numSlotsNeeded == 1) {
sequence = __sync_fetch_and_add(&pTrcCxt->pTrcInfra->g_slot_count, 1);
slotIndex = sequence & pTrcCxt->pTrcInfra->slot_index_mask;
} else {
for (;;) {
sequence = __sync_fetch_and_add(&pTrcCxt->pTrcInfra->g_slot_count, numSlotsNeeded);
slotIndex = sequence & pTrcCxt->pTrcInfra->slot_index_mask;
* if the start slot index and the number of slots does not exceed
* the total number of slots in the buffer that means the buffer end
* is not in the middle of slot area (slot area is contiguous).
*/
if ((slotIndex + numSlotsNeeded) <= pTrcCxt->pTrcInfra->num_slots) {
break;
}
}
}
void* slotAddress = (char*)pTrcCxt->pTrcInfra + sizeof(trace_infra) + slotIndex * SLOT_SIZE;
FILL_TRACE_HADER(slotAddress, sequence, numSlotsNeeded);
char* pBuf = (char*)slotAddress + sizeof(trace_slot_head);
FILL_TRACE_RECORD(pBuf, type, probe, rec_id, data_len);
if (pData != NULL) {
int totalSize = numSlotsNeeded * SLOT_SIZE - sizeof(trace_record) - sizeof(trace_slot_head);
pBuf = (char*)slotAddress + sizeof(trace_slot_head) + sizeof(trace_record);
FILL_TRACE_PROBEDATA(pBuf, totalSize, fmt_type, data_len, pData);
}
pBuf = ((char*)slotAddress + (numSlotsNeeded * SLOT_SIZE)) - sizeof(trace_slot_tail);
trace_slot_tail* pTail = (trace_slot_tail*)pBuf;
__sync_synchronize();
pTail->tail_sequence = ~(sequence);
}
static void write_trace_file(const void* pSlot, off_t offset, int size)
{
trace_context* pTrcCxt = getTraceContext();
FILE* fp = NULL;
if (pthread_mutex_lock(getTraceFileMutex()) != 0) {
printf("Failed to lock the output file.\n");
return;
}
if (access(pTrcCxt->pTrcCfg->filePath, F_OK) == -1) {
fp = trace_fopen(pTrcCxt->pTrcCfg->filePath, "a+");
if (fp != NULL) {
if ((fwrite(pTrcCxt->pTrcCfg, sizeof(trace_config), 1, fp) != 1) ||
(fwrite(pTrcCxt->pTrcInfra, sizeof(trace_infra), 1, fp) != 1) || (fwrite(pSlot, size, 1, fp) != 1)) {
printf("Failed to write trace file.\n");
}
}
} else {
fp = trace_fopen(pTrcCxt->pTrcCfg->filePath, "r+");
if (fp != NULL) {
if ((fseek(fp, 0, SEEK_SET) != 0) || (fwrite(pTrcCxt->pTrcCfg, sizeof(trace_config), 1, fp) != 1) ||
(fseek(fp, sizeof(trace_config), SEEK_SET) != 0) ||
(fwrite(pTrcCxt->pTrcInfra, sizeof(trace_infra), 1, fp) != 1) || (fseek(fp, offset, SEEK_SET) != 0) ||
(fwrite(pSlot, size, 1, fp) != 1)) {
printf("Failed to append write trace file.\n");
}
}
}
if (fp != NULL) {
(void)fflush(fp);
(void)trace_fclose(fp);
}
(void)pthread_mutex_unlock(getTraceFileMutex());
}
static void do_file_trace(const trace_type type, const uint32_t probe, const uint32_t rec_id,
const trace_data_fmt fmt_type, const char* pData, size_t data_len)
{
uint64_t sequence;
void* pSlot = NULL;
uint32_t numSlotsNeeded;
off_t offset;
trace_context* pTrcCxt = getTraceContext();
numSlotsNeeded = (pData == NULL) ? 1 : 1 + gsTrcCalcNeededNumOfSlots(data_len);
__sync_fetch_and_add(&pTrcCxt->pTrcInfra->g_Counter, 1);
sequence = __sync_fetch_and_add(&pTrcCxt->pTrcInfra->g_slot_count, numSlotsNeeded);
offset = sizeof(trace_config) + sizeof(trace_infra) + SLOT_SIZE * sequence;
* This size can be explained as "file size" (equal to "buffer size" in
* do_memory_trace) which is expected to be used to calculate the max
* slot number in format function.
*/
pTrcCxt->pTrcCfg->size = 2 * roundToNearestPowerOfTwo((sequence + numSlotsNeeded) * SLOT_SIZE);
int totalSize = SLOT_SIZE * numSlotsNeeded;
pSlot = (void*)malloc(totalSize);
if (pSlot != NULL) {
FILL_TRACE_HADER(pSlot, sequence, numSlotsNeeded);
char* pBuf = (char*)pSlot + sizeof(trace_slot_head);
FILL_TRACE_RECORD(pBuf, type, probe, rec_id, data_len);
if (pData != NULL) {
int bufSize = totalSize - sizeof(trace_slot_head) - sizeof(trace_record);
pBuf = (char*)pSlot + sizeof(trace_slot_head) + sizeof(trace_record);
FILL_TRACE_PROBEDATA(pBuf, bufSize, fmt_type, data_len, pData);
}
pBuf = ((char*)pSlot + (numSlotsNeeded * SLOT_SIZE)) - sizeof(trace_slot_tail);
trace_slot_tail* pTail = (trace_slot_tail*)pBuf;
pTail->tail_sequence = ~(sequence);
write_trace_file(pSlot, offset, totalSize);
free(pSlot);
}
}
static void gstrace_internal(const trace_type type, const uint32_t probe, const uint32_t rec_id,
const trace_data_fmt fmt_type, const char* pData, size_t data_len)
{
trace_context* pTrcCxt = getTraceContext();
* (1) while it is going to destory trace config, do nothing.
* (2) trace is not initialized or activated.
* (3) traceId is not in whitelist.
*/
if (unlikely(isTraceEnabled(pTrcCxt)) && isTraceIdRequired(rec_id)) {
if (likely(pTrcCxt->pTrcCfg->status == TRACE_STATUS_RECORDING)) {
__sync_fetch_and_add(&pTrcCxt->pTrcCfg->status_counter, 1);
* After increasing reference counting, do work if trace status is
* still RECORDING.
*/
if (pTrcCxt->pTrcCfg->status == TRACE_STATUS_RECORDING) {
attachTraceBufferIfEnabled();
if (pTrcCxt->pTrcInfra != NULL) {
if (pTrcCxt->pTrcCfg->bTrcToFile) {
do_file_trace(type, probe, rec_id, fmt_type, pData, data_len);
} else {
do_memory_trace(type, probe, rec_id, fmt_type, pData, data_len);
}
}
}
__sync_fetch_and_sub(&pTrcCxt->pTrcCfg->status_counter, 1);
} else if (pTrcCxt->pTrcCfg->status == TRACE_STATUS_PREPARE_STOP) {
if (pTrcCxt->pTrcCfg->status_counter != 0) {
} else {
if (__sync_bool_compare_and_swap(
&pTrcCxt->pTrcCfg->status, TRACE_STATUS_PREPARE_STOP, TRACE_STATUS_BEGIN_STOP)) {
detachTraceBufferIfDisabled();
}
}
} else {
}
} else {
if (isTraceEnabled(pTrcCxt) && pTrcCxt->pTrcCfg->status == TRACE_STATUS_PREPARE_STOP) {
if (__sync_bool_compare_and_swap(
&pTrcCxt->pTrcCfg->status, TRACE_STATUS_PREPARE_STOP, TRACE_STATUS_BEGIN_STOP)) {
detachTraceBufferIfDisabled();
}
printf("traceId is not in whitelist.\n");
}
}
}
void gstrace_data(
const uint32_t probe, const uint32_t rec_id, const trace_data_fmt fmt_type, const char* pData, size_t data_len)
{
if (data_len <= SLOT_SIZE * (MAX_TRC_SLOTS - 1 - 1)) {
gstrace_internal(TRACE_DATA, probe, rec_id, fmt_type, pData, data_len);
}
}
void gstrace_entry(const uint32_t rec_id)
{
if (gtCurTryCounter != NULL) {
(*gtCurTryCounter)++;
}
gstrace_internal(TRACE_ENTRY, 0, rec_id, TRC_DATA_FMT_NONE, NULL, 0);
}
void gstrace_exit(const uint32_t rec_id)
{
gstrace_internal(TRACE_EXIT, 0, rec_id, TRC_DATA_FMT_NONE, NULL, 0);
if (gtCurTryCounter != NULL) {
(*gtCurTryCounter)--;
}
}
int* gstrace_tryblock_entry(int* newTryCounter)
{
int* oldTryCounter = gtCurTryCounter;
gtCurTryCounter = newTryCounter;
*gtCurTryCounter = 0;
return oldTryCounter;
}
void gstrace_tryblock_exit(bool inCatch, int* oldTryCounter)
{
if (inCatch && gtCurTryCounter != NULL && *gtCurTryCounter != 0) {
* code, try/catch and so on, an special trace id(GS_TRC_ID_TRY_CATCH)
* is used here to record gtCurTryCounter which stands for the number
* of missing EXITs. While parsing the dumped trace file, we can create
* so many EXITs for all unmatched ENTRYs. */
gstrace_internal(TRACE_EXIT, 0, GS_TRC_ID_TRY_CATCH, TRC_DATA_FMT_NONE, NULL, *gtCurTryCounter);
}
gtCurTryCounter = oldTryCounter;
}
static trace_msg_code dump_trace_context(int fd)
{
trace_context* pTrcCxt = getTraceContext();
size_t bytesWritten;
bytesWritten = write(fd, (void*)pTrcCxt->pTrcCfg, sizeof(trace_config));
if (bytesWritten != sizeof(trace_config)) {
return TRACE_WRITE_CFG_HEADER_ERR;
}
bytesWritten = write(fd, (void*)pTrcCxt->pTrcInfra, sizeof(trace_infra));
if (bytesWritten != sizeof(trace_infra)) {
return TRACE_WRITE_BUFFER_HEADER_ERR;
} else {
return TRACE_OK;
}
}
static trace_msg_code dump_trace_buffer(int fd, const char* outPath)
{
trace_context* pTrcCxt = getTraceContext();
size_t bytesToWrite;
char* pBuf = (char*)pTrcCxt->pTrcInfra + sizeof(trace_infra);
bytesToWrite = pTrcCxt->pTrcInfra->total_size - sizeof(trace_infra);
while (bytesToWrite > 0) {
int64_t nbyte = write(fd, (void*)pBuf, bytesToWrite);
if (nbyte < 0) {
break;
}
pBuf += nbyte;
bytesToWrite -= nbyte;
}
if (bytesToWrite != 0) {
return TRACE_WRITE_BUFFER_ERR;
} else {
printf("Shared memory buffer has been dumped to file: %s.\n", outPath);
return TRACE_OK;
}
}
trace_msg_code gstrace_dump(int key, const char* outPath)
{
trace_context* pTrcCxt = getTraceContext();
trace_msg_code ret;
if (!checkProcess(key)) {
return TRACE_PROCESS_NOT_EXIST;
}
if (attachTraceCfgSharedMem(key) != TRACE_OK) {
return TRACE_ATTACH_CFG_SHARE_MEMORY_ERR;
}
if (!pTrcCxt->pTrcCfg->bEnabled) {
return TRACE_DISABLE_ERR;
}
if (attachTraceBufferSharedMem(key) != TRACE_OK) {
return TRACE_ATTACH_BUFFER_SHARE_MEMORY_ERR;
}
int fd = trace_open_filedesc(outPath, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
if (fd == -1) {
return TRACE_OPEN_OUTPUT_FILE_ERR;
}
ret = dump_trace_context(fd);
if (ret == TRACE_OK) {
ret = dump_trace_buffer(fd, outPath);
}
(void)trace_close_filedesc(fd);
return ret;
}
static char* getStatusString(int status)
{
switch (status) {
case TRACE_STATUS_RECORDING:
return "TRACING";
case TRACE_STATUS_PREPARE_STOP:
return "PREPARE STOP";
case TRACE_STATUS_BEGIN_STOP:
return "STOPPING";
case TRACE_STATUS_END_STOP:
default:
break;
}
return "STOPPED";
}
trace_msg_code gstrace_config(int key)
{
int rc;
trace_context* pTrcCxt = getTraceContext();
rc = attachTraceCfgSharedMem(key);
if (rc != TRACE_OK) {
return TRACE_ATTACH_CFG_SHARE_MEMORY_ERR;
}
if (pTrcCxt->pTrcCfg->bEnabled) {
printf("gstrace is enabled\n");
printf("Trace buffer size is %lu bytes\n", pTrcCxt->pTrcCfg->size);
printf("Trace status is %s\n", getStatusString(pTrcCxt->pTrcCfg->status));
printf("Trace status counter is %d\n", pTrcCxt->pTrcCfg->status_counter);
} else {
printf("gstrace is NOT enabled\n");
}
return TRACE_OK;
}
uint32_t getFunctionIdxByName(const char* funcName, uint32_t* comp)
{
uint32_t nComps = sizeof(GS_TRC_COMP_NAMES_BY_COMP) / sizeof(comp_name);
for (uint32_t i = 1; i < nComps; ++i) {
for (uint32_t j = 1; j < GS_TRC_FUNC_NAMES_BY_COMP[i].func_num; ++j) {
if (0 == strcmp(GS_TRC_FUNC_NAMES_BY_COMP[i].func_names[j], funcName)) {
*comp = i;
return j;
}
}
}
*comp = 0;
return 0;
}
uint32_t getCompIdxByName(const char* compName)
{
uint32_t nComps = sizeof(GS_TRC_COMP_NAMES_BY_COMP) / sizeof(comp_name);
for (uint32_t i = 1; i < nComps; ++i) {
if (0 == strcmp(GS_TRC_COMP_NAMES_BY_COMP[i].compName, compName)) {
return i;
}
}
return 0;
}
const char* getCompNameById(uint32_t trc_id)
{
uint32_t nComps = sizeof(GS_TRC_COMP_NAMES_BY_COMP) / sizeof(comp_name);
uint32_t comp_idx = COMPONENT_IDX(trc_id);
for (uint32_t i = 1; i < nComps; ++i) {
if (GS_TRC_COMP_NAMES_BY_COMP[i].comp == comp_idx) {
return GS_TRC_COMP_NAMES_BY_COMP[i].compName;
}
}
return "unknown";
}
const char* getTraceConfigHash(void)
{
return HASH_TRACE_CONFIG;
}
const char* getTraceFunctionName(uint32_t trc_id)
{
uint32_t comp_idx = COMPONENT_IDX(trc_id);
uint32_t func_idx = FUNCTION_IDX(trc_id);
return GS_TRC_FUNC_NAMES_BY_COMP[comp_idx].func_names[func_idx];
}
const char* getTraceTypeString(trace_type type)
{
switch (type) {
case TRACE_ENTRY:
return "ENTRY";
case TRACE_EXIT:
return "EXIT";
case TRACE_DATA:
return "DATA";
default:
return "UNKNOWN TRACE TYPE";
}
}
FILE* trace_fopen(const char* open_file, const char* mode)
{
char file_name[MAX_PATH_LEN];
FILE* fp = NULL;
int ret = snprintf_s(file_name, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s", open_file);
securec_check_ss_c(ret, "\0", "\0");
canonicalize_path(file_name);
fp = fopen(file_name, mode);
if (fp != NULL && chmod(file_name, S_IWUSR | S_IRUSR) != 0) {
fclose(fp);
return NULL;
}
return fp;
}
int trace_fclose(FILE* stream)
{
return (stream != NULL) ? fclose(stream) : 0;
}
int trace_open_filedesc(const char* open_file, int oflag, int mode)
{
char file_name[MAX_PATH_LEN];
int ret = snprintf_s(file_name, MAX_PATH_LEN, MAX_PATH_LEN - 1, "%s", open_file);
securec_check_ss_c(ret, "\0", "\0");
canonicalize_path(file_name);
return open(file_name, oflag, mode);
}
int trace_close_filedesc(int fd)
{
return (fd != -1) ? close(fd) : 0;
}