summaryrefslogtreecommitdiff
path: root/erts/emulator/beam/beam_common.h
blob: f3725d0d2ec90e67693b58f25d4598b3ccbc7900 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 1996-2021. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */

#ifndef _BEAM_COMMON_H_
#define _BEAM_COMMON_H_

#if defined(HARDDEBUG)

#  define CHECK_TERM(T) size_object(T)

#  define CHECK_ARGS(PC)                 \
do {                                     \
  int i_;                                \
  int Arity_ = PC[-1];                   \
  for (i_ = 0; i_ < Arity_; i_++) {      \
	CHECK_TERM(x(i_));               \
  }                                      \
} while (0)

#else
#  define CHECK_TERM(T) ASSERT(!is_CP(T))
#  define CHECK_ARGS(T)
#endif

#ifdef DEBUG

#ifdef BEAMASM

#define ERTS_DBG_CHK_REDS(P, FC)					\
    do {								\
	    ASSERT(FC <= CONTEXT_REDS);					\
	    ASSERT(erts_proc_sched_data(c_p)->virtual_reds		\
		   <= CONTEXT_REDS - (FC));				\
} while (0)
#else
#define ERTS_DBG_CHK_REDS(P, FC)					\
    do {								\
	if (ERTS_PROC_GET_SAVED_CALLS_BUF((P))) {			\
	    ASSERT(FC <= 0);						\
	    ASSERT(erts_proc_sched_data(c_p)->virtual_reds		\
		   <= 0 - (FC));					\
	}								\
	else {								\
	    ASSERT(FC <= CONTEXT_REDS);					\
	    ASSERT(erts_proc_sched_data(c_p)->virtual_reds		\
		   <= CONTEXT_REDS - (FC));				\
	}								\
} while (0)
#endif

#else
#define ERTS_DBG_CHK_REDS(P, FC)
#endif

#define SWAPIN             \
    HTOP = HEAP_TOP(c_p);  \
    E = c_p->stop

#define SWAPOUT            \
    HEAP_TOP(c_p) = HTOP;  \
    c_p->stop = E

#define HEAVY_SWAPIN       \
    SWAPIN;		   \
    FCALLS = c_p->fcalls

#define HEAVY_SWAPOUT      \
    SWAPOUT;		   \
    c_p->fcalls = FCALLS

#ifdef ERTS_ENABLE_LOCK_CHECK
#    define PROCESS_MAIN_CHK_LOCKS(P)                   \
do {                                                    \
    if ((P))                                            \
	erts_proc_lc_chk_only_proc_main((P));           \
    ERTS_LC_ASSERT(!erts_thr_progress_is_blocking());   \
} while (0)
#    define ERTS_REQ_PROC_MAIN_LOCK(P)				\
do {                                                            \
    if ((P))                                                    \
	erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,     \
				  __FILE__, __LINE__);          \
} while (0)
#    define ERTS_UNREQ_PROC_MAIN_LOCK(P)				\
do {									\
    if ((P))								\
	erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN);		\
} while (0)
#else
#  define PROCESS_MAIN_CHK_LOCKS(P)
#  define ERTS_REQ_PROC_MAIN_LOCK(P)
#  define ERTS_UNREQ_PROC_MAIN_LOCK(P)
#endif

#define x(N) reg[N]
#define y(N) E[N]
#define r(N) x(N)
#define Q(N) (N*sizeof(Eterm *))
#define l(N) (freg[N].fd)

#define Arg(N)       I[(N)+1]

#define GetSource(raw, dst)			\
   do {						\
     dst = raw;                                 \
     switch (loader_tag(dst)) {			\
     case LOADER_X_REG:				\
        dst = x(loader_x_reg_index(dst));       \
        break;					\
     case LOADER_Y_REG:				\
        ASSERT(loader_y_reg_index(dst) >= 1);	\
        dst = y(loader_y_reg_index(dst));       \
        break;					\
     }						\
     CHECK_TERM(dst);				\
   } while (0)

#define PUT_TERM_REG(term, desc)		\
do {						\
    switch (loader_tag(desc)) {			\
    case LOADER_X_REG:				\
	x(loader_x_reg_index(desc)) = (term);	\
	break;					\
    case LOADER_Y_REG:				\
	y(loader_y_reg_index(desc)) = (term);	\
	break;					\
    default:					\
	ASSERT(0);				\
	break;					\
    }						\
} while(0)

#ifdef USE_VM_PROBES
#  define USE_VM_CALL_PROBES
#endif

#ifdef USE_VM_CALL_PROBES

#define DTRACE_LOCAL_CALL(p, cmfa)                                      \
    if (DTRACE_ENABLED(local_function_entry)) {                         \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);             \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);                  \
        int depth = STACK_START(p) - STACK_TOP(p);                      \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);              \
        DTRACE3(local_function_entry, process_name, mfa_buf, depth);    \
    }

#define DTRACE_GLOBAL_CALL(p, cmfa)                                     \
    if (DTRACE_ENABLED(global_function_entry)) {	                    \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);             \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);                  \
        int depth = STACK_START(p) - STACK_TOP(p);                      \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);              \
        DTRACE3(global_function_entry, process_name, mfa_buf, depth);	\
    }

#define DTRACE_RETURN(p, cmfa)                                  \
    if (DTRACE_ENABLED(function_return)) {                      \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);     \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);          \
        int depth = STACK_START(p) - STACK_TOP(p);              \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);      \
        DTRACE3(function_return, process_name, mfa_buf, depth); \
    }

#define DTRACE_BIF_ENTRY(p, cmfa)                                   \
    if (DTRACE_ENABLED(bif_entry)) {                                \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);         \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);              \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);          \
        DTRACE2(bif_entry, process_name, mfa_buf);                  \
    }

#define DTRACE_BIF_RETURN(p, cmfa)                                  \
    if (DTRACE_ENABLED(bif_return)) {                               \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);         \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);              \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);          \
        DTRACE2(bif_return, process_name, mfa_buf);                 \
    }

#define DTRACE_NIF_ENTRY(p, cmfa)                                       \
    if (DTRACE_ENABLED(nif_entry)) {                                    \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);             \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);                  \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);              \
        DTRACE2(nif_entry, process_name, mfa_buf);                      \
    }

#define DTRACE_NIF_RETURN(p, cmfa)                                      \
    if (DTRACE_ENABLED(nif_return)) {                                   \
        DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);             \
        DTRACE_CHARBUF(mfa_buf, DTRACE_TERM_BUF_SIZE);                  \
        dtrace_fun_decode(p, cmfa, process_name, mfa_buf);              \
        DTRACE2(nif_return, process_name, mfa_buf);                     \
    }

#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p,e)                               \
    do {                                                                  \
        if (DTRACE_ENABLED(global_function_entry)) {                      \
            ErtsDispatchable *disp__ = &(e)->dispatch;                    \
            ErtsCodePtr fp__ = disp__->addresses[erts_active_code_ix()];  \
            DTRACE_GLOBAL_CALL((p), erts_code_to_codemfa(fp__));          \
        }                                                                 \
    } while(0)

#define DTRACE_RETURN_FROM_PC(p, i)                                     \
    if (DTRACE_ENABLED(function_return)) {                              \
        const ErtsCodeMFA* cmfa  = erts_find_function_from_pc(i);       \
        if (cmfa) {                                                     \
            DTRACE_RETURN((p), cmfa);                                   \
        }                                                               \
    }

#else /* USE_VM_PROBES */
#define DTRACE_LOCAL_CALL(p, mfa)        do {} while (0)
#define DTRACE_GLOBAL_CALL(p, mfa)       do {} while (0)
#define DTRACE_GLOBAL_CALL_FROM_EXPORT(p, e) do {} while (0)
#define DTRACE_RETURN(p, mfa)            do {} while (0)
#define DTRACE_RETURN_FROM_PC(p, i)      do {} while (0)
#define DTRACE_BIF_ENTRY(p, mfa)         do {} while (0)
#define DTRACE_BIF_RETURN(p, mfa)        do {} while (0)
#define DTRACE_NIF_ENTRY(p, mfa)         do {} while (0)
#define DTRACE_NIF_RETURN(p, mfa)        do {} while (0)
#endif /* USE_VM_PROBES */

/*
 * We reuse some of fields in the save area in the process structure.
 * This is safe to do, since this space is only actively used when
 * the process is switched out.
 */
#define REDS_IN(p)  ((p)->def_arg_reg[5])

ErtsCodeMFA *ubif2mfa(void* uf);
ErtsCodePtr handle_error(Process* c_p, ErtsCodePtr pc,
                         Eterm* reg, const ErtsCodeMFA* bif_mfa);
Export* call_error_handler(Process* p, const ErtsCodeMFA* mfa,
                           Eterm* reg, Eterm func);
Export* fixed_apply(Process* p, Eterm* reg, Uint arity,
                    ErtsCodePtr I, Uint offs);
Export* apply(Process* p, Eterm* reg, ErtsCodePtr I, Uint offs);
ErtsCodePtr call_fun(Process* p, int arity, Eterm* reg, Eterm args);
ErtsCodePtr apply_fun(Process* p, Eterm fun, Eterm args, Eterm* reg);
int is_function2(Eterm Term, Uint arity);
Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live,
                      Uint n, const Eterm* data);
Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal,
                                Uint live, const Eterm* data);
Eterm erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live,
                               Uint n, const Eterm* data);
Eterm erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live,
                               Uint n, const Eterm* data);
Eterm get_map_element(Eterm map, Eterm key);
Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx);
int raw_raise(Eterm stacktrace, Eterm exc_class, Eterm value, Process *c_p);
void erts_sanitize_freason(Process* c_p, Eterm exc);
Eterm add_stacktrace(Process* c_p, Eterm Value, Eterm exc);
void copy_out_registers(Process *c_p, Eterm *reg);
void copy_in_registers(Process *c_p, Eterm *reg);
void check_monitor_long_schedule(Process *c_p, Uint64 start_time,
                                 ErtsCodePtr start_time_i);

extern ErtsCodePtr beam_run_process;
extern ErtsCodePtr beam_normal_exit;
extern ErtsCodePtr beam_exit;
extern ErtsCodePtr beam_save_calls;
extern ErtsCodePtr beam_bif_export_trap;
extern ErtsCodePtr beam_export_trampoline;
extern ErtsCodePtr beam_continue_exit;
extern ErtsCodePtr beam_unloaded_fun;

extern ErtsCodePtr beam_return_to_trace;   /* OpCode(i_return_to_trace) */
extern ErtsCodePtr beam_return_trace;      /* OpCode(i_return_trace) */
extern ErtsCodePtr beam_exception_trace;   /* OpCode(i_exception_trace) */
extern ErtsCodePtr beam_return_time_trace; /* OpCode(i_return_time_trace) */

/** @brief Inspects an Erlang stack frame, returning the base of the data
 *         (first Y register).
 * @param[in] frame The frame to inspect. Must point at a CP.
 * @param[out] return_address The return address of \p frame */
ERTS_GLB_INLINE
const Eterm *erts_inspect_frame(Eterm *frame, ErtsCodePtr *return_address);

#if ERTS_GLB_INLINE_INCL_FUNC_DEF
ERTS_GLB_INLINE
const Eterm *erts_inspect_frame(Eterm *frame, ErtsCodePtr *return_address) {
    ASSERT(is_CP(frame[0]));

    if (ERTS_LIKELY(erts_frame_layout == ERTS_FRAME_LAYOUT_RA)) {
        *return_address = (ErtsCodePtr)cp_val(frame[0]);
        return &frame[1];
    }

    ASSERT(cp_val(frame[0]) == NULL || frame < (Eterm*)cp_val(frame[0]));
    *return_address = (ErtsCodePtr)cp_val(frame[1]);
    return &frame[2];
}
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */

#endif /* _BEAM_COMMON_H_ */