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 <gtest/gtest.h>
19
20 #include "cmd_inject_impl.h"
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_FALSE(RunInjectCmd({"--exclude-perf"}, nullptr));
91 std::string perf_with_recording_process =
92 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_with_recording_process.data");
93 ASSERT_TRUE(RunInjectCmd({"--exclude-perf", "-i", perf_with_recording_process}, nullptr));
94 }
95
TEST(cmd_inject,output_option)96 TEST(cmd_inject, output_option) {
97 TemporaryFile tmpfile;
98 close(tmpfile.release());
99 ASSERT_TRUE(RunInjectCmd({"--output", "autofdo", "-o", tmpfile.path}));
100 ASSERT_TRUE(RunInjectCmd({"--output", "branch-list", "-o", tmpfile.path}));
101 std::string autofdo_data;
102 ASSERT_TRUE(RunInjectCmd({"-i", tmpfile.path, "--output", "autofdo"}, &autofdo_data));
103 CheckMatchingExpectedData(autofdo_data);
104 }
105
TEST(cmd_inject,branch_to_proto_string)106 TEST(cmd_inject, branch_to_proto_string) {
107 std::vector<bool> branch;
108 for (size_t i = 0; i < 100; i++) {
109 branch.push_back(i % 2 == 0);
110 std::string s = BranchToProtoString(branch);
111 for (size_t j = 0; j <= i; j++) {
112 bool b = s[j >> 3] & (1 << (j & 7));
113 ASSERT_EQ(b, branch[j]);
114 }
115 std::vector<bool> branch2 = ProtoStringToBranch(s, branch.size());
116 ASSERT_EQ(branch, branch2);
117 }
118 }
119
TEST(cmd_inject,skip_empty_output_file)120 TEST(cmd_inject, skip_empty_output_file) {
121 TemporaryFile tmpfile;
122 close(tmpfile.release());
123 ASSERT_TRUE(RunInjectCmd(
124 {"--binary", "not_exist_binary", "--output", "branch-list", "-o", tmpfile.path}));
125 // The empty output file should not be produced.
126 ASSERT_FALSE(IsRegularFile(tmpfile.path));
127 tmpfile.DoNotRemove();
128 }
129
TEST(cmd_inject,inject_kernel_data)130 TEST(cmd_inject, inject_kernel_data) {
131 const std::string recording_file =
132 GetTestData(std::string("etm") + OS_PATH_SEPARATOR + "perf_kernel.data");
133
134 // Inject directly to autofdo format.
135 TemporaryFile tmpfile;
136 close(tmpfile.release());
137 ASSERT_TRUE(RunInjectCmd({"-i", recording_file, "-o", tmpfile.path}));
138 std::string autofdo_output;
139 ASSERT_TRUE(android::base::ReadFileToString(tmpfile.path, &autofdo_output));
140 ASSERT_NE(autofdo_output.find("rq_stats.ko"), std::string::npos);
141
142 // Inject through etm branch list.
143 TemporaryFile tmpfile2;
144 close(tmpfile2.release());
145 ASSERT_TRUE(RunInjectCmd({"-i", recording_file, "-o", tmpfile.path, "--output", "branch-list"}));
146 ASSERT_TRUE(RunInjectCmd({"-i", tmpfile.path, "-o", tmpfile2.path}));
147 std::string output;
148 ASSERT_TRUE(android::base::ReadFileToString(tmpfile2.path, &output));
149 ASSERT_EQ(output, autofdo_output);
150 }
151