Home | History | Annotate | Line # | Download | only in unit
      1 #include "test/jemalloc_test.h"
      2 #include "test/san.h"
      3 
      4 const char *malloc_conf =
      5     "tcache_ncached_max:256-1024:1001|2048-2048:0|8192-8192:1,tcache_max:4096";
      6 extern void tcache_bin_info_compute(
      7     cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]);
      8 extern bool                    tcache_get_default_ncached_max_set(szind_t ind);
      9 extern const cache_bin_info_t *tcache_get_default_ncached_max(void);
     10 
     11 static void
     12 check_bins_info(cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]) {
     13 	size_t mib_get[4], mib_get_len;
     14 	mib_get_len = sizeof(mib_get) / sizeof(size_t);
     15 	const char *get_name = "thread.tcache.ncached_max.read_sizeclass";
     16 	size_t      ncached_max;
     17 	size_t      sz = sizeof(size_t);
     18 	expect_d_eq(mallctlnametomib(get_name, mib_get, &mib_get_len), 0,
     19 	    "Unexpected mallctlnametomib() failure");
     20 
     21 	for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) {
     22 		size_t bin_size = sz_index2size(i);
     23 		expect_d_eq(
     24 		    mallctlbymib(mib_get, mib_get_len, (void *)&ncached_max,
     25 		        &sz, (void *)&bin_size, sizeof(size_t)),
     26 		    0, "Unexpected mallctlbymib() failure");
     27 		expect_zu_eq(ncached_max, tcache_bin_info[i].ncached_max,
     28 		    "Unexpected ncached_max for bin %d", i);
     29 		/* Check ncached_max returned under a non-bin size. */
     30 		bin_size--;
     31 		size_t temp_ncached_max = 0;
     32 		expect_d_eq(mallctlbymib(mib_get, mib_get_len,
     33 		                (void *)&temp_ncached_max, &sz,
     34 		                (void *)&bin_size, sizeof(size_t)),
     35 		    0, "Unexpected mallctlbymib() failure");
     36 		expect_zu_eq(temp_ncached_max, ncached_max,
     37 		    "Unexpected ncached_max for inaccurate bin size.");
     38 	}
     39 }
     40 
     41 static void *
     42 ncached_max_check(void *args) {
     43 	cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX];
     44 	cache_bin_info_t tcache_bin_info_backup[TCACHE_NBINS_MAX];
     45 	tsd_t           *tsd = tsd_fetch();
     46 	tcache_t        *tcache = tsd_tcachep_get(tsd);
     47 	assert(tcache != NULL);
     48 	tcache_slow_t *tcache_slow = tcache->tcache_slow;
     49 
     50 	tcache_bin_info_compute(tcache_bin_info);
     51 	memcpy(
     52 	    tcache_bin_info_backup, tcache_bin_info, sizeof(tcache_bin_info));
     53 	/* Check ncached_max set by malloc_conf. */
     54 	for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) {
     55 		bool           first_range = (i >= sz_size2index(256)
     56                     && i <= sz_size2index(1024));
     57 		bool           second_range = (i == sz_size2index(2048));
     58 		bool           third_range = (i == sz_size2index(8192));
     59 		cache_bin_sz_t target_ncached_max = 0;
     60 		if (first_range || second_range || third_range) {
     61 			target_ncached_max = first_range
     62 			    ? 1001
     63 			    : (second_range ? 0 : 1);
     64 			expect_true(tcache_get_default_ncached_max_set(i),
     65 			    "Unexpected state for bin %u", i);
     66 			expect_zu_eq(target_ncached_max,
     67 			    tcache_bin_info[i].ncached_max,
     68 			    "Unexpected generated ncached_max for bin %u", i);
     69 			expect_zu_eq(target_ncached_max,
     70 			    tcache_get_default_ncached_max()[i].ncached_max,
     71 			    "Unexpected pre-set ncached_max for bin %u", i);
     72 		} else {
     73 			expect_false(tcache_get_default_ncached_max_set(i),
     74 			    "Unexpected state for bin %u", i);
     75 		}
     76 	}
     77 	unsigned nbins = tcache_nbins_get(tcache_slow);
     78 	for (szind_t i = nbins; i < TCACHE_NBINS_MAX; i++) {
     79 		cache_bin_info_init(&tcache_bin_info[i], 0);
     80 	}
     81 	/* Check the initial bin settings. */
     82 	check_bins_info(tcache_bin_info);
     83 
     84 	size_t mib_set[4], mib_set_len;
     85 	mib_set_len = sizeof(mib_set) / sizeof(size_t);
     86 	const char *set_name = "thread.tcache.ncached_max.write";
     87 	expect_d_eq(mallctlnametomib(set_name, mib_set, &mib_set_len), 0,
     88 	    "Unexpected mallctlnametomib() failure");
     89 
     90 	/* Test the ncached_max set with tcache on. */
     91 	char  inputs[100] = "8-128:1|160-160:11|170-320:22|224-8388609:0";
     92 	char *inputp = inputs;
     93 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
     94 	                (void *)&inputp, sizeof(char *)),
     95 	    0, "Unexpected mallctlbymib() failure");
     96 	for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) {
     97 		if (i >= sz_size2index(8) && i <= sz_size2index(128)) {
     98 			cache_bin_info_init(&tcache_bin_info[i], 1);
     99 		}
    100 		if (i == sz_size2index(160)) {
    101 			cache_bin_info_init(&tcache_bin_info[i], 11);
    102 		}
    103 		if (i >= sz_size2index(170) && i <= sz_size2index(320)) {
    104 			cache_bin_info_init(&tcache_bin_info[i], 22);
    105 		}
    106 		if (i >= sz_size2index(224)) {
    107 			cache_bin_info_init(&tcache_bin_info[i], 0);
    108 		}
    109 		if (i >= nbins) {
    110 			cache_bin_info_init(&tcache_bin_info[i], 0);
    111 		}
    112 	}
    113 	check_bins_info(tcache_bin_info);
    114 
    115 	/*
    116 	 * Close the tcache and set ncached_max of some bins.  It will be
    117 	 * set properly but thread.tcache.ncached_max.read still returns 0
    118 	 * since the bin is not available yet.  After enabling the tcache,
    119 	 * the new setting will not be carried on.  Instead, the default
    120 	 * settings will be applied.
    121 	 */
    122 	bool   e0 = false, e1;
    123 	size_t bool_sz = sizeof(bool);
    124 	expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e1, &bool_sz,
    125 	                (void *)&e0, bool_sz),
    126 	    0, "Unexpected mallctl() error");
    127 	expect_true(e1, "Unexpected previous tcache state");
    128 	strcpy(inputs, "0-112:8");
    129 	/* Setting returns ENOENT when the tcache is disabled. */
    130 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    131 	                (void *)&inputp, sizeof(char *)),
    132 	    ENOENT, "Unexpected mallctlbymib() failure");
    133 	/* All ncached_max should return 0 once tcache is disabled. */
    134 	for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) {
    135 		cache_bin_info_init(&tcache_bin_info[i], 0);
    136 	}
    137 	check_bins_info(tcache_bin_info);
    138 
    139 	e0 = true;
    140 	expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e1, &bool_sz,
    141 	                (void *)&e0, bool_sz),
    142 	    0, "Unexpected mallctl() error");
    143 	expect_false(e1, "Unexpected previous tcache state");
    144 	memcpy(tcache_bin_info, tcache_bin_info_backup,
    145 	    sizeof(tcache_bin_info_backup));
    146 	for (szind_t i = tcache_nbins_get(tcache_slow); i < TCACHE_NBINS_MAX;
    147 	     i++) {
    148 		cache_bin_info_init(&tcache_bin_info[i], 0);
    149 	}
    150 	check_bins_info(tcache_bin_info);
    151 
    152 	/*
    153 	 * Set ncached_max of bins not enabled yet.  Then, enable them by
    154 	 * resetting tcache_max.  The ncached_max changes should stay.
    155 	 */
    156 	size_t tcache_max = 1024;
    157 	assert_d_eq(mallctl("thread.tcache.max", NULL, NULL,
    158 	                (void *)&tcache_max, sizeof(size_t)),
    159 	    .0, "Unexpected.mallctl().failure");
    160 	for (szind_t i = sz_size2index(1024) + 1; i < TCACHE_NBINS_MAX; i++) {
    161 		cache_bin_info_init(&tcache_bin_info[i], 0);
    162 	}
    163 	strcpy(inputs, "2048-6144:123");
    164 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    165 	                (void *)&inputp, sizeof(char *)),
    166 	    0, "Unexpected mallctlbymib() failure");
    167 	check_bins_info(tcache_bin_info);
    168 
    169 	tcache_max = 6144;
    170 	assert_d_eq(mallctl("thread.tcache.max", NULL, NULL,
    171 	                (void *)&tcache_max, sizeof(size_t)),
    172 	    .0, "Unexpected.mallctl().failure");
    173 	memcpy(tcache_bin_info, tcache_bin_info_backup,
    174 	    sizeof(tcache_bin_info_backup));
    175 	for (szind_t i = sz_size2index(2048); i < TCACHE_NBINS_MAX; i++) {
    176 		if (i <= sz_size2index(6144)) {
    177 			cache_bin_info_init(&tcache_bin_info[i], 123);
    178 		} else if (i > sz_size2index(6144)) {
    179 			cache_bin_info_init(&tcache_bin_info[i], 0);
    180 		}
    181 	}
    182 	check_bins_info(tcache_bin_info);
    183 
    184 	/* Test an empty input, it should do nothing. */
    185 	strcpy(inputs, "");
    186 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    187 	                (void *)&inputp, sizeof(char *)),
    188 	    0, "Unexpected mallctlbymib() failure");
    189 	check_bins_info(tcache_bin_info);
    190 
    191 	/* Test a half-done string, it should return EINVAL and do nothing. */
    192 	strcpy(inputs, "4-1024:7|256-1024");
    193 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    194 	                (void *)&inputp, sizeof(char *)),
    195 	    EINVAL, "Unexpected mallctlbymib() failure");
    196 	check_bins_info(tcache_bin_info);
    197 
    198 	/*
    199 	 * Test an invalid string with start size larger than end size.  It
    200 	 * should return success but do nothing.
    201 	 */
    202 	strcpy(inputs, "1024-256:7");
    203 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    204 	                (void *)&inputp, sizeof(char *)),
    205 	    0, "Unexpected mallctlbymib() failure");
    206 	check_bins_info(tcache_bin_info);
    207 
    208 	/*
    209 	 * Test a string exceeding the length limit, it should return EINVAL
    210 	 * and do nothing.
    211 	 */
    212 	char *long_inputs = (char *)malloc(10000 * sizeof(char));
    213 	expect_true(long_inputs != NULL, "Unexpected allocation failure.");
    214 	for (int i = 0; i < 200; i++) {
    215 		memcpy(long_inputs + i * 9, "4-1024:3|", 9);
    216 	}
    217 	memcpy(long_inputs + 200 * 9, "4-1024:3", 8);
    218 	long_inputs[200 * 9 + 8] = '\0';
    219 	inputp = long_inputs;
    220 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    221 	                (void *)&inputp, sizeof(char *)),
    222 	    EINVAL, "Unexpected mallctlbymib() failure");
    223 	check_bins_info(tcache_bin_info);
    224 	free(long_inputs);
    225 
    226 	/*
    227 	 * Test a string with invalid characters, it should return EINVAL
    228 	 * and do nothing.
    229 	 */
    230 	strcpy(inputs, "k8-1024:77p");
    231 	inputp = inputs;
    232 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    233 	                (void *)&inputp, sizeof(char *)),
    234 	    EINVAL, "Unexpected mallctlbymib() failure");
    235 	check_bins_info(tcache_bin_info);
    236 
    237 	/* Test large ncached_max, it should return success but capped. */
    238 	strcpy(inputs, "1024-1024:65540");
    239 	expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL,
    240 	                (void *)&inputp, sizeof(char *)),
    241 	    0, "Unexpected mallctlbymib() failure");
    242 	cache_bin_info_init(
    243 	    &tcache_bin_info[sz_size2index(1024)], CACHE_BIN_NCACHED_MAX);
    244 	check_bins_info(tcache_bin_info);
    245 
    246 	return NULL;
    247 }
    248 
    249 TEST_BEGIN(test_ncached_max) {
    250 	test_skip_if(!config_stats);
    251 	test_skip_if(!opt_tcache);
    252 	test_skip_if(san_uaf_detection_enabled());
    253 	/* TODO: change nthreads to 8 to reduce CI loads. */
    254 	unsigned nthreads = 108;
    255 	VARIABLE_ARRAY(thd_t, threads, nthreads);
    256 	for (unsigned i = 0; i < nthreads; i++) {
    257 		thd_create(&threads[i], ncached_max_check, NULL);
    258 	}
    259 	for (unsigned i = 0; i < nthreads; i++) {
    260 		thd_join(threads[i], NULL);
    261 	}
    262 }
    263 TEST_END
    264 
    265 int
    266 main(void) {
    267 	return test(test_ncached_max);
    268 }
    269