Merge "Add --sync support to push."
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index 0756876..144efc8 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -21,6 +21,7 @@
// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.
// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.
constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash";
+constexpr char kTombstonedJavaTraceSocketName[] = "tombstoned_java_trace";
constexpr char kTombstonedInterceptSocketName[] = "tombstoned_intercept";
enum class CrashPacketType : uint8_t {
diff --git a/debuggerd/include/debuggerd/tombstoned.h b/debuggerd/include/debuggerd/tombstoned.h
index d158d50..908517d 100644
--- a/debuggerd/include/debuggerd/tombstoned.h
+++ b/debuggerd/include/debuggerd/tombstoned.h
@@ -21,6 +21,6 @@
#include <android-base/unique_fd.h>
bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,
- android::base::unique_fd* output_fd);
+ android::base::unique_fd* output_fd, bool is_native_crash = true);
bool tombstoned_notify_completion(int tombstoned_socket);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 2248a21..80dbef5 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -50,86 +50,154 @@
kCrashStatusQueued,
};
+struct Crash;
+
+class CrashType {
+ public:
+ CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,
+ size_t max_concurrent_dumps)
+ : file_name_prefix_(file_name_prefix),
+ dir_path_(dir_path),
+ dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),
+ max_artifacts_(max_artifacts),
+ next_artifact_(0),
+ max_concurrent_dumps_(max_concurrent_dumps),
+ num_concurrent_dumps_(0) {
+ if (dir_fd_ == -1) {
+ PLOG(FATAL) << "failed to open directory: " << dir_path;
+ }
+
+ // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the
+ // same filename could be handed out to multiple processes.
+ CHECK(max_artifacts_ > max_concurrent_dumps_);
+
+ find_oldest_artifact();
+ }
+
+ unique_fd get_output_fd() {
+ unique_fd result;
+ char buf[PATH_MAX];
+ snprintf(buf, sizeof(buf), "%s%02d", file_name_prefix_.c_str(), next_artifact_);
+ // Unlink and create the file, instead of using O_TRUNC, to avoid two processes
+ // interleaving their output in case we ever get into that situation.
+ if (unlinkat(dir_fd_, buf, 0) != 0 && errno != ENOENT) {
+ PLOG(FATAL) << "failed to unlink tombstone at " << dir_path_ << buf;
+ }
+
+ result.reset(openat(dir_fd_, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
+ if (result == -1) {
+ PLOG(FATAL) << "failed to create tombstone at " << dir_path_ << buf;
+ }
+
+ next_artifact_ = (next_artifact_ + 1) % max_artifacts_;
+ return result;
+ }
+
+ bool maybe_enqueue_crash(Crash* crash) {
+ if (num_concurrent_dumps_ == max_concurrent_dumps_) {
+ queued_requests_.push_back(crash);
+ return true;
+ }
+
+ return false;
+ }
+
+ void maybe_dequeue_crashes(void (*handler)(Crash* crash)) {
+ while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {
+ Crash* next_crash = queued_requests_.front();
+ queued_requests_.pop_front();
+ handler(next_crash);
+ }
+ }
+
+ void on_crash_started() { ++num_concurrent_dumps_; }
+
+ void on_crash_completed() { --num_concurrent_dumps_; }
+
+ static CrashType* const tombstone;
+ static CrashType* const java_trace;
+
+ private:
+ void find_oldest_artifact() {
+ size_t oldest_tombstone = 0;
+ time_t oldest_time = std::numeric_limits<time_t>::max();
+
+ for (size_t i = 0; i < max_artifacts_; ++i) {
+ std::string path = android::base::StringPrintf("%s/%s%02zu", dir_path_.c_str(),
+ file_name_prefix_.c_str(), i);
+ struct stat st;
+ if (stat(path.c_str(), &st) != 0) {
+ if (errno == ENOENT) {
+ oldest_tombstone = i;
+ break;
+ } else {
+ PLOG(ERROR) << "failed to stat " << path;
+ continue;
+ }
+ }
+
+ if (st.st_mtime < oldest_time) {
+ oldest_tombstone = i;
+ oldest_time = st.st_mtime;
+ }
+ }
+
+ next_artifact_ = oldest_tombstone;
+ }
+
+ const std::string file_name_prefix_;
+
+ const std::string dir_path_;
+ const int dir_fd_;
+
+ const size_t max_artifacts_;
+ int next_artifact_;
+
+ const size_t max_concurrent_dumps_;
+ size_t num_concurrent_dumps_;
+
+ std::deque<Crash*> queued_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashType);
+};
+
+// Whether java trace dumps are produced via tombstoned.
+static constexpr bool kJavaTraceDumpsEnabled = false;
+
+/* static */ CrashType* const CrashType::tombstone =
+ new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */,
+ 1 /* max_concurrent_dumps */);
+
+/* static */ CrashType* const CrashType::java_trace =
+ (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */,
+ 64 /* max_artifacts */, 4 /* max_concurrent_dumps */)
+ : nullptr);
+
// Ownership of Crash is a bit messy.
// It's either owned by an active event that must have a timeout, or owned by
// queued_requests, in the case that multiple crashes come in at the same time.
struct Crash {
- ~Crash() {
- event_free(crash_event);
- }
+ ~Crash() { event_free(crash_event); }
unique_fd crash_fd;
pid_t crash_pid;
event* crash_event = nullptr;
+
+ // Not owned by |Crash|.
+ CrashType* crash_type = nullptr;
};
-static constexpr char kTombstoneDirectory[] = "/data/tombstones/";
-static constexpr size_t kTombstoneCount = 10;
-static int tombstone_directory_fd = -1;
-static int next_tombstone = 0;
-
-static constexpr size_t kMaxConcurrentDumps = 1;
-static size_t num_concurrent_dumps = 0;
-
-static std::deque<Crash*> queued_requests;
-
// Forward declare the callbacks so they can be placed in a sensible order.
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*);
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);
static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);
-static void find_oldest_tombstone() {
- size_t oldest_tombstone = 0;
- time_t oldest_time = std::numeric_limits<time_t>::max();
-
- for (size_t i = 0; i < kTombstoneCount; ++i) {
- std::string path = android::base::StringPrintf("%stombstone_%02zu", kTombstoneDirectory, i);
- struct stat st;
- if (stat(path.c_str(), &st) != 0) {
- if (errno == ENOENT) {
- oldest_tombstone = i;
- break;
- } else {
- PLOG(ERROR) << "failed to stat " << path;
- continue;
- }
- }
-
- if (st.st_mtime < oldest_time) {
- oldest_tombstone = i;
- oldest_time = st.st_mtime;
- }
- }
-
- next_tombstone = oldest_tombstone;
-}
-
-static unique_fd get_tombstone_fd() {
- // If kMaxConcurrentDumps is greater than 1, then theoretically the same
- // filename could be handed out to multiple processes. Unlink and create the
- // file, instead of using O_TRUNC, to avoid two processes interleaving their
- // output.
- unique_fd result;
- char buf[PATH_MAX];
- snprintf(buf, sizeof(buf), "tombstone_%02d", next_tombstone);
- if (unlinkat(tombstone_directory_fd, buf, 0) != 0 && errno != ENOENT) {
- PLOG(FATAL) << "failed to unlink tombstone at " << kTombstoneDirectory << buf;
- }
-
- result.reset(
- openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
- if (result == -1) {
- PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
- }
-
- next_tombstone = (next_tombstone + 1) % kTombstoneCount;
- return result;
-}
-
static void perform_request(Crash* crash) {
unique_fd output_fd;
- if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
- output_fd = get_tombstone_fd();
+ // Note that java traces are not interceptible.
+ if ((crash->crash_type == CrashType::java_trace) ||
+ !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
+ output_fd = crash->crash_type->get_output_fd();
}
TombstonedCrashPacket response = {
@@ -152,23 +220,15 @@
event_add(crash->crash_event, &timeout);
}
- ++num_concurrent_dumps;
+ crash->crash_type->on_crash_started();
return;
fail:
delete crash;
}
-static void dequeue_requests() {
- while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- perform_request(next_crash);
- }
-}
-
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
- void*) {
+ void* crash_type) {
event_base* base = evconnlistener_get_base(listener);
Crash* crash = new Crash();
@@ -176,12 +236,15 @@
event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
crash->crash_fd.reset(sockfd);
crash->crash_event = crash_event;
+ crash->crash_type = static_cast<CrashType*>(crash_type);
event_add(crash_event, &timeout);
}
static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
ssize_t rc;
Crash* crash = static_cast<Crash*>(arg);
+ CrashType* type = crash->crash_type;
+
TombstonedCrashPacket request = {};
if ((ev & EV_TIMEOUT) != 0) {
@@ -208,12 +271,27 @@
goto fail;
}
- crash->crash_pid = request.packet.dump_request.pid;
+ if (type == CrashType::tombstone) {
+ crash->crash_pid = request.packet.dump_request.pid;
+ } else {
+ // Requests for java traces are sent from untrusted processes, so we
+ // must not trust the PID sent down with the request. Instead, we ask the
+ // kernel.
+ ucred cr = {};
+ socklen_t len = sizeof(cr);
+ int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
+ if (ret != 0) {
+ PLOG(ERROR) << "Failed to getsockopt(..SO_PEERCRED)";
+ goto fail;
+ }
+
+ crash->crash_pid = cr.pid;
+ }
+
LOG(INFO) << "received crash request for pid " << crash->crash_pid;
- if (num_concurrent_dumps == kMaxConcurrentDumps) {
+ if (type->maybe_enqueue_crash(crash)) {
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
- queued_requests.push_back(crash);
} else {
perform_request(crash);
}
@@ -229,7 +307,7 @@
Crash* crash = static_cast<Crash*>(arg);
TombstonedCrashPacket request = {};
- --num_concurrent_dumps;
+ crash->crash_type->on_crash_completed();
if ((ev & EV_READ) == 0) {
goto fail;
@@ -252,10 +330,11 @@
}
fail:
+ CrashType* type = crash->crash_type;
delete crash;
// If there's something queued up, let them proceed.
- dequeue_requests();
+ type->maybe_dequeue_crashes(perform_request);
}
int main(int, char* []) {
@@ -269,13 +348,6 @@
};
debuggerd_register_handlers(&action);
- tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
- if (tombstone_directory_fd == -1) {
- PLOG(FATAL) << "failed to open tombstone directory";
- }
-
- find_oldest_tombstone();
-
int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);
int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);
@@ -293,10 +365,24 @@
intercept_manager = new InterceptManager(base, intercept_socket);
- evconnlistener* listener =
- evconnlistener_new(base, crash_accept_cb, nullptr, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
- if (!listener) {
- LOG(FATAL) << "failed to create evconnlistener";
+ evconnlistener* tombstone_listener = evconnlistener_new(
+ base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ if (!tombstone_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for tombstones.";
+ }
+
+ if (kJavaTraceDumpsEnabled) {
+ const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);
+ if (java_trace_socket == -1) {
+ PLOG(FATAL) << "failed to get socket from init";
+ }
+
+ evutil_make_socket_nonblocking(java_trace_socket);
+ evconnlistener* java_trace_listener = evconnlistener_new(
+ base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+ if (!java_trace_listener) {
+ LOG(FATAL) << "failed to create evconnlistener for java traces.";
+ }
}
LOG(INFO) << "tombstoned successfully initialized";
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index b8345ca..53ef01c 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -7,4 +7,5 @@
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
+ socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/tombstoned_client.cpp b/debuggerd/tombstoned_client.cpp
index 4741fa6..e878b6a 100644
--- a/debuggerd/tombstoned_client.cpp
+++ b/debuggerd/tombstoned_client.cpp
@@ -30,9 +30,11 @@
using android::base::unique_fd;
-bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd) {
- unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
+ bool is_native_crash) {
+ unique_fd sockfd(socket_local_client(
+ (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName),
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
if (sockfd == -1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s",
strerror(errno));
diff --git a/init/devices.cpp b/init/devices.cpp
index d8258cf..e0351a3 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -30,6 +30,7 @@
#include <string.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
+#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/wait.h>
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 838406d..cdfc698 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -331,7 +331,7 @@
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
- const std::set<std::string> to_starts{"watchdogd", "vold"};
+ const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 2b3443f..919b65b 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -139,14 +139,8 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/odm/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/odm/default.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/odm/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/odm/etc/fs_config_files" },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/oem/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/oem/etc/fs_config_files" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
@@ -163,10 +157,6 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/vendor/build.prop" },
- { 00600, AID_ROOT, AID_ROOT, 0, "system/vendor/default.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/vendor/etc/fs_config_dirs" },
- { 00444, AID_ROOT, AID_ROOT, 0, "system/vendor/etc/fs_config_files" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
@@ -202,17 +192,11 @@
// Support RT scheduling in Bluetooth
{ 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_SYS_NICE),
- "system/vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
- { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
- CAP_MASK_LONG(CAP_SYS_NICE),
"vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
// Support wifi_hal_legacy administering a network interface.
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
- "system/vendor/bin/hw/android.hardware.wifi@1.0-service" },
- { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
- CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hw/android.hardware.wifi@1.0-service" },
// A non-privileged zygote that spawns isolated processes for web rendering.
@@ -233,8 +217,6 @@
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/lib64/valgrind/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" },
{ 00755, AID_ROOT, AID_SHELL, 0, "vendor/xbin/*" },
@@ -273,6 +255,36 @@
return fd;
}
+// if path is "vendor/<stuff>", "oem/<stuff>" or "odm/<stuff>"
+static bool is_partition(const char* path, size_t len) {
+ static const char* partitions[] = {"vendor/", "oem/", "odm/"};
+ for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
+ size_t plen = strlen(partitions[i]);
+ if (len <= plen) continue;
+ if (!strncmp(path, partitions[i], plen)) return true;
+ }
+ return false;
+}
+
+// alias prefixes of "<partition>/<stuff>" to "system/<partition>/<stuff>" or
+// "system/<partition>/<stuff>" to "<partition>/<stuff>"
+static bool prefix_cmp(const char* prefix, const char* path, size_t len) {
+ if (!strncmp(prefix, path, len)) return true;
+
+ static const char system[] = "system/";
+ if (!strncmp(path, system, strlen(system))) {
+ path += strlen(system);
+ } else if (len <= strlen(system)) {
+ return false;
+ } else if (strncmp(prefix, system, strlen(system))) {
+ return false;
+ } else {
+ prefix += strlen(system);
+ len -= strlen(system);
+ }
+ return is_partition(prefix, len) && !strncmp(prefix, path, len);
+}
+
static bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {
if (dir) {
if (plen < len) {
@@ -281,13 +293,13 @@
} else {
// If name ends in * then allow partial matches.
if (prefix[len - 1] == '*') {
- return !strncmp(prefix, path, len - 1);
+ return prefix_cmp(prefix, path, len - 1);
}
if (plen != len) {
return false;
}
}
- return !strncmp(prefix, path, len);
+ return prefix_cmp(prefix, path, len);
}
void fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,
diff --git a/libcutils/tests/fs_config.cpp b/libcutils/tests/fs_config.cpp
index 3917a0b..a62cd51 100644
--- a/libcutils/tests/fs_config.cpp
+++ b/libcutils/tests/fs_config.cpp
@@ -14,63 +14,188 @@
* limitations under the License.
*/
+#include <inttypes.h>
+
#include <string>
#include <gtest/gtest.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
-extern const struct fs_path_config* __for_testing_only__android_dirs;
-extern const struct fs_path_config* __for_testing_only__android_files;
+extern const fs_path_config* __for_testing_only__android_dirs;
+extern const fs_path_config* __for_testing_only__android_files;
-static void check_one(const struct fs_path_config* paths, const std::string& prefix,
- const std::string& alternate) {
- for (size_t idx = 0; paths[idx].prefix; ++idx) {
- std::string path(paths[idx].prefix);
- if (android::base::StartsWith(path, prefix.c_str())) {
- path = alternate + path.substr(prefix.length());
- size_t second;
- for (second = 0; paths[second].prefix; ++second) {
- if (path == paths[second].prefix) break;
+// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we
+// hit a nullptr termination, before we declare the list is just too big or
+// could be missing the nullptr.
+static constexpr size_t max_idx = 4096;
+
+static bool check_unique(std::vector<const char*>& paths, const std::string& config_name,
+ const std::string& prefix) {
+ bool retval = false;
+
+ std::string alternate = "system/" + prefix;
+
+ for (size_t idx = 0; idx < paths.size(); ++idx) {
+ size_t second;
+ std::string path(paths[idx]);
+ // check if there are multiple identical paths
+ for (second = idx + 1; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate paths in " << config_name << ": " << paths[idx];
+ retval = true;
+ break;
}
- if (!paths[second].prefix) {
- // guaranteed to fail expectations, trigger test failure with
- // a message that reports the violation as an inequality.
- EXPECT_STREQ((prefix + path.substr(alternate.length())).c_str(), path.c_str());
+ }
+
+ // check if path is <partition>/
+ if (android::base::StartsWith(path, prefix.c_str())) {
+ // rebuild path to be system/<partition>/... to check for alias
+ path = alternate + path.substr(prefix.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) {
+ GTEST_LOG_(ERROR) << "duplicate alias paths in " << config_name << ": "
+ << paths[idx] << " and " << paths[second]
+ << " (remove latter)";
+ retval = true;
+ break;
+ }
+ }
+ continue;
+ }
+
+ // check if path is system/<partition>/
+ if (android::base::StartsWith(path, alternate.c_str())) {
+ // rebuild path to be <partition>/... to check for alias
+ path = prefix + path.substr(alternate.size());
+ for (second = 0; second < paths.size(); ++second) {
+ if (path == paths[second]) break;
+ }
+ if (second >= paths.size()) {
+ GTEST_LOG_(ERROR) << "replace path in " << config_name << ": " << paths[idx]
+ << " with " << path;
+ retval = true;
}
}
}
+ return retval;
}
-static void check_two(const struct fs_path_config* paths, const std::string& prefix) {
+static bool check_unique(const fs_path_config* paths, const char* type_name,
+ const std::string& prefix) {
+ std::string config("system/core/libcutils/fs_config.cpp:android_");
+ config += type_name;
+ config += "[]";
+
+ bool retval = false;
+ std::vector<const char*> paths_tmp;
+ for (size_t idx = 0; paths[idx].prefix; ++idx) {
+ if (idx > max_idx) {
+ GTEST_LOG_(WARNING) << config << ": has no end (missing null prefix)";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(paths[idx].prefix);
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))
+
+static bool check_unique(const std::string& config, const std::string& prefix) {
+ int retval = false;
+
+ std::string data;
+ if (!android::base::ReadFileToString(config, &data)) return retval;
+
+ const fs_path_config_from_file* pc =
+ reinterpret_cast<const fs_path_config_from_file*>(data.c_str());
+ size_t len = data.size();
+
+ std::vector<const char*> paths_tmp;
+ size_t entry_number = 0;
+ while (len > 0) {
+ uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;
+ if (host_len > len) {
+ GTEST_LOG_(WARNING) << config << ": truncated at entry " << entry_number << " ("
+ << host_len << " > " << len << ")";
+ const std::string unknown("?");
+ GTEST_LOG_(WARNING)
+ << config << ": entry[" << entry_number << "]={ "
+ << "len=" << ((len >= endof(pc, len))
+ ? android::base::StringPrintf("%" PRIu16, pc->len)
+ : unknown)
+ << ", mode=" << ((len >= endof(pc, mode))
+ ? android::base::StringPrintf("0%" PRIo16, pc->mode)
+ : unknown)
+ << ", uid=" << ((len >= endof(pc, uid))
+ ? android::base::StringPrintf("%" PRIu16, pc->uid)
+ : unknown)
+ << ", gid=" << ((len >= endof(pc, gid))
+ ? android::base::StringPrintf("%" PRIu16, pc->gid)
+ : unknown)
+ << ", capabilities="
+ << ((len >= endof(pc, capabilities))
+ ? android::base::StringPrintf("0x%" PRIx64, pc->capabilities)
+ : unknown)
+ << ", prefix="
+ << ((len >= offsetof(fs_path_config_from_file, prefix))
+ ? android::base::StringPrintf(
+ "\"%.*s...", (int)(len - offsetof(fs_path_config_from_file, prefix)),
+ pc->prefix)
+ : unknown)
+ << " }";
+ retval = true;
+ break;
+ }
+ paths_tmp.push_back(pc->prefix);
+
+ pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +
+ host_len);
+ len -= host_len;
+ ++entry_number;
+ }
+
+ return check_unique(paths_tmp, config, prefix) || retval;
+}
+
+void check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {
ASSERT_FALSE(paths == nullptr);
- std::string alternate = "system/" + prefix;
- check_one(paths, prefix, alternate);
- check_one(paths, alternate, prefix);
+ ASSERT_FALSE(type_name == nullptr);
+ ASSERT_FALSE(prefix == nullptr);
+ bool check_internal = check_unique(paths, type_name, prefix);
+ EXPECT_FALSE(check_internal);
+ bool check_overrides =
+ check_unique(std::string("/") + prefix + "etc/fs_config_" + type_name, prefix);
+ EXPECT_FALSE(check_overrides);
}
TEST(fs_config, vendor_dirs_alias) {
- check_two(__for_testing_only__android_dirs, "vendor/");
+ check_two(__for_testing_only__android_dirs, "dirs", "vendor/");
}
TEST(fs_config, vendor_files_alias) {
- check_two(__for_testing_only__android_files, "vendor/");
+ check_two(__for_testing_only__android_files, "files", "vendor/");
}
TEST(fs_config, oem_dirs_alias) {
- check_two(__for_testing_only__android_dirs, "oem/");
+ check_two(__for_testing_only__android_dirs, "dirs", "oem/");
}
TEST(fs_config, oem_files_alias) {
- check_two(__for_testing_only__android_files, "oem/");
+ check_two(__for_testing_only__android_files, "files", "oem/");
}
TEST(fs_config, odm_dirs_alias) {
- check_two(__for_testing_only__android_dirs, "odm/");
+ check_two(__for_testing_only__android_dirs, "dirs", "odm/");
}
TEST(fs_config, odm_files_alias) {
- check_two(__for_testing_only__android_files, "odm/");
+ check_two(__for_testing_only__android_files, "files", "odm/");
}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ded6c8c..76a4aff 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -79,10 +79,16 @@
if (monotonic) {
if (!android::isMonotonic(e->mRealTime)) {
LogKlog::convertRealToMonotonic(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
} else {
if (android::isMonotonic(e->mRealTime)) {
LogKlog::convertMonotonicToReal(e->mRealTime);
+ if ((e->mRealTime.tv_nsec % 1000) == 0) {
+ e->mRealTime.tv_nsec++;
+ }
}
}
++it;
@@ -194,6 +200,11 @@
return -EINVAL;
}
+ // Slip the time by 1 nsec if the incoming lands on xxxxxx000 ns.
+ // This prevents any chance that an outside source can request an
+ // exact entry with time specified in ms or us precision.
+ if ((realtime.tv_nsec % 1000) == 0) ++realtime.tv_nsec;
+
LogBufferElement* elem =
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
@@ -1109,6 +1120,9 @@
LogBufferElement* element = *it;
if (element->getRealTime() > start) {
last = it;
+ } else if (element->getRealTime() == start) {
+ last = ++it;
+ break;
} else if (!--count || (element->getRealTime() < min)) {
break;
}
@@ -1116,7 +1130,7 @@
it = last;
}
- log_time max = start;
+ log_time curr = start;
LogBufferElement* lastElement = nullptr; // iterator corruption paranoia
static const size_t maxSkip = 4194304; // maximum entries to skip
@@ -1142,10 +1156,6 @@
continue;
}
- if (element->getRealTime() <= start) {
- continue;
- }
-
// NB: calling out to another object with wrlock() held (safe)
if (filter) {
int ret = (*filter)(element, arg);
@@ -1172,10 +1182,10 @@
unlock();
// range locking in LastLogTimes looks after us
- max = element->flushTo(reader, this, privileged, sameTid);
+ curr = element->flushTo(reader, this, privileged, sameTid);
- if (max == element->FLUSH_ERROR) {
- return max;
+ if (curr == element->FLUSH_ERROR) {
+ return curr;
}
skip = maxSkip;
@@ -1183,7 +1193,7 @@
}
unlock();
- return max;
+ return curr;
}
std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,