Files
openGauss-server/src/gausskernel/cbb/instruments/gs_stack/gs_stack.cpp
2023-06-26 00:05:47 +08:00

744 lines
24 KiB
C++

/*
* Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
*
* 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.
* ---------------------------------------------------------------------------------------
*
* gs_stack.cpp
*
* IDENTIFICATION
* src/gaussdbkernel/cbb/instruments/gs_stack/gs_stack.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "c.h"
#include "funcapi.h"
#include "pgstat.h"
#include <dlfcn.h>
#include <execinfo.h>
#include "access/hash.h"
#include "utils/elf_parser.h"
#include <cxxabi.h>
#define MAX_LEN_KEY 30
#define GS_STACK_HASHTBL "stack hash table"
const int FINISH_STACK = 2;
const int US_PER_WAIT = 5;
const int MAX_SIZE_GS_STACK_HASH = 100;
const char *const g_gs_stack_signal_file = "gs_stack.cmd";
const char *const g_gs_stack_result_file = "gs_stack.ret";
const char *const g_gs_stack_tmp_file = "gs_stack.ret.tmp";
typedef struct {
size_t length_name;
char elfname[MAX_LEN_KEY];
} GsStackKey;
typedef struct {
GsStackKey key;
int header_counter;
int symbol_counter[SYMHDR_NUM];
bool dyn;
Elf64_Sym* sym[SYMHDR_NUM];
char* str_buff[SYMHDR_NUM];
}GsStackEntry;
typedef struct {
ThreadId tid;
pid_t lwtid;
}ThreadInfo;
uint32 GsStackHashFunc(const void *key, Size keysize)
{
const GsStackKey *item = (const GsStackKey *) key;
uint32 val1 = DatumGetUInt32(hash_any((const unsigned char *)item->elfname, item->length_name));
return val1;
}
int GsStackMatch(const void* key1, const void* key2, Size keysize)
{
const GsStackKey* k1 = (const GsStackKey*)key1;
const GsStackKey* k2 = (const GsStackKey*)key2;
if (k1 != NULL && k2 != NULL && k1->length_name == k2->length_name &&
(strncmp(k1->elfname, k2->elfname, k1->length_name) == 0)) {
return 0;
}
return 1;
}
void InitGsStack()
{
// init memory context
if (g_instance.stat_cxt.GsStackContext == NULL) {
g_instance.stat_cxt.GsStackContext = AllocSetContextCreate(g_instance.instance_context,
"GsStackContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
}
HASHCTL ctl;
errno_t rc;
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.hcxt = g_instance.stat_cxt.GsStackContext;
ctl.keysize = sizeof(GsStackKey);
ctl.entrysize = sizeof(GsStackEntry);
ctl.hash = GsStackHashFunc;
ctl.match = GsStackMatch;
g_instance.stat_cxt.GsStackHashTbl = hash_create(GS_STACK_HASHTBL,
MAX_SIZE_GS_STACK_HASH,
&ctl,
HASH_ELEM | HASH_SHRCTX | HASH_FUNCTION | HASH_COMPARE | HASH_NOEXCEPT);
}
uint get_backtrace_refresh_flag(void)
{
return (uint)g_instance.stat_cxt.backtrace_info.refresh_flag;
}
void get_backtrace(void)
{
g_instance.stat_cxt.backtrace_info.number_pc =
backtrace(g_instance.stat_cxt.backtrace_info.backtrace_buff, STACK_PRINT_LIMIT);
g_instance.stat_cxt.backtrace_info.refresh_flag = FINISH_STACK;
}
void print_stack(SIGNAL_ARGS)
{
get_backtrace();
}
bool ready_to_start_backtrace(uint old_refresh_flag)
{
(void)gs_compare_and_swap_u32(&(g_instance.stat_cxt.backtrace_info.refresh_flag), old_refresh_flag, 1);
if (g_instance.stat_cxt.backtrace_info.refresh_flag == 1) {
return true;
}
return false;
}
void init_backtrace_info(void)
{
g_instance.stat_cxt.backtrace_info.refresh_flag = 1;
int ret = memset_s(g_instance.stat_cxt.backtrace_info.backtrace_buff, STACK_PRINT_LIMIT, 0, STACK_PRINT_LIMIT);
securec_check(ret, "\0", "\0");
g_instance.stat_cxt.backtrace_info.number_pc = 0;
}
void free_entry(GsStackEntry *entry)
{
if (entry == NULL) {
return;
}
int i;
for (i = 0; i < entry->header_counter; i++) {
if (entry->sym[i] != NULL) {
pfree(entry->sym[i]);
entry->sym[i] = NULL;
}
if (entry->str_buff[i] != NULL) {
pfree(entry->str_buff[i]);
entry->str_buff[i] = NULL;
}
}
}
bool load_string_table(GsStackEntry *entry, int fd, const Elf64_Shdr* symhdr, const Elf64_Ehdr ehdr, Elf64_Shdr shdr)
{
off_t off;
size_t len_str_buff[SYMHDR_NUM] = {0};
for (int ti = 0; ti < entry->header_counter; ti++) {
/* sh_offset gives the byte offset from the beginning of the file to the first byte in the section */
off = (off_t)(symhdr[ti].sh_offset);
if (lseek(fd, off, SEEK_SET) != off) {
return false;
}
/* symbol table holds a table of fixed-size entries, sh_entsize gives the size in bytes of each entry */
entry->symbol_counter[ti] = (int)(symhdr[ti].sh_size / symhdr[ti].sh_entsize);
/* sh_size gives the section's size in bytes */
entry->sym[ti] = (Elf64_Sym*)palloc0(symhdr[ti].sh_size);
for (int si = 0; si < entry->symbol_counter[ti]; si++) {
if (read(fd, &entry->sym[ti][si], sizeof(Elf64_Sym)) != sizeof(Elf64_Sym)) {
return false;
}
}
/* sh_link holds a section header table index link, for UNIX system V,
The section header index of the associated string table */
off = (off_t)(ehdr.e_shoff + symhdr[ti].sh_link * ehdr.e_shentsize);
if (lseek(fd, off, SEEK_SET) != off) {
return false;
}
if (read(fd, &shdr, sizeof(Elf64_Shdr)) != sizeof(Elf64_Shdr)) {
return false;
}
off = (off_t)(shdr.sh_offset);
if (lseek(fd, off, SEEK_SET) != off) {
return false;
}
len_str_buff[ti] = shdr.sh_size + 1;
entry->str_buff[ti] = (char*)palloc0(len_str_buff[ti]);
if (read(fd, entry->str_buff[ti], shdr.sh_size) == -1) {
return false;
}
}
return true;
}
bool load_symbol_to_hashtbl(GsStackEntry *entry, int fd)
{
const int limit = 1000;
Elf64_Ehdr ehdr;
Elf64_Shdr shdr;
Elf64_Shdr symhdr[SYMHDR_NUM] = {0};
off_t off;
if (read(fd, &ehdr, sizeof(Elf64_Ehdr)) != sizeof(Elf64_Ehdr)) {
return false;
}
entry->dyn = (ehdr.e_type == ET_DYN) ? true : false;
/* e_shnum holds the number of entries in the section header table. */
if (ehdr.e_shnum > limit) {
return false;
}
entry->header_counter = 0;
for (unsigned int i = 0; i < ehdr.e_shnum; i++) {
/* e_shoff holds the section header table's file offset in bytes. */
/* e_shentsize holds a section header's size in bytes. A section header is
one entry in the section header table, all entries are the same size */
off = (off_t)(ehdr.e_shoff + i * ehdr.e_shentsize);
if (pread(fd, &shdr, sizeof(Elf64_Shdr), off) != sizeof(Elf64_Shdr)) {
return false;
}
/* sh_type categorizes the section's contents and semantics. SHT_SYMTAB hold a symbol table. Currently,
an object file may have only one section of each type, but this restriction may be relaxed in the future.
Typically, SHT_SYMTAB provides symbols for link editing, though it may also be used for dynamic linking.
As a complete symbol table, it may contain many symbols unnecessary for dynamic linking.
Consequently, an object file may also contain a SHT_DYNSYM section, which holds a minimal set of dynamic
linking symbols, to save space. */
if ((shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) &&
entry->header_counter < SYMHDR_NUM) {
if (memcpy_s(&(symhdr[entry->header_counter]), sizeof(Elf64_Shdr), &shdr, sizeof(Elf64_Shdr))) {
return false;
}
entry->header_counter++;
}
}
return load_string_table(entry, fd, symhdr, ehdr, shdr);
}
bool find_symbol_entry(const char *filename, HTAB *gs_stack_hashtbl, GsStackEntry **entry)
{
Assert(filename);
GsStackKey key_gs_stack = {0};
bool ret = true;
int n_ret;
bool found = false;
char* base_name = basename((char*)filename);
key_gs_stack.length_name = strlen(base_name);
n_ret = snprintf_s(key_gs_stack.elfname, MAX_LEN_KEY, MAX_LEN_KEY - 1, "%s", base_name);
if (n_ret < 0) {
/* if file name longer than keysize, we just choose first keysize byte from filename as key. */
ereport(LOG, (errmsg("elf name %s is too long.", base_name)));
}
*entry = (GsStackEntry *)hash_search(gs_stack_hashtbl, (const void*)(&key_gs_stack),
HASH_ENTER, &found);
if (!found) {
int fd = open(filename, O_RDONLY);
if (fd != -1) {
ret = load_symbol_to_hashtbl(*entry, fd);
(void)close(fd);
}
if (fd == -1 || ret != true) {
if (ret != true) {
free_entry(*entry);
}
if (hash_search(gs_stack_hashtbl,
(const void*)(&key_gs_stack), HASH_REMOVE, NULL) == NULL) {
ereport(ERROR, (
errcode(ERRCODE_DATA_CORRUPTED), errmsg("gs_stack hash table corrupted")));
}
return false;
}
}
return true;
}
void demangle_one_symbol(const char * symbol_name, char* demangled_symbol_name, size_t len)
{
size_t len_demangle = 0;
int status;
errno_t errorno;
char* demangle_buf = abi::__cxa_demangle(symbol_name, NULL, &len_demangle, &status);
if (status != 0) {
ereport(LOG, (errmsg("can not demangle %s. status is %d", symbol_name, status)));
if (demangle_buf != NULL) {
free(demangle_buf);
demangle_buf = NULL;
}
errorno = memcpy_s(demangled_symbol_name, len, symbol_name, len);
securec_check(errorno, "\0", "\0");
} else {
ereport(LOG, (errmsg("demangle from %s to %s len_demangle is %zu", symbol_name, demangle_buf, len_demangle)));
errorno = memcpy_s(demangled_symbol_name, len, demangle_buf, len_demangle);
free(demangle_buf);
securec_check(errorno, "\0", "\0");
}
}
bool resolve_addr_from_entry(const GsStackEntry *entry, uintptr_t addr, char *buf, size_t len, uint *offset)
{
for (int ti = 0; ti < entry->header_counter; ti++) {
if (entry->sym[ti] == NULL) {
continue;
}
for (int si = 0; si < entry->symbol_counter[ti]; si++) {
if ((ELF64_ST_TYPE(entry->sym[ti][si].st_info) != STT_FUNC)
|| entry->sym[ti][si].st_value > addr
|| (entry->sym[ti][si].st_value + entry->sym[ti][si].st_size) < addr) {
continue;
}
*offset = (uint)(addr - entry->sym[ti][si].st_value);
demangle_one_symbol(entry->str_buff[ti] + entry->sym[ti][si].st_name, buf, len);
return true;
}
}
return false;
}
void addr_to_name_bin(void* pc, const Dl_info dlinfo, StringInfoData* call_stack)
{
bool ret_resolve = false;
GsStackEntry* symbol_entry = NULL;
char tmp_buf[1024] = {0};
uint sym_off;
MemoryContext oldcontext = MemoryContextSwitchTo(g_instance.stat_cxt.GsStackContext);
uintptr_t dl_off = (uintptr_t)pc - (uintptr_t)dlinfo.dli_fbase;
if (find_symbol_entry(dlinfo.dli_fname, g_instance.stat_cxt.GsStackHashTbl, &symbol_entry)) {
uintptr_t addr = symbol_entry->dyn ? dl_off : (uintptr_t)pc;
ret_resolve = resolve_addr_from_entry(symbol_entry, addr, tmp_buf, sizeof(tmp_buf), &sym_off);
(void)MemoryContextSwitchTo(oldcontext);
if (ret_resolve) {
appendStringInfo(call_stack, "%s + 0x%x\n", tmp_buf, sym_off);
} else {
appendStringInfo(call_stack, "%p\n", pc);
}
} else {
(void)MemoryContextSwitchTo(oldcontext);
appendStringInfo(call_stack, "%p\n", pc);
}
}
void addr_to_name_reuse(void* pc, StringInfoData* call_stack)
{
Dl_info dlinfo;
char tmp_buf[1024] = {0};
if (dladdr(pc, &dlinfo) &&
dlinfo.dli_fbase && dlinfo.dli_fname) {
/*
* If a function's name does not in the dynamic symbol table,
* dladdr can't resolve the function's symbol. In this case, we
* should parse the executable file to find nearest function name.
*/
uint sym_off;
if (dlinfo.dli_saddr && dlinfo.dli_sname) {
sym_off = (uint)((uintptr_t)pc - (uintptr_t)dlinfo.dli_saddr);
ereport(LOG, (errmsg("dlinfo.dli_sname %s.", dlinfo.dli_sname)));
demangle_one_symbol(dlinfo.dli_sname, tmp_buf, sizeof(tmp_buf));
appendStringInfo(call_stack, "%s + 0x%x\n", tmp_buf, sym_off);
} else {
addr_to_name_bin(pc, dlinfo, call_stack);
}
} else {
appendStringInfo(call_stack, "%p\n", pc);
}
}
bool ready_to_get_backtrace(void)
{
return (g_instance.stat_cxt.backtrace_info.refresh_flag == FINISH_STACK);
}
void finish_backtrace(void)
{
g_instance.stat_cxt.backtrace_info.refresh_flag = 0;
}
void get_stack(StringInfoData* call_stack)
{
int i;
#ifdef ENABLE_MEMORY_CHECK
/* not show __interceptor_backtrace.part.110<-get_backtrace<-print_stack<-gs_signal_handle
<-gs_res_signal_handler<-__restore_rt */
const int number_pc_show = 6;
#else
/* not show get_backtrace<-print_stack<-gs_signal_handle<-gs_res_signal_handler<-__restore_rt */
const int number_pc_show = 5;
#endif
for (i = number_pc_show; i < g_instance.stat_cxt.backtrace_info.number_pc; i++) {
addr_to_name_reuse(g_instance.stat_cxt.backtrace_info.backtrace_buff[i], call_stack);
}
}
void get_stack_according_to_tid(ThreadId tid, StringInfoData* call_stack)
{
const int MAX_WAIT = 1000;
int i;
if (tid == 0) {
ereport(WARNING,
(errmodule(MOD_GSSTACK),
errcode(ERRCODE_INVALID_STATUS),
(errmsg("can not get backtrace for thread 0."),
errdetail("0 is not a valid thread id."))));
appendStringInfo(call_stack, "invalid thread id\n");
return;
}
(void)LWLockAcquire(GsStackLock, LW_EXCLUSIVE);
PG_TRY();
{
init_backtrace_info();
signal_child(tid, SIGURG, -1);
for (i = 0; i < MAX_WAIT; i++) {
if (ready_to_get_backtrace()) {
break;
}
pg_usleep(US_PER_WAIT);
}
ereport(LOG, (errmsg("wait %d times.", i)));
if (i == MAX_WAIT) {
ereport(WARNING,
(errmodule(MOD_GSSTACK),
errcode(ERRCODE_INVALID_STATUS),
(errmsg("can not get backtrace for thread %lu.", tid),
errdetail("This thread maybe finished,"
"or the signal handler of this thread had not been registed."))));
appendStringInfo(call_stack, "thread %lu not available\n", tid);
} else {
get_stack(call_stack);
}
finish_backtrace();
}
PG_CATCH();
{
LWLockRelease(GsStackLock);
PG_RE_THROW();
}
PG_END_TRY();
LWLockRelease(GsStackLock);
}
bool get_thread_info_from_signal_slot(pid_t lwtid, ThreadInfo** thread_info, int* size)
{
unsigned int loop;
int real_size = 0;
(void)pthread_mutex_lock(&(g_instance.signal_base->slots_lock));
GsSignalSlot* signal_slot = g_instance.signal_base->slots;
*thread_info = (ThreadInfo*)palloc0(sizeof(ThreadInfo) * g_instance.signal_base->slots_size);
unsigned int slot_size = g_instance.signal_base->slots_size;
for (loop = 0; loop < slot_size; loop++) {
if (lwtid == 0) {
if (signal_slot->thread_id != 0) {
(*thread_info)[real_size].tid = signal_slot->thread_id;
(*thread_info)[real_size].lwtid = signal_slot->lwtid;
real_size++;
}
} else {
if (signal_slot->lwtid == lwtid) {
(*thread_info)[0].tid = signal_slot->thread_id;
(*thread_info)[0].lwtid = signal_slot->lwtid;
real_size = 1;
break;
}
}
signal_slot++;
}
(void)pthread_mutex_unlock(&(g_instance.signal_base->slots_lock));
*size = real_size;
return (real_size >= 1);
}
void get_stack_according_to_lwtid(pid_t lwtid, StringInfoData* call_stack)
{
int loop = 0;
ThreadInfo* thread_info = NULL;
int slot_size = 0;
bool found = get_thread_info_from_signal_slot(lwtid, &thread_info, &slot_size);
if (lwtid != 0) {
if (found && thread_info[0].tid > 0) {
appendStringInfo(call_stack, "tid<%lu> lwtid<%d>\n", thread_info[0].tid, lwtid);
get_stack_according_to_tid(thread_info[0].tid, call_stack);
} else {
ereport(ERROR,
(errmodule(MOD_GSSTACK), errcode(ERRCODE_INVALID_STATUS),
(errmsg("can not get backtrace for thread %d.", lwtid),
errdetail("please check if the thread is alive."))));
}
} else {
for (loop = 0; loop < slot_size; loop++) {
if (thread_info[loop].tid != 0) {
appendStringInfo(call_stack,
"Thread %d tid<%lu> lwtid<%d>\n", loop, thread_info[loop].tid, thread_info[loop].lwtid);
get_stack_according_to_tid(thread_info[loop].tid, call_stack);
appendStringInfo(call_stack, "\n");
}
}
}
if (thread_info != NULL) {
pfree(thread_info);
}
}
void check_and_process_gs_stack()
{
struct stat stat_buf;
if (stat(g_gs_stack_signal_file, &stat_buf) != 0) {
return;
}
g_instance.stat_cxt.stack_perf_start = true;
}
void gs_stack_unlink_file(const char* filename)
{
if (filename == NULL) {
return;
}
int ret = unlink(filename);
if (ret != 0 && (errno != ENOENT)) {
ereport(WARNING, (errmsg("unlink %s failed.", filename)));
}
}
void gs_stack_read_signal_file(pid_t* lwtid, pid_t* ctl_pid)
{
size_t ret;
FILE* fd = fopen(g_gs_stack_signal_file, "r");
if (fd == NULL) {
gs_stack_unlink_file(g_gs_stack_signal_file);
ereport(ERROR, (errmsg("open sig file failed.")));
}
ret = fread(ctl_pid, sizeof(pid_t), 1, fd);
if (ret != 1) {
(void)fclose(fd);
gs_stack_unlink_file(g_gs_stack_signal_file);
ereport(ERROR, (errmsg("read ctl pid failed err.")));
}
ret = fread(lwtid, sizeof(pid_t), 1, fd);
if (ret != 1) {
(void)fclose(fd);
gs_stack_unlink_file(g_gs_stack_signal_file);
ereport(ERROR, (errmsg("read ctl lwtid failed err.")));
}
(void)fclose(fd);
gs_stack_unlink_file(g_gs_stack_signal_file);
}
void gs_stack_write_result_file(const char* result, int length, int ctl_pid)
{
int mode = O_WRONLY | O_CREAT | PG_BINARY;
int flags = 0600;
int fd = open(g_gs_stack_tmp_file, mode, flags);
if (fd < 0) {
ereport(LOG, (errmsg("open result file failed.")));
return;
}
ssize_t ret = write(fd, &ctl_pid, sizeof(int));
if (ret == -1) {
ereport(LOG, (errmsg("write ctl pid to file failed.")));
(void)close(fd);
return;
}
ret = write(fd, &length, sizeof(int));
if (ret == -1) {
ereport(LOG, (errmsg("write stack size to file failed.")));
(void)close(fd);
return;
}
ret = write(fd, result, length);
if (ret == -1) {
ereport(LOG, (errmsg("write stack to file failed.")));
(void)close(fd);
return;
}
int iret = fsync(fd);
if (iret != 0) {
ereport(LOG, (errmsg("sync tmp file failed.")));
(void)close(fd);
return;
}
(void)close(fd);
gs_stack_unlink_file(g_gs_stack_result_file);
iret = rename(g_gs_stack_tmp_file, g_gs_stack_result_file);
if (iret != 0) {
ereport(LOG, (errmsg("rename tmp file to stack failed.")));
return;
}
}
void get_stack_and_write_result()
{
pid_t lwtid = 0;
pid_t ctl_pid = 0;
StringInfoData result;
MemoryContext oldcontext = MemoryContextSwitchTo(g_instance.stat_cxt.GsStackContext);
PG_TRY();
{
initStringInfo(&result);
gs_stack_read_signal_file(&lwtid, &ctl_pid);
get_stack_according_to_lwtid(lwtid, &result);
}
PG_CATCH();
{
/* Must reset elog.c's state */
(void)MemoryContextSwitchTo(g_instance.stat_cxt.GsStackContext);
ErrorData* edata = CopyErrorData();
FlushErrorState();
appendStringInfo(&result, "%s", edata->message);
/* release edata */
FreeErrorData(edata);
}
PG_END_TRY();
gs_stack_write_result_file(result.data, result.len, ctl_pid);
FreeStringInfo(&result);
(void)MemoryContextSwitchTo(oldcontext);
}
void print_all_stack()
{
StringInfoData result;
MemoryContext oldcontext = MemoryContextSwitchTo(g_instance.stat_cxt.GsStackContext);
PG_TRY();
{
initStringInfo(&result);
get_stack_according_to_lwtid(0, &result);
}
PG_CATCH();
{
/* Must reset elog.c's state */
ErrorData* edata = CopyErrorData();
FlushErrorState();
appendStringInfo(&result, "%s", edata->message);
/* release edata */
FreeErrorData(edata);
}
PG_END_TRY();
ereport(LOG, (errmsg("Print all thread stack \n%s", result.data)));
FreeStringInfo(&result);
(void)MemoryContextSwitchTo(oldcontext);
}
void save_all_stack_to_tuple(PG_FUNCTION_ARGS)
{
const int attrs = 3;
int i = 1;
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
MemoryContext oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
StringInfoData call_stack;
initStringInfo(&call_stack);
TupleDesc tupdesc = CreateTemplateTupleDesc(attrs, false);
TupleDescInitEntry(tupdesc, (AttrNumber)i++, "tid", INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber)i++, "lwtid", INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber)i, "stack", TEXTOID, -1, 0);
rsinfo->returnMode = SFRM_Materialize;
rsinfo->setResult = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem);
rsinfo->setDesc = BlessTupleDesc(tupdesc);
(void)MemoryContextSwitchTo(oldcontext);
errno_t rc;
Datum values[attrs];
bool nulls[attrs];
ThreadInfo* thread_info = NULL;
int slot_size = 0;
(void)get_thread_info_from_signal_slot(0, &thread_info, &slot_size);
for (i = 0; i <= slot_size; i++) {
if (thread_info[i].tid > 0) {
rc = memset_s(values, sizeof(values), 0, sizeof(values));
securec_check(rc, "\0", "\0");
rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls));
securec_check(rc, "\0", "\0");
values[ARG_0] = Int64GetDatum(thread_info[i].tid);
values[ARG_1] = Int32GetDatum(thread_info[i].lwtid);
get_stack_according_to_tid(thread_info[i].tid, &call_stack);
values[ARG_2] = CStringGetTextDatum(call_stack.data);
tuplestore_putvalues(rsinfo->setResult, tupdesc, values, nulls);
resetStringInfo(&call_stack);
}
}
pfree(thread_info);
FreeStringInfo(&call_stack);
tuplestore_donestoring(rsinfo->setResult);
}
/* print stacks of threads */
Datum gs_stack(PG_FUNCTION_ARGS)
{
if (!superuser() && (!isMonitoradmin(GetUserId())))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("Must be system admin or monitor admin to get stack."))));
ThreadId tid;
StringInfoData call_stack;
initStringInfo(&call_stack);
if (PG_NARGS() == 1) {
tid = (ThreadId)DatumGetUInt64(PG_GETARG_DATUM(0));
get_stack_according_to_tid(tid, &call_stack);
} else {
save_all_stack_to_tuple(fcinfo);
return (Datum)0;
}
PG_RETURN_TEXT_P(cstring_to_text(call_stack.data));
}