Merge "Fix fd leak in Binder"
diff --git a/Android.bp b/Android.bp
index 086a2c6..cd05b21 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3,6 +3,7 @@
from: "include/android",
to: "android",
srcs: ["include/android/**/*.h"],
+ license: "NOTICE",
}
subdirs = [
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 3db41c2..db61661 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -1,4 +1,6 @@
cc_library_static {
name: "libdumpstate.default",
srcs: ["libdumpstate_default.cpp"],
+ // TODO: should use libdumpstateheaders, but if fails with 'undefined'
+ static_libs: ["libbase", "libziparchive", "libz"]
}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index 695e464..116650d 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -20,6 +20,25 @@
liblog \
libselinux
+# ====================#
+# libdumpstateheaders #
+# ====================#
+# TODO: this module is necessary so the device-specific libdumpstate implementations do not
+# need to add any other dependency (like libbase). Should go away once dumpstate HAL changes.
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS = $(LOCAL_PATH)
+LOCAL_MODULE := libdumpstateheaders
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
+ $(COMMON_SHARED_LIBRARIES)
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
+ $(COMMON_ZIP_LIBRARIES)
+# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
+LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
+LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
+
+include $(BUILD_STATIC_LIBRARY)
+
# ==========#
# dumpstate #
# ==========#
diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md
index c33fc1f..b995b80 100644
--- a/cmds/dumpstate/bugreport-format.md
+++ b/cmds/dumpstate/bugreport-format.md
@@ -55,6 +55,10 @@
- `title.txt`: whose value is a single-line summary of the problem.
- `description.txt`: whose value is a multi-line, detailed description of the problem.
+## Android O versions
+On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made:
+- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`).
+
## Intermediate versions
During development, the versions will be suffixed with _-devX_ or
_-devX-EXPERIMENTAL_FEATURE_, where _X_ is a number that increases as the
@@ -63,8 +67,8 @@
For example, the initial version during _Android N_ development was
**1.0-dev1**. When `dumpsys` was split in 2 sections but not all tools were
ready to parse that format, the version was named **1.0-dev2**,
-which had to be passed do `dumpsys` explicitly (i.e., trhough a
-`-V 1.0-dev2` argument). Once that format became stable and tools
+which had to be passed to `dumpsys` explicitly (by setting the `dumpstate.version` system property).
+Once that format became stable and tools
knew how to parse it, the default version became **1.0-dev2**.
Similarly, if changes in the file format are made after the initial release of
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a48f112..740eb19 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
#include <dirent.h>
#include <errno.h>
@@ -38,17 +39,15 @@
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <hardware_legacy/power.h>
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
#include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
#include <openssl/sha.h>
@@ -56,8 +55,8 @@
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
-// TODO: variables below should be part of dumpstate object
-static std::unique_ptr<ZipWriter> zip_writer;
+// TODO: variables and functions below should be part of dumpstate object
+
static std::set<std::string> mount_points;
void add_mountinfo();
@@ -108,6 +107,7 @@
static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
+static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
* otherwise, gets just those modified in the last half an hour. */
@@ -119,7 +119,7 @@
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
struct stat st;
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
- (zip_writer || (time_t) st.st_mtime >= thirty_minutes_ago)) {
+ (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
data[i].fd = fd;
} else {
close(fd);
@@ -146,7 +146,7 @@
if (mount_points.find(linkname) == mount_points.end()) {
// First time this mount point was found: add it
snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
- if (add_zip_entry(ZIP_ROOT_DIR + path, path)) {
+ if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
mount_points.insert(linkname);
} else {
MYLOGE("Unable to add mountinfo %s to zip file\n", path);
@@ -155,10 +155,10 @@
}
void add_mountinfo() {
- if (!zip_writer) return;
+ if (!ds.IsZipping()) return;
std::string title = "MOUNT INFO";
mount_points.clear();
- DurationReporter durationReporter(title, nullptr);
+ DurationReporter duration_reporter(title, nullptr);
for_each_pid(do_mountinfo, nullptr);
MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
}
@@ -229,8 +229,8 @@
long long cur_size = 0;
const char *trace_path = "/data/misc/anrd/";
- if (!zip_writer) {
- MYLOGE("Not dumping anrd trace because zip_writer is not set\n");
+ if (!ds.IsZipping()) {
+ MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
return false;
}
@@ -301,7 +301,7 @@
}
}
// Add to the zip file.
- if (!add_zip_entry("anrd_trace.txt", path)) {
+ if (!ds.AddZipEntry("anrd_trace.txt", path)) {
MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
} else {
if (remove(path)) {
@@ -317,8 +317,8 @@
}
static void dump_systrace() {
- if (!zip_writer) {
- MYLOGD("Not dumping systrace because zip_writer is not set\n");
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
return;
}
std::string systrace_path = ds.GetPath("-systrace.txt");
@@ -348,7 +348,7 @@
// MYLOGE("could not stop systrace ");
// }
}
- if (!add_zip_entry("systrace.txt", systrace_path)) {
+ if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
} else {
if (remove(systrace_path.c_str())) {
@@ -362,9 +362,9 @@
return;
}
- std::string raft_log_path = ds.GetPath("-raft_log.txt");
- if (raft_log_path.empty()) {
- MYLOGD("raft_log_path is empty\n");
+ std::string raft_path = ds.GetPath("-raft_log.txt");
+ if (raft_path.empty()) {
+ MYLOGD("raft_path is empty\n");
return;
}
@@ -375,22 +375,97 @@
}
CommandOptions options = CommandOptions::WithTimeout(600).Build();
- if (!zip_writer) {
- // Write compressed and encoded raft logs to stdout if not zip_writer.
+ if (!ds.IsZipping()) {
+ // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
return;
}
- RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_log_path}, options);
- if (!add_zip_entry("raft_log.txt", raft_log_path)) {
- MYLOGE("Unable to add raft log %s to zip file\n", raft_log_path.c_str());
+ RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
+ if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
+ MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
} else {
- if (remove(raft_log_path.c_str())) {
- MYLOGE("Error removing raft file %s: %s\n", raft_log_path.c_str(), strerror(errno));
+ if (remove(raft_path.c_str())) {
+ MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
}
}
}
+/**
+ * Finds the last modified file in the directory dir whose name starts with file_prefix.
+ *
+ * Function returns empty string when it does not find a file
+ */
+static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
+ const std::string& file_prefix) {
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
+ if (d == nullptr) {
+ MYLOGD("Error %d opening %s\n", errno, dir.c_str());
+ return "";
+ }
+
+ // Find the newest file matching the file_prefix in dir
+ struct dirent *de;
+ time_t last_modified_time = 0;
+ std::string last_modified_file = "";
+ struct stat s;
+
+ while ((de = readdir(d.get()))) {
+ std::string file = std::string(de->d_name);
+ if (!file_prefix.empty()) {
+ if (!android::base::StartsWith(file, file_prefix.c_str())) continue;
+ }
+ file = dir + "/" + file;
+ int ret = stat(file.c_str(), &s);
+
+ if ((ret == 0) && (s.st_mtime > last_modified_time)) {
+ last_modified_file = file;
+ last_modified_time = s.st_mtime;
+ }
+ }
+
+ return last_modified_file;
+}
+
+static void DumpModemLogs() {
+ DurationReporter durationReporter("DUMP MODEM LOGS");
+ if (IsUserBuild()) {
+ return;
+ }
+
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
+ return;
+ }
+
+ std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
+
+ if(file_prefix.empty()) {
+ MYLOGD("No modem log : file_prefix is empty\n");
+ return;
+ }
+
+ // TODO: use bugreport_dir_ directly when this function is moved to Dumpstate class
+ std::string bugreport_dir = dirname(ds.GetPath("").c_str());
+ MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
+ bugreport_dir.c_str(), file_prefix.c_str());
+
+ std::string modem_log_file = GetLastModifiedFileWithPrefix(bugreport_dir, file_prefix);
+
+ struct stat s;
+ if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
+ MYLOGD("Modem log %s does not exist\n", modem_log_file.c_str());
+ return;
+ }
+
+ std::string filename = basename(modem_log_file.c_str());
+ if (!ds.AddZipEntry(filename, modem_log_file)) {
+ MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
+ } else {
+ MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
+ }
+}
+
static bool skip_not_stat(const char *path) {
static const char stat[] = "/stat";
size_t len = strlen(path);
@@ -574,105 +649,15 @@
return 0;
}
-/* Copied policy from system/core/logd/LogBuffer.cpp */
-
-#define LOG_BUFFER_SIZE (256 * 1024)
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-
-static bool valid_size(unsigned long value) {
- if ((value < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < value)) {
- return false;
- }
-
- long pages = sysconf(_SC_PHYS_PAGES);
- if (pages < 1) {
- return true;
- }
-
- long pagesize = sysconf(_SC_PAGESIZE);
- if (pagesize <= 1) {
- pagesize = PAGE_SIZE;
- }
-
- // maximum memory impact a somewhat arbitrary ~3%
- pages = (pages + 31) / 32;
- unsigned long maximum = pages * pagesize;
-
- if ((maximum < LOG_BUFFER_MIN_SIZE) || (LOG_BUFFER_MAX_SIZE < maximum)) {
- return true;
- }
-
- return value <= maximum;
-}
-
-// TODO: migrate to logd/LogBuffer.cpp or use android::base::GetProperty
-static unsigned long property_get_size(const char *key) {
- unsigned long value;
- char *cp, property[PROPERTY_VALUE_MAX];
-
- property_get(key, property, "");
- value = strtoul(property, &cp, 10);
-
- switch(*cp) {
- case 'm':
- case 'M':
- value *= 1024;
- /* FALLTHRU */
- case 'k':
- case 'K':
- value *= 1024;
- /* FALLTHRU */
- case '\0':
- break;
-
- default:
- value = 0;
- }
-
- if (!valid_size(value)) {
- value = 0;
- }
-
- return value;
-}
-
/* timeout in ms */
static unsigned long logcat_timeout(const char *name) {
- static const char global_tuneable[] = "persist.logd.size"; // Settings App
- static const char global_default[] = "ro.logd.size"; // BoardConfig.mk
- char key[PROP_NAME_MAX];
- unsigned long property_size, default_size;
-
- default_size = property_get_size(global_tuneable);
- if (!default_size) {
- default_size = property_get_size(global_default);
- }
-
- snprintf(key, sizeof(key), "%s.%s", global_tuneable, name);
- property_size = property_get_size(key);
-
- if (!property_size) {
- snprintf(key, sizeof(key), "%s.%s", global_default, name);
- property_size = property_get_size(key);
- }
-
- if (!property_size) {
- property_size = default_size;
- }
-
- if (!property_size) {
- property_size = LOG_BUFFER_SIZE;
- }
-
+ log_id_t id = android_name_to_log_id(name);
+ unsigned long property_size = __android_logger_get_buffer_size(id);
/* Engineering margin is ten-fold our guess */
return 10 * (property_size + worst_write_perf) / worst_write_perf;
}
-/* End copy from system/core/logd/LogBuffer.cpp */
-
-// TODO: move to utils.cpp
-void Dumpstate::PrintHeader() {
+void Dumpstate::PrintHeader() const {
std::string build, fingerprint, radio, bootloader, network;
char date[80];
@@ -696,14 +681,18 @@
printf("Network: %s\n", network.c_str());
printf("Kernel: ");
- DumpFile("", "/proc/version");
+ JustDumpFile("", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", id_, getpid(),
- dryRun_, args_.c_str(), extraOptions_.c_str());
+ printf("Dumpstate info: id=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, getpid(),
+ dry_run_, args_.c_str(), extra_options_.c_str());
printf("\n");
}
+bool Dumpstate::IsZipping() const {
+ return zip_writer_ != nullptr;
+}
+
// List of file extensions that can cause a zip file attachment to be rejected by some email
// service providers.
static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
@@ -712,10 +701,10 @@
".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
};
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd) {
- if (!zip_writer) {
- MYLOGD("Not adding zip entry %s from fd because zip_writer is not set\n",
- entry_name.c_str());
+bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
std::string valid_name = entry_name;
@@ -733,11 +722,11 @@
// Logging statement below is useful to time how long each entry takes, but it's too verbose.
// MYLOGD("Adding zip entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
- get_mtime(fd, ds.now_));
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
+ get_mtime(fd, ds.now_));
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
@@ -750,73 +739,72 @@
MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
return false;
}
- err = zip_writer->WriteBytes(buffer.data(), bytes_read);
+ err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
if (err) {
- MYLOGE("zip_writer->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
return true;
}
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK
- | O_CLOEXEC)));
+bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
if (fd == -1) {
MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
return false;
}
- return add_zip_entry_from_fd(entry_name, fd.get());
+ return AddZipEntryFromFd(entry_name, fd.get());
}
/* adds a file to the existing zipped bugreport */
static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
- return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
+ return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}
-// TODO: move to util.cpp
-void add_dir(const std::string& dir, bool recursive) {
- if (!zip_writer) {
- MYLOGD("Not adding dir %s because zip_writer is not set\n", dir.c_str());
+void Dumpstate::AddDir(const std::string& dir, bool recursive) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
return;
}
MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
- DurationReporter durationReporter(dir, nullptr);
+ DurationReporter duration_reporter(dir, nullptr);
dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
}
-/* adds a text entry entry to the existing zip file. */
-static bool add_text_zip_entry(const std::string& entry_name, const std::string& content) {
- if (!zip_writer) {
- MYLOGD("Not adding text zip entry %s because zip_writer is not set\n", entry_name.c_str());
+bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
+ if (!IsZipping()) {
+ MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
+ entry_name.c_str());
return false;
}
MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
- int32_t err = zip_writer->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
- if (err) {
- MYLOGE("zip_writer->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
+ if (err != 0) {
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->WriteBytes(content.c_str(), content.length());
- if (err) {
- MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
- ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->WriteBytes(content.c_str(), content.length());
+ if (err != 0) {
+ MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
+ ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->FinishEntry();
- if (err) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ err = zip_writer_->FinishEntry();
+ if (err != 0) {
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -834,8 +822,89 @@
RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
}
+static void AddAnrTraceFiles() {
+ bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
+ std::string dump_traces_dir;
+
+ /* show the traces we collected in main(), if that was done */
+ if (dump_traces_path != nullptr) {
+ if (add_to_zip) {
+ dump_traces_dir = dirname(dump_traces_path);
+ MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
+ ds.AddDir(dump_traces_dir, true);
+ } else {
+ MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
+ dump_traces_path);
+ ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
+ }
+ }
+
+ std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ std::string anr_traces_dir = dirname(anr_traces_path.c_str());
+
+ // Make sure directory is not added twice.
+ // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
+ // generated by dump_traces() - and anr_traces_path - which is retrieved from a system
+ // property - but in reality they're the same path (although the former could be nullptr).
+ // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
+ // be revisited.
+ bool already_dumped = anr_traces_dir == dump_traces_dir;
+
+ MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
+ dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
+
+ if (anr_traces_path.empty()) {
+ printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
+ } else {
+ int fd = TEMP_FAILURE_RETRY(
+ open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
+ if (fd < 0) {
+ printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
+ strerror(errno));
+ } else {
+ if (add_to_zip) {
+ if (!already_dumped) {
+ MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
+ anr_traces_dir.c_str());
+ ds.AddDir(anr_traces_dir, true);
+ already_dumped = true;
+ }
+ } else {
+ MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
+ anr_traces_path.c_str());
+ dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
+ }
+ }
+ }
+
+ if (add_to_zip && already_dumped) {
+ MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
+ return;
+ }
+
+ /* slow traces for slow operations */
+ struct stat st;
+ if (!anr_traces_path.empty()) {
+ int tail = anr_traces_path.size() - 1;
+ while (tail > 0 && anr_traces_path.at(tail) != '/') {
+ tail--;
+ }
+ int i = 0;
+ while (1) {
+ anr_traces_path = anr_traces_path.substr(0, tail + 1) +
+ android::base::StringPrintf("slow%02d.txt", i);
+ if (stat(anr_traces_path.c_str(), &st)) {
+ // No traces file at this index, done with the files.
+ break;
+ }
+ ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
+ i++;
+ }
+ }
+}
+
static void dumpstate() {
- DurationReporter durationReporter("DUMPSTATE");
+ DurationReporter duration_reporter("DUMPSTATE");
unsigned long timeout;
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
@@ -863,7 +932,7 @@
RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
RunCommand("PRINTENV", {"printenv"});
- RunCommand("NETSTAT", {"netstat", "-n"});
+ RunCommand("NETSTAT", {"netstat", "-nW"});
struct stat s;
if (stat("/proc/modules", &s) != 0) {
MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
@@ -879,9 +948,9 @@
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
/* Dump Bluetooth HCI logs */
- add_dir("/data/misc/bluetooth/logs", true);
+ ds.AddDir("/data/misc/bluetooth/logs", true);
- if (!ds.doEarlyScreenshot_) {
+ if (!ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
ds.TakeScreenshot();
}
@@ -911,44 +980,7 @@
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
- /* show the traces we collected in main(), if that was done */
- if (dump_traces_path != NULL) {
- DumpFile("VM TRACES JUST NOW", dump_traces_path);
- }
-
- /* only show ANR traces if they're less than 15 minutes old */
- struct stat st;
- std::string anrTracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
- if (anrTracesPath.empty()) {
- printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
- } else {
- int fd = TEMP_FAILURE_RETRY(
- open(anrTracesPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
- if (fd < 0) {
- printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anrTracesPath.c_str(), strerror(errno));
- } else {
- dump_file_from_fd("VM TRACES AT LAST ANR", anrTracesPath.c_str(), fd);
- }
- }
-
- /* slow traces for slow operations */
- if (!anrTracesPath.empty()) {
- int tail = anrTracesPath.size() - 1;
- while (tail > 0 && anrTracesPath.at(tail) != '/') {
- tail--;
- }
- int i = 0;
- while (1) {
- anrTracesPath =
- anrTracesPath.substr(0, tail + 1) + android::base::StringPrintf("slow%02d.txt", i);
- if (stat(anrTracesPath.c_str(), &st)) {
- // No traces file at this index, done with the files.
- break;
- }
- DumpFile("VM TRACES WHEN SLOW", anrTracesPath.c_str());
- i++;
- }
- }
+ AddAnrTraceFiles();
int dumped = 0;
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
@@ -956,8 +988,8 @@
const char *name = tombstone_data[i].name;
int fd = tombstone_data[i].fd;
dumped = 1;
- if (zip_writer) {
- if (!add_zip_entry_from_fd(ZIP_ROOT_DIR + name, fd)) {
+ if (ds.IsZipping()) {
+ if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
MYLOGE("Unable to add tombstone %s to zip file\n", name);
}
} else {
@@ -977,6 +1009,7 @@
DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
+ struct stat st;
if (!stat(PSTORE_LAST_KMSG, &st)) {
/* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
@@ -1120,9 +1153,14 @@
RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
+ // DumpModemLogs adds the modem logs if available to the bugreport.
+ // Do this at the end to allow for sufficient time for the modem logs to be
+ // collected.
+ DumpModemLogs();
+
printf("========================================================\n");
printf("== Final progress (pid %d): %d/%d (originally %d)\n", getpid(), ds.progress_,
- ds.weightTotal_, WEIGHT_TOTAL);
+ ds.weight_total_, WEIGHT_TOTAL);
printf("========================================================\n");
printf("== dumpstate: done (id %lu)\n", ds.id_);
printf("========================================================\n");
@@ -1147,8 +1185,7 @@
"progress (requires -o and -B)\n"
" -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
"shouldn't be used with -P)\n"
- " -V: sets the bugreport format version (valid values: %s)\n",
- VERSION_DEFAULT.c_str());
+ " -v: prints the dumpstate header and exit\n");
exit(exitCode);
}
@@ -1182,11 +1219,10 @@
sigaction(SIGQUIT, &sa, NULL); // quit
}
-/* adds the temporary report to the existing .zip file, closes the .zip file, and removes the
- temporary file.
- */
-static bool finish_zip_file(const std::string& bugreport_name, const std::string& bugreport_path,
- const std::string& log_path) {
+bool Dumpstate::FinishZipFile() {
+ std::string entry_name = base_name_ + "-" + name_ + ".txt";
+ MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
+ tmp_path_.c_str());
// Final timestamp
char date[80];
time_t the_real_now_please_stand_up = time(nullptr);
@@ -1194,38 +1230,41 @@
MYLOGD("dumpstate id %lu finished around %s (%ld s)\n", ds.id_, date,
the_real_now_please_stand_up - ds.now_);
- if (!add_zip_entry(bugreport_name, bugreport_path)) {
+ if (!ds.AddZipEntry(entry_name, tmp_path_)) {
MYLOGE("Failed to add text entry to .zip file\n");
return false;
}
- if (!add_text_zip_entry("main_entry.txt", bugreport_name)) {
+ if (!AddTextZipEntry("main_entry.txt", entry_name)) {
MYLOGE("Failed to add main_entry.txt to .zip file\n");
return false;
}
// Add log file (which contains stderr output) to zip...
fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
- if (!add_zip_entry("dumpstate_log.txt", log_path.c_str())) {
+ if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
MYLOGE("Failed to add dumpstate log to .zip file\n");
return false;
}
// ... and re-opens it for further logging.
- redirect_to_existing_file(stderr, const_cast<char*>(log_path.c_str()));
+ redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
fprintf(stderr, "\n");
- int32_t err = zip_writer->Finish();
- if (err) {
- MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+ int32_t err = zip_writer_->Finish();
+ if (err != 0) {
+ MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
+ // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
+ ds.zip_file.reset(nullptr);
+
if (IsUserBuild()) {
- MYLOGD("Removing temporary file %s\n", bugreport_path.c_str())
- if (remove(bugreport_path.c_str())) {
- ALOGW("remove(%s): %s\n", bugreport_path.c_str(), strerror(errno));
+ MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
+ if (remove(tmp_path_.c_str()) != 0) {
+ ALOGW("remove(%s): %s\n", tmp_path_.c_str(), strerror(errno));
}
} else {
- MYLOGD("Keeping temporary file %s on non-user build\n", bugreport_path.c_str())
+ MYLOGD("Keeping temporary file %s on non-user build\n", tmp_path_.c_str())
}
return true;
@@ -1276,42 +1315,12 @@
int do_fb = 0;
int do_broadcast = 0;
int is_remote_mode = 0;
-
- MYLOGI("begin\n");
-
- if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
- MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
- } else {
- MYLOGD("Wake lock acquired.\n");
- atexit(wake_lock_releaser);
- register_sig_handler();
- }
-
- if (ds.IsDryRun()) {
- MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
- }
-
- // TODO: use helper function to convert argv into a string
- for (int i = 0; i < argc; i++) {
- ds.args_ += argv[i];
- if (i < argc - 1) {
- ds.args_ += " ";
- }
- }
-
- ds.extraOptions_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
- MYLOGI("Dumpstate args: %s (extra options: %s)\n", ds.args_.c_str(), ds.extraOptions_.c_str());
-
- /* gets the sequential id */
- int lastId = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
- ds.id_ = ++lastId;
- android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(lastId));
- MYLOGI("dumpstate id: %lu\n", ds.id_);
+ bool show_header_only = false;
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_score_adj", "we");
+ FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
if (oom_adj) {
fputs("-1000", oom_adj);
fclose(oom_adj);
@@ -1329,18 +1338,18 @@
while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
switch (c) {
// clang-format off
- case 'd': do_add_date = 1; break;
- case 'z': do_zip_file = 1; break;
- case 'o': use_outfile = optarg; break;
- case 's': use_socket = 1; break;
- case 'S': use_control_socket = 1; break;
- case 'v': break; // compatibility no-op
- case 'q': do_vibrate = 0; break;
- case 'p': do_fb = 1; break;
- case 'P': ds.updateProgress_ = true; break;
- case 'R': is_remote_mode = 1; break;
- case 'B': do_broadcast = 1; break;
- case 'V': ds.version_ = optarg; break;
+ case 'd': do_add_date = 1; break;
+ case 'z': do_zip_file = 1; break;
+ case 'o': use_outfile = optarg; break;
+ case 's': use_socket = 1; break;
+ case 'S': use_control_socket = 1; break;
+ case 'v': show_header_only = true; break;
+ case 'q': do_vibrate = 0; break;
+ case 'p': do_fb = 1; break;
+ case 'P': ds.update_progress_ = true; break;
+ case 'R': is_remote_mode = 1; break;
+ case 'B': do_broadcast = 1; break;
+ case 'V': break; // compatibility no-op
case 'h':
ShowUsageAndExit(0);
break;
@@ -1351,29 +1360,35 @@
}
}
- if (!ds.extraOptions_.empty()) {
+ // TODO: use helper function to convert argv into a string
+ for (int i = 0; i < argc; i++) {
+ ds.args_ += argv[i];
+ if (i < argc - 1) {
+ ds.args_ += " ";
+ }
+ }
+
+ ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
+ if (!ds.extra_options_.empty()) {
// Framework uses a system property to override some command-line args.
// Currently, it contains the type of the requested bugreport.
- if (ds.extraOptions_ == "bugreportplus") {
- MYLOGD("Running as bugreportplus: add -P, remove -p\n");
- ds.updateProgress_ = true;
+ if (ds.extra_options_ == "bugreportplus") {
+ ds.update_progress_ = true;
do_fb = 0;
- } else if (ds.extraOptions_ == "bugreportremote") {
- MYLOGD("Running as bugreportremote: add -q -R, remove -p\n");
+ } else if (ds.extra_options_ == "bugreportremote") {
do_vibrate = 0;
is_remote_mode = 1;
do_fb = 0;
- } else if (ds.extraOptions_ == "bugreportwear") {
- MYLOGD("Running as bugreportwear: add -P\n");
- ds.updateProgress_ = true;
+ } else if (ds.extra_options_ == "bugreportwear") {
+ ds.update_progress_ = true;
} else {
- MYLOGE("Unknown extra option: %s\n", ds.extraOptions_.c_str());
+ MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
}
// Reset the property
android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
}
- if ((do_zip_file || do_add_date || ds.updateProgress_ || do_broadcast) && !use_outfile) {
+ if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
ExitOnInvalidArgs();
}
@@ -1381,21 +1396,55 @@
ExitOnInvalidArgs();
}
- if (ds.updateProgress_ && !do_broadcast) {
+ if (ds.update_progress_ && !do_broadcast) {
ExitOnInvalidArgs();
}
- if (is_remote_mode && (ds.updateProgress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
+ if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
ExitOnInvalidArgs();
}
- if (ds.version_ != VERSION_DEFAULT) {
- ShowUsageAndExit();
+ if (ds.version_ == VERSION_DEFAULT) {
+ ds.version_ = VERSION_CURRENT;
}
+ if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
+ MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
+ ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
+ VERSION_SPLIT_ANR.c_str());
+ exit(1);
+ }
+
+ if (show_header_only) {
+ ds.PrintHeader();
+ exit(0);
+ }
+
+ /* gets the sequential id */
+ int last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
+ ds.id_ = ++last_id;
+ android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
+
+ MYLOGI("begin\n");
+
+ if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
+ MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
+ } else {
+ MYLOGD("Wake lock acquired.\n");
+ atexit(wake_lock_releaser);
+ register_sig_handler();
+ }
+
+ if (ds.IsDryRun()) {
+ MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
+ }
+
+ MYLOGI("dumpstate info: id=%lu, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
+ ds.extra_options_.c_str());
+
MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
- ds.doEarlyScreenshot_ = ds.updateProgress_;
+ ds.do_early_screenshot_ = ds.update_progress_;
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
@@ -1405,42 +1454,30 @@
if (use_control_socket) {
MYLOGD("Opening control socket\n");
- ds.controlSocketFd_ = open_socket("dumpstate");
- ds.updateProgress_ = 1;
+ ds.control_socket_fd_ = open_socket("dumpstate");
+ ds.update_progress_ = 1;
}
- /* full path of the temporary file containing the bugreport */
- std::string tmp_path;
-
- /* full path of the file containing the dumpstate logs */
- std::string log_path;
-
- /* pointer to the actual path, be it zip or text */
- std::string path;
-
- /* pointer to the zipped file */
- std::unique_ptr<FILE, int(*)(FILE*)> zip_file(NULL, fclose);
-
/* redirect output if needed */
bool is_redirecting = !use_socket && use_outfile;
if (is_redirecting) {
- ds.bugreportDir_ = dirname(use_outfile);
- ds.baseName_ = basename(use_outfile);
+ ds.bugreport_dir_ = dirname(use_outfile);
+ ds.base_name_ = basename(use_outfile);
if (do_add_date) {
char date[80];
strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
- ds.suffix_ = date;
+ ds.name_ = date;
} else {
- ds.suffix_ = "undated";
+ ds.name_ = "undated";
}
std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
- ds.baseName_ = ds.baseName_ + "-" + buildId;
+ ds.base_name_ += "-" + buildId;
if (do_fb) {
- ds.screenshotPath_ = ds.GetPath(".png");
+ ds.screenshot_path_ = ds.GetPath(".png");
}
- tmp_path = ds.GetPath(".tmp");
- log_path = ds.GetPath("-dumpstate_log-" + std::to_string(getpid()) + ".txt");
+ ds.tmp_path_ = ds.GetPath(".tmp");
+ ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(getpid()) + ".txt");
MYLOGD(
"Bugreport dir: %s\n"
@@ -1449,29 +1486,29 @@
"Log path: %s\n"
"Temporary path: %s\n"
"Screenshot path: %s\n",
- ds.bugreportDir_.c_str(), ds.baseName_.c_str(), ds.suffix_.c_str(), log_path.c_str(),
- tmp_path.c_str(), ds.screenshotPath_.c_str());
+ ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
+ ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (do_zip_file) {
- path = ds.GetPath(".zip");
- MYLOGD("Creating initial .zip file (%s)\n", path.c_str());
- create_parent_dirs(path.c_str());
- zip_file.reset(fopen(path.c_str(), "wb"));
- if (!zip_file) {
- MYLOGE("fopen(%s, 'wb'): %s\n", path.c_str(), strerror(errno));
+ ds.path_ = ds.GetPath(".zip");
+ MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
+ create_parent_dirs(ds.path_.c_str());
+ ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
+ if (ds.zip_file == nullptr) {
+ MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
do_zip_file = 0;
} else {
- zip_writer.reset(new ZipWriter(zip_file.get()));
+ ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
}
- add_text_zip_entry("version.txt", ds.version_);
+ ds.AddTextZipEntry("version.txt", ds.version_);
}
- if (ds.updateProgress_) {
+ if (ds.update_progress_) {
if (do_broadcast) {
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
- "--es", "android.intent.extra.NAME", ds.suffix_,
+ "--es", "android.intent.extra.NAME", ds.name_,
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(getpid()),
"--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
@@ -1480,7 +1517,7 @@
send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
}
if (use_control_socket) {
- dprintf(ds.controlSocketFd_, "BEGIN:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
}
}
}
@@ -1501,8 +1538,8 @@
}
}
- if (do_fb && ds.doEarlyScreenshot_) {
- if (ds.screenshotPath_.empty()) {
+ if (do_fb && ds.do_early_screenshot_) {
+ if (ds.screenshot_path_.empty()) {
// should not have happened
MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
} else {
@@ -1512,24 +1549,25 @@
}
if (do_zip_file) {
- if (chown(path.c_str(), AID_SHELL, AID_SHELL)) {
- MYLOGE("Unable to change ownership of zip file %s: %s\n", path.c_str(), strerror(errno));
+ if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
+ MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
+ strerror(errno));
}
}
if (is_redirecting) {
- redirect_to_file(stderr, const_cast<char*>(log_path.c_str()));
- if (chown(log_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
- log_path.c_str(), strerror(errno));
+ ds.log_path_.c_str(), strerror(errno));
}
/* TODO: rather than generating a text file now and zipping it later,
it would be more efficient to redirect stdout to the zip entry
directly, but the libziparchive doesn't support that option yet. */
- redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
- if (chown(tmp_path.c_str(), AID_SHELL, AID_SHELL)) {
+ redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
+ if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
- tmp_path.c_str(), strerror(errno));
+ ds.tmp_path_.c_str(), strerror(errno));
}
}
// NOTE: there should be no stdout output until now, otherwise it would break the header.
@@ -1559,12 +1597,12 @@
/* Run some operations that require root. */
get_tombstone_fds(tombstone_data);
- add_dir(RECOVERY_DIR, true);
- add_dir(RECOVERY_DATA_DIR, true);
- add_dir(LOGPERSIST_DATA_DIR, false);
+ ds.AddDir(RECOVERY_DIR, true);
+ ds.AddDir(RECOVERY_DATA_DIR, true);
+ ds.AddDir(LOGPERSIST_DATA_DIR, false);
if (!IsUserBuild()) {
- add_dir(PROFILE_DATA_DIR_CUR, true);
- add_dir(PROFILE_DATA_DIR_REF, true);
+ ds.AddDir(PROFILE_DATA_DIR_CUR, true);
+ ds.AddDir(PROFILE_DATA_DIR_REF, true);
}
add_mountinfo();
dump_iptables();
@@ -1604,57 +1642,57 @@
}
}
if (change_suffix) {
- MYLOGI("changing suffix from %s to %s\n", ds.suffix_.c_str(), name.c_str());
- ds.suffix_ = name;
- if (!ds.screenshotPath_.empty()) {
- std::string newScreenshotPath = ds.GetPath(".png");
- if (rename(ds.screenshotPath_.c_str(), newScreenshotPath.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", ds.screenshotPath_.c_str(),
- newScreenshotPath.c_str(), strerror(errno));
+ MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
+ ds.name_ = name;
+ if (!ds.screenshot_path_.empty()) {
+ std::string new_screenshot_path = ds.GetPath(".png");
+ if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
+ new_screenshot_path.c_str(), strerror(errno));
} else {
- ds.screenshotPath_ = newScreenshotPath;
+ ds.screenshot_path_ = new_screenshot_path;
}
}
}
bool do_text_file = true;
if (do_zip_file) {
- std::string entry_name = ds.baseName_ + "-" + ds.suffix_ + ".txt";
- MYLOGD("Adding main entry (%s) to .zip bugreport\n", entry_name.c_str());
- if (!finish_zip_file(entry_name, tmp_path, log_path)) {
+ if (!ds.FinishZipFile()) {
MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
do_text_file = true;
} else {
do_text_file = false;
// Since zip file is already created, it needs to be renamed.
std::string new_path = ds.GetPath(".zip");
- if (path != new_path) {
- MYLOGD("Renaming zip file from %s to %s\n", path.c_str(), new_path.c_str());
- if (rename(path.c_str(), new_path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", path.c_str(),
- new_path.c_str(), strerror(errno));
+ if (ds.path_ != new_path) {
+ MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+ if (rename(ds.path_.c_str(), new_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+ strerror(errno));
} else {
- path = new_path;
+ ds.path_ = new_path;
}
}
}
}
if (do_text_file) {
- path = ds.GetPath(".txt");
- MYLOGD("Generating .txt bugreport at %s from %s\n", path.c_str(), tmp_path.c_str());
- if (rename(tmp_path.c_str(), path.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", tmp_path.c_str(), path.c_str(), strerror(errno));
- path.clear();
+ ds.path_ = ds.GetPath(".txt");
+ MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
+ ds.tmp_path_.c_str());
+ if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
+ strerror(errno));
+ ds.path_.clear();
}
}
if (use_control_socket) {
if (do_text_file) {
- dprintf(ds.controlSocketFd_,
+ dprintf(ds.control_socket_fd_,
"FAIL:could not create zip file, check %s "
"for more details\n",
- log_path.c_str());
+ ds.log_path_.c_str());
} else {
- dprintf(ds.controlSocketFd_, "OK:%s\n", path.c_str());
+ dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
}
}
}
@@ -1669,27 +1707,27 @@
/* tell activity manager we're done */
if (do_broadcast) {
- if (!path.empty()) {
- MYLOGI("Final bugreport path: %s\n", path.c_str());
+ if (!ds.path_.empty()) {
+ MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
// clang-format off
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
"--ei", "android.intent.extra.PID", std::to_string(getpid()),
- "--ei", "android.intent.extra.MAX", std::to_string(ds.weightTotal_),
- "--es", "android.intent.extra.BUGREPORT", path,
- "--es", "android.intent.extra.DUMPSTATE_LOG", log_path
+ "--ei", "android.intent.extra.MAX", std::to_string(ds.weight_total_),
+ "--es", "android.intent.extra.BUGREPORT", ds.path_,
+ "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
if (do_fb) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
- am_args.push_back(ds.screenshotPath_);
+ am_args.push_back(ds.screenshot_path_);
}
if (is_remote_mode) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
- am_args.push_back(SHA256_file_hash(path));
+ am_args.push_back(SHA256_file_hash(ds.path_));
send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
} else {
send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
@@ -1699,16 +1737,16 @@
}
}
- MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weightTotal_, WEIGHT_TOTAL);
+ MYLOGD("Final progress: %d/%d (originally %d)\n", ds.progress_, ds.weight_total_, WEIGHT_TOTAL);
MYLOGI("done (id %lu)\n", ds.id_);
if (is_redirecting) {
fclose(stderr);
}
- if (use_control_socket && ds.controlSocketFd_ != -1) {
+ if (use_control_socket && ds.control_socket_fd_ != -1) {
MYLOGD("Closing control socket\n");
- close(ds.controlSocketFd_);
+ close(ds.control_socket_fd_);
}
return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 54d91f7..950f185 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -37,6 +37,9 @@
#include <string>
#include <vector>
+#include <android-base/macros.h>
+#include <ziparchive/zip_writer.h>
+
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
// TODO: remove once not used
@@ -92,6 +95,8 @@
std::string title_;
FILE* out_;
uint64_t started_;
+
+ DISALLOW_COPY_AND_ASSIGN(DurationReporter);
};
/*
@@ -119,9 +124,9 @@
long timeout_;
bool always_;
- RootMode rootMode_;
- StdoutMode stdoutMode_;
- std::string loggingMessage_;
+ RootMode root_mode_;
+ StdoutMode stdout_mode_;
+ std::string logging_message_;
friend class CommandOptions;
friend class CommandOptionsBuilder;
@@ -129,7 +134,7 @@
CommandOptions(const CommandOptionsValues& values);
- const CommandOptionsValues values_;
+ const CommandOptionsValues values;
public:
class CommandOptionsBuilder {
@@ -150,7 +155,7 @@
private:
CommandOptionsBuilder(long timeout);
- CommandOptionsValues values_;
+ CommandOptionsValues values;
friend class CommandOptions;
};
@@ -197,7 +202,18 @@
*
* See bugreport-format.md for more info.
*/
-static std::string VERSION_DEFAULT = "1.0";
+static std::string VERSION_CURRENT = "1.0";
+
+/*
+ * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version
+ * will be bumped to 2.0-dev-1.
+ */
+static std::string VERSION_SPLIT_ANR = "2.0-dev-1";
+
+/*
+ * "Alias" for the current version.
+ */
+static std::string VERSION_DEFAULT = "default";
/*
* Main class driving a bugreport generation.
@@ -218,12 +234,15 @@
*
* Dry-run mode is enabled by setting the system property dumpstate.dry_run to true.
*/
- bool IsDryRun();
+ bool IsDryRun() const;
/*
* Gets whether device is running a `user` build.
*/
- bool IsUserBuild();
+ bool IsUserBuild() const;
+
+ /* Checkes whether dumpstate is generating a zipped bugreport. */
+ bool IsZipping() const;
/*
* Forks a command, waits for it to finish, and returns its status.
@@ -246,12 +265,12 @@
* description).
* |dumpsys_args| `dumpsys` arguments (except `-t`).
* |options| optional argument defining the command's behavior.
- * |dumpsysTimeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
+ * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the
* timeout from `options`)
*/
- void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+ void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
- long dumpsysTimeout = 0);
+ long dumpsys_timeout = 0);
/*
* Prints the contents of a file.
@@ -263,6 +282,26 @@
int DumpFile(const std::string& title, const std::string& path);
/*
+ * Adds a new entry to the existing zip file.
+ * */
+ bool AddZipEntry(const std::string& entry_name, const std::string& entry_path);
+
+ /*
+ * Adds a new entry to the existing zip file.
+ */
+ bool AddZipEntryFromFd(const std::string& entry_name, int fd);
+
+ /*
+ * Adds a text entry entry to the existing zip file.
+ */
+ bool AddTextZipEntry(const std::string& entry_name, const std::string& content);
+
+ /*
+ * Adds all files from a directory to the zipped bugreport file.
+ */
+ void AddDir(const std::string& dir, bool recursive);
+
+ /*
* Takes a screenshot and save it to the given `path`.
*
* If `path` is empty, uses a standard path based on the bugreport name.
@@ -277,10 +316,16 @@
void UpdateProgress(int delta);
/* Prints the dumpstate header on `stdout`. */
- void PrintHeader();
+ void PrintHeader() const;
+
+ /*
+ * Adds the temporary report to the existing .zip file, closes the .zip file, and removes the
+ * temporary file.
+ */
+ bool FinishZipFile();
/* Gets the path of a bugreport file with the given suffix. */
- std::string GetPath(const std::string& suffix);
+ std::string GetPath(const std::string& suffix) const;
// TODO: initialize fields on constructor
@@ -288,53 +333,79 @@
unsigned long id_;
// Whether progress updates should be published.
- bool updateProgress_ = false;
+ bool update_progress_ = false;
// Whether it should take an screenshot earlier in the process.
- bool doEarlyScreenshot_ = false;
+ bool do_early_screenshot_ = false;
// Currrent progress.
int progress_ = 0;
// Total estimated progress.
- int weightTotal_ = WEIGHT_TOTAL;
+ int weight_total_ = WEIGHT_TOTAL;
// When set, defines a socket file-descriptor use to report progress to bugreportz.
- int controlSocketFd_ = -1;
+ int control_socket_fd_ = -1;
// Bugreport format version;
- std::string version_ = VERSION_DEFAULT;
+ std::string version_ = VERSION_CURRENT;
// Command-line arguments as string
std::string args_;
// Extra options passed as system property.
- std::string extraOptions_;
+ std::string extra_options_;
// Full path of the directory where the bugreport files will be written.
- std::string bugreportDir_;
+ std::string bugreport_dir_;
// Full path of the temporary file containing the screenshot (when requested).
- std::string screenshotPath_;
+ std::string screenshot_path_;
time_t now_;
- // Suffix of the bugreport files - it's typically the date (when invoked with -d),
- // although it could be changed by the user using a system property.
- std::string suffix_;
+ // Base name (without suffix or extensions) of the bugreport files, typically
+ // `bugreport-BUILD_ID`.
+ std::string base_name_;
- // Base name (without suffix or extensions) of the bugreport files.
- std::string baseName_;
+ // Name is the suffix part of the bugreport files - it's typically the date (when invoked with
+ // `-d`), but it could be changed by the user..
+ std::string name_;
+
+ // Full path of the temporary file containing the bugreport.
+ std::string tmp_path_;
+
+ // Full path of the file containing the dumpstate logs.
+ std::string log_path_;
+
+ // Pointer to the actual path, be it zip or text.
+ std::string path_;
+
+ // Pointer to the zipped file.
+ std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose};
+
+ // Pointer to the zip structure.
+ std::unique_ptr<ZipWriter> zip_writer_;
private:
// Used by GetInstance() only.
- Dumpstate(bool dryRun = false, const std::string& buildType = "user");
+ Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
+ const std::string& build_type = "user");
+
+ // Internal version of RunCommand that just runs it, without updating progress.
+ int JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+ const CommandOptions& options) const;
+
+ // Internal version of RunCommand that just dumps it, without updating progress.
+ int JustDumpFile(const std::string& title, const std::string& path) const;
// Whether this is a dry run.
- bool dryRun_;
+ bool dry_run_;
// Build type (such as 'user' or 'eng').
- std::string buildType_;
+ std::string build_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
// for_each_pid_func = void (*)(int, const char*);
@@ -343,15 +414,6 @@
typedef void(for_each_pid_func)(int, const char*);
typedef void(for_each_tid_func)(int, int, const char*);
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry(const std::string& entry_name, const std::string& entry_path);
-
-/* adds a new entry to the existing zip file. */
-bool add_zip_entry_from_fd(const std::string& entry_name, int fd);
-
-/* adds all files from a directory to the zipped bugreport file */
-void add_dir(const std::string& dir, bool recursive);
-
/* saves the the contents of a file as a long */
int read_file_as_long(const char *path, long int *output);
diff --git a/cmds/dumpstate/libdumpstate_default.cpp b/cmds/dumpstate/libdumpstate_default.cpp
index fd840df..f3997ab 100644
--- a/cmds/dumpstate/libdumpstate_default.cpp
+++ b/cmds/dumpstate/libdumpstate_default.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include "dumpstate.h"
void dumpstate_board(void)
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 8d70704..4073c3c 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -52,15 +52,15 @@
void SetUp() {
SetDryRun(false);
SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)"));
- ds.updateProgress_ = false;
+ ds.update_progress_ = false;
}
// Runs a command and capture `stdout` and `stderr`.
- int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+ int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
CaptureStdout();
CaptureStderr();
- int status = ds.RunCommand(title, fullCommand, options);
+ int status = ds.RunCommand(title, full_command, options);
out = GetCapturedStdout();
err = GetCapturedStderr();
return status;
@@ -76,14 +76,14 @@
return status;
}
- void SetDryRun(bool dryRun) {
- ALOGD("Setting dryRun_ to %s\n", dryRun ? "true" : "false");
- ds.dryRun_ = dryRun;
+ void SetDryRun(bool dry_run) {
+ ALOGD("Setting dry_run_ to %s\n", dry_run ? "true" : "false");
+ ds.dry_run_ = dry_run;
}
- void SetBuildType(const std::string& buildType) {
- ALOGD("Setting buildType_ to '%s'\n", buildType.c_str());
- ds.buildType_ = buildType;
+ void SetBuildType(const std::string& build_type) {
+ ALOGD("Setting build_type_ to '%s'\n", build_type.c_str());
+ ds.build_type_ = build_type;
}
bool IsUserBuild() {
@@ -97,41 +97,41 @@
}
// TODO: remove when progress is set by Binder callbacks.
- void AssertSystemProperty(const std::string& key, const std::string& expectedValue) {
+ void AssertSystemProperty(const std::string& key, const std::string& expected_value) {
std::string actualValue = android::base::GetProperty(key, "not set");
- EXPECT_THAT(expectedValue, StrEq(actualValue)) << "invalid value for property " << key;
+ EXPECT_THAT(expected_value, StrEq(actualValue)) << "invalid value for property " << key;
}
- std::string GetProgressMessage(int progress, int weightTotal, int oldWeightTotal = 0) {
+ std::string GetProgressMessage(int progress, int weigh_total, int old_weigh_total = 0) {
EXPECT_EQ(progress, ds.progress_) << "invalid progress";
- EXPECT_EQ(weightTotal, ds.weightTotal_) << "invalid weightTotal";
+ EXPECT_EQ(weigh_total, ds.weight_total_) << "invalid weigh_total";
AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
std::to_string(progress));
- bool maxIncreased = oldWeightTotal > 0;
+ bool max_increased = old_weigh_total > 0;
- std::string adjustmentMessage = "";
- if (maxIncreased) {
+ std::string adjustment_message = "";
+ if (max_increased) {
AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
- std::to_string(weightTotal));
- adjustmentMessage = android::base::StringPrintf(
- "Adjusting total weight from %d to %d\n", oldWeightTotal, weightTotal);
+ std::to_string(weigh_total));
+ adjustment_message = android::base::StringPrintf(
+ "Adjusting total weight from %d to %d\n", old_weigh_total, weigh_total);
}
return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
- adjustmentMessage.c_str(), getpid(), progress,
- weightTotal);
+ adjustment_message.c_str(), getpid(), progress,
+ weigh_total);
}
// `stdout` and `stderr` from the last command ran.
std::string out, err;
- std::string testPath = dirname(android::base::GetExecutablePath().c_str());
- std::string fixturesPath = testPath + "/../dumpstate_test_fixture/";
- std::string testDataPath = fixturesPath + "/testdata/";
- std::string simpleCommand = fixturesPath + "dumpstate_test_fixture";
- std::string echoCommand = "/system/bin/echo";
+ std::string test_path = dirname(android::base::GetExecutablePath().c_str());
+ std::string fixtures_path = test_path + "/../dumpstate_test_fixture/";
+ std::string test_data_path = fixtures_path + "/testdata/";
+ std::string simple_command = fixtures_path + "dumpstate_test_fixture";
+ std::string echo_command = "/system/bin/echo";
Dumpstate& ds = Dumpstate::GetInstance();
};
@@ -141,52 +141,52 @@
}
TEST_F(DumpstateTest, RunCommandNoTitle) {
- EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+ EXPECT_EQ(0, RunCommand("", {simple_command}));
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("stderr\n"));
}
TEST_F(DumpstateTest, RunCommandWithTitle) {
- EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleCommand}));
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
EXPECT_THAT(err, StrEq("stderr\n"));
// We don't know the exact duration, so we check the prefix and suffix
EXPECT_THAT(out,
- StartsWith("------ I AM GROOT (" + simpleCommand + ") ------\nstdout\n------"));
+ StartsWith("------ I AM GROOT (" + simple_command + ") ------\nstdout\n------"));
EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
}
TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
EXPECT_EQ(
- 0, RunCommand("", {simpleCommand},
+ 0, RunCommand("", {simple_command},
CommandOptions::WithTimeout(10).Log("COMMAND, Y U NO LOG FIRST?").Build()));
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("COMMAND, Y U NO LOG FIRST?stderr\n"));
}
TEST_F(DumpstateTest, RunCommandRedirectStderr) {
- EXPECT_EQ(0, RunCommand("", {simpleCommand},
+ EXPECT_EQ(0, RunCommand("", {simple_command},
CommandOptions::WithTimeout(10).RedirectStderr().Build()));
EXPECT_THAT(out, IsEmpty());
EXPECT_THAT(err, StrEq("stdout\nstderr\n"));
}
TEST_F(DumpstateTest, RunCommandWithOneArg) {
- EXPECT_EQ(0, RunCommand("", {echoCommand, "one"}));
+ EXPECT_EQ(0, RunCommand("", {echo_command, "one"}));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("one\n"));
}
TEST_F(DumpstateTest, RunCommandWithMultipleArgs) {
- EXPECT_EQ(0, RunCommand("", {echoCommand, "one", "is", "the", "loniest", "number"}));
+ EXPECT_EQ(0, RunCommand("", {echo_command, "one", "is", "the", "loniest", "number"}));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("one is the loniest number\n"));
}
TEST_F(DumpstateTest, RunCommandDryRun) {
SetDryRun(true);
- EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleCommand}));
+ EXPECT_EQ(0, RunCommand("I AM GROOT", {simple_command}));
// We don't know the exact duration, so we check the prefix and suffix
- EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleCommand +
+ EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simple_command +
") ------\n\t(skipped on dry run)\n------"));
EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
EXPECT_THAT(err, IsEmpty());
@@ -194,14 +194,14 @@
TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
SetDryRun(true);
- EXPECT_EQ(0, RunCommand("", {simpleCommand}));
+ EXPECT_EQ(0, RunCommand("", {simple_command}));
EXPECT_THAT(out, IsEmpty());
EXPECT_THAT(err, IsEmpty());
}
TEST_F(DumpstateTest, RunCommandDryRunAlways) {
SetDryRun(true);
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Always().Build()));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Always().Build()));
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("stderr\n"));
}
@@ -213,28 +213,28 @@
}
TEST_F(DumpstateTest, RunCommandFails) {
- EXPECT_EQ(42, RunCommand("", {simpleCommand, "--exit", "42"}));
- EXPECT_THAT(
- out, StrEq("stdout\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
- EXPECT_THAT(
- err, StrEq("stderr\n*** command '" + simpleCommand + " --exit 42' failed: exit code 42\n"));
+ EXPECT_EQ(42, RunCommand("", {simple_command, "--exit", "42"}));
+ EXPECT_THAT(out, StrEq("stdout\n*** command '" + simple_command +
+ " --exit 42' failed: exit code 42\n"));
+ EXPECT_THAT(err, StrEq("stderr\n*** command '" + simple_command +
+ " --exit 42' failed: exit code 42\n"));
}
TEST_F(DumpstateTest, RunCommandCrashes) {
- EXPECT_NE(0, RunCommand("", {simpleCommand, "--crash"}));
+ EXPECT_NE(0, RunCommand("", {simple_command, "--crash"}));
// We don't know the exit code, so check just the prefix.
EXPECT_THAT(
- out, StartsWith("stdout\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+ out, StartsWith("stdout\n*** command '" + simple_command + " --crash' failed: exit code"));
EXPECT_THAT(
- err, StartsWith("stderr\n*** command '" + simpleCommand + " --crash' failed: exit code"));
+ err, StartsWith("stderr\n*** command '" + simple_command + " --crash' failed: exit code"));
}
TEST_F(DumpstateTest, RunCommandTimesout) {
- EXPECT_EQ(-1, RunCommand("", {simpleCommand, "--sleep", "2"},
+ EXPECT_EQ(-1, RunCommand("", {simple_command, "--sleep", "2"},
CommandOptions::WithTimeout(1).Build()));
- EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simpleCommand +
+ EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + simple_command +
" --sleep 2' timed out after 1"));
- EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simpleCommand +
+ EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + simple_command +
" --sleep 2' timed out after 1"));
}
@@ -243,7 +243,7 @@
CaptureStderr();
std::thread t([=]() {
- EXPECT_EQ(SIGTERM, ds.RunCommand("", {simpleCommand, "--pid", "--sleep", "20"},
+ EXPECT_EQ(SIGTERM, ds.RunCommand("", {simple_command, "--pid", "--sleep", "20"},
CommandOptions::WithTimeout(100).Always().Build()));
});
@@ -270,53 +270,53 @@
out = GetCapturedStdout();
err = GetCapturedStderr();
- EXPECT_THAT(out, StrEq("*** command '" + simpleCommand +
+ EXPECT_THAT(out, StrEq("*** command '" + simple_command +
" --pid --sleep 20' failed: killed by signal 15\n"));
- EXPECT_THAT(err, StrEq("*** command '" + simpleCommand +
+ EXPECT_THAT(err, StrEq("*** command '" + simple_command +
" --pid --sleep 20' failed: killed by signal 15\n"));
}
TEST_F(DumpstateTest, RunCommandProgress) {
- ds.updateProgress_ = true;
+ ds.update_progress_ = true;
ds.progress_ = 0;
- ds.weightTotal_ = 30;
+ ds.weight_total_ = 30;
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(20).Build()));
- std::string progressMessage = GetProgressMessage(20, 30);
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
+ std::string progress_message = GetProgressMessage(20, 30);
EXPECT_THAT(out, StrEq("stdout\n"));
- EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(10).Build()));
- progressMessage = GetProgressMessage(30, 30);
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
+ progress_message = GetProgressMessage(30, 30);
EXPECT_THAT(out, StrEq("stdout\n"));
- EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
// Run a command that will increase maximum timeout.
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).Build()));
- progressMessage = GetProgressMessage(31, 36, 30); // 20% increase
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(31, 36, 30); // 20% increase
EXPECT_THAT(out, StrEq("stdout\n"));
- EXPECT_THAT(err, StrEq("stderr\n" + progressMessage));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
- // Make sure command ran while in dryRun is counted.
+ // Make sure command ran while in dry_run is counted.
SetDryRun(true);
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(4).Build()));
- progressMessage = GetProgressMessage(35, 36);
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
+ progress_message = GetProgressMessage(35, 36);
EXPECT_THAT(out, IsEmpty());
- EXPECT_THAT(err, StrEq(progressMessage));
+ EXPECT_THAT(err, StrEq(progress_message));
}
TEST_F(DumpstateTest, RunCommandDropRoot) {
// First check root case - only available when running with 'adb root'.
uid_t uid = getuid();
if (uid == 0) {
- EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"}));
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"}));
EXPECT_THAT(out, StrEq("0\nstdout\n"));
EXPECT_THAT(err, StrEq("stderr\n"));
return;
}
// Then drop root.
- EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"},
CommandOptions::WithTimeout(1).DropRoot().Build()));
EXPECT_THAT(out, StrEq("2000\nstdout\n"));
EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n"));
@@ -330,11 +330,11 @@
DropRoot();
- EXPECT_EQ(0, RunCommand("", {simpleCommand}, CommandOptions::WithTimeout(1).AsRoot().Build()));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).AsRoot().Build()));
// We don't know the exact path of su, so we just check for the 'root ...' commands
EXPECT_THAT(out, StartsWith("Skipping"));
- EXPECT_THAT(out, EndsWith("root " + simpleCommand + "' on user build.\n"));
+ EXPECT_THAT(out, EndsWith("root " + simple_command + "' on user build.\n"));
EXPECT_THAT(err, IsEmpty());
}
@@ -346,7 +346,7 @@
DropRoot();
- EXPECT_EQ(0, RunCommand("", {simpleCommand, "--uid"},
+ EXPECT_EQ(0, RunCommand("", {simple_command, "--uid"},
CommandOptions::WithTimeout(1).AsRoot().Build()));
EXPECT_THAT(out, StrEq("0\nstdout\n"));
@@ -370,55 +370,55 @@
}
TEST_F(DumpstateTest, DumpFileSingleLine) {
- EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
}
TEST_F(DumpstateTest, DumpFileSingleLineWithNewLine) {
- EXPECT_EQ(0, DumpFile("", testDataPath + "single-line-with-newline.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line-with-newline.txt"));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("I AM LINE1\n"));
}
TEST_F(DumpstateTest, DumpFileMultipleLines) {
- EXPECT_EQ(0, DumpFile("", testDataPath + "multiple-lines.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "multiple-lines.txt"));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
}
TEST_F(DumpstateTest, DumpFileMultipleLinesWithNewLine) {
- EXPECT_EQ(0, DumpFile("", testDataPath + "multiple-lines-with-newline.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "multiple-lines-with-newline.txt"));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, StrEq("I AM LINE1\nI AM LINE2\nI AM LINE3\n"));
}
TEST_F(DumpstateTest, DumpFileOnDryRunNoTitle) {
SetDryRun(true);
- EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(out, IsEmpty());
}
TEST_F(DumpstateTest, DumpFileOnDryRun) {
SetDryRun(true);
- EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", testDataPath + "single-line.txt"));
+ EXPECT_EQ(0, DumpFile("Might as well dump. Dump!", test_data_path + "single-line.txt"));
EXPECT_THAT(err, IsEmpty());
- EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + testDataPath +
+ EXPECT_THAT(out, StartsWith("------ Might as well dump. Dump! (" + test_data_path +
"single-line.txt) ------\n\t(skipped on dry run)\n------"));
EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
EXPECT_THAT(err, IsEmpty());
}
TEST_F(DumpstateTest, DumpFileUpdateProgress) {
- ds.updateProgress_ = true;
+ ds.update_progress_ = true;
ds.progress_ = 0;
- ds.weightTotal_ = 30;
+ ds.weight_total_ = 30;
- EXPECT_EQ(0, DumpFile("", testDataPath + "single-line.txt"));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
- std::string progressMessage = GetProgressMessage(5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+ std::string progress_message = GetProgressMessage(5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
- EXPECT_THAT(err, StrEq(progressMessage));
+ EXPECT_THAT(err, StrEq(progress_message));
EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 83fcf21..c41cca4 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -17,29 +17,31 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
#include <limits.h>
#include <poll.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string>
#include <string.h>
#include <sys/capability.h>
#include <sys/inotify.h>
+#include <sys/klog.h>
+#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
-#include <sys/klog.h>
#include <time.h>
#include <unistd.h>
+#include <string>
#include <vector>
-#include <sys/prctl.h>
#define LOG_TAG "dumpstate"
#include <android-base/file.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <cutils/debugger.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -58,9 +60,9 @@
// TODO: temporary variables and functions used during C++ refactoring
static Dumpstate& ds = Dumpstate::GetInstance();
-static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+static int RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options = CommandOptions::DEFAULT) {
- return ds.RunCommand(title, fullCommand, options);
+ return ds.RunCommand(title, full_command, options);
}
static bool IsDryRun() {
return Dumpstate::GetInstance().IsDryRun();
@@ -94,82 +96,83 @@
CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
-CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values_(timeout) {
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values(timeout) {
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
- values_.always_ = true;
+ values.always_ = true;
return *this;
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() {
- values_.rootMode_ = SU_ROOT;
+ values.root_mode_ = SU_ROOT;
return *this;
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::DropRoot() {
- values_.rootMode_ = DROP_ROOT;
+ values.root_mode_ = DROP_ROOT;
return *this;
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::RedirectStderr() {
- values_.stdoutMode_ = REDIRECT_TO_STDERR;
+ values.stdout_mode_ = REDIRECT_TO_STDERR;
return *this;
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log(
const std::string& message) {
- values_.loggingMessage_ = message;
+ values.logging_message_ = message;
return *this;
}
CommandOptions CommandOptions::CommandOptionsBuilder::Build() {
- return CommandOptions(values_);
+ return CommandOptions(values);
}
CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
: timeout_(timeout),
always_(false),
- rootMode_(DONT_DROP_ROOT),
- stdoutMode_(NORMAL_STDOUT),
- loggingMessage_("") {
+ root_mode_(DONT_DROP_ROOT),
+ stdout_mode_(NORMAL_STDOUT),
+ logging_message_("") {
}
-CommandOptions::CommandOptions(const CommandOptionsValues& values) : values_(values) {
+CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
}
long CommandOptions::Timeout() const {
- return values_.timeout_;
+ return values.timeout_;
}
bool CommandOptions::Always() const {
- return values_.always_;
+ return values.always_;
}
RootMode CommandOptions::RootMode() const {
- return values_.rootMode_;
+ return values.root_mode_;
}
StdoutMode CommandOptions::StdoutMode() const {
- return values_.stdoutMode_;
+ return values.stdout_mode_;
}
std::string CommandOptions::LoggingMessage() const {
- return values_.loggingMessage_;
+ return values.logging_message_;
}
CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
return CommandOptions::CommandOptionsBuilder(timeout);
}
-Dumpstate::Dumpstate(bool dryRun, const std::string& buildType)
- : now_(time(nullptr)), dryRun_(dryRun), buildType_(buildType) {
+Dumpstate::Dumpstate(const std::string& version, bool dry_run, const std::string& build_type)
+ : version_(version), now_(time(nullptr)), dry_run_(dry_run), build_type_(build_type) {
}
Dumpstate& Dumpstate::GetInstance() {
- static Dumpstate sSingleton(android::base::GetBoolProperty("dumpstate.dry_run", false),
+ static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT),
+ android::base::GetBoolProperty("dumpstate.dry_run", false),
android::base::GetProperty("ro.build.type", "(unknown)"));
- return sSingleton;
+ return singleton_;
}
DurationReporter::DurationReporter(const std::string& title) : DurationReporter(title, stdout) {
@@ -200,16 +203,17 @@
return (uint64_t) ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
}
-bool Dumpstate::IsDryRun() {
- return dryRun_;
+bool Dumpstate::IsDryRun() const {
+ return dry_run_;
}
-bool Dumpstate::IsUserBuild() {
- return "user" == buildType_;
+bool Dumpstate::IsUserBuild() const {
+ return "user" == build_type_;
}
-std::string Dumpstate::GetPath(const std::string& suffix) {
- return bugreportDir_ + "/" + baseName_ + "-" + suffix_ + suffix;
+std::string Dumpstate::GetPath(const std::string& suffix) const {
+ return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
+ name_.c_str(), suffix.c_str());
}
void for_each_userid(void (*func)(int), const char *header) {
@@ -525,6 +529,7 @@
RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT_10);
}
+// TODO: when converted to a Dumpstate function, it should be const
static int _dump_file_from_fd(const std::string& title, const char* path, int fd) {
if (!title.empty()) {
printf("------ %s (%s", title.c_str(), path);
@@ -543,11 +548,6 @@
}
printf(") ------\n");
}
- if (IsDryRun()) {
- UpdateProgress(WEIGHT_FILE);
- close(fd);
- return 0;
- }
bool newline = false;
fd_set read_set;
@@ -594,7 +594,7 @@
}
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
- DurationReporter durationReporter(title);
+ DurationReporter duration_reporter(title);
if (IsDryRun()) {
if (!title.empty()) {
printf("------ %s (%s) ------\n", title.c_str(), path.c_str());
@@ -603,7 +603,10 @@
UpdateProgress(WEIGHT_FILE);
return 0;
}
+ return JustDumpFile(title, path);
+}
+int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const {
int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
if (fd < 0) {
int err = errno;
@@ -772,41 +775,41 @@
return true;
}
-int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
+int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command,
const CommandOptions& options) {
- if (fullCommand.empty()) {
+ if (full_command.empty()) {
MYLOGE("No arguments on command '%s'\n", title.c_str());
return -1;
}
- int size = fullCommand.size() + 1; // null terminated
- int startingIndex = 0;
+ int size = full_command.size() + 1; // null terminated
+ int starting_index = 0;
if (options.RootMode() == SU_ROOT) {
- startingIndex = 2; // "su" "root"
- size += startingIndex;
+ starting_index = 2; // "su" "root"
+ size += starting_index;
}
std::vector<const char*> args;
args.resize(size);
- std::string commandString;
+ std::string command_string;
if (options.RootMode() == SU_ROOT) {
args[0] = SU_PATH;
- commandString += SU_PATH;
+ command_string += SU_PATH;
args[1] = "root";
- commandString += " root ";
+ command_string += " root ";
}
- int i = startingIndex;
- for (auto arg = fullCommand.begin(); arg != fullCommand.end(); ++arg) {
+ int i = starting_index;
+ for (auto arg = full_command.begin(); arg != full_command.end(); ++arg) {
args[i++] = arg->c_str();
- commandString += arg->c_str();
- if (arg != fullCommand.end() - 1) {
- commandString += " ";
+ command_string += arg->c_str();
+ if (arg != full_command.end() - 1) {
+ command_string += " ";
}
}
args[i] = nullptr;
const char* path = args[0];
- const char* command = commandString.c_str();
+ const char* command = command_string.c_str();
if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
printf("Skipping '%s' on user build.\n", command);
@@ -818,11 +821,11 @@
}
fflush(stdout);
- DurationReporter durationReporter(title);
+ DurationReporter duration_reporter(title);
- const std::string& loggingMessage = options.LoggingMessage();
- if (!loggingMessage.empty()) {
- MYLOGI(loggingMessage.c_str(), commandString.c_str());
+ const std::string& logging_message = options.LoggingMessage();
+ if (!logging_message.empty()) {
+ MYLOGI(logging_message.c_str(), command_string.c_str());
}
if (IsDryRun() && !options.Always()) {
@@ -833,7 +836,7 @@
return 0;
}
- bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
+ int status = JustRunCommand(command, path, args, options);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -841,6 +844,17 @@
* Ideally, it should use a options.EstimatedDuration() instead...*/
int weight = options.Timeout();
+ if (weight > 0) {
+ UpdateProgress(weight);
+ }
+
+ return status;
+}
+
+int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
+ const CommandOptions& options) const {
+ bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
+
uint64_t start = DurationReporter::Nanotime();
pid_t pid = fork();
@@ -925,17 +939,14 @@
MYLOGE("*** command '%s' failed: exit code %d\n", command, status);
}
- if (weight > 0) {
- UpdateProgress(weight);
- }
return status;
}
-void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
+void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
const CommandOptions& options, long dumpsysTimeout) {
long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout();
std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)};
- dumpsys.insert(dumpsys.end(), dumpsysArgs.begin(), dumpsysArgs.end());
+ dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end());
RunCommand(title, dumpsys, options);
}
@@ -1127,32 +1138,31 @@
/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
const char *dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES", nullptr);
- if (IsDryRun()) return nullptr;
+ DurationReporter duration_reporter("DUMP TRACES");
const char* result = nullptr;
- std::string tracesPath = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
- if (tracesPath.empty()) return nullptr;
+ std::string traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
+ if (traces_path.empty()) return nullptr;
/* move the old traces.txt (if any) out of the way temporarily */
- std::string anrTracesPath = tracesPath + ".anr";
- if (rename(tracesPath.c_str(), anrTracesPath.c_str()) && errno != ENOENT) {
- MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), anrTracesPath.c_str(), strerror(errno));
+ std::string anrtraces_path = traces_path + ".anr";
+ if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno));
return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = TEMP_FAILURE_RETRY(open(tracesPath.c_str(),
+ int fd = TEMP_FAILURE_RETRY(open(traces_path.c_str(),
O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC,
0666)); /* -rw-rw-rw- */
if (fd < 0) {
- MYLOGE("%s: %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno));
return nullptr;
}
int chmod_ret = fchmod(fd, 0666);
if (chmod_ret < 0) {
- MYLOGE("fchmod on %s failed: %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno));
close(fd);
return nullptr;
}
@@ -1175,9 +1185,9 @@
goto error_close_fd;
}
- wfd = inotify_add_watch(ifd, tracesPath.c_str(), IN_CLOSE_WRITE);
+ wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE);
if (wfd < 0) {
- MYLOGE("inotify_add_watch(%s): %s\n", tracesPath.c_str(), strerror(errno));
+ MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno));
goto error_close_ifd;
}
@@ -1261,15 +1271,17 @@
MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
}
- static std::string dumpTracesPath = tracesPath + ".bugreport";
- if (rename(tracesPath.c_str(), dumpTracesPath.c_str())) {
- MYLOGE("rename(%s, %s): %s\n", tracesPath.c_str(), dumpTracesPath.c_str(), strerror(errno));
+ static std::string dumptraces_path = android::base::StringPrintf(
+ "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str()));
+ if (rename(traces_path.c_str(), dumptraces_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(),
+ strerror(errno));
goto error_close_ifd;
}
- result = dumpTracesPath.c_str();
+ result = dumptraces_path.c_str();
/* replace the saved [ANR] traces.txt file */
- rename(anrTracesPath.c_str(), tracesPath.c_str());
+ rename(anrtraces_path.c_str(), traces_path.c_str());
error_close_ifd:
close(ifd);
@@ -1301,7 +1313,7 @@
// TODO: make this function thread safe if sections are generated in parallel.
void Dumpstate::UpdateProgress(int delta) {
- if (!updateProgress_) return;
+ if (!update_progress_) return;
progress_ += delta;
@@ -1309,12 +1321,12 @@
char value[PROPERTY_VALUE_MAX];
// adjusts max on the fly
- if (progress_ > weightTotal_) {
- int newTotal = weightTotal_ * 1.2;
- MYLOGD("Adjusting total weight from %d to %d\n", weightTotal_, newTotal);
- weightTotal_ = newTotal;
+ if (progress_ > weight_total_) {
+ int new_total = weight_total_ * 1.2;
+ MYLOGD("Adjusting total weight from %d to %d\n", weight_total_, new_total);
+ weight_total_ = new_total;
snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
- snprintf(value, sizeof(value), "%d", weightTotal_);
+ snprintf(value, sizeof(value), "%d", weight_total_);
int status = property_set(key, value);
if (status != 0) {
MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
@@ -1327,16 +1339,16 @@
if (progress_ % 100 == 0) {
// We don't want to spam logcat, so only log multiples of 100.
- MYLOGD("Setting progress (%s): %s/%d\n", key, value, weightTotal_);
+ MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
} else {
// stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
// directly for debuggging.
- fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weightTotal_);
+ fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
}
- if (controlSocketFd_ >= 0) {
- dprintf(controlSocketFd_, "PROGRESS:%d/%d\n", progress_, weightTotal_);
- fsync(controlSocketFd_);
+ if (control_socket_fd_ >= 0) {
+ dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress_, weight_total_);
+ fsync(control_socket_fd_);
}
int status = property_set(key, value);
@@ -1347,14 +1359,14 @@
}
void Dumpstate::TakeScreenshot(const std::string& path) {
- const std::string& realPath = path.empty() ? screenshotPath_ : path;
+ const std::string& real_path = path.empty() ? screenshot_path_ : path;
int status =
- RunCommand("", {"/system/bin/screencap", "-p", realPath},
+ RunCommand("", {"/system/bin/screencap", "-p", real_path},
CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build());
if (status == 0) {
- MYLOGD("Screenshot saved on %s\n", realPath.c_str());
+ MYLOGD("Screenshot saved on %s\n", real_path.c_str());
} else {
- MYLOGE("Failed to take screenshot on %s\n", realPath.c_str());
+ MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
}
}
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 2df48aa..078db26 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -104,7 +104,7 @@
* if the label of that top-level file actually changed. This can save us
* significant time by avoiding no-op traversals of large filesystem trees.
*/
-static int restorecon_app_data_lazy(const char* path, const char* seinfo, uid_t uid) {
+static int restorecon_app_data_lazy(const std::string& path, const char* seinfo, uid_t uid) {
int res = 0;
char* before = nullptr;
char* after = nullptr;
@@ -112,15 +112,15 @@
// Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
// libselinux. Not needed here.
- if (lgetfilecon(path, &before) < 0) {
+ if (lgetfilecon(path.c_str(), &before) < 0) {
PLOG(ERROR) << "Failed before getfilecon for " << path;
goto fail;
}
- if (selinux_android_restorecon_pkgdir(path, seinfo, uid, 0) < 0) {
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid, 0) < 0) {
PLOG(ERROR) << "Failed top-level restorecon for " << path;
goto fail;
}
- if (lgetfilecon(path, &after) < 0) {
+ if (lgetfilecon(path.c_str(), &after) < 0) {
PLOG(ERROR) << "Failed after getfilecon for " << path;
goto fail;
}
@@ -130,7 +130,7 @@
if (strcmp(before, after)) {
LOG(DEBUG) << "Detected label change from " << before << " to " << after << " at " << path
<< "; running recursive restorecon";
- if (selinux_android_restorecon_pkgdir(path, seinfo, uid,
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seinfo, uid,
SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
PLOG(ERROR) << "Failed recursive restorecon for " << path;
goto fail;
@@ -146,6 +146,11 @@
return res;
}
+static int restorecon_app_data_lazy(const std::string& parent, const char* name, const char* seinfo,
+ uid_t uid) {
+ return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seinfo, uid);
+}
+
static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << path;
@@ -172,7 +177,9 @@
}
// Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path.c_str(), seinfo, uid)) {
+ if (restorecon_app_data_lazy(path, seinfo, uid) ||
+ restorecon_app_data_lazy(path, "cache", seinfo, uid) ||
+ restorecon_app_data_lazy(path, "code_cache", seinfo, uid)) {
return -1;
}
@@ -191,7 +198,7 @@
}
// Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path.c_str(), seinfo, uid)) {
+ if (restorecon_app_data_lazy(path, seinfo, uid)) {
return -1;
}
diff --git a/cmds/surfacereplayer/proto/Android.mk b/cmds/surfacereplayer/proto/Android.mk
index b87d34f..3cf1148 100644
--- a/cmds/surfacereplayer/proto/Android.mk
+++ b/cmds/surfacereplayer/proto/Android.mk
@@ -18,10 +18,7 @@
LOCAL_SRC_FILES := $(call all-proto-files-under, src)
-LOCAL_SHARED_LIBRARIES := \
- libprotobuf-cpp-full
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_OPTIMIZE_TYPE := lite
LOCAL_MODULE := libtrace_proto
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 45060af..0bc08a9 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -1,4 +1,5 @@
syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
message Trace {
repeated Increment increment = 1;
diff --git a/cmds/surfacereplayer/replayer/Android.mk b/cmds/surfacereplayer/replayer/Android.mk
index dac4314..3ec3178 100644
--- a/cmds/surfacereplayer/replayer/Android.mk
+++ b/cmds/surfacereplayer/replayer/Android.mk
@@ -40,7 +40,8 @@
libgui \
libui \
libutils \
- libprotobuf-cpp-full \
+ libprotobuf-cpp-lite \
+ libbase \
LOCAL_STATIC_LIBRARIES := \
libtrace_proto \
@@ -57,7 +58,7 @@
Main.cpp \
LOCAL_SHARED_LIBRARIES := \
- libprotobuf-cpp-full \
+ libprotobuf-cpp-lite \
libsurfacereplayer \
libutils \
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index ace10d1..a49da37 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -20,6 +20,8 @@
#include <android/native_window.h>
+#include <android-base/file.h>
+
#include <binder/IMemory.h>
#include <gui/BufferQueue.h>
@@ -61,14 +63,18 @@
mStopTimeStamp(stopHere) {
srand(RAND_COLOR_SEED);
- std::fstream input(filename, std::ios::in | std::ios::binary);
-
- mLoaded = mTrace.ParseFromIstream(&input);
- if (!mLoaded) {
+ std::string input;
+ if (!android::base::ReadFileToString(filename, &input, true)) {
std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
abort();
}
+ mLoaded = mTrace.ParseFromString(input);
+ if (!mLoaded) {
+ std::cerr << "Trace did not load." << std::endl;
+ abort();
+ }
+
mCurrentTime = mTrace.increment(0).time_stamp();
sReplayingManually.store(replayManually);
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index f10cbc3..467e0fd 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -51,8 +51,8 @@
<!-- Feature to specify if the device support managed users. -->
<feature name="android.software.managed_users" />
- <!-- Feature to specify if the device supports a VR mode. -->
- <feature name="android.software.vr.mode" />
+ <!-- Feature to specify if the device supports a VR mode.
+ feature name="android.software.vr.mode" -->
<!-- Devices with all optimizations required to be a "VR Ready" device that
pass all CTS tests for this feature must include feature
android.hardware.vr.high_performance -->
diff --git a/include/binder/TextOutput.h b/include/binder/TextOutput.h
index 974a194..851e01f 100644
--- a/include/binder/TextOutput.h
+++ b/include/binder/TextOutput.h
@@ -18,16 +18,15 @@
#define ANDROID_TEXTOUTPUT_H
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <stdint.h>
#include <string.h>
+#include <sstream>
// ---------------------------------------------------------------------------
namespace android {
-class String8;
-class String16;
-
class TextOutput
{
public:
@@ -66,30 +65,26 @@
TextOutput& indent(TextOutput& to);
TextOutput& dedent(TextOutput& to);
-TextOutput& operator<<(TextOutput& to, const char* str);
-TextOutput& operator<<(TextOutput& to, char); // writes raw character
-TextOutput& operator<<(TextOutput& to, bool);
-TextOutput& operator<<(TextOutput& to, int);
-TextOutput& operator<<(TextOutput& to, long);
-TextOutput& operator<<(TextOutput& to, unsigned int);
-TextOutput& operator<<(TextOutput& to, unsigned long);
-TextOutput& operator<<(TextOutput& to, long long);
-TextOutput& operator<<(TextOutput& to, unsigned long long);
-TextOutput& operator<<(TextOutput& to, float);
-TextOutput& operator<<(TextOutput& to, double);
-TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
-TextOutput& operator<<(TextOutput& to, const void*);
-TextOutput& operator<<(TextOutput& to, const String8& val);
-TextOutput& operator<<(TextOutput& to, const String16& val);
+template<typename T>
+TextOutput& operator<<(TextOutput& to, const T& val)
+{
+ std::stringstream strbuf;
+ strbuf << val;
+ std::string str = strbuf.str();
+ to.print(str.c_str(), str.size());
+ return to;
+}
-class TypeCode
+TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func);
+
+class TypeCode
{
public:
inline TypeCode(uint32_t code);
inline ~TypeCode();
inline uint32_t typeCode() const;
-
+
private:
uint32_t mCode;
};
@@ -124,6 +119,32 @@
};
TextOutput& operator<<(TextOutput& to, const HexDump& val);
+inline TextOutput& operator<<(TextOutput& to,
+ decltype(std::endl<char,
+ std::char_traits<char>>)
+ /*val*/) {
+ endl(to);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const char &c)
+{
+ to.print(&c, 1);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const bool &val)
+{
+ if (val) to.print("true", 4);
+ else to.print("false", 5);
+ return to;
+}
+
+inline TextOutput& operator<<(TextOutput& to, const String16& val)
+{
+ to << String8(val).string();
+ return to;
+}
// ---------------------------------------------------------------------------
// No user servicable parts below.
@@ -146,18 +167,6 @@
return to;
}
-inline TextOutput& operator<<(TextOutput& to, const char* str)
-{
- to.print(str, strlen(str));
- return to;
-}
-
-inline TextOutput& operator<<(TextOutput& to, char c)
-{
- to.print(&c, 1);
- return to;
-}
-
inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func)
{
return (*func)(to);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 9b5f0d7..c9f688f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -64,10 +64,6 @@
namespace android {
-static const char* getReturnString(size_t idx);
-static const void* printReturnCommand(TextOutput& out, const void* _cmd);
-static const void* printCommand(TextOutput& out, const void* _cmd);
-
// Static const and functions will be optimized out if not used,
// when LOG_NDEBUG and references in IF_LOG_COMMANDS() are optimized out.
static const char *kReturnStrings[] = {
@@ -111,8 +107,9 @@
"BC_DEAD_BINDER_DONE"
};
-static const char* getReturnString(size_t idx)
+static const char* getReturnString(uint32_t cmd)
{
+ size_t idx = cmd & 0xff;
if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
return kReturnStrings[idx];
else
@@ -526,8 +523,8 @@
}
} while (result != -ECONNREFUSED && result != -EBADF);
- LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
- (void*)pthread_self(), getpid(), (void*)result);
+ LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
+ (void*)pthread_self(), getpid(), result);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp
index 2ed5188..101eba3 100644
--- a/libs/binder/TextOutput.cpp
+++ b/libs/binder/TextOutput.cpp
@@ -29,111 +29,14 @@
// ---------------------------------------------------------------------------
-TextOutput::TextOutput() {
+TextOutput::TextOutput() {
}
-TextOutput::~TextOutput() {
+TextOutput::~TextOutput() {
}
// ---------------------------------------------------------------------------
-TextOutput& operator<<(TextOutput& to, bool val)
-{
- if (val) to.print("true", 4);
- else to.print("false", 5);
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, int val)
-{
- char buf[16];
- sprintf(buf, "%d", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long val)
-{
- char buf[16];
- sprintf(buf, "%ld", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned int val)
-{
- char buf[16];
- sprintf(buf, "%u", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long val)
-{
- char buf[16];
- sprintf(buf, "%lu", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, long long val)
-{
- char buf[32];
- sprintf(buf, "%Ld", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, unsigned long long val)
-{
- char buf[32];
- sprintf(buf, "%Lu", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-static TextOutput& print_float(TextOutput& to, double value)
-{
- char buf[64];
- sprintf(buf, "%g", value);
- if( !strchr(buf, '.') && !strchr(buf, 'e') &&
- !strchr(buf, 'E') ) {
- strncat(buf, ".0", sizeof(buf)-1);
- }
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, float val)
-{
- return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, double val)
-{
- return print_float(to,val);
-}
-
-TextOutput& operator<<(TextOutput& to, const void* val)
-{
- char buf[32];
- snprintf(buf, sizeof(buf), "%p", val);
- to.print(buf, strlen(buf));
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
- to << val.string();
- return to;
-}
-
-TextOutput& operator<<(TextOutput& to, const String16& val)
-{
- to << String8(val).string();
- return to;
-}
-
static void textOutputPrinter(void* cookie, const char* txt)
{
((TextOutput*)cookie)->print(txt, strlen(txt));
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 4f77eed..2152206 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -51,3 +51,13 @@
"-O3",
],
}
+
+cc_test {
+ name: "binderTextOutputTest",
+ srcs: ["binderTextOutputTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+}
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index e02844e..ff5912f 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -229,7 +229,9 @@
.sender_euid = 0,
.data_size = 0,
.offsets_size = 0,
- .data = {0, 0},
+ .data = {
+ .ptr = {0, 0},
+ },
},
};
struct {
@@ -350,4 +352,3 @@
return RUN_ALL_TESTS();
}
-
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index cb0e965..757291c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -34,6 +34,7 @@
static testing::Environment* binder_env;
static char *binderservername;
+static char *binderserversuffix;
static char binderserverarg[] = "--binderserver";
static String16 binderLibTestServiceName = String16("test.binderLib");
@@ -72,6 +73,7 @@
binderserverarg,
stri,
strpipefd1,
+ binderserversuffix,
NULL
};
@@ -969,6 +971,8 @@
int run_server(int index, int readypipefd)
{
+ binderLibTestServiceName += String16(binderserversuffix);
+
status_t ret;
sp<IServiceManager> sm = defaultServiceManager();
{
@@ -999,15 +1003,19 @@
int main(int argc, char **argv) {
int ret;
- if (argc == 3 && !strcmp(argv[1], "--servername")) {
+ if (argc == 4 && !strcmp(argv[1], "--servername")) {
binderservername = argv[2];
} else {
binderservername = argv[0];
}
- if (argc == 4 && !strcmp(argv[1], binderserverarg)) {
+ if (argc == 5 && !strcmp(argv[1], binderserverarg)) {
+ binderserversuffix = argv[4];
return run_server(atoi(argv[2]), atoi(argv[3]));
}
+ binderserversuffix = new char[16];
+ snprintf(binderserversuffix, 16, "%d", getpid());
+ binderLibTestServiceName += String16(binderserversuffix);
::testing::InitGoogleTest(&argc, argv);
binder_env = AddGlobalTestEnvironment(new BinderLibTestEnv());
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp
new file mode 100644
index 0000000..f6dd22d
--- /dev/null
+++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2016 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits>
+#include <cstddef>
+
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+#include <gtest/gtest.h>
+
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include <binder/Debug.h>
+
+static void CheckMessage(const CapturedStderr& cap,
+ const char* expected,
+ bool singleline) {
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ if (singleline)
+ output.erase(std::remove(output.begin(), output.end(), '\n'));
+ ASSERT_STREQ(output.c_str(), expected);
+}
+
+#define CHECK_LOG_(input, expect, singleline) \
+{ \
+ CapturedStderr cap; \
+ android::aerr << input << android::endl; \
+ CheckMessage(cap, expect, singleline); \
+} \
+
+#define CHECK_VAL_(val, singleline) \
+{ \
+ std::stringstream ss; \
+ ss << val; \
+ std::string s = ss.str(); \
+ CHECK_LOG_(val, s.c_str(), singleline); \
+} \
+
+#define CHECK_LOG(input, expect) CHECK_LOG_(input, expect, true)
+#define CHECK_VAL(val) CHECK_VAL_(val, true)
+
+TEST(TextOutput, HandlesStdEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << std::endl;
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesCEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << "\n";
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandlesAndroidEndl) {
+ CapturedStderr cap;
+ android::aerr << "foobar" << android::endl;
+ std::string output;
+ ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+ android::base::ReadFdToString(cap.fd(), &output);
+ ASSERT_STREQ(output.c_str(), "foobar\n");
+}
+
+TEST(TextOutput, HandleEmptyString) {
+ CHECK_LOG("", "");
+}
+
+TEST(TextOutput, HandleString) {
+ CHECK_LOG("foobar", "foobar");
+}
+
+TEST(TextOutput, HandleNum) {
+ CHECK_LOG(12345, "12345");
+}
+
+TEST(TextOutput, HandleBool) {
+ CHECK_LOG(false, "false");
+}
+
+TEST(TextOutput, HandleChar) {
+ CHECK_LOG('T', "T");
+}
+
+TEST(TextOutput, HandleParcel) {
+ android::Parcel val;
+ CHECK_LOG(val, "Parcel(NULL)");
+}
+
+TEST(TextOutput, HandleHexDump) {
+ const char buf[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+ android::HexDump val(buf, sizeof(buf));
+ CHECK_LOG(val, "03020100 07060504 0b0a0908 0f0e0d0c '................'");
+}
+
+TEST(TextOutput, HandleHexDumpCustom) {
+ const char buf[4] = {0x11,0x22,0x33,0x44};
+ android::HexDump val(buf, sizeof(buf), 4);
+ CHECK_LOG(val, "11 22 33 44 '.\"3D'");
+}
+
+TEST(TextOutput, HandleTypeCode) {
+ android::TypeCode val(1234);
+ CHECK_LOG(val, "'\\x04\\xd2'");
+}
+
+TEST(TextOutput, HandleCookie) {
+ int32_t val = 321; //0x141
+ CHECK_LOG((void*)(long)val, "0x141");
+}
+
+TEST(TextOutput, HandleString8) {
+ android::String8 val("foobar");
+ CHECK_LOG(val, "foobar");
+}
+
+TEST(TextOutput, HandleString16) {
+ android::String16 val("foobar");
+ CHECK_LOG(val, "foobar");
+}
+
+template <typename T>
+class TextTest : public testing::Test {};
+
+typedef testing::Types<short, unsigned short,
+ int, unsigned int,
+ long, unsigned long,
+ long long, unsigned long long,
+ float, double, long double> TestTypes;
+TYPED_TEST_CASE(TextTest, TestTypes);
+
+TYPED_TEST(TextTest, TextMax)
+{
+ TypeParam max = std::numeric_limits<TypeParam>::max();
+ CHECK_VAL(max);
+}
+
+TYPED_TEST(TextTest, TestMin)
+{
+ TypeParam min = std::numeric_limits<TypeParam>::min();
+ CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestDenom)
+{
+ TypeParam min = std::numeric_limits<TypeParam>::denorm_min();
+ CHECK_VAL(min);
+}
+
+TYPED_TEST(TextTest, TestEpsilon)
+{
+ TypeParam eps = std::numeric_limits<TypeParam>::epsilon();
+ CHECK_VAL(eps);
+}
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index aa0db45..10e999c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -1228,14 +1228,19 @@
EGL_NONE,
};
if (!crop.isValid()) {
- // No crop rect to set, so terminate the attrib array before the crop.
- attrs[2] = EGL_NONE;
+ // No crop rect to set, so leave the crop out of the attrib array. Make
+ // sure to propagate the protected content attrs if they are set.
+ attrs[2] = attrs[10];
+ attrs[3] = attrs[11];
+ attrs[4] = EGL_NONE;
} else if (!isEglImageCroppable(crop)) {
// The crop rect is not at the origin, so we can't set the crop on the
// EGLImage because that's not allowed by the EGL_ANDROID_image_crop
// extension. In the future we can add a layered extension that
// removes this restriction if there is hardware that can support it.
- attrs[2] = EGL_NONE;
+ attrs[2] = attrs[10];
+ attrs[3] = attrs[11];
+ attrs[4] = EGL_NONE;
}
eglInitialize(dpy, 0, 0);
EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 164be20..c520bda 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -17,6 +17,7 @@
from: "include",
to: "",
srcs: ["include/EGL/**/*.h"],
+ license: "include/EGL/NOTICE",
}
ndk_headers {
@@ -24,6 +25,7 @@
from: "include",
to: "",
srcs: ["include/GLES/**/*.h"],
+ license: "include/GLES/NOTICE",
}
ndk_headers {
@@ -31,6 +33,7 @@
from: "include",
to: "",
srcs: ["include/GLES2/**/*.h"],
+ license: "include/GLES2/NOTICE",
}
ndk_headers {
@@ -38,6 +41,7 @@
from: "include",
to: "",
srcs: ["include/GLES3/**/*.h"],
+ license: "include/GLES3/NOTICE",
}
ndk_headers {
@@ -45,6 +49,7 @@
from: "include",
to: "",
srcs: ["include/KHR/**/*.h"],
+ license: "include/KHR/NOTICE",
}
subdirs = [
diff --git a/opengl/include/EGL/NOTICE b/opengl/include/EGL/NOTICE
new file mode 100644
index 0000000..55f5efa
--- /dev/null
+++ b/opengl/include/EGL/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2007-2009 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/GLES/NOTICE b/opengl/include/GLES/NOTICE
new file mode 100644
index 0000000..4dc6614
--- /dev/null
+++ b/opengl/include/GLES/NOTICE
@@ -0,0 +1,2 @@
+This document is licensed under the SGI Free Software B License Version 2.0.
+For details, see http://oss.sgi.com/projects/FreeB/ .
diff --git a/opengl/include/GLES2/NOTICE b/opengl/include/GLES2/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES2/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/GLES3/NOTICE b/opengl/include/GLES3/NOTICE
new file mode 100644
index 0000000..7a94373
--- /dev/null
+++ b/opengl/include/GLES3/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2013-2015 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/opengl/include/KHR/NOTICE b/opengl/include/KHR/NOTICE
new file mode 100644
index 0000000..36796e8
--- /dev/null
+++ b/opengl/include/KHR/NOTICE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2009 The Khronos Group Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and/or associated documentation files (the
+"Materials"), to deal in the Materials without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Materials, and to
+permit persons to whom the Materials are furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index f695edb..ba036dd 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -145,7 +145,7 @@
libgui \
libpowermanager \
libvulkan \
- libprotobuf-cpp-full \
+ libprotobuf-cpp-lite \
libhidl \
libhwbinder \
libbase \
diff --git a/services/surfaceflinger/DdmConnection.h b/services/surfaceflinger/DdmConnection.h
index b6b088b..938d14b 100644
--- a/services/surfaceflinger/DdmConnection.h
+++ b/services/surfaceflinger/DdmConnection.h
@@ -24,6 +24,9 @@
class DdmConnection {
public:
+ // Creates a JVM and registers all handlers to DDMS.
+ // This allows tools relying on DDMS to find surfaceflinger
+ // (e.g: Memory Leak finder, heap analyzer, ...)
static void start(const char* name);
};
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e5b57f5..a0e040b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1836,12 +1836,9 @@
void SurfaceFlinger::commitTransaction()
{
- sp<const DisplayDevice> hw = getDefaultDisplayDevice();
-
- if (!mLayersPendingRemoval.isEmpty() && hw->isDisplayOn()) {
+ if (!mLayersPendingRemoval.isEmpty()) {
// Notify removed layers now that they can't be drawn from
for (size_t i = 0; i < mLayersPendingRemoval.size(); i++) {
- mCurrentState.layersSortedByZ.remove(mLayersPendingRemoval[i]);
recordBufferingStats(mLayersPendingRemoval[i]->getName().string(),
mLayersPendingRemoval[i]->getOccupancyHistory(true));
mLayersPendingRemoval[i]->onRemoved();
@@ -2288,10 +2285,14 @@
return NO_ERROR;
}
- mLayersPendingRemoval.push(layer);
- mLayersRemoved = true;
- setTransactionFlags(eTransactionNeeded);
- return NO_ERROR;
+ ssize_t index = mCurrentState.layersSortedByZ.remove(layer);
+ if (index >= 0) {
+ mLayersPendingRemoval.push(layer);
+ mLayersRemoved = true;
+ setTransactionFlags(eTransactionNeeded);
+ return NO_ERROR;
+ }
+ return status_t(index);
}
uint32_t SurfaceFlinger::peekTransactionFlags(uint32_t /* flags */) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 4ae3580..a12276a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -20,6 +20,9 @@
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "SurfaceInterceptor.h"
+
+#include <android-base/file.h>
+
#include <cutils/log.h>
#include <utils/Trace.h>
@@ -119,14 +122,18 @@
status_t SurfaceInterceptor::writeProtoFileLocked() {
ATRACE_CALL();
- std::ofstream output(mOutputFileName, std::ios::out | std::ios::trunc | std::ios::binary);
- // SerializeToOstream returns false when it's missing required data or when it could not write
+ std::string output;
+
if (!mTrace.IsInitialized()) {
return NOT_ENOUGH_DATA;
}
- if (!mTrace.SerializeToOstream(&output)) {
+ if (!mTrace.SerializeToString(&output)) {
return PERMISSION_DENIED;
}
+ if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
+ return PERMISSION_DENIED;
+ }
+
return NO_ERROR;
}
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index 97d99d0..d97cf5e 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -17,6 +17,7 @@
from: "include",
to: "",
srcs: ["include/vulkan/**/*.h"],
+ license: "include/vulkan/NOTICE",
}
subdirs = [
diff --git a/vulkan/include/vulkan/NOTICE b/vulkan/include/vulkan/NOTICE
new file mode 100644
index 0000000..c958fba
--- /dev/null
+++ b/vulkan/include/vulkan/NOTICE
@@ -0,0 +1,13 @@
+Copyright (c) 2015-2016 The Khronos Group Inc.
+
+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.