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/file.h> 20 #include <android-base/stringprintf.h> 21 #include <android-base/test_utils.h> 22 23 #include <thread> 24 25 #include "command.h" 26 #include "environment.h" 27 #include "event_selection_set.h" 28 #include "get_test_data.h" 29 #include "test_util.h" 30 31 static std::unique_ptr<Command> StatCmd() { 32 return CreateCommandInstance("stat"); 33 } 34 35 TEST(stat_cmd, no_options) { ASSERT_TRUE(StatCmd()->Run({"sleep", "1"})); } 36 37 TEST(stat_cmd, event_option) { 38 ASSERT_TRUE(StatCmd()->Run({"-e", "cpu-clock,task-clock", "sleep", "1"})); 39 } 40 41 TEST(stat_cmd, system_wide_option) { 42 TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "sleep", "1"}))); 43 } 44 45 TEST(stat_cmd, verbose_option) { 46 ASSERT_TRUE(StatCmd()->Run({"--verbose", "sleep", "1"})); 47 } 48 49 TEST(stat_cmd, tracepoint_event) { 50 TEST_IN_ROOT(ASSERT_TRUE( 51 StatCmd()->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}))); 52 } 53 54 TEST(stat_cmd, event_modifier) { 55 ASSERT_TRUE( 56 StatCmd()->Run({"-e", "cpu-cycles:u,cpu-cycles:k", "sleep", "1"})); 57 } 58 59 void RunWorkloadFunction() { 60 while (true) { 61 for (volatile int i = 0; i < 10000; ++i); 62 usleep(1); 63 } 64 } 65 66 void CreateProcesses(size_t count, 67 std::vector<std::unique_ptr<Workload>>* workloads) { 68 workloads->clear(); 69 // Create workloads run longer than profiling time. 70 for (size_t i = 0; i < count; ++i) { 71 std::unique_ptr<Workload> workload; 72 if (GetDefaultAppPackageName().empty()) { 73 workload = Workload::CreateWorkload(RunWorkloadFunction); 74 } else { 75 workload = Workload::CreateWorkload({"run-as", GetDefaultAppPackageName(), "./workload"}); 76 workload->SetKillFunction([](pid_t pid) { 77 Workload::RunCmd({"run-as", GetDefaultAppPackageName(), "kill", std::to_string(pid)}); 78 }); 79 } 80 ASSERT_TRUE(workload != nullptr); 81 ASSERT_TRUE(workload->Start()); 82 workloads->push_back(std::move(workload)); 83 } 84 } 85 86 TEST(stat_cmd, existing_processes) { 87 std::vector<std::unique_ptr<Workload>> workloads; 88 CreateProcesses(2, &workloads); 89 std::string pid_list = android::base::StringPrintf( 90 "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); 91 ASSERT_TRUE(StatCmd()->Run({"-p", pid_list, "sleep", "1"})); 92 } 93 94 TEST(stat_cmd, existing_threads) { 95 std::vector<std::unique_ptr<Workload>> workloads; 96 CreateProcesses(2, &workloads); 97 // Process id can be used as thread id in linux. 98 std::string tid_list = android::base::StringPrintf( 99 "%d,%d", workloads[0]->GetPid(), workloads[1]->GetPid()); 100 ASSERT_TRUE(StatCmd()->Run({"-t", tid_list, "sleep", "1"})); 101 } 102 103 TEST(stat_cmd, no_monitored_threads) { ASSERT_FALSE(StatCmd()->Run({""})); } 104 105 TEST(stat_cmd, group_option) { 106 ASSERT_TRUE( 107 StatCmd()->Run({"--group", "cpu-clock,page-faults", "sleep", "1"})); 108 ASSERT_TRUE(StatCmd()->Run({"--group", "cpu-cycles,instructions", "--group", 109 "cpu-cycles:u,instructions:u", "--group", 110 "cpu-cycles:k,instructions:k", "sleep", "1"})); 111 } 112 113 TEST(stat_cmd, auto_generated_summary) { 114 TemporaryFile tmp_file; 115 ASSERT_TRUE(StatCmd()->Run({"--group", "instructions:u,instructions:k", "-o", 116 tmp_file.path, "sleep", "1"})); 117 std::string s; 118 ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &s)); 119 size_t pos = s.find("instructions:u"); 120 ASSERT_NE(s.npos, pos); 121 pos = s.find("instructions:k", pos); 122 ASSERT_NE(s.npos, pos); 123 pos += strlen("instructions:k"); 124 // Check if the summary of instructions is generated. 125 ASSERT_NE(s.npos, s.find("instructions", pos)); 126 } 127 128 TEST(stat_cmd, duration_option) { 129 ASSERT_TRUE( 130 StatCmd()->Run({"--duration", "1.2", "-p", std::to_string(getpid()), "--in-app"})); 131 ASSERT_TRUE(StatCmd()->Run({"--duration", "1", "sleep", "2"})); 132 } 133 134 TEST(stat_cmd, interval_option) { 135 TemporaryFile tmp_file; 136 ASSERT_TRUE( 137 StatCmd()->Run({"--interval", "500.0", "--duration", "1.2", "-o", 138 tmp_file.path, "sleep", "2"})); 139 std::string s; 140 ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &s)); 141 size_t count = 0; 142 size_t pos = 0; 143 std::string subs = "statistics:"; 144 while((pos = s.find(subs, pos)) != s.npos) { 145 pos += subs.size(); 146 ++count ; 147 } 148 ASSERT_EQ(count, 3UL); 149 } 150 151 TEST(stat_cmd, interval_option_in_system_wide) { 152 TEST_IN_ROOT(ASSERT_TRUE(StatCmd()->Run({"-a", "--interval", "100", "--duration", "0.3"}))); 153 } 154 155 TEST(stat_cmd, no_modifier_for_clock_events) { 156 for (const std::string& e : {"cpu-clock", "task-clock"}) { 157 for (const std::string& m : {"u", "k"}) { 158 ASSERT_FALSE(StatCmd()->Run({"-e", e + ":" + m, "sleep", "0.1"})) 159 << "event " << e << ":" << m; 160 } 161 } 162 } 163 164 TEST(stat_cmd, handle_SIGHUP) { 165 std::thread thread([]() { 166 sleep(1); 167 kill(getpid(), SIGHUP); 168 }); 169 thread.detach(); 170 ASSERT_TRUE(StatCmd()->Run({"sleep", "1000000"})); 171 } 172 173 TEST(stat_cmd, stop_when_no_more_targets) { 174 std::atomic<int> tid(0); 175 std::thread thread([&]() { 176 tid = gettid(); 177 sleep(1); 178 }); 179 thread.detach(); 180 while (tid == 0); 181 ASSERT_TRUE(StatCmd()->Run({"-t", std::to_string(tid), "--in-app"})); 182 } 183 184 TEST(stat_cmd, sample_speed_should_be_zero) { 185 EventSelectionSet set(true); 186 ASSERT_TRUE(set.AddEventType("cpu-cycles")); 187 set.AddMonitoredProcesses({getpid()}); 188 ASSERT_TRUE(set.OpenEventFiles({-1})); 189 std::vector<EventAttrWithId> attrs = set.GetEventAttrWithId(); 190 ASSERT_GT(attrs.size(), 0u); 191 for (auto& attr : attrs) { 192 ASSERT_EQ(attr.attr->sample_period, 0u); 193 ASSERT_EQ(attr.attr->sample_freq, 0u); 194 ASSERT_EQ(attr.attr->freq, 0u); 195 } 196 } 197