From e7ba485d8ab62c0d2d729a27cbef94bcab27ccff Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Tue, 14 Mar 2017 18:10:48 -0700 Subject: [PATCH] Introduce minikin_stress_tests to find race condition. This is designed for catching race condition. The stress_tests is splited from unit test binary since this takes 30 seconds on angler. Bug: 36223724 Bug: 36208043 Test: ran minikin_stress_tests Change-Id: I1bf4ba43e6e97cd04e7d6dd42d388dd17ce64c7b --- tests/stresstest/Android.mk | 56 ++++++++++++++ tests/stresstest/MultithreadTest.cpp | 109 +++++++++++++++++++++++++++ tests/stresstest/how_to_run.txt | 3 + 3 files changed, 168 insertions(+) create mode 100644 tests/stresstest/Android.mk create mode 100644 tests/stresstest/MultithreadTest.cpp create mode 100644 tests/stresstest/how_to_run.txt diff --git a/tests/stresstest/Android.mk b/tests/stresstest/Android.mk new file mode 100644 index 00000000000..961978b5ccf --- /dev/null +++ b/tests/stresstest/Android.mk @@ -0,0 +1,56 @@ +# Copyright (C) 2017 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. + +# see how_to_run.txt for instructions on running these tests + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_TEST_DATA := $(foreach f,$(LOCAL_TEST_DATA),frameworks/minikin/tests:$(f)) + +LOCAL_MODULE := minikin_stress_tests +LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_CLASS := NATIVE_TESTS + +LOCAL_STATIC_LIBRARIES := libminikin + +# Shared libraries which are dependencies of minikin; these are not automatically +# pulled in by the build system (and thus sadly must be repeated). + +LOCAL_SHARED_LIBRARIES := \ + libskia \ + libft2 \ + libharfbuzz_ng \ + libicuuc \ + liblog \ + libutils \ + libz + +LOCAL_STATIC_LIBRARIES += \ + libxml2 + +LOCAL_SRC_FILES += \ + ../util/FontTestUtils.cpp \ + ../util/MinikinFontForTest.cpp \ + MultithreadTest.cpp \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/../../libs/minikin/ \ + $(LOCAL_PATH)/../util \ + external/libxml2/include \ + +LOCAL_CPPFLAGS += -Werror -Wall -Wextra + +include $(BUILD_NATIVE_TEST) diff --git a/tests/stresstest/MultithreadTest.cpp b/tests/stresstest/MultithreadTest.cpp new file mode 100644 index 00000000000..08c94b99fe5 --- /dev/null +++ b/tests/stresstest/MultithreadTest.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 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 + +#include +#include +#include +#include + +#include + +#include "MinikinInternal.h" +#include "minikin/FontCollection.h" +#include "minikin/Layout.h" +#include "../util/FontTestUtils.h" + +namespace minikin { + +const char* SYSTEM_FONT_PATH = "/system/fonts/"; +const char* SYSTEM_FONT_XML = "/system/etc/fonts.xml"; + +constexpr int LAYOUT_COUNT_PER_COLLECTION = 500; +constexpr int COLLECTION_COUNT_PER_THREAD = 15; +constexpr int NUM_THREADS = 10; + +std::mutex gMutex; +std::condition_variable gCv; +bool gReady = false; + +static std::vector generateTestText( + std::mt19937* mt, int lettersInWord, int wordsInText) { + std::uniform_int_distribution dist('A', 'Z'); + + std::vector text; + text.reserve((lettersInWord + 1) * wordsInText - 1); + for (int i = 0; i < wordsInText; ++i) { + if (i != 0) { + text.emplace_back(' '); + } + for (int j = 0; j < lettersInWord; ++j) { + text.emplace_back(dist(*mt)); + } + } + return text; +} + +static void thread_main(int tid) { + { + // Wait until all threads are created. + std::unique_lock lock(gMutex); + gCv.wait(lock, [] { return gReady; }); + } + + std::mt19937 mt(tid); + MinikinPaint paint; + + for (int i = 0; i < COLLECTION_COUNT_PER_THREAD; ++i) { + std::shared_ptr collection( + getFontCollection(SYSTEM_FONT_PATH, SYSTEM_FONT_XML)); + + for (int j = 0; j < LAYOUT_COUNT_PER_COLLECTION; ++j) { + // Generates 10 of 3-letter words so that the word sometimes hit the cache. + Layout layout; + std::vector text = generateTestText(&mt, 3, 10); + layout.doLayout(text.data(), 0, text.size(), text.size(), kBidi_LTR, FontStyle(), + paint, collection); + std::vector advances(text.size()); + layout.getAdvances(advances.data()); + for (size_t k = 0; k < advances.size(); ++k) { + // MinikinFontForTest always returns 10.0f for horizontal advance. + LOG_ALWAYS_FATAL_IF(advances[k] != 10.0f, "Memory corruption detected."); + } + } + } +} + +TEST(MultithreadTest, ThreadSafeStressTest) { + std::vector threads; + + { + std::unique_lock lock(gMutex); + threads.reserve(NUM_THREADS); + for (int i = 0; i < NUM_THREADS; ++i) { + threads.emplace_back(&thread_main, i); + } + gReady = true; + } + gCv.notify_all(); + + for (auto& thread : threads) { + thread.join(); + } +} + +} // namespace minikin diff --git a/tests/stresstest/how_to_run.txt b/tests/stresstest/how_to_run.txt new file mode 100644 index 00000000000..ba4dbdf1f73 --- /dev/null +++ b/tests/stresstest/how_to_run.txt @@ -0,0 +1,3 @@ +mmm -j8 frameworks/minikin/tests/stresstest && +adb sync data && +adb shell /data/nativetest/minikin_tests/minikin_stress_tests