• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <set>
20 #include <unordered_map>
21 
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 #include <android-base/test_utils.h>
25 
26 #include "command.h"
27 #include "event_selection_set.h"
28 #include "get_test_data.h"
29 #include "perf_regs.h"
30 #include "read_apk.h"
31 #include "test_util.h"
32 
ReportCmd()33 static std::unique_ptr<Command> ReportCmd() {
34   return CreateCommandInstance("report");
35 }
36 
37 class ReportCommandTest : public ::testing::Test {
38  protected:
Report(const std::string perf_data,const std::vector<std::string> & add_args=std::vector<std::string> ())39   void Report(const std::string perf_data,
40               const std::vector<std::string>& add_args = std::vector<std::string>()) {
41     ReportRaw(GetTestData(perf_data), add_args);
42   }
43 
ReportRaw(const std::string perf_data,const std::vector<std::string> & add_args=std::vector<std::string> ())44   void ReportRaw(const std::string perf_data,
45                  const std::vector<std::string>& add_args = std::vector<std::string>()) {
46     success = false;
47     std::vector<std::string> args = {"-i", perf_data,
48         "--symfs", GetTestDataDir(), "-o", tmp_file.path};
49     args.insert(args.end(), add_args.begin(), add_args.end());
50     ASSERT_TRUE(ReportCmd()->Run(args));
51     ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &content));
52     ASSERT_TRUE(!content.empty());
53     std::vector<std::string> raw_lines = android::base::Split(content, "\n");
54     lines.clear();
55     for (const auto& line : raw_lines) {
56       std::string s = android::base::Trim(line);
57       if (!s.empty()) {
58         lines.push_back(s);
59       }
60     }
61     ASSERT_GE(lines.size(), 2u);
62     success = true;
63   }
64 
65   TemporaryFile tmp_file;
66   std::string content;
67   std::vector<std::string> lines;
68   bool success;
69 };
70 
TEST_F(ReportCommandTest,no_option)71 TEST_F(ReportCommandTest, no_option) {
72   Report(PERF_DATA);
73   ASSERT_TRUE(success);
74   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
75 }
76 
TEST_F(ReportCommandTest,sort_option_pid)77 TEST_F(ReportCommandTest, sort_option_pid) {
78   Report(PERF_DATA, {"--sort", "pid"});
79   ASSERT_TRUE(success);
80   size_t line_index = 0;
81   while (line_index < lines.size() && lines[line_index].find("Pid") == std::string::npos) {
82     line_index++;
83   }
84   ASSERT_LT(line_index + 2, lines.size());
85 }
86 
TEST_F(ReportCommandTest,sort_option_more_than_one)87 TEST_F(ReportCommandTest, sort_option_more_than_one) {
88   Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"});
89   ASSERT_TRUE(success);
90   size_t line_index = 0;
91   while (line_index < lines.size() && lines[line_index].find("Overhead") == std::string::npos) {
92     line_index++;
93   }
94   ASSERT_LT(line_index + 1, lines.size());
95   ASSERT_NE(lines[line_index].find("Command"), std::string::npos);
96   ASSERT_NE(lines[line_index].find("Pid"), std::string::npos);
97   ASSERT_NE(lines[line_index].find("Shared Object"), std::string::npos);
98   ASSERT_NE(lines[line_index].find("Symbol"), std::string::npos);
99   ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos);
100 }
101 
TEST_F(ReportCommandTest,children_option)102 TEST_F(ReportCommandTest, children_option) {
103   Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"});
104   ASSERT_TRUE(success);
105   std::unordered_map<std::string, std::pair<double, double>> map;
106   for (size_t i = 0; i < lines.size(); ++i) {
107     char name[1024];
108     std::pair<double, double> pair;
109     if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second, name) == 3) {
110       map.insert(std::make_pair(name, pair));
111     }
112   }
113   ASSERT_NE(map.find("GlobalFunc"), map.end());
114   ASSERT_NE(map.find("main"), map.end());
115   auto func_pair = map["GlobalFunc"];
116   auto main_pair = map["main"];
117   ASSERT_GE(main_pair.first, func_pair.first);
118   ASSERT_GE(func_pair.first, func_pair.second);
119   ASSERT_GE(func_pair.second, main_pair.second);
120 }
121 
CheckCalleeMode(std::vector<std::string> & lines)122 static bool CheckCalleeMode(std::vector<std::string>& lines) {
123   bool found = false;
124   for (size_t i = 0; i + 2 < lines.size(); ++i) {
125     if (lines[i].find("GlobalFunc") != std::string::npos &&
126         lines[i + 1].find("|") != std::string::npos &&
127         lines[i + 2].find("main") != std::string::npos) {
128       found = true;
129       break;
130     }
131   }
132   return found;
133 }
134 
CheckCallerMode(std::vector<std::string> & lines)135 static bool CheckCallerMode(std::vector<std::string>& lines) {
136   bool found = false;
137   for (size_t i = 0; i + 2 < lines.size(); ++i) {
138     if (lines[i].find("main") != std::string::npos &&
139         lines[i + 1].find("|") != std::string::npos &&
140         lines[i + 2].find("GlobalFunc") != std::string::npos) {
141       found = true;
142       break;
143     }
144   }
145   return found;
146 }
147 
TEST_F(ReportCommandTest,callgraph_option)148 TEST_F(ReportCommandTest, callgraph_option) {
149   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
150   ASSERT_TRUE(success);
151   ASSERT_TRUE(CheckCalleeMode(lines));
152   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "callee"});
153   ASSERT_TRUE(success);
154   ASSERT_TRUE(CheckCalleeMode(lines));
155   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "caller"});
156   ASSERT_TRUE(success);
157   ASSERT_TRUE(CheckCallerMode(lines));
158 }
159 
AllItemsWithString(std::vector<std::string> & lines,const std::vector<std::string> & strs)160 static bool AllItemsWithString(std::vector<std::string>& lines, const std::vector<std::string>& strs) {
161   size_t line_index = 0;
162   while (line_index < lines.size() && lines[line_index].find("Overhead") == std::string::npos) {
163     line_index++;
164   }
165   if (line_index == lines.size() || line_index + 1 == lines.size()) {
166     return false;
167   }
168   line_index++;
169   for (; line_index < lines.size(); ++line_index) {
170     bool exist = false;
171     for (auto& s : strs) {
172       if (lines[line_index].find(s) != std::string::npos) {
173         exist = true;
174         break;
175       }
176     }
177     if (!exist) {
178       return false;
179     }
180   }
181   return true;
182 }
183 
TEST_F(ReportCommandTest,pid_filter_option)184 TEST_F(ReportCommandTest, pid_filter_option) {
185   Report(PERF_DATA);
186   ASSERT_TRUE("success");
187   ASSERT_FALSE(AllItemsWithString(lines, {"26083"}));
188   ASSERT_FALSE(AllItemsWithString(lines, {"26083", "26090"}));
189   Report(PERF_DATA, {"--pids", "26083"});
190   ASSERT_TRUE(success);
191   ASSERT_TRUE(AllItemsWithString(lines, {"26083"}));
192   Report(PERF_DATA, {"--pids", "26083,26090"});
193   ASSERT_TRUE(success);
194   ASSERT_TRUE(AllItemsWithString(lines, {"26083", "26090"}));
195 }
196 
TEST_F(ReportCommandTest,tid_filter_option)197 TEST_F(ReportCommandTest, tid_filter_option) {
198   Report(PERF_DATA);
199   ASSERT_TRUE("success");
200   ASSERT_FALSE(AllItemsWithString(lines, {"26083"}));
201   ASSERT_FALSE(AllItemsWithString(lines, {"26083", "26090"}));
202   Report(PERF_DATA, {"--tids", "26083"});
203   ASSERT_TRUE(success);
204   ASSERT_TRUE(AllItemsWithString(lines, {"26083"}));
205   Report(PERF_DATA, {"--tids", "26083,26090"});
206   ASSERT_TRUE(success);
207   ASSERT_TRUE(AllItemsWithString(lines, {"26083", "26090"}));
208 }
209 
TEST_F(ReportCommandTest,comm_filter_option)210 TEST_F(ReportCommandTest, comm_filter_option) {
211   Report(PERF_DATA, {"--sort", "comm"});
212   ASSERT_TRUE(success);
213   ASSERT_FALSE(AllItemsWithString(lines, {"t1"}));
214   ASSERT_FALSE(AllItemsWithString(lines, {"t1", "t2"}));
215   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1"});
216   ASSERT_TRUE(success);
217   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
218   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1,t2"});
219   ASSERT_TRUE(success);
220   ASSERT_TRUE(AllItemsWithString(lines, {"t1", "t2"}));
221 }
222 
TEST_F(ReportCommandTest,dso_filter_option)223 TEST_F(ReportCommandTest, dso_filter_option) {
224   Report(PERF_DATA, {"--sort", "dso"});
225   ASSERT_TRUE(success);
226   ASSERT_FALSE(AllItemsWithString(lines, {"/t1"}));
227   ASSERT_FALSE(AllItemsWithString(lines, {"/t1", "/t2"}));
228   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1"});
229   ASSERT_TRUE(success);
230   ASSERT_TRUE(AllItemsWithString(lines, {"/t1"}));
231   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1,/t2"});
232   ASSERT_TRUE(success);
233   ASSERT_TRUE(AllItemsWithString(lines, {"/t1", "/t2"}));
234 }
235 
TEST_F(ReportCommandTest,use_branch_address)236 TEST_F(ReportCommandTest, use_branch_address) {
237   Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"});
238   std::set<std::pair<std::string, std::string>> hit_set;
239   bool after_overhead = false;
240   for (const auto& line : lines) {
241     if (!after_overhead && line.find("Overhead") != std::string::npos) {
242       after_overhead = true;
243     } else if (after_overhead) {
244       char from[80];
245       char to[80];
246       if (sscanf(line.c_str(), "%*f%%%s%s", from, to) == 2) {
247         hit_set.insert(std::make_pair<std::string, std::string>(from, to));
248       }
249     }
250   }
251   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>("GlobalFunc", "CalledFunc")),
252             hit_set.end());
253   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>("CalledFunc", "GlobalFunc")),
254             hit_set.end());
255 }
256 
TEST_F(ReportCommandTest,report_symbols_of_nativelib_in_apk)257 TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) {
258   Report(NATIVELIB_IN_APK_PERF_DATA);
259   ASSERT_TRUE(success);
260   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos);
261   ASSERT_NE(content.find("Func2"), std::string::npos);
262 }
263 
264 #if defined(__linux__)
265 
RecordCmd()266 static std::unique_ptr<Command> RecordCmd() {
267   return CreateCommandInstance("record");
268 }
269 
TEST_F(ReportCommandTest,dwarf_callgraph)270 TEST_F(ReportCommandTest, dwarf_callgraph) {
271   if (IsDwarfCallChainSamplingSupported()) {
272     TemporaryFile tmp_file;
273     ASSERT_TRUE(RecordCmd()->Run({"-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
274     ReportRaw(tmp_file.path, {"-g"});
275     ASSERT_TRUE(success);
276   } else {
277     GTEST_LOG_(INFO)
278         << "This test does nothing as dwarf callchain sampling is not supported on this device.";
279   }
280 }
281 
TEST_F(ReportCommandTest,report_dwarf_callgraph_of_nativelib_in_apk)282 TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) {
283   // NATIVELIB_IN_APK_PERF_DATA is recorded on arm64, so can only report callgraph on arm64.
284   if (GetBuildArch() == ARCH_ARM64) {
285     Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"});
286     ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)), std::string::npos);
287     ASSERT_NE(content.find("Func2"), std::string::npos);
288     ASSERT_NE(content.find("Func1"), std::string::npos);
289     ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
290   } else {
291     GTEST_LOG_(INFO) << "This test does nothing as it is only run on arm64 devices";
292   }
293 }
294 
295 #endif
296 
297