/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include "cmd_api_impl.h" #include "command.h" #include "environment.h" #include "event_type.h" #include "utils.h" #include "workload.h" namespace simpleperf { namespace { const std::string SIMPLEPERF_DATA_DIR = "simpleperf_data"; class PrepareCommand : public Command { public: PrepareCommand() : Command("api-prepare", "Prepare recording via app api", "Usage: simpleperf api-prepare\n") { } bool Run(const std::vector& args); }; bool PrepareCommand::Run(const std::vector&) { // Enable profiling. if (!CheckPerfEventLimit()) { return false; } // Create tracepoint_events file. return EventTypeManager::Instance().WriteTracepointsToFile("/data/local/tmp/tracepoint_events"); } class CollectCommand : public Command { public: CollectCommand() : Command("api-collect", "Collect recording data generated by app api", // clang-format off "Usage: simpleperf api-collect [options]\n" "--app the android application having recording data\n" "-o record_zipfile_path the path to store recording data\n" " Default is simpleperf_data.zip.\n" #if 0 // Below options are only used internally and shouldn't be visible to the public. "--in-app We are already running in the app's context.\n" "--out-fd Write output to a file descriptor.\n" "--stop-signal-fd Stop recording when fd is readable.\n" #endif // clang-format on ) { } bool Run(const std::vector& args); private: bool ParseOptions(const std::vector& args); void HandleStopSignal(); bool CollectRecordingData(); bool RemoveRecordingData(); std::string app_name_; std::string output_filepath_ = "simpleperf_data.zip"; bool in_app_context_ = false; android::base::unique_fd out_fd_; android::base::unique_fd stop_signal_fd_; }; bool CollectCommand::Run(const std::vector& args) { if (!ParseOptions(args)) { return false; } if (in_app_context_) { HandleStopSignal(); return CollectRecordingData() && RemoveRecordingData(); } return RunInAppContext(app_name_, Name(), args, 0, output_filepath_, false); } bool CollectCommand::ParseOptions(const std::vector& args) { OptionValueMap options; std::vector> ordered_options; if (!PreprocessOptions(args, GetApiCollectCmdOptionFormats(), &options, &ordered_options, nullptr)) { return false; } if (auto value = options.PullValue("--app"); value) { app_name_ = *value->str_value; } in_app_context_ = options.PullBoolValue("--in-app"); if (auto value = options.PullValue("-o"); value) { output_filepath_ = *value->str_value; } if (auto value = options.PullValue("--out-fd"); value) { out_fd_.reset(static_cast(value->uint_value)); } if (auto value = options.PullValue("--stop-signal-fd"); value) { stop_signal_fd_.reset(static_cast(value->uint_value)); } CHECK(options.values.empty()); CHECK(ordered_options.empty()); if (!in_app_context_) { if (app_name_.empty()) { LOG(ERROR) << "--app is missing"; return false; } } return true; } void CollectCommand::HandleStopSignal() { int fd = stop_signal_fd_.release(); std::thread thread([fd]() { char c; static_cast(read(fd, &c, 1)); exit(1); }); thread.detach(); } bool CollectCommand::CollectRecordingData() { std::unique_ptr fp(android::base::Fdopen(std::move(out_fd_), "w"), fclose); if (fp == nullptr) { PLOG(ERROR) << "failed to call fdopen"; return false; } std::vector buffer(64 * 1024); ZipWriter zip_writer(fp.get()); for (const auto& name : GetEntriesInDir(SIMPLEPERF_DATA_DIR)) { // No need to collect temporary files. const std::string path = SIMPLEPERF_DATA_DIR + "/" + name; if (android::base::StartsWith(name, "TemporaryFile-") || !IsRegularFile(path)) { continue; } int result = zip_writer.StartEntry(name.c_str(), ZipWriter::kCompress); if (result != 0) { LOG(ERROR) << "failed to start zip entry " << name << ": " << zip_writer.ErrorCodeString(result); return false; } android::base::unique_fd in_fd(FileHelper::OpenReadOnly(path)); if (in_fd == -1) { PLOG(ERROR) << "failed to open " << path; return false; } while (true) { ssize_t nread = TEMP_FAILURE_RETRY(read(in_fd, buffer.data(), buffer.size())); if (nread < 0) { PLOG(ERROR) << "failed to read " << path; return false; } if (nread == 0) { break; } result = zip_writer.WriteBytes(buffer.data(), nread); if (result != 0) { LOG(ERROR) << "failed to write zip entry " << name << ": " << zip_writer.ErrorCodeString(result); return false; } } result = zip_writer.FinishEntry(); if (result != 0) { LOG(ERROR) << "failed to finish zip entry " << name << ": " << zip_writer.ErrorCodeString(result); return false; } } int result = zip_writer.Finish(); if (result != 0) { LOG(ERROR) << "failed to finish zip writer: " << zip_writer.ErrorCodeString(result); return false; } return true; } bool CollectCommand::RemoveRecordingData() { return Workload::RunCmd({"rm", "-rf", SIMPLEPERF_DATA_DIR}); } } // namespace void RegisterAPICommands() { RegisterCommand("api-prepare", [] { return std::unique_ptr(new PrepareCommand()); }); RegisterCommand("api-collect", [] { return std::unique_ptr(new CollectCommand()); }); } } // namespace simpleperf