mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-01-28 07:50:48 -08:00
Catch-up merge from master into branch/2023-05-31/hash-arrays
This commit is contained in:
commit
4823617e1b
23 changed files with 1200 additions and 127 deletions
240
mps/code/addrobj.c
Normal file
240
mps/code/addrobj.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/* addrobj.c: BASE ADDRESS FROM INTERIOR POINTER TEST
|
||||
*
|
||||
* Copyright (c) 2023 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview This test is for mps_addr_object(). Its intention is to
|
||||
* verify that the function returns the appropriate base pointer to an
|
||||
* object when provided with an interior pointer. It also tests that the
|
||||
* function fails appropriately when the provided with a pointer to
|
||||
* unmanaged memory, or to an object in a pool that doesn't support this
|
||||
* feature.
|
||||
*
|
||||
* .limitations Objects that have been moved should cause the function to
|
||||
* fail with MPS_RES_FAIL, however this is not tested. It could be tested if
|
||||
* a testbench deliberately created a forwarding object, however this might
|
||||
* confuse a pool that does automatic garbage collection such as AMC or AMCZ,
|
||||
* so any such test would need to be designed to handle that.
|
||||
* This test only examines behaviour in AMCZ and MVFF pools, i.e. A pool (AMCZ)
|
||||
* which currently implements mps_addr_object() and one (MVFF) that doesn't.
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include "mpscmvff.h"
|
||||
#include "stdio.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Define an object size to allocate. The size chosen doesn't matter much, except that this testbench assumes
|
||||
that the object is large enough that a pointer could point to the interior of the object, without also
|
||||
pointing to the base pointer of the object at the same time. For char pointers, this is probably 2 bytes.
|
||||
Since we are using the Dylan library, we define the size of the object in terms of Dylan slots. See
|
||||
fmtdytst.c for details of the Dylan object structure.*/
|
||||
#define N_SLOT_TESTOBJ 100
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
mps_arena_t arena;
|
||||
mps_pool_t amcz_pool, mvff_pool;
|
||||
mps_ap_t obj_ap;
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_root_t testobj_root;
|
||||
mps_res_t res;
|
||||
/* In another testbench (extcon.c) we observed unreliable failures to do with registering the cold end
|
||||
of the stack. See GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
declare this as a separate root. */
|
||||
static mps_addr_t testobj;
|
||||
mps_addr_t out, in;
|
||||
|
||||
/* Create arena */
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "mps_arena_create_k");
|
||||
|
||||
|
||||
/* INTRO TO TESTS: There are several tests. They test the expected "normal" operation of the
|
||||
function, using an interior pointer, also corner cases where the interior pointer equals the
|
||||
base pointer, where it equals the limit pointer. We also test asking about an address in unmanaged
|
||||
memory, and about an address in a pool which currently does not support mps_addr_object. If you write
|
||||
more tests, describe them here.*/
|
||||
|
||||
|
||||
/* TEST 1: Test using an interior pointer in an object in an AMCZ pool.
|
||||
At the time of writing this test, the AMCZ pool is the only pool where
|
||||
there exists a requirement to provide base addresses from interior pointers.
|
||||
Currently, the AMCZ pool (and by extension, the AMC pool which shares the same
|
||||
module as AMCZ) is the only pool for which mps_addr_object is implemented */
|
||||
|
||||
/* Use the dylan format for convenience */
|
||||
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt");
|
||||
|
||||
/* Create the pool */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
die(mps_pool_create_k(&amcz_pool, arena, mps_class_amcz(), args), "mps_pool_create_k amcz");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Create an area of ambiguous pointers to keep the object alive and in place, in this case
|
||||
the area only contains room for a single reference since we are only using one object to test */
|
||||
die(mps_root_create_area(&testobj_root, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&testobj, &testobj+1,
|
||||
mps_scan_area, NULL),
|
||||
"mps_root_create_area");
|
||||
|
||||
/* Create the allocation point */
|
||||
die(mps_ap_create_k(&obj_ap, amcz_pool, mps_args_none), "mps_ap_create_k");
|
||||
|
||||
/* Make a Dylan object, size = (N_SLOT_TESTOBJ+2) * sizeof(mps_word_t).
|
||||
(See fmtdytst.c for size calculation) */
|
||||
{
|
||||
/* Because make_dylan_vector returns its pointer-to-object as an mps_word_t rather than an
|
||||
mps_addr_t, and commits the object, we need to somehow safely allocate our object without
|
||||
type punning and without risking that our object be destroyed.
|
||||
Rather than redefine our reference table with type mps_word_t, which hides the intention of the table,
|
||||
park the arena to disable garbage collection. Allocate our dylan object on the (unregistered) stack
|
||||
storing its address in an mps_word_t. Then store this mps_word_t as an mps_addr_t in our reference
|
||||
table, and release the arena since our object is now safely pinned.
|
||||
Another approach would be to create another static registered root for ambiguous references of type
|
||||
mps_word_t and then copy to the mps_addr_t root, which would avoid needing to park the arena.
|
||||
*/
|
||||
mps_word_t p_word;
|
||||
mps_arena_park(arena);
|
||||
die(make_dylan_vector(&p_word, obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
|
||||
/* If we hadn't parked the arena, our vector might have been GC'd here */
|
||||
testobj = (mps_addr_t)p_word;
|
||||
mps_arena_release(arena);
|
||||
}
|
||||
|
||||
/* Construct a pointer to roughly halfway inside the object */
|
||||
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ/2) * sizeof(mps_word_t));
|
||||
|
||||
/* Ensure that this is an interior pointer, and not the base pointer,
|
||||
since we want to make sure we are testing with a true interior pointer and not
|
||||
one that also happens to be the base pointer. This Insist is intended to protect
|
||||
against the testbench losing its ability to test "true" interior pointers (i.e. ones
|
||||
which don't match the base pointer) if the test object sizes were changed to be very
|
||||
small. Note that we don't currently consider the "limit" of the object as a corner case
|
||||
(so we don't Insist(in != limit) ) but we do consider limit+1, i.e. the pointer to the
|
||||
next object to be a corner case. This test could be updated to consider in == limit as a
|
||||
corner case. */
|
||||
Insist(in > testobj);
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(out == testobj);
|
||||
Insist(res == MPS_RES_OK);
|
||||
printf("Interior pointer input: passed\n");
|
||||
|
||||
|
||||
/* TEST 2: Test using the base pointer itself as an input*/
|
||||
|
||||
in = testobj;
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(out == testobj);
|
||||
Insist(res == MPS_RES_OK);
|
||||
printf("Base pointer input: passed\n");
|
||||
|
||||
|
||||
|
||||
/* TEST 3: Test using a pointer one-off-the-end of the object*/
|
||||
|
||||
in = (mps_addr_t)((char *)testobj + (N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
|
||||
|
||||
/* Do Test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
Insist(res == MPS_RES_FAIL);
|
||||
printf("Pointer to next object input: passed\n");
|
||||
|
||||
|
||||
/* Clean up from above tests */
|
||||
mps_root_destroy(testobj_root);
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(amcz_pool);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
|
||||
|
||||
/* TEST 4: Test using a pointer in unmanaged memory */
|
||||
|
||||
/* Use malloc to allocate non-mps-managed memory on the heap */
|
||||
in = malloc(sizeof(mps_word_t));
|
||||
Insist(NULL != in);
|
||||
|
||||
/* Do the test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
|
||||
/* Expect MPS to fail to find a base pointer for addresses not in managed memory */
|
||||
Insist(res == MPS_RES_FAIL);
|
||||
printf("Pointer to unmanaged memory input: passed\n");
|
||||
|
||||
/* clean up from this test */
|
||||
if (NULL != in)
|
||||
free(in);
|
||||
|
||||
|
||||
/* TEST 5: Test using a pointer in a pool which currently doesn't implement mps_addr_object */
|
||||
|
||||
/* Create mvff pool for which mps_addr_object is not implemented */
|
||||
die(mps_pool_create_k(&mvff_pool, arena, mps_class_mvff(), mps_args_none), "mps_pool_create_k mvff");
|
||||
|
||||
/* allocate an object (just some memory) in this pool */
|
||||
die(mps_alloc(&in, mvff_pool, sizeof(mps_word_t)), "mps_alloc");
|
||||
|
||||
/* Do the test */
|
||||
res = mps_addr_object(&out, arena, in);
|
||||
|
||||
Insist(res == MPS_RES_UNIMPL);
|
||||
printf("Pointer to object in pool where mps_addr_object not implemented: passed\n");
|
||||
|
||||
|
||||
/* If more tests are added here, briefly describe them above under "INTRO TO TESTS" comment */
|
||||
|
||||
/* Final clean up */
|
||||
mps_free(mvff_pool, in, sizeof(mps_word_t));
|
||||
mps_pool_destroy(mvff_pool);
|
||||
mps_arena_destroy(arena);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
testlib_init(argc, argv);
|
||||
|
||||
test_main();
|
||||
|
||||
printf("%s: Conculsion, failed to find any defects.\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
|
@ -1364,6 +1364,22 @@ Bool ArenaHasAddr(Arena arena, Addr addr)
|
|||
return TractOfAddr(&tract, arena, addr);
|
||||
}
|
||||
|
||||
/* ArenaAddrObject -- return base pointer of managed object */
|
||||
Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr)
|
||||
{
|
||||
Tract tract;
|
||||
|
||||
AVER(pReturn != NULL);
|
||||
AVERT(Arena, arena);
|
||||
|
||||
if (!TractOfAddr(&tract, arena, addr)) {
|
||||
/* address does not belong to the arena */
|
||||
return ResFAIL;
|
||||
}
|
||||
|
||||
return PoolAddrObject(pReturn, TractPool(tract), addr);
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
|
|
|
|||
|
|
@ -428,6 +428,10 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
|
|||
{
|
||||
Chunk chunk;
|
||||
VMChunk vmChunk;
|
||||
Arena arena;
|
||||
Addr base;
|
||||
Size size;
|
||||
VMArena vmArena;
|
||||
|
||||
AVERT(Tree, tree);
|
||||
AVER(closure == UNUSED_POINTER);
|
||||
|
|
@ -437,8 +441,14 @@ static Bool vmChunkDestroy(Tree tree, void *closure)
|
|||
AVERT(Chunk, chunk);
|
||||
vmChunk = Chunk2VMChunk(chunk);
|
||||
AVERT(VMChunk, vmChunk);
|
||||
arena = ChunkArena(chunk);
|
||||
vmArena = MustBeA(VMArena, arena);
|
||||
base = chunk->base;
|
||||
size = ChunkSize(chunk);
|
||||
|
||||
(void)vmArenaUnmapSpare(ChunkArena(chunk), ChunkSize(chunk), chunk);
|
||||
(*vmArena->contracted)(arena, base, size);
|
||||
|
||||
(void)vmArenaUnmapSpare(arena, size, chunk);
|
||||
|
||||
SparseArrayFinish(&vmChunk->pages);
|
||||
|
||||
|
|
@ -778,6 +788,7 @@ static void VMArenaDestroy(Arena arena)
|
|||
* <design/arena#.chunk.delete> */
|
||||
arena->primary = NULL;
|
||||
TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy, UNUSED_POINTER);
|
||||
AVER(arena->chunkTree == TreeEMPTY);
|
||||
|
||||
/* Must wait until the chunks are destroyed, since vmChunkDestroy
|
||||
calls vmArenaUnmapSpare which uses the spare land. */
|
||||
|
|
@ -1223,7 +1234,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
|
|||
{
|
||||
Chunk chunk;
|
||||
Arena arena = closure;
|
||||
VMArena vmArena = MustBeA(VMArena, arena);
|
||||
|
||||
AVERT(Tree, tree);
|
||||
|
||||
|
|
@ -1232,11 +1242,6 @@ static Bool vmChunkCompact(Tree tree, void *closure)
|
|||
if(chunk != arena->primary
|
||||
&& BTIsResRange(chunk->allocTable, 0, chunk->pages))
|
||||
{
|
||||
Addr base = chunk->base;
|
||||
Size size = ChunkSize(chunk);
|
||||
/* Callback before destroying the chunk, as the arena is (briefly)
|
||||
invalid afterwards. See job003893. */
|
||||
(*vmArena->contracted)(arena, base, size);
|
||||
vmChunkDestroy(tree, UNUSED_POINTER);
|
||||
return TRUE;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ LIB_TARGETS=mps.a mpsplan.a
|
|||
|
||||
TEST_TARGETS=\
|
||||
abqtest \
|
||||
addrobj \
|
||||
airtest \
|
||||
amcss \
|
||||
amcsshe \
|
||||
|
|
@ -267,6 +268,7 @@ TEST_TARGETS=\
|
|||
btcv \
|
||||
bttest \
|
||||
djbench \
|
||||
extcon \
|
||||
finalcv \
|
||||
finaltest \
|
||||
forktest \
|
||||
|
|
@ -445,6 +447,9 @@ ifdef VARIETY
|
|||
$(PFM)/$(VARIETY)/abqtest: $(PFM)/$(VARIETY)/abqtest.o \
|
||||
$(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/addrobj: $(PFM)/$(VARIETY)/addrobj.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/airtest: $(PFM)/$(VARIETY)/airtest.o \
|
||||
$(FMTSCMOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
|
|
@ -487,6 +492,9 @@ $(PFM)/$(VARIETY)/bttest: $(PFM)/$(VARIETY)/bttest.o \
|
|||
$(PFM)/$(VARIETY)/djbench: $(PFM)/$(VARIETY)/djbench.o \
|
||||
$(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)/$(VARIETY)/extcon: $(PFM)/$(VARIETY)/extcon.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
$(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \
|
||||
$(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a
|
||||
|
||||
|
|
|
|||
|
|
@ -177,6 +177,9 @@ $(PFM)\cool\mps.lib: $(MPMOBJ)
|
|||
$(PFM)\$(VARIETY)\abqtest.exe: $(PFM)\$(VARIETY)\abqtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\addrobj.exe: $(PFM)\$(VARIETY)\addrobj.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\airtest.exe: $(PFM)\$(VARIETY)\airtest.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTSCHEMEOBJ) $(TESTLIBOBJ)
|
||||
|
||||
|
|
@ -225,6 +228,9 @@ $(PFM)\$(VARIETY)\cvmicv.exe: $(PFM)\$(VARIETY)\cvmicv.obj \
|
|||
$(PFM)\$(VARIETY)\djbench.exe: $(PFM)\$(VARIETY)\djbench.obj \
|
||||
$(TESTLIBOBJ) $(TESTTHROBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\extcon.exe: $(PFM)\$(VARIETY)\extcon.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
$(PFM)\$(VARIETY)\finalcv.exe: $(PFM)\$(VARIETY)\finalcv.obj \
|
||||
$(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ LIB_TARGETS=mps.lib
|
|||
|
||||
TEST_TARGETS=\
|
||||
abqtest.exe \
|
||||
addrobj.exe \
|
||||
airtest.exe \
|
||||
amcss.exe \
|
||||
amcsshe.exe \
|
||||
|
|
@ -73,6 +74,7 @@ TEST_TARGETS=\
|
|||
btcv.exe \
|
||||
bttest.exe \
|
||||
djbench.exe \
|
||||
extcon.exe \
|
||||
finalcv.exe \
|
||||
finaltest.exe \
|
||||
fotest.exe \
|
||||
|
|
|
|||
306
mps/code/extcon.c
Normal file
306
mps/code/extcon.c
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/* extcon.c: ARENA EXTENDED AND CONTRACTED CALLBACK TEST
|
||||
*
|
||||
* $Id$
|
||||
* Copyright (c) 2022-2023 Ravenbrook Limited. See end of file for license.
|
||||
*
|
||||
* .overview: This test case allocates a bunch of large objects, of a size
|
||||
* similar to the size of the arena, to force the arena to extend. It then
|
||||
* discards the base pointers to those objects, and forces a collection.
|
||||
*
|
||||
* .limitations: This test checks that the EXTENDED and CONTRACTED
|
||||
* callbacks were called at least once, and that they are called the
|
||||
* same number of times. It does not check that the extensions and
|
||||
* contractions themselves were performed correctly, nor does it check
|
||||
* that an appropriate number of extensions and contractions took
|
||||
* place, nor does it check that they took place at sensible times.
|
||||
*
|
||||
* .dylan: This test uses Dylan format objects in common with most
|
||||
* other tests for convenience and brevity.
|
||||
*/
|
||||
|
||||
#include "mps.h"
|
||||
#include "testlib.h"
|
||||
#include "fmtdy.h"
|
||||
#include "fmtdytst.h"
|
||||
#include "mpsavm.h"
|
||||
#include "mpscamc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Number of test objects to allocate */
|
||||
#define N_TESTOBJ 100
|
||||
/* The number of slots determines the size of each object */
|
||||
#define N_SLOT_TESTOBJ 10000
|
||||
/* The initial arena size is requested to be bigger the test object by
|
||||
this many bytes */
|
||||
#define SIZEDIFF 10
|
||||
|
||||
/* Set alignment to mps_word_ts */
|
||||
#define ALIGNMENT sizeof(mps_word_t)
|
||||
|
||||
/* Align size upwards to the next multiple of the word size. */
|
||||
#define ALIGN_WORD(size) \
|
||||
(((size) + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
|
||||
|
||||
/* Global objects*/
|
||||
static mps_arena_t arena; /* the arena */
|
||||
static mps_pool_t obj_pool; /* pool for test objects */
|
||||
static mps_ap_t obj_ap; /* allocation point used to allocate objects */
|
||||
|
||||
/* Count of number of arena contractions and extensions */
|
||||
static int n_contract = 0;
|
||||
static int n_extend = 0;
|
||||
|
||||
/* Callback functions for arena extension and contraction */
|
||||
static void arena_extended_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
|
||||
{
|
||||
testlib_unused(arena_in);
|
||||
testlib_unused(addr);
|
||||
testlib_unused(size);
|
||||
printf("Arena extended by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
|
||||
n_extend++;
|
||||
}
|
||||
|
||||
static void arena_contracted_cb(mps_arena_t arena_in, mps_addr_t addr, size_t size)
|
||||
{
|
||||
testlib_unused(arena_in);
|
||||
testlib_unused(addr);
|
||||
testlib_unused(size);
|
||||
printf("Arena contracted by %"PRIuLONGEST" bytes\n", (ulongest_t)size);
|
||||
n_contract++;
|
||||
}
|
||||
|
||||
/* Messages for testbench debugging */
|
||||
static void print_messages(void)
|
||||
{
|
||||
mps_message_type_t type;
|
||||
|
||||
while (mps_message_queue_type(&type, arena)) {
|
||||
mps_message_t message;
|
||||
|
||||
cdie(mps_message_get(&message, arena, type),
|
||||
"get");
|
||||
|
||||
switch(type) {
|
||||
case mps_message_type_gc_start():
|
||||
printf("GC start at %"PRIuLONGEST": %s\n",
|
||||
(ulongest_t)mps_message_clock(arena, message),
|
||||
mps_message_gc_start_why(arena, message));
|
||||
break;
|
||||
|
||||
case mps_message_type_gc():
|
||||
printf("GC end at %"PRIuLONGEST" "
|
||||
"condemned %"PRIuLONGEST" "
|
||||
"not condemned %"PRIuLONGEST" "
|
||||
"live %"PRIuLONGEST"\n",
|
||||
(ulongest_t)mps_message_clock(arena, message),
|
||||
(ulongest_t)mps_message_gc_condemned_size(arena, message),
|
||||
(ulongest_t)mps_message_gc_not_condemned_size(arena, message),
|
||||
(ulongest_t)mps_message_gc_live_size(arena, message));
|
||||
break;
|
||||
|
||||
default:
|
||||
cdie(0, "message type");
|
||||
break;
|
||||
}
|
||||
|
||||
mps_message_discard(arena, message);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disabling inlining is necessary (but perhaps not sufficient) if using stack roots.
|
||||
See comment below with link to GitHub issue*/
|
||||
ATTRIBUTE_NOINLINE
|
||||
static void test_main(void *cold_stack_end)
|
||||
{
|
||||
mps_fmt_t obj_fmt;
|
||||
mps_thr_t thread;
|
||||
mps_root_t stack_root, testobj_root;
|
||||
size_t arena_size, obj_size;
|
||||
int i;
|
||||
/* In the original version of extcon this was a stack root, but we
|
||||
observed unreliable failures to do with registering the cold end
|
||||
of the stack. See GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
declare this as a separate root. */
|
||||
static mps_word_t testobj[N_TESTOBJ];
|
||||
|
||||
/* The testobj array must be below (on all current Posix platforms)
|
||||
the cold end of the stack in order for the MPS to scan it. We
|
||||
have observed a Heisenbug where GCC will inline test_main into
|
||||
main and lose this condition if the expression below is removed.
|
||||
This is a problem we are analysing in GitHub issue #210
|
||||
<https://github.com/Ravenbrook/mps/issues/210>. For now, we
|
||||
disable this Insist to allow the test to run with a static
|
||||
testobj array. */
|
||||
#if 0
|
||||
Insist((void *)&testobj[N_TESTOBJ] <= cold_stack_end);
|
||||
if ((void *)&testobj[N_TESTOBJ] > cold_stack_end)
|
||||
printf("Cold stack marker invalid!\n");
|
||||
else
|
||||
printf("Cold stack marker probably valid.\n");
|
||||
#endif
|
||||
|
||||
/* Make initial arena size slightly bigger than the test object size to force an extension as early as possible */
|
||||
/* See definition of make_dylan_vector() in fmtdytst.c for calculation of vector size */
|
||||
obj_size = ALIGN_WORD((N_SLOT_TESTOBJ + 2) * sizeof(mps_word_t));
|
||||
arena_size = ALIGN_WORD(obj_size + SIZEDIFF);
|
||||
|
||||
/* Create arena and register callbacks */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arena_size);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)&arena_extended_cb);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)&arena_contracted_cb);
|
||||
die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "mps_arena_create_k");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
printf("Initial reservation %"PRIuLONGEST".\n", (ulongest_t)mps_arena_reserved(arena));
|
||||
|
||||
die(dylan_fmt(&obj_fmt, arena), "dylan_fmt()");
|
||||
|
||||
/* Create new pool */
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt);
|
||||
die(mps_pool_create_k(&obj_pool, arena, mps_class_amcz(), args),
|
||||
"mps_pool_create_k");
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
/* Register thread */
|
||||
die(mps_thread_reg(&thread, arena), "Thread reg");
|
||||
|
||||
/* Register stack roots */
|
||||
/* Since this testbench is currently not using a stack root, #IF 0 this out */
|
||||
testlib_unused(cold_stack_end);
|
||||
testlib_unused(stack_root);
|
||||
#if 0
|
||||
die(mps_root_create_thread(&stack_root, arena, thread, cold_stack_end), "Create Stack root");
|
||||
#endif
|
||||
|
||||
/* Register ambiguous array of object roots. */
|
||||
die(mps_root_create_area(&testobj_root, arena,
|
||||
mps_rank_ambig(), (mps_rm_t)0,
|
||||
&testobj[0], &testobj[N_TESTOBJ],
|
||||
mps_scan_area, NULL),
|
||||
"root_create_area(testobj)");
|
||||
|
||||
/* Create allocation point */
|
||||
die(mps_ap_create_k(&obj_ap, obj_pool, mps_args_none), "Create Allocation point");
|
||||
|
||||
mps_message_type_enable(arena, mps_message_type_gc_start());
|
||||
mps_message_type_enable(arena, mps_message_type_gc());
|
||||
|
||||
/* Allocate objects and force arena extension */
|
||||
for (i = 0; i < N_TESTOBJ; i++) {
|
||||
|
||||
die(make_dylan_vector(&testobj[i], obj_ap, N_SLOT_TESTOBJ), "make_dylan_vector");
|
||||
|
||||
printf("Object %d committed. "
|
||||
"Arena reserved: %"PRIuLONGEST".\n",
|
||||
i,
|
||||
(ulongest_t)mps_arena_reserved(arena));
|
||||
|
||||
print_messages();
|
||||
}
|
||||
|
||||
/* overwrite all the references to the objects*/
|
||||
for (i = 0; i < N_TESTOBJ; i++) {
|
||||
|
||||
/* bonus test of mps_addr_object */
|
||||
#if 0 /* Comment this out until mps_addr_object becomes available. */
|
||||
mps_addr_t out;
|
||||
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
|
||||
|
||||
/* use "i" to as a convenient way to generate different interior pointers
|
||||
To guarantee the i index will give us an interior pointer the number of test
|
||||
objects must be <= the number of integers in each object */
|
||||
Insist(N_TESTOBJ <= N_INT_TESTOBJ);
|
||||
die(mps_addr_object(&out, arena, &(testobj[i])->int_array[i]), "Address object");
|
||||
|
||||
Insist(out == testobj[i]);
|
||||
|
||||
/* end piggy back testbench */
|
||||
#endif
|
||||
|
||||
/* now overwrite the ref */
|
||||
testobj[i] = (mps_word_t)NULL;
|
||||
|
||||
print_messages();
|
||||
}
|
||||
|
||||
/* Collect */
|
||||
mps_arena_collect(arena);
|
||||
|
||||
print_messages();
|
||||
|
||||
/* Clean up */
|
||||
mps_root_destroy(testobj_root);
|
||||
/* mps_root_destroy(stack_root);*/ /*commented out while not using stack root */
|
||||
mps_thread_dereg(thread);
|
||||
mps_ap_destroy(obj_ap);
|
||||
mps_pool_destroy(obj_pool);
|
||||
mps_fmt_destroy(obj_fmt);
|
||||
mps_arena_destroy(arena);
|
||||
|
||||
/* Destroying the arena should cause contraction callbacks on all
|
||||
remaining chunks, even if they had contents. */
|
||||
Insist(n_extend == n_contract);
|
||||
|
||||
printf("Arena extended %d times\n", n_extend);
|
||||
printf("Arena contracted %d times\n", n_contract);
|
||||
|
||||
/* comment out some diagnostics for investigating issue #210 mentioned above */
|
||||
#if 0
|
||||
printf("&testobj[N_TESTOBJ] = %p\n", (void *)&testobj[N_TESTOBJ]);
|
||||
printf("cold_stack_end = %p\n", cold_stack_end);
|
||||
#endif
|
||||
if (n_extend == 0)
|
||||
printf("No callbacks received upon arena extended!\n");
|
||||
if (n_contract == 0)
|
||||
printf("No callbacks received upon arena contracted!\n");
|
||||
|
||||
if (n_contract == 0 || n_extend == 0)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
void *stack_marker = &stack_marker;
|
||||
|
||||
testlib_init(argc, argv);
|
||||
|
||||
test_main(stack_marker);
|
||||
|
||||
printf("%s: Conclusion: Failed to find any defects.\n", argv[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2022-2023 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
|
@ -236,6 +236,7 @@ extern Res PoolTraceBegin(Pool pool, Trace trace);
|
|||
extern void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
|
||||
extern Size PoolTotalSize(Pool pool);
|
||||
extern Size PoolFreeSize(Pool pool);
|
||||
extern Res PoolAddrObject(Addr *pReturn, Pool pool, Addr addr);
|
||||
|
||||
extern Res PoolAbsInit(Pool pool, Arena arena, PoolClass klass, ArgList arg);
|
||||
extern void PoolAbsFinish(Inst inst);
|
||||
|
|
@ -267,6 +268,7 @@ extern void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p);
|
|||
extern PoolDebugMixin PoolNoDebugMixin(Pool pool);
|
||||
extern BufferClass PoolNoBufferClass(void);
|
||||
extern Size PoolNoSize(Pool pool);
|
||||
extern Res PoolTrivAddrObject(Addr *pReturn, Pool pool, Addr addr);
|
||||
|
||||
/* See .critical.macros. */
|
||||
#define PoolFreeMacro(pool, old, size) Method(Pool, pool, free)(pool, old, size)
|
||||
|
|
@ -536,6 +538,7 @@ extern Res ArenaStartCollect(Globals globals, TraceStartWhy why);
|
|||
extern Res ArenaCollect(Globals globals, TraceStartWhy why);
|
||||
extern Bool ArenaBusy(Arena arena);
|
||||
extern Bool ArenaHasAddr(Arena arena, Addr addr);
|
||||
extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr);
|
||||
extern void ArenaChunkInsert(Arena arena, Chunk chunk);
|
||||
extern void ArenaChunkRemoved(Arena arena, Chunk chunk);
|
||||
extern void ArenaAccumulateTime(Arena arena, Clock start, Clock now);
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ typedef struct mps_pool_class_s {
|
|||
PoolRampEndMethod rampEnd; /* end a ramp pattern */
|
||||
PoolFramePushMethod framePush; /* push an allocation frame */
|
||||
PoolFramePopMethod framePop; /* pop an allocation frame */
|
||||
PoolAddrObjectMethod addrObject; /* return object's base pointer */
|
||||
PoolFreeWalkMethod freewalk; /* walk over free blocks */
|
||||
PoolBufferClassMethod bufferClass; /* default BufferClass of pool */
|
||||
PoolDebugMixinMethod debugMixin; /* find the debug mixin, if any */
|
||||
|
|
|
|||
|
|
@ -214,6 +214,7 @@ typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn,
|
|||
Pool pool, Buffer buf);
|
||||
typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf,
|
||||
AllocFrame frame);
|
||||
typedef Res (*PoolAddrObjectMethod)(Addr *pReturn, Pool pool, Addr addr);
|
||||
typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockVisitor f, void *p);
|
||||
typedef BufferClass (*PoolBufferClassMethod)(void);
|
||||
typedef PoolDebugMixin (*PoolDebugMixinMethod)(Pool pool);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,13 @@ typedef mps_addr_t (*mps_fmt_isfwd_t)(mps_addr_t);
|
|||
typedef void (*mps_fmt_pad_t)(mps_addr_t, size_t);
|
||||
typedef mps_addr_t (*mps_fmt_class_t)(mps_addr_t);
|
||||
|
||||
/* Callbacks indicating that the arena has extended or contracted.
|
||||
* These are used to register chunks with RtlInstallFunctionTableCallback
|
||||
* <https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback>
|
||||
* so that the client can unwind the stack through functions in the arena.
|
||||
*/
|
||||
typedef void (*mps_arena_extended_t)(mps_arena_t, void *, size_t);
|
||||
typedef void (*mps_arena_contracted_t)(mps_arena_t, void *, size_t);
|
||||
|
||||
/* Keyword argument lists */
|
||||
|
||||
|
|
@ -171,6 +178,12 @@ extern const struct mps_key_s _mps_key_ARENA_SIZE;
|
|||
extern const struct mps_key_s _mps_key_ARENA_ZONED;
|
||||
#define MPS_KEY_ARENA_ZONED (&_mps_key_ARENA_ZONED)
|
||||
#define MPS_KEY_ARENA_ZONED_FIELD b
|
||||
extern const struct mps_key_s _mps_key_arena_extended;
|
||||
#define MPS_KEY_ARENA_EXTENDED (&_mps_key_arena_extended)
|
||||
#define MPS_KEY_ARENA_EXTENDED_FIELD fun
|
||||
extern const struct mps_key_s _mps_key_arena_contracted;
|
||||
#define MPS_KEY_ARENA_CONTRACTED (&_mps_key_arena_contracted)
|
||||
#define MPS_KEY_ARENA_CONTRACTED_FIELD fun
|
||||
extern const struct mps_key_s _mps_key_FORMAT;
|
||||
#define MPS_KEY_FORMAT (&_mps_key_FORMAT)
|
||||
#define MPS_KEY_FORMAT_FIELD format
|
||||
|
|
@ -835,6 +848,8 @@ extern mps_res_t _mps_fix2(mps_ss_t, mps_addr_t *);
|
|||
(ss)->_ufs = _mps_ufs; \
|
||||
MPS_END
|
||||
|
||||
/* Misc interface */
|
||||
extern mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr);
|
||||
|
||||
#endif /* mps_h */
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@
|
|||
buildPhases = (
|
||||
);
|
||||
dependencies = (
|
||||
319F7A192A30D2F000E5B418 /* PBXTargetDependency */,
|
||||
3104AFF6156D37BC000A585A /* PBXTargetDependency */,
|
||||
3114A644156E94FB001E0AA3 /* PBXTargetDependency */,
|
||||
22FACEF1188809B5000FDBC1 /* PBXTargetDependency */,
|
||||
|
|
@ -89,8 +90,6 @@
|
|||
3114A677156E961C001E0AA3 /* PBXTargetDependency */,
|
||||
3114A612156E943B001E0AA3 /* PBXTargetDependency */,
|
||||
22B2BC3D18B643B300C33E63 /* PBXTargetDependency */,
|
||||
2291A5E6175CB207001D4920 /* PBXTargetDependency */,
|
||||
2291A5E8175CB20E001D4920 /* PBXTargetDependency */,
|
||||
3114A5CC156E932C001E0AA3 /* PBXTargetDependency */,
|
||||
3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */,
|
||||
22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */,
|
||||
|
|
@ -158,16 +157,6 @@
|
|||
2291A5B5175CAB2F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
|
||||
2291A5B7175CAB2F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
2291A5BE175CAB4E001D4920 /* awlutth.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5A9175CAA9B001D4920 /* awlutth.c */; };
|
||||
2291A5C5175CAFCA001D4920 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
|
||||
2291A5C6175CAFCA001D4920 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
|
||||
2291A5C7175CAFCA001D4920 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
|
||||
2291A5C8175CAFCA001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
|
||||
2291A5CB175CAFCA001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
2291A5D8175CB05F001D4920 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
|
||||
2291A5D9175CB05F001D4920 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
|
||||
2291A5DA175CB05F001D4920 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
|
||||
2291A5DB175CB05F001D4920 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
|
||||
2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
2291A5ED175CB5E2001D4920 /* landtest.c in Sources */ = {isa = PBXBuildFile; fileRef = 2291A5E9175CB4EC001D4920 /* landtest.c */; };
|
||||
22B2BC2E18B6434F00C33E63 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; };
|
||||
22B2BC3718B6437C00C33E63 /* scheme-advanced.c in Sources */ = {isa = PBXBuildFile; fileRef = 22B2BC2B18B6434000C33E63 /* scheme-advanced.c */; };
|
||||
|
|
@ -301,6 +290,12 @@
|
|||
3124CAFC156BE82900753214 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
3150AE53156ABA2500A6E22A /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
318DA8D31892B27E0089718C /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
|
||||
319F7A082A30D08500E5B418 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; };
|
||||
319F7A0A2A30D08500E5B418 /* fmtdy.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC6156BE48D00753214 /* fmtdy.c */; };
|
||||
319F7A0B2A30D08500E5B418 /* fmtdytst.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC7156BE48D00753214 /* fmtdytst.c */; };
|
||||
319F7A0C2A30D08500E5B418 /* fmtno.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CACC156BE4C200753214 /* fmtno.c */; };
|
||||
319F7A0E2A30D08500E5B418 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
319F7A172A30D11400E5B418 /* addrobj.c in Sources */ = {isa = PBXBuildFile; fileRef = 319F7A152A30D11400E5B418 /* addrobj.c */; };
|
||||
31A47BA4156C1E130039B1C2 /* mps.c in Sources */ = {isa = PBXBuildFile; fileRef = 31A47BA3156C1E130039B1C2 /* mps.c */; };
|
||||
31D60007156D3C6200337B26 /* segsmss.c in Sources */ = {isa = PBXBuildFile; fileRef = 31D60006156D3C5F00337B26 /* segsmss.c */; };
|
||||
31D60008156D3C7400337B26 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; };
|
||||
|
|
@ -468,20 +463,6 @@
|
|||
remoteGlobalIDString = 2291A5AC175CAB2F001D4920;
|
||||
remoteInfo = awlutth;
|
||||
};
|
||||
2291A5C3175CAFCA001D4920 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
|
||||
remoteInfo = mps;
|
||||
};
|
||||
2291A5D5175CB05F001D4920 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
|
||||
remoteInfo = mps;
|
||||
};
|
||||
229E228719EAB10D00E21417 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
|
|
@ -923,6 +904,20 @@
|
|||
remoteGlobalIDString = 31108A3A1C6B90E900E728EA;
|
||||
remoteInfo = tagtest;
|
||||
};
|
||||
319F7A062A30D08500E5B418 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 31EEABFA156AAF9D00714D05;
|
||||
remoteInfo = mps;
|
||||
};
|
||||
319F7A182A30D2F000E5B418 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 319F7A042A30D08500E5B418;
|
||||
remoteInfo = addrobj;
|
||||
};
|
||||
31A47BA9156C210D0039B1C2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */;
|
||||
|
|
@ -1092,24 +1087,6 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
2291A5CC175CAFCA001D4920 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
2291A5DE175CB05F001D4920 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
22B2BC3118B6434F00C33E63 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -1407,6 +1384,15 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
319F7A0F2A30D08500E5B418 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
31D6000B156D3CB200337B26 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -1729,6 +1715,8 @@
|
|||
31942AA91C8EC446001AAF32 /* sp.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sp.txt; path = ../design/sp.txt; sourceTree = "<group>"; };
|
||||
31942AAB1C8EC446001AAF32 /* stack-scan.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "stack-scan.txt"; path = "../design/stack-scan.txt"; sourceTree = "<group>"; };
|
||||
31942AB01C8EC446001AAF32 /* testthr.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = testthr.txt; path = ../design/testthr.txt; sourceTree = "<group>"; };
|
||||
319F7A142A30D08500E5B418 /* addrobj */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = addrobj; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
319F7A152A30D11400E5B418 /* addrobj.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = addrobj.c; sourceTree = "<group>"; };
|
||||
31A47BA3156C1E130039B1C2 /* mps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mps.c; sourceTree = "<group>"; };
|
||||
31C83ADD1786281C0031A0DB /* protxc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = protxc.h; sourceTree = "<group>"; };
|
||||
31CD33BB173A9F1500524741 /* mpscams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpscams.h; sourceTree = "<group>"; };
|
||||
|
|
@ -1856,22 +1844,6 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2291A5CA175CAFCA001D4920 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2291A5CB175CAFCA001D4920 /* libmps.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
2291A5DC175CB05F001D4920 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2291A5DD175CB05F001D4920 /* libmps.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
22B2BC3018B6434F00C33E63 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -2134,6 +2106,14 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
319F7A0D2A30D08500E5B418 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
319F7A0E2A30D08500E5B418 /* libmps.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
31D6000A156D3CB200337B26 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -2340,6 +2320,7 @@
|
|||
3124CAB3156BE1B700753214 /* Tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
319F7A152A30D11400E5B418 /* addrobj.c */,
|
||||
3114A63D156E94EA001E0AA3 /* abqtest.c */,
|
||||
22FACED1188807FF000FDBC1 /* airtest.c */,
|
||||
3124CAF5156BE81100753214 /* amcss.c */,
|
||||
|
|
@ -2485,6 +2466,7 @@
|
|||
223E796519EAB00B00DC26A6 /* sncss */,
|
||||
22EA3F4520D2B0D90065F5B6 /* forktest */,
|
||||
2265D71D20E53F9C003019E8 /* mpseventpy */,
|
||||
319F7A142A30D08500E5B418 /* addrobj */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -3365,6 +3347,24 @@
|
|||
productReference = 318DA8CD1892B0F30089718C /* djbench */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
319F7A042A30D08500E5B418 /* addrobj */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 319F7A102A30D08500E5B418 /* Build configuration list for PBXNativeTarget "addrobj" */;
|
||||
buildPhases = (
|
||||
319F7A072A30D08500E5B418 /* Sources */,
|
||||
319F7A0D2A30D08500E5B418 /* Frameworks */,
|
||||
319F7A0F2A30D08500E5B418 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
319F7A052A30D08500E5B418 /* PBXTargetDependency */,
|
||||
);
|
||||
name = addrobj;
|
||||
productName = finalcv;
|
||||
productReference = 319F7A142A30D08500E5B418 /* addrobj */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
31D6000C156D3CB200337B26 /* awluthe */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 31D60014156D3CB200337B26 /* Build configuration list for PBXNativeTarget "awluthe" */;
|
||||
|
|
@ -3555,6 +3555,7 @@
|
|||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 31EEABD8156AAE9E00714D05;
|
||||
|
|
@ -3617,6 +3618,7 @@
|
|||
31FCAE0917692403008C034C /* scheme */,
|
||||
22B2BC2C18B6434F00C33E63 /* scheme-advanced */,
|
||||
31108A3A1C6B90E900E728EA /* tagtest */,
|
||||
319F7A042A30D08500E5B418 /* addrobj */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
|
@ -4090,6 +4092,18 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
319F7A072A30D08500E5B418 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
319F7A172A30D11400E5B418 /* addrobj.c in Sources */,
|
||||
319F7A082A30D08500E5B418 /* testlib.c in Sources */,
|
||||
319F7A0A2A30D08500E5B418 /* fmtdy.c in Sources */,
|
||||
319F7A0B2A30D08500E5B418 /* fmtdytst.c in Sources */,
|
||||
319F7A0C2A30D08500E5B418 /* fmtno.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
31D60009156D3CB200337B26 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -4287,16 +4301,6 @@
|
|||
target = 2291A5AC175CAB2F001D4920 /* awlutth */;
|
||||
targetProxy = 2291A5BF175CAB5F001D4920 /* PBXContainerItemProxy */;
|
||||
};
|
||||
2291A5C2175CAFCA001D4920 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 31EEABFA156AAF9D00714D05 /* mps */;
|
||||
targetProxy = 2291A5C3175CAFCA001D4920 /* PBXContainerItemProxy */;
|
||||
};
|
||||
2291A5D4175CB05F001D4920 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 31EEABFA156AAF9D00714D05 /* mps */;
|
||||
targetProxy = 2291A5D5175CB05F001D4920 /* PBXContainerItemProxy */;
|
||||
};
|
||||
229E228819EAB10D00E21417 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 223E795819EAB00B00DC26A6 /* sncss */;
|
||||
|
|
@ -4612,6 +4616,16 @@
|
|||
target = 31108A3A1C6B90E900E728EA /* tagtest */;
|
||||
targetProxy = 314CB6EA1C6D272A0073CA42 /* PBXContainerItemProxy */;
|
||||
};
|
||||
319F7A052A30D08500E5B418 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 31EEABFA156AAF9D00714D05 /* mps */;
|
||||
targetProxy = 319F7A062A30D08500E5B418 /* PBXContainerItemProxy */;
|
||||
};
|
||||
319F7A192A30D2F000E5B418 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 319F7A042A30D08500E5B418 /* addrobj */;
|
||||
targetProxy = 319F7A182A30D2F000E5B418 /* PBXContainerItemProxy */;
|
||||
};
|
||||
31A47BAA156C210D0039B1C2 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 31EEABFA156AAF9D00714D05 /* mps */;
|
||||
|
|
@ -4891,34 +4905,6 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
2291A5CE175CAFCA001D4920 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2291A5CF175CAFCA001D4920 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2291A5E0175CB05F001D4920 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2291A5E1175CB05F001D4920 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
22B2BC3318B6434F00C33E63 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
|
@ -5633,20 +5619,6 @@
|
|||
};
|
||||
name = RASH;
|
||||
};
|
||||
318DA8E51892C0D00089718C /* RASH */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = RASH;
|
||||
};
|
||||
318DA8E61892C0D00089718C /* RASH */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = RASH;
|
||||
};
|
||||
318DA8E71892C0D00089718C /* RASH */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
|
@ -5816,6 +5788,27 @@
|
|||
};
|
||||
name = RASH;
|
||||
};
|
||||
319F7A112A30D08500E5B418 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
319F7A122A30D08500E5B418 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
319F7A132A30D08500E5B418 /* RASH */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = RASH;
|
||||
};
|
||||
31D60015156D3CB200337B26 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
|
@ -6553,6 +6546,16 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
319F7A102A30D08500E5B418 /* Build configuration list for PBXNativeTarget "addrobj" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
319F7A112A30D08500E5B418 /* Debug */,
|
||||
319F7A122A30D08500E5B418 /* Release */,
|
||||
319F7A132A30D08500E5B418 /* RASH */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
31D60014156D3CB200337B26 /* Build configuration list for PBXNativeTarget "awluthe" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "319F7A042A30D08500E5B418"
|
||||
BuildableName = "addrobj"
|
||||
BlueprintName = "addrobj"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
viewDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "319F7A042A30D08500E5B418"
|
||||
BuildableName = "addrobj"
|
||||
BlueprintName = "addrobj"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "319F7A042A30D08500E5B418"
|
||||
BuildableName = "addrobj"
|
||||
BlueprintName = "addrobj"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
|
||||
BuildableName = "finaltest"
|
||||
BlueprintName = "finaltest"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
viewDebuggingEnabled = "No">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
|
||||
BuildableName = "finaltest"
|
||||
BlueprintName = "finaltest"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "3114A5D5156E93A0001E0AA3"
|
||||
BuildableName = "finaltest"
|
||||
BlueprintName = "finaltest"
|
||||
ReferencedContainer = "container:mps.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -448,6 +448,37 @@ mps_bool_t mps_addr_pool(mps_pool_t *mps_pool_o,
|
|||
}
|
||||
|
||||
|
||||
/* mps_addr_object -- find base pointer of a managed object */
|
||||
|
||||
mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr)
|
||||
{
|
||||
Res res;
|
||||
Addr p;
|
||||
|
||||
AVER(p_o != NULL);
|
||||
|
||||
/* This function cannot be called while walking the heap, unlike
|
||||
* mps_arena_has_addr(). This is because it is designed to be called
|
||||
* with an active mutator, so takes the arena lock. This is in order
|
||||
* that it sees a consistent view of MPS structures and the heap,
|
||||
* and can peek behind the barrier.
|
||||
*/
|
||||
ArenaEnter(arena);
|
||||
AVERT(Arena, arena);
|
||||
res = ArenaAddrObject(&p, arena, (Addr)addr);
|
||||
ArenaLeave(arena);
|
||||
/* We require the object to be ambiguously referenced (hence pinned)
|
||||
* so that p doesn't become invalid before it is written to *p_o.
|
||||
* (We can't simply put this write before the ArenaLeave(), because
|
||||
* p_o could point to MPS-managed memory that is behind a barrier.)
|
||||
*/
|
||||
if (res == ResOK)
|
||||
*p_o = (mps_addr_t)p;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* mps_addr_fmt -- what format might this address have?
|
||||
*
|
||||
* .per-pool: There's no reason why all objects in a pool should have
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ Bool PoolClassCheck(PoolClass klass)
|
|||
CHECKL(FUNCHECK(klass->debugMixin));
|
||||
CHECKL(FUNCHECK(klass->totalSize));
|
||||
CHECKL(FUNCHECK(klass->freeSize));
|
||||
CHECKL(FUNCHECK(klass->addrObject));
|
||||
|
||||
/* Check that pool classes overide sets of related methods. */
|
||||
CHECKL((klass->init == PoolAbsInit) ==
|
||||
|
|
@ -303,6 +304,21 @@ Size PoolFreeSize(Pool pool)
|
|||
}
|
||||
|
||||
|
||||
/* PoolAddrObject -- return base pointer from interior pointer
|
||||
*
|
||||
* Note: addr is not necessarily inside the pool, even though
|
||||
* mps_addr_object dispatches via the tract table. This allows this
|
||||
* function to be used more generally internally. The pool should
|
||||
* check (it has to anyway).
|
||||
*/
|
||||
|
||||
Res PoolAddrObject(Addr *pReturn, Pool pool, Addr addr)
|
||||
{
|
||||
AVER(pReturn != NULL);
|
||||
AVERT(Pool, pool);
|
||||
return Method(Pool, pool, addrObject)(pReturn, pool, addr);
|
||||
}
|
||||
|
||||
/* PoolDescribe -- describe a pool */
|
||||
|
||||
Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth)
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ DEFINE_CLASS(Pool, AbstractPool, klass)
|
|||
klass->debugMixin = PoolNoDebugMixin;
|
||||
klass->totalSize = PoolNoSize;
|
||||
klass->freeSize = PoolNoSize;
|
||||
klass->addrObject = PoolTrivAddrObject;
|
||||
klass->sig = PoolClassSig;
|
||||
AVERT(PoolClass, klass);
|
||||
}
|
||||
|
|
@ -476,6 +477,16 @@ Size PoolNoSize(Pool pool)
|
|||
}
|
||||
|
||||
|
||||
Res PoolTrivAddrObject(Addr *pReturn, Pool pool, Addr addr)
|
||||
{
|
||||
AVERT(Pool, pool);
|
||||
AVER(pReturn != NULL);
|
||||
UNUSED(addr);
|
||||
|
||||
return ResUNIMPL;
|
||||
}
|
||||
|
||||
|
||||
/* C. COPYRIGHT AND LICENSE
|
||||
*
|
||||
* Copyright (C) 2001-2020 Ravenbrook Limited <https://www.ravenbrook.com/>.
|
||||
|
|
|
|||
|
|
@ -1892,6 +1892,90 @@ static void amcWalkAll(Pool pool, FormattedObjectsVisitor f, void *p, size_t s)
|
|||
}
|
||||
}
|
||||
|
||||
/* AMCAddrObject -- return base pointer from interior pointer
|
||||
*
|
||||
* amcAddrObjectSearch implements the scan for an object containing
|
||||
* the interior pointer by skipping using format methods.
|
||||
*
|
||||
* AMCAddrObject locates the segment containing the interior pointer
|
||||
* and wraps amcAddrObjectSearch in the necessary shield operations to
|
||||
* give it access.
|
||||
*/
|
||||
|
||||
static Res amcAddrObjectSearch(Addr *pReturn,
|
||||
Pool pool,
|
||||
Addr objBase,
|
||||
Addr searchLimit,
|
||||
Addr addr)
|
||||
{
|
||||
Format format;
|
||||
Size hdrSize;
|
||||
|
||||
AVER(pReturn != NULL);
|
||||
AVERT(Pool, pool);
|
||||
AVER(objBase <= searchLimit);
|
||||
|
||||
format = pool->format;
|
||||
hdrSize = format->headerSize;
|
||||
while (objBase < searchLimit) {
|
||||
Addr objRef = AddrAdd(objBase, hdrSize);
|
||||
Addr objLimit = AddrSub((*format->skip)(objRef), hdrSize);
|
||||
AVER(objBase < objLimit);
|
||||
|
||||
if (addr < objLimit) {
|
||||
AVER(objBase <= addr);
|
||||
AVER(addr < objLimit);
|
||||
|
||||
/* Don't return base pointer if object is moved */
|
||||
if (NULL == (*format->isMoved)(objRef)) {
|
||||
*pReturn = objRef;
|
||||
return ResOK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
objBase = objLimit;
|
||||
}
|
||||
return ResFAIL;
|
||||
}
|
||||
|
||||
static Res AMCAddrObject(Addr *pReturn, Pool pool, Addr addr)
|
||||
{
|
||||
Res res;
|
||||
Arena arena;
|
||||
Addr base, limit;
|
||||
Buffer buffer;
|
||||
Seg seg;
|
||||
|
||||
AVER(pReturn != NULL);
|
||||
AVERT(Pool, pool);
|
||||
|
||||
arena = PoolArena(pool);
|
||||
if (!SegOfAddr(&seg, arena, addr) || SegPool(seg) != pool)
|
||||
return ResFAIL;
|
||||
|
||||
base = SegBase(seg);
|
||||
if (SegBuffer(&buffer, seg))
|
||||
/* We use BufferGetInit here (and not BufferScanLimit) because we
|
||||
* want to be able to find objects that have been allocated and
|
||||
* committed since the last flip. These objects lie between the
|
||||
* addresses returned by BufferScanLimit (which returns the value
|
||||
* of init at the last flip) and BufferGetInit.
|
||||
*
|
||||
* Strictly speaking we only need a limit that is at least the
|
||||
* maximum of the objects on the segments. This is because addr
|
||||
* *must* point inside a live object and we stop skipping once we
|
||||
* have found it. The init pointer serves this purpose.
|
||||
*/
|
||||
limit = BufferGetInit(buffer);
|
||||
else
|
||||
limit = SegLimit(seg);
|
||||
|
||||
ShieldExpose(arena, seg);
|
||||
res = amcAddrObjectSearch(pReturn, pool, base, limit, addr);
|
||||
ShieldCover(arena, seg);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* AMCTotalSize -- total memory allocated from the arena */
|
||||
|
||||
|
|
@ -2007,6 +2091,7 @@ DEFINE_CLASS(Pool, AMCZPool, klass)
|
|||
klass->bufferClass = amcBufClassGet;
|
||||
klass->totalSize = AMCTotalSize;
|
||||
klass->freeSize = AMCFreeSize;
|
||||
klass->addrObject = AMCAddrObject;
|
||||
AVERT(PoolClass, klass);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
#include <signal.h> /* sig_atomic_t */
|
||||
#include <sys/mman.h> /* see .feature.li in config.h */
|
||||
#include <sys/types.h> /* mmap, munmap */
|
||||
#include <unistd.h> /* getpagesize */
|
||||
#include <unistd.h> /* sysconf, _SC_PAGESIZE */
|
||||
|
||||
SRCID(vmix, "$Id$");
|
||||
|
||||
|
|
@ -59,10 +59,11 @@ SRCID(vmix, "$Id$");
|
|||
|
||||
Size PageSize(void)
|
||||
{
|
||||
int pageSize;
|
||||
long pageSize;
|
||||
|
||||
/* Find out the operating system page size */
|
||||
pageSize = getpagesize();
|
||||
/* Find out the operating system page size
|
||||
(see design.mps.vm.impl.ix.page.size) */
|
||||
pageSize = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Check the page size will fit in a Size. */
|
||||
AVER((unsigned long)pageSize <= (unsigned long)(Size)-1);
|
||||
|
|
|
|||
|
|
@ -269,7 +269,13 @@ Unix implementation
|
|||
|
||||
_`.impl.ix`: In ``vmix.c``.
|
||||
|
||||
_`.impl.ix.page.size`: The page size is given by ``getpagesize()``.
|
||||
_`.impl.ix.page.size`: The page size is given by
|
||||
``sysconf(_SC_PAGESIZE)``. We avoid ``getpagesize()``, which is a
|
||||
legacy function in Posix:
|
||||
|
||||
Applications should use the sysconf() function instead.
|
||||
|
||||
— `The Single UNIX ® Specification, Version 2 <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpagesize.html>`__
|
||||
|
||||
_`.impl.ix.param`: Decodes no keyword arguments.
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,18 @@ New features
|
|||
address-based hash tables using :term:`location dependency`. See
|
||||
:ref:`pool-amc-hash-arrays`.
|
||||
|
||||
#. The new function :c:func:`mps_addr_object` allows clients to
|
||||
discover the base pointer of an object from a pointer to anywhere
|
||||
inside the object. This is intended to support stack tracing and
|
||||
debugging for client programs that allocate their code on the
|
||||
heap.
|
||||
|
||||
#. A :term:`virtual memory arena` can now be configured to call
|
||||
functions when it acquires a new chunk of :term:`address space`,
|
||||
and when it returns a chunk of address space to the operation
|
||||
system. This is intended to support dynamic function tables in
|
||||
Windows. See :ref:`topic-arena-extension`.
|
||||
|
||||
|
||||
Interface changes
|
||||
.................
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ Client arenas
|
|||
* :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its
|
||||
size.
|
||||
|
||||
It also accepts three optional keyword arguments:
|
||||
It also accepts five optional keyword arguments:
|
||||
|
||||
* :c:macro:`MPS_KEY_COMMIT_LIMIT` (type :c:type:`size_t`) is
|
||||
the maximum amount of memory, in :term:`bytes (1)`, that the MPS
|
||||
|
|
@ -159,6 +159,18 @@ Client arenas
|
|||
may pause the :term:`client program` for. See
|
||||
:c:func:`mps_arena_pause_time_set` for details.
|
||||
|
||||
* :c:macro:`MPS_KEY_ARENA_EXTENDED` (type :c:type:`mps_fun_t`) is
|
||||
a function that will be called immediately after the arena is
|
||||
*extended*: that is, just after it acquires a new chunk of address
|
||||
space from the operating system. See :ref:`topic-arena-extension`
|
||||
for details.
|
||||
|
||||
* :c:macro:`MPS_KEY_ARENA_CONTRACTED` (type :c:type:`mps_fun_t`)
|
||||
is a function that will be called immediately before the arena is
|
||||
*contracted*: that is, just before it finishes with a chunk of
|
||||
address space and returns it to the operating system. See
|
||||
:ref:`topic-arena-extension` for details.
|
||||
|
||||
For example::
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
|
|
@ -983,8 +995,8 @@ Arena introspection and debugging
|
|||
from MPS-managed memory, then it may attempt to re-enter the
|
||||
MPS, which will fail as the MPS is not re-entrant.
|
||||
|
||||
.. |RtlInstallFunctionTableCallback| replace:: ``RtlInstallFunctionTableCallback()``
|
||||
.. _RtlInstallFunctionTableCallback: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx
|
||||
.. |RtlInstallFunctionTableCallback| replace:: :c:func:`RtlInstallFunctionTableCallback`
|
||||
.. _RtlInstallFunctionTableCallback: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtlinstallfunctiontablecallback
|
||||
|
||||
If this happens, in order to allow the debugger to finish
|
||||
decoding the call stack, the only remedy is to put the arena
|
||||
|
|
@ -1040,3 +1052,138 @@ Arena introspection and debugging
|
|||
:c:func:`mps_addr_pool`, and to find out which :term:`object
|
||||
format` describes the object at the address, use
|
||||
:c:func:`mps_addr_fmt`.
|
||||
|
||||
|
||||
.. c:function:: mps_res_t mps_addr_object(mps_addr_t *p_o, mps_arena_t arena, mps_addr_t addr)
|
||||
|
||||
Find the :term:`base pointer` of an :term:`object` if provided with an
|
||||
:term:`interior pointer` to that object, or the object's base pointer,
|
||||
provided the object exists in a pool that supports this feature.
|
||||
|
||||
``p_o`` points to a location that will hold the object's base pointer.
|
||||
|
||||
``arena`` is an arena.
|
||||
|
||||
``addr`` is an address that might be an interior or base pointer.
|
||||
|
||||
Returns MPS_RES_OK if a base pointer to an object into which ``addr``
|
||||
points was successfully returned.
|
||||
|
||||
Returns MPS_RES_FAIL if ``addr`` points to memory not managed by the
|
||||
``arena`` or if ``addr`` points to the interior of an object which has
|
||||
been moved by a :term:`moving memory manager`.
|
||||
|
||||
Returns MPS_RES_UNIMPL if ``addr`` is found to be managed by a :term:`pool`
|
||||
which does not currently implement this feature.
|
||||
|
||||
:c:func:`mps_addr_object` allows client programs that allocate
|
||||
code on the heap to implement debugging and stack tracing, in that it provides
|
||||
a way to unwind a client program's stack by finding the block of code to which the
|
||||
program counter or function return addresses currently point. It can be called
|
||||
multiple times as needed to build a complete trace of the client program's stack.
|
||||
|
||||
This function does not support debugging in situations where the arena
|
||||
itself has encountered a runtime error. For cases where the MPS encounters
|
||||
runtime errors, see :c:func:`mps_arena_postmortem`.
|
||||
|
||||
.. note::
|
||||
|
||||
This function is intended to assist with debugging fatal
|
||||
errors in the :term:`client program`. It is not expected to be
|
||||
needed in normal use, i.e. as part of the regular operation of code in
|
||||
production, since it is not optimized for performance. If you find yourself
|
||||
wanting to use this function other than in the use case described, there may
|
||||
be a better way to meet your requirements: please
|
||||
:ref:`contact us <contact>`.
|
||||
|
||||
If you would like this function to work in a pool in which it's currently
|
||||
unimplemented, please :ref:`contact us <contact>`.
|
||||
|
||||
|
||||
.. index::
|
||||
single: arena extension callbacks; introduction
|
||||
single: extension callbacks; introduction
|
||||
single: arena contraction callbacks; introduction
|
||||
single: contraction callbacks; introduction
|
||||
|
||||
.. _topic-arena-extension:
|
||||
|
||||
Arena extension callbacks
|
||||
-------------------------
|
||||
|
||||
There are situations in which the :term:`client program` needs to be
|
||||
informed about the chunks of address space that an :term:`arena` is
|
||||
managing. To support this, the MPS allows the client program to
|
||||
specify two callback functions when creating a :term:`virtual memory
|
||||
arena`: one function is called when the arena is *extended* (that is,
|
||||
when it acquires a new chunk of address space from the operating
|
||||
system), and the other when the arena is *contracted* (that is, when
|
||||
it returns a chunk of address space to the operating system).
|
||||
|
||||
The use case that this feature is designed to support is debugging of
|
||||
dynamically generated code in 64-bit Windows. Microsoft's
|
||||
documentation for |RtlInstallFunctionTableCallback|_ says:
|
||||
|
||||
Function tables are used on 64-bit Windows to determine how to
|
||||
unwind or walk the stack. These tables are usually generated by
|
||||
the compiler and stored as part of the image. However,
|
||||
applications must provide the function table for dynamically
|
||||
generated code.
|
||||
|
||||
An application may install a dynamic function table by calling
|
||||
|RtlInstallFunctionTableCallback|_, passing the region of memory in
|
||||
which the dynamically generated functions can be found, and may later
|
||||
delete the table by calling |RtlDeleteFunctionTable|_.
|
||||
|
||||
.. |RtlDeleteFunctionTable| replace:: :c:func:`RtlDeleteFunctionTable`
|
||||
.. _RtlDeleteFunctionTable: https://docs.microsoft.com/en-gb/windows/win32/api/winnt/nf-winnt-rtldeletefunctiontable
|
||||
|
||||
So if the client program is storing dynamically generated functions in
|
||||
MPS-managed memory, then it could define callback functions that
|
||||
install and delete the function table callback for the dynamically
|
||||
generated code, like this::
|
||||
|
||||
void arena_extended(mps_arena_t arena, void *base, size_t size)
|
||||
{
|
||||
RtlInstallFunctionTableCallback(...);
|
||||
}
|
||||
|
||||
void arena_contracted(mps_arena_t arena, void *base, size_t size)
|
||||
{
|
||||
RtlDeleteFunctionTable(...);
|
||||
}
|
||||
|
||||
and then pass these two functions using :term:`keyword arguments` to
|
||||
:c:func:`mps_arena_create_k`::
|
||||
|
||||
MPS_ARGS_BEGIN(args) {
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_EXTENDED, (mps_fun_t)arena_extended);
|
||||
MPS_ARGS_ADD(args, MPS_KEY_ARENA_CONTRACTED, (mps_fun_t)arena_contracted);
|
||||
/* ... other keyword arguments ... */
|
||||
res = mps_arena_create_k(&arena, mps_arena_class_vm(), args);
|
||||
} MPS_ARGS_END(args);
|
||||
|
||||
The callback functions receive three arguments: ``arena`` (the arena
|
||||
being extended or contracted), ``base`` (the base address of the chunk
|
||||
of address space that has just been acquired from, or is about to be
|
||||
returned to, the operating system), and ``size`` (the size of the
|
||||
chunk, in bytes). They must not call any function in the MPS, and must
|
||||
not access any memory managed by the MPS.
|
||||
|
||||
.. note::
|
||||
|
||||
The extenstion callback is also called immediately after the arena
|
||||
is created, in other words, the creation of the arena is treated as
|
||||
a special example of an extension of the arena.
|
||||
|
||||
The contraction callback is called on all remaining chunks when
|
||||
the arena is destroyed. There will be at least one callback.
|
||||
|
||||
Every contraction of the arena will match one-to-one with the arena
|
||||
extensions that have already taken place. After creation, any
|
||||
contractions performed by the arena will be the same size as the
|
||||
extensions that have already taken place. Contractions never occur as
|
||||
amalgamations nor as fractions of previous arena extensions.
|
||||
|
||||
Arena extension callbacks are only supported by :term:`virtual
|
||||
memory arenas`.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
Test case Flags Notes
|
||||
============= ================ ==========================================
|
||||
abqtest
|
||||
addrobj
|
||||
airtest
|
||||
amcss =P
|
||||
amcsshe =P
|
||||
|
|
@ -16,6 +17,7 @@ awlutth =T
|
|||
btcv
|
||||
bttest =N interactive
|
||||
djbench =N benchmark
|
||||
extcon =W TODO: Enable when we can update Xcode project. See GitHub issue #217 <https://github.com/Ravenbrook/mps/issues/217>.
|
||||
finalcv =P
|
||||
finaltest =P
|
||||
forktest =X
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue