summaryrefslogtreecommitdiff
path: root/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2015-11-16 17:30:32 -0800
committerChristopher Ferris <cferris@google.com>2016-01-25 10:54:21 -0800
commit63860cb8fd1adf3f679b9b4ad876323a8d65cd9d (patch)
tree25aeae686d92efcb5e08a54e8cba1e8c31efe33b /libc/malloc_debug/tests/malloc_debug_config_tests.cpp
parentad9c3f34f762ed45cce5dbb93218124ed31f6873 (diff)
Malloc debug rewrite.
The major components of the rewrite: - Completely remove the qemu shared library code. Nobody was using it and it appears to have broken at some point. - Adds the ability to enable/disable different options independently. - Adds a new option that can enable the backtrace on alloc/free when a process gets a specific signal. - Adds a new way to enable malloc debug. If a special property is set, and the process has an environment variable set, then debug malloc will be enabled. This allows something that might be a derivative of app_process to be started with an environment variable being enabled. - get_malloc_leak_info() used to return one element for each pointer that had the exact same backtrace. The new version returns information for every one of the pointers with same backtrace. It turns out ddms already automatically coalesces these, so the old method simply hid the fact that there where multiple pointers with the same amount of backtrace. - Moved all of the malloc debug specific code into the library. Nothing related to the malloc debug data structures remains in libc. - Removed the calls to the debug malloc cleanup routine. Instead, I added an atexit call with the debug malloc cleanup routine. This gets around most problems related to the timing of doing the cleanup. The new properties and environment variables: libc.debug.malloc.options Set by option name (such as "backtrace"). Setting this to a bad value will cause a usage statement to be printed to the log. libc.debug.malloc.program Same as before. If this is set, then only the program named will be launched with malloc debug enabled. This is not a complete match, but if any part of the property is in the program name, malloc debug is enabled. libc.debug.malloc.env_enabled If set, then malloc debug is only enabled if the running process has the environment variable LIBC_DEBUG_MALLOC_ENABLE set. Bug: 19145921 Change-Id: I7b0e58cc85cc6d4118173fe1f8627a391b64c0d7
Diffstat (limited to 'libc/malloc_debug/tests/malloc_debug_config_tests.cpp')
-rw-r--r--libc/malloc_debug/tests/malloc_debug_config_tests.cpp380
1 files changed, 380 insertions, 0 deletions
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
new file mode 100644
index 000000000..ad25948b1
--- /dev/null
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "Config.h"
+
+#include "log_fake.h"
+
+extern "C" int property_set(const char*, const char*);
+
+class MallocDebugConfigTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ resetLogs();
+ }
+
+ void TearDown() override {
+ }
+
+ std::unique_ptr<Config> config;
+
+ bool InitConfig(const char* property_value) {
+ config.reset(new Config);
+ property_set("libc.debug.malloc.options", property_value);
+ return config->SetFromProperties();
+ }
+};
+
+std::string usage_string(
+ "6 malloc_debug malloc debug options usage:\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug front_guard[=XX]\n"
+ "6 malloc_debug Enables a front guard on all allocations. If XX is set\n"
+ "6 malloc_debug it sets the number of bytes in the guard. The default is\n"
+ "6 malloc_debug 32 bytes.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug rear_guard[=XX]\n"
+ "6 malloc_debug Enables a rear guard on all allocations. If XX is set\n"
+ "6 malloc_debug it sets the number of bytes in the guard. The default is\n"
+ "6 malloc_debug 32 bytes.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug guard[=XX]\n"
+ "6 malloc_debug Enables both a front guard and a rear guard on all allocations.\n"
+ "6 malloc_debug If XX is set it sets the number of bytes in both guards.\n"
+ "6 malloc_debug The default is 32 bytes.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug backtrace[=XX]\n"
+ "6 malloc_debug Enable capturing the backtrace at the point of allocation.\n"
+ "6 malloc_debug If XX is set it sets the number of backtrace frames.\n"
+ "6 malloc_debug The default is 16 frames.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug backtrace_enable_on_signal[=XX]\n"
+ "6 malloc_debug Enable capturing the backtrace at the point of allocation.\n"
+ "6 malloc_debug The backtrace capture is not enabled until the process\n"
+ "6 malloc_debug receives a signal. If XX is set it sets the number of backtrace\n"
+ "6 malloc_debug frames. The default is 16 frames.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug fill_on_alloc[=XX]\n"
+ "6 malloc_debug On first allocation, fill with the value 0xeb.\n"
+ "6 malloc_debug If XX is set it will only fill up to XX bytes of the\n"
+ "6 malloc_debug allocation. The default is to fill the entire allocation.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug fill_on_free[=XX]\n"
+ "6 malloc_debug On free, fill with the value 0xef. If XX is set it will\n"
+ "6 malloc_debug only fill up to XX bytes of the allocation. The default is to\n"
+ "6 malloc_debug fill the entire allocation.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug fill[=XX]\n"
+ "6 malloc_debug On both first allocation free, fill with the value 0xeb on\n"
+ "6 malloc_debug first allocation and the value 0xef. If XX is set, only fill\n"
+ "6 malloc_debug up to XX bytes. The default is to fill the entire allocation.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug expand_alloc[=XX]\n"
+ "6 malloc_debug Allocate an extra number of bytes for every allocation call.\n"
+ "6 malloc_debug If XX is set, that is the number of bytes to expand the\n"
+ "6 malloc_debug allocation by. The default is 16 bytes.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug free_track[=XX]\n"
+ "6 malloc_debug When a pointer is freed, do not free the memory right away.\n"
+ "6 malloc_debug Instead, keep XX of these allocations around and then verify\n"
+ "6 malloc_debug that they have not been modified when the total number of freed\n"
+ "6 malloc_debug allocations exceeds the XX amount. When the program terminates,\n"
+ "6 malloc_debug the rest of these allocations are verified.\n"
+ "6 malloc_debug The default is to record 100 allocations.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug leak_track\n"
+ "6 malloc_debug Enable the leak tracking of memory allocations.\n"
+);
+
+TEST_F(MallocDebugConfigTest, unknown_option) {
+
+ ASSERT_FALSE(InitConfig("unknown_option"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg("6 malloc_debug malloc_testing: unknown option unknown_option\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, unparseable_number) {
+ ASSERT_FALSE(InitConfig("backtrace=XXX"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace'\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, illegal_value_zero) {
+ ASSERT_FALSE(InitConfig("backtrace=0"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace', value must be > 0: 0\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, no_space) {
+ ASSERT_FALSE(InitConfig("backtrace=10front_guard"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: bad value for option 'backtrace', "
+ "non space found after option: front_guard\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, illegal_value_negative) {
+ ASSERT_FALSE(InitConfig("backtrace=-1"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace', value must be > 0: -1\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, overflow) {
+ ASSERT_FALSE(InitConfig("backtrace=99999999999999999999"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: bad value for option 'backtrace': "
+ "Math result not representable\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, set_value_error) {
+ ASSERT_FALSE(InitConfig("leak_track=12"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'leak_track' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, space_before_equal) {
+ ASSERT_TRUE(InitConfig("backtrace =10"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(10U, config->backtrace_frames);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, space_after_equal) {
+ ASSERT_TRUE(InitConfig("backtrace= 10"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(10U, config->backtrace_frames);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, extra_space) {
+ ASSERT_TRUE(InitConfig(" backtrace=64 "));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(64U, config->backtrace_frames);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, multiple_options) {
+ ASSERT_TRUE(InitConfig(" backtrace=64 front_guard=24"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS | FRONT_GUARD, config->options);
+ ASSERT_EQ(64U, config->backtrace_frames);
+ ASSERT_EQ(24U, config->front_guard_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, front_guard) {
+ ASSERT_TRUE(InitConfig("front_guard=24"));
+ ASSERT_EQ(FRONT_GUARD, config->options);
+ ASSERT_EQ(24U, config->front_guard_bytes);
+
+ ASSERT_TRUE(InitConfig("front_guard"));
+ ASSERT_EQ(FRONT_GUARD, config->options);
+ ASSERT_EQ(32U, config->front_guard_bytes);
+
+ ASSERT_TRUE(InitConfig("front_guard=39"));
+ ASSERT_EQ(FRONT_GUARD, config->options);
+ ASSERT_EQ(40U, config->front_guard_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, rear_guard) {
+ ASSERT_TRUE(InitConfig("rear_guard=50"));
+ ASSERT_EQ(REAR_GUARD, config->options);
+ ASSERT_EQ(50U, config->rear_guard_bytes);
+
+ ASSERT_TRUE(InitConfig("rear_guard"));
+ ASSERT_EQ(REAR_GUARD, config->options);
+ ASSERT_EQ(32U, config->rear_guard_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, guard) {
+ ASSERT_TRUE(InitConfig("guard=32"));
+ ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
+ ASSERT_EQ(32U, config->front_guard_bytes);
+ ASSERT_EQ(32U, config->rear_guard_bytes);
+
+ ASSERT_TRUE(InitConfig("guard"));
+ ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
+ ASSERT_EQ(32U, config->front_guard_bytes);
+ ASSERT_EQ(32U, config->rear_guard_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace) {
+ ASSERT_TRUE(InitConfig("backtrace=64"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(64U, config->backtrace_frames);
+
+ ASSERT_TRUE(InitConfig("backtrace"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(16U, config->backtrace_frames);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_enable_on_signal) {
+ ASSERT_TRUE(InitConfig("backtrace_enable_on_signal=64"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(64U, config->backtrace_frames);
+
+ ASSERT_TRUE(InitConfig("backtrace_enable_on_signal"));
+ ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+ ASSERT_EQ(16U, config->backtrace_frames);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill_on_alloc) {
+ ASSERT_TRUE(InitConfig("fill_on_alloc=64"));
+ ASSERT_EQ(FILL_ON_ALLOC, config->options);
+ ASSERT_EQ(64U, config->fill_on_alloc_bytes);
+
+ ASSERT_TRUE(InitConfig("fill_on_alloc"));
+ ASSERT_EQ(FILL_ON_ALLOC, config->options);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill_on_free) {
+ ASSERT_TRUE(InitConfig("fill_on_free=64"));
+ ASSERT_EQ(FILL_ON_FREE, config->options);
+ ASSERT_EQ(64U, config->fill_on_free_bytes);
+
+ ASSERT_TRUE(InitConfig("fill_on_free"));
+ ASSERT_EQ(FILL_ON_FREE, config->options);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill) {
+ ASSERT_TRUE(InitConfig("fill=64"));
+ ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
+ ASSERT_EQ(64U, config->fill_on_alloc_bytes);
+ ASSERT_EQ(64U, config->fill_on_free_bytes);
+
+ ASSERT_TRUE(InitConfig("fill"));
+ ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, expand_alloc) {
+ ASSERT_TRUE(InitConfig("expand_alloc=1234"));
+ ASSERT_EQ(EXPAND_ALLOC, config->options);
+ ASSERT_EQ(1234U, config->expand_alloc_bytes);
+
+ ASSERT_TRUE(InitConfig("expand_alloc"));
+ ASSERT_EQ(EXPAND_ALLOC, config->options);
+ ASSERT_EQ(16U, config->expand_alloc_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, free_track) {
+ ASSERT_TRUE(InitConfig("free_track=1234"));
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+ ASSERT_EQ(1234U, config->free_track_allocations);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+ ASSERT_TRUE(InitConfig("free_track"));
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+ ASSERT_EQ(100U, config->free_track_allocations);
+ ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, free_track_and_fill_on_free) {
+ ASSERT_TRUE(InitConfig("free_track=1234 fill_on_free=32"));
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+ ASSERT_EQ(1234U, config->free_track_allocations);
+ ASSERT_EQ(32U, config->fill_on_free_bytes);
+
+ ASSERT_TRUE(InitConfig("free_track fill_on_free=60"));
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+ ASSERT_EQ(100U, config->free_track_allocations);
+ ASSERT_EQ(60U, config->fill_on_free_bytes);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, leak_track) {
+ ASSERT_TRUE(InitConfig("leak_track"));
+ ASSERT_EQ(LEAK_TRACK | TRACK_ALLOCS, config->options);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, leak_track_fail) {
+ ASSERT_FALSE(InitConfig("leak_track=100"));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'leak_track' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}