OSDN Git Service

make_ext4fs: add option to specify the number of inodes [DO NOT MERGE]
[android-x86/system-extras.git] / simpleperf / cmd_record_test.cpp
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 <android-base/stringprintf.h>
20 #include <android-base/test_utils.h>
21 #include <sys/syscall.h>
22
23 #include <map>
24 #include <memory>
25 #include <thread>
26
27 #include "command.h"
28 #include "environment.h"
29 #include "event_selection_set.h"
30 #include "get_test_data.h"
31 #include "record.h"
32 #include "record_file.h"
33 #include "test_util.h"
34 #include "thread_tree.h"
35
36 using namespace PerfFileFormat;
37
38 static std::unique_ptr<Command> RecordCmd() {
39   return CreateCommandInstance("record");
40 }
41
42 static bool RunRecordCmd(std::vector<std::string> v,
43                          const char* output_file = nullptr) {
44   std::unique_ptr<TemporaryFile> tmpfile;
45   std::string out_file;
46   if (output_file != nullptr) {
47     out_file = output_file;
48   } else {
49     tmpfile.reset(new TemporaryFile);
50     out_file = tmpfile->path;
51   }
52   v.insert(v.end(), {"-o", out_file, "sleep", SLEEP_SEC});
53   return RecordCmd()->Run(v);
54 }
55
56 TEST(record_cmd, no_options) { ASSERT_TRUE(RunRecordCmd({})); }
57
58 TEST(record_cmd, system_wide_option) {
59   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
60 }
61
62 TEST(record_cmd, sample_period_option) {
63   ASSERT_TRUE(RunRecordCmd({"-c", "100000"}));
64 }
65
66 TEST(record_cmd, event_option) {
67   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock"}));
68 }
69
70 TEST(record_cmd, freq_option) {
71   ASSERT_TRUE(RunRecordCmd({"-f", "99"}));
72   ASSERT_TRUE(RunRecordCmd({"-F", "99"}));
73 }
74
75 TEST(record_cmd, output_file_option) {
76   TemporaryFile tmpfile;
77   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", SLEEP_SEC}));
78 }
79
80 TEST(record_cmd, dump_kernel_mmap) {
81   TemporaryFile tmpfile;
82   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
83   std::unique_ptr<RecordFileReader> reader =
84       RecordFileReader::CreateInstance(tmpfile.path);
85   ASSERT_TRUE(reader != nullptr);
86   std::vector<std::unique_ptr<Record>> records = reader->DataSection();
87   ASSERT_GT(records.size(), 0U);
88   bool have_kernel_mmap = false;
89   for (auto& record : records) {
90     if (record->type() == PERF_RECORD_MMAP) {
91       const MmapRecord* mmap_record =
92           static_cast<const MmapRecord*>(record.get());
93       if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0 ||
94           strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME_PERF) == 0) {
95         have_kernel_mmap = true;
96         break;
97       }
98     }
99   }
100   ASSERT_TRUE(have_kernel_mmap);
101 }
102
103 TEST(record_cmd, dump_build_id_feature) {
104   TemporaryFile tmpfile;
105   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
106   std::unique_ptr<RecordFileReader> reader =
107       RecordFileReader::CreateInstance(tmpfile.path);
108   ASSERT_TRUE(reader != nullptr);
109   const FileHeader& file_header = reader->FileHeader();
110   ASSERT_TRUE(file_header.features[FEAT_BUILD_ID / 8] &
111               (1 << (FEAT_BUILD_ID % 8)));
112   ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
113 }
114
115 TEST(record_cmd, tracepoint_event) {
116   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"})));
117 }
118
119 TEST(record_cmd, branch_sampling) {
120   if (IsBranchSamplingSupported()) {
121     ASSERT_TRUE(RunRecordCmd({"-b"}));
122     ASSERT_TRUE(RunRecordCmd({"-j", "any,any_call,any_ret,ind_call"}));
123     ASSERT_TRUE(RunRecordCmd({"-j", "any,k"}));
124     ASSERT_TRUE(RunRecordCmd({"-j", "any,u"}));
125     ASSERT_FALSE(RunRecordCmd({"-j", "u"}));
126   } else {
127     GTEST_LOG_(INFO) << "This test does nothing as branch stack sampling is "
128                         "not supported on this device.";
129   }
130 }
131
132 TEST(record_cmd, event_modifier) {
133   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles:u"}));
134 }
135
136 TEST(record_cmd, fp_callchain_sampling) {
137   ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
138 }
139
140 TEST(record_cmd, system_wide_fp_callchain_sampling) {
141   TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
142 }
143
144 TEST(record_cmd, dwarf_callchain_sampling) {
145   if (IsDwarfCallChainSamplingSupported()) {
146     std::vector<std::unique_ptr<Workload>> workloads;
147     CreateProcesses(1, &workloads);
148     std::string pid = std::to_string(workloads[0]->GetPid());
149     ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf"}));
150     ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,16384"}));
151     ASSERT_FALSE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,65536"}));
152     ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}));
153   } else {
154     GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
155                         "not supported on this device.";
156   }
157 }
158
159 TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
160   if (IsDwarfCallChainSamplingSupported()) {
161     TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
162   } else {
163     GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
164                         "not supported on this device.";
165   }
166 }
167
168 TEST(record_cmd, no_unwind_option) {
169   if (IsDwarfCallChainSamplingSupported()) {
170     ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
171   } else {
172     GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
173                         "not supported on this device.";
174   }
175   ASSERT_FALSE(RunRecordCmd({"--no-unwind"}));
176 }
177
178 TEST(record_cmd, post_unwind_option) {
179   if (IsDwarfCallChainSamplingSupported()) {
180     std::vector<std::unique_ptr<Workload>> workloads;
181     CreateProcesses(1, &workloads);
182     std::string pid = std::to_string(workloads[0]->GetPid());
183     ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf", "--post-unwind"}));
184   } else {
185     GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
186                         "not supported on this device.";
187   }
188   ASSERT_FALSE(RunRecordCmd({"--post-unwind"}));
189   ASSERT_FALSE(
190       RunRecordCmd({"--call-graph", "dwarf", "--no-unwind", "--post-unwind"}));
191 }
192
193 TEST(record_cmd, existing_processes) {
194   std::vector<std::unique_ptr<Workload>> workloads;
195   CreateProcesses(2, &workloads);
196   std::string pid_list = android::base::StringPrintf(
197       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
198   ASSERT_TRUE(RunRecordCmd({"-p", pid_list}));
199 }
200
201 TEST(record_cmd, existing_threads) {
202   std::vector<std::unique_ptr<Workload>> workloads;
203   CreateProcesses(2, &workloads);
204   // Process id can also be used as thread id in linux.
205   std::string tid_list = android::base::StringPrintf(
206       "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid());
207   ASSERT_TRUE(RunRecordCmd({"-t", tid_list}));
208 }
209
210 TEST(record_cmd, no_monitored_threads) { ASSERT_FALSE(RecordCmd()->Run({""})); }
211
212 TEST(record_cmd, more_than_one_event_types) {
213   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles,cpu-clock"}));
214   ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles", "-e", "cpu-clock"}));
215 }
216
217 TEST(record_cmd, mmap_page_option) {
218   ASSERT_TRUE(RunRecordCmd({"-m", "1"}));
219   ASSERT_FALSE(RunRecordCmd({"-m", "0"}));
220   ASSERT_FALSE(RunRecordCmd({"-m", "7"}));
221 }
222
223 static void CheckKernelSymbol(const std::string& path, bool need_kallsyms,
224                               bool* success) {
225   *success = false;
226   std::unique_ptr<RecordFileReader> reader =
227       RecordFileReader::CreateInstance(path);
228   ASSERT_TRUE(reader != nullptr);
229   std::vector<std::unique_ptr<Record>> records = reader->DataSection();
230   bool has_kernel_symbol_records = false;
231   for (const auto& record : records) {
232     if (record->type() == SIMPLE_PERF_RECORD_KERNEL_SYMBOL) {
233       has_kernel_symbol_records = true;
234     }
235   }
236   bool require_kallsyms = need_kallsyms && CheckKernelSymbolAddresses();
237   ASSERT_EQ(require_kallsyms, has_kernel_symbol_records);
238   *success = true;
239 }
240
241 TEST(record_cmd, kernel_symbol) {
242   TemporaryFile tmpfile;
243   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
244   bool success;
245   CheckKernelSymbol(tmpfile.path, true, &success);
246   ASSERT_TRUE(success);
247   ASSERT_TRUE(RunRecordCmd({"--no-dump-kernel-symbols"}, tmpfile.path));
248   CheckKernelSymbol(tmpfile.path, false, &success);
249   ASSERT_TRUE(success);
250 }
251
252 // Check if the dso/symbol records in perf.data matches our expectation.
253 static void CheckDsoSymbolRecords(const std::string& path,
254                                   bool can_have_dso_symbol_records,
255                                   bool* success) {
256   *success = false;
257   std::unique_ptr<RecordFileReader> reader =
258       RecordFileReader::CreateInstance(path);
259   ASSERT_TRUE(reader != nullptr);
260   std::vector<std::unique_ptr<Record>> records = reader->DataSection();
261   bool has_dso_record = false;
262   bool has_symbol_record = false;
263   std::map<uint64_t, bool> dso_hit_map;
264   for (const auto& record : records) {
265     if (record->type() == SIMPLE_PERF_RECORD_DSO) {
266       has_dso_record = true;
267       uint64_t dso_id = static_cast<const DsoRecord*>(record.get())->dso_id;
268       ASSERT_EQ(dso_hit_map.end(), dso_hit_map.find(dso_id));
269       dso_hit_map.insert(std::make_pair(dso_id, false));
270     } else if (record->type() == SIMPLE_PERF_RECORD_SYMBOL) {
271       has_symbol_record = true;
272       uint64_t dso_id = static_cast<const SymbolRecord*>(record.get())->dso_id;
273       auto it = dso_hit_map.find(dso_id);
274       ASSERT_NE(dso_hit_map.end(), it);
275       it->second = true;
276     }
277   }
278   if (can_have_dso_symbol_records) {
279     // It is possible that there are no samples hitting functions having symbol.
280     // In that case, there are no dso/symbol records.
281     ASSERT_EQ(has_dso_record, has_symbol_record);
282     for (auto& pair : dso_hit_map) {
283       ASSERT_TRUE(pair.second);
284     }
285   } else {
286     ASSERT_FALSE(has_dso_record);
287     ASSERT_FALSE(has_symbol_record);
288   }
289   *success = true;
290 }
291
292 TEST(record_cmd, dump_symbols) {
293   TemporaryFile tmpfile;
294   ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
295   bool success;
296   CheckDsoSymbolRecords(tmpfile.path, false, &success);
297   ASSERT_TRUE(success);
298   ASSERT_TRUE(RunRecordCmd({"--dump-symbols"}, tmpfile.path));
299   CheckDsoSymbolRecords(tmpfile.path, true, &success);
300   ASSERT_TRUE(success);
301   if (IsDwarfCallChainSamplingSupported()) {
302     std::vector<std::unique_ptr<Workload>> workloads;
303     CreateProcesses(1, &workloads);
304     std::string pid = std::to_string(workloads[0]->GetPid());
305     ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}, tmpfile.path));
306     bool success;
307     CheckDsoSymbolRecords(tmpfile.path, false, &success);
308     ASSERT_TRUE(success);
309     ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g", "--dump-symbols"}, tmpfile.path));
310     CheckDsoSymbolRecords(tmpfile.path, true, &success);
311     ASSERT_TRUE(success);
312   }
313 }
314
315 TEST(record_cmd, group_option) {
316   ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "-m", "16"}));
317   ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "--group",
318                             "cpu-cycles:u,cpu-clock:u", "--group",
319                             "cpu-cycles:k,cpu-clock:k", "-m", "16"}));
320 }
321
322 TEST(record_cmd, symfs_option) { ASSERT_TRUE(RunRecordCmd({"--symfs", "/"})); }
323
324 TEST(record_cmd, duration_option) {
325   TemporaryFile tmpfile;
326   ASSERT_TRUE(RecordCmd()->Run({"--duration", "1.2", "-p",
327                                 std::to_string(getpid()), "-o", tmpfile.path}));
328   ASSERT_TRUE(
329       RecordCmd()->Run({"--duration", "1", "-o", tmpfile.path, "sleep", "2"}));
330 }
331
332 TEST(record_cmd, support_modifier_for_clock_events) {
333   for (const std::string& e : {"cpu-clock", "task-clock"}) {
334     for (const std::string& m : {"u", "k"}) {
335       ASSERT_TRUE(RunRecordCmd({"-e", e + ":" + m})) << "event " << e << ":"
336                                                      << m;
337     }
338   }
339 }
340
341 TEST(record_cmd, handle_SIGHUP) {
342   TemporaryFile tmpfile;
343   std::thread thread([]() {
344     sleep(1);
345     kill(getpid(), SIGHUP);
346   });
347   thread.detach();
348   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", "1000000"}));
349 }
350
351 TEST(record_cmd, stop_when_no_more_targets) {
352   TemporaryFile tmpfile;
353   std::atomic<int> tid(0);
354   std::thread thread([&]() {
355     tid = syscall(__NR_gettid);
356     sleep(1);
357   });
358   thread.detach();
359   while (tid == 0);
360   ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-t", std::to_string(tid)}));
361 }