2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <gtest/gtest.h>
19 #include <android-base/stringprintf.h>
20 #include <android-base/test_utils.h>
21 #include <sys/syscall.h>
28 #include "environment.h"
29 #include "event_selection_set.h"
30 #include "get_test_data.h"
32 #include "record_file.h"
33 #include "test_util.h"
34 #include "thread_tree.h"
36 using namespace PerfFileFormat;
38 static std::unique_ptr<Command> RecordCmd() {
39 return CreateCommandInstance("record");
42 static bool RunRecordCmd(std::vector<std::string> v,
43 const char* output_file = nullptr) {
44 std::unique_ptr<TemporaryFile> tmpfile;
46 if (output_file != nullptr) {
47 out_file = output_file;
49 tmpfile.reset(new TemporaryFile);
50 out_file = tmpfile->path;
52 v.insert(v.end(), {"-o", out_file, "sleep", SLEEP_SEC});
53 return RecordCmd()->Run(v);
56 TEST(record_cmd, no_options) { ASSERT_TRUE(RunRecordCmd({})); }
58 TEST(record_cmd, system_wide_option) {
59 TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a"})));
62 TEST(record_cmd, sample_period_option) {
63 ASSERT_TRUE(RunRecordCmd({"-c", "100000"}));
66 TEST(record_cmd, event_option) {
67 ASSERT_TRUE(RunRecordCmd({"-e", "cpu-clock"}));
70 TEST(record_cmd, freq_option) {
71 ASSERT_TRUE(RunRecordCmd({"-f", "99"}));
72 ASSERT_TRUE(RunRecordCmd({"-F", "99"}));
75 TEST(record_cmd, output_file_option) {
76 TemporaryFile tmpfile;
77 ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", SLEEP_SEC}));
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;
100 ASSERT_TRUE(have_kernel_mmap);
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);
115 TEST(record_cmd, tracepoint_event) {
116 TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "-e", "sched:sched_switch"})));
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"}));
127 GTEST_LOG_(INFO) << "This test does nothing as branch stack sampling is "
128 "not supported on this device.";
132 TEST(record_cmd, event_modifier) {
133 ASSERT_TRUE(RunRecordCmd({"-e", "cpu-cycles:u"}));
136 TEST(record_cmd, fp_callchain_sampling) {
137 ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
140 TEST(record_cmd, system_wide_fp_callchain_sampling) {
141 TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
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"}));
154 GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
155 "not supported on this device.";
159 TEST(record_cmd, system_wide_dwarf_callchain_sampling) {
160 if (IsDwarfCallChainSamplingSupported()) {
161 TEST_IN_ROOT(RunRecordCmd({"-a", "--call-graph", "dwarf"}));
163 GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
164 "not supported on this device.";
168 TEST(record_cmd, no_unwind_option) {
169 if (IsDwarfCallChainSamplingSupported()) {
170 ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--no-unwind"}));
172 GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
173 "not supported on this device.";
175 ASSERT_FALSE(RunRecordCmd({"--no-unwind"}));
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"}));
185 GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
186 "not supported on this device.";
188 ASSERT_FALSE(RunRecordCmd({"--post-unwind"}));
190 RunRecordCmd({"--call-graph", "dwarf", "--no-unwind", "--post-unwind"}));
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}));
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}));
210 TEST(record_cmd, no_monitored_threads) { ASSERT_FALSE(RecordCmd()->Run({""})); }
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"}));
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"}));
223 static void CheckKernelSymbol(const std::string& path, bool need_kallsyms,
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;
236 bool require_kallsyms = need_kallsyms && CheckKernelSymbolAddresses();
237 ASSERT_EQ(require_kallsyms, has_kernel_symbol_records);
241 TEST(record_cmd, kernel_symbol) {
242 TemporaryFile tmpfile;
243 ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
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);
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,
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);
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);
286 ASSERT_FALSE(has_dso_record);
287 ASSERT_FALSE(has_symbol_record);
292 TEST(record_cmd, dump_symbols) {
293 TemporaryFile tmpfile;
294 ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
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));
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);
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"}));
322 TEST(record_cmd, symfs_option) { ASSERT_TRUE(RunRecordCmd({"--symfs", "/"})); }
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}));
329 RecordCmd()->Run({"--duration", "1", "-o", tmpfile.path, "sleep", "2"}));
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 << ":"
341 TEST(record_cmd, handle_SIGHUP) {
342 TemporaryFile tmpfile;
343 std::thread thread([]() {
345 kill(getpid(), SIGHUP);
348 ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", "1000000"}));
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);
360 ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-t", std::to_string(tid)}));