1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2000-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include "sys.h"
25 #include "beam_catches.h"
26 #include "global.h"
27 
28 /* R14B04 has about 380 catches when starting erlang */
29 #define DEFAULT_TABSIZE (1024)
30 typedef struct {
31     ErtsCodePtr cp;
32     unsigned cdr;
33 } beam_catch_t;
34 
35 #ifdef DEBUG
36 #  define IF_DEBUG(x) x
37 #else
38 #  define IF_DEBUG(x)
39 #endif
40 
41 struct bc_pool {
42     int free_list;
43     unsigned high_mark;
44     unsigned tabsize;
45     beam_catch_t *beam_catches;
46     /*
47      * Note that the 'beam_catches' area is shared by pools. Used slots
48      * are readonly as long as the module is not purgable. The free-list is
49      * protected by the code_ix lock.
50      */
51 
52     IF_DEBUG(int is_staging;)
53 };
54 
55 static struct bc_pool bccix[ERTS_NUM_CODE_IX];
56 
beam_catches_init(void)57 void beam_catches_init(void)
58 {
59     int i;
60 
61     bccix[0].tabsize   = DEFAULT_TABSIZE;
62     bccix[0].free_list = -1;
63     bccix[0].high_mark = 0;
64     bccix[0].beam_catches = erts_alloc(ERTS_ALC_T_CATCHES,
65 				     sizeof(beam_catch_t)*DEFAULT_TABSIZE);
66     IF_DEBUG(bccix[0].is_staging = 0);
67     for (i=1; i<ERTS_NUM_CODE_IX; i++) {
68 	bccix[i] = bccix[i-1];
69     }
70      /* For initial load: */
71     IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 1);
72 }
73 
74 
gc_old_vec(beam_catch_t * vec)75 static void gc_old_vec(beam_catch_t* vec)
76 {
77     int i;
78     for (i=0; i<ERTS_NUM_CODE_IX; i++) {
79 	if (bccix[i].beam_catches == vec) {
80 	    return;
81 	}
82     }
83     erts_free(ERTS_ALC_T_CATCHES, vec);
84 }
85 
86 
beam_catches_start_staging(void)87 void beam_catches_start_staging(void)
88 {
89     ErtsCodeIndex dst = erts_staging_code_ix();
90     ErtsCodeIndex src = erts_active_code_ix();
91     beam_catch_t* prev_vec = bccix[dst].beam_catches;
92 
93     ASSERT(!bccix[src].is_staging && !bccix[dst].is_staging);
94 
95     bccix[dst] = bccix[src];
96     gc_old_vec(prev_vec);
97     IF_DEBUG(bccix[dst].is_staging = 1);
98 }
99 
beam_catches_end_staging(int commit)100 void beam_catches_end_staging(int commit)
101 {
102     IF_DEBUG(bccix[erts_staging_code_ix()].is_staging = 0);
103 }
104 
beam_catches_cons(ErtsCodePtr cp,unsigned cdr,ErtsCodePtr ** cppp)105 unsigned beam_catches_cons(ErtsCodePtr cp, unsigned cdr, ErtsCodePtr **cppp)
106 {
107     int i;
108     struct bc_pool* p = &bccix[erts_staging_code_ix()];
109 
110     ASSERT(p->is_staging);
111     /*
112      * Allocate from free_list while it is non-empty.
113      * If free_list is empty, allocate at high_mark.
114      */
115     if (p->free_list >= 0) {
116 	i = p->free_list;
117 	p->free_list = p->beam_catches[i].cdr;
118     }
119     else {
120 	if (p->high_mark >= p->tabsize) {
121 	    /* No free slots and table is full: realloc table */
122 	    beam_catch_t* prev_vec = p->beam_catches;
123 	    unsigned newsize = p->tabsize*2;
124 
125 	    p->beam_catches = erts_alloc(ERTS_ALC_T_CATCHES,
126 					 newsize*sizeof(beam_catch_t));
127 	    sys_memcpy(p->beam_catches, prev_vec,
128 		       p->tabsize*sizeof(beam_catch_t));
129 	    gc_old_vec(prev_vec);
130 	    p->tabsize = newsize;
131 	}
132 	i = p->high_mark++;
133     }
134 
135     p->beam_catches[i].cp  = cp;
136     p->beam_catches[i].cdr = cdr;
137     if (cppp) {
138         *cppp = &p->beam_catches[i].cp;
139     }
140 
141     return i;
142 }
143 
beam_catches_car(unsigned i)144 ErtsCodePtr beam_catches_car(unsigned i)
145 {
146     struct bc_pool* p = &bccix[erts_active_code_ix()];
147 
148     if (i >= p->tabsize ) {
149 	erts_exit(ERTS_ERROR_EXIT, "beam_catches_delmod: index %#x is out of range\r\n", i);
150     }
151     return p->beam_catches[i].cp;
152 }
153 
beam_catches_car_staging(unsigned i)154 ErtsCodePtr beam_catches_car_staging(unsigned i)
155 {
156     struct bc_pool* p = &bccix[erts_staging_code_ix()];
157 
158     if (i >= p->tabsize ) {
159 	erts_exit(ERTS_ERROR_EXIT, "beam_catches_delmod: index %#x is out of range\r\n", i);
160     }
161     return p->beam_catches[i].cp;
162 }
163 
beam_catches_delmod(unsigned head,const BeamCodeHeader * hdr,unsigned code_bytes,ErtsCodeIndex code_ix)164 void beam_catches_delmod(unsigned head,
165                          const BeamCodeHeader *hdr,
166                          unsigned code_bytes,
167                          ErtsCodeIndex code_ix)
168 {
169     struct bc_pool* p = &bccix[code_ix];
170     const char *code_start;
171     unsigned i, cdr;
172 
173     code_start = (const char*)hdr;
174 
175     ASSERT((code_ix == erts_active_code_ix()) != bccix[erts_staging_code_ix()].is_staging);
176 
177     for(i = head; i != (unsigned)-1;) {
178         const char *catch_addr = (char*)p->beam_catches[i].cp;
179 
180         if (i >= p->tabsize) {
181             erts_exit(ERTS_ERROR_EXIT,
182                       "beam_catches_delmod: index %#x is out of range\r\n", i);
183         }
184 
185         if (!ErtsInArea(catch_addr, code_start, code_bytes)) {
186             erts_exit(ERTS_ERROR_EXIT,
187                       "beam_catches_delmod: item %#x has cp %p which is not "
188                       "in module's range [%p,%p[\r\n",
189                       i,
190                       p->beam_catches[i].cp,
191                       code_start,
192                       &code_start[code_bytes]);
193         }
194 
195         p->beam_catches[i].cp = NULL;
196         cdr = p->beam_catches[i].cdr;
197         p->beam_catches[i].cdr = p->free_list;
198         p->free_list = i;
199         i = cdr;
200     }
201 }
202