Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/psi/ialloc.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2021 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
17
/* Memory allocator for Ghostscript interpreter */
18
#include "gx.h"
19
#include "memory_.h"
20
#include "gsexit.h"
21
#include "ierrors.h"
22
#include "gsstruct.h"
23
#include "iref.h"   /* must precede iastate.h */
24
#include "iastate.h"
25
#include "igc.h"    /* for gs_gc_reclaim */
26
#include "ipacked.h"
27
#include "iutil.h"
28
#include "ivmspace.h"
29
#include "store.h"
30
31
/*
32
 * Define global and local instances.
33
 */
34
public_st_gs_dual_memory();
35
36
/* Initialize the allocator */
37
int
38
ialloc_init(gs_dual_memory_t *dmem, gs_memory_t * rmem, uint clump_size,
39
            bool level2)
40
89.2k
{
41
89.2k
    gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, clump_size);
42
89.2k
    gs_ref_memory_t *ilmem_stable = ialloc_alloc_state(rmem, clump_size);
43
89.2k
    gs_ref_memory_t *igmem = 0;
44
89.2k
    gs_ref_memory_t *igmem_stable = 0;
45
89.2k
    gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, clump_size);
46
89.2k
    int i;
47
48
89.2k
    if (ilmem == 0 || ilmem_stable == 0 || ismem == 0)
49
0
        goto fail;
50
89.2k
    ilmem->stable_memory = (gs_memory_t *)ilmem_stable;
51
89.2k
    if (level2) {
52
89.2k
        igmem = ialloc_alloc_state(rmem, clump_size);
53
89.2k
        igmem_stable = ialloc_alloc_state(rmem, clump_size);
54
89.2k
        if (igmem == 0 || igmem_stable == 0)
55
0
            goto fail;
56
89.2k
        igmem->stable_memory = (gs_memory_t *)igmem_stable;
57
89.2k
    } else
58
0
        igmem = ilmem, igmem_stable = ilmem_stable;
59
446k
    for (i = 0; i < countof(dmem->spaces_indexed); i++)
60
357k
        dmem->spaces_indexed[i] = 0;
61
89.2k
    dmem->space_local = ilmem;
62
89.2k
    dmem->space_global = igmem;
63
89.2k
    dmem->space_system = ismem;
64
89.2k
    dmem->spaces.vm_reclaim = gs_gc_reclaim; /* real GC */
65
89.2k
    dmem->reclaim = 0;    /* no interpreter GC yet */
66
    /* Level 1 systems have only local VM. */
67
89.2k
    igmem->space = avm_global;
68
89.2k
    igmem_stable->space = avm_global;
69
89.2k
    ilmem->space = avm_local; /* overrides if ilmem == igmem */
70
89.2k
    ilmem_stable->space = avm_local; /* ditto */
71
89.2k
    ismem->space = avm_system;
72
#   if IGC_PTR_STABILITY_CHECK
73
    igmem->space_id = (i_vm_global << 1) + 1;
74
    igmem_stable->space_id = i_vm_global << 1;
75
    ilmem->space_id = (i_vm_local << 1) + 1;  /* overrides if ilmem == igmem */
76
    ilmem_stable->space_id = i_vm_local << 1; /* ditto */
77
    ismem->space_id = (i_vm_system << 1);
78
#   endif
79
89.2k
    ialloc_set_space(dmem, avm_global);
80
89.2k
    return 0;
81
0
 fail:
82
0
    ialloc_free_state(igmem_stable);
83
0
    ialloc_free_state(igmem);
84
0
    ialloc_free_state(ismem);
85
0
    ialloc_free_state(ilmem_stable);
86
0
    ialloc_free_state(ilmem);
87
0
    return_error(gs_error_VMerror);
88
89.2k
}
89
90
/* Free the allocator */
91
void
92
ialloc_finit(gs_dual_memory_t *mem)
93
89.2k
{
94
89.2k
    if (mem != NULL) {
95
89.2k
        gs_ref_memory_t *ilmem = mem->space_local;
96
89.2k
        gs_ref_memory_t *igmem = mem->space_global;
97
89.2k
        gs_ref_memory_t *ismem = mem->space_system;
98
99
89.2k
        if (ilmem != NULL) {
100
89.2k
            gs_ref_memory_t *ilmem_stable = (gs_ref_memory_t *)(ilmem->stable_memory);
101
89.2k
            gs_memory_free_all((gs_memory_t *)ilmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
102
89.2k
            gs_memory_free_all((gs_memory_t *)ilmem, FREE_ALL_EVERYTHING, "ialloc_finit");
103
89.2k
        }
104
105
89.2k
        if (igmem != NULL) {
106
89.2k
            gs_ref_memory_t *igmem_stable = (gs_ref_memory_t *)(igmem->stable_memory);
107
89.2k
            gs_memory_free_all((gs_memory_t *)igmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
108
89.2k
            gs_memory_free_all((gs_memory_t *)igmem, FREE_ALL_EVERYTHING, "ialloc_finit");
109
89.2k
        }
110
111
89.2k
        if (ismem != NULL)
112
89.2k
            gs_memory_free_all((gs_memory_t *)ismem, FREE_ALL_EVERYTHING, "ialloc_finit");
113
89.2k
     }
114
89.2k
}
115
116
/* ================ Local/global VM ================ */
117
118
/* Get the space attribute of an allocator */
119
uint
120
imemory_space(const gs_ref_memory_t * iimem)
121
1.06G
{
122
1.06G
    return iimem->space;
123
1.06G
}
124
125
/* Select the allocation space. */
126
void
127
ialloc_set_space(gs_dual_memory_t * dmem, uint space)
128
282M
{
129
282M
    gs_ref_memory_t *mem = dmem->spaces_indexed[space >> r_space_shift];
130
131
282M
    dmem->current = mem;
132
282M
    dmem->current_space = mem->space;
133
282M
}
134
135
/* Get the l_new attribute of a current allocator. */
136
/* (A copy of the new_mask in the gs_dual_memory_t.) */
137
uint
138
imemory_new_mask(const gs_ref_memory_t *imem)
139
660M
{
140
660M
    return imem->new_mask;
141
660M
}
142
143
/* Get the save level of an allocator. */
144
int
145
imemory_save_level(const gs_ref_memory_t *imem)
146
9.16M
{
147
9.16M
    return imem->save_level;
148
9.16M
}
149
150
/* Reset the requests. */
151
void
152
ialloc_reset_requested(gs_dual_memory_t * dmem)
153
1.52M
{
154
1.52M
    dmem->space_system->gc_status.requested = 0;
155
1.52M
    dmem->space_global->gc_status.requested = 0;
156
1.52M
    dmem->space_local->gc_status.requested = 0;
157
1.52M
}
158
159
/* ================ Refs ================ */
160
161
#ifdef DEBUG
162
static int
163
ialloc_trace_space(const gs_ref_memory_t *imem)
164
{
165
    return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
166
}
167
#endif
168
169
/* Register a ref root. */
170
int
171
gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t **root,
172
                     void **pp, client_name_t cname)
173
1.33M
{
174
1.33M
    return gs_register_root(mem, root, ptr_ref_type, pp, cname);
175
1.33M
}
176
177
/*
178
 * As noted in iastate.h, every run of refs has an extra ref at the end
179
 * to hold relocation information for the garbage collector;
180
 * since sizeof(ref) % obj_align_mod == 0, we never need to
181
 * allocate any additional padding space at the end of the block.
182
 */
183
184
/* Allocate an array of refs. */
185
int
186
gs_alloc_ref_array(gs_ref_memory_t * mem, ref * parr, uint attrs,
187
                   uint num_refs, client_name_t cname)
188
1.54G
{
189
1.54G
    ref *obj;
190
1.54G
    int i;
191
192
    /* If we're allocating a run of refs already, */
193
    /* and we aren't about to overflow the maximum run length, use it. */
194
1.54G
    if (mem->cc && mem->cc->has_refs == true && mem->cc->rtop == mem->cc->cbot &&
195
1.54G
        num_refs < (mem->cc->ctop - mem->cc->cbot) / sizeof(ref) &&
196
1.54G
        mem->cc->rtop - (byte *) mem->cc->rcur + num_refs * sizeof(ref) <
197
1.52G
        max_size_st_refs
198
1.54G
        ) {
199
1.31G
        ref *end;
200
201
1.31G
        obj = (ref *) mem->cc->rtop - 1;    /* back up over last ref */
202
1.31G
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:+$ ]%s(%u) = "PRI_INTPTR"\n",
203
1.31G
                   ialloc_trace_space(mem), client_name_string(cname),
204
1.31G
                   num_refs, (intptr_t)obj);
205
1.31G
        mem->cc->rcur[-1].o_size += num_refs * sizeof(ref);
206
1.31G
        end = (ref *) (mem->cc->rtop = mem->cc->cbot +=
207
1.31G
                       num_refs * sizeof(ref));
208
1.31G
        make_mark(end - 1);
209
1.31G
    } else {
210
        /*
211
         * Allocate a new run.  We have to distinguish 3 cases:
212
         *      - Same clump: cc unchanged, end == cc->cbot.
213
         *      - Large clump: cc unchanged, end != cc->cbot.
214
         *      - New clump: cc changed.
215
         */
216
236M
        clump_t *cc = mem->cc;
217
236M
        ref *end;
218
236M
        alloc_change_t *cp = 0;
219
236M
        int code = 0;
220
221
236M
        if ((gs_memory_t *)mem != mem->stable_memory) {
222
232M
            code = alloc_save_change_alloc(mem, "gs_alloc_ref_array", &cp);
223
232M
            if (code < 0)
224
0
                return code;
225
232M
        }
226
236M
        obj = gs_alloc_struct_array((gs_memory_t *) mem, num_refs + 1,
227
236M
                                    ref, &st_refs, cname);
228
236M
        if (obj == 0)
229
270
            return_error(gs_error_VMerror);
230
        /* Set the terminating ref now. */
231
236M
        end = (ref *) obj + num_refs;
232
236M
        make_mark(end);
233
        /* Set has_refs in the clump. */
234
236M
        if (mem->cc && (mem->cc != cc || mem->cc->cbot == (byte *) (end + 1))) {
235
            /* Ordinary clump. */
236
231M
            mem->cc->rcur = (obj_header_t *) obj;
237
231M
            mem->cc->rtop = (byte *) (end + 1);
238
231M
            mem->cc->has_refs = true;
239
231M
        } else {
240
            /* Large clump. */
241
            /* This happens only for very large arrays, */
242
            /* so it doesn't need to be cheap. */
243
4.45M
            clump_locator_t cl;
244
245
4.45M
            cl.memory = mem;
246
4.45M
            cl.cp = mem->root;
247
            /* clump_locate_ptr() should *never* fail here */
248
4.45M
            if (clump_locate_ptr(obj, &cl)) {
249
4.45M
                cl.cp->has_refs = true;
250
4.45M
            }
251
0
            else {
252
0
                gs_abort((gs_memory_t *) mem);
253
0
            }
254
4.45M
        }
255
236M
        if (cp) {
256
13.1M
            mem->changes = cp;
257
13.1M
            cp->where = (ref_packed *)obj;
258
13.1M
        }
259
236M
    }
260
36.8G
    for (i = 0; i < num_refs; i++) {
261
35.2G
        make_null(&(obj[i]));
262
35.2G
    }
263
1.54G
    make_array(parr, attrs | mem->space, num_refs, obj);
264
1.54G
    return 0;
265
1.54G
}
266
267
/* Resize an array of refs.  Currently this is only implemented */
268
/* for shrinking, not for growing. */
269
int
270
gs_resize_ref_array(gs_ref_memory_t * mem, ref * parr,
271
                    uint new_num_refs, client_name_t cname)
272
11.0k
{
273
11.0k
    uint old_num_refs = r_size(parr);
274
11.0k
    uint diff;
275
11.0k
    ref *obj = parr->value.refs;
276
277
11.0k
    if (new_num_refs > old_num_refs || !r_has_type(parr, t_array))
278
0
        return_error(gs_error_Fatal);
279
11.0k
    diff = old_num_refs - new_num_refs;
280
    /* Check for LIFO.  See gs_free_ref_array for more details. */
281
11.0k
    if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
282
11.0k
        (byte *) (obj + (old_num_refs + 1)) == mem->cc->rtop
283
11.0k
        ) {
284
        /* Shorten the refs object. */
285
305
        ref *end = (ref *) (mem->cc->cbot = mem->cc->rtop -=
286
305
                            diff * sizeof(ref));
287
288
305
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$ ]%s(%u) "PRI_INTPTR"\n",
289
305
                   ialloc_trace_space(mem), client_name_string(cname), diff,
290
305
                   (intptr_t)obj);
291
305
        mem->cc->rcur[-1].o_size -= diff * sizeof(ref);
292
305
        make_mark(end - 1);
293
10.6k
    } else {
294
        /* Punt. */
295
10.6k
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$#]%s(%u) "PRI_INTPTR"\n",
296
10.6k
                   ialloc_trace_space(mem), client_name_string(cname), diff,
297
10.6k
                   (intptr_t)obj);
298
10.6k
        mem->lost.refs += diff * sizeof(ref);
299
10.6k
    }
300
11.0k
    r_set_size(parr, new_num_refs);
301
11.0k
    return 0;
302
11.0k
}
303
304
/* Deallocate an array of refs.  Only do this if LIFO, or if */
305
/* the array occupies an entire clump by itself. */
306
void
307
gs_free_ref_array(gs_ref_memory_t * mem, ref * parr, client_name_t cname)
308
66.8M
{
309
66.8M
    uint num_refs = r_size(parr);
310
66.8M
    ref *obj = parr->value.refs;
311
312
    /*
313
     * Compute the storage size of the array, and check for LIFO
314
     * freeing or a separate clump.  Note that the array might be packed;
315
     * for the moment, if it's anything but a t_array, punt.
316
     * The +1s are for the extra ref for the GC.
317
     */
318
66.8M
    if (!r_has_type(parr, t_array))
319
66.8M
        DO_NOTHING;    /* don't look for special cases */
320
18.3M
    else if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
321
18.3M
             (byte *) (obj + (num_refs + 1)) == mem->cc->rtop
322
18.3M
        ) {
323
64
        if ((obj_header_t *) obj == mem->cc->rcur) {
324
            /* Deallocate the entire refs object. */
325
59
            if ((gs_memory_t *)mem != mem->stable_memory)
326
53
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
327
59
            gs_free_object((gs_memory_t *) mem, obj, cname);
328
59
            mem->cc->rcur = 0;
329
59
            mem->cc->rtop = 0;
330
59
        } else {
331
            /* Deallocate it at the end of the refs object. */
332
5
            if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$ ]%s(%u) "PRI_INTPTR"\n",
333
5
                       ialloc_trace_space(mem), client_name_string(cname),
334
5
                       num_refs, (intptr_t)obj);
335
5
            mem->cc->rcur[-1].o_size -= num_refs * sizeof(ref);
336
5
            mem->cc->rtop = mem->cc->cbot = (byte *) (obj + 1);
337
5
            make_mark(obj);
338
5
        }
339
64
        return;
340
18.3M
    } else if (num_refs >= (mem->large_size / ARCH_SIZEOF_REF - 1)) {
341
        /* See if this array has a clump all to itself. */
342
        /* We only make this check when freeing very large objects, */
343
        /* so it doesn't need to be cheap. */
344
2.77M
        clump_locator_t cl;
345
346
2.77M
        cl.memory = mem;
347
2.77M
        cl.cp = mem->root;
348
2.77M
        if (clump_locate_ptr(obj, &cl) &&
349
2.77M
            obj == (ref *) ((obj_header_t *) (cl.cp->cbase) + 1) &&
350
2.77M
            (byte *) (obj + (num_refs + 1)) == cl.cp->cend
351
2.77M
            ) {
352
            /* Free the clump. */
353
2.77M
            if_debug4m('a', (const gs_memory_t *)mem, "[a%d:-$L]%s(%u) "PRI_INTPTR"\n",
354
2.77M
                       ialloc_trace_space(mem), client_name_string(cname),
355
2.77M
                       num_refs, (intptr_t)obj);
356
2.77M
            if ((gs_memory_t *)mem != mem->stable_memory) {
357
37.5k
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
358
37.5k
            }
359
2.77M
            alloc_free_clump(cl.cp, mem);
360
2.77M
            return;
361
2.77M
        }
362
2.77M
    }
363
    /* Punt, but fill the array with nulls so that there won't be */
364
    /* dangling references to confuse the garbage collector. */
365
66.8M
    if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$#]%s(%u) "PRI_INTPTR"\n",
366
64.0M
               ialloc_trace_space(mem), client_name_string(cname), num_refs,
367
64.0M
               (intptr_t)obj);
368
64.0M
    {
369
64.0M
        uint size;
370
371
64.0M
        switch (r_type(parr)) {
372
48.4M
            case t_shortarray:
373
48.4M
                size = num_refs * sizeof(ref_packed);
374
48.4M
                break;
375
0
            case t_mixedarray:{
376
                /* We have to parse the array to compute the storage size. */
377
0
                uint i = 0;
378
0
                const ref_packed *p = parr->value.packed;
379
380
0
                for (; i < num_refs; ++i)
381
0
                    p = packed_next(p);
382
0
                size = (const byte *)p - (const byte *)parr->value.packed;
383
0
                break;
384
0
            }
385
15.6M
            case t_array:
386
15.6M
                size = num_refs * sizeof(ref);
387
15.6M
                break;
388
0
            default:
389
0
                lprintf3("Unknown type 0x%x in free_ref_array(%u,"PRI_INTPTR")!",
390
0
                         r_type(parr), num_refs, (intptr_t)obj);
391
0
                return;
392
64.0M
        }
393
        /*
394
         * If there are any leftover packed elements, we don't
395
         * worry about them, since they can't be dangling references.
396
         */
397
64.0M
        refset_null_new(obj, size / sizeof(ref), 0);
398
64.0M
        mem->lost.refs += size;
399
64.0M
    }
400
64.0M
}
401
402
/* Allocate a string ref. */
403
int
404
gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
405
                    uint attrs, uint nbytes, client_name_t cname)
406
34.8k
{
407
34.8k
    byte *str = gs_alloc_string((gs_memory_t *) mem, nbytes, cname);
408
409
34.8k
    if (str == 0)
410
0
        return_error(gs_error_VMerror);
411
34.8k
    make_string(psref, attrs | mem->space, nbytes, str);
412
34.8k
    return 0;
413
34.8k
}