| #include "test/jemalloc_test.h" |
| |
| #include "jemalloc/internal/prof_sys.h" |
| |
| static const char *test_filename = "test_filename"; |
| static bool did_prof_dump_open; |
| |
| static int |
| prof_dump_open_file_intercept(const char *filename, int mode) { |
| int fd; |
| |
| did_prof_dump_open = true; |
| |
| /* |
| * Stronger than a strcmp() - verifying that we internally directly use |
| * the caller supplied char pointer. |
| */ |
| expect_ptr_eq(filename, test_filename, |
| "Dump file name should be \"%s\"", test_filename); |
| |
| fd = open("/dev/null", O_WRONLY); |
| assert_d_ne(fd, -1, "Unexpected open() failure"); |
| |
| return fd; |
| } |
| |
| TEST_BEGIN(test_mdump_normal) { |
| test_skip_if(!config_prof); |
| |
| prof_dump_open_file_t *open_file_orig = prof_dump_open_file; |
| |
| void *p = mallocx(1, 0); |
| assert_ptr_not_null(p, "Unexpected mallocx() failure"); |
| |
| prof_dump_open_file = prof_dump_open_file_intercept; |
| did_prof_dump_open = false; |
| expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, |
| sizeof(test_filename)), 0, |
| "Unexpected mallctl failure while dumping"); |
| expect_true(did_prof_dump_open, "Expected a profile dump"); |
| |
| dallocx(p, 0); |
| |
| prof_dump_open_file = open_file_orig; |
| } |
| TEST_END |
| |
| static int |
| prof_dump_open_file_error(const char *filename, int mode) { |
| return -1; |
| } |
| |
| /* |
| * In the context of test_mdump_output_error, prof_dump_write_file_count is the |
| * total number of times prof_dump_write_file_error() is expected to be called. |
| * In the context of test_mdump_maps_error, prof_dump_write_file_count is the |
| * total number of times prof_dump_write_file_error() is expected to be called |
| * starting from the one that contains an 'M' (beginning the "MAPPED_LIBRARIES" |
| * header). |
| */ |
| static int prof_dump_write_file_count; |
| |
| static ssize_t |
| prof_dump_write_file_error(int fd, const void *s, size_t len) { |
| --prof_dump_write_file_count; |
| |
| expect_d_ge(prof_dump_write_file_count, 0, |
| "Write is called after error occurs"); |
| |
| if (prof_dump_write_file_count == 0) { |
| return -1; |
| } else { |
| /* |
| * Any non-negative number indicates success, and for |
| * simplicity we just use 0. When prof_dump_write_file_count |
| * is positive, it means that we haven't reached the write that |
| * we want to fail; when prof_dump_write_file_count is |
| * negative, it means that we've already violated the |
| * expect_d_ge(prof_dump_write_file_count, 0) statement above, |
| * but instead of aborting, we continue the rest of the test, |
| * and we indicate that all the writes after the failed write |
| * are successful. |
| */ |
| return 0; |
| } |
| } |
| |
| static void |
| expect_write_failure(int count) { |
| prof_dump_write_file_count = count; |
| expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, |
| sizeof(test_filename)), EFAULT, "Dump should err"); |
| expect_d_eq(prof_dump_write_file_count, 0, |
| "Dumping stopped after a wrong number of writes"); |
| } |
| |
| TEST_BEGIN(test_mdump_output_error) { |
| test_skip_if(!config_prof); |
| test_skip_if(!config_debug); |
| |
| prof_dump_open_file_t *open_file_orig = prof_dump_open_file; |
| prof_dump_write_file_t *write_file_orig = prof_dump_write_file; |
| |
| prof_dump_write_file = prof_dump_write_file_error; |
| |
| void *p = mallocx(1, 0); |
| assert_ptr_not_null(p, "Unexpected mallocx() failure"); |
| |
| /* |
| * When opening the dump file fails, there shouldn't be any write, and |
| * mallctl() should return failure. |
| */ |
| prof_dump_open_file = prof_dump_open_file_error; |
| expect_write_failure(0); |
| |
| /* |
| * When the n-th write fails, there shouldn't be any more write, and |
| * mallctl() should return failure. |
| */ |
| prof_dump_open_file = prof_dump_open_file_intercept; |
| expect_write_failure(1); /* First write fails. */ |
| expect_write_failure(2); /* Second write fails. */ |
| |
| dallocx(p, 0); |
| |
| prof_dump_open_file = open_file_orig; |
| prof_dump_write_file = write_file_orig; |
| } |
| TEST_END |
| |
| static int |
| prof_dump_open_maps_error() { |
| return -1; |
| } |
| |
| static bool started_piping_maps_file; |
| |
| static ssize_t |
| prof_dump_write_maps_file_error(int fd, const void *s, size_t len) { |
| /* The main dump doesn't contain any capital 'M'. */ |
| if (!started_piping_maps_file && strchr(s, 'M') != NULL) { |
| started_piping_maps_file = true; |
| } |
| |
| if (started_piping_maps_file) { |
| return prof_dump_write_file_error(fd, s, len); |
| } else { |
| /* Return success when we haven't started piping maps. */ |
| return 0; |
| } |
| } |
| |
| static void |
| expect_maps_write_failure(int count) { |
| int mfd = prof_dump_open_maps(); |
| if (mfd == -1) { |
| /* No need to continue if we just can't find the maps file. */ |
| return; |
| } |
| close(mfd); |
| started_piping_maps_file = false; |
| expect_write_failure(count); |
| expect_true(started_piping_maps_file, "Should start piping maps"); |
| } |
| |
| TEST_BEGIN(test_mdump_maps_error) { |
| test_skip_if(!config_prof); |
| test_skip_if(!config_debug); |
| |
| prof_dump_open_file_t *open_file_orig = prof_dump_open_file; |
| prof_dump_write_file_t *write_file_orig = prof_dump_write_file; |
| prof_dump_open_maps_t *open_maps_orig = prof_dump_open_maps; |
| |
| prof_dump_open_file = prof_dump_open_file_intercept; |
| prof_dump_write_file = prof_dump_write_maps_file_error; |
| |
| void *p = mallocx(1, 0); |
| assert_ptr_not_null(p, "Unexpected mallocx() failure"); |
| |
| /* |
| * When opening the maps file fails, there shouldn't be any maps write, |
| * and mallctl() should return success. |
| */ |
| prof_dump_open_maps = prof_dump_open_maps_error; |
| started_piping_maps_file = false; |
| prof_dump_write_file_count = 0; |
| expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename, |
| sizeof(test_filename)), 0, |
| "mallctl should not fail in case of maps file opening failure"); |
| expect_false(started_piping_maps_file, "Shouldn't start piping maps"); |
| expect_d_eq(prof_dump_write_file_count, 0, |
| "Dumping stopped after a wrong number of writes"); |
| |
| /* |
| * When the n-th maps write fails (given that we are able to find the |
| * maps file), there shouldn't be any more maps write, and mallctl() |
| * should return failure. |
| */ |
| prof_dump_open_maps = open_maps_orig; |
| expect_maps_write_failure(1); /* First write fails. */ |
| expect_maps_write_failure(2); /* Second write fails. */ |
| |
| dallocx(p, 0); |
| |
| prof_dump_open_file = open_file_orig; |
| prof_dump_write_file = write_file_orig; |
| } |
| TEST_END |
| |
| int |
| main(void) { |
| return test( |
| test_mdump_normal, |
| test_mdump_output_error, |
| test_mdump_maps_error); |
| } |