diff options
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/beam/erl_alloc.c | 27 | ||||
-rw-r--r-- | erts/emulator/beam/erl_alloc_util.c | 10 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE.erl | 78 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE_data/Makefile.src | 3 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE_data/allocator_test.h | 2 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE_data/migration.c | 229 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE_data/testcase_driver.c | 14 | ||||
-rw-r--r-- | erts/emulator/test/alloc_SUITE_data/testcase_driver.h | 1 |
8 files changed, 350 insertions, 14 deletions
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 4223b357f1..0aa45acd82 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -3615,6 +3615,33 @@ UWord erts_alc_test(UWord op, UWord a1, UWord a2, UWord a3) case 0xf15: erts_free(ERTS_ALC_T_TEST, (void*)a1); return 0; + case 0xf16: { + Uint extra_hdr_sz = UNIT_CEILING((Uint)a1); + ErtsAllocatorThrSpec_t* ts = &erts_allctr_thr_spec[ERTS_ALC_A_TEST]; + Uint offset = ts->allctr[0]->mbc_header_size; + void* orig_creating_mbc = ts->allctr[0]->creating_mbc; + void* orig_destroying_mbc = ts->allctr[0]->destroying_mbc; + void* new_creating_mbc = *(void**)a2; /* inout arg */ + void* new_destroying_mbc = *(void**)a3; /* inout arg */ + int i; + + for (i=0; i < ts->size; i++) { + Allctr_t* ap = ts->allctr[i]; + if (ap->mbc_header_size != offset + || ap->creating_mbc != orig_creating_mbc + || ap->destroying_mbc != orig_destroying_mbc + || ap->mbc_list.first != NULL) + return -1; + } + for (i=0; i < ts->size; i++) { + ts->allctr[i]->mbc_header_size += extra_hdr_sz; + ts->allctr[i]->creating_mbc = new_creating_mbc; + ts->allctr[i]->destroying_mbc = new_destroying_mbc; + } + *(void**)a2 = orig_creating_mbc; + *(void**)a3 = orig_destroying_mbc; + return offset; + } default: break; } diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 173cb091b6..8229a15824 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -6074,6 +6074,16 @@ erts_alcu_test(UWord op, UWord a1, UWord a2) case 0x023: return (UWord) 0; case 0x024: return (UWord) 0; #endif + case 0x025: /* UMEM2BLK_TEST*/ +#ifdef DEBUG +# ifdef HARD_DEBUG + return (UWord)UMEM2BLK(a1-3*sizeof(UWord)); +# else + return (UWord)UMEM2BLK(a1-2*sizeof(UWord)); +# endif +#else + return (UWord)UMEM2BLK(a1); +#endif default: ASSERT(0); return ~((UWord) 0); } diff --git a/erts/emulator/test/alloc_SUITE.erl b/erts/emulator/test/alloc_SUITE.erl index 7c7ddde5d4..9b0d4737b1 100644 --- a/erts/emulator/test/alloc_SUITE.erl +++ b/erts/emulator/test/alloc_SUITE.erl @@ -31,7 +31,8 @@ rbtree/1, mseg_clear_cache/1, erts_mmap/1, - cpool/1]). + cpool/1, + migration/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -43,7 +44,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, coalesce, threads, realloc_copy, bucket_index, - bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool]. + bucket_mask, rbtree, mseg_clear_cache, erts_mmap, cpool, migration]. groups() -> []. @@ -112,6 +113,8 @@ cpool(suite) -> []; cpool(doc) -> []; cpool(Cfg) -> ?line drv_case(Cfg). +migration(Cfg) -> drv_case(Cfg, concurrent, "+MZe true"). + erts_mmap(Config) when is_list(Config) -> case {?t:os_type(), is_halfword_vm()} of {{unix, _}, false} -> @@ -176,18 +179,17 @@ erts_mmap_do(Config, SCO, SCRPM, SCRFSD) -> %% %% drv_case(Config) -> - drv_case(Config, ""). + drv_case(Config, one_shot, ""). -drv_case(Config, Command) when is_list(Config), - is_list(Command) -> +drv_case(Config, Mode, NodeOpts) when is_list(Config) -> case ?t:os_type() of {Family, _} when Family == unix; Family == win32 -> - ?line {ok, Node} = start_node(Config), + ?line {ok, Node} = start_node(Config, NodeOpts), ?line Self = self(), ?line Ref = make_ref(), ?line spawn_link(Node, fun () -> - Res = run_drv_case(Config, Command), + Res = run_drv_case(Config, Mode), Self ! {Ref, Res} end), ?line Result = receive {Ref, Rslt} -> Rslt end, @@ -199,7 +201,7 @@ drv_case(Config, Command) when is_list(Config), | io_lib:format("~p",[SkipOs])])} end. -run_drv_case(Config, Command) -> +run_drv_case(Config, Mode) -> ?line DataDir = ?config(data_dir,Config), ?line CaseName = ?config(testcase,Config), case erl_ddll:load_driver(DataDir, CaseName) of @@ -208,6 +210,19 @@ run_drv_case(Config, Command) -> io:format("~s\n", [erl_ddll:format_error(Error)]), ?line ?t:fail() end, + + case Mode of + one_shot -> + Result = one_shot(CaseName, ""); + + concurrent -> + Result = concurrent(CaseName) + end, + + ?line ok = erl_ddll:unload_driver(CaseName), + ?line Result. + +one_shot(CaseName, Command) -> ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), ?line true = is_port(Port), ?line Port ! {self(), {command, Command}}, @@ -217,8 +232,45 @@ run_drv_case(Config, Command) -> {Port, closed} -> ok end, - ?line ok = erl_ddll:unload_driver(CaseName), - ?line Result. + Result. + + +many_shot(CaseName, Command) -> + ?line Port = open_port({spawn, atom_to_list(CaseName)}, []), + ?line true = is_port(Port), + Result = repeat_while(fun() -> + ?line Port ! {self(), {command, Command}}, + receive_drv_result(Port, CaseName) =:= continue + end), + ?line Port ! {self(), close}, + ?line receive + {Port, closed} -> + ok + end, + Result. + +concurrent(CaseName) -> + one_shot(CaseName, "init"), + PRs = lists:map(fun(I) -> spawn_opt(fun() -> + many_shot(CaseName, "") + end, + [monitor, {scheduler,I}]) + end, + lists:seq(1, erlang:system_info(schedulers))), + lists:foreach(fun({Pid,Ref}) -> + receive {'DOWN', Ref, process, Pid, Reason} -> + Reason + end + end, + PRs), + ok. + +repeat_while(Fun) -> + io:format("~p calls fun\n", [self()]), + case Fun() of + true -> repeat_while(Fun); + false -> ok + end. receive_drv_result(Port, CaseName) -> ?line receive @@ -236,11 +288,11 @@ receive_drv_result(Port, CaseName) -> {succeeded, Port, CaseName, ""} -> ?line succeeded; {succeeded, Port, CaseName, Comment} -> - ?line {comment, Comment} + ?line {comment, Comment}; + continue -> + continue end. -start_node(Config) -> - start_node(Config, []). start_node(Config, Opts) when is_list(Config), is_list(Opts) -> Pa = filename:dirname(code:which(?MODULE)), Name = list_to_atom(atom_to_list(?MODULE) diff --git a/erts/emulator/test/alloc_SUITE_data/Makefile.src b/erts/emulator/test/alloc_SUITE_data/Makefile.src index a441fe946b..e31de54e1b 100644 --- a/erts/emulator/test/alloc_SUITE_data/Makefile.src +++ b/erts/emulator/test/alloc_SUITE_data/Makefile.src @@ -25,7 +25,8 @@ TEST_DRVS = basic@dll@ \ bucket_mask@dll@ \ rbtree@dll@ \ mseg_clear_cache@dll@ \ - cpool@dll@ + cpool@dll@ \ + migration@dll@ CC = @CC@ LD = @LD@ diff --git a/erts/emulator/test/alloc_SUITE_data/allocator_test.h b/erts/emulator/test/alloc_SUITE_data/allocator_test.h index a7f12c6ca8..bfd0bb3094 100644 --- a/erts/emulator/test/alloc_SUITE_data/allocator_test.h +++ b/erts/emulator/test/alloc_SUITE_data/allocator_test.h @@ -85,6 +85,7 @@ typedef void* erts_cond; #define CPOOL_DELETE(A,B) ((Carrier_t *) ALC_TEST2(0x022, (A), (B))) #define CPOOL_IS_EMPTY(A) ((int) ALC_TEST1(0x023, (A))) #define CPOOL_IS_IN_POOL(A,B) ((int) ALC_TEST2(0x024, (A), (B))) +#define UMEM2BLK_TEST(P) ((Block_t*) ALC_TEST1(0x025, (P))) /* From erl_goodfit_alloc.c */ #define BKT_IX(A, S) ((Ulong) ALC_TEST2(0x100, (A), (S))) @@ -144,5 +145,6 @@ typedef void* erts_cond; #define IS_SMP_ENABLED ((int) ALC_TEST0(0xf13)) #define ALLOC_TEST(S) ((void*) ALC_TEST1(0xf14, (S))) #define FREE_TEST(P) ((void) ALC_TEST1(0xf15, (P))) +#define SET_TEST_MBC_USER_HEADER(SZ,CMBC,DMBC) ((int)ALC_TEST3(0xf16, (SZ), (CMBC), (DMBC))) #endif diff --git a/erts/emulator/test/alloc_SUITE_data/migration.c b/erts/emulator/test/alloc_SUITE_data/migration.c new file mode 100644 index 0000000000..dd58a0d3dd --- /dev/null +++ b/erts/emulator/test/alloc_SUITE_data/migration.c @@ -0,0 +1,229 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2014. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Test the carrier migration logic + */ + +#ifndef __WIN32__ +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#endif +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "testcase_driver.h" +#include "allocator_test.h" + +#define FATAL_ASSERT(A) \ + ((void) ((A) \ + ? 1 \ + : (fatal_assert_failed(#A, \ + (char *) __FILE__, \ + __LINE__), \ + 0))) + +static void +fatal_assert_failed(char* expr, char* file, int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d: Assertion failed: %s\n", + file, line, expr); + fflush(stderr); + abort(); +} + + +char * +testcase_name(void) +{ + return "migration"; +} + +void +testcase_cleanup(TestCaseState_t *tcs) +{ +} + +#define MAX_BLOCK_PER_THR 100 +#define MAX_ROUNDS 10 + +typedef struct MyBlock_ { + struct MyBlock_* next; + struct MyBlock_** prevp; +} MyBlock; + +typedef struct { + MyBlock* blockv[MAX_BLOCK_PER_THR]; + enum { GROWING, SHRINKING, CLEANUP, DONE } phase; + int ix; + int round; +} MigrationState; + +typedef struct { + ErlDrvMutex* mtx; + int nblocks; + MyBlock* first; +} MyCrrInfo; + + +static int crr_info_offset = -1; +static void (*orig_create_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier); +static void (*orig_destroying_mbc_fn)(Allctr_t *allctr, Carrier_t *carrier); + +static void my_creating_mbc(Allctr_t *allctr, Carrier_t *carrier) +{ + MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset); + if (orig_create_mbc_fn) + orig_create_mbc_fn(allctr, carrier); + + mci->mtx = erl_drv_mutex_create("alloc_SUITE.migration"); + mci->nblocks = 0; + mci->first = NULL; +} + +static void my_destroying_mbc(Allctr_t *allctr, Carrier_t *carrier) +{ + MyCrrInfo* mci = (MyCrrInfo*) ((char*)carrier + crr_info_offset); + + FATAL_ASSERT(mci->nblocks == 0); + FATAL_ASSERT(mci->first == NULL); + erl_drv_mutex_destroy(mci->mtx); + + if (orig_destroying_mbc_fn) + orig_destroying_mbc_fn(allctr, carrier); +} + + +static void setup(TestCaseState_t* tcs) +{ + void* creating_mbc_arg = (void*)my_creating_mbc; + void* destroying_mbc_arg = (void*)my_destroying_mbc; + crr_info_offset = SET_TEST_MBC_USER_HEADER(sizeof(MyCrrInfo), + &creating_mbc_arg, + &destroying_mbc_arg); + ASSERT(tcs, crr_info_offset >= 0); + orig_create_mbc_fn = creating_mbc_arg; + orig_destroying_mbc_fn = destroying_mbc_arg; +} + +static void add_block(MyBlock* p) +{ + MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); + + erl_drv_mutex_lock(mci->mtx); + mci->nblocks++; + p->next = mci->first; + p->prevp = &mci->first; + mci->first = p; + if (p->next) + p->next->prevp = &p->next; + erl_drv_mutex_unlock(mci->mtx); +} + +static void remove_block(MyBlock* p) +{ + MyCrrInfo* mci = (MyCrrInfo*)((char*)BLK_TO_MBC(UMEM2BLK_TEST(p)) + crr_info_offset); + + erl_drv_mutex_lock(mci->mtx); + mci->nblocks--; + if (p->next) + p->next->prevp = p->prevp; + *p->prevp = p->next; + erl_drv_mutex_unlock(mci->mtx); +} + +void +testcase_run(TestCaseState_t *tcs) +{ + MigrationState* state = (MigrationState*) tcs->extra; + + if (tcs->command_len == 4 + && memcmp(tcs->command, "init", tcs->command_len) == 0) { + setup(tcs); + return; + } + + if (!tcs->extra) { + if (!IS_SMP_ENABLED) + testcase_skipped(tcs, "No SMP support"); + + tcs->extra = driver_alloc(sizeof(MigrationState)); + state = (MigrationState*) tcs->extra; + memset(state->blockv, 0, sizeof(state->blockv)); + state->phase = GROWING; + state->ix = 0; + state->round = 0; + } + + switch (state->phase) { + case GROWING: { + MyBlock* p; + FATAL_ASSERT(!state->blockv[state->ix]); + p = ALLOC_TEST((1 << 18) / 5); + FATAL_ASSERT(p); + add_block(p); + state->blockv[state->ix] = p; + do { + if (++state->ix >= MAX_BLOCK_PER_THR) { + state->phase = SHRINKING; + state->ix = 0; + break; + } + } while (state->blockv[state->ix] != NULL); + break; + } + case SHRINKING: + FATAL_ASSERT(state->blockv[state->ix]); + remove_block(state->blockv[state->ix]); + FREE_TEST(state->blockv[state->ix]); + state->blockv[state->ix] = NULL; + + state->ix += 1 + ((state->ix % 3) == 0); + if (state->ix >= MAX_BLOCK_PER_THR) { + if (++state->round >= MAX_ROUNDS) { + state->phase = CLEANUP; + } else { + state->phase = GROWING; + } + state->ix = 0; + } + break; + + case CLEANUP: + if (state->blockv[state->ix]) { + remove_block(state->blockv[state->ix]); + FREE_TEST(state->blockv[state->ix]); + } + if (++state->ix >= MAX_BLOCK_PER_THR) + state->phase = DONE; + break; + + default: + FATAL_ASSERT(!"Invalid phase"); + } + + if (state->phase == DONE) { + driver_free(tcs->extra); + tcs->extra = NULL; + } + else + testcase_continue(tcs); +} diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c index bc674c56b7..d04eb1bcb0 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.c +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.c @@ -39,6 +39,7 @@ #define TESTCASE_FAILED 0 #define TESTCASE_SKIPPED 1 #define TESTCASE_SUCCEEDED 2 +#define TESTCASE_CONTINUE 3 typedef struct { TestCaseState_t visible; @@ -129,6 +130,12 @@ testcase_drv_run(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) } switch (itcs->result) { + case TESTCASE_CONTINUE: + msg[0] = ERL_DRV_ATOM; + msg[1] = driver_mk_atom("continue"); + erl_drv_output_term(itcs->port_id, msg, 2); + return; + case TESTCASE_SUCCEEDED: result_atom = driver_mk_atom("succeeded"); break; @@ -239,6 +246,13 @@ void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...) longjmp(itcs->done_jmp_buf, 1); } +void testcase_continue(TestCaseState_t *tcs) +{ + InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; + itcs->result = TESTCASE_CONTINUE; + longjmp(itcs->done_jmp_buf, 1); +} + void testcase_failed(TestCaseState_t *tcs, char *frmt, ...) { InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs; diff --git a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h index 5d17eaec64..5d439735b7 100644 --- a/erts/emulator/test/alloc_SUITE_data/testcase_driver.h +++ b/erts/emulator/test/alloc_SUITE_data/testcase_driver.h @@ -37,6 +37,7 @@ typedef struct { void testcase_printf(TestCaseState_t *tcs, char *frmt, ...); void testcase_succeeded(TestCaseState_t *tcs, char *frmt, ...); void testcase_skipped(TestCaseState_t *tcs, char *frmt, ...); +void testcase_continue(TestCaseState_t *tcs); void testcase_failed(TestCaseState_t *tcs, char *frmt, ...); int testcase_assertion_failed(TestCaseState_t *tcs, char *file, int line, char *assertion); |