1*7bdf38e5Schristos #include "test/jemalloc_test.h" 2*7bdf38e5Schristos 3*7bdf38e5Schristos #include "jemalloc/internal/prof_sys.h" 4*7bdf38e5Schristos 5*7bdf38e5Schristos static const char *test_filename = "test_filename"; 6*7bdf38e5Schristos static bool did_prof_dump_open; 7*7bdf38e5Schristos 8*7bdf38e5Schristos static int 9*7bdf38e5Schristos prof_dump_open_file_intercept(const char *filename, int mode) { 10*7bdf38e5Schristos int fd; 11*7bdf38e5Schristos 12*7bdf38e5Schristos did_prof_dump_open = true; 13*7bdf38e5Schristos 14*7bdf38e5Schristos /* 15*7bdf38e5Schristos * Stronger than a strcmp() - verifying that we internally directly use 16*7bdf38e5Schristos * the caller supplied char pointer. 17*7bdf38e5Schristos */ 18*7bdf38e5Schristos expect_ptr_eq(filename, test_filename, 19*7bdf38e5Schristos "Dump file name should be \"%s\"", test_filename); 20*7bdf38e5Schristos 21*7bdf38e5Schristos fd = open("/dev/null", O_WRONLY); 22*7bdf38e5Schristos assert_d_ne(fd, -1, "Unexpected open() failure"); 23*7bdf38e5Schristos 24*7bdf38e5Schristos return fd; 25*7bdf38e5Schristos } 26*7bdf38e5Schristos 27*7bdf38e5Schristos TEST_BEGIN(test_mdump_normal) { 28*7bdf38e5Schristos test_skip_if(!config_prof); 29*7bdf38e5Schristos 30*7bdf38e5Schristos prof_dump_open_file_t *open_file_orig = prof_dump_open_file; 31*7bdf38e5Schristos 32*7bdf38e5Schristos void *p = mallocx(1, 0); 33*7bdf38e5Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 34*7bdf38e5Schristos 35*7bdf38e5Schristos prof_dump_open_file = prof_dump_open_file_intercept; 36*7bdf38e5Schristos did_prof_dump_open = false; 37*7bdf38e5Schristos expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, 38*7bdf38e5Schristos sizeof(test_filename)), 0, 39*7bdf38e5Schristos "Unexpected mallctl failure while dumping"); 40*7bdf38e5Schristos expect_true(did_prof_dump_open, "Expected a profile dump"); 41*7bdf38e5Schristos 42*7bdf38e5Schristos dallocx(p, 0); 43*7bdf38e5Schristos 44*7bdf38e5Schristos prof_dump_open_file = open_file_orig; 45*7bdf38e5Schristos } 46*7bdf38e5Schristos TEST_END 47*7bdf38e5Schristos 48*7bdf38e5Schristos static int 49*7bdf38e5Schristos prof_dump_open_file_error(const char *filename, int mode) { 50*7bdf38e5Schristos return -1; 51*7bdf38e5Schristos } 52*7bdf38e5Schristos 53*7bdf38e5Schristos /* 54*7bdf38e5Schristos * In the context of test_mdump_output_error, prof_dump_write_file_count is the 55*7bdf38e5Schristos * total number of times prof_dump_write_file_error() is expected to be called. 56*7bdf38e5Schristos * In the context of test_mdump_maps_error, prof_dump_write_file_count is the 57*7bdf38e5Schristos * total number of times prof_dump_write_file_error() is expected to be called 58*7bdf38e5Schristos * starting from the one that contains an 'M' (beginning the "MAPPED_LIBRARIES" 59*7bdf38e5Schristos * header). 60*7bdf38e5Schristos */ 61*7bdf38e5Schristos static int prof_dump_write_file_count; 62*7bdf38e5Schristos 63*7bdf38e5Schristos static ssize_t 64*7bdf38e5Schristos prof_dump_write_file_error(int fd, const void *s, size_t len) { 65*7bdf38e5Schristos --prof_dump_write_file_count; 66*7bdf38e5Schristos 67*7bdf38e5Schristos expect_d_ge(prof_dump_write_file_count, 0, 68*7bdf38e5Schristos "Write is called after error occurs"); 69*7bdf38e5Schristos 70*7bdf38e5Schristos if (prof_dump_write_file_count == 0) { 71*7bdf38e5Schristos return -1; 72*7bdf38e5Schristos } else { 73*7bdf38e5Schristos /* 74*7bdf38e5Schristos * Any non-negative number indicates success, and for 75*7bdf38e5Schristos * simplicity we just use 0. When prof_dump_write_file_count 76*7bdf38e5Schristos * is positive, it means that we haven't reached the write that 77*7bdf38e5Schristos * we want to fail; when prof_dump_write_file_count is 78*7bdf38e5Schristos * negative, it means that we've already violated the 79*7bdf38e5Schristos * expect_d_ge(prof_dump_write_file_count, 0) statement above, 80*7bdf38e5Schristos * but instead of aborting, we continue the rest of the test, 81*7bdf38e5Schristos * and we indicate that all the writes after the failed write 82*7bdf38e5Schristos * are successful. 83*7bdf38e5Schristos */ 84*7bdf38e5Schristos return 0; 85*7bdf38e5Schristos } 86*7bdf38e5Schristos } 87*7bdf38e5Schristos 88*7bdf38e5Schristos static void 89*7bdf38e5Schristos expect_write_failure(int count) { 90*7bdf38e5Schristos prof_dump_write_file_count = count; 91*7bdf38e5Schristos expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, 92*7bdf38e5Schristos sizeof(test_filename)), EFAULT, "Dump should err"); 93*7bdf38e5Schristos expect_d_eq(prof_dump_write_file_count, 0, 94*7bdf38e5Schristos "Dumping stopped after a wrong number of writes"); 95*7bdf38e5Schristos } 96*7bdf38e5Schristos 97*7bdf38e5Schristos TEST_BEGIN(test_mdump_output_error) { 98*7bdf38e5Schristos test_skip_if(!config_prof); 99*7bdf38e5Schristos test_skip_if(!config_debug); 100*7bdf38e5Schristos 101*7bdf38e5Schristos prof_dump_open_file_t *open_file_orig = prof_dump_open_file; 102*7bdf38e5Schristos prof_dump_write_file_t *write_file_orig = prof_dump_write_file; 103*7bdf38e5Schristos 104*7bdf38e5Schristos prof_dump_write_file = prof_dump_write_file_error; 105*7bdf38e5Schristos 106*7bdf38e5Schristos void *p = mallocx(1, 0); 107*7bdf38e5Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 108*7bdf38e5Schristos 109*7bdf38e5Schristos /* 110*7bdf38e5Schristos * When opening the dump file fails, there shouldn't be any write, and 111*7bdf38e5Schristos * mallctl() should return failure. 112*7bdf38e5Schristos */ 113*7bdf38e5Schristos prof_dump_open_file = prof_dump_open_file_error; 114*7bdf38e5Schristos expect_write_failure(0); 115*7bdf38e5Schristos 116*7bdf38e5Schristos /* 117*7bdf38e5Schristos * When the n-th write fails, there shouldn't be any more write, and 118*7bdf38e5Schristos * mallctl() should return failure. 119*7bdf38e5Schristos */ 120*7bdf38e5Schristos prof_dump_open_file = prof_dump_open_file_intercept; 121*7bdf38e5Schristos expect_write_failure(1); /* First write fails. */ 122*7bdf38e5Schristos expect_write_failure(2); /* Second write fails. */ 123*7bdf38e5Schristos 124*7bdf38e5Schristos dallocx(p, 0); 125*7bdf38e5Schristos 126*7bdf38e5Schristos prof_dump_open_file = open_file_orig; 127*7bdf38e5Schristos prof_dump_write_file = write_file_orig; 128*7bdf38e5Schristos } 129*7bdf38e5Schristos TEST_END 130*7bdf38e5Schristos 131*7bdf38e5Schristos static int 132*7bdf38e5Schristos prof_dump_open_maps_error() { 133*7bdf38e5Schristos return -1; 134*7bdf38e5Schristos } 135*7bdf38e5Schristos 136*7bdf38e5Schristos static bool started_piping_maps_file; 137*7bdf38e5Schristos 138*7bdf38e5Schristos static ssize_t 139*7bdf38e5Schristos prof_dump_write_maps_file_error(int fd, const void *s, size_t len) { 140*7bdf38e5Schristos /* The main dump doesn't contain any capital 'M'. */ 141*7bdf38e5Schristos if (!started_piping_maps_file && strchr(s, 'M') != NULL) { 142*7bdf38e5Schristos started_piping_maps_file = true; 143*7bdf38e5Schristos } 144*7bdf38e5Schristos 145*7bdf38e5Schristos if (started_piping_maps_file) { 146*7bdf38e5Schristos return prof_dump_write_file_error(fd, s, len); 147*7bdf38e5Schristos } else { 148*7bdf38e5Schristos /* Return success when we haven't started piping maps. */ 149*7bdf38e5Schristos return 0; 150*7bdf38e5Schristos } 151*7bdf38e5Schristos } 152*7bdf38e5Schristos 153*7bdf38e5Schristos static void 154*7bdf38e5Schristos expect_maps_write_failure(int count) { 155*7bdf38e5Schristos int mfd = prof_dump_open_maps(); 156*7bdf38e5Schristos if (mfd == -1) { 157*7bdf38e5Schristos /* No need to continue if we just can't find the maps file. */ 158*7bdf38e5Schristos return; 159*7bdf38e5Schristos } 160*7bdf38e5Schristos close(mfd); 161*7bdf38e5Schristos started_piping_maps_file = false; 162*7bdf38e5Schristos expect_write_failure(count); 163*7bdf38e5Schristos expect_true(started_piping_maps_file, "Should start piping maps"); 164*7bdf38e5Schristos } 165*7bdf38e5Schristos 166*7bdf38e5Schristos TEST_BEGIN(test_mdump_maps_error) { 167*7bdf38e5Schristos test_skip_if(!config_prof); 168*7bdf38e5Schristos test_skip_if(!config_debug); 169*7bdf38e5Schristos 170*7bdf38e5Schristos prof_dump_open_file_t *open_file_orig = prof_dump_open_file; 171*7bdf38e5Schristos prof_dump_write_file_t *write_file_orig = prof_dump_write_file; 172*7bdf38e5Schristos prof_dump_open_maps_t *open_maps_orig = prof_dump_open_maps; 173*7bdf38e5Schristos 174*7bdf38e5Schristos prof_dump_open_file = prof_dump_open_file_intercept; 175*7bdf38e5Schristos prof_dump_write_file = prof_dump_write_maps_file_error; 176*7bdf38e5Schristos 177*7bdf38e5Schristos void *p = mallocx(1, 0); 178*7bdf38e5Schristos assert_ptr_not_null(p, "Unexpected mallocx() failure"); 179*7bdf38e5Schristos 180*7bdf38e5Schristos /* 181*7bdf38e5Schristos * When opening the maps file fails, there shouldn't be any maps write, 182*7bdf38e5Schristos * and mallctl() should return success. 183*7bdf38e5Schristos */ 184*7bdf38e5Schristos prof_dump_open_maps = prof_dump_open_maps_error; 185*7bdf38e5Schristos started_piping_maps_file = false; 186*7bdf38e5Schristos prof_dump_write_file_count = 0; 187*7bdf38e5Schristos expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, 188*7bdf38e5Schristos sizeof(test_filename)), 0, 189*7bdf38e5Schristos "mallctl should not fail in case of maps file opening failure"); 190*7bdf38e5Schristos expect_false(started_piping_maps_file, "Shouldn't start piping maps"); 191*7bdf38e5Schristos expect_d_eq(prof_dump_write_file_count, 0, 192*7bdf38e5Schristos "Dumping stopped after a wrong number of writes"); 193*7bdf38e5Schristos 194*7bdf38e5Schristos /* 195*7bdf38e5Schristos * When the n-th maps write fails (given that we are able to find the 196*7bdf38e5Schristos * maps file), there shouldn't be any more maps write, and mallctl() 197*7bdf38e5Schristos * should return failure. 198*7bdf38e5Schristos */ 199*7bdf38e5Schristos prof_dump_open_maps = open_maps_orig; 200*7bdf38e5Schristos expect_maps_write_failure(1); /* First write fails. */ 201*7bdf38e5Schristos expect_maps_write_failure(2); /* Second write fails. */ 202*7bdf38e5Schristos 203*7bdf38e5Schristos dallocx(p, 0); 204*7bdf38e5Schristos 205*7bdf38e5Schristos prof_dump_open_file = open_file_orig; 206*7bdf38e5Schristos prof_dump_write_file = write_file_orig; 207*7bdf38e5Schristos } 208*7bdf38e5Schristos TEST_END 209*7bdf38e5Schristos 210*7bdf38e5Schristos int 211*7bdf38e5Schristos main(void) { 212*7bdf38e5Schristos return test( 213*7bdf38e5Schristos test_mdump_normal, 214*7bdf38e5Schristos test_mdump_output_error, 215*7bdf38e5Schristos test_mdump_maps_error); 216*7bdf38e5Schristos } 217