1 /* 2 * Copyright (C) 2018 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 "thread_tree.h" 18 19 #include <gtest/gtest.h> 20 21 using namespace simpleperf; 22 23 class ThreadTreeTest : public ::testing::Test { 24 protected: 25 void AddMap(uint64_t start, uint64_t end, const std::string& name) { 26 thread_tree_.AddThreadMap(0, 0, start, end - start, start, name); 27 if (expected_names_.size() < end) { 28 expected_names_.resize(end); 29 } 30 for (uint64_t i = start; i < end; ++i) { 31 expected_names_[i] = name; 32 } 33 } 34 35 void CheckMaps() { 36 std::vector<std::string> names; 37 ThreadEntry* thread = thread_tree_.FindThreadOrNew(0, 0); 38 ASSERT_TRUE(thread != nullptr); 39 ASSERT_TRUE(thread->maps != nullptr); 40 uint64_t prev_end = 0; 41 for (auto& pair : thread->maps->maps) { 42 ASSERT_GE(pair.first, prev_end); 43 prev_end = pair.second->get_end_addr(); 44 ASSERT_EQ(pair.second->start_addr, pair.first); 45 ASSERT_GT(pair.second->len, 0u); 46 ASSERT_EQ(pair.second->pgoff, pair.first); 47 if (names.size() < pair.second->get_end_addr()) { 48 names.resize(pair.second->get_end_addr()); 49 } 50 for (uint64_t i = pair.first; i < pair.second->get_end_addr(); ++i) { 51 names[i] = pair.second->dso->Path(); 52 } 53 } 54 ASSERT_EQ(names, expected_names_); 55 // Check result of ThreadTree::FindMap. 56 for (size_t i = 0; i < expected_names_.size(); ++i) { 57 const MapEntry* entry = thread_tree_.FindMap(thread, i, false); 58 ASSERT_TRUE(entry != nullptr); 59 if (expected_names_[i].empty()) { 60 ASSERT_TRUE(thread_tree_.IsUnknownDso(entry->dso)); 61 } else { 62 ASSERT_EQ(entry->dso->Path(), expected_names_[i]); 63 } 64 } 65 } 66 67 std::vector<std::string> expected_names_; 68 ThreadTree thread_tree_; 69 }; 70 71 TEST_F(ThreadTreeTest, maps_smoke) { 72 AddMap(0, 5, "0"); 73 AddMap(10, 15, "1"); 74 CheckMaps(); 75 76 // Overlap left. 77 AddMap(1, 6, "2"); 78 CheckMaps(); 79 AddMap(4, 5, "3"); 80 CheckMaps(); 81 82 // Overlap right. 83 AddMap(9, 12, "4"); 84 CheckMaps(); 85 AddMap(8, 15, "5"); 86 CheckMaps(); 87 AddMap(7, 16, "6"); 88 CheckMaps(); 89 90 // Overlap all. 91 AddMap(0, 17, "7"); 92 CheckMaps(); 93 } 94 95 TEST_F(ThreadTreeTest, jit_maps_before_fork) { 96 // Maps for JIT symfiles can arrive before fork records. 97 thread_tree_.AddThreadMap(0, 0, 0, 1, 0, "0", map_flags::PROT_JIT_SYMFILE_MAP); 98 thread_tree_.AddThreadMap(1, 1, 1, 1, 1, "1"); 99 thread_tree_.ForkThread(0, 0, 1, 1); 100 expected_names_ = {"0", "1"}; 101 CheckMaps(); 102 ThreadEntry* thread = thread_tree_.FindThreadOrNew(0, 0); 103 ASSERT_TRUE(thread != nullptr); 104 const MapEntry* map = thread_tree_.FindMap(thread, 0); 105 ASSERT_TRUE(map != nullptr); 106 ASSERT_EQ(map->flags, map_flags::PROT_JIT_SYMFILE_MAP); 107 } 108 109 TEST_F(ThreadTreeTest, reused_tid) { 110 // Process 1 has thread 1 and 2. 111 thread_tree_.ForkThread(1, 2, 1, 1); 112 // Thread 2 exits. 113 thread_tree_.ExitThread(1, 2); 114 // Thread 1 forks process 2. 115 thread_tree_.ForkThread(2, 2, 1, 1); 116 } 117 118 TEST_F(ThreadTreeTest, reused_tid_without_thread_exit) { 119 // Similar to the above test, but the thread exit record is missing. 120 thread_tree_.ForkThread(1, 2, 1, 1); 121 thread_tree_.ForkThread(2, 2, 1, 1); 122 } 123