1 /* 2 * Copyright (C) 2015 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 "sample_tree.h" 20 21 struct ExpectedSampleInMap { 22 int pid; 23 int tid; 24 const char* comm; 25 std::string dso_name; 26 uint64_t map_start_addr; 27 size_t sample_count; 28 }; 29 30 static void SampleMatchExpectation(const SampleEntry& sample, const ExpectedSampleInMap& expected, 31 bool* has_error) { 32 *has_error = true; 33 ASSERT_TRUE(sample.thread != nullptr); 34 ASSERT_EQ(expected.pid, sample.thread->pid); 35 ASSERT_EQ(expected.tid, sample.thread->tid); 36 ASSERT_STREQ(expected.comm, sample.thread_comm); 37 ASSERT_TRUE(sample.map != nullptr); 38 ASSERT_EQ(expected.dso_name, sample.map->dso->Path()); 39 ASSERT_EQ(expected.map_start_addr, sample.map->start_addr); 40 ASSERT_EQ(expected.sample_count, sample.sample_count); 41 *has_error = false; 42 } 43 44 static void CheckSampleCallback(const SampleEntry& sample, 45 std::vector<ExpectedSampleInMap>& expected_samples, size_t* pos) { 46 ASSERT_LT(*pos, expected_samples.size()); 47 bool has_error; 48 SampleMatchExpectation(sample, expected_samples[*pos], &has_error); 49 ASSERT_FALSE(has_error) << "Error matching sample at pos " << *pos; 50 ++*pos; 51 } 52 53 static int CompareSampleFunction(const SampleEntry& sample1, const SampleEntry& sample2) { 54 if (sample1.thread->pid != sample2.thread->pid) { 55 return sample1.thread->pid - sample2.thread->pid; 56 } 57 if (sample1.thread->tid != sample2.thread->tid) { 58 return sample1.thread->tid - sample2.thread->tid; 59 } 60 if (strcmp(sample1.thread_comm, sample2.thread_comm) != 0) { 61 return strcmp(sample1.thread_comm, sample2.thread_comm); 62 } 63 if (sample1.map->dso->Path() != sample2.map->dso->Path()) { 64 return sample1.map->dso->Path() > sample2.map->dso->Path() ? 1 : -1; 65 } 66 if (sample1.map->start_addr != sample2.map->start_addr) { 67 return sample1.map->start_addr - sample2.map->start_addr; 68 } 69 return 0; 70 } 71 72 void VisitSampleTree(SampleTree* sample_tree, 73 const std::vector<ExpectedSampleInMap>& expected_samples) { 74 size_t pos = 0; 75 sample_tree->VisitAllSamples( 76 std::bind(&CheckSampleCallback, std::placeholders::_1, expected_samples, &pos)); 77 ASSERT_EQ(expected_samples.size(), pos); 78 } 79 80 class SampleTreeTest : public testing::Test { 81 protected: 82 virtual void SetUp() { 83 thread_tree.AddThread(1, 1, "p1t1"); 84 thread_tree.AddThread(1, 11, "p1t11"); 85 thread_tree.AddThread(2, 2, "p2t2"); 86 thread_tree.AddThreadMap(1, 1, 1, 5, 0, 0, "process1_thread1"); 87 thread_tree.AddThreadMap(1, 1, 6, 5, 0, 0, "process1_thread1_map2"); 88 thread_tree.AddThreadMap(1, 11, 1, 10, 0, 0, "process1_thread11"); 89 thread_tree.AddThreadMap(2, 2, 1, 20, 0, 0, "process2_thread2"); 90 thread_tree.AddKernelMap(10, 20, 0, 0, "kernel"); 91 sample_tree = std::unique_ptr<SampleTree>(new SampleTree(&thread_tree, CompareSampleFunction)); 92 } 93 94 void VisitSampleTree(const std::vector<ExpectedSampleInMap>& expected_samples) { 95 ::VisitSampleTree(sample_tree.get(), expected_samples); 96 } 97 98 ThreadTree thread_tree; 99 std::unique_ptr<SampleTree> sample_tree; 100 }; 101 102 TEST_F(SampleTreeTest, ip_in_map) { 103 sample_tree->AddSample(1, 1, 1, 0, 0, false); 104 sample_tree->AddSample(1, 1, 2, 0, 0, false); 105 sample_tree->AddSample(1, 1, 5, 0, 0, false); 106 std::vector<ExpectedSampleInMap> expected_samples = { 107 {1, 1, "p1t1", "process1_thread1", 1, 3}, 108 }; 109 VisitSampleTree(expected_samples); 110 } 111 112 TEST_F(SampleTreeTest, different_pid) { 113 sample_tree->AddSample(1, 1, 1, 0, 0, false); 114 sample_tree->AddSample(2, 2, 1, 0, 0, false); 115 std::vector<ExpectedSampleInMap> expected_samples = { 116 {1, 1, "p1t1", "process1_thread1", 1, 1}, {2, 2, "p2t2", "process2_thread2", 1, 1}, 117 }; 118 VisitSampleTree(expected_samples); 119 } 120 121 TEST_F(SampleTreeTest, different_tid) { 122 sample_tree->AddSample(1, 1, 1, 0, 0, false); 123 sample_tree->AddSample(1, 11, 1, 0, 0, false); 124 std::vector<ExpectedSampleInMap> expected_samples = { 125 {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 11, "p1t11", "process1_thread11", 1, 1}, 126 }; 127 VisitSampleTree(expected_samples); 128 } 129 130 TEST_F(SampleTreeTest, different_comm) { 131 sample_tree->AddSample(1, 1, 1, 0, 0, false); 132 thread_tree.AddThread(1, 1, "p1t1_comm2"); 133 sample_tree->AddSample(1, 1, 1, 0, 0, false); 134 std::vector<ExpectedSampleInMap> expected_samples = { 135 {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 1, "p1t1_comm2", "process1_thread1", 1, 1}, 136 }; 137 VisitSampleTree(expected_samples); 138 } 139 140 TEST_F(SampleTreeTest, different_map) { 141 sample_tree->AddSample(1, 1, 1, 0, 0, false); 142 sample_tree->AddSample(1, 1, 6, 0, 0, false); 143 std::vector<ExpectedSampleInMap> expected_samples = { 144 {1, 1, "p1t1", "process1_thread1", 1, 1}, {1, 1, "p1t1", "process1_thread1_map2", 6, 1}, 145 }; 146 VisitSampleTree(expected_samples); 147 } 148 149 TEST_F(SampleTreeTest, unmapped_sample) { 150 sample_tree->AddSample(1, 1, 0, 0, 0, false); 151 sample_tree->AddSample(1, 1, 31, 0, 0, false); 152 sample_tree->AddSample(1, 1, 70, 0, 0, false); 153 // Match the unknown map. 154 std::vector<ExpectedSampleInMap> expected_samples = { 155 {1, 1, "p1t1", "unknown", 0, 3}, 156 }; 157 VisitSampleTree(expected_samples); 158 } 159 160 TEST_F(SampleTreeTest, map_kernel) { 161 sample_tree->AddSample(1, 1, 10, 0, 0, true); 162 sample_tree->AddSample(1, 1, 10, 0, 0, false); 163 std::vector<ExpectedSampleInMap> expected_samples = { 164 {1, 1, "p1t1", "kernel", 10, 1}, {1, 1, "p1t1", "process1_thread1_map2", 6, 1}, 165 }; 166 VisitSampleTree(expected_samples); 167 } 168 169 TEST(sample_tree, overlapped_map) { 170 ThreadTree thread_tree; 171 SampleTree sample_tree(&thread_tree, CompareSampleFunction); 172 thread_tree.AddThread(1, 1, "thread1"); 173 thread_tree.AddThreadMap(1, 1, 1, 10, 0, 0, "map1"); // Add map 1. 174 sample_tree.AddSample(1, 1, 5, 0, 0, false); // Hit map 1. 175 thread_tree.AddThreadMap(1, 1, 5, 20, 0, 0, "map2"); // Add map 2. 176 sample_tree.AddSample(1, 1, 6, 0, 0, false); // Hit map 2. 177 sample_tree.AddSample(1, 1, 4, 0, 0, false); // Hit map 1. 178 thread_tree.AddThreadMap(1, 1, 2, 7, 0, 0, "map3"); // Add map 3. 179 sample_tree.AddSample(1, 1, 7, 0, 0, false); // Hit map 3. 180 sample_tree.AddSample(1, 1, 10, 0, 0, false); // Hit map 2. 181 182 std::vector<ExpectedSampleInMap> expected_samples = { 183 {1, 1, "thread1", "map1", 1, 2}, 184 {1, 1, "thread1", "map2", 5, 1}, 185 {1, 1, "thread1", "map2", 9, 1}, 186 {1, 1, "thread1", "map3", 2, 1}, 187 }; 188 VisitSampleTree(&sample_tree, expected_samples); 189 } 190