Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 45.7% 32 / 0 / 70
Functions: 60.0% 6 / 0 / 10
Branches: 14.7% 5 / 0 / 34

OMCompiler/SimulationRuntime/c/util/omc_stackoverflow.c
Line Branch Exec Source
1 /*
2 * This file belongs to the OpenModelica Run-Time System
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings
5 * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights
6 * reserved.
7 *
8 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE
9 * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY
10 * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S
11 * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL
12 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
13 *
14 * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License
15 * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs:
16 * http://www.openmodelica.org or https://github.com/OpenModelica/ or
17 * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU
18 * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW
19 * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause.
20 *
21 * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY
23 * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF
24 * OSMC-PL.
25 *
26 */
27
28 /* Stack overflow handling */
29
30 #if (defined(__linux__) || defined(__FreeBSD__)) && !defined(_GNU_SOURCE)
31 #define _GNU_SOURCE 1
32 /* for pthread_getattr_np */
33 #endif
34
35 #if defined(__FreeBSD__)
36 #include <pthread_np.h>
37 #endif
38
39 #include "../gc/omc_gc.h"
40 #include "omc_init.h"
41
42 10418 void mmc_clearStacktraceMessages(threadData_t *threadData)
43 {
44 10418 threadData->localRoots[LOCAL_ROOT_STACK_OVERFLOW] = 0;
45 10418 }
46
47 140 int mmc_hasStacktraceMessages(threadData_t *threadData)
48 {
49 140 return threadData->localRoots[LOCAL_ROOT_STACK_OVERFLOW] != 0;
50 }
51
52 #if (defined(__linux__) && defined(__GLIBC__)) || defined(__APPLE__) || defined(__FreeBSD__)
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <signal.h>
58 #include <execinfo.h>
59 #include <sys/time.h>
60 #include <sys/resource.h>
61 #include <assert.h>
62 #include <pthread.h>
63 #include <setjmp.h>
64 #include <unistd.h>
65
66 /* If we find a SIGSEGV near the end of the stack, it is probably due to a stack overflow. 64kB for a function frame seems reasonable. */
67 #define LIMIT_FOR_STACK_OVERFLOW 65536
68
69 static void *trace[MMC_SEGV_TRACE_NFRAMES];
70 static int trace_size;
71 static int trace_size_skip=0; /* First index we should use; that is skip handler, etc */
72 static struct sigaction default_segv_action;
73
74 ✗ void printStacktraceMessages(void) {
75 int i,j=-1,k;
76 ✗ char **messages = backtrace_symbols(trace, trace_size);
77 ✗ fprintf(stderr,"[bt] Execution path:\n");
78 ✗ for (i=trace_size_skip; i<trace_size; ++i)
79 {
80 ✗ if (i<trace_size-1 && trace[i] == trace[i+1]) {
81 ✗ j=j==-1?i:j;
82 ✗ } else if (j>=0) {
83 ✗ k=19-fprintf(stderr,"[bt] #%d..%d", j-trace_size_skip, i-trace_size_skip);
84 ✗ while (k-->0) fprintf(stderr, " ");
85 ✗ fprintf(stderr,"%s\n", messages[i]);
86 j=-1;
87 } else {
88 ✗ k=19-fprintf(stderr,"[bt] #%d ", i-trace_size_skip);
89 ✗ while (k-->0) fprintf(stderr, " ");
90 ✗ fprintf(stderr,"%s\n", messages[i]);
91 }
92 }
93 ✗ if (trace_size==MMC_SEGV_TRACE_NFRAMES) {
94 ✗ fprintf(stderr,"[bt] [...]\n");
95 }
96 ✗ free(messages);
97 ✗ }
98
99 ✗ void mmc_setStacktraceMessages(int numSkip, int numFrames) {
100 ✗ trace_size = 0;
101 ✗ trace_size = backtrace(trace, numFrames == 0 ? MMC_SEGV_TRACE_NFRAMES : numFrames > MMC_SEGV_TRACE_NFRAMES ? MMC_SEGV_TRACE_NFRAMES : numFrames);
102 ✗ trace_size_skip = numSkip;
103 ✗ }
104
105
106 static sigset_t segvset;
107
108 ✗ static void handler(int signo, siginfo_t *si, void *ptr)
109 {
110 int unused __attribute__((unused)), isStackOverflow;
111 ✗ threadData_t *threadData = (threadData_t*)pthread_getspecific(mmc_thread_data_key);
112 ✗ isStackOverflow = si->si_addr < threadData->stackBottom
113 ✗ && (char*)si->si_addr > (char*)threadData->stackBottom - LIMIT_FOR_STACK_OVERFLOW;
114 if (isStackOverflow) {
115 ✗ mmc_setStacktraceMessages(1,0);
116 ✗ sigprocmask(SIG_UNBLOCK, &segvset, NULL);
117 ✗ longjmp(*threadData->mmc_stack_overflow_jumper,1);
118 }
119 /* This backtrace uses very little stack-space, and segmentation faults we always want to print... */
120 ✗ mmc_setStacktraceMessages(1,16);
121 ✗ unused=write(2, "\nLimited backtrace at point of segmentation fault\n", 50);
122 ✗ backtrace_symbols_fd(trace+trace_size_skip, trace_size-trace_size_skip, 2);
123 ✗ sigaction(SIGSEGV, &default_segv_action, 0);
124 ✗ }
125
126 #if defined(__APPLE__)
127 #include <AvailabilityMacros.h>
128 #include <sys/sysctl.h>
129 #endif
130
131 68436 static void* getStackBase(void) {
132 /* Warning: These functions are highly non-portable and are recommended to not be used.
133 * We only tested them on Linux and OSX.
134 * On OSX we get the top of the stack and the size
135 * On Linux we get the bottom, so we don't need the size... YMMV
136 */
137 void* stackBottom;
138 68436 pthread_t self = pthread_self();
139 #if !defined(__APPLE__)
140 68436 size_t size = 0;
141 pthread_attr_t sattr;
142 68436 pthread_attr_init(&sattr);
143 #if defined(__FreeBSD__)
144 assert(0==pthread_attr_get_np(self, &sattr));
145 #else // normal Linux
146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68436 times.
68436 assert(0==pthread_getattr_np(self, &sattr));
147 #endif
148
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 68436 times.
68436 assert(0==pthread_attr_getstack(&sattr, &stackBottom, &size));
149 #if defined(__FreeBSD__)
150 // if we get a 0 stackBottom, try a different strategy
151 if (!stackBottom) {
152 void* stack_addr = NULL;
153 assert(0==pthread_attr_getstackaddr(&sattr, &stack_addr));
154 // if we get a 0 as stack address, all bets are off
155 assert(stack_addr);
156 assert(0==pthread_attr_getstacksize(&sattr, &size));
157 stackBottom = (void*) (((long)stack_addr) - size);
158 }
159 #endif
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68436 times.
68436 assert(stackBottom);
161 68436 pthread_attr_destroy(&sattr);
162 #else
163 void* stack_addr = pthread_get_stackaddr_np(self);
164 size_t size = pthread_get_stacksize_np(self);
165 stackBottom = (void*) (((long)stack_addr) - size);
166 /* This workaround is only built if compiling for/on legacy OS X 10.9 (Mavericks) or older */
167 #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1090
168 if (pthread_main_np()) {
169 char str[256];
170 size_t size_str = sizeof(str);
171 int ret = sysctlbyname("kern.osrelease", str, &size_str, NULL, 0);
172 if (0==ret && str[0]=='1' && str[1]=='3' && str[2]=='.') {
173 /* OSX Mavericks returns a wrong stack size... Assume default 8MB */
174 stackBottom = (void*) (((long)stack_addr) - 8 * 1024 * 1024);
175 }
176 }
177 #endif
178 #endif
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68436 times.
68436 assert(size > 128*1024);
180 68436 return (char*)stackBottom + 64*1024;
181 }
182
183 /* The alternate signal stack has to outlive every frame that could fault on it,
184 so it is allocated once and never freed; the handle keeps it reachable. */
185 static char *segv_altstack = NULL;
186
187 3847 void init_metamodelica_segv_handler(void)
188 {
189 stack_t ss;
190 3847 struct sigaction sa = {
191 .sa_sigaction = handler,
192 .sa_flags = SA_ONSTACK | SA_SIGINFO
193 };
194
195 3847 segv_altstack = (char*) malloc(SIGSTKSZ);
196 3847 ss.ss_size = SIGSTKSZ;
197 3847 ss.ss_sp = segv_altstack;
198 3847 ss.ss_flags = 0;
199 3847 sigaltstack(&ss, 0);
200 3847 sigfillset(&sa.sa_mask);
201 3847 sigaction(SIGSEGV, &sa, &default_segv_action);
202 3847 sigfillset(&segvset);
203 3847 }
204
205 68436 void mmc_init_stackoverflow(threadData_t *threadData)
206 {
207 /* Keep a known bottom: a library omc dlopens with RTLD_DEEPBIND has its own
208 mmc_thread_data_key, so its in_<fn> wrapper lands here with omc's
209 threadData, and a bottom taken from this deep frame trips the next MMC_SO(). */
210
1/2
✓ Branch 0 taken 68436 times.
✗ Branch 1 not taken.
68436 if (threadData->stackBottom) {
211 return;
212 }
213 68436 threadData->stackBottom = getStackBase();
214 }
215
216 #else
217
218 void printStacktraceMessages()
219 {
220 }
221
222 void init_metamodelica_segv_handler(void)
223 {
224 }
225
226 void mmc_init_stackoverflow(threadData_t *threadData)
227 {
228 threadData->stackBottom = (void *)0x1; /* Dummy until Windows detects the stack bottom */
229 }
230 #endif
231
232 /* omc records the trace as a MetaModelica list, through a hook meta/ installs
233 (meta_modelica_segv.c); a simulation leaves it unset. */
234 static void (*stacktrace_capture)(threadData_t*, int, int) = NULL;
235
236 2677 void omc_set_stacktrace_capture(void (*fn)(threadData_t*, int, int))
237 {
238 2677 stacktrace_capture = fn;
239 2677 }
240
241 ✗ void mmc_do_stackoverflow(threadData_t *threadData)
242 {
243 ✗ if (stacktrace_capture) {
244 ✗ stacktrace_capture(threadData, 1, MMC_SEGV_TRACE_NFRAMES);
245 }
246 ✗ longjmp(*threadData->mmc_stack_overflow_jumper, 1);
247 }
248