simpleperf.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  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. #include "simpleperf.h"
  17. #include <limits.h>
  18. #include <stdarg.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <sys/socket.h>
  22. #include <sys/stat.h>
  23. #include <sys/wait.h>
  24. #include <time.h>
  25. #include <unistd.h>
  26. #include <mutex>
  27. #include <sstream>
  28. #include <android/log.h>
  29. namespace simpleperf {
  30. enum RecordCmd {
  31. CMD_PAUSE_RECORDING = 1,
  32. CMD_RESUME_RECORDING,
  33. };
  34. class RecordOptionsImpl {
  35. public:
  36. std::string output_filename;
  37. std::string event = "cpu-cycles";
  38. size_t freq = 4000;
  39. double duration_in_second = 0.0;
  40. std::vector<pid_t> threads;
  41. bool dwarf_callgraph = false;
  42. bool fp_callgraph = false;
  43. bool trace_offcpu = false;
  44. };
  45. RecordOptions::RecordOptions() : impl_(new RecordOptionsImpl) {
  46. }
  47. RecordOptions::~RecordOptions() {
  48. delete impl_;
  49. }
  50. RecordOptions& RecordOptions::SetOutputFilename(const std::string &filename) {
  51. impl_->output_filename = filename;
  52. return *this;
  53. }
  54. RecordOptions& RecordOptions::SetEvent(const std::string &event) {
  55. impl_->event = event;
  56. return *this;
  57. }
  58. RecordOptions& RecordOptions::SetSampleFrequency(size_t freq) {
  59. impl_->freq = freq;
  60. return *this;
  61. }
  62. RecordOptions& RecordOptions::SetDuration(double duration_in_second) {
  63. impl_->duration_in_second = duration_in_second;
  64. return *this;
  65. }
  66. RecordOptions& RecordOptions::SetSampleThreads(const std::vector<pid_t> &threads) {
  67. impl_->threads = threads;
  68. return *this;
  69. }
  70. RecordOptions& RecordOptions::RecordDwarfCallGraph() {
  71. impl_->dwarf_callgraph = true;
  72. impl_->fp_callgraph = false;
  73. return *this;
  74. }
  75. RecordOptions& RecordOptions::RecordFramePointerCallGraph() {
  76. impl_->fp_callgraph = true;
  77. impl_->dwarf_callgraph = false;
  78. return *this;
  79. }
  80. RecordOptions& RecordOptions::TraceOffCpu() {
  81. impl_->trace_offcpu = true;
  82. return *this;
  83. }
  84. static std::string GetDefaultOutputFilename() {
  85. time_t t = time(nullptr);
  86. struct tm tm;
  87. if (localtime_r(&t, &tm) != &tm) {
  88. return "perf.data";
  89. }
  90. char* buf = nullptr;
  91. asprintf(&buf, "perf-%02d-%02d-%02d-%02d-%02d.data", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
  92. tm.tm_min, tm.tm_sec);
  93. std::string result = buf;
  94. free(buf);
  95. return result;
  96. }
  97. std::vector<std::string> RecordOptions::ToRecordArgs() const {
  98. std::vector<std::string> args;
  99. std::string output_filename = impl_->output_filename;
  100. if (output_filename.empty()) {
  101. output_filename = GetDefaultOutputFilename();
  102. }
  103. args.insert(args.end(), {"-o", output_filename});
  104. args.insert(args.end(), {"-e", impl_->event});
  105. args.insert(args.end(), {"-f", std::to_string(impl_->freq)});
  106. if (impl_->duration_in_second != 0.0) {
  107. args.insert(args.end(), {"--duration", std::to_string(impl_->duration_in_second)});
  108. }
  109. if (impl_->threads.empty()) {
  110. args.insert(args.end(), {"-p", std::to_string(getpid())});
  111. } else {
  112. std::ostringstream os;
  113. os << *(impl_->threads.begin());
  114. for (auto it = std::next(impl_->threads.begin()); it != impl_->threads.end(); ++it) {
  115. os << "," << *it;
  116. }
  117. args.insert(args.end(), {"-t", os.str()});
  118. }
  119. if (impl_->dwarf_callgraph) {
  120. args.push_back("-g");
  121. } else if (impl_->fp_callgraph) {
  122. args.insert(args.end(), {"--call-graph", "fp"});
  123. }
  124. if (impl_->trace_offcpu) {
  125. args.push_back("--trace-offcpu");
  126. }
  127. return args;
  128. }
  129. static void Abort(const char* fmt, ...) {
  130. va_list vl;
  131. va_start(vl, fmt);
  132. __android_log_vprint(ANDROID_LOG_FATAL, "simpleperf", fmt, vl);
  133. va_end(vl);
  134. abort();
  135. }
  136. class ProfileSessionImpl {
  137. public:
  138. ProfileSessionImpl(const std::string& app_data_dir)
  139. : app_data_dir_(app_data_dir),
  140. simpleperf_data_dir_(app_data_dir + "/simpleperf_data") {}
  141. ~ProfileSessionImpl();
  142. void StartRecording(const std::vector<std::string>& args);
  143. void PauseRecording();
  144. void ResumeRecording();
  145. void StopRecording();
  146. private:
  147. std::string FindSimpleperf();
  148. std::string FindSimpleperfInTempDir();
  149. void CheckIfPerfEnabled();
  150. void CreateSimpleperfDataDir();
  151. void CreateSimpleperfProcess(const std::string& simpleperf_path,
  152. const std::vector<std::string>& record_args);
  153. void SendCmd(const std::string& cmd);
  154. std::string ReadReply();
  155. enum State {
  156. NOT_YET_STARTED,
  157. STARTED,
  158. PAUSED,
  159. STOPPED,
  160. };
  161. const std::string app_data_dir_;
  162. const std::string simpleperf_data_dir_;
  163. std::mutex lock_; // Protect all members below.
  164. State state_ = NOT_YET_STARTED;
  165. pid_t simpleperf_pid_ = -1;
  166. int control_fd_ = -1;
  167. int reply_fd_ = -1;
  168. };
  169. ProfileSessionImpl::~ProfileSessionImpl() {
  170. if (control_fd_ != -1) {
  171. close(control_fd_);
  172. }
  173. if (reply_fd_ != -1) {
  174. close(reply_fd_);
  175. }
  176. }
  177. void ProfileSessionImpl::StartRecording(const std::vector<std::string> &args) {
  178. std::lock_guard<std::mutex> guard(lock_);
  179. if (state_ != NOT_YET_STARTED) {
  180. Abort("startRecording: session in wrong state %d", state_);
  181. }
  182. std::string simpleperf_path = FindSimpleperf();
  183. CheckIfPerfEnabled();
  184. CreateSimpleperfDataDir();
  185. CreateSimpleperfProcess(simpleperf_path, args);
  186. state_ = STARTED;
  187. }
  188. void ProfileSessionImpl::PauseRecording() {
  189. std::lock_guard<std::mutex> guard(lock_);
  190. if (state_ != STARTED) {
  191. Abort("pauseRecording: session in wrong state %d", state_);
  192. }
  193. SendCmd("pause");
  194. state_ = PAUSED;
  195. }
  196. void ProfileSessionImpl::ResumeRecording() {
  197. std::lock_guard<std::mutex> guard(lock_);
  198. if (state_ != PAUSED) {
  199. Abort("resumeRecording: session in wrong state %d", state_);
  200. }
  201. SendCmd("resume");
  202. state_ = STARTED;
  203. }
  204. void ProfileSessionImpl::StopRecording() {
  205. std::lock_guard<std::mutex> guard(lock_);
  206. if (state_ != STARTED && state_ != PAUSED) {
  207. Abort("stopRecording: session in wrong state %d", state_);
  208. }
  209. // Send SIGINT to simpleperf to stop recording.
  210. if (kill(simpleperf_pid_, SIGINT) == -1) {
  211. Abort("failed to stop simpleperf: %s", strerror(errno));
  212. }
  213. int status;
  214. pid_t result = TEMP_FAILURE_RETRY(waitpid(simpleperf_pid_, &status, 0));
  215. if (result == -1) {
  216. Abort("failed to call waitpid: %s", strerror(errno));
  217. }
  218. if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
  219. Abort("simpleperf exited with error, status = 0x%x", status);
  220. }
  221. state_ = STOPPED;
  222. }
  223. void ProfileSessionImpl::SendCmd(const std::string& cmd) {
  224. std::string data = cmd + "\n";
  225. if (TEMP_FAILURE_RETRY(write(control_fd_, &data[0], data.size())) !=
  226. static_cast<ssize_t>(data.size())) {
  227. Abort("failed to send cmd to simpleperf: %s", strerror(errno));
  228. }
  229. if (ReadReply() != "ok") {
  230. Abort("failed to run cmd in simpleperf: %s", cmd.c_str());
  231. }
  232. }
  233. static bool IsExecutableFile(const std::string& path) {
  234. struct stat st;
  235. if (stat(path.c_str(), &st) == 0) {
  236. if (S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR)) {
  237. return true;
  238. }
  239. }
  240. return false;
  241. }
  242. std::string ProfileSessionImpl::FindSimpleperf() {
  243. // 1. Try /data/local/tmp/simpleperf first. Probably it's newer than /system/bin/simpleperf.
  244. std::string simpleperf_path = FindSimpleperfInTempDir();
  245. if (!simpleperf_path.empty()) {
  246. return simpleperf_path;
  247. }
  248. // 2. Try /system/bin/simpleperf, which is available on Android >= Q.
  249. simpleperf_path = "/system/bin/simpleperf";
  250. if (IsExecutableFile(simpleperf_path)) {
  251. return simpleperf_path;
  252. }
  253. Abort("can't find simpleperf on device. Please run api_profiler.py.");
  254. return "";
  255. }
  256. std::string ProfileSessionImpl::FindSimpleperfInTempDir() {
  257. const std::string path = "/data/local/tmp/simpleperf";
  258. if (!IsExecutableFile(path)) {
  259. return "";
  260. }
  261. // Copy it to app_dir to execute it.
  262. const std::string to_path = app_data_dir_ + "/simpleperf";
  263. const std::string copy_cmd = "cp " + path + " " + to_path;
  264. if (system(copy_cmd.c_str()) != 0) {
  265. return "";
  266. }
  267. const std::string test_cmd = to_path;
  268. // For apps with target sdk >= 29, executing app data file isn't allowed. So test executing it.
  269. if (system(test_cmd.c_str()) != 0) {
  270. return "";
  271. }
  272. return to_path;
  273. }
  274. static std::string ReadFile(FILE* fp) {
  275. std::string s;
  276. char buf[200];
  277. while (true) {
  278. ssize_t n = fread(buf, 1, sizeof(buf), fp);
  279. if (n <= 0) {
  280. break;
  281. }
  282. s.insert(s.end(), buf, buf + n);
  283. }
  284. return s;
  285. }
  286. void ProfileSessionImpl::CheckIfPerfEnabled() {
  287. FILE* fp = popen("/system/bin/getprop security.perf_harden", "re");
  288. if (fp == nullptr) {
  289. return; // Omit check if getprop doesn't exist.
  290. }
  291. std::string s = ReadFile(fp);
  292. pclose(fp);
  293. if (!s.empty() && s[0] == '1') {
  294. Abort("linux perf events aren't enabled on the device. Please run api_profiler.py.");
  295. }
  296. }
  297. void ProfileSessionImpl::CreateSimpleperfDataDir() {
  298. struct stat st;
  299. if (stat(simpleperf_data_dir_.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
  300. return;
  301. }
  302. if (mkdir(simpleperf_data_dir_.c_str(), 0700) == -1) {
  303. Abort("failed to create simpleperf data dir %s: %s", simpleperf_data_dir_.c_str(),
  304. strerror(errno));
  305. }
  306. }
  307. void ProfileSessionImpl::CreateSimpleperfProcess(const std::string &simpleperf_path,
  308. const std::vector<std::string> &record_args) {
  309. // 1. Create control/reply pips.
  310. int control_fd[2];
  311. int reply_fd[2];
  312. if (pipe(control_fd) != 0 || pipe(reply_fd) != 0) {
  313. Abort("failed to call pipe: %s", strerror(errno));
  314. }
  315. // 2. Prepare simpleperf arguments.
  316. std::vector<std::string> args;
  317. args.emplace_back(simpleperf_path);
  318. args.emplace_back("record");
  319. args.emplace_back("--log-to-android-buffer");
  320. args.insert(args.end(), {"--log", "debug"});
  321. args.emplace_back("--stdio-controls-profiling");
  322. args.emplace_back("--in-app");
  323. args.insert(args.end(), {"--tracepoint-events", "/data/local/tmp/tracepoint_events"});
  324. args.insert(args.end(), record_args.begin(), record_args.end());
  325. char* argv[args.size() + 1];
  326. for (size_t i = 0; i < args.size(); ++i) {
  327. argv[i] = &args[i][0];
  328. }
  329. argv[args.size()] = nullptr;
  330. // 3. Start simpleperf process.
  331. int pid = fork();
  332. if (pid == -1) {
  333. Abort("failed to fork: %s", strerror(errno));
  334. }
  335. if (pid == 0) {
  336. // child process
  337. close(control_fd[1]);
  338. dup2(control_fd[0], 0); // simpleperf read control cmd from fd 0.
  339. close(control_fd[0]);
  340. close(reply_fd[0]);
  341. dup2(reply_fd[1], 1); // simpleperf writes reply to fd 1.
  342. close(reply_fd[0]);
  343. chdir(simpleperf_data_dir_.c_str());
  344. execvp(argv[0], argv);
  345. Abort("failed to call exec: %s", strerror(errno));
  346. }
  347. // parent process
  348. close(control_fd[0]);
  349. control_fd_ = control_fd[1];
  350. close(reply_fd[1]);
  351. reply_fd_ = reply_fd[0];
  352. simpleperf_pid_ = pid;
  353. // 4. Wait until simpleperf starts recording.
  354. std::string start_flag = ReadReply();
  355. if (start_flag != "started") {
  356. Abort("failed to receive simpleperf start flag");
  357. }
  358. }
  359. std::string ProfileSessionImpl::ReadReply() {
  360. std::string s;
  361. while (true) {
  362. char c;
  363. ssize_t result = TEMP_FAILURE_RETRY(read(reply_fd_, &c, 1));
  364. if (result <= 0 || c == '\n') {
  365. break;
  366. }
  367. s.push_back(c);
  368. }
  369. return s;
  370. }
  371. ProfileSession::ProfileSession() {
  372. FILE* fp = fopen("/proc/self/cmdline", "r");
  373. if (fp == nullptr) {
  374. Abort("failed to open /proc/self/cmdline: %s", strerror(errno));
  375. }
  376. std::string s = ReadFile(fp);
  377. fclose(fp);
  378. for (int i = 0; i < s.size(); i++) {
  379. if (s[i] == '\0') {
  380. s = s.substr(0, i);
  381. break;
  382. }
  383. }
  384. std::string app_data_dir = "/data/data/" + s;
  385. impl_ = new ProfileSessionImpl(app_data_dir);
  386. }
  387. ProfileSession::ProfileSession(const std::string& app_data_dir)
  388. : impl_(new ProfileSessionImpl(app_data_dir)) {}
  389. ProfileSession::~ProfileSession() {
  390. delete impl_;
  391. }
  392. void ProfileSession::StartRecording(const RecordOptions &options) {
  393. StartRecording(options.ToRecordArgs());
  394. }
  395. void ProfileSession::StartRecording(const std::vector<std::string> &record_args) {
  396. impl_->StartRecording(record_args);
  397. }
  398. void ProfileSession::PauseRecording() {
  399. impl_->PauseRecording();
  400. }
  401. void ProfileSession::ResumeRecording() {
  402. impl_->ResumeRecording();
  403. }
  404. void ProfileSession::StopRecording() {
  405. impl_->StopRecording();
  406. }
  407. } // namespace simpleperf