Home | History | Annotate | Download | only in unit
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "gtest/gtest.h"
     18 
     19 #include "Caches.h"
     20 #include "thread/TaskManager.h"
     21 #include "tests/common/TestUtils.h"
     22 
     23 #include <memunreachable/memunreachable.h>
     24 
     25 #include <cstdio>
     26 #include <iostream>
     27 #include <map>
     28 #include <unordered_set>
     29 #include <signal.h>
     30 #include <unistd.h>
     31 
     32 using namespace std;
     33 using namespace android;
     34 using namespace android::uirenderer;
     35 
     36 static auto CRASH_SIGNALS = {
     37         SIGABRT,
     38         SIGSEGV,
     39         SIGBUS,
     40 };
     41 
     42 static map<int, struct sigaction> gSigChain;
     43 
     44 static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) {
     45     auto testinfo = ::testing::UnitTest::GetInstance()->current_test_info();
     46     printf("[  FAILED  ] %s.%s\n", testinfo->test_case_name(),
     47             testinfo->name());
     48     printf("[  FATAL!  ] Process crashed, aborting tests!\n");
     49     fflush(stdout);
     50 
     51     // restore the default sighandler and re-raise
     52     struct sigaction sa = gSigChain[sig];
     53     sigaction(sig, &sa, nullptr);
     54     raise(sig);
     55 }
     56 
     57 static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) {
     58     // merge them all
     59     UnreachableMemoryInfo merged;
     60     unordered_set<uintptr_t> addrs;
     61     merged.allocation_bytes = 0;
     62     merged.leak_bytes = 0;
     63     merged.num_allocations = 0;
     64     merged.num_leaks = 0;
     65     for (auto& info : infolist) {
     66         // We'll be a little hazzy about these ones and just hope the biggest
     67         // is the most accurate
     68         merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes);
     69         merged.num_allocations = max(merged.num_allocations, info.num_allocations);
     70         for (auto& leak : info.leaks) {
     71              if (addrs.find(leak.begin) == addrs.end()) {
     72                  merged.leaks.push_back(leak);
     73                  merged.num_leaks++;
     74                  merged.leak_bytes += leak.size;
     75                  addrs.insert(leak.begin);
     76              }
     77         }
     78     }
     79 
     80     // Now log the result
     81     if (merged.num_leaks) {
     82         cout << endl << "Leaked memory!" << endl;
     83         if (!merged.leaks[0].backtrace.num_frames) {
     84             cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'"
     85                     << endl << "and 'setprop libc.debug.malloc.options backtrace=8'"
     86                     << " to get backtraces" << endl;
     87         }
     88         cout << merged.ToString(false);
     89     }
     90 }
     91 
     92 static void checkForLeaks() {
     93     // TODO: Until we can shutdown the RT thread we need to do this in
     94     // two passes as GetUnreachableMemory has limited insight into
     95     // thread-local caches so some leaks will not be properly tagged as leaks
     96     nsecs_t before = systemTime();
     97     UnreachableMemoryInfo rtMemInfo;
     98     TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
     99         if (Caches::hasInstance()) {
    100             Caches::getInstance().tasks.stop();
    101         }
    102         // Check for leaks
    103         if (!GetUnreachableMemory(rtMemInfo)) {
    104             cerr << "Failed to get unreachable memory!" << endl;
    105             return;
    106         }
    107     });
    108     UnreachableMemoryInfo uiMemInfo;
    109     if (!GetUnreachableMemory(uiMemInfo)) {
    110         cerr << "Failed to get unreachable memory!" << endl;
    111         return;
    112     }
    113     logUnreachable({rtMemInfo, uiMemInfo});
    114     nsecs_t after = systemTime();
    115     cout << "Leak check took " << ns2ms(after - before) << "ms" << endl;
    116 }
    117 
    118 int main(int argc, char* argv[]) {
    119     // Register a crash handler
    120     struct sigaction sa;
    121     memset(&sa, 0, sizeof(sa));
    122     sa.sa_sigaction = &gtestSigHandler;
    123     sa.sa_flags = SA_SIGINFO;
    124     for (auto sig : CRASH_SIGNALS) {
    125         struct sigaction old_sa;
    126         sigaction(sig, &sa, &old_sa);
    127         gSigChain.insert(pair<int, struct sigaction>(sig, old_sa));
    128     }
    129 
    130     // Run the tests
    131     testing::InitGoogleTest(&argc, argv);
    132     int ret = RUN_ALL_TESTS();
    133     checkForLeaks();
    134     return ret;
    135 }
    136 
    137