1 /*
2 * Copyright (C) 2019 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 <android-base/file.h>
18 #include <android-base/test_utils.h>
19 #include <gtest/gtest.h>
20
21 #include "command.h"
22 #include "get_test_data.h"
23 #include "test_util.h"
24 #include "utils.h"
25
26 using namespace simpleperf;
27
InjectCmd()28 static std::unique_ptr<Command> InjectCmd() {
29 return CreateCommandInstance("inject");
30 }
31
RunInjectCmd(std::vector<std::string> && args)32 static bool RunInjectCmd(std::vector<std::string>&& args) {
33 bool has_input = std::find(args.begin(), args.end(), "-i") != args.end();
34 if (!has_input) {
35 args.insert(args.end(), {"-i", GetTestData(PERF_DATA_ETM_TEST_LOOP)});
36 }
37 args.insert(args.end(), {"--symdir", GetTestDataDir() + "etm"});
38 return InjectCmd()->Run(args);
39 }
40
RunInjectCmd(std::vector<std::string> && args,std::string * output)41 static bool RunInjectCmd(std::vector<std::string>&& args, std::string* output) {
42 TemporaryFile tmpfile;
43 close(tmpfile.release());
44 args.insert(args.end(), {"-o", tmpfile.path});
45 if (!RunInjectCmd(std::move(args))) {
46 return false;
47 }
48 if (output != nullptr) {
49 return android::base::ReadFileToString(tmpfile.path, output);
50 }
51 return true;
52 }
53
CheckMatchingExpectedData(std::string & data)54 static void CheckMatchingExpectedData(std::string& data) {
55 std::string expected_data;
56 ASSERT_TRUE(android::base::ReadFileToString(
57 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_inject.data"), &expected_data));
58 data.erase(std::remove(data.begin(), data.end(), '\r'), data.end());
59 ASSERT_EQ(data, expected_data);
60 }
61
TEST(cmd_inject,smoke)62 TEST(cmd_inject, smoke) {
63 std::string data;
64 ASSERT_TRUE(RunInjectCmd({}, &data));
65 // Test that we can find instr range in etm_test_loop binary.
66 ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
67 CheckMatchingExpectedData(data);
68 }
69
TEST(cmd_inject,binary_option)70 TEST(cmd_inject, binary_option) {
71 // Test that data for etm_test_loop is generated when selected by --binary.
72 std::string data;
73 ASSERT_TRUE(RunInjectCmd({"--binary", "etm_test_loop"}, &data));
74 ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
75
76 // Test that data for etm_test_loop is generated when selected by regex.
77 ASSERT_TRUE(RunInjectCmd({"--binary", "etm_t.*_loop"}, &data));
78 ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
79
80 // Test that data for etm_test_loop isn't generated when not selected by --binary.
81 ASSERT_TRUE(RunInjectCmd({"--binary", "no_etm_test_loop"}, &data));
82 ASSERT_EQ(data.find("etm_test_loop"), std::string::npos);
83
84 // Test that data for etm_test_loop isn't generated when not selected by regex.
85 ASSERT_TRUE(RunInjectCmd({"--binary", "no_etm_test_.*"}, &data));
86 ASSERT_EQ(data.find("etm_test_loop"), std::string::npos);
87 }
88
TEST(cmd_inject,exclude_perf_option)89 TEST(cmd_inject, exclude_perf_option) {
90 ASSERT_TRUE(RunInjectCmd({"--exclude-perf"}, nullptr));
91 }
92
TEST(cmd_inject,output_option)93 TEST(cmd_inject, output_option) {
94 TemporaryFile tmpfile;
95 close(tmpfile.release());
96 ASSERT_TRUE(RunInjectCmd({"--output", "autofdo", "-o", tmpfile.path}));
97 ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-o", tmpfile.path}));
98 std::string autofdo_data;
99 ASSERT_TRUE(RunInjectCmd({"-i", tmpfile.path, "--output", "autofdo"}, &autofdo_data));
100 CheckMatchingExpectedData(autofdo_data);
101 }
102
TEST(cmd_inject,skip_empty_output_file)103 TEST(cmd_inject, skip_empty_output_file) {
104 TemporaryFile tmpfile;
105 close(tmpfile.release());
106 ASSERT_TRUE(RunInjectCmd(
107 {"--binary", "not_exist_binary", "--output", "branch-list", "-o", tmpfile.path}));
108 // The empty output file should not be produced.
109 ASSERT_FALSE(IsRegularFile(tmpfile.path));
110 tmpfile.DoNotRemove();
111 }
112
TEST(cmd_inject,inject_kernel_data)113 TEST(cmd_inject, inject_kernel_data) {
114 const std::string recording_file =
115 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_kernel.data");
116
117 // Inject directly to autofdo format.
118 TemporaryFile tmpfile;
119 close(tmpfile.release());
120 ASSERT_TRUE(RunInjectCmd({"-i", recording_file, "-o", tmpfile.path}));
121 std::string autofdo_output;
122 ASSERT_TRUE(android::base::ReadFileToString(tmpfile.path, &autofdo_output));
123 ASSERT_NE(autofdo_output.find("rq_stats.ko"), std::string::npos);
124
125 // Inject through etm branch list.
126 TemporaryFile tmpfile2;
127 close(tmpfile2.release());
128 ASSERT_TRUE(RunInjectCmd({"-i", recording_file, "-o", tmpfile.path, "--output", "branch-list"}));
129 ASSERT_TRUE(RunInjectCmd({"-i", tmpfile.path, "-o", tmpfile2.path}));
130 std::string output;
131 ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &output));
132 ASSERT_EQ(output, autofdo_output);
133 }
134
TEST(cmd_inject,unformatted_trace)135 TEST(cmd_inject, unformatted_trace) {
136 std::string data;
137 std::string perf_with_unformatted_trace =
138 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_with_unformatted_trace.data");
139 ASSERT_TRUE(RunInjectCmd({"-i", perf_with_unformatted_trace}, &data));
140 // Test that we can find instr range in etm_test_loop binary.
141 ASSERT_NE(data.find("etm_test_loop"), std::string::npos);
142 CheckMatchingExpectedData(data);
143 }
144
TEST(cmd_inject,multiple_input_files)145 TEST(cmd_inject, multiple_input_files) {
146 std::string data;
147 std::string perf_data = GetTestData(PERF_DATA_ETM_TEST_LOOP);
148 std::string perf_with_unformatted_trace =
149 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_with_unformatted_trace.data");
150
151 // Test input files separated by comma.
152 ASSERT_TRUE(RunInjectCmd({"-i", perf_with_unformatted_trace + "," + perf_data}, &data));
153 ASSERT_NE(data.find("106c->1074:200"), std::string::npos);
154
155 // Test input files from different -i options.
156 ASSERT_TRUE(RunInjectCmd({"-i", perf_with_unformatted_trace, "-i", perf_data}, &data));
157 ASSERT_NE(data.find("106c->1074:200"), std::string::npos);
158
159 // Test input files provided by input_file_list.
160 TemporaryFile tmpfile;
161 std::string input_file_list = perf_data + "\n" + perf_with_unformatted_trace + "\n";
162 ASSERT_TRUE(android::base::WriteStringToFd(input_file_list, tmpfile.fd));
163 close(tmpfile.release());
164 ASSERT_TRUE(RunInjectCmd({"-i", std::string("@") + tmpfile.path}, &data));
165 ASSERT_NE(data.find("106c->1074:200"), std::string::npos);
166 }
167
TEST(cmd_inject,merge_branch_list_files)168 TEST(cmd_inject, merge_branch_list_files) {
169 TemporaryFile tmpfile;
170 close(tmpfile.release());
171 ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-o", tmpfile.path}));
172 TemporaryFile tmpfile2;
173 close(tmpfile2.release());
174 ASSERT_TRUE(RunInjectCmd({"-i", std::string(tmpfile.path) + "," + tmpfile.path, "--output",
175 "branch-list", "-o", tmpfile2.path}));
176 std::string autofdo_data;
177 ASSERT_TRUE(RunInjectCmd({"-i", tmpfile2.path, "--output", "autofdo"}, &autofdo_data));
178 ASSERT_NE(autofdo_data.find("106c->1074:200"), std::string::npos);
179 }
180
TEST(cmd_inject,report_warning_when_overflow)181 TEST(cmd_inject, report_warning_when_overflow) {
182 CapturedStderr capture;
183 std::vector<std::unique_ptr<TemporaryFile>> branch_list_files;
184 std::vector<std::unique_ptr<TemporaryFile>> input_files;
185
186 branch_list_files.emplace_back(new TemporaryFile);
187 close(branch_list_files.back()->release());
188 ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-o", branch_list_files.back()->path}));
189 for (size_t i = 1; i <= 7; i++) {
190 // Create input file list, repeating branch list file for 1000 times.
191 std::string s;
192 for (size_t j = 0; j < 1000; j++) {
193 s += std::string(branch_list_files.back()->path) + "\n";
194 }
195 input_files.emplace_back(new TemporaryFile);
196 ASSERT_TRUE(android::base::WriteStringToFd(s, input_files.back()->fd));
197 close(input_files.back()->release());
198
199 // Merge branch list files.
200 branch_list_files.emplace_back(new TemporaryFile);
201 close(branch_list_files.back()->release());
202 ASSERT_TRUE(
203 RunInjectCmd({"--output", "branch-list", "-i", std::string("@") + input_files.back()->path,
204 "-o", branch_list_files.back()->path}));
205 }
206 capture.Stop();
207 const std::string WARNING_MSG = "Branch count overflow happened.";
208 ASSERT_NE(capture.str().find(WARNING_MSG), std::string::npos);
209
210 // Warning also happens when converting branch lists to AutoFDO format.
211 capture.Reset();
212 capture.Start();
213 std::string autofdo_data;
214 ASSERT_TRUE(RunInjectCmd({"-i", branch_list_files.back()->path}, &autofdo_data));
215 capture.Stop();
216 ASSERT_NE(capture.str().find(WARNING_MSG), std::string::npos);
217 ASSERT_NE(autofdo_data.find("106c->1074:18446744073709551615"), std::string::npos);
218 }
219
TEST(cmd_inject,accept_missing_aux_data)220 TEST(cmd_inject, accept_missing_aux_data) {
221 // Recorded with "-e cs-etm:u --user-buffer-size 64k sleep 1".
222 std::string perf_data = GetTestData("etm/perf_with_missing_aux_data.data");
223 TemporaryFile tmpfile;
224 close(tmpfile.release());
225 ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-i", perf_data, "-o", tmpfile.path}));
226 }
227