diff --git a/Makefile.in b/Makefile.in index 1c9e4004..87ddd338 100644 --- a/Makefile.in +++ b/Makefile.in @@ -228,6 +228,7 @@ TESTS_UNIT := \ $(srcroot)test/unit/prof_gdump.c \ $(srcroot)test/unit/prof_idump.c \ $(srcroot)test/unit/prof_log.c \ + $(srcroot)test/unit/prof_mdump.c \ $(srcroot)test/unit/prof_recent.c \ $(srcroot)test/unit/prof_reset.c \ $(srcroot)test/unit/prof_tctx.c \ diff --git a/include/jemalloc/internal/prof_externs.h b/include/jemalloc/internal/prof_externs.h index d644be64..9a2b1224 100644 --- a/include/jemalloc/internal/prof_externs.h +++ b/include/jemalloc/internal/prof_externs.h @@ -98,7 +98,7 @@ typedef int (prof_dump_open_file_t)(const char *, int); extern prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file; typedef ssize_t (prof_dump_write_file_t)(int, const void *, size_t); extern prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file; -typedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *); +typedef void (prof_dump_header_t)(tsdn_t *, const prof_cnt_t *); extern prof_dump_header_t *JET_MUTABLE prof_dump_header; typedef int (prof_dump_open_maps_t)(); extern prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps; diff --git a/src/prof_data.c b/src/prof_data.c index d5f55241..210b153f 100644 --- a/src/prof_data.c +++ b/src/prof_data.c @@ -55,6 +55,20 @@ static ckh_t bt2gctx; */ static prof_tdata_tree_t tdatas; +/* The following are needed for dumping and are protected by prof_dump_mtx. */ +/* + * Whether there has been an error in the dumping process, which could have + * happened either in file opening or in file writing. When an error has + * already occurred, we will stop further writing to the file. + */ +static bool prof_dump_error; +/* + * Whether error should be handled locally: if true, then we print out error + * message as well as abort (if opt_abort is true) when an error occurred, and + * we also report the error back to the caller in the end; if false, then we + * only report the error back to the caller in the end. + */ +static bool prof_dump_handle_error_locally; /* * This buffer is rather large for stack allocation, so use a single buffer for * all profile dumps. @@ -459,6 +473,30 @@ prof_bt_count(void) { return bt_count; } +static void +prof_dump_check_possible_error(bool err_cond, const char *format, ...) { + assert(!prof_dump_error); + if (!err_cond) { + return; + } + + prof_dump_error = true; + if (!prof_dump_handle_error_locally) { + return; + } + + va_list ap; + char buf[PROF_PRINTF_BUFSIZE]; + va_start(ap, format); + malloc_vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + malloc_write(buf); + + if (opt_abort) { + abort(); + } +} + static int prof_dump_open_file_impl(const char *filename, int mode) { return creat(filename, mode); @@ -466,61 +504,37 @@ prof_dump_open_file_impl(const char *filename, int mode) { prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file = prof_dump_open_file_impl; -static int -prof_dump_open(bool propagate_err, const char *filename) { - int fd; - - fd = prof_dump_open_file(filename, 0644); - if (fd == -1 && !propagate_err) { - malloc_printf(": failed to open \"%s\"\n", filename); - if (opt_abort) { - abort(); - } - } - - return fd; +static void +prof_dump_open(const char *filename) { + prof_dump_fd = prof_dump_open_file(filename, 0644); + prof_dump_check_possible_error(prof_dump_fd == -1, + ": failed to open \"%s\"\n", filename); } prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file = malloc_write_fd; -static bool -prof_dump_flush(bool propagate_err) { - bool ret = false; - ssize_t err; - +static void +prof_dump_flush() { cassert(config_prof); - - err = prof_dump_write_file(prof_dump_fd, prof_dump_buf, - prof_dump_buf_end); - if (err == -1) { - if (!propagate_err) { - malloc_write(": failed to write during heap " - "profile flush\n"); - if (opt_abort) { - abort(); - } - } - ret = true; + if (!prof_dump_error) { + ssize_t err = prof_dump_write_file(prof_dump_fd, prof_dump_buf, + prof_dump_buf_end); + prof_dump_check_possible_error(err == -1, + ": failed to write during heap profile flush\n"); } prof_dump_buf_end = 0; - - return ret; } -static bool -prof_dump_close(bool propagate_err) { - bool ret; - - assert(prof_dump_fd != -1); - ret = prof_dump_flush(propagate_err); - close(prof_dump_fd); - prof_dump_fd = -1; - - return ret; +static void +prof_dump_close() { + if (prof_dump_fd != -1) { + prof_dump_flush(); + close(prof_dump_fd); + } } -static bool -prof_dump_write(bool propagate_err, const char *s) { +static void +prof_dump_write(const char *s) { size_t i, slen, n; cassert(config_prof); @@ -530,9 +544,7 @@ prof_dump_write(bool propagate_err, const char *s) { while (i < slen) { /* Flush the buffer if it is full. */ if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) { - if (prof_dump_flush(propagate_err) && propagate_err) { - return true; - } + prof_dump_flush(); } if (prof_dump_buf_end + slen - i <= PROF_DUMP_BUFSIZE) { @@ -547,23 +559,18 @@ prof_dump_write(bool propagate_err, const char *s) { i += n; } assert(i == slen); - - return false; } -JEMALLOC_FORMAT_PRINTF(2, 3) -static bool -prof_dump_printf(bool propagate_err, const char *format, ...) { - bool ret; +JEMALLOC_FORMAT_PRINTF(1, 2) +static void +prof_dump_printf(const char *format, ...) { va_list ap; char buf[PROF_PRINTF_BUFSIZE]; va_start(ap, format); malloc_vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); - ret = prof_dump_write(propagate_err, buf); - - return ret; + prof_dump_write(buf); } static void @@ -630,17 +637,10 @@ prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) { return NULL; } -struct prof_tctx_dump_iter_arg_s { - tsdn_t *tsdn; - bool propagate_err; -}; - static prof_tctx_t * -prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) { - struct prof_tctx_dump_iter_arg_s *arg = - (struct prof_tctx_dump_iter_arg_s *)opaque; - - malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock); +prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) { + tsdn_t *tsdn = (tsdn_t *)arg; + malloc_mutex_assert_owner(tsdn, tctx->gctx->lock); switch (tctx->state) { case prof_tctx_state_initializing: @@ -649,13 +649,11 @@ prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) { break; case prof_tctx_state_dumping: case prof_tctx_state_purgatory: - if (prof_dump_printf(arg->propagate_err, + prof_dump_printf( " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": " "%"FMTu64"]\n", tctx->thr_uid, tctx->dump_cnts.curobjs, tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs, - tctx->dump_cnts.accumbytes)) { - return tctx; - } + tctx->dump_cnts.accumbytes); break; default: not_reached(); @@ -817,53 +815,37 @@ prof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, static prof_tdata_t * prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata, - void *arg) { - bool propagate_err = *(bool *)arg; - + void *unused) { if (!tdata->dumping) { return NULL; } - if (prof_dump_printf(propagate_err, + prof_dump_printf( " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n", tdata->thr_uid, tdata->cnt_summed.curobjs, tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs, tdata->cnt_summed.accumbytes, (tdata->thread_name != NULL) ? " " : "", - (tdata->thread_name != NULL) ? tdata->thread_name : "")) { - return tdata; - } + (tdata->thread_name != NULL) ? tdata->thread_name : ""); return NULL; } -static bool -prof_dump_header_impl(tsdn_t *tsdn, bool propagate_err, - const prof_cnt_t *cnt_all) { - bool ret; - - if (prof_dump_printf(propagate_err, - "heap_v2/%"FMTu64"\n" +static void +prof_dump_header_impl(tsdn_t *tsdn, const prof_cnt_t *cnt_all) { + prof_dump_printf("heap_v2/%"FMTu64"\n" " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs, - cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) { - return true; - } + cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes); malloc_mutex_lock(tsdn, &tdatas_mtx); - ret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter, - (void *)&propagate_err) != NULL); + tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter, NULL); malloc_mutex_unlock(tsdn, &tdatas_mtx); - return ret; } prof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl; -static bool -prof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx, - const prof_bt_t *bt, prof_gctx_tree_t *gctxs) { - bool ret; - unsigned i; - struct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg; - +static void +prof_dump_gctx(tsdn_t *tsdn, prof_gctx_t *gctx, const prof_bt_t *bt, + prof_gctx_tree_t *gctxs) { cassert(config_prof); malloc_mutex_assert_owner(tsdn, gctx->lock); @@ -874,42 +856,21 @@ prof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx, assert(gctx->cnt_summed.curbytes == 0); assert(gctx->cnt_summed.accumobjs == 0); assert(gctx->cnt_summed.accumbytes == 0); - ret = false; - goto label_return; + return; } - if (prof_dump_printf(propagate_err, "@")) { - ret = true; - goto label_return; - } - for (i = 0; i < bt->len; i++) { - if (prof_dump_printf(propagate_err, " %#"FMTxPTR, - (uintptr_t)bt->vec[i])) { - ret = true; - goto label_return; - } + prof_dump_write("@"); + for (unsigned i = 0; i < bt->len; i++) { + prof_dump_printf(" %#"FMTxPTR, (uintptr_t)bt->vec[i]); } - if (prof_dump_printf(propagate_err, - "\n" - " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", + prof_dump_printf( + "\n t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n", gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes, - gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) { - ret = true; - goto label_return; - } + gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes); - prof_tctx_dump_iter_arg.tsdn = tsdn; - prof_tctx_dump_iter_arg.propagate_err = propagate_err; - if (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter, - (void *)&prof_tctx_dump_iter_arg) != NULL) { - ret = true; - goto label_return; - } - - ret = false; -label_return: - return ret; + tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter, + (void *)tsdn); } #ifndef _WIN32 @@ -959,45 +920,26 @@ prof_dump_open_maps_impl() { prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps = prof_dump_open_maps_impl; -static bool -prof_dump_maps(bool propagate_err) { - bool ret; +static void +prof_dump_maps() { int mfd = prof_dump_open_maps(); + if (mfd == -1) { + return; + } - if (mfd != -1) { - ssize_t nread; - - if (prof_dump_write(propagate_err, "\nMAPPED_LIBRARIES:\n") && - propagate_err) { - ret = true; - goto label_return; + prof_dump_write("\nMAPPED_LIBRARIES:\n"); + ssize_t nread = 0; + do { + prof_dump_buf_end += nread; + if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) { + /* Make space in prof_dump_buf before read(). */ + prof_dump_flush(); } - nread = 0; - do { - prof_dump_buf_end += nread; - if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) { - /* Make space in prof_dump_buf before read(). */ - if (prof_dump_flush(propagate_err) && - propagate_err) { - ret = true; - goto label_return; - } - } - nread = malloc_read_fd(mfd, - &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE - - prof_dump_buf_end); - } while (nread > 0); - } else { - ret = true; - goto label_return; - } + nread = malloc_read_fd(mfd, &prof_dump_buf[prof_dump_buf_end], + PROF_DUMP_BUFSIZE - prof_dump_buf_end); + } while (nread > 0); - ret = false; -label_return: - if (mfd != -1) { - close(mfd); - } - return ret; + close(mfd); } /* @@ -1035,29 +977,13 @@ prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx, #endif } -struct prof_gctx_dump_iter_arg_s { - tsdn_t *tsdn; - bool propagate_err; -}; - static prof_gctx_t * prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) { - prof_gctx_t *ret; - struct prof_gctx_dump_iter_arg_s *arg = - (struct prof_gctx_dump_iter_arg_s *)opaque; - - malloc_mutex_lock(arg->tsdn, gctx->lock); - - if (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt, - gctxs)) { - ret = gctx; - goto label_return; - } - - ret = NULL; -label_return: - malloc_mutex_unlock(arg->tsdn, gctx->lock); - return ret; + tsdn_t *tsdn = (tsdn_t *)opaque; + malloc_mutex_lock(tsdn, gctx->lock); + prof_dump_gctx(tsdn, gctx, &gctx->bt, gctxs); + malloc_mutex_unlock(tsdn, gctx->lock); + return NULL; } static void @@ -1104,43 +1030,23 @@ prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata, static bool prof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename, - bool leakcheck, prof_tdata_t *tdata, - struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg, - struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg, - struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg, + bool leakcheck, prof_tdata_t *tdata, const prof_cnt_t *cnt_all, prof_gctx_tree_t *gctxs) { + prof_dump_error = false; + prof_dump_handle_error_locally = !propagate_err; + /* Create dump file. */ - if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) { - return true; - } - + prof_dump_open(filename); /* Dump profile header. */ - if (prof_dump_header(tsd_tsdn(tsd), propagate_err, - &prof_tdata_merge_iter_arg->cnt_all)) { - goto label_write_error; - } - + prof_dump_header(tsd_tsdn(tsd), cnt_all); /* Dump per gctx profile stats. */ - prof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd); - prof_gctx_dump_iter_arg->propagate_err = propagate_err; - if (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter, - (void *)prof_gctx_dump_iter_arg) != NULL) { - goto label_write_error; - } - + gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter, (void *)tsd_tsdn(tsd)); /* Dump /proc//maps if possible. */ - if (prof_dump_maps(propagate_err)) { - goto label_write_error; - } + prof_dump_maps(); + /* Close dump file. */ + prof_dump_close(); - if (prof_dump_close(propagate_err)) { - return true; - } - - return false; -label_write_error: - prof_dump_close(propagate_err); - return true; + return prof_dump_error; } bool @@ -1160,12 +1066,10 @@ prof_dump(tsd_t *tsd, bool propagate_err, const char *filename, prof_gctx_tree_t gctxs; struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg; struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg; - struct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg; prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg, &gctxs); - bool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata, - &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg, - &prof_gctx_dump_iter_arg, &gctxs); + bool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, + tdata, &prof_tdata_merge_iter_arg.cnt_all, &gctxs); prof_gctx_finish(tsd, &gctxs); malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx); diff --git a/test/unit/prof_mdump.c b/test/unit/prof_mdump.c new file mode 100644 index 00000000..3779c24e --- /dev/null +++ b/test/unit/prof_mdump.c @@ -0,0 +1,214 @@ +#include "test/jemalloc_test.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); +} diff --git a/test/unit/prof_mdump.sh b/test/unit/prof_mdump.sh new file mode 100644 index 00000000..d14cb8c5 --- /dev/null +++ b/test/unit/prof_mdump.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +if [ "x${enable_prof}" = "x1" ] ; then + export MALLOC_CONF="prof:true,lg_prof_sample:0" +fi + diff --git a/test/unit/prof_reset.c b/test/unit/prof_reset.c index 29fa02bb..dc64a04c 100644 --- a/test/unit/prof_reset.c +++ b/test/unit/prof_reset.c @@ -83,13 +83,10 @@ TEST_END bool prof_dump_header_intercepted = false; prof_cnt_t cnt_all_copy = {0, 0, 0, 0}; -static bool -prof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err, - const prof_cnt_t *cnt_all) { +static void +prof_dump_header_intercept(tsdn_t *tsdn, const prof_cnt_t *cnt_all) { prof_dump_header_intercepted = true; memcpy(&cnt_all_copy, cnt_all, sizeof(prof_cnt_t)); - - return false; } TEST_BEGIN(test_prof_reset_cleanup) {