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 <stdio.h> 18 19 #include <memory> 20 #include <string> 21 #include <thread> 22 #include <vector> 23 24 #include <android-base/file.h> 25 #include <android-base/logging.h> 26 #include <android-base/parseint.h> 27 #include <android-base/strings.h> 28 #include <android-base/unique_fd.h> 29 #include <ziparchive/zip_writer.h> 30 31 #include "command.h" 32 #include "event_type.h" 33 #include "environment.h" 34 #include "utils.h" 35 #include "workload.h" 36 37 namespace { 38 const std::string SIMPLEPERF_DATA_DIR = "simpleperf_data"; 39 40 class PrepareCommand : public Command { 41 public: 42 PrepareCommand() 43 : Command("api-prepare", "Prepare recording via app api", 44 "Usage: simpleperf api-prepare\n" 45 ) {} 46 bool Run(const std::vector<std::string>& args); 47 }; 48 49 bool PrepareCommand::Run(const std::vector<std::string>&) { 50 // Enable profiling. 51 if (!CheckPerfEventLimit()) { 52 return false; 53 } 54 // Create tracepoint_events file. 55 if (!android::base::WriteStringToFile(GetTracepointEvents(), 56 "/data/local/tmp/tracepoint_events")) { 57 PLOG(ERROR) << "failed to write tracepoint_events file"; 58 return false; 59 } 60 return true; 61 } 62 63 class CollectCommand : public Command { 64 public: 65 CollectCommand() 66 : Command("api-collect", "Collect recording data generated by app api", 67 // clang-format off 68 "Usage: simpleperf api-collect [options]\n" 69 "--app <package_name> the android application having recording data\n" 70 "-o record_zipfile_path the path to store recording data\n" 71 " Default is simpleperf_data.zip.\n" 72 #if 0 73 // Below options are only used internally and shouldn't be visible to the public. 74 "--in-app We are already running in the app's context.\n" 75 "--out-fd <fd> Write output to a file descriptor.\n" 76 "--stop-signal-fd <fd> Stop recording when fd is readable.\n" 77 #endif 78 // clang-format on 79 ) {} 80 bool Run(const std::vector<std::string>& args); 81 82 private: 83 bool ParseOptions(const std::vector<std::string>& args); 84 void HandleStopSignal(); 85 bool CollectRecordingData(); 86 bool RemoveRecordingData(); 87 88 std::string app_name_; 89 std::string output_filepath_ = "simpleperf_data.zip"; 90 bool in_app_context_ = false; 91 android::base::unique_fd out_fd_; 92 android::base::unique_fd stop_signal_fd_; 93 }; 94 95 bool CollectCommand::Run(const std::vector<std::string>& args) { 96 if (!ParseOptions(args)) { 97 return false; 98 } 99 if (in_app_context_) { 100 HandleStopSignal(); 101 return CollectRecordingData() && RemoveRecordingData(); 102 } 103 return RunInAppContext(app_name_, Name(), args, 0, output_filepath_, false); 104 } 105 106 bool CollectCommand::ParseOptions(const std::vector<std::string>& args) { 107 for (size_t i = 0; i < args.size(); ++i) { 108 if (args[i] == "--app") { 109 if (!NextArgumentOrError(args, &i)) { 110 return false; 111 } 112 app_name_ = args[i]; 113 } else if (args[i] == "--in-app") { 114 in_app_context_ = true; 115 } else if (args[i] == "-o") { 116 if (!NextArgumentOrError(args, &i)) { 117 return false; 118 } 119 output_filepath_ = args[i]; 120 } else if (args[i] == "--out-fd") { 121 int fd; 122 if (!GetUintOption(args, &i, &fd)) { 123 return false; 124 } 125 out_fd_.reset(fd); 126 } else if (args[i] == "--stop-signal-fd") { 127 int fd; 128 if (!GetUintOption(args, &i, &fd)) { 129 return false; 130 } 131 stop_signal_fd_.reset(fd); 132 } else { 133 ReportUnknownOption(args, i); 134 return false; 135 } 136 } 137 if (!in_app_context_) { 138 if (app_name_.empty()) { 139 LOG(ERROR) << "--app is missing"; 140 return false; 141 } 142 } 143 return true; 144 } 145 146 void CollectCommand::HandleStopSignal() { 147 int fd = stop_signal_fd_.release(); 148 std::thread thread([fd]() { 149 char c; 150 static_cast<void>(read(fd, &c, 1)); 151 exit(1); 152 }); 153 thread.detach(); 154 } 155 156 bool CollectCommand::CollectRecordingData() { 157 std::unique_ptr<FILE, decltype(&fclose)> fp(android::base::Fdopen(std::move(out_fd_), "w"), 158 fclose); 159 if (fp == nullptr) { 160 PLOG(ERROR) << "failed to call fdopen"; 161 return false; 162 } 163 std::vector<char> buffer(64 * 1024); 164 ZipWriter zip_writer(fp.get()); 165 for (const auto& name : GetEntriesInDir(SIMPLEPERF_DATA_DIR)) { 166 // No need to collect temporary files. 167 const std::string path = SIMPLEPERF_DATA_DIR + "/" + name; 168 if (android::base::StartsWith(name, "TemporaryFile-") || !IsRegularFile(path)) { 169 continue; 170 } 171 int result = zip_writer.StartEntry(name.c_str(), ZipWriter::kCompress); 172 if (result != 0) { 173 LOG(ERROR) << "failed to start zip entry " << name << ": " 174 << zip_writer.ErrorCodeString(result); 175 return false; 176 } 177 android::base::unique_fd in_fd(FileHelper::OpenReadOnly(path)); 178 if (in_fd == -1) { 179 PLOG(ERROR) << "failed to open " << path; 180 return false; 181 } 182 while (true) { 183 ssize_t nread = TEMP_FAILURE_RETRY(read(in_fd, buffer.data(), buffer.size())); 184 if (nread < 0) { 185 PLOG(ERROR) << "failed to read " << path; 186 return false; 187 } 188 if (nread == 0) { 189 break; 190 } 191 result = zip_writer.WriteBytes(buffer.data(), nread); 192 if (result != 0) { 193 LOG(ERROR) << "failed to write zip entry " << name << ": " 194 << zip_writer.ErrorCodeString(result); 195 return false; 196 } 197 } 198 result = zip_writer.FinishEntry(); 199 if (result != 0) { 200 LOG(ERROR) << "failed to finish zip entry " << name << ": " 201 << zip_writer.ErrorCodeString(result); 202 return false; 203 } 204 } 205 int result = zip_writer.Finish(); 206 if (result != 0) { 207 LOG(ERROR) << "failed to finish zip writer: " << zip_writer.ErrorCodeString(result); 208 return false; 209 } 210 return true; 211 } 212 213 bool CollectCommand::RemoveRecordingData() { 214 return Workload::RunCmd({"rm", "-rf", SIMPLEPERF_DATA_DIR}); 215 } 216 } // namespace 217 218 void RegisterAPICommands() { 219 RegisterCommand("api-prepare", 220 []{ return std::unique_ptr<Command>(new PrepareCommand()); }); 221 RegisterCommand("api-collect", 222 []{ return std::unique_ptr<Command>(new CollectCommand()); }); 223 } 224