OSDN Git Service

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