1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1998-2020. 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 #ifndef _DB_UTIL_H
22 #define _DB_UTIL_H
23 
24 #include "erl_flxctr.h"
25 #include "global.h"
26 #include "erl_message.h"
27 #include "erl_bif_unique.h"
28 
29 /*#define HARDDEBUG 1*/
30 
31 #ifdef DEBUG
32 /*
33 ** DMC_DEBUG does NOT need DEBUG, but DEBUG needs DMC_DEBUG
34 */
35 #define DMC_DEBUG 1
36 #define ETS_DBG_FORCE_TRAP 1
37 #endif
38 
39 /*
40  * These values can be returned from the functions performing the
41  * BIF operation for different types of tables. When the
42  * actual operations have been performed, the BIF function
43  * checks for negative returns and issues BIF_ERRORS based
44  * upon these values.
45  */
46 #define DB_ERROR_NONE_FALSE 1     /* No error am_false reult */
47 #define DB_ERROR_NONE       0     /* No error */
48 #define DB_ERROR_BADITEM   -1     /* The item was malformed ie no
49 				     tuple or to small*/
50 #define DB_ERROR_BADTABLE  -2     /* The Table is inconsisitent */
51 #define DB_ERROR_SYSRES    -3     /* Out of system resources */
52 #define DB_ERROR_BADKEY    -4     /* Returned if a key that should
53 				     exist does not. */
54 #define DB_ERROR_BADPARAM  -5     /* Returned if a specified slot does
55 				     not exist (hash table only) or
56 				     the state parameter in db_match_object
57 				     is broken.*/
58 #define DB_ERROR_UNSPEC    -10    /* Unspecified error */
59 
60 /*#define DEBUG_CLONE*/
61 
62 /*
63  * A datatype for a database entry stored out of a process heap
64  */
65 typedef struct db_term {
66     struct erl_off_heap_header* first_oh; /* Off heap data for term. */
67     Uint size;		   /* Heap size of term in "words" */
68 #ifdef DEBUG_CLONE
69     Eterm* debug_clone;    /* An uncompressed copy */
70 #endif
71     Eterm tpl[1];          /* Term data. Top tuple always first */
72 
73     /* Compression: is_immed and key element are uncompressed.
74        Compressed elements are stored in external format after each other
75        last in dbterm. The top tuple elements contains byte offsets, to
76        the start of the data, tagged as headers.
77        The allocated size of the dbterm in bytes is stored at tpl[arity+1].
78      */
79 } DbTerm;
80 
81 #define DB_MUST_RESIZE 1
82 #define DB_NEW_OBJECT 2
83 #define DB_INC_TRY_GROW 4
84 
85 /* Info about a database entry while it's being updated
86  * (by update_counter or update_element)
87  */
88 typedef struct {
89     DbTable* tb;
90     DbTerm* dbterm;
91     void** bp;         /* {Hash|Tree}DbTerm** */
92     Uint new_size;
93     int flags;
94     union {
95         struct {
96             struct DbTableHashLockAndCounter* lck_ctr;
97         } hash;
98         struct {
99             struct DbTableCATreeNode* base_node;
100             struct DbTableCATreeNode* parent;
101             int current_level;
102         } catree;
103     } u;
104 } DbUpdateHandle;
105 
106 /* How safe are we from double-hits or missed objects
107  * when iterating without fixation?
108  */
109 enum DbIterSafety {
110     ITER_UNSAFE,      /* Must fixate to be safe */
111     ITER_SAFE_LOCKED, /* Safe while table is locked, not between trap calls */
112     ITER_SAFE         /* No need to fixate at all */
113 };
114 
115 typedef struct db_table_method
116 {
117     int (*db_create)(Process *p, DbTable* tb);
118     int (*db_first)(Process* p,
119 		    DbTable* tb, /* [in out] */
120 		    Eterm* ret   /* [out] */);
121     int (*db_next)(Process* p,
122 		   DbTable* tb, /* [in out] */
123 		   Eterm key,   /* [in] */
124 		   Eterm* ret /* [out] */);
125     int (*db_last)(Process* p,
126 		   DbTable* tb, /* [in out] */
127 		   Eterm* ret   /* [out] */);
128     int (*db_prev)(Process* p,
129 		   DbTable* tb, /* [in out] */
130 		   Eterm key,
131 		   Eterm* ret);
132     int (*db_put)(DbTable* tb, /* [in out] */
133 		  Eterm obj,
134 		  int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
135                   SWord *consumed_reds_p);
136     int (*db_get)(Process* p,
137 		  DbTable* tb, /* [in out] */
138 		  Eterm key,
139 		  Eterm* ret);
140     int (*db_get_element)(Process* p,
141 			  DbTable* tb, /* [in out] */
142 			  Eterm key,
143 			  int index,
144 			  Eterm* ret);
145     int (*db_member)(DbTable* tb, /* [in out] */
146 		     Eterm key,
147 		     Eterm* ret);
148     int (*db_erase)(DbTable* tb,  /* [in out] */
149 		    Eterm key,
150 		    Eterm* ret);
151     int (*db_erase_object)(DbTable* tb, /* [in out] */
152 			   Eterm obj,
153 			   Eterm* ret);
154     int (*db_slot)(Process* p,
155 		   DbTable* tb, /* [in out] */
156 		   Eterm slot,
157 		   Eterm* ret);
158     int (*db_select_chunk)(Process* p,
159 			   DbTable* tb, /* [in out] */
160                            Eterm tid,
161 			   Eterm pattern,
162 			   Sint chunk_size,
163 			   int reverse,
164 			   Eterm* ret,
165                            enum DbIterSafety);
166     int (*db_select)(Process* p,
167 		     DbTable* tb, /* [in out] */
168                      Eterm tid,
169 		     Eterm pattern,
170 		     int reverse,
171 		     Eterm* ret,
172                      enum DbIterSafety);
173     int (*db_select_delete)(Process* p,
174 			    DbTable* tb, /* [in out] */
175                             Eterm tid,
176 			    Eterm pattern,
177 			    Eterm* ret,
178                             enum DbIterSafety);
179     int (*db_select_continue)(Process* p,
180 			      DbTable* tb, /* [in out] */
181 			      Eterm continuation,
182 			      Eterm* ret,
183                               enum DbIterSafety*);
184     int (*db_select_delete_continue)(Process* p,
185 				     DbTable* tb, /* [in out] */
186 				     Eterm continuation,
187 				     Eterm* ret,
188                                      enum DbIterSafety*);
189     int (*db_select_count)(Process* p,
190 			   DbTable* tb, /* [in out] */
191                            Eterm tid,
192 			   Eterm pattern,
193 			   Eterm* ret,
194                            enum DbIterSafety);
195     int (*db_select_count_continue)(Process* p,
196 				    DbTable* tb, /* [in out] */
197 				    Eterm continuation,
198 				    Eterm* ret,
199                                     enum DbIterSafety*);
200     int (*db_select_replace)(Process* p,
201             DbTable* tb, /* [in out] */
202             Eterm tid,
203             Eterm pattern,
204             Eterm* ret,
205             enum DbIterSafety);
206     int (*db_select_replace_continue)(Process* p,
207             DbTable* tb, /* [in out] */
208             Eterm continuation,
209             Eterm* ret,
210             enum DbIterSafety*);
211     int (*db_take)(Process *, DbTable *, Eterm, Eterm *);
212 
213     SWord (*db_delete_all_objects)(Process* p,
214                                    DbTable* db,
215                                    SWord reds,
216                                    Eterm* nitems_holder_wb);
217     Eterm (*db_delete_all_objects_get_nitems_from_holder)(Process* p,
218                                                           Eterm nitems_holder);
219     int (*db_free_empty_table)(DbTable* db);
220     SWord (*db_free_table_continue)(DbTable* db, SWord reds);
221 
222     void (*db_print)(fmtfn_t to,
223 		     void* to_arg,
224 		     int show,
225 		     DbTable* tb /* [in out] */ );
226 
227     void (*db_foreach_offheap)(DbTable* db,  /* [in out] */
228 			       void (*func)(ErlOffHeap *, void *),
229 			       void *arg);
230 
231     /* Lookup a dbterm for updating. Return false if not found. */
232     int (*db_lookup_dbterm)(Process *, DbTable *, Eterm key, Eterm obj,
233                             DbUpdateHandle* handle);
234 
235     /* Must be called for each db_lookup_dbterm that returned true, even if
236     ** dbterm was not updated. If the handle was of a new object and cret is
237     ** not DB_ERROR_NONE, the object is removed from the table. */
238     void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
239     void* (*db_eterm_to_dbterm)(int compress, int keypos, Eterm obj);
240     void* (*db_dbterm_list_prepend)(void* list, void* db_term);
241     void* (*db_dbterm_list_remove_first)(void** list);
242     int (*db_put_dbterm)(DbTable* tb, /* [in out] */
243                          void* obj,
244                          int key_clash_fail, /* DB_ERROR_BADKEY if key exists */
245                          SWord *consumed_reds_p);
246     void (*db_free_dbterm)(int compressed, void* obj);
247     Eterm (*db_get_dbterm_key)(DbTable* tb, void* db_term);
248     int (*db_get_binary_info)(Process*, DbTable* tb, Eterm key, Eterm* ret);
249     /* Raw first/next same as first/next but also return pseudo deleted keys.
250        Only internal use by ets:info(_,binary) */
251     int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
252     int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
253 } DbTableMethod;
254 
255 typedef struct db_fixation {
256     /* Node in fixed_tabs list */
257     struct {
258         struct db_fixation *next, *prev;
259         Binary* btid;
260     } tabs;
261 
262     /* Node in fixing_procs tree */
263     struct {
264         struct db_fixation *left, *right, *parent;
265         int is_red;
266         Process* p;
267     } procs;
268 
269     /* Number of fixations on table from procs.p
270      * Protected by table write lock or read lock + fixlock
271      */
272     Uint counter;
273 } DbFixation;
274 
275 typedef struct {
276     DbTable *next;
277     DbTable *prev;
278 } DbTableList;
279 
280 #define ERTS_DB_TABLE_NITEMS_COUNTER_ID 0
281 #define ERTS_DB_TABLE_MEM_COUNTER_ID 1
282 
283 /*
284  * This structure contains data for all different types of database
285  * tables. Note that these fields must match the same fields
286  * in the table-type specific structures.
287  * The reason it is placed here and not in db.h is that some table
288  * operations may be the same on different types of tables.
289  */
290 
291 typedef struct db_table_common {
292     erts_refc_t refc;     /* reference count of table struct */
293     erts_refc_t fix_count;/* fixation counter */
294     DbTableList all;
295     DbTableList owned;
296     erts_rwmtx_t rwlock;  /* rw lock on table */
297     erts_mtx_t fixlock;   /* Protects fixing_procs and time */
298     int is_thread_safe;       /* No fine locking inside table needed */
299     Uint32 type;              /* table type, *read only* after creation */
300     Eterm owner;              /* Pid of the creator */
301     Eterm heir;               /* Pid of the heir */
302     UWord heir_data;          /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
303     Uint64 heir_started_interval;  /* To further identify the heir */
304     Eterm the_name;           /* an atom */
305     Binary *btid;
306     DbTableMethod* meth;      /* table methods */
307     /* The ErtsFlxCtr below contains:
308      * - Total number of items in table
309      * - Total memory size (NOTE: in bytes!) */
310     ErtsFlxCtr counters;
311     char extra_for_flxctr[ERTS_FLXCTR_NR_OF_EXTRA_BYTES(2)];
312     struct {                  /* Last fixation time */
313 	ErtsMonotonicTime monotonic;
314 	ErtsMonotonicTime offset;
315     } time;
316     DbFixation* fixing_procs; /* Tree of processes who have done safe_fixtable,
317                                  "local" fixations not included. */
318     /* All 32-bit fields */
319     Uint32 status;            /* bit masks defined  below */
320     int keypos;               /* defaults to 1 */
321     int compress;
322 
323     /* For unfinished operations that needs to be helped */
324     void (*continuation)(long *reds_ptr,
325                          void** state,
326                          void* extra_context); /* To help yielded process */
327     erts_atomic_t continuation_state;
328     Binary* continuation_res_bin;
329 #ifdef ETS_DBG_FORCE_TRAP
330     erts_atomic_t dbg_force_trap;  /* &1 force enabled, &2 trap this call */
331 #endif
332 } DbTableCommon;
333 
334 /* These are status bit patterns */
335 #define DB_PRIVATE        (1 << 0)
336 #define DB_PROTECTED      (1 << 1)
337 #define DB_PUBLIC         (1 << 2)
338 #define DB_DELETE         (1 << 3) /* table is being deleted */
339 #define DB_SET            (1 << 4)
340 #define DB_BAG            (1 << 5)
341 #define DB_DUPLICATE_BAG  (1 << 6)
342 #define DB_ORDERED_SET    (1 << 7)
343 #define DB_CA_ORDERED_SET (1 << 8)
344 #define DB_FINE_LOCKED    (1 << 9)  /* write_concurrency */
345 #define DB_FREQ_READ      (1 << 10) /* read_concurrency */
346 #define DB_NAMED_TABLE    (1 << 11)
347 #define DB_BUSY           (1 << 12)
348 
349 #define DB_CATREE_FORCE_SPLIT (1 << 31)  /* erts_debug */
350 #define DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN (1 << 30)  /* erts_debug */
351 
352 #define IS_HASH_TABLE(Status) (!!((Status) & \
353 				  (DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
354 #define IS_TREE_TABLE(Status) (!!((Status) & \
355 				  DB_ORDERED_SET))
356 #define IS_CATREE_TABLE(Status) (!!((Status) & \
357                                     DB_CA_ORDERED_SET))
358 #define NFIXED(T) (erts_refc_read(&(T)->common.fix_count,0))
359 #define IS_FIXED(T) (NFIXED(T) != 0)
360 
361 #define META_DB_LOCK_FREE() (erts_no_schedulers == 1)
362 #define DB_LOCK_FREE(T) META_DB_LOCK_FREE()
363 
364 /*
365  * tplp is an untagged pointer to a tuple we know is large enough
366  * and dth is a pointer to a DbTableHash.
367  */
368 #define GETKEY(dth, tplp)   (*((tplp) + ((DbTableCommon*)(dth))->keypos))
369 
370 
371 ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj);
372 Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp,
373 			ErlOffHeap* off_heap);
374 int db_eq_comp(DbTableCommon* tb, Eterm a, DbTerm* b);
375 DbTerm* db_alloc_tmp_uncompressed(DbTableCommon* tb, DbTerm* org);
376 void db_free_tmp_uncompressed(DbTerm* obj);
377 
378 ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp,
379 					      Eterm** hpp, ErlOffHeap* off_heap);
380 ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b);
381 Wterm db_do_read_element(DbUpdateHandle* handle, Sint position);
382 
383 #if ERTS_GLB_INLINE_INCL_FUNC_DEF
384 
db_copy_key(Process * p,DbTable * tb,DbTerm * obj)385 ERTS_GLB_INLINE Eterm db_copy_key(Process* p, DbTable* tb, DbTerm* obj)
386 {
387     Eterm key = GETKEY(tb, obj->tpl);
388     if IS_CONST(key) return key;
389     else {
390 	Uint size = size_object(key);
391 	Eterm* hp = HAlloc(p, size);
392 	Eterm res = copy_struct(key, size, &hp, &MSO(p));
393 	ASSERT(EQ(res,key));
394 	return res;
395     }
396 }
397 
db_copy_object_from_ets(DbTableCommon * tb,DbTerm * bp,Eterm ** hpp,ErlOffHeap * off_heap)398 ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp,
399 					      Eterm** hpp, ErlOffHeap* off_heap)
400 {
401     if (tb->compress) {
402 	return db_copy_from_comp(tb, bp, hpp, off_heap);
403     }
404     else {
405 	return copy_shallow(bp->tpl, bp->size, hpp, off_heap);
406     }
407 }
408 
db_eq(DbTableCommon * tb,Eterm a,DbTerm * b)409 ERTS_GLB_INLINE int db_eq(DbTableCommon* tb, Eterm a, DbTerm* b)
410 {
411     if (!tb->compress) {
412 	return EQ(a, make_tuple(b->tpl));
413     }
414     else {
415 	return db_eq_comp(tb, a, b);
416     }
417 }
418 
419 #endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
420 
421 
422 #define DB_READ  (DB_PROTECTED|DB_PUBLIC)
423 #define DB_WRITE DB_PUBLIC
424 #define DB_INFO  (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE)
425 #define DB_READ_TBL_STRUCT (DB_PROTECTED|DB_PUBLIC|DB_PRIVATE|DB_BUSY)
426 
427 #define ONLY_WRITER(P,T) (((T)->common.status & (DB_PRIVATE|DB_PROTECTED)) \
428 			  && (T)->common.owner == (P)->common.id)
429 
430 #define ONLY_READER(P,T) (((T)->common.status & DB_PRIVATE) && \
431 (T)->common.owner == (P)->common.id)
432 
433 /* Function prototypes */
434 BIF_RETTYPE db_get_trace_control_word(Process* p);
435 BIF_RETTYPE db_set_trace_control_word(Process* p, Eterm tcw);
436 BIF_RETTYPE db_get_trace_control_word_0(BIF_ALIST_0);
437 BIF_RETTYPE db_set_trace_control_word_1(BIF_ALIST_1);
438 
439 void db_initialize_util(void);
440 Eterm db_getkey(int keypos, Eterm obj);
441 void db_cleanup_offheap_comp(DbTerm* p);
442 void db_free_term(DbTable *tb, void* basep, Uint offset);
443 void db_free_term_no_tab(int compress, void* basep, Uint offset);
444 Uint db_term_size(DbTable *tb, void* basep, Uint offset);
445 void* db_store_term(DbTableCommon *tb, DbTerm* old, Uint offset, Eterm obj);
446 void* db_store_term_comp(DbTableCommon *tb, /*May be NULL*/
447                          int keypos,
448                          DbTerm* old,
449                          Uint offset,Eterm obj);
450 Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p, DbTerm* obj,
451 			       Uint pos, Eterm** hpp, Uint extra);
452 int db_has_map(Eterm obj);
453 int db_has_variable(Eterm obj);
454 int db_is_variable(Eterm obj);
455 void db_do_update_element(DbUpdateHandle* handle,
456 			  Sint position,
457 			  Eterm newval);
458 void db_finalize_resize(DbUpdateHandle* handle, Uint offset);
459 Eterm db_add_counter(Eterm** hpp, Wterm counter, Eterm incr);
460 Binary *db_match_set_compile(Process *p, Eterm matchexpr,
461 			     Uint flags, Uint *freasonp);
462 int db_match_keeps_key(int keypos, Eterm match, Eterm guard, Eterm body);
463 int erts_db_match_prog_destructor(Binary *);
464 
465 typedef struct match_prog {
466     ErlHeapFragment *term_save; /* Only if needed, a list of message
467 				    buffers for off heap copies
468 				    (i.e. binaries)*/
469     int single_variable;     /* ets:match needs to know this. */
470     int num_bindings;        /* Size of heap */
471     /* The following two are only filled in when match specs
472        are used for tracing */
473     struct erl_heap_fragment *saved_program_buf;
474     Eterm saved_program;
475     Uint heap_size;          /* size of: heap + eheap + stack */
476     Uint stack_offset;
477 #ifdef DMC_DEBUG
478     UWord* prog_end;		/* End of program */
479 #endif
480     UWord text[1];		/* Beginning of program */
481 } MatchProg;
482 
483 /*
484  * The heap-eheap-stack block of a MatchProg is nowadays allocated
485  * when the match program is run.
486  * - heap: variable bindings
487  * - eheap: erlang heap storage
488  * - eheap: a "large enough" stack
489  */
490 
491 #define DMC_ERR_STR_LEN 100
492 
493 typedef enum { dmcWarning, dmcError} DMCErrorSeverity;
494 
495 typedef struct dmc_error {
496     char error_string[DMC_ERR_STR_LEN + 1]; /* printf format string
497 					       with %d for the variable
498 					       number (if applicable) */
499     int variable;                           /* -1 if no variable is referenced
500 					       in error string */
501     struct dmc_error *next;
502     DMCErrorSeverity severity;              /* Error or warning */
503 } DMCError;
504 
505 typedef struct dmc_err_info {
506     unsigned int *var_trans; /* Translations of variable names,
507 				initiated to NULL
508 				and free'd with sys_free if != NULL
509 				after compilation */
510     int num_trans;
511     int error_added;         /* indicates if the error list contains
512 				any fatal errors (dmcError severity) */
513     DMCError *first;         /* List of errors */
514 } DMCErrInfo;
515 
516 /*
517 ** Compilation flags
518 **
519 ** The dialect is in the 3 least significant bits and are to be interspaced by
520 ** by at least 2 (decimal), thats why ((Uint) 2) isn't used. This is to be
521 ** able to add DBIF_GUARD or DBIF BODY to it to use in the match_spec bif
522 ** table. The rest of the word is used like ordinary flags, one bit for each
523 ** flag. Note that DCOMP_TABLE and DCOMP_TRACE are mutually exclusive.
524 */
525 #define DCOMP_TABLE ((Uint) 1) /* Ets and dets. The body returns a value,
526 		       * and the parameter to the execution is a tuple. */
527 #define DCOMP_TRACE ((Uint) 4) /* Trace. More functions are allowed, and the
528 		       * parameter to the execution will be an array. */
529 #define DCOMP_DIALECT_MASK ((Uint) 0x7) /* To mask out the bits marking
530 					   dialect */
531 #define DCOMP_FAKE_DESTRUCTIVE ((Uint) 8) /* When this is active, no setting of
532 					     trace control words or seq_trace tokens will be done. */
533 
534 /* Allow lock seizing operations on the tracee and 3rd party processes */
535 #define DCOMP_ALLOW_TRACE_OPS ((Uint) 0x10)
536 
537 /* This is call trace */
538 #define DCOMP_CALL_TRACE ((Uint) 0x20)
539 
540 Binary *db_match_compile(Eterm *matchexpr, Eterm *guards,
541 			 Eterm *body, int num_matches,
542 			 Uint flags,
543 			 DMCErrInfo *err_info,
544                          Uint *freasonp);
545 /* Returns newly allocated MatchProg binary with refc == 0*/
546 
547 Eterm db_match_dbterm_uncompressed(DbTableCommon* tb, Process* c_p, Binary* bprog,
548                                    DbTerm* obj, Eterm** hpp, Uint extra);
549 Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog,
550 		      DbTerm* obj, Eterm** hpp, Uint extra);
551 
552 Eterm db_prog_match(Process *p, Process *self,
553                     Binary *prog, Eterm term,
554 		    Eterm *termp, int arity,
555 		    enum erts_pam_run_flags in_flags,
556 		    Uint32 *return_flags /* Zeroed on enter */);
557 
558 /* returns DB_ERROR_NONE if matches, 1 if not matches and some db error on
559    error. */
560 DMCErrInfo *db_new_dmc_err_info(void);
561 /* Returns allocated error info, where errors are collected for lint. */
562 Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei);
563 /* Formats an error info structure into a list of tuples. */
564 void db_free_dmc_err_info(DMCErrInfo *ei);
565 /* Completely free's an error info structure, including all recorded
566    errors */
567 
568 ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm **hpp);
569 ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary(Eterm term);
570 ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary_unchecked(Eterm term);
571 
572 /** @brief Ensure off-heap header is word aligned, make a temporary copy if
573  * not. Needed when inspecting ETS off-heap lists that may contain unaligned
574  * ProcBins if table is 'compressed'.
575  */
576 struct erts_tmp_aligned_offheap
577 {
578     ProcBin proc_bin;
579 };
580 ERTS_GLB_INLINE void erts_align_offheap(union erl_off_heap_ptr*,
581                                         struct erts_tmp_aligned_offheap* tmp);
582 
583 #if ERTS_GLB_INLINE_INCL_FUNC_DEF
584 
585 /*
586  * Convert a match program to a "magic" ref to return up to erlang
587  */
erts_db_make_match_prog_ref(Process * p,Binary * mp,Eterm ** hpp)588 ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm **hpp)
589 {
590     return erts_mk_magic_ref(hpp, &MSO(p), mp);
591 }
592 
593 ERTS_GLB_INLINE Binary *
erts_db_get_match_prog_binary_unchecked(Eterm term)594 erts_db_get_match_prog_binary_unchecked(Eterm term)
595 {
596     Binary *bp = erts_magic_ref2bin(term);
597     ASSERT(bp->intern.flags & BIN_FLAG_MAGIC);
598     ASSERT((ERTS_MAGIC_BIN_DESTRUCTOR(bp) == erts_db_match_prog_destructor));
599     return bp;
600 }
601 
602 ERTS_GLB_INLINE Binary *
erts_db_get_match_prog_binary(Eterm term)603 erts_db_get_match_prog_binary(Eterm term)
604 {
605     Binary *bp;
606     if (!is_internal_magic_ref(term))
607 	return NULL;
608     bp = erts_magic_ref2bin(term);
609     ASSERT(bp->intern.flags & BIN_FLAG_MAGIC);
610     if (ERTS_MAGIC_BIN_DESTRUCTOR(bp) != erts_db_match_prog_destructor)
611 	return NULL;
612     return bp;
613 }
614 
615 ERTS_GLB_INLINE void
erts_align_offheap(union erl_off_heap_ptr * ohp,struct erts_tmp_aligned_offheap * tmp)616 erts_align_offheap(union erl_off_heap_ptr* ohp,
617                    struct erts_tmp_aligned_offheap* tmp)
618 {
619     if ((UWord)ohp->voidp % sizeof(UWord) != 0) {
620         /*
621          * ETS store word unaligned ProcBins in its compressed format.
622          * Make a temporary aligned copy.
623          *
624          * Warning, must pass (void*)-variable to memcpy. Otherwise it will
625          * cause Bus error on Sparc due to false compile time assumptions
626          * about word aligned memory (type cast is not enough).
627          */
628         sys_memcpy(tmp, ohp->voidp, sizeof(*tmp));
629         ASSERT(tmp->proc_bin.thing_word == HEADER_PROC_BIN);
630         ohp->pb = &tmp->proc_bin;
631     }
632 }
633 
634 #endif
635 
636 /*
637 ** Convenience when compiling into Binary structures
638 */
639 #define IsMatchProgBinary(BP) \
640   (((BP)->intern.flags & BIN_FLAG_MAGIC) \
641    && ERTS_MAGIC_BIN_DESTRUCTOR((BP)) == erts_db_match_prog_destructor)
642 
643 #define Binary2MatchProg(BP) \
644   (ASSERT(IsMatchProgBinary((BP))), \
645    ((MatchProg *) ERTS_MAGIC_BIN_DATA((BP))))
646 
647 #endif /* _DB_UTIL_H */
648