Merge "Document FlattenableHelper design decision"
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 388b506..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
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 29589cd..740eb19 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -39,6 +39,7 @@
#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>
@@ -47,7 +48,6 @@
#include <private/android_logger.h>
#include "dumpstate.h"
-#include "ziparchive/zip_writer.h"
#include <openssl/sha.h>
@@ -57,10 +57,6 @@
// TODO: variables and functions below should be part of dumpstate object
-// TODO: Can't be added to dumpstate.h because including "ziparchive/zip_writer.h" would not work.
-// That's probably because of the dumpstate -> libdumpstate -> device implementation setup, which
-// might be changed anyways - let's keep it here and wait
-static std::unique_ptr<ZipWriter> zip_writer;
static std::set<std::string> mount_points;
void add_mountinfo();
@@ -395,6 +391,81 @@
}
}
+/**
+ * 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);
@@ -619,7 +690,7 @@
}
bool Dumpstate::IsZipping() const {
- return zip_writer != nullptr;
+ return zip_writer_ != nullptr;
}
// List of file extensions that can cause a zip file attachment to be rejected by some email
@@ -651,10 +722,10 @@
// 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_));
+ 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(),
+ MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
ZipWriter::ErrorCodeString(err));
return false;
}
@@ -668,16 +739,16 @@
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();
+ err = zip_writer_->FinishEntry();
if (err != 0) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -717,23 +788,23 @@
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_);
+ 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(),
+ 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());
+ err = zip_writer_->WriteBytes(content.c_str(), content.length());
if (err != 0) {
- MYLOGE("zip_writer->WriteBytes(%s): %s\n", entry_name.c_str(),
+ MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
ZipWriter::ErrorCodeString(err));
return false;
}
- err = zip_writer->FinishEntry();
+ err = zip_writer_->FinishEntry();
if (err != 0) {
- MYLOGE("zip_writer->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -751,6 +822,87 @@
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 duration_reporter("DUMPSTATE");
unsigned long timeout;
@@ -780,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");
@@ -828,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++) {
@@ -894,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);
@@ -1037,6 +1153,11 @@
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.weight_total_, WEIGHT_TOTAL);
@@ -1128,9 +1249,9 @@
redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
fprintf(stderr, "\n");
- int32_t err = zip_writer->Finish();
+ int32_t err = zip_writer_->Finish();
if (err != 0) {
- MYLOGE("zip_writer->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
+ MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
return false;
}
@@ -1287,9 +1408,10 @@
ds.version_ = VERSION_CURRENT;
}
- if (ds.version_ != VERSION_CURRENT) {
- MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s')\n",
- ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str());
+ 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);
}
@@ -1376,7 +1498,7 @@
MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
do_zip_file = 0;
} else {
- zip_writer.reset(new ZipWriter(ds.zip_file.get()));
+ ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
}
ds.AddTextZipEntry("version.txt", ds.version_);
}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 568256a..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);
};
/*
@@ -200,6 +205,12 @@
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";
@@ -373,6 +384,9 @@
// 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(const std::string& version = VERSION_CURRENT, bool dry_run = false,
@@ -390,6 +404,8 @@
// Build type (such as 'user' or 'eng').
std::string build_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(Dumpstate);
};
// for_each_pid_func = void (*)(int, const char*);
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/utils.cpp b/cmds/dumpstate/utils.cpp
index 31c8697..c41cca4 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -17,24 +17,25 @@
#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"
@@ -1137,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;
}
@@ -1185,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;
}
@@ -1271,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);
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/include/binder/TextOutput.h b/include/binder/TextOutput.h
index 0e9975a..851e01f 100644
--- a/include/binder/TextOutput.h
+++ b/include/binder/TextOutput.h
@@ -18,6 +18,7 @@
#define ANDROID_TEXTOUTPUT_H
#include <utils/Errors.h>
+#include <utils/String8.h>
#include <stdint.h>
#include <string.h>
@@ -26,9 +27,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class String8;
-class String16;
-
class TextOutput
{
public:
@@ -121,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.
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 601df46..470fc59 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2094,11 +2094,15 @@
status_t err = NO_ERROR;
for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
- fds[i] = dup(this->readFileDescriptor());
- if (fds[i] < 0) {
+ int fd = this->readFileDescriptor();
+ if (fd < 0 || ((fds[i] = dup(fd)) < 0)) {
err = BAD_VALUE;
ALOGE("dup() failed in Parcel::read, i is %zu, fds[i] is %d, fd_count is %zu, error: %s",
- i, fds[i], fd_count, strerror(errno));
+ i, fds[i], fd_count, strerror(fd < 0 ? -fd : errno));
+ // Close all the file descriptors that were dup-ed.
+ for (size_t j=0; j<i ;j++) {
+ close(fds[j]);
+ }
}
}
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/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/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;
}