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