diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 70995acbb4ca4..9a9a89bba53f9 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -227,15 +227,16 @@ include $(CLEAR_VARS) LOCAL_MODULE := hwui_unit_tests LOCAL_MODULE_TAGS := tests LOCAL_STATIC_LIBRARIES := libhwui_static_null_gpu +LOCAL_SHARED_LIBRARIES := libmemunreachable LOCAL_CFLAGS := \ $(hwui_cflags) \ -DHWUI_NULL_GPU LOCAL_SRC_FILES += \ $(hwui_test_common_src_files) \ + tests/unit/main.cpp \ tests/unit/CanvasStateTests.cpp \ tests/unit/ClipAreaTests.cpp \ - tests/unit/CrashHandlerInjector.cpp \ tests/unit/DamageAccumulatorTests.cpp \ tests/unit/DeviceInfoTests.cpp \ tests/unit/FatVectorTests.cpp \ diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 9595a85e99dd4..c809ff4f85e8d 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -19,9 +19,6 @@ #include "DeferredLayerUpdater.h" #include "LayerRenderer.h" -#include -#include - namespace android { namespace uirenderer { @@ -136,27 +133,7 @@ void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, canvas->drawTextOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint); } -static void defaultCrashHandler() { - fprintf(stderr, "RenderThread crashed!"); -} - -static std::function gCrashHandler = defaultCrashHandler; -static sighandler_t gPreviousSignalHandler; - -static void signalHandler(int sig) { - gCrashHandler(); - if (gPreviousSignalHandler) { - gPreviousSignalHandler(sig); - } -} - -void TestUtils::setRenderThreadCrashHandler(std::function crashHandler) { - gCrashHandler = crashHandler; -} - void TestUtils::TestTask::run() { - gPreviousSignalHandler = signal(SIGABRT, signalHandler); - // RenderState only valid once RenderThread is running, so queried here RenderState& renderState = renderthread::RenderThread::getInstance().renderState(); @@ -164,9 +141,6 @@ void TestUtils::TestTask::run() { rtCallback(renderthread::RenderThread::getInstance()); renderState.flush(Caches::FlushMode::Full); renderState.onGLContextDestroyed(); - - // Restore the previous signal handler - signal(SIGABRT, gPreviousSignalHandler); } } /* namespace uirenderer */ diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 6f237059dd334..28ac1166fc5c2 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -180,8 +180,6 @@ public: typedef std::function RtCallback; - static void setRenderThreadCrashHandler(std::function crashHandler); - class TestTask : public renderthread::RenderTask { public: TestTask(RtCallback rtCallback) diff --git a/libs/hwui/tests/unit/CrashHandlerInjector.cpp b/libs/hwui/tests/unit/CrashHandlerInjector.cpp deleted file mode 100644 index b1c678d1c6faf..0000000000000 --- a/libs/hwui/tests/unit/CrashHandlerInjector.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2016 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 "tests/common/TestUtils.h" - -#include -#include - -using namespace android::uirenderer; - -static void gunitCrashHandler() { - auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info(); - printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(), - testinfo->name()); - printf("[ FATAL! ] RenderThread crashed, aborting tests!\n"); - fflush(stdout); -} - -static void hookError() { - TestUtils::setRenderThreadCrashHandler(gunitCrashHandler); -} - -class HookErrorInit { -public: - HookErrorInit() { hookError(); } -}; - -static HookErrorInit sInit; diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp new file mode 100644 index 0000000000000..2e55236c91f12 --- /dev/null +++ b/libs/hwui/tests/unit/main.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 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 "gtest/gtest.h" + +#include "Caches.h" +#include "thread/TaskManager.h" +#include "tests/common/TestUtils.h" + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace android; +using namespace android::uirenderer; + +static auto CRASH_SIGNALS = { + SIGABRT, + SIGSEGV, + SIGBUS, +}; + +static map gSigChain; + +static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { + auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info(); + printf("[ FAILED ] %s.%s\n", testinfo->test_case_name(), + testinfo->name()); + printf("[ FATAL! ] Process crashed, aborting tests!\n"); + fflush(stdout); + + // restore the default sighandler and re-raise + struct sigaction sa = gSigChain[sig]; + sigaction(sig, &sa, nullptr); + raise(sig); +} + +static void logUnreachable(initializer_list infolist) { + // merge them all + UnreachableMemoryInfo merged; + unordered_set addrs; + merged.allocation_bytes = 0; + merged.leak_bytes = 0; + merged.num_allocations = 0; + merged.num_leaks = 0; + for (auto& info : infolist) { + // We'll be a little hazzy about these ones and just hope the biggest + // is the most accurate + merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes); + merged.num_allocations = max(merged.num_allocations, info.num_allocations); + for (auto& leak : info.leaks) { + if (addrs.find(leak.begin) == addrs.end()) { + merged.leaks.push_back(leak); + merged.num_leaks++; + merged.leak_bytes += leak.size; + addrs.insert(leak.begin); + } + } + } + + // Now log the result + if (merged.num_leaks) { + cout << endl << "Leaked memory!" << endl; + if (!merged.leaks[0].num_backtrace_frames) { + cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'" + << endl << "and 'setprop libc.debug.malloc.options backtrace=8'" + << " to get backtraces" << endl; + } + cout << merged.ToString(false); + } +} + +static void checkForLeaks() { + // TODO: Until we can shutdown the RT thread we need to do this in + // two passes as GetUnreachableMemory has limited insight into + // thread-local caches so some leaks will not be properly tagged as leaks + nsecs_t before = systemTime(); + UnreachableMemoryInfo rtMemInfo; + TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { + if (Caches::hasInstance()) { + Caches::getInstance().tasks.stop(); + } + // Check for leaks + if (!GetUnreachableMemory(rtMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + }); + UnreachableMemoryInfo uiMemInfo; + if (!GetUnreachableMemory(uiMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + logUnreachable({rtMemInfo, uiMemInfo}); + nsecs_t after = systemTime(); + cout << "Leak check took " << ns2ms(after - before) << "ms" << endl; +} + +int main(int argc, char* argv[]) { + // Register a crash handler + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = >estSigHandler; + sa.sa_flags = SA_SIGINFO; + for (auto sig : CRASH_SIGNALS) { + struct sigaction old_sa; + sigaction(sig, &sa, &old_sa); + gSigChain.insert(pair(sig, old_sa)); + } + + // Run the tests + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + checkForLeaks(); + return ret; +} +