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