Merge "tests/utils.h should #include <sys/sysmacros.h>"
diff --git a/libc/Android.bp b/libc/Android.bp
index b2ebe09..3877145 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -613,6 +613,13 @@
"upstream-openbsd/lib/libc/string/strcat.c",
],
},
+ cortex_a73: {
+ exclude_srcs: [
+ "upstream-openbsd/lib/libc/string/memmove.c",
+ "upstream-openbsd/lib/libc/string/stpcpy.c",
+ "upstream-openbsd/lib/libc/string/strcat.c",
+ ],
+ },
denver: {
exclude_srcs: [
"upstream-openbsd/lib/libc/string/memmove.c",
@@ -969,6 +976,32 @@
"bionic/__strcpy_chk.cpp",
],
},
+ cortex_a73: {
+ srcs: [
+ "arch-arm/cortex-a7/bionic/memset.S",
+
+ "arch-arm/denver/bionic/memcpy.S",
+ "arch-arm/denver/bionic/memmove.S",
+ "arch-arm/denver/bionic/__strcat_chk.S",
+ "arch-arm/denver/bionic/__strcpy_chk.S",
+
+ "arch-arm/krait/bionic/strcmp.S",
+
+ "arch-arm/cortex-a15/bionic/stpcpy.S",
+ "arch-arm/cortex-a15/bionic/strcat.S",
+ "arch-arm/cortex-a15/bionic/strcpy.S",
+ "arch-arm/cortex-a15/bionic/strlen.S",
+ ],
+ exclude_srcs: [
+ "arch-arm/generic/bionic/memcpy.S",
+ "arch-arm/generic/bionic/memset.S",
+ "arch-arm/generic/bionic/strcmp.S",
+ "arch-arm/generic/bionic/strcpy.S",
+ "arch-arm/generic/bionic/strlen.c",
+ "bionic/__strcat_chk.cpp",
+ "bionic/__strcpy_chk.cpp",
+ ],
+ },
denver: {
srcs: [
"arch-arm/denver/bionic/memcpy.S",
@@ -1092,6 +1125,14 @@
"arch-arm64/generic/bionic/memmove.S",
],
},
+ cortex_a73: {
+ srcs: [
+ "arch-arm64/cortex-a53/bionic/memmove.S",
+ ],
+ exclude_srcs: [
+ "arch-arm64/generic/bionic/memmove.S",
+ ],
+ },
},
mips: {
diff --git a/libc/arch-arm64/generic/bionic/memset.S b/libc/arch-arm64/generic/bionic/memset.S
index 3416cfb..12fc09d 100644
--- a/libc/arch-arm64/generic/bionic/memset.S
+++ b/libc/arch-arm64/generic/bionic/memset.S
@@ -80,7 +80,7 @@
#define tmp1w w5
#define tmp2 x6
#define tmp2w w6
-#define zva_len x5
+#define zva_len x7
#define zva_lenw w7
#define L(l) .L ## l
diff --git a/libc/bionic/jemalloc.h b/libc/bionic/jemalloc.h
index fceb323..f7e8770 100644
--- a/libc/bionic/jemalloc.h
+++ b/libc/bionic/jemalloc.h
@@ -26,6 +26,7 @@
__BEGIN_DECLS
struct mallinfo je_mallinfo();
+int je_mallopt(int, int);
int je_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
void je_malloc_disable();
void je_malloc_enable();
diff --git a/libc/bionic/jemalloc_wrapper.cpp b/libc/bionic/jemalloc_wrapper.cpp
index e33d560..266b966 100644
--- a/libc/bionic/jemalloc_wrapper.cpp
+++ b/libc/bionic/jemalloc_wrapper.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <malloc.h>
#include <sys/param.h>
#include <unistd.h>
@@ -46,3 +47,38 @@
}
return je_memalign(boundary, size);
}
+
+int je_mallopt(int param, int value) {
+ // The only parameter we currently understand is M_DECAY_TIME.
+ if (param == M_DECAY_TIME) {
+ // Only support setting the value to 1 or 0.
+ ssize_t decay_time;
+ if (value) {
+ decay_time = 1;
+ } else {
+ decay_time = 0;
+ }
+ // First get the total number of arenas.
+ unsigned narenas;
+ size_t sz = sizeof(unsigned);
+ if (je_mallctl("arenas.narenas", &narenas, &sz, nullptr, 0) != 0) {
+ return 0;
+ }
+
+ // Set the decay time for any arenas that will be created in the future.
+ if (je_mallctl("arenas.decay_time", nullptr, nullptr, &decay_time, sizeof(decay_time)) != 0) {
+ return 0;
+ }
+
+ // Change the decay on the already existing arenas.
+ char buffer[100];
+ for (unsigned i = 0; i < narenas; i++) {
+ snprintf(buffer, sizeof(buffer), "arena.%d.decay_time", i);
+ if (je_mallctl(buffer, nullptr, nullptr, &decay_time, sizeof(decay_time)) != 0) {
+ break;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 18c998d..1f201d1 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -68,6 +68,7 @@
Malloc(iterate),
Malloc(malloc_disable),
Malloc(malloc_enable),
+ Malloc(mallopt),
};
// In a VM process, this is set to 1 after fork()ing out of zygote.
@@ -101,6 +102,14 @@
return Malloc(mallinfo)();
}
+extern "C" int mallopt(int param, int value) {
+ auto _mallopt = __libc_globals->malloc_dispatch.mallopt;
+ if (__predict_false(_mallopt != nullptr)) {
+ return _mallopt(param, value);
+ }
+ return Malloc(mallopt)(param, value);
+}
+
extern "C" void* malloc(size_t bytes) {
auto _malloc = __libc_globals->malloc_dispatch.malloc;
if (__predict_false(_malloc != nullptr)) {
@@ -247,6 +256,10 @@
prefix, "mallinfo")) {
return false;
}
+ if (!InitMallocFunction<MallocMallopt>(malloc_impl_handler, &table->mallopt,
+ prefix, "mallopt")) {
+ return false;
+ }
if (!InitMallocFunction<MallocMalloc>(malloc_impl_handler, &table->malloc,
prefix, "malloc")) {
return false;
diff --git a/libc/include/malloc.h b/libc/include/malloc.h
index 412e3f0..db5da04 100644
--- a/libc/include/malloc.h
+++ b/libc/include/malloc.h
@@ -77,6 +77,11 @@
*/
int malloc_info(int, FILE*) __INTRODUCED_IN(23);
+/* mallopt options */
+#define M_DECAY_TIME -100
+
+int mallopt(int, int) __INTRODUCED_IN(26);
+
__END_DECLS
#endif /* LIBC_INCLUDE_MALLOC_H_ */
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 7500742..4654f99 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1551,4 +1551,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1268,4 +1268,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 93d31a7..f68f1c5 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1392,4 +1392,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1268,4 +1268,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 08ef8be..bea2fe9 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1391,4 +1391,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 9875eda..2c04e4d 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1268,4 +1268,5 @@
malloc_disable;
malloc_enable;
malloc_iterate;
+ mallopt;
} LIBC_O;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index addb5d4..014d385 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -77,6 +77,7 @@
void* debug_realloc(void* pointer, size_t bytes);
void* debug_calloc(size_t nmemb, size_t bytes);
struct mallinfo debug_mallinfo();
+int debug_mallopt(int param, int value);
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
int debug_iterate(uintptr_t base, size_t size,
void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
@@ -639,6 +640,10 @@
return g_dispatch->mallinfo();
}
+int debug_mallopt(int param, int value) {
+ return g_dispatch->mallopt(param, value);
+}
+
int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
if (DebugCallsDisabled()) {
return g_dispatch->posix_memalign(memptr, alignment, size);
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 219c21e..4fdba2e 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -58,6 +58,7 @@
void debug_free_malloc_leak_info(uint8_t*);
struct mallinfo debug_mallinfo();
+int debug_mallopt(int, int);
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
void* debug_pvalloc(size_t);
@@ -128,6 +129,7 @@
nullptr,
nullptr,
nullptr,
+ mallopt,
};
void VerifyAllocCalls() {
@@ -1474,6 +1476,20 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugTest, debug_mallopt) {
+ Init("guard");
+
+ void* pointer = debug_malloc(150);
+ ASSERT_TRUE(pointer != nullptr);
+
+ EXPECT_EQ(0, debug_mallopt(-1000, 1));
+
+ debug_free(pointer);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugTest, debug_posix_memalign) {
Init("guard");
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index 02a092f..cdae466 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -45,6 +45,7 @@
typedef int (*MallocIterate)(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
typedef void (*MallocMallocDisable)();
typedef void (*MallocMallocEnable)();
+typedef int (*MallocMallopt)(int, int);
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
typedef void* (*MallocPvalloc)(size_t);
@@ -69,6 +70,7 @@
MallocIterate iterate;
MallocMallocDisable malloc_disable;
MallocMallocEnable malloc_enable;
+ MallocMallopt mallopt;
} __attribute__((aligned(32)));
#endif
diff --git a/tests/leak_test.cpp b/tests/leak_test.cpp
index 9ddb2ff..a356640 100644
--- a/tests/leak_test.cpp
+++ b/tests/leak_test.cpp
@@ -49,28 +49,61 @@
return result;
}
-#define LEAK_TEST(test_case_name, test_name) \
- static void __leak_test__##test_case_name##__##test_name(); \
- TEST(test_case_name, test_name) { \
- auto previous_size = GetMappingSize(); \
- __leak_test__##test_case_name##__##test_name(); \
- auto current_size = GetMappingSize(); \
- if (current_size > previous_size) { \
- FAIL() << "increase in process map size: " << previous_size << " -> " << current_size; \
- } \
- } \
- static void __leak_test__##test_case_name##__##test_name()
-
-LEAK_TEST(leak, smoke) {
- // Do nothing.
+static void WaitUntilAllExited(pid_t* pids, size_t pid_count) {
+ // Wait until all children have exited.
+ bool alive = true;
+ while (alive) {
+ alive = false;
+ for (size_t i = 0; i < pid_count; ++i) {
+ if (pids[i] != 0) {
+ if (kill(pids[i], 0) == 0) {
+ alive = true;
+ } else {
+ EXPECT_EQ(errno, ESRCH);
+ pids[i] = 0; // Skip in next loop.
+ }
+ }
+ }
+ }
}
-LEAK_TEST(leak, xfail) {
- UNUSED(mmap(nullptr, PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+class LeakChecker {
+ public:
+ LeakChecker() {
+ Reset();
+ }
+
+ ~LeakChecker() {
+ Check();
+ }
+
+ void Reset() {
+ previous_size_ = GetMappingSize();
+ }
+
+ void DumpTo(std::ostream& os) const {
+ os << previous_size_;
+ }
+
+ private:
+ size_t previous_size_;
+
+ void Check() {
+ auto current_size = GetMappingSize();
+ if (current_size > previous_size_) {
+ FAIL() << "increase in process map size: " << previous_size_ << " -> " << current_size;
+ }
+ }
+};
+
+std::ostream& operator<<(std::ostream& os, const LeakChecker& lc) {
+ lc.DumpTo(os);
+ return os;
}
// http://b/36045112
-LEAK_TEST(pthread_leak, join) {
+TEST(pthread_leak, join) {
+ LeakChecker lc;
for (int i = 0; i < 100; ++i) {
pthread_t thread;
ASSERT_EQ(0, pthread_create(&thread, nullptr, [](void*) -> void* { return nullptr; }, nullptr));
@@ -79,22 +112,42 @@
}
// http://b/36045112
-LEAK_TEST(pthread_leak, detach) {
- pthread_barrier_t barrier;
- constexpr int thread_count = 100;
- ASSERT_EQ(0, pthread_barrier_init(&barrier, nullptr, thread_count + 1));
- for (int i = 0; i < thread_count; ++i) {
- pthread_t thread;
- const auto thread_function = +[](void* barrier) -> void* {
- pthread_barrier_wait(static_cast<pthread_barrier_t*>(barrier));
- return nullptr;
- };
- ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, &barrier));
- ASSERT_EQ(0, pthread_detach(thread));
+TEST(pthread_leak, detach) {
+ LeakChecker lc;
+
+ for (size_t pass = 0; pass < 2; ++pass) {
+ pthread_barrier_t barrier;
+ constexpr int thread_count = 100;
+ ASSERT_EQ(pthread_barrier_init(&barrier, nullptr, thread_count + 1), 0);
+
+ // Start child threads.
+ struct thread_data { pthread_barrier_t* barrier; pid_t* tid; };
+ pid_t tids[thread_count];
+ for (int i = 0; i < thread_count; ++i) {
+ thread_data* td = new thread_data{&barrier, &tids[i]};
+ const auto thread_function = +[](void* ptr) -> void* {
+ thread_data* data = static_cast<thread_data*>(ptr);
+ *data->tid = gettid();
+ pthread_barrier_wait(data->barrier);
+ // Doing this delete allocates new VMAs for jemalloc bookkeeping,
+ // but the two-pass nature of this test means we can check that
+ // it's a pool rather than an unbounded leak.
+ delete data;
+ return nullptr;
+ };
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr, thread_function, td));
+ ASSERT_EQ(0, pthread_detach(thread));
+ }
+
+ pthread_barrier_wait(&barrier);
+ ASSERT_EQ(pthread_barrier_destroy(&barrier), 0);
+
+ WaitUntilAllExited(tids, arraysize(tids));
+
+ // houdini keeps a thread pool, so we ignore the first pass while the
+ // pool fills, but then on the second pass require that the "pool" isn't
+ // actually an unbounded leak. https://issuetracker.google.com/37920774.
+ if (pass == 0) lc.Reset();
}
-
- pthread_barrier_wait(&barrier);
-
- // Give the threads some time to exit.
- std::this_thread::sleep_for(100ms);
}
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 37499ba..0220ae2 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -22,55 +22,47 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE := libdlext_test_zip_zipaligned
LOCAL_MODULE_SUFFIX := .zip
-LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_zip
LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
include $(BUILD_SYSTEM)/base_rules.mk
my_shared_libs := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libdlext_test_zip.so \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libatest_simple_zip.so
+ $(call intermediates-dir-for,SHARED_LIBRARIES,libdlext_test_zip,,,$(bionic_2nd_arch_prefix))/libdlext_test_zip.so \
+ $(call intermediates-dir-for,SHARED_LIBRARIES,libatest_simple_zip,,,$(bionic_2nd_arch_prefix))/libatest_simple_zip.so
-$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(BIONIC_TESTS_ZIPALIGN)
+$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBS := $(my_shared_libs)
+$(LOCAL_BUILT_MODULE): $(my_shared_libs) $(BIONIC_TESTS_ZIPALIGN)
@echo "Aligning zip: $@"
$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir
- $(hide) cp $^ $(dir $@)/libdir
+ $(hide) cp $(PRIVATE_SHARED_LIBS) $(dir $@)/libdir
$(hide) (cd $(dir $@) && touch empty_file.txt && zip -qrD0 $(notdir $@).unaligned empty_file.txt libdir/*.so)
$(hide) $(BIONIC_TESTS_ZIPALIGN) 4096 $@.unaligned $@
include $(CLEAR_VARS)
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE := libdlext_test_runpath_zip_zipaligned
LOCAL_MODULE_SUFFIX := .zip
-LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_runpath_zip
LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
include $(BUILD_SYSTEM)/base_rules.mk
-my_shared_libs := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
+lib_d := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_d_zip,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_d_zip.so
+lib_a := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_a,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_a.so
+lib_b := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_b,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_b.so
+lib_c := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_c,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_c.so
+lib_x := $(call intermediates-dir-for,SHARED_LIBRARIES,libtest_dt_runpath_x,,,$(bionic_2nd_arch_prefix))/libtest_dt_runpath_x.so
-
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_d_zip.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_a.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_b.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_c.so
-$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := \
- $($(bionic_2nd_arch_prefix)TARGET_OUT_INTERMEDIATE_LIBRARIES)/libtest_dt_runpath_x.so
-$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(BIONIC_TESTS_ZIPALIGN)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_D := $(lib_d)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_A := $(lib_a)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_B := $(lib_b)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_C := $(lib_c)
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIB_X := $(lib_x)
+$(LOCAL_BUILT_MODULE) : $(lib_d) $(lib_a) $(lib_b) $(lib_c) $(lib_x) $(BIONIC_TESTS_ZIPALIGN)
@echo "Aligning zip: $@"
$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir && \
mkdir -p $(dir $@)/libdir/dt_runpath_a && mkdir -p $(dir $@)/libdir/dt_runpath_b_c_x
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 8fba1c4..a7b9d52 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -500,3 +500,10 @@
delete[] values_64;
delete[] values_ldouble;
}
+
+TEST(malloc, mallopt_smoke) {
+ errno = 0;
+ ASSERT_EQ(0, mallopt(-1000, 1));
+ // mallopt doesn't set errno.
+ ASSERT_EQ(0, errno);
+}