• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2009, 2022 IBM Corp.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v2.0
6  * and Eclipse Distribution License v1.0 which accompany this distribution.
7  *
8  * The Eclipse Public License is available at
9  *    https://www.eclipse.org/legal/epl-2.0/
10  * and the Eclipse Distribution License is available at
11  *   http://www.eclipse.org/org/documents/edl-v10.php.
12  *
13  * Contributors:
14  *    Ian Craggs - initial API and implementation and/or initial documentation
15  *******************************************************************************/
16 
17 #include "StackTrace.h"
18 #include "Log.h"
19 #include "LinkedList.h"
20 
21 #include "Clients.h"
22 #include "Thread.h"
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 
28 #if defined(_WIN32) || defined(_WIN64)
29 #define snprintf _snprintf
30 #endif
31 
32 /*BE
33 def STACKENTRY
34 {
35 	n32 ptr STRING open "name"
36 	n32 dec "line"
37 }
38 
39 defList(STACKENTRY)
40 BE*/
41 
42 #define MAX_STACK_DEPTH 50
43 #define MAX_FUNCTION_NAME_LENGTH 30
44 #define MAX_THREADS 255
45 
46 typedef struct
47 {
48 	thread_id_type threadid;
49 	char name[MAX_FUNCTION_NAME_LENGTH];
50 	int line;
51 } stackEntry;
52 
53 typedef struct
54 {
55 	thread_id_type id;
56 	int maxdepth;
57 	int current_depth;
58 	stackEntry callstack[MAX_STACK_DEPTH];
59 } threadEntry;
60 
61 #include "StackTrace.h"
62 
63 #if !defined(NOSTACKTRACE)
64 
65 static int thread_count = 0;
66 static threadEntry threads[MAX_THREADS];
67 static threadEntry *my_thread = NULL;
68 
69 #if defined(_WIN32) || defined(_WIN64) || defined(COMPAT_CMSIS)
70 mutex_type stack_mutex;
71 #else
72 static pthread_mutex_t stack_mutex_store = PTHREAD_MUTEX_INITIALIZER;
73 static mutex_type stack_mutex = &stack_mutex_store;
74 #endif
75 
76 
77 int setStack(int create);
78 
79 
setStack(int create)80 int setStack(int create)
81 {
82 	int i = -1;
83 	thread_id_type curid = Thread_getid();
84 
85 	my_thread = NULL;
86 	for (i = 0; i < MAX_THREADS && i < thread_count; ++i)
87 	{
88 		if (threads[i].id == curid)
89 		{
90 			my_thread = &threads[i];
91 			break;
92 		}
93 	}
94 
95 	if (my_thread == NULL && create && thread_count < MAX_THREADS)
96 	{
97 		my_thread = &threads[thread_count];
98 		my_thread->id = curid;
99 		my_thread->maxdepth = 0;
100 		my_thread->current_depth = 0;
101 		++thread_count;
102 	}
103 	return my_thread != NULL; /* good == 1 */
104 }
105 
StackTrace_entry(const char * name,int line,enum LOG_LEVELS trace_level)106 void StackTrace_entry(const char* name, int line, enum LOG_LEVELS trace_level)
107 {
108 	Thread_lock_mutex(stack_mutex);
109 	if (!setStack(1))
110 		goto exit;
111 	if (trace_level != -1)
112 		Log_stackTrace(trace_level, 9, my_thread->id, my_thread->current_depth, name, line, NULL);
113 	strncpy(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1);
114 	my_thread->callstack[(my_thread->current_depth)++].line = line;
115 	if (my_thread->current_depth > my_thread->maxdepth)
116 		my_thread->maxdepth = my_thread->current_depth;
117 	if (my_thread->current_depth >= MAX_STACK_DEPTH)
118 		Log(LOG_FATAL, -1, "Max stack depth exceeded");
119 exit:
120 	Thread_unlock_mutex(stack_mutex);
121 }
122 
123 
StackTrace_exit(const char * name,int line,void * rc,enum LOG_LEVELS trace_level)124 void StackTrace_exit(const char* name, int line, void* rc, enum LOG_LEVELS trace_level)
125 {
126 	Thread_lock_mutex(stack_mutex);
127 	if (!setStack(0))
128 		goto exit;
129 	if (--(my_thread->current_depth) < 0)
130 		Log(LOG_FATAL, -1, "Minimum stack depth exceeded for thread %lu", my_thread->id);
131 	if (strncmp(my_thread->callstack[my_thread->current_depth].name, name, sizeof(my_thread->callstack[0].name)-1) != 0)
132 		Log(LOG_FATAL, -1, "Stack mismatch. Entry:%s Exit:%s\n", my_thread->callstack[my_thread->current_depth].name, name);
133 	if (trace_level != -1)
134 	{
135 		if (rc == NULL)
136 			Log_stackTrace(trace_level, 10, my_thread->id, my_thread->current_depth, name, line, NULL);
137 		else
138 			Log_stackTrace(trace_level, 11, my_thread->id, my_thread->current_depth, name, line, (int*)rc);
139 	}
140 exit:
141 	Thread_unlock_mutex(stack_mutex);
142 }
143 
144 
StackTrace_printStack(FILE * dest)145 void StackTrace_printStack(FILE* dest)
146 {
147 	FILE* file = stdout;
148 	int t = 0;
149 
150 	if (dest)
151 		file = dest;
152 	for (t = 0; t < thread_count; ++t)
153 	{
154 		threadEntry *cur_thread = &threads[t];
155 
156 		if (cur_thread->id > 0)
157 		{
158 			int i = cur_thread->current_depth - 1;
159 
160 			fprintf(file, "=========== Start of stack trace for thread %lu ==========\n", (unsigned long)cur_thread->id);
161 			if (i >= 0)
162 			{
163 				fprintf(file, "%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
164 				while (--i >= 0)
165 					fprintf(file, "   at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
166 			}
167 			fprintf(file, "=========== End of stack trace for thread %lu ==========\n\n", (unsigned long)cur_thread->id);
168 		}
169 	}
170 	if (file != stdout && file != stderr && file != NULL)
171 		fclose(file);
172 }
173 
174 
StackTrace_get(thread_id_type threadid,char * buf,int bufsize)175 char* StackTrace_get(thread_id_type threadid, char* buf, int bufsize)
176 {
177 	int t = 0;
178 
179 	if (bufsize < 100)
180 		goto exit;
181 	buf[0] = '\0';
182 	for (t = 0; t < thread_count; ++t)
183 	{
184 		threadEntry *cur_thread = &threads[t];
185 
186 		if (cur_thread->id == threadid)
187 		{
188 			int i = cur_thread->current_depth - 1;
189 			int curpos = 0;
190 
191 			if (i >= 0)
192 			{
193 				curpos += snprintf(&buf[curpos], bufsize - curpos -1,
194 						"%s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
195 				while (--i >= 0)
196 					curpos += snprintf(&buf[curpos], bufsize - curpos -1, /* lgtm [cpp/overflowing-snprintf] */
197 							"   at %s (%d)\n", cur_thread->callstack[i].name, cur_thread->callstack[i].line);
198 				if (buf[--curpos] == '\n')
199 					buf[curpos] = '\0';
200 			}
201 			break;
202 		}
203 	}
204 exit:
205 	return buf;
206 }
207 
208 #endif
209