summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2014-05-07 15:25:44 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-05-07 15:25:44 +0000
commit35cff760dfc5d9d1432955503ffff02286ac20ef (patch)
tree6632356200fc4bb0188c43acddd56ade53ab1596
parente292875cf55265bea7f78049e31d50c15a3a34e0 (diff)
parent26052616915fe2324755687c7db73d69c521b34d (diff)
downloadbionic-35cff760dfc5d9d1432955503ffff02286ac20ef.zip
bionic-35cff760dfc5d9d1432955503ffff02286ac20ef.tar.gz
bionic-35cff760dfc5d9d1432955503ffff02286ac20ef.tar.bz2
Merge "Test that relro sharing actually saves memory."
-rw-r--r--tests/Android.mk4
-rw-r--r--tests/dlext_test.cpp123
2 files changed, 127 insertions, 0 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index 9db372a..3aef5bb 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -252,8 +252,12 @@ bionic-unit-tests_ldflags := \
-Wl,--export-dynamic \
-Wl,-u,DlSymTestFunction \
+bionic-unit-tests_c_includes := \
+ $(call include-path-for, libpagemap) \
+
bionic-unit-tests_shared_libraries_target := \
libdl \
+ libpagemap \
module := bionic-unit-tests
module_tag := optional
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 4b2a5e2..b56fc41 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -24,8 +24,11 @@
#include <unistd.h>
#include <android/dlext.h>
#include <sys/mman.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <pagemap/pagemap.h>
+
#define ASSERT_DL_NOTNULL(ptr) \
ASSERT_TRUE(ptr != NULL) << "dlerror: " << dlerror()
@@ -209,6 +212,8 @@ protected:
EXPECT_EQ(4, f());
}
+ void SpawnChildrenAndMeasurePss(const char* lib, bool share_relro, size_t* pss_out);
+
android_dlextinfo extinfo_;
char relro_file_[PATH_MAX];
};
@@ -230,3 +235,121 @@ TEST_F(DlExtRelroSharingTest, RelroFileEmpty) {
ASSERT_NO_FATAL_FAILURE(TryUsingRelro(LIBNAME));
}
+
+TEST_F(DlExtRelroSharingTest, VerifyMemorySaving) {
+ ASSERT_NO_FATAL_FAILURE(CreateRelroFile(LIBNAME));
+ int relro_fd = open(relro_file_, O_RDONLY);
+ ASSERT_NOERROR(relro_fd);
+ extinfo_.flags |= ANDROID_DLEXT_USE_RELRO;
+ extinfo_.relro_fd = relro_fd;
+ int pipefd[2];
+ ASSERT_NOERROR(pipe(pipefd));
+
+ size_t without_sharing, with_sharing;
+ ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, false, &without_sharing));
+ ASSERT_NO_FATAL_FAILURE(SpawnChildrenAndMeasurePss(LIBNAME, true, &with_sharing));
+
+ // We expect the sharing to save at least 10% of the total PSS. In practice
+ // it saves 40%+ for this test.
+ size_t expected_size = without_sharing - (without_sharing/10);
+ EXPECT_LT(with_sharing, expected_size);
+}
+
+void getPss(pid_t pid, size_t* pss_out) {
+ pm_kernel_t* kernel;
+ ASSERT_EQ(0, pm_kernel_create(&kernel));
+
+ pm_process_t* process;
+ ASSERT_EQ(0, pm_process_create(kernel, pid, &process));
+
+ pm_map_t** maps;
+ size_t num_maps;
+ ASSERT_EQ(0, pm_process_maps(process, &maps, &num_maps));
+
+ size_t total_pss = 0;
+ for (size_t i = 0; i < num_maps; i++) {
+ pm_memusage_t usage;
+ ASSERT_EQ(0, pm_map_usage(maps[i], &usage));
+ total_pss += usage.pss;
+ }
+ *pss_out = total_pss;
+
+ free(maps);
+ pm_process_destroy(process);
+ pm_kernel_destroy(kernel);
+}
+
+void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool share_relro,
+ size_t* pss_out) {
+ const int CHILDREN = 20;
+
+ // Create children
+ pid_t childpid[CHILDREN];
+ int childpipe[CHILDREN];
+ for (int i=0; i<CHILDREN; ++i) {
+ char read_buf;
+ int child_done_pipe[2], parent_done_pipe[2];
+ ASSERT_NOERROR(pipe(child_done_pipe));
+ ASSERT_NOERROR(pipe(parent_done_pipe));
+
+ pid_t child = fork();
+ if (child == 0) {
+ // close the 'wrong' ends of the pipes in the child
+ close(child_done_pipe[0]);
+ close(parent_done_pipe[1]);
+
+ // open the library
+ void* handle;
+ if (share_relro) {
+ handle = android_dlopen_ext(lib, RTLD_NOW, &extinfo_);
+ } else {
+ handle = dlopen(lib, RTLD_NOW);
+ }
+ if (handle == NULL) {
+ fprintf(stderr, "in child: %s\n", dlerror());
+ exit(1);
+ }
+
+ // close write end of child_done_pipe to signal the parent that we're done.
+ close(child_done_pipe[1]);
+
+ // wait for the parent to close parent_done_pipe, then exit
+ read(parent_done_pipe[0], &read_buf, 1);
+ exit(0);
+ }
+
+ ASSERT_NOERROR(child);
+
+ // close the 'wrong' ends of the pipes in the parent
+ close(child_done_pipe[1]);
+ close(parent_done_pipe[0]);
+
+ // wait for the child to be done
+ read(child_done_pipe[0], &read_buf, 1);
+ close(child_done_pipe[0]);
+
+ // save the child's pid and the parent_done_pipe
+ childpid[i] = child;
+ childpipe[i] = parent_done_pipe[1];
+ }
+
+ // Sum the PSS of all the children
+ size_t total_pss = 0;
+ for (int i=0; i<CHILDREN; ++i) {
+ size_t child_pss;
+ ASSERT_NO_FATAL_FAILURE(getPss(childpid[i], &child_pss));
+ total_pss += child_pss;
+ }
+ *pss_out = total_pss;
+
+ // Close pipes and wait for children to exit
+ for (int i=0; i<CHILDREN; ++i) {
+ ASSERT_NOERROR(close(childpipe[i]));
+ }
+ for (int i=0; i<CHILDREN; ++i) {
+ int status;
+ ASSERT_EQ(childpid[i], waitpid(childpid[i], &status, 0));
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ }
+}