/******************************************************************************* * Copyright (c) 2009, 2023 IBM Corp. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * https://www.eclipse.org/legal/epl-2.0/ * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Ian Craggs - initial API and implementation and/or initial documentation *******************************************************************************/ #include "StackTrace.h" #include "Log.h" #include "LinkedList.h" #include "Clients.h" #include "Thread.h" #include #include #include #if defined(_WIN32) || defined(_WIN64) #define snprintf _snprintf #endif /*BE def STACKENTRY { n32 ptr STRING open "name" n32 dec "line" } defList(STACKENTRY) BE*/ #define MAX_STACK_DEPTH 50 #define MAX_FUNCTION_NAME_LENGTH 30 #define MAX_THREADS 255 typedef struct { thread_id_type threadid; char name[MAX_FUNCTION_NAME_LENGTH]; int line; } stackEntry; typedef struct { thread_id_type id; int maxdepth; int current_depth; stackEntry callstack[MAX_STACK_DEPTH]; } threadEntry; #include "StackTrace.h" #if !defined(NOSTACKTRACE) static int thread_count = 0; static threadEntry threads[MAX_THREADS]; static threadEntry *my_thread = NULL; #if defined(_WIN32) || defined(_WIN64) mutex_type stack_mutex; #else static pthread_mutex_t stack_mutex_store = PTHREAD_MUTEX_INITIALIZER; static mutex_type stack_mutex = &stack_mutex_store; #endif int setStack(int create); int setStack(int create) { int i = -1; thread_id_type curid = Paho_thread_getid(); my_thread = NULL; for (i = 0; i < MAX_THREADS && i < thread_count; ++i) { if (threads[i].id == curid) { my_thread = &threads[i]; break; } } if (my_thread == NULL && create && thread_count < MAX_THREADS) { my_thread = &threads[thread_count]; my_thread->id = curid; my_thread->maxdepth = 0; my_thread->current_depth = 0; ++thread_count; } return my_thread != NULL; /* good == 1 */ } void StackTrace_entry(const char* name, int line, enum LOG_LEVELS trace_level) { Paho_thread_lock_mutex(stack_mutex); if (!setStack(1)) goto exit; if (trace_level != -1) Log_stackTrace(trace_level, 9, my_thread->id, my_thread->current_depth, name, line, NULL); strncpy(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1); my_thread->callstack[(my_thread->current_depth)++].line = line; if (my_thread->current_depth > my_thread->maxdepth) my_thread->maxdepth = my_thread->current_depth; if (my_thread->current_depth >= MAX_STACK_DEPTH) Log(LOG_FATAL, -1, "Max stack depth exceeded"); exit: Paho_thread_unlock_mutex(stack_mutex); } void StackTrace_exit(const char* name, int line, void* rc, enum LOG_LEVELS trace_level) { Paho_thread_lock_mutex(stack_mutex); if (!setStack(0)) goto exit; if (--(my_thread->current_depth) < 0) Log(LOG_FATAL, -1, "Minimum stack depth exceeded for thread %lu", my_thread->id); if (strncmp(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1) != 0) Log(LOG_FATAL, -1, "Stack mismatch. Entry:%s Exit:%s\n", my_thread->callstack[my_thread->current_depth].name, name); if (trace_level != -1) { if (rc == NULL) Log_stackTrace(trace_level, 10, my_thread->id, my_thread->current_depth, name, line, NULL); else Log_stackTrace(trace_level, 11, my_thread->id, my_thread->current_depth, name, line, (int*)rc); } exit: Paho_thread_unlock_mutex(stack_mutex); } void StackTrace_printStack(FILE* dest) { FILE* file = stdout; int t = 0; if (dest) file = dest; for (t = 0; t < thread_count; ++t) { threadEntry *cur_thread = &threads[t]; if (cur_thread->id > 0) { int i = cur_thread->current_depth - 1; fprintf(file, "=========== Start of stack trace for thread %lu ==========\n", (unsigned long)cur_thread->id); if (i >= 0) { fprintf(file, "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line); while (--i >= 0) fprintf(file, " at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line); } fprintf(file, "=========== End of stack trace for thread %lu ==========\n\n", (unsigned long)cur_thread->id); } } if (file != stdout && file != stderr && file != NULL) fclose(file); } char* StackTrace_get(thread_id_type threadid, char* buf, int bufsize) { int t = 0; if (bufsize < 100) goto exit; buf[0] = '\0'; for (t = 0; t < thread_count; ++t) { threadEntry *cur_thread = &threads[t]; if (cur_thread->id == threadid) { int i = cur_thread->current_depth - 1; int curpos = 0; if (i >= 0) { curpos += snprintf(&buf[curpos], bufsize - curpos -1, "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line); while (--i >= 0) curpos += snprintf(&buf[curpos], bufsize - curpos -1, /* lgtm [cpp/overflowing-snprintf] */ " at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line); if (buf[--curpos] == '\n') buf[curpos] = '\0'; } break; } } exit: return buf; } #endif