diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2018-03-30 02:56:08 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-03-30 02:56:08 +0000 |
commit | 09ad261e387886214bb2fae25ac6d3ace18d898b (patch) | |
tree | b575aeed51c6ab0c75ac85bd72414d18eb7389eb | |
parent | cd3c54452bd0fab8214f8915313c83ac46d9c697 (diff) | |
parent | 7f85bff629e68efd3f5127fc43edddcadfecb007 (diff) | |
download | extras-09ad261e387886214bb2fae25ac6d3ace18d898b.tar.gz |
Merge changes I3415d7c1,I0931a59c,Iae2a74f8,I2cad55f2
* changes:
Perfprofd: Move dropbox functionality into core
Perfprofd: Move proto to its own static library
Perfprofd: Factor out the command-line-based loop
Perfprofd: Move dropbox code to its own static library
-rw-r--r-- | perfprofd/Android.bp | 45 | ||||
-rw-r--r-- | perfprofd/binder_interface/Android.bp | 3 | ||||
-rw-r--r-- | perfprofd/binder_interface/perfprofd_binder.cc | 82 | ||||
-rw-r--r-- | perfprofd/config.h | 3 | ||||
-rw-r--r-- | perfprofd/configreader.cc | 4 | ||||
-rw-r--r-- | perfprofd/dropbox/Android.bp | 52 | ||||
-rw-r--r-- | perfprofd/dropbox/dropbox.cc | 129 | ||||
-rw-r--r-- | perfprofd/dropbox/dropbox.h | 37 | ||||
-rw-r--r-- | perfprofd/dropbox/dropbox_host.cc | 35 | ||||
-rw-r--r-- | perfprofd/perfprofd_cmdline.cc | 255 | ||||
-rw-r--r-- | perfprofd/perfprofd_cmdline.h | 39 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.cc | 244 | ||||
-rw-r--r-- | perfprofd/perfprofdcore.h | 21 | ||||
-rw-r--r-- | perfprofd/perfprofdmain.cc | 1 | ||||
-rw-r--r-- | perfprofd/tests/Android.bp | 5 | ||||
-rw-r--r-- | perfprofd/tests/perfprofd_test.cc | 1 |
16 files changed, 633 insertions, 323 deletions
diff --git a/perfprofd/Android.bp b/perfprofd/Android.bp index 9b55fbda..243477a3 100644 --- a/perfprofd/Android.bp +++ b/perfprofd/Android.bp @@ -66,6 +66,41 @@ cc_defaults { } } +// Static library for the record proto and its I/O. + +cc_library_static { + name: "libperfprofd_record_proto", + defaults: [ + "perfprofd_defaults", + ], + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, + + static_libs: [ + "libbase", + "libprotobuf-cpp-lite", + "libquipper", + "libz", + ], + srcs: [ + "perfprofd_io.cc", + "perfprofd_record.proto", + ], + + proto: { + export_proto_headers: true, + include_dirs: ["external/perf_data_converter/src/quipper"], + type: "lite", + }, + + export_include_dirs: ["."], // Really only the -fwd.h. + export_static_lib_headers: ["libquipper"], +} + // // Static library containing guts of AWP daemon. // @@ -87,23 +122,19 @@ cc_defaults { "libsimpleperf_elf_read", ], whole_static_libs: [ + "libperfprofd_dropbox", + "libperfprofd_record_proto", "libquipper", ], srcs: [ - "perfprofd_record.proto", "perf_data_converter.cc", "configreader.cc", "cpuconfig.cc", "perfprofdcore.cc", - "perfprofd_io.cc", + "perfprofd_cmdline.cc", "symbolizer.cc" ], - proto: { - export_proto_headers: true, - include_dirs: ["external/perf_data_converter/src/quipper"], - type: "lite", - }, cflags: [ "-Wno-gnu-anonymous-struct", ], diff --git a/perfprofd/binder_interface/Android.bp b/perfprofd/binder_interface/Android.bp index d7bff41b..c40036bd 100644 --- a/perfprofd/binder_interface/Android.bp +++ b/perfprofd/binder_interface/Android.bp @@ -30,9 +30,6 @@ cc_library_static { "libperfprofdcore", "libprotobuf-cpp-lite", ], - shared_libs: [ - "libservices", - ], srcs: [ "perfprofd_binder.cc", ":perfprofd_aidl", diff --git a/perfprofd/binder_interface/perfprofd_binder.cc b/perfprofd/binder_interface/perfprofd_binder.cc index eeb53602..87e0c5f6 100644 --- a/perfprofd/binder_interface/perfprofd_binder.cc +++ b/perfprofd/binder_interface/perfprofd_binder.cc @@ -35,7 +35,6 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> -#include <android/os/DropBoxManager.h> #include <binder/BinderService.h> #include <binder/IResultReceiver.h> #include <binder/Status.h> @@ -49,6 +48,7 @@ #include "perfprofd_record.pb.h" #include "config.h" +#include "dropbox.h" #include "perfprofdcore.h" #include "perfprofd_io.h" @@ -60,8 +60,6 @@ using Status = ::android::binder::Status; class BinderConfig : public Config { public: - bool send_to_dropbox = false; - bool is_profiling = false; void Sleep(size_t seconds) override { @@ -97,8 +95,6 @@ class BinderConfig : public Config { // Copy base fields. *static_cast<Config*>(this) = static_cast<const Config&>(rhs); - send_to_dropbox = rhs.send_to_dropbox; - return *this; } @@ -151,79 +147,21 @@ class PerfProfdNativeService : public BinderService<PerfProfdNativeService>, int seq_ = 0; }; -static Status WriteDropboxFile(android::perfprofd::PerfprofdRecord* encodedProfile, - Config* config) { - android::base::unique_fd tmp_fd; - { - char path[PATH_MAX]; - snprintf(path, - sizeof(path), - "%s%cdropboxtmp-XXXXXX", - config->destination_directory.c_str(), - OS_PATH_SEPARATOR); - tmp_fd.reset(mkstemp(path)); - if (tmp_fd.get() == -1) { - PLOG(ERROR) << "Could not create temp file " << path; - return Status::fromExceptionCode(1, "Could not create temp file"); - } - if (unlink(path) != 0) { - PLOG(WARNING) << "Could not unlink binder temp file"; - } - } - - // Dropbox takes ownership of the fd, and if it is not readonly, - // a selinux violation will occur. Get a read-only version. - android::base::unique_fd read_only; - { - char fdpath[64]; - snprintf(fdpath, arraysize(fdpath), "/proc/self/fd/%d", tmp_fd.get()); - read_only.reset(open(fdpath, O_RDONLY | O_CLOEXEC)); - if (read_only.get() < 0) { - PLOG(ERROR) << "Could not create read-only fd"; - return Status::fromExceptionCode(1, "Could not create read-only fd"); - } - } - - constexpr bool kCompress = true; // Ignore the config here. Dropbox will always end up - // compressing the data, might as well make the temp - // file smaller and help it out. - using DropBoxManager = android::os::DropBoxManager; - constexpr int kDropboxFlags = DropBoxManager::IS_GZIPPED; - - if (!SerializeProtobuf(encodedProfile, std::move(tmp_fd), kCompress)) { - return Status::fromExceptionCode(1, "Could not serialize to temp file"); - } - - sp<DropBoxManager> dropbox(new DropBoxManager()); - return dropbox->addFile(String16("perfprofd"), read_only.release(), kDropboxFlags); -} - bool PerfProfdNativeService::BinderHandler( android::perfprofd::PerfprofdRecord* encodedProfile, Config* config) { CHECK(config != nullptr); + if (encodedProfile == nullptr) { + return false; + } + if (static_cast<BinderConfig*>(config)->send_to_dropbox) { - size_t size = encodedProfile->ByteSize(); - Status status; - if (size < 1024 * 1024) { - // For a small size, send as a byte buffer directly. - std::unique_ptr<uint8_t[]> data(new uint8_t[size]); - encodedProfile->SerializeWithCachedSizesToArray(data.get()); - - using DropBoxManager = android::os::DropBoxManager; - sp<DropBoxManager> dropbox(new DropBoxManager()); - status = dropbox->addData(String16("perfprofd"), - data.get(), - size, - 0); - } else { - // For larger buffers, we need to go through the filesystem. - status = WriteDropboxFile(encodedProfile, config); + std::string error_msg; + if (!dropbox::SendToDropbox(encodedProfile, config->destination_directory, &error_msg)) { + LOG(WARNING) << "Failed dropbox submission: " << error_msg; + return false; } - if (!status.isOk()) { - LOG(WARNING) << "Failed dropbox submission: " << status.toString8(); - } - return status.isOk(); + return true; } if (encodedProfile == nullptr) { diff --git a/perfprofd/config.h b/perfprofd/config.h index 4c1f12b1..774f7e86 100644 --- a/perfprofd/config.h +++ b/perfprofd/config.h @@ -96,6 +96,9 @@ struct Config { // If true, use libz to compress the output proto. bool compress = true; + // If true, send the proto to dropbox instead to a file. + bool send_to_dropbox = false; + // Sleep for the given number of seconds. virtual void Sleep(size_t seconds) = 0; diff --git a/perfprofd/configreader.cc b/perfprofd/configreader.cc index f7d6fd29..d3396b33 100644 --- a/perfprofd/configreader.cc +++ b/perfprofd/configreader.cc @@ -130,6 +130,9 @@ void ConfigReader::addDefaultEntries() // If true, use libz to compress the output proto. addUnsignedEntry("compress", 0, 0, 1); + + // If true, send the proto to dropbox instead to a file. + addUnsignedEntry("dropbox", 0, 0, 1); } void ConfigReader::addUnsignedEntry(const char *key, @@ -329,4 +332,5 @@ void ConfigReader::FillConfig(Config* config) { config->process = -1; config->use_elf_symbolizer = getBoolValue("use_elf_symbolizer"); config->compress = getBoolValue("compress"); + config->send_to_dropbox = getBoolValue("dropbox"); } diff --git a/perfprofd/dropbox/Android.bp b/perfprofd/dropbox/Android.bp new file mode 100644 index 00000000..c3b8f3b8 --- /dev/null +++ b/perfprofd/dropbox/Android.bp @@ -0,0 +1,52 @@ +// +// Copyright (C) 2018 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. +// + +// +// Static library for dropbox submission. +// +cc_library_static { + name: "libperfprofd_dropbox", + defaults: [ + "perfprofd_defaults", + ], + host_supported: true, + + export_include_dirs: ["."], + static_libs: [ + "libbase", + "libperfprofd_record_proto", + "libprotobuf-cpp-lite", + ], + target: { + android: { + srcs: [ + "dropbox.cc", + ], + static_libs: [ + "libbinder", + "libutils", + ], + shared_libs: [ + "libservices", + ], + }, + host: { + srcs: [ + "dropbox_host.cc", + ], + }, + }, +} diff --git a/perfprofd/dropbox/dropbox.cc b/perfprofd/dropbox/dropbox.cc new file mode 100644 index 00000000..2b1dc2ef --- /dev/null +++ b/perfprofd/dropbox/dropbox.cc @@ -0,0 +1,129 @@ +/* + * + * Copyright 2017, 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 "dropbox.h" + +#include <cstdio> +#include <cstdlib> +#include <memory> + +#include <inttypes.h> +#include <unistd.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <android/os/DropBoxManager.h> +#include <binder/Status.h> +#include <utils/String8.h> + +#include "perfprofd_record.pb.h" + +#include "perfprofd_io.h" + +namespace android { +namespace perfprofd { +namespace dropbox { + +namespace { + +bool WriteDropboxFile(android::perfprofd::PerfprofdRecord* encodedProfile, + const std::string& temp_dir, + std::string* error_msg) { + android::base::unique_fd tmp_fd; + { + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/dropboxtmp-XXXXXX", temp_dir.c_str()); + tmp_fd.reset(mkstemp(path)); + if (tmp_fd.get() == -1) { + *error_msg = android::base::StringPrintf("Could not create temp file %s: %s", + path, + strerror(errno)); + return false; + } + if (unlink(path) != 0) { + PLOG(WARNING) << "Could not unlink binder temp file"; + } + } + + // Dropbox takes ownership of the fd, and if it is not readonly, + // a selinux violation will occur. Get a read-only version. + android::base::unique_fd read_only; + { + char fdpath[64]; + snprintf(fdpath, arraysize(fdpath), "/proc/self/fd/%d", tmp_fd.get()); + read_only.reset(open(fdpath, O_RDONLY | O_CLOEXEC)); + if (read_only.get() < 0) { + *error_msg = android::base::StringPrintf("Could not create read-only fd: %s", + strerror(errno)); + return false; + } + } + + constexpr bool kCompress = true; // Ignore the config here. Dropbox will always end up + // compressing the data, might as well make the temp + // file smaller and help it out. + using DropBoxManager = android::os::DropBoxManager; + constexpr int kDropboxFlags = DropBoxManager::IS_GZIPPED; + + if (!SerializeProtobuf(encodedProfile, std::move(tmp_fd), kCompress)) { + *error_msg = "Could not serialize to temp file"; + return false; + } + + sp<DropBoxManager> dropbox(new DropBoxManager()); + android::binder::Status status = dropbox->addFile(String16("perfprofd"), + read_only.release(), + kDropboxFlags); + if (!status.isOk()) { + *error_msg = status.toString8(); + return false; + } + return true; +} + +} // namespace + +bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, + const std::string& temp_directory, + std::string* error_msg) { + size_t size = profile->ByteSize(); + if (size < 1024 * 1024) { + // For a small size, send as a byte buffer directly. + std::unique_ptr<uint8_t[]> data(new uint8_t[size]); + profile->SerializeWithCachedSizesToArray(data.get()); + + using DropBoxManager = android::os::DropBoxManager; + sp<DropBoxManager> dropbox(new DropBoxManager()); + android::binder::Status status = dropbox->addData(String16("perfprofd"), + data.get(), + size, + 0); + if (!status.isOk()) { + *error_msg = status.toString8(); + return false; + } + return true; + } else { + // For larger buffers, we need to go through the filesystem. + return WriteDropboxFile(profile, temp_directory, error_msg); + } +} + +} // namespace dropbox +} // namespace perfprofd +} // namespace android diff --git a/perfprofd/dropbox/dropbox.h b/perfprofd/dropbox/dropbox.h new file mode 100644 index 00000000..b25d2cc2 --- /dev/null +++ b/perfprofd/dropbox/dropbox.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2018, 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. + */ + +#ifndef SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ +#define SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ + +#include <string> + +#include "perfprofd_record-fwd.h" + +namespace android { +namespace perfprofd { +namespace dropbox { + +bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, + const std::string& temp_directory, + std::string* error_msg); + +} // namespace dropbox +} // namespace perfprofd +} // namespace android + +#endif // SYSTEM_EXTRAS_PERFPROFD_DROPBOX_DROPBOX_H_ diff --git a/perfprofd/dropbox/dropbox_host.cc b/perfprofd/dropbox/dropbox_host.cc new file mode 100644 index 00000000..5c08aa85 --- /dev/null +++ b/perfprofd/dropbox/dropbox_host.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2018, 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 "dropbox.h" + +#include <android-base/macros.h> + +namespace android { +namespace perfprofd { +namespace dropbox { + +bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile, + const std::string& temp_directory ATTRIBUTE_UNUSED, + std::string* error_msg) { + *error_msg = "Dropbox not supported on host"; + return false; +} + +} // namespace dropbox +} // namespace perfprofd +} // namespace android diff --git a/perfprofd/perfprofd_cmdline.cc b/perfprofd/perfprofd_cmdline.cc new file mode 100644 index 00000000..fb9c2c17 --- /dev/null +++ b/perfprofd/perfprofd_cmdline.cc @@ -0,0 +1,255 @@ +/* + * + * Copyright 2015, 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 "perfprofd_cmdline.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <set> +#include <string> + +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/stringprintf.h> + +#include "perfprofd_record.pb.h" + +#include "configreader.h" +#include "dropbox.h" +#include "perfprofdcore.h" +#include "perfprofd_io.h" + +// +// Perf profiling daemon -- collects system-wide profiles using +// +// simpleperf record -a +// +// and encodes them so that they can be uploaded by a separate service. +// + +// + +// +// Output file from 'perf record'. +// +#define PERF_OUTPUT "perf.data" + +// +// Path to the perf file to convert and exit? Empty value is the default, daemon mode. +// +static std::string perf_file_to_convert = ""; + +// +// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it +// out of a sleep() call so as to trigger a new collection (debugging) +// +static void sig_hup(int /* signum */) +{ + LOG(WARNING) << "SIGHUP received"; +} + +// +// Parse command line args. Currently supported flags: +// * "-c PATH" sets the path of the config file to PATH. +// * "-x PATH" reads PATH as a perf data file and saves it as a file in +// perf_profile.proto format. ".encoded" suffix is appended to PATH to form +// the output file path. +// +static void parse_args(int argc, char** argv) +{ + int ac; + + for (ac = 1; ac < argc; ++ac) { + if (!strcmp(argv[ac], "-c")) { + if (ac >= argc-1) { + LOG(ERROR) << "malformed command line: -c option requires argument)"; + continue; + } + ConfigReader::setConfigFilePath(argv[ac+1]); + ++ac; + } else if (!strcmp(argv[ac], "-x")) { + if (ac >= argc-1) { + LOG(ERROR) << "malformed command line: -x option requires argument)"; + continue; + } + perf_file_to_convert = argv[ac+1]; + ++ac; + } else { + LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; + continue; + } + } +} + +// +// Post-processes after profile is collected and converted to protobuf. +// * GMS core stores processed file sequence numbers in +// /data/data/com.google.android.gms/files/perfprofd_processed.txt +// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence +// numbers that have been processed and append the current seq number +// Returns true if the current_seq should increment. +// +static bool post_process(const Config& config, int current_seq) +{ + const std::string& dest_dir = config.destination_directory; + std::string processed_file_path = + config.config_directory + "/" + PROCESSED_FILENAME; + std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; + + + std::set<int> processed; + FILE *fp = fopen(processed_file_path.c_str(), "r"); + if (fp != NULL) { + int seq; + while(fscanf(fp, "%d\n", &seq) > 0) { + if (remove(android::base::StringPrintf( + "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { + processed.insert(seq); + } + } + fclose(fp); + } + + std::set<int> produced; + fp = fopen(produced_file_path.c_str(), "r"); + if (fp != NULL) { + int seq; + while(fscanf(fp, "%d\n", &seq) > 0) { + if (processed.find(seq) == processed.end()) { + produced.insert(seq); + } + } + fclose(fp); + } + + uint32_t maxLive = config.max_unprocessed_profiles; + if (produced.size() >= maxLive) { + return false; + } + + produced.insert(current_seq); + fp = fopen(produced_file_path.c_str(), "w"); + if (fp == NULL) { + PLOG(WARNING) << "Cannot write " << produced_file_path; + return false; + } + for (std::set<int>::const_iterator iter = produced.begin(); + iter != produced.end(); ++iter) { + fprintf(fp, "%d\n", *iter); + } + fclose(fp); + chmod(produced_file_path.c_str(), + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + return true; +} + +// +// Initialization +// + +static void init(ConfigReader &config) +{ + if (!config.readFile()) { + LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); + } + + CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), + config.getStringValue("destination_directory").c_str()); + + signal(SIGHUP, sig_hup); +} + +// +// Main routine: +// 1. parse cmd line args +// 2. read config file +// 3. loop: { +// sleep for a while +// perform a profile collection +// } +// +int perfprofd_main(int argc, char** argv, Config* config) +{ + ConfigReader config_reader; + + LOG(INFO) << "starting Android Wide Profiling daemon"; + + parse_args(argc, argv); + init(config_reader); + config_reader.FillConfig(config); + + if (!perf_file_to_convert.empty()) { + std::string encoded_path = perf_file_to_convert + ".encoded"; + encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); + return 0; + } + + // Early exit if we're not supposed to run on this build flavor + if (!IsDebugBuild() && config->only_debug_build) { + LOG(INFO) << "early exit due to inappropriate build type"; + return 0; + } + + auto config_fn = [config]() { + return config; + }; + auto reread_config = [&config_reader, config]() { + // Reread config file -- the uploader may have rewritten it as a result + // of a gservices change + config_reader.readFile(); + config_reader.FillConfig(config); + }; + int seq = 0; + auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { + if (proto == nullptr) { + return false; + } + if (handler_config->send_to_dropbox) { + std::string error_msg; + if (!android::perfprofd::dropbox::SendToDropbox(proto, + handler_config->destination_directory, + &error_msg)) { + LOG(ERROR) << "Failed dropbox submission: " << error_msg; + return false; + } + } else { + std::string data_file_path(handler_config->destination_directory); + data_file_path += "/"; + data_file_path += PERF_OUTPUT; + std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); + if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { + return false; + } + if (!post_process(*handler_config, seq)) { + return false; + } + } + seq++; + return true; + }; + ProfilingLoop(config_fn, reread_config, handler); + + LOG(INFO) << "finishing Android Wide Profiling daemon"; + return 0; +} diff --git a/perfprofd/perfprofd_cmdline.h b/perfprofd/perfprofd_cmdline.h new file mode 100644 index 00000000..5a6b766c --- /dev/null +++ b/perfprofd/perfprofd_cmdline.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015, 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. + */ + +#ifndef SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ +#define SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ + +// Semaphore file that indicates that the user is opting in +#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt" + +// File containing a list of sequence numbers corresponding to profiles +// that have been processed/uploaded. Written by the GmsCore uploader, +// within the GmsCore files directory. +#define PROCESSED_FILENAME "perfprofd_processed.txt" + +// File containing a list of sequence numbers corresponding to profiles +// that have been created by the perfprofd but not yet uploaded. Written +// by perfprofd within the destination directory; consumed by GmsCore. +#define PRODUCED_FILENAME "perfprofd_produced.txt" + +struct Config; + +// Main routine for perfprofd daemon +int perfprofd_main(int argc, char **argv, Config* config); + +#endif // SYSTEM_EXTRAS_PERFPROFD_PERFPROFD_CMDLINE_H_ diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc index 4fa666d3..a5b4b480 100644 --- a/perfprofd/perfprofdcore.cc +++ b/perfprofd/perfprofdcore.cc @@ -29,10 +29,7 @@ #include <time.h> #include <unistd.h> -#include <cctype> -#include <map> #include <memory> -#include <set> #include <sstream> #include <string> @@ -40,7 +37,6 @@ #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> #ifdef __BIONIC__ #include <android-base/properties.h> @@ -48,7 +44,7 @@ #include "perfprofd_record.pb.h" -#include "configreader.h" +#include "config.h" #include "cpuconfig.h" #include "perf_data_converter.h" #include "perfprofdcore.h" @@ -97,6 +93,8 @@ typedef enum { } CKPROFILE_RESULT; +static bool common_initialized = false; + // // Are we running in the emulator? If so, stub out profile collection // Starts as uninitialized (-1), then set to 1 or 0 at init time. @@ -105,14 +103,8 @@ static int running_in_emulator = -1; // // Is this a debug build ('userdebug' or 'eng')? -// Starts as uninitialized (-1), then set to 1 or 0 at init time. -// -static int is_debug_build = -1; - -// -// Path to the perf file to convert and exit? Empty value is the default, daemon mode. // -static std::string perf_file_to_convert = ""; +static bool is_debug_build = false; // // Random number generator seed (set at startup time). @@ -120,51 +112,9 @@ static std::string perf_file_to_convert = ""; static unsigned short random_seed[3]; // -// SIGHUP handler. Sending SIGHUP to the daemon can be used to break it -// out of a sleep() call so as to trigger a new collection (debugging) -// -static void sig_hup(int /* signum */) -{ - LOG(WARNING) << "SIGHUP received"; -} - -// -// Parse command line args. Currently supported flags: -// * "-c PATH" sets the path of the config file to PATH. -// * "-x PATH" reads PATH as a perf data file and saves it as a file in -// perf_profile.proto format. ".encoded" suffix is appended to PATH to form -// the output file path. -// -static void parse_args(int argc, char** argv) -{ - int ac; - - for (ac = 1; ac < argc; ++ac) { - if (!strcmp(argv[ac], "-c")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -c option requires argument)"; - continue; - } - ConfigReader::setConfigFilePath(argv[ac+1]); - ++ac; - } else if (!strcmp(argv[ac], "-x")) { - if (ac >= argc-1) { - LOG(ERROR) << "malformed command line: -x option requires argument)"; - continue; - } - perf_file_to_convert = argv[ac+1]; - ++ac; - } else { - LOG(ERROR) << "malformed command line: unknown option or arg " << argv[ac] << ")"; - continue; - } - } -} - -// // Convert a CKPROFILE_RESULT to a string // -const char *ckprofile_result_to_string(CKPROFILE_RESULT result) +static const char *ckprofile_result_to_string(CKPROFILE_RESULT result) { switch (result) { case DO_COLLECT_PROFILE: @@ -183,29 +133,6 @@ const char *ckprofile_result_to_string(CKPROFILE_RESULT result) } // -// Convert a PROFILE_RESULT to a string -// -const char *profile_result_to_string(PROFILE_RESULT result) -{ - switch(result) { - case OK_PROFILE_COLLECTION: - return "profile collection succeeded"; - case ERR_FORK_FAILED: - return "fork() system call failed"; - case ERR_PERF_RECORD_FAILED: - return "perf record returned bad exit status"; - case ERR_PERF_ENCODE_FAILED: - return "failure encoding perf.data to protobuf"; - case ERR_OPEN_ENCODED_FILE_FAILED: - return "failed to open encoded perf file"; - case ERR_WRITE_ENCODED_FILE_FAILED: - return "write to encoded perf file failed"; - default: - return "unknown"; - } -} - -// // Check to see whether we should perform a profile collection // static CKPROFILE_RESULT check_profiling_enabled(const Config& config) @@ -387,9 +314,9 @@ bool get_charging() return result; } -bool postprocess_proc_stat_contents(const std::string &pscontents, - long unsigned *idleticks, - long unsigned *remainingticks) +static bool postprocess_proc_stat_contents(const std::string &pscontents, + long unsigned *idleticks, + long unsigned *remainingticks) { long unsigned usertime, nicetime, systime, idletime, iowaittime; long unsigned irqtime, softirqtime; @@ -663,68 +590,6 @@ static void cleanup_destination_dir(const std::string& dest_dir) } // -// Post-processes after profile is collected and converted to protobuf. -// * GMS core stores processed file sequence numbers in -// /data/data/com.google.android.gms/files/perfprofd_processed.txt -// * Update /data/misc/perfprofd/perfprofd_produced.txt to remove the sequence -// numbers that have been processed and append the current seq number -// Returns true if the current_seq should increment. -// -static bool post_process(const Config& config, int current_seq) -{ - const std::string& dest_dir = config.destination_directory; - std::string processed_file_path = - config.config_directory + "/" + PROCESSED_FILENAME; - std::string produced_file_path = dest_dir + "/" + PRODUCED_FILENAME; - - - std::set<int> processed; - FILE *fp = fopen(processed_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (remove(android::base::StringPrintf( - "%s/perf.data.encoded.%d", dest_dir.c_str(),seq).c_str()) == 0) { - processed.insert(seq); - } - } - fclose(fp); - } - - std::set<int> produced; - fp = fopen(produced_file_path.c_str(), "r"); - if (fp != NULL) { - int seq; - while(fscanf(fp, "%d\n", &seq) > 0) { - if (processed.find(seq) == processed.end()) { - produced.insert(seq); - } - } - fclose(fp); - } - - uint32_t maxLive = config.max_unprocessed_profiles; - if (produced.size() >= maxLive) { - return false; - } - - produced.insert(current_seq); - fp = fopen(produced_file_path.c_str(), "w"); - if (fp == NULL) { - PLOG(WARNING) << "Cannot write " << produced_file_path; - return false; - } - for (std::set<int>::const_iterator iter = produced.begin(); - iter != produced.end(); ++iter) { - fprintf(fp, "%d\n", *iter); - } - fclose(fp); - chmod(produced_file_path.c_str(), - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); - return true; -} - -// // Collect a perf profile. Steps for this operation are: // - kick off 'perf record' // - read perf.data, convert to protocol buf @@ -852,7 +717,7 @@ static void set_seed(uint32_t use_fixed_seed) random_seed[2] = (random_seed[0] ^ random_seed[1]); } -static void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { +void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { // Children of init inherit an artificially low OOM score -- this is not // desirable for perfprofd (its OOM score should be on par with // other user processes). @@ -874,27 +739,13 @@ static void CommonInit(uint32_t use_fixed_seed, const char* dest_dir) { running_in_emulator = false; is_debug_build = true; #endif -} -// -// Initialization -// -static void init(const Config& config) -{ - // TODO: Consider whether we want to clean things or just overwrite. - CommonInit(config.use_fixed_seed, nullptr); + common_initialized = true; } -static void init(ConfigReader &config) -{ - if (!config.readFile()) { - LOG(ERROR) << "unable to open configuration file " << config.getConfigFilePath(); - } - - CommonInit(static_cast<uint32_t>(config.getUnsignedValue("use_fixed_seed")), - config.getStringValue("destination_directory").c_str()); - - signal(SIGHUP, sig_hup); +bool IsDebugBuild() { + CHECK(common_initialized); + return is_debug_build; } template <typename ConfigFn, typename UpdateFn> @@ -952,7 +803,7 @@ static void ProfilingLoopImpl(ConfigFn config, UpdateFn update, HandlerFn handle } void ProfilingLoop(Config& config, HandlerFn handler) { - init(config); + CommonInit(config.use_fixed_seed, nullptr); auto config_fn = [&config]() { return &config;; @@ -962,67 +813,8 @@ void ProfilingLoop(Config& config, HandlerFn handler) { ProfilingLoopImpl(config_fn, do_nothing, handler); } -// -// Main routine: -// 1. parse cmd line args -// 2. read config file -// 3. loop: { -// sleep for a while -// perform a profile collection -// } -// -int perfprofd_main(int argc, char** argv, Config* config) -{ - ConfigReader config_reader; - - LOG(INFO) << "starting Android Wide Profiling daemon"; - - parse_args(argc, argv); - init(config_reader); - config_reader.FillConfig(config); - - if (!perf_file_to_convert.empty()) { - std::string encoded_path = perf_file_to_convert + ".encoded"; - encode_to_proto(perf_file_to_convert, encoded_path.c_str(), *config, 0, nullptr); - return 0; - } - - // Early exit if we're not supposed to run on this build flavor - if (is_debug_build != 1 && config->only_debug_build) { - LOG(INFO) << "early exit due to inappropriate build type"; - return 0; - } - - auto config_fn = [config]() { - return config; - }; - auto reread_config = [&config_reader, config]() { - // Reread config file -- the uploader may have rewritten it as a result - // of a gservices change - config_reader.readFile(); - config_reader.FillConfig(config); - }; - int seq = 0; - auto handler = [&seq](android::perfprofd::PerfprofdRecord* proto, Config* handler_config) { - if (proto == nullptr) { - return false; - } - std::string data_file_path(handler_config->destination_directory); - data_file_path += "/"; - data_file_path += PERF_OUTPUT; - std::string path = android::base::StringPrintf("%s.encoded.%d", data_file_path.c_str(), seq); - if (!android::perfprofd::SerializeProtobuf(proto, path.c_str(), handler_config->compress)) { - return false; - } - - if (!post_process(*handler_config, seq)) { - return false; - } - seq++; - return true; - }; - ProfilingLoopImpl(config_fn, reread_config, handler); - - LOG(INFO) << "finishing Android Wide Profiling daemon"; - return 0; +void ProfilingLoop(std::function<Config*()> config_fn, + std::function<void()> update_fn, + HandlerFn handler) { + ProfilingLoopImpl(config_fn, update_fn, handler); } diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h index 73a9c567..2adf114d 100644 --- a/perfprofd/perfprofdcore.h +++ b/perfprofd/perfprofdcore.h @@ -29,21 +29,7 @@ namespace perfprofd { struct Symbolizer; } -// Semaphore file that indicates that the user is opting in -#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been processed/uploaded. Written by the GmsCore uploader, -// within the GmsCore files directory. -#define PROCESSED_FILENAME "perfprofd_processed.txt" - -// File containing a list of sequence numbers corresponding to profiles -// that have been created by the perfprofd but not yet uploaded. Written -// by perfprofd within the destination directory; consumed by GmsCore. -#define PRODUCED_FILENAME "perfprofd_produced.txt" - -// Main routine for perfprofd daemon -extern int perfprofd_main(int argc, char **argv, Config* config); +void CommonInit(uint32_t use_fixed_seed, const char* dest_dir); // // This enumeration holds the results of what happened when on an @@ -86,6 +72,9 @@ using HandlerFn = std::function<bool(android::perfprofd::PerfprofdRecord* proto, Config* config)>; void ProfilingLoop(Config& config, HandlerFn handler); +void ProfilingLoop(std::function<Config*()> config_fn, + std::function<void()> update_fn, + HandlerFn handler); // // Exposed for unit testing @@ -95,4 +84,6 @@ extern bool get_booting(); extern bool get_charging(); extern bool get_camera_active(); +bool IsDebugBuild(); + #endif diff --git a/perfprofd/perfprofdmain.cc b/perfprofd/perfprofdmain.cc index 403e0253..0f9f53e9 100644 --- a/perfprofd/perfprofdmain.cc +++ b/perfprofd/perfprofdmain.cc @@ -21,6 +21,7 @@ #include "config.h" #include "perfprofd_binder.h" +#include "perfprofd_cmdline.h" #include "perfprofdcore.h" extern int perfprofd_main(int argc, char** argv, Config* config); diff --git a/perfprofd/tests/Android.bp b/perfprofd/tests/Android.bp index 9fa0d730..f601aaf5 100644 --- a/perfprofd/tests/Android.bp +++ b/perfprofd/tests/Android.bp @@ -48,6 +48,11 @@ cc_test { required: [ "simpleperf", ], + shared_libs: [ + "libbinder", + "libservices", + "libutils", + ], }, }, diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc index 61eb09d2..79f8ea64 100644 --- a/perfprofd/tests/perfprofd_test.cc +++ b/perfprofd/tests/perfprofd_test.cc @@ -43,6 +43,7 @@ #include "configreader.h" #include "map_utils.h" #include "perfprofdcore.h" +#include "perfprofd_cmdline.h" #include "quipper_helper.h" #include "symbolizer.h" |