Merge changes from topic 'pre-early-mount'
* changes:
init: fstab: add support to read fstab entries from device tree
init: early_mount: add support to mount non-verity partitions early
init: remove the existing early_mount code
init: refactor: add support for doing early coldboot
ueventd: make selinux labeling optional for device creation
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 5a3b401..7afa616 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -313,3 +313,15 @@
void AdbCloser::Close(int fd) {
adb_close(fd);
}
+
+int usage(const char* fmt, ...) {
+ fprintf(stderr, "adb: ");
+
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ return 1;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 16317e0..2b59034 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,6 +21,8 @@
#include <android-base/macros.h>
+int usage(const char*, ...);
+
void close_stdin();
bool getcwd(std::string* cwd);
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9b59d05..b7e76a6 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -182,11 +182,8 @@
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
};
-// Implemented in commandline.cpp
-int usage();
-
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc > 2) return usage();
+ if (argc > 2) return usage("usage: adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 2befa0c..3b2df2e 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -231,11 +231,6 @@
// clang-format on
}
-int usage() {
- help();
- return 1;
-}
-
#if defined(_WIN32)
// Implemented in sysdeps_win32.cpp.
@@ -1235,7 +1230,7 @@
}
static int restore(int argc, const char** argv) {
- if (argc != 2) return usage();
+ if (argc != 2) return usage("restore requires an argument");
const char* filename = argv[1];
int tarFd = adb_open(filename, O_RDONLY);
@@ -1443,19 +1438,19 @@
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
} else if (!strcmp(argv[0], "--reply-fd")) {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("--reply-fd requires an argument");
const char* reply_fd_str = argv[1];
argc--;
argv++;
ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
- return usage();
+ return 1;
}
} else if (!strncmp(argv[0], "-p", 2)) {
const char* product = nullptr;
if (argv[0][2] == '\0') {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("-p requires an argument");
product = argv[1];
argc--;
argv++;
@@ -1465,13 +1460,13 @@
gProductOutPath = find_product_out_path(product);
if (gProductOutPath.empty()) {
fprintf(stderr, "adb: could not resolve \"-p %s\"\n", product);
- return usage();
+ return 1;
}
} else if (argv[0][0]=='-' && argv[0][1]=='s') {
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if (argc < 2 || argv[0][2] != '\0') return usage();
+ if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
serial = argv[1];
argc--;
argv++;
@@ -1484,7 +1479,7 @@
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("-H requires an argument");
server_host_str = argv[1];
argc--;
argv++;
@@ -1493,7 +1488,7 @@
}
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("-P requires an argument");
server_port_str = argv[1];
argc--;
argv++;
@@ -1501,7 +1496,7 @@
server_port_str = argv[0] + 2;
}
} else if (!strcmp(argv[0], "-L")) {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("-L requires an argument");
server_socket_str = argv[1];
argc--;
argv++;
@@ -1566,7 +1561,7 @@
if (no_daemon || is_daemon) {
if (is_daemon && (ack_reply_fd == -1)) {
fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
- return usage();
+ return 1;
}
r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
} else {
@@ -1579,7 +1574,8 @@
}
if (argc == 0) {
- return usage();
+ help();
+ return 1;
}
/* handle wait-for-* prefix */
@@ -1696,7 +1692,7 @@
}
}
else if (!strcmp(argv[0], "sideload")) {
- if (argc != 2) return usage();
+ if (argc != 2) return usage("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
} else {
@@ -1730,7 +1726,7 @@
bool reverse = !strcmp(argv[0], "reverse");
++argv;
--argc;
- if (argc < 1) return usage();
+ if (argc < 1) return usage("%s requires an argument", argv[0]);
// Determine the <host-prefix> for this command.
std::string host_prefix;
@@ -1750,24 +1746,24 @@
std::string cmd, error;
if (strcmp(argv[0], "--list") == 0) {
- if (argc != 1) return usage();
+ if (argc != 1) return usage("--list doesn't take any arguments");
return adb_query_command(host_prefix + ":list-forward");
} else if (strcmp(argv[0], "--remove-all") == 0) {
- if (argc != 1) return usage();
+ if (argc != 1) return usage("--remove-all doesn't take any arguments");
cmd = host_prefix + ":killforward-all";
} else if (strcmp(argv[0], "--remove") == 0) {
// forward --remove <local>
- if (argc != 2) return usage();
+ if (argc != 2) return usage("--remove requires an argument");
cmd = host_prefix + ":killforward:" + argv[1];
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
- if (argc != 3) return usage();
+ if (argc != 3) return usage("--no-rebind takes two arguments");
if (forward_targets_are_valid(argv[1], argv[2], &error)) {
cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
}
} else {
// forward <local> <remote>
- if (argc != 2) return usage();
+ if (argc != 2) return usage("forward takes two arguments");
if (forward_targets_are_valid(argv[0], argv[1], &error)) {
cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
}
@@ -1796,7 +1792,7 @@
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
- if (argc != 2) return usage();
+ if (argc != 2) return usage("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
@@ -1805,7 +1801,7 @@
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty() || !dst) return usage();
+ if (srcs.empty() || !dst) return usage("push requires an argument");
return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
@@ -1814,22 +1810,22 @@
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty()) return usage();
+ if (srcs.empty()) return usage("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("install requires an argument");
if (_use_legacy_install()) {
return install_app_legacy(transport_type, serial, argc, argv);
}
return install_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("install-multiple requires an argument");
return install_multiple_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
- if (argc < 2) return usage();
+ if (argc < 2) return usage("uninstall requires an argument");
if (_use_legacy_install()) {
return uninstall_app_legacy(transport_type, serial, argc, argv);
}
@@ -1852,12 +1848,12 @@
// A local path or "android"/"data" arg was specified.
src = argv[1];
} else {
- return usage();
+ return usage("usage: adb sync [-l] [PARTITION]");
}
if (src != "" &&
src != "system" && src != "data" && src != "vendor" && src != "oem") {
- return usage();
+ return usage("don't know how to sync %s partition", src.c_str());
}
std::string system_src_path = product_file("system");
@@ -1909,7 +1905,7 @@
return restore(argc, argv);
}
else if (!strcmp(argv[0], "keygen")) {
- if (argc < 2) return usage();
+ if (argc != 2) return usage("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
@@ -1926,11 +1922,11 @@
/* "adb /?" is a common idiom under Windows */
- else if (!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
+ else if (!strcmp(argv[0], "--help") || !strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) {
help();
return 0;
}
- else if (!strcmp(argv[0], "version")) {
+ else if (!strcmp(argv[0], "--version") || !strcmp(argv[0], "version")) {
fprintf(stdout, "%s", adb_version().c_str());
return 0;
}
@@ -1961,12 +1957,12 @@
std::string err;
return adb_query_command("host:reconnect-offline");
} else {
- return usage();
+ return usage("usage: adb reconnect [device|offline]");
}
}
}
- usage();
+ usage("unknown command %s", argv[0]);
return 1;
}
diff --git a/base/Android.bp b/base/Android.bp
index b9a6e0b..a7600f3 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -37,6 +37,9 @@
cppflags: libbase_cppflags,
export_include_dirs: ["include"],
shared_libs: ["liblog"],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
target: {
android: {
srcs: [
@@ -86,6 +89,9 @@
"strings_test.cpp",
"test_main.cpp",
],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
target: {
android: {
srcs: ["properties_test.cpp"],
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4d7082a..e275fa2 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -58,6 +58,9 @@
// tell you whether or not your call succeeded. A `false` return value definitely means failure.
bool SetProperty(const std::string& key, const std::string& value);
+// Waits for the system property `key` to have the value `expected_value`, .
+void WaitForProperty(const std::string& key, const std::string& expected_value);
+
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 37daf9a..0f0f638 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+
#include "android-base/properties.h"
#include <sys/system_properties.h>
+#include <sys/_system_properties.h>
#include <string>
@@ -78,5 +81,43 @@
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
+struct WaitForPropertyData {
+ bool done;
+ const std::string* expected_value;
+ unsigned last_read_serial;
+};
+
+static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
+ WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
+ if (*data->expected_value == value) {
+ data->done = true;
+ } else {
+ data->last_read_serial = serial;
+ }
+}
+
+void WaitForProperty(const std::string& key, const std::string& expected_value) {
+ // Find the property's prop_info*.
+ const prop_info* pi;
+ unsigned global_serial = 0;
+ while ((pi = __system_property_find(key.c_str())) == nullptr) {
+ // The property doesn't even exist yet.
+ // Wait for a global change and then look again.
+ global_serial = __system_property_wait_any(global_serial);
+ }
+
+ WaitForPropertyData data;
+ data.expected_value = &expected_value;
+ data.done = false;
+ while (true) {
+ // Check whether the property has the value we're looking for?
+ __system_property_read_callback(pi, WaitForPropertyCallback, &data);
+ if (data.done) return;
+
+ // It didn't so wait for it to change before checking again.
+ __system_property_wait(pi, data.last_read_serial);
+ }
+}
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index da89ec5..d8186be 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -18,7 +18,12 @@
#include <gtest/gtest.h>
+#include <atomic>
+#include <chrono>
#include <string>
+#include <thread>
+
+using namespace std::chrono_literals;
TEST(properties, smoke) {
android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -119,3 +124,18 @@
TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
+
+TEST(properties, WaitForProperty) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ std::this_thread::sleep_for(100ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a");
+ flag = true;
+ android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b");
+ thread.join();
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index 46fe939..bfdaf12 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -36,11 +36,12 @@
size_t base = 0;
size_t found;
- do {
+ while (true) {
found = s.find_first_of(delimiters, base);
result.push_back(s.substr(base, found - base));
+ if (found == s.npos) break;
base = found + 1;
- } while (found != s.npos);
+ }
return result;
}
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index 7a65a00..7ed5b2b 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -251,3 +251,7 @@
ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "bar"));
ASSERT_FALSE(android::base::EqualsIgnoreCase("foo", "fool"));
}
+
+TEST(strings, ubsan_28729303) {
+ android::base::Split("/dev/null", ":");
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 3b3d698..636477d 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -57,7 +57,13 @@
static std::string GetSystemTempDir() {
#if defined(__ANDROID__)
- return "/data/local/tmp";
+ const char* tmpdir = "/data/local/tmp";
+ if (access(tmpdir, R_OK | W_OK | X_OK) == 0) {
+ return tmpdir;
+ }
+ // Tests running in app context can't access /data/local/tmp,
+ // so try current directory if /data/local/tmp is not accessible.
+ return ".";
#elif defined(_WIN32)
char tmp_dir[MAX_PATH];
DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index d98a9d7..f744ad1 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
bootstat_lib_src_files = [
"boot_event_record_store.cpp",
- "histogram_logger.cpp",
"uptime_parser.cpp",
]
@@ -27,15 +26,12 @@
"-Wall",
"-Wextra",
"-Werror",
-
- // 524291 corresponds to sysui_histogram, from
- // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
- "-DHISTOGRAM_LOG_TAG=524291",
],
shared_libs: [
"libbase",
"libcutils",
"liblog",
+ "libmetricslogger",
],
whole_static_libs: ["libgtest_prod"],
// Clang is required because of C++14
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index f902af3..2648594 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -26,7 +26,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include "histogram_logger.h"
#include "uptime_parser.h"
namespace {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a626704..a7354a7 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,14 +28,16 @@
#include <map>
#include <memory>
#include <string>
+#include <vector>
#include <android/log.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/strings.h>
#include <cutils/properties.h>
+#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
-#include "histogram_logger.h"
#include "uptime_parser.h"
namespace {
@@ -47,7 +49,7 @@
auto events = boot_event_store.GetAllBootEvents();
for (auto i = events.cbegin(); i != events.cend(); ++i) {
- bootstat::LogHistogram(i->first, i->second);
+ android::metricslogger::LogHistogram(i->first, i->second);
}
}
@@ -218,6 +220,27 @@
}
}
+// Parses and records the set of bootloader stages and associated boot times
+// from the ro.boot.boottime system property.
+void RecordBootloaderTimings(BootEventRecordStore* boot_event_store) {
+ // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN'.
+ std::string value = GetProperty("ro.boot.boottime");
+
+ auto stages = android::base::Split(value, ",");
+ for (auto const &stageTiming : stages) {
+ // |stageTiming| is of the form 'stage:time'.
+ auto stageTimingValues = android::base::Split(stageTiming, ":");
+ DCHECK_EQ(2, stageTimingValues.size());
+
+ std::string stageName = stageTimingValues[0];
+ int32_t time_ms;
+ if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {
+ boot_event_store->AddBootEventWithValue(
+ "boottime.bootloader." + stageName, time_ms);
+ }
+ }
+}
+
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
@@ -271,6 +294,8 @@
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
+
+ RecordBootloaderTimings(&boot_event_store);
}
// Records the boot_reason metric by querying the ro.boot.bootreason system
@@ -292,18 +317,18 @@
if (current_time_utc < 0) {
// UMA does not display negative values in buckets, so convert to positive.
- bootstat::LogHistogram(
+ android::metricslogger::LogHistogram(
"factory_reset_current_time_failure", std::abs(current_time_utc));
- // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
boot_event_store.AddBootEventWithValue(
"factory_reset_current_time_failure", std::abs(current_time_utc));
return;
} else {
- bootstat::LogHistogram("factory_reset_current_time", current_time_utc);
+ android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
- // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
boot_event_store.AddBootEventWithValue(
"factory_reset_current_time", current_time_utc);
@@ -322,9 +347,9 @@
// Calculate and record the difference in time between now and the
// factory_reset time.
time_t factory_reset_utc = record.second;
- bootstat::LogHistogram("factory_reset_record_value", factory_reset_utc);
+ android::metricslogger::LogHistogram("factory_reset_record_value", factory_reset_utc);
- // Logging via BootEventRecordStore to see if using bootstat::LogHistogram
+ // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
boot_event_store.AddBootEventWithValue(
"factory_reset_record_value", factory_reset_utc);
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index b072412..f4756d5 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -8,7 +8,8 @@
#
# post-fs-data: /data is writable
# property:init.svc.bootanim=running: The boot animation is running
-on post-fs-data && property:init.svc.bootanim=running
+# property:ro.crypto.type=block: FDE device
+on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 58eaed7..2889356 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -18,10 +18,12 @@
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
-#include <syscall.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/un.h>
+#include <syscall.h>
#include <unistd.h>
#include <limits>
@@ -51,24 +53,25 @@
using android::base::unique_fd;
using android::base::StringPrintf;
-static bool pid_contains_tid(pid_t pid, pid_t tid) {
- std::string task_path = StringPrintf("/proc/%d/task/%d", pid, tid);
- return access(task_path.c_str(), F_OK) == 0;
+static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
+ struct stat st;
+ std::string task_path = StringPrintf("task/%d", tid);
+ return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;
}
// Attach to a thread, and verify that it's still a member of the given process
-static bool ptrace_seize_thread(pid_t pid, pid_t tid, std::string* error) {
+static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) {
if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) {
*error = StringPrintf("failed to attach to thread %d: %s", tid, strerror(errno));
return false;
}
// Make sure that the task we attached to is actually part of the pid we're dumping.
- if (!pid_contains_tid(pid, tid)) {
+ if (!pid_contains_tid(pid_proc_fd, tid)) {
if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
PLOG(FATAL) << "failed to detach from thread " << tid;
}
- *error = StringPrintf("thread %d is not in process %d", tid, pid);
+ *error = StringPrintf("thread %d is not in process", tid);
return false;
}
@@ -190,6 +193,24 @@
_exit(1);
}
+static void drop_capabilities() {
+ __user_cap_header_struct capheader;
+ memset(&capheader, 0, sizeof(capheader));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ __user_cap_data_struct capdata[2];
+ memset(&capdata, 0, sizeof(capdata));
+
+ if (capset(&capheader, &capdata[0]) == -1) {
+ PLOG(FATAL) << "failed to drop capabilities";
+ }
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
+ PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS";
+ }
+}
+
static void check_process(int proc_fd, pid_t expected_pid) {
android::procinfo::ProcessInfo proc_info;
if (!android::procinfo::GetProcessInfoFromProcPidFd(proc_fd, &proc_info)) {
@@ -263,7 +284,7 @@
check_process(target_proc_fd, target);
std::string attach_error;
- if (!ptrace_seize_thread(target, main_tid, &attach_error)) {
+ if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) {
LOG(FATAL) << attach_error;
}
@@ -304,6 +325,7 @@
}
int signo = siginfo.si_signo;
+ bool fatal_signal = signo != DEBUGGER_SIGNAL;
bool backtrace = false;
uintptr_t abort_address = 0;
@@ -319,17 +341,16 @@
// Now that we have the signal that kicked things off, attach all of the
// sibling threads, and then proceed.
- bool fatal_signal = signo != DEBUGGER_SIGNAL;
- std::set<pid_t> siblings;
std::set<pid_t> attached_siblings;
- if (fatal_signal || backtrace) {
+ {
+ std::set<pid_t> siblings;
if (!android::procinfo::GetProcessTids(target, &siblings)) {
PLOG(FATAL) << "failed to get process siblings";
}
siblings.erase(main_tid);
for (pid_t sibling_tid : siblings) {
- if (!ptrace_seize_thread(target, sibling_tid, &attach_error)) {
+ if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) {
LOG(WARNING) << attach_error;
} else {
attached_siblings.insert(sibling_tid);
@@ -337,20 +358,29 @@
}
}
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
+ if (!backtrace_map) {
+ LOG(FATAL) << "failed to create backtrace map";
+ }
+
+ // Collect the list of open files.
+ OpenFilesList open_files;
+ if (!backtrace) {
+ populate_open_files_list(target, &open_files);
+ }
+
+ // Drop our capabilities now that we've attached to the threads we care about.
+ drop_capabilities();
+
check_process(target_proc_fd, target);
// TODO: Use seccomp to lock ourselves down.
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(main_tid));
std::string amfd_data;
if (backtrace) {
dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, attached_siblings, 0);
} else {
- // Collect the list of open files.
- OpenFilesList open_files;
- populate_open_files_list(target, &open_files);
-
engrave_tombstone(output_fd.get(), backtrace_map.get(), open_files, target, main_tid,
attached_siblings, abort_address, fatal_signal ? &amfd_data : nullptr);
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e7503e9..e22d6a9 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -40,10 +40,10 @@
using android::base::unique_fd;
#if defined(__LP64__)
-#define CRASHER_PATH "/system/xbin/crasher64"
+#define CRASHER_PATH "/system/bin/crasher64"
#define ARCH_SUFFIX "64"
#else
-#define CRASHER_PATH "/system/xbin/crasher"
+#define CRASHER_PATH "/system/bin/crasher"
#define ARCH_SUFFIX ""
#endif
@@ -192,7 +192,7 @@
std::string type = "wait-" + crash_type;
StartProcess([type]() {
execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
- err(1, "exec failed");
+ exit(errno);
});
}
@@ -216,7 +216,9 @@
FAIL() << "failed to wait for crasher: " << strerror(errno);
}
- if (!WIFSIGNALED(status)) {
+ if (WIFEXITED(status)) {
+ FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
+ } else if (!WIFSIGNALED(status)) {
FAIL() << "crasher didn't terminate via a signal";
}
ASSERT_EQ(signo, WTERMSIG(status));
@@ -405,3 +407,35 @@
});
AssertDeath(SIGUSR1);
}
+
+TEST(crash_dump, zombie) {
+ pid_t forkpid = fork();
+
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+
+ pid_t rc;
+ int status;
+
+ if (forkpid == 0) {
+ errno = 0;
+ rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
+ if (rc != -1 || errno != ECHILD) {
+ errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
+ }
+
+ raise(DEBUGGER_SIGNAL);
+
+ errno = 0;
+ rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
+ if (rc != -1 || errno != ECHILD) {
+ errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
+ }
+ _exit(0);
+ } else {
+ rc = waitpid(forkpid, &status, 0);
+ ASSERT_EQ(forkpid, rc);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(0, WEXITSTATUS(status));
+ }
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 9469bbd..353f642 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -39,6 +39,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/capability.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/socket.h>
@@ -207,7 +208,7 @@
}
// Don't use fork(2) to avoid calling pthread_atfork handlers.
- int forkpid = clone(nullptr, nullptr, SIGCHLD, nullptr);
+ int forkpid = clone(nullptr, nullptr, 0, nullptr);
if (forkpid == -1) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s",
strerror(errno));
@@ -216,6 +217,11 @@
close(pipefds[0]);
close(pipefds[1]);
+ // Set all of the ambient capability bits we can, so that crash_dump can ptrace us.
+ for (unsigned long i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) != -1; ++i) {
+ prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0);
+ }
+
char buf[10];
snprintf(buf, sizeof(buf), "%d", thread_info->crashing_tid);
execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, buf, nullptr);
@@ -242,10 +248,12 @@
close(pipefds[0]);
// Don't leave a zombie child.
- siginfo_t child_siginfo;
- if (TEMP_FAILURE_RETRY(waitid(P_PID, forkpid, &child_siginfo, WEXITED)) != 0) {
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) {
__libc_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
strerror(errno));
+ } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
+ __libc_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
thread_info->crash_dump_started = false;
}
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e7f1a07..a4a0b52 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1303,6 +1303,36 @@
return num;
}
+static std::string fb_fix_numeric_var(std::string var) {
+ // Some bootloaders (angler, for example), send spurious leading whitespace.
+ var = android::base::Trim(var);
+ // Some bootloaders (hammerhead, for example) use implicit hex.
+ // This code used to use strtol with base 16.
+ if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+ return var;
+}
+
+static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
+ std::string sizeString;
+ if (!fb_getvar(transport, name.c_str(), &sizeString)) {
+ /* This device does not report flash block sizes, so return 0 */
+ return 0;
+ }
+ sizeString = fb_fix_numeric_var(sizeString);
+
+ unsigned size;
+ if (!android::base::ParseUint(sizeString, &size)) {
+ fprintf(stderr, "Couldn't parse %s '%s'.\n", name.c_str(), sizeString.c_str());
+ return 0;
+ }
+ if (size < 4096 || (size & (size - 1)) != 0) {
+ fprintf(stderr, "Invalid %s %u: must be a power of 2 and at least 4096.\n",
+ name.c_str(), size);
+ return 0;
+ }
+ return size;
+}
+
static void fb_perform_format(Transport* transport,
const char* partition, int skip_if_not_supported,
const char* type_override, const char* size_override,
@@ -1345,11 +1375,7 @@
}
partition_size = size_override;
}
- // Some bootloaders (angler, for example), send spurious leading whitespace.
- partition_size = android::base::Trim(partition_size);
- // Some bootloaders (hammerhead, for example) use implicit hex.
- // This code used to use strtol with base 16.
- if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
+ partition_size = fb_fix_numeric_var(partition_size);
gen = fs_get_generator(partition_type);
if (!gen) {
@@ -1370,7 +1396,12 @@
}
fd = fileno(tmpfile());
- if (fs_generator_generate(gen, fd, size, initial_dir)) {
+
+ unsigned eraseBlkSize, logicalBlkSize;
+ eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
+ logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
+
+ if (fs_generator_generate(gen, fd, size, initial_dir, eraseBlkSize, logicalBlkSize)) {
fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
close(fd);
return;
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 9b73165..5d9ccfe 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -14,18 +14,21 @@
#include <ext4_utils/make_ext4fs.h>
#include <sparse/sparse.h>
-static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir)
+static int generate_ext4_image(int fd, long long partSize, const std::string& initial_dir,
+ unsigned eraseBlkSize, unsigned logicalBlkSize)
{
if (initial_dir.empty()) {
- make_ext4fs_sparse_fd(fd, partSize, NULL, NULL);
+ make_ext4fs_sparse_fd_align(fd, partSize, NULL, NULL, eraseBlkSize, logicalBlkSize);
} else {
- make_ext4fs_sparse_fd_directory(fd, partSize, NULL, NULL, initial_dir.c_str());
+ make_ext4fs_sparse_fd_directory_align(fd, partSize, NULL, NULL, initial_dir.c_str(),
+ eraseBlkSize, logicalBlkSize);
}
return 0;
}
#ifdef USE_F2FS
-static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir)
+static int generate_f2fs_image(int fd, long long partSize, const std::string& initial_dir,
+ unsigned /* unused */, unsigned /* unused */)
{
if (!initial_dir.empty()) {
fprintf(stderr, "Unable to set initial directory on F2FS filesystem\n");
@@ -39,7 +42,8 @@
const char* fs_type; //must match what fastboot reports for partition type
//returns 0 or error value
- int (*generate)(int fd, long long partSize, const std::string& initial_dir);
+ int (*generate)(int fd, long long partSize, const std::string& initial_dir,
+ unsigned eraseBlkSize, unsigned logicalBlkSize);
} generators[] = {
{ "ext4", generate_ext4_image},
@@ -58,7 +62,7 @@
}
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
- const std::string& initial_dir)
+ const std::string& initial_dir, unsigned eraseBlkSize, unsigned logicalBlkSize)
{
- return gen->generate(tmpFileNo, partSize, initial_dir);
+ return gen->generate(tmpFileNo, partSize, initial_dir, eraseBlkSize, logicalBlkSize);
}
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 0a68507..0a5f5a4 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -8,6 +8,6 @@
const struct fs_generator* fs_get_generator(const std::string& fs_type);
int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize,
- const std::string& initial_dir);
+ const std::string& initial_dir, unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0);
#endif
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 032ae31..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -302,13 +302,6 @@
// So, we have a device, finally. Grab its vitals.
-
- kr = (*dev)->USBDeviceOpen(dev);
- if (kr != 0) {
- WARN("USBDeviceOpen");
- goto out;
- }
-
kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
if (kr != 0) {
ERR("GetDeviceVendor");
@@ -381,16 +374,12 @@
goto error;
}
- out:
-
- (*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
return 0;
error:
if (dev != NULL) {
- (*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
}
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 70140d8..dd08271 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -215,8 +215,8 @@
// Reads digest.
if (digest.size() != expected_digest_size) {
- LERROR << "Unexpected digest size: " << digest.size() << " (expected: "
- << expected_digest_size << ")";
+ LERROR << "Unexpected digest size: " << digest.size()
+ << " (expected: " << expected_digest_size << ")";
return false;
}
@@ -482,22 +482,6 @@
return true;
}
-static inline bool polling_vbmeta_blk_device(struct fstab *fstab)
-{
- // It needs the block device symlink: fstab_rec->blk_device to read
- // /vbmeta partition. However, the symlink created by ueventd might
- // not be ready at this point. Use test_access() to poll it before
- // trying to read the partition.
- struct fstab_rec *fstab_entry =
- fs_mgr_get_entry_for_mount_point(fstab, "/vbmeta");
-
- // Makes sure /vbmeta block device is ready to access.
- if (fs_mgr_test_access(fstab_entry->blk_device) < 0) {
- return false;
- }
- return true;
-}
-
static bool init_is_avb_used()
{
// When AVB is used, boot loader should set androidboot.vbmeta.{hash_alg,
@@ -529,11 +513,6 @@
{
FS_MGR_CHECK(fstab != nullptr);
- if (!polling_vbmeta_blk_device(fstab)) {
- LERROR << "Failed to find block device of /vbmeta";
- return FS_MGR_SETUP_AVB_FAIL;
- }
-
// Gets the expected hash value of vbmeta images from
// kernel cmdline.
if (!load_vbmeta_prop(&fs_mgr_vbmeta_prop)) {
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 7683166..f96f124 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -66,7 +66,7 @@
fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
if (fstab_entry == nullptr) {
- LERROR << "Partition (" << partition << ") not found in fstab";
+ LERROR << "/misc mount point not found in fstab";
return AVB_IO_RESULT_ERROR_IO;
}
@@ -79,6 +79,13 @@
path = by_name_prefix + partition_name;
}
+ // Ensures the device path (a symlink created by init) is ready to
+ // access. fs_mgr_test_access() will test a few iterations if the
+ // path doesn't exist yet.
+ if (fs_mgr_test_access(path.c_str()) < 0) {
+ return AVB_IO_RESULT_ERROR_IO;
+ }
+
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
diff --git a/init/.clang-format b/init/.clang-format
new file mode 100644
index 0000000..48d423f
--- /dev/null
+++ b/init/.clang-format
@@ -0,0 +1,14 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+IndentWidth: 4
+Standard: Auto
+TabWidth: 8
+UseTab: Never
+DerivePointerAlignment: false
+PointerAlignment: Left
+...
diff --git a/init/Android.mk b/init/Android.mk
index 9e61fb2..a10a714 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -134,3 +134,8 @@
LOCAL_CLANG := true
LOCAL_CPPFLAGS := -Wall -Wextra -Werror
include $(BUILD_NATIVE_TEST)
+
+
+# Include targets in subdirs.
+# =========================================================
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 965a81f..2388edc 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -265,10 +265,14 @@
if (!svc) {
return -1;
}
- if (!svc->Start()) {
+ if (!start_waiting_for_exec()) {
return -1;
}
- waiting_for_exec = true;
+ if (!svc->Start()) {
+ stop_waiting_for_exec();
+ ServiceManager::GetInstance().RemoveService(*svc);
+ return -1;
+ }
return 0;
}
@@ -1018,7 +1022,7 @@
<< "\") failed: value too long";
return -1;
}
- if (!wait_property(name, value)) {
+ if (!start_waiting_for_property(name, value)) {
LOG(ERROR) << "do_wait_for_prop(\"" << name << "\", \"" << value
<< "\") failed: init already in waiting";
return -1;
diff --git a/init/init.cpp b/init/init.cpp
index eb7431c..9c1e23b 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -82,7 +82,7 @@
const char *ENV[32];
-bool waiting_for_exec = false;
+static std::unique_ptr<Timer> waiting_for_exec(nullptr);
static int epoll_fd = -1;
@@ -131,7 +131,24 @@
return -1;
}
-bool wait_property(const char *name, const char *value)
+bool start_waiting_for_exec()
+{
+ if (waiting_for_exec) {
+ return false;
+ }
+ waiting_for_exec.reset(new Timer());
+ return true;
+}
+
+void stop_waiting_for_exec()
+{
+ if (waiting_for_exec) {
+ LOG(INFO) << "Wait for exec took " << *waiting_for_exec;
+ waiting_for_exec.reset();
+ }
+}
+
+bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
return false;
@@ -142,7 +159,8 @@
wait_prop_value = value;
waiting_for_prop.reset(new Timer());
} else {
- LOG(INFO) << "wait_property(\"" << name << "\", \"" << value << "\"): already set";
+ LOG(INFO) << "start_waiting_for_property(\""
+ << name << "\", \"" << value << "\"): already set";
}
return true;
}
diff --git a/init/init.h b/init/init.h
index 4e4da32..3768c02 100644
--- a/init/init.h
+++ b/init/init.h
@@ -23,7 +23,6 @@
class Service;
extern const char *ENV[32];
-extern bool waiting_for_exec;
extern std::string default_console;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
@@ -36,6 +35,10 @@
int add_environment(const char* key, const char* val);
-bool wait_property(const char *name, const char *value);
+bool start_waiting_for_exec();
+
+void stop_waiting_for_exec();
+
+bool start_waiting_for_property(const char *name, const char *value);
#endif /* _INIT_INIT_H */
diff --git a/init/service.cpp b/init/service.cpp
index 0f7f62f..e186f27 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -997,7 +997,7 @@
}
if (svc->Reap()) {
- waiting_for_exec = false;
+ stop_waiting_for_exec();
RemoveService(*svc);
}
diff --git a/init/test_service/Android.mk b/init/test_service/Android.mk
new file mode 100644
index 0000000..30c9e9d
--- /dev/null
+++ b/init/test_service/Android.mk
@@ -0,0 +1,27 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Sample service for testing.
+# =========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_service
+LOCAL_SRC_FILES := test_service.cpp
+
+LOCAL_SHARED_LIBRARIES += libbase
+
+LOCAL_INIT_RC := test_service.rc
+
+include $(BUILD_EXECUTABLE)
diff --git a/init/test_service/README.md b/init/test_service/README.md
new file mode 100644
index 0000000..6773235
--- /dev/null
+++ b/init/test_service/README.md
@@ -0,0 +1,43 @@
+# Sample service for testing
+This is a sample service that can be used for testing init.
+
+## Design
+The service includes a `.rc` file that allows starting it from init.
+
+ service test_service /system/bin/test_service CapAmb 0000000000003000
+ class main
+ user system
+ group system
+ capabilities NET_ADMIN NET_RAW
+ disabled
+ oneshot
+
+The service accepts any even number of arguments on the command line
+(i.e. any number of pairs of arguments.)
+It will attempt to find the first element of each pair of arguments in
+`/proc/self/status`, and attempt to exactly match the second element of the pair
+to the relevant line of `proc/self/status`.
+
+### Example
+In the above case, the service will look for lines containing `CapAmb`:
+
+ cat /proc/self/status
+ ...
+ CapAmb: 0000000000003000
+
+And then attempt to exactly match the token after `:`, `0000000000003000`,
+with the command-line argument.
+If they match, the service exits successfully. If not, the service will exit
+with an error.
+
+## Usage
+ mmma -j <jobs> system/core/init/testservice
+ adb root
+ adb remount
+ adb sync
+ adb reboot
+ adb root
+ adb shell start test_service
+ adb logcat -b all -d | grep test_service
+
+Look for an exit status of 0.
diff --git a/init/test_service/test_service.cpp b/init/test_service/test_service.cpp
new file mode 100644
index 0000000..e7206f8
--- /dev/null
+++ b/init/test_service/test_service.cpp
@@ -0,0 +1,78 @@
+// 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 <unistd.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+void Usage(char* argv[]) {
+ printf("Usage: %s <status field> <value> [<status field> <value>]*\n", argv[0]);
+ printf("E.g.: $ %s Uid \"1000 1000 1000 1000\"\n", argv[0]);
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 3) {
+ Usage(argv);
+ LOG(FATAL) << "no status field requested";
+ }
+ if (argc % 2 == 0) {
+ // Since |argc| counts argv[0], if |argc| is odd, then the number of
+ // command-line arguments is even.
+ Usage(argv);
+ LOG(FATAL) << "need even number of command-line arguments";
+ }
+
+ std::string status;
+ bool res = android::base::ReadFileToString("/proc/self/status", &status, true);
+ if (!res) {
+ PLOG(FATAL) << "could not read /proc/self/status";
+ }
+
+ std::map<std::string, std::string> fields;
+ std::vector<std::string> lines = android::base::Split(status, "\n");
+ for (const auto& line : lines) {
+ std::vector<std::string> tokens = android::base::Split(line, ":");
+ if (tokens.size() >= 2) {
+ std::string field = tokens[0];
+ std::string value = android::base::Trim(tokens[1]);
+ if (field.length() > 0) {
+ fields[field] = value;
+ }
+ }
+ }
+
+ bool test_fails = false;
+ size_t uargc = static_cast<size_t>(argc); // |argc| >= 3.
+ for (size_t i = 1; i < static_cast<size_t>(argc); i = i + 2) {
+ std::string expected_value = argv[i + 1];
+ auto f = fields.find(argv[i]);
+ if (f != fields.end()) {
+ if (f->second != expected_value) {
+ LOG(ERROR) << "field '" << argv[i] << "' expected '" << expected_value
+ << "', actual '" << f->second << "'";
+ test_fails = true;
+ }
+ } else {
+ LOG(WARNING) << "could not find field '" << argv[i] << "'";
+ }
+ }
+
+ return test_fails ? 1 : 0;
+}
diff --git a/init/test_service/test_service.rc b/init/test_service/test_service.rc
new file mode 100644
index 0000000..91e1a0f
--- /dev/null
+++ b/init/test_service/test_service.rc
@@ -0,0 +1,7 @@
+service test_service /system/bin/test_service CapAmb 0000000000003000
+ class main
+ user system
+ group system
+ capabilities NET_ADMIN NET_RAW
+ disabled
+ oneshot
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 361b925..915afbd 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -60,9 +60,18 @@
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
- std::string hardware = property_get("ro.hardware");
-
ueventd_parse_config_file("/ueventd.rc");
+ ueventd_parse_config_file("/vendor/ueventd.rc");
+ ueventd_parse_config_file("/odm/ueventd.rc");
+
+ /*
+ * keep the current product name base configuration so
+ * we remain backwards compatible and allow it to override
+ * everything
+ * TODO: cleanup platform ueventd.rc to remove vendor specific
+ * device node entries (b/34968103)
+ */
+ std::string hardware = property_get("ro.hardware");
ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
device_init();
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index db35d33..1a1abd5 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -31,7 +31,7 @@
constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
void OpenTempFile(android::base::unique_fd* fd) {
- fd->reset(open(kTempFile, O_CREAT | O_RDWR));
+ fd->reset(open(kTempFile, O_CREAT | O_RDWR, 0600));
ASSERT_NE(-1, *fd) << strerror(errno);
unlink(kTempFile);
ASSERT_NE(-1, *fd) << strerror(errno);
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 5e54328..0a2f5a3 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -21,6 +21,7 @@
#include <dwarf.h>
}
+#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
@@ -30,6 +31,7 @@
#include <unistd.h>
#include <memory>
+#include <mutex>
#include <string>
#include <vector>
@@ -90,9 +92,6 @@
has_debug_frame(false), has_gnu_debugdata(false) { }
};
-static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>& g_debug_frames =
- *new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
-
void Space::Clear() {
start = 0;
end = 0;
@@ -282,39 +281,14 @@
// vaddr in the elf file.
uint64_t ip_vaddr = ip - map.start + debug_frame->min_vaddr;
- if (debug_frame->has_arm_exidx) {
- auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
- if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
- // Use binary search to find the correct function.
- auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
- static_cast<uint32_t>(ip_vaddr));
- if (it != func_vaddrs.begin()) {
- --it;
- // Found the exidx entry.
- size_t index = it - func_vaddrs.begin();
- proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
- proc_info->unwind_info = reinterpret_cast<void*>(
- static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
- debug_frame->arm_exidx.exidx_vaddr +
- debug_frame->min_vaddr));
-
- // Prepare arm_exidx space and arm_extab space.
- arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
- arm_exidx_space_.end = arm_exidx_space_.start +
- debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
- arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
- debug_frame->arm_exidx.exidx_data.data());
-
- arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
- arm_extab_space_.end = arm_extab_space_.start +
- debug_frame->arm_exidx.extab_data.size();
- arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
- return true;
- }
- }
- }
-
+ // The unwind info can come from .ARM.exidx or .eh_frame, or .debug_frame/.gnu_debugdata.
+ // First check .eh_frame/.debug_frame, then check .ARM.exidx. Because .eh_frame/.debug_frame has
+ // function range for each entry, by matching ip address with the function range, we know exactly
+ // whether the ip address hits an entry. But .ARM.exidx doesn't have function range for each
+ // entry, it thinks that an ip address hits an entry when (entry.addr <= ip < next_entry.addr).
+ // To prevent ip addresses hit in .eh_frame/.debug_frame being regarded as addresses hit in
+ // .ARM.exidx, we need to check .eh_frame/.debug_frame first.
if (debug_frame->has_eh_frame) {
if (ip_vaddr >= debug_frame->eh_frame.min_func_vaddr &&
ip_vaddr < debug_frame->text_end_vaddr) {
@@ -323,7 +297,6 @@
eh_frame_hdr_space_.end =
eh_frame_hdr_space_.start + debug_frame->eh_frame.hdr_data.size();
eh_frame_hdr_space_.data = debug_frame->eh_frame.hdr_data.data();
-
eh_frame_space_.start = ip - ip_vaddr + debug_frame->eh_frame.vaddr;
eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.data.size();
eh_frame_space_.data = debug_frame->eh_frame.data.data();
@@ -345,7 +318,6 @@
}
}
}
-
if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
unw_dyn_info_t di;
unw_word_t segbase = map.start - map.offset;
@@ -359,6 +331,40 @@
}
}
}
+
+ if (debug_frame->has_arm_exidx) {
+ auto& func_vaddrs = debug_frame->arm_exidx.func_vaddr_array;
+ if (ip_vaddr >= func_vaddrs[0] && ip_vaddr < debug_frame->text_end_vaddr) {
+ // Use binary search to find the correct function.
+ auto it = std::upper_bound(func_vaddrs.begin(), func_vaddrs.end(),
+ static_cast<uint32_t>(ip_vaddr));
+ if (it != func_vaddrs.begin()) {
+ --it;
+ // Found the exidx entry.
+ size_t index = it - func_vaddrs.begin();
+ proc_info->start_ip = *it;
+ proc_info->format = UNW_INFO_FORMAT_ARM_EXIDX;
+ proc_info->unwind_info = reinterpret_cast<void*>(
+ static_cast<uintptr_t>(index * sizeof(ArmIdxEntry) +
+ debug_frame->arm_exidx.exidx_vaddr +
+ debug_frame->min_vaddr));
+ eh_frame_hdr_space_.Clear();
+ eh_frame_space_.Clear();
+ // Prepare arm_exidx space and arm_extab space.
+ arm_exidx_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.exidx_vaddr;
+ arm_exidx_space_.end = arm_exidx_space_.start +
+ debug_frame->arm_exidx.exidx_data.size() * sizeof(ArmIdxEntry);
+ arm_exidx_space_.data = reinterpret_cast<const uint8_t*>(
+ debug_frame->arm_exidx.exidx_data.data());
+
+ arm_extab_space_.start = debug_frame->min_vaddr + debug_frame->arm_exidx.extab_vaddr;
+ arm_extab_space_.end = arm_extab_space_.start +
+ debug_frame->arm_exidx.extab_data.size();
+ arm_extab_space_.data = debug_frame->arm_exidx.extab_data.data();
+ return true;
+ }
+ }
+ }
return false;
}
@@ -549,18 +555,31 @@
return "";
}
+static std::mutex g_lock;
+static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>* g_debug_frames = nullptr;
+
static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
if (cache_file_) {
- auto it = g_debug_frames.find(filename);
- if (it != g_debug_frames.end()) {
- return it->second.get();
+ std::lock_guard<std::mutex> lock(g_lock);
+ if (g_debug_frames != nullptr) {
+ auto it = g_debug_frames->find(filename);
+ if (it != g_debug_frames->end()) {
+ return it->second.get();
+ }
}
}
DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
if (cache_file_) {
- g_debug_frames.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+ std::lock_guard<std::mutex> lock(g_lock);
+ if (g_debug_frames == nullptr) {
+ g_debug_frames = new std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>>;
+ }
+ auto pair = g_debug_frames->emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+ if (!pair.second) {
+ debug_frame = pair.first->second.get();
+ }
}
return debug_frame;
}
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
index 49fcb29..465b3f9 100644
--- a/libbacktrace/backtrace_offline_test.cpp
+++ b/libbacktrace/backtrace_offline_test.cpp
@@ -220,25 +220,7 @@
#endif
}
-static void BacktraceOfflineTest(const std::string& testlib_name) {
- const std::string arch = GetArch();
- if (arch.empty()) {
- GTEST_LOG_(INFO) << "This test does nothing on current arch.";
- return;
- }
- const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
- std::string testdata;
- ASSERT_TRUE(android::base::ReadFileToString(offline_testdata_path, &testdata));
-
- const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
- struct stat st;
- if (stat(testlib_path.c_str(), &st) == -1) {
- GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
- return;
- }
-
- // Parse offline_testdata.
- std::vector<std::string> lines = android::base::Split(testdata, "\n");
+struct OfflineTestData {
int pid;
int tid;
std::vector<backtrace_map_t> maps;
@@ -246,63 +228,93 @@
backtrace_stackinfo_t stack_info;
std::vector<uint8_t> stack;
std::vector<FunctionSymbol> symbols;
+};
+
+bool ReadOfflineTestData(const std::string offline_testdata_path, OfflineTestData* testdata) {
+ std::string s;
+ if (!android::base::ReadFileToString(offline_testdata_path, &s)) {
+ return false;
+ }
+ // Parse offline_testdata.
+ std::vector<std::string> lines = android::base::Split(s, "\n");
+ memset(&testdata->unw_context, 0, sizeof(testdata->unw_context));
for (const auto& line : lines) {
if (android::base::StartsWith(line, "pid:")) {
- sscanf(line.c_str(), "pid: %d tid: %d", &pid, &tid);
+ sscanf(line.c_str(), "pid: %d tid: %d", &testdata->pid, &testdata->tid);
} else if (android::base::StartsWith(line, "map:")) {
- maps.resize(maps.size() + 1);
+ testdata->maps.resize(testdata->maps.size() + 1);
+ backtrace_map_t& map = testdata->maps.back();
int pos;
sscanf(line.c_str(),
"map: start: %" SCNxPTR " end: %" SCNxPTR " offset: %" SCNxPTR
" load_base: %" SCNxPTR " flags: %d name: %n",
- &maps.back().start, &maps.back().end, &maps.back().offset,
- &maps.back().load_base, &maps.back().flags, &pos);
- maps.back().name = android::base::Trim(line.substr(pos));
+ &map.start, &map.end, &map.offset, &map.load_base, &map.flags, &pos);
+ map.name = android::base::Trim(line.substr(pos));
} else if (android::base::StartsWith(line, "registers:")) {
size_t size;
int pos;
sscanf(line.c_str(), "registers: %zu %n", &size, &pos);
- ASSERT_EQ(sizeof(unw_context), size);
- HexStringToRawData(&line[pos], &unw_context, size);
+ if (sizeof(testdata->unw_context) != size) {
+ return false;
+ }
+ HexStringToRawData(&line[pos], &testdata->unw_context, size);
} else if (android::base::StartsWith(line, "stack:")) {
size_t size;
int pos;
sscanf(line.c_str(),
"stack: start: %" SCNx64 " end: %" SCNx64 " size: %zu %n",
- &stack_info.start, &stack_info.end, &size, &pos);
- stack.resize(size);
- HexStringToRawData(&line[pos], &stack[0], size);
- stack_info.data = stack.data();
+ &testdata->stack_info.start, &testdata->stack_info.end, &size, &pos);
+ testdata->stack.resize(size);
+ HexStringToRawData(&line[pos], &testdata->stack[0], size);
+ testdata->stack_info.data = testdata->stack.data();
} else if (android::base::StartsWith(line, "function:")) {
- symbols.resize(symbols.size() + 1);
+ testdata->symbols.resize(testdata->symbols.size() + 1);
+ FunctionSymbol& symbol = testdata->symbols.back();
int pos;
sscanf(line.c_str(),
"function: start: %" SCNxPTR " end: %" SCNxPTR " name: %n",
- &symbols.back().start, &symbols.back().end,
- &pos);
- symbols.back().name = line.substr(pos);
+ &symbol.start, &symbol.end, &pos);
+ symbol.name = line.substr(pos);
}
}
+ return true;
+}
+
+static void BacktraceOfflineTest(const std::string& testlib_name) {
+ const std::string arch = GetArch();
+ if (arch.empty()) {
+ GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+ return;
+ }
+ const std::string testlib_path = "testdata/" + arch + "/" + testlib_name;
+ struct stat st;
+ if (stat(testlib_path.c_str(), &st) == -1) {
+ GTEST_LOG_(INFO) << "This test is skipped as " << testlib_path << " doesn't exist.";
+ return;
+ }
+
+ const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata";
+ OfflineTestData testdata;
+ ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
// Fix path of libbacktrace_testlib.so.
- for (auto& map : maps) {
+ for (auto& map : testdata.maps) {
if (map.name.find("libbacktrace_test.so") != std::string::npos) {
map.name = testlib_path;
}
}
// Do offline backtrace.
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid, maps));
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
ASSERT_TRUE(map != nullptr);
std::unique_ptr<Backtrace> backtrace(
- Backtrace::CreateOffline(pid, tid, map.get(), stack_info));
+ Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
ASSERT_TRUE(backtrace != nullptr);
- ucontext_t ucontext = GetUContextFromUnwContext(unw_context);
+ ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
-
// Collect pc values of the call stack frames.
std::vector<uintptr_t> pc_values;
for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
@@ -311,17 +323,20 @@
size_t test_one_index = 0;
for (size_t i = 0; i < pc_values.size(); ++i) {
- if (FunctionNameForAddress(pc_values[i], symbols) == "test_level_one") {
+ if (FunctionNameForAddress(pc_values[i], testdata.symbols) == "test_level_one") {
test_one_index = i;
break;
}
}
ASSERT_GE(test_one_index, 3u);
- ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], symbols));
- ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1], symbols));
- ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2], symbols));
- ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3], symbols));
+ ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index], testdata.symbols));
+ ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1],
+ testdata.symbols));
+ ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2],
+ testdata.symbols));
+ ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3],
+ testdata.symbols));
}
TEST(libbacktrace, offline_eh_frame) {
@@ -339,3 +354,47 @@
TEST(libbacktrace, offline_arm_exidx) {
BacktraceOfflineTest("libbacktrace_test_arm_exidx.so");
}
+
+// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
+// overlap with each other, which appears in /system/lib/libart.so.
+TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
+ const std::string arch = GetArch();
+ if (arch.empty() || arch != "arm") {
+ GTEST_LOG_(INFO) << "This test does nothing on current arch.";
+ return;
+ }
+ const std::string testlib_path = "testdata/" + arch + "/libart.so";
+ struct stat st;
+ ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;
+
+ const std::string offline_testdata_path = "testdata/" + arch + "/offline_testdata_for_libart";
+ OfflineTestData testdata;
+ ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));
+
+ // Fix path of /system/lib/libart.so.
+ for (auto& map : testdata.maps) {
+ if (map.name.find("libart.so") != std::string::npos) {
+ map.name = testlib_path;
+ }
+ }
+
+ // Do offline backtrace.
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
+ ASSERT_TRUE(map != nullptr);
+
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
+ ASSERT_TRUE(backtrace != nullptr);
+
+ ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
+ ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+ // The last frame is outside of libart.so
+ ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
+ for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
+ uintptr_t vaddr_in_file = backtrace->GetFrame(i)->pc - testdata.maps[0].start +
+ testdata.maps[0].load_base;
+ std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
+ ASSERT_EQ(name, testdata.symbols[i].name);
+ }
+}
diff --git a/libbacktrace/testdata/arm/libart.so b/libbacktrace/testdata/arm/libart.so
new file mode 100644
index 0000000..bed8e35
--- /dev/null
+++ b/libbacktrace/testdata/arm/libart.so
Binary files differ
diff --git a/libbacktrace/testdata/arm/offline_testdata_for_libart b/libbacktrace/testdata/arm/offline_testdata_for_libart
new file mode 100644
index 0000000..63f6a07
--- /dev/null
+++ b/libbacktrace/testdata/arm/offline_testdata_for_libart
@@ -0,0 +1,10 @@
+pid: 32232 tid: 32233
+registers: 64 000000000000000000000000000000006473602451b3e2e700000000d82fd1ff5600000000908eec00000000d42dd1ff00000000c02dd1ff617171e9617171e9
+map: start: e9380000 end: e9766000 offset: 0 load_base: b000 flags: 5 name: /system/lib/libart.so
+stack: start: ffd12dc0 end: ffd16000 size: 12864 00000000000c5024070000000300000005070a0a0100000051b3e2e700000000d82fd1ff560000004c2ed1ff000000000000000081b771e9d82fd1ff000000004c2ed1ff0c2ed1ff40a8d27024bf76e900908eec000000000834d1ff0000000000000000000000000d000000050000000000000000000000080000000101d1ff44b8bfeb4b0000000000000000000000e8b8952400000000fc2ed1ff4fb3e2e7bc49ac6f00908eecb02ed1ffd82fd1ff040000008c908eec942fd1ffd5c141e9d82fd1ff4fb3e2e7542fd1ff336c68e940000000400000007030d1fff031d1ff00000000bc49ac6f5c30d1ff942fd1ff842fd1ffd82fd1ff00000000b8f1786f4fb3e2e7610d67e9d82fd1ff4fb3e2e77880adeb7980adeb7a80adeb7b80adeb7c80adeb7d80adeb7e80adeb7f80adeb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007430d1ff02000000e8b89524e8d895240200000000908eec5c30d1ffbc49ac6f4fb3e2e74030d1ffe8d8952400000000b8f1786fbc49ac6f332367e94fb3e2e701000000637171e9637171e9000000005c30d1ff8430d1ffe0c08bec882fd1ff4fb3e2e70200000004000000942fd1ffe8b8952400908eec58d8952458d895247fbd69e90500000000400fe40100000000908eec58d89524060000009c86bd6f6b876fe900908eece0c08bec00008eec0000000000000000000000000000000044b8bfeb4b000000009be86f040000000038d1ff01000000c8e7446f060000000000000000908eec30d89524e8b895249c86bd6f7893476f00908eec00000000358c6fe970400fe4116e71e9a0285a6fa4d49c6f4489bd6f30d8952458d89524e8d8952400908eeca431d1ff2c31d1ffb75861e90100000000908eec30528bec409181e958d8952431abed6fac33576fb438d1ff030000007800502400000000a0005024060000007893476f00908eec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004489bd6f78005024d00c5024a0005024a431d1ff2c31d1ff9b99aa71a4d49c6f30d8952400000000e8d895244489bd6fa8e5bc6fc8b895240100000000000000b033d1ff56000000637171e900000000d00c5024c8b89524000000000100000000000000b033d1ff560000006431d1ffa431d1ff000000009fb671e9b033d1ff00000000a431d1ff6431d1ffc431d1ff000000000000000081b771e9b033d1ff00000000c431d1ff8431d1ff01000000020000002429000001000000dc501b002033d1ff0100000018f9736f0100000000908eec58d8952440f180e9a8ec01245b215ce8a4d49c6f00908eec0832d1ffb033d1ff040000008c908eeca832d1ffabc141e9b033d1ff5b215ce82832d1ffb033d1ff080000008c908eec000000000035d1ff0834d1ffa832d1ffa4d49c6f04000000cca312e800908eec6832d1ffb033d1ff0834d1ff6bc354e9b033d1ff5b215ce8cca312e800908eec8832d1ffb033d1ff0834d1ff6bc354e900908eeca4d49c6f44b8bfeb1833d1ff000000006832d1ffb033d1ff478054e9b033d1ff1b8054e90834d1ffa4d49c6f0000000000000000000000000000000008000000000000000834d1ff0000000000000000000000000000000000000000000000000000000058d895240000000000000000000000000000000000000000000000000000000058d89524b17e54e98c56af6f00000000a4d49c6f288944e800908eec00000000d032d1ff0000000000000000000000000000000000000000000000007e8ea6c358a58cec00f580e90834d1ffa4d49c6f58d8952400908eecb033d1ffe9100000da8844e8833c70e9e9100000b033d1ff0200000058d8952408b1796f0200000000908eecda8844e82c34d1ff00908eece9100000005d70e9070000007d1300006034d1ff98d170e9b033d1ff0834d1ff148844e800908eecb033d1ffa034d1ffa833d1ff0100000044b8bfeb41f252e9e91fdeeaa491deea000000004700000001000000d9c4ddea0000000000000000b834d1ff00b051ff0834d1ff00908eecf833d1ffa034d1ff148844e800000000020000004d4c53e900000000000000000000000000908eec44b8bfeb0834d1ff3835d1ff148844e85035d1ffbb936fe90000000044b8bfebb033d1ffda8844e8148844e8000000000d0000005a0000007d137d13d00c502400000000600480240400000070048024f80c5024170000000100000002000000000000000040000000000000d0018024d00c502400000000600480240000000070048024f80c5024000000000000000000000000000000000000000000000000d001802465906fe97b2e5ce8000000000300000000000000881388131a00000001000000000000004cdd76e9010000007b2e5ce8020000009835d1ff5835d1ffc435d1ff010000000000000000000000010000000000000000dd76e90834d1ff0d0000000100000000000000000000005035d1ff9036d1ff00000000a435d1ff7e8ea6c3080000000000000000000000000000000000000038cb7e7044b8bfeb7d2e5ce800000000c037d1ff5600000000908eec00000000cc35d1ff55af71e9e0285a6f040000000800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e00000018eb01243173d870040000007d2e5ce800000000c037d1ff5600000000000000cc35d1ff637171e90000000018eb012402000000010000007d2e5ce800000000c037d1ff560000004436d1ff000000000000000081b771e9c037d1ff000000004436d1ff0436d1ff00e68dec0800000001000000a437d1ff010000001c73d870000000000000000043000000339768e9040000006c36d1ff0e000000b436d1ff8cc97e706c36d1ff0e000000adf861e918eb01243173d870040000007b2e5ce844b8bfeb00908eeca836d1ffc037d1ff040000008c908eec7c37d1ffd5c141e9c037d1ff7b2e5ce80000000000908eecd036d1ff00000000b038d1ff183ad1ff0000000044b8bfeb1038d1ff7c37d1ff6c37d1ffc037d1ff7b2e5ce8000000007b2e5ce8610d67e9c037d1ff7b2e5ce8280477e99835456f960300009a35456f10aa5e6f9a35456f9835456f68b85e6f881e77e9b30a47e9e81382e94c95b4ec7100000000908eec9c908eec30528bec1038d1ff7b2e5ce800000000c78469e91038d1ff0aeb3b52208989ec150645e9010000001038d1ff6c37d1ff44b8bfeb6c37d1ff00000000d837d1ff1038d1ff7b2e5ce8000000006c38d1ff8f0b67e97b2e5ce818eb012400000000000000000838d1ff7b2e5ce802000000040000007c37d1ff18eb01249835456f00000000901e77e9180000000300000000908eec480000004800000043000000640477e97669747954687265070000001a00000060eb0124000000000000000000000000a500000044b8bfeb1038d1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce96c38d1ff18eb012400908eeceeac73e943000000640477e9000059008bc95ce900908eec30528bec409181e900908eec430000005900000000528bec409181e900004300710000000300000030528bec89c75ce944b8bfebe2050000103dd1ff03000000a3f6a7eb89c75ce96c38d1ff7e8ea6c389c75ce997f5a7eb710000000000000030528bec7e8ea6c3e83cd1ff2079002488beba6ff0ca726f5600000000908eec000000005439d1ff8b1db8aa803a89ec7e8ea6c3000000009173d870ec55af6f00000000010000004892796f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003801802488beba6ff0ca726f56000000000000005439d1ff9d3daa71cc55af6f7039d1ff0b0000006839d1ff7d2e5ce800000000483bd1ff637171e900000000207900240b00000058a58cec40f180e9010000007d2e5ce800000000483bd1ff56000000cc39d1ff000000000000000081b771e9483bd1ff00000000cc39d1ff8c39d1ff05000000050000000c000000040000000100000012000000958c00000100000074d73500483bd1ff01000000e880746f0100000000908eec903ad1ff40f180e97e02000000908eec2079002400000000383ad1ff7b2e5ce8cc55af6f00908eec303ad1ff483bd1ff040000008c908eec043bd1ffd5c141e9483bd1ff7b2e5ce840f180e900908eec583ad1ff00000000000000000000000000000000cc55af6f983bd1ff043bd1fff43ad1ff483bd1ff7b2e5ce8000000007b2e5ce8610d67e9483bd1ff7b2e5ce8280477e94892796f860100004e92796f00a088ec4e92796f4892796f18a688ec881e77e9b30a47e978e388ec4c95b4ec2100000000908eec9c908eec30528bec983bd1ff7b2e5ce800000000c78469e9983bd1ff06b005fdf0298aec150645e901000000983bd1fff43ad1ffcc55af6ff43ad1ff00000000603bd1ff983bd1ff7b2e5ce800000000f43bd1ff8f0b67e97b2e5ce8e00864e80000000000000000903bd1ff7b2e5ce80200000004000000043bd1ff207900249c908eec04000000583bd1ff603bd1ff4892796f04000000ac3bd1ff01000000901e77e917885ee9010000004d5cb1eb485cb1eb00908eec4892796f00000000000000000000000000004300cc55af6f983bd1ff00908eeceeac73e943000000640477e9901e77e9e6ac73e961705ce9f43bd1ff55000000ac3bd1ffeeac73e943000000640477e900005900e3225ce900908eec30528bec409181e900908eec430000005900000000528bec409181e9000043005500000078e388ec2100000009215ce901000000ce3fb8aae83cd1ff40420f00a3f6a7eb09215ce9f43bd1ff7e8ea6c309215ce9ed0ea8eb2100000075270000003289ec0000000030528becef665c74db0e42e911ac58e99daf58e9103dd1ff010000007e8ea6c31b000000385cd1ff385cd1ff02000000103dd1ff0300000087e26deae43cd1ff0200000001000000a31eb8aa020000007c3cd1ff18ac89ec1dac89ec0f000000fc94b4ec7c3cd1ff18ac89ec7e8ea6c3e83cd1ff884dd1ff741ab8aaa81ab8aa000000000700000004000000e43cd1ff3b19b8aa000000000000000000000000000000000000000000000000884dd1ff0000000001000000844dd1ff7e8ea6c3f065b4ec00fd0000205db8aa308789ec010000000000000004000000b8e78aec18ac89ec005db8aa2ceab2eb101082e935000000000000000800000001100000ba5bd1ff99000000b8e78aec205db8aa508789ec030000000000000004000000e2050000108789ec00000000d991aeece583aeec10d0acec10d0acec50d0acec6170705f70726f63657373333200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a4ec00000000001000009dfe6feb00000000975673eb000000002516b8aa844dd1ff08000000a84dd1ff0000000000000000000000007c4dd1ff3b996feb00000000000000000000000000000000000000005015b8aad45cb8aadc5cb8aae85cb8aa804dd1ff0000000015b8aeec08000000ba5bd1ffd45bd1ffe05bd1ffee5bd1ff0f5cd1ff335cd1ff355cd1ff385cd1ff00000000535cd1ff6f5cd1ff825cd1ff9d5cd1ffbf5cd1ffd45cd1ffee5cd1ff015dd1ff1c5dd1ffe35ed1fffc5ed1ff465fd1ffc55fd1ff0000000010000000d6b0270006000000001000001100000064000000030000003400b8aa040000002000000005000000090000000700000000d0adec080000000000000009000000ec14b8aa0b000000752700000c000000752700000d000000752700000e000000752700001700000000000000190000007c4ed1ff1a0000001f0000001f000000de5fd1ff0f0000008c4ed1ff00000000000000000000000086da76325883c1a6b44d586d68c7843576386c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000636f6d2e6578616d706c652e7375646f67616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f3d2f73797374656d2f62696e2f6170705f70726f63657373333200414e44524f49445f444154413d2f6461746100444f574e4c4f41445f43414348453d2f646174612f636163686500414e44524f49445f534f434b45545f7a79676f74655f7365636f6e646172793d3900414e44524f49445f524f4f543d2f73797374656d00415345435f4d4f554e54504f494e543d2f6d6e742f6173656300414e44524f49445f424f4f544c4f474f3d3100414e44524f49445f4153534554533d2f73797374656d2f61707000424f4f54434c415353504154483d2f73797374656d2f6672616d65776f726b2f636f72652d6f6a2e6a61723a2f73797374656d2f6672616d65776f726b2f636f72652d6c69626172742e6a61723a2f73797374656d2f6672616d65776f726b2f636f6e7363727970742e6a61723a2f73797374656d2f6672616d65776f726b2f6f6b687474702e6a61723a2f73797374656d2f6672616d65776f726b2f6c65676163792d746573742e6a61723a2f73797374656d2f6672616d65776f726b2f626f756e6379636173746c652e6a61723a2f73797374656d2f6672616d65776f726b2f6578742e6a61723a2f73797374656d2f6672616d65776f726b2f6672616d65776f726b2e6a61723a2f73797374656d2f6672616d65776f726b2f74656c6570686f6e792d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f766f69702d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f696d732d636f6d6d6f6e2e6a61723a2f73797374656d2f6672616d65776f726b2f6170616368652d786d6c2e6a61723a2f73797374656d2f6672616d65776f726b2f6f72672e6170616368652e687474702e6c65676163792e626f6f742e6a617200414e44524f49445f53544f524147453d2f73746f7261676500504154483d2f7362696e3a2f73797374656d2f7362696e3a2f73797374656d2f62696e3a2f73797374656d2f7862696e3a2f76656e646f722f62696e3a2f76656e646f722f7862696e0053595354454d534552564552434c415353504154483d2f73797374656d2f6672616d65776f726b2f73657276696365732e6a61723a2f73797374656d2f6672616d65776f726b2f65746865726e65742d736572766963652e6a61723a2f73797374656d2f6672616d65776f726b2f776966692d736572766963652e6a61720045585445524e414c5f53544f524147453d2f736463617264002f73797374656d2f62696e2f6170705f70726f636573733332000000000000000000
+function: start: 3a2121 end: 3a217a name: art_quick_invoke_stub_internal
+function: start: 3a66a5 end: 3a6787 name: art_quick_invoke_static_stub
+function: start: a7129 end: a72f1 name: art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)
+function: start: 2fbd35 end: 2fc789 name: art::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::ArgArray*, art::JValue*, char const*)
+function: start: 2fcf75 end: 2fd88d name: art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned int)
+function: start: 2a089d end: 2a08bb name: art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobject*)
\ No newline at end of file
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 013999a..7e27c3e 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -161,7 +161,7 @@
/* 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/bin/hw/android.hardware.wifi@1.0-service" },
+ "vendor/bin/hw/android.hardware.wifi@1.0-service" },
/* Support Bluetooth legacy hal accessing /sys/class/rfkill */
{ 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
@@ -177,11 +177,8 @@
CAP_MASK_LONG(CAP_SETPCAP),
"system/bin/webview_zygote64" },
- { 00755, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SYS_PTRACE),
- "system/bin/crash_dump32" },
- { 00755, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SYS_PTRACE),
- "system/bin/crash_dump64" },
-
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
+ { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" },
{ 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" },
diff --git a/libcutils/include/cutils/properties.h b/libcutils/include/cutils/properties.h
index adf670b..b45f58f 100644
--- a/libcutils/include/cutils/properties.h
+++ b/libcutils/include/cutils/properties.h
@@ -43,7 +43,12 @@
** If the property read fails or returns an empty value, the default
** value is used (if nonnull).
*/
-int property_get(const char *key, char *value, const char *default_value);
+int property_get(const char *key, char *value, const char *default_value)
+/* Sometimes we use not-Bionic with this, so we need this check. */
+#if defined(__BIONIC_FORTIFY)
+ __overloadable __RENAME_CLANG(property_get)
+#endif
+ ;
/* property_get_bool: returns the value of key coerced into a
** boolean. If the property is not set, then the default value is returned.
@@ -106,14 +111,40 @@
/* property_set: returns 0 on success, < 0 on failure
*/
int property_set(const char *key, const char *value);
-
-int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
-#if defined(__BIONIC_FORTIFY) && !defined(__clang__)
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);
+
+#if defined(__BIONIC_FORTIFY)
+#define __property_get_err_str "property_get() called with too small of a buffer"
+
+#if defined(__clang__)
+
+/* Some projects use -Weverything; enable_if is clang-specific.
+** FIXME: This is marked used because we'll otherwise get complaints about an
+** unused static function. This is more robust than marking it unused, since
+** -Wused-but-marked-unused is a thing that will complain if this function is
+** actually used, thus making FORTIFY noisier when an error happens. It's going
+** to go away anyway during our FORTIFY cleanup.
+**/
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+__BIONIC_ERROR_FUNCTION_VISIBILITY
+int property_get(const char *key, char *value, const char *default_value)
+ __overloadable
+ __enable_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&
+ __bos(value) < PROPERTY_VALUE_MAX, __property_get_err_str)
+ __errorattr(__property_get_err_str)
+ __attribute__((used));
+#pragma clang diagnostic pop
+
+/* No object size? No FORTIFY.
+*/
+
+#else /* defined(__clang__) */
extern int __property_get_real(const char *, char *, const char *)
__asm__(__USER_LABEL_PREFIX__ "property_get");
-__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer");
+__errordecl(__property_get_too_small_error, __property_get_err_str);
__BIONIC_FORTIFY_INLINE
int property_get(const char *key, char *value, const char *default_value) {
@@ -124,7 +155,10 @@
return __property_get_real(key, value, default_value);
}
-#endif
+#endif /* defined(__clang__) */
+
+#undef __property_get_err_str
+#endif /* defined(__BIONIC_FORTIFY) */
#ifdef __cplusplus
}
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 43ad574..d2645e6 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -129,7 +129,7 @@
void* cookie;
};
-static void trampoline(void* raw_data, const char* name, const char* value) {
+static void trampoline(void* raw_data, const char* name, const char* value, unsigned /*serial*/) {
callback_data* data = reinterpret_cast<callback_data*>(raw_data);
data->callback(name, value, data->cookie);
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 747fcc8..310dbf4 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,13 +15,17 @@
//
liblog_sources = [
+ "config_read.c",
+ "config_write.c",
+ "local_logger.c",
"log_event_list.c",
"log_event_write.c",
- "logger_write.c",
- "config_write.c",
- "logger_name.c",
- "logger_lock.c",
"log_ratelimit.cpp",
+ "logger_lock.c",
+ "logger_name.c",
+ "logger_read.c",
+ "logger_write.c",
+ "logprint.c",
]
liblog_host_sources = [
"fake_log_device.c",
@@ -29,15 +33,12 @@
]
liblog_target_sources = [
"event_tag_map.cpp",
- "config_read.c",
"log_time.cpp",
"properties.c",
- "logprint.c",
"pmsg_reader.c",
"pmsg_writer.c",
"logd_reader.c",
"logd_writer.c",
- "logger_read.c",
]
// Shared and static library for host and device
diff --git a/liblog/README b/liblog/README
index 610338c..40a39ad 100644
--- a/liblog/README
+++ b/liblog/README
@@ -108,6 +108,11 @@
int android_log_destroy(android_log_context *ctx)
+ #include <log/log_frontend.h>
+
+ int android_set_log_frontend(int frontend_flag)
+ int android_get_log_frontend()
+
Link with -llog
DESCRIPTION
@@ -162,6 +167,13 @@
when opening the sub-log. It is recommended to open the log
ANDROID_LOG_RDONLY in these cases.
+ android_set_log_frontend() selects frontend filters. Argument is either
+ LOGGER_DEFAULT, LOGGER_LOGD, LOGGER_NULL or LOGGER_LOCAL. Log to logger
+ daemon for default or logd, drop contents on floor, or log into local
+ memory respectively. Both android_set_log_frontend() and
+ android_get_log_frontend() return the current frontend mask, or a
+ negative errno for any problems.
+
ERRORS
If messages fail, a negative error code will be returned to the caller.
@@ -194,4 +206,4 @@
- 17 Oct 2016 LIBLOG(3)
+ 08 Feb 2017 LIBLOG(3)
diff --git a/liblog/config_read.c b/liblog/config_read.c
index 1f54152..b9a281b 100644
--- a/liblog/config_read.c
+++ b/liblog/config_read.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <log/log_frontend.h>
+
#include "config_read.h"
#include "logger.h"
@@ -52,11 +54,35 @@
}
LIBLOG_HIDDEN void __android_log_config_read() {
-#if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_read logdLoggerRead;
- extern struct android_log_transport_read pmsgLoggerRead;
+ if (__android_log_frontend & LOGGER_LOCAL) {
+ extern struct android_log_transport_read localLoggerRead;
- __android_log_add_transport(&__android_log_transport_read, &logdLoggerRead);
- __android_log_add_transport(&__android_log_persist_read, &pmsgLoggerRead);
+ __android_log_add_transport(&__android_log_transport_read,
+ &localLoggerRead);
+ }
+
+#if (FAKE_LOG_DEVICE == 0)
+ if ((__android_log_frontend == LOGGER_DEFAULT) ||
+ (__android_log_frontend & LOGGER_LOGD)) {
+ extern struct android_log_transport_read logdLoggerRead;
+ extern struct android_log_transport_read pmsgLoggerRead;
+
+ __android_log_add_transport(&__android_log_transport_read,
+ &logdLoggerRead);
+ __android_log_add_transport(&__android_log_persist_read,
+ &pmsgLoggerRead);
+ }
#endif
}
+
+LIBLOG_HIDDEN void __android_log_config_read_close() {
+ struct android_log_transport_read *transport;
+ struct listnode *n;
+
+ read_transport_for_each_safe(transport, n, &__android_log_transport_read) {
+ list_remove(&transport->node);
+ }
+ read_transport_for_each_safe(transport, n, &__android_log_persist_read) {
+ list_remove(&transport->node);
+ }
+}
diff --git a/liblog/config_read.h b/liblog/config_read.h
index 49a3b75..892e80d 100644
--- a/liblog/config_read.h
+++ b/liblog/config_read.h
@@ -28,22 +28,31 @@
#define read_transport_for_each(transp, transports) \
for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node); \
- ((transp) != node_to_item(transports, \
- struct android_log_transport_read, node)); \
+ struct android_log_transport_read, node); \
+ ((transp) != node_to_item((transports), \
+ struct android_log_transport_read, \
+ node)) && \
+ ((transp) != node_to_item((transp)->node.next, \
+ struct android_log_transport_read, \
+ node)); \
(transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_read, node)) \
+ struct android_log_transport_read, node))
#define read_transport_for_each_safe(transp, n, transports) \
for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_read, node), \
+ struct android_log_transport_read, node), \
(n) = (transp)->node.next; \
- ((transp) != node_to_item(transports, \
- struct android_log_transport_read, node)); \
- (transp) = node_to_item(n, struct android_log_transport_read, node), \
+ ((transp) != node_to_item((transports), \
+ struct android_log_transport_read, \
+ node)) && \
+ ((transp) != node_to_item((n), struct android_log_transport_read, \
+ node)); \
+ (transp) = node_to_item((n), struct android_log_transport_read, \
+ node), \
(n) = (transp)->node.next)
LIBLOG_HIDDEN void __android_log_config_read();
+LIBLOG_HIDDEN void __android_log_config_read_close();
__END_DECLS
diff --git a/liblog/config_write.c b/liblog/config_write.c
index d689f63..583dcff 100644
--- a/liblog/config_write.c
+++ b/liblog/config_write.c
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <log/log_frontend.h>
+
#include "config_write.h"
#include "logger.h"
@@ -52,15 +54,42 @@
}
LIBLOG_HIDDEN void __android_log_config_write() {
+ if (__android_log_frontend & LOGGER_LOCAL) {
+ extern struct android_log_transport_write localLoggerWrite;
+
+ __android_log_add_transport(&__android_log_transport_write,
+ &localLoggerWrite);
+ }
+
+ if ((__android_log_frontend == LOGGER_DEFAULT) ||
+ (__android_log_frontend & LOGGER_LOGD)) {
#if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_write logdLoggerWrite;
- extern struct android_log_transport_write pmsgLoggerWrite;
+ extern struct android_log_transport_write logdLoggerWrite;
+ extern struct android_log_transport_write pmsgLoggerWrite;
- __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
- __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
+ __android_log_add_transport(&__android_log_transport_write,
+ &logdLoggerWrite);
+ __android_log_add_transport(&__android_log_persist_write,
+ &pmsgLoggerWrite);
#else
- extern struct android_log_transport_write fakeLoggerWrite;
+ extern struct android_log_transport_write fakeLoggerWrite;
- __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
+ __android_log_add_transport(&__android_log_transport_write,
+ &fakeLoggerWrite);
#endif
+ }
+}
+
+LIBLOG_HIDDEN void __android_log_config_write_close() {
+ struct android_log_transport_write *transport;
+ struct listnode *n;
+
+ write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
+ transport->logMask = 0;
+ list_remove(&transport->node);
+ }
+ write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
+ transport->logMask = 0;
+ list_remove(&transport->node);
+ }
}
diff --git a/liblog/config_write.h b/liblog/config_write.h
index 3b01a9a..8825411 100644
--- a/liblog/config_write.h
+++ b/liblog/config_write.h
@@ -29,21 +29,30 @@
#define write_transport_for_each(transp, transports) \
for ((transp) = node_to_item((transports)->next, \
struct android_log_transport_write, node); \
- ((transp) != node_to_item(transports, \
- struct android_log_transport_write, node)); \
+ ((transp) != node_to_item((transports), \
+ struct android_log_transport_write, \
+ node)) && \
+ ((transp) != node_to_item((transp)->node.next, \
+ struct android_log_transport_write, \
+ node)); \
(transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_write, node)) \
+ struct android_log_transport_write, node))
#define write_transport_for_each_safe(transp, n, transports) \
for ((transp) = node_to_item((transports)->next, \
struct android_log_transport_write, node), \
(n) = (transp)->node.next; \
- ((transp) != node_to_item(transports, \
- struct android_log_transport_write, node)); \
- (transp) = node_to_item(n, struct android_log_transport_write, node), \
+ ((transp) != node_to_item((transports), \
+ struct android_log_transport_write, \
+ node)) && \
+ ((transp) != node_to_item((n), struct android_log_transport_write, \
+ node)); \
+ (transp) = node_to_item((n), struct android_log_transport_write, \
+ node), \
(n) = (transp)->node.next)
LIBLOG_HIDDEN void __android_log_config_write();
+LIBLOG_HIDDEN void __android_log_config_write_close();
__END_DECLS
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index c53ea2c..1f08eb4 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,15 +27,63 @@
#include <experimental/string_view>
#include <functional>
+#include <string>
#include <unordered_map>
#include <log/event_tag_map.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+#include <private/android_logger.h>
#include "log_portability.h"
+#include "logd_reader.h"
#define OUT_TAG "EventTagMap"
-typedef std::experimental::string_view MapString;
+class MapString {
+private:
+ const std::string* alloc; // HAS-AN
+ const std::experimental::string_view str; // HAS-A
+
+public:
+ operator const std::experimental::string_view() const { return str; }
+
+ const char* data() const { return str.data(); }
+ size_t length() const { return str.length(); }
+
+ bool operator== (const MapString& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!= (const MapString& rval) const {
+ return !(*this == rval);
+ }
+
+ MapString(const char* str, size_t len) : alloc(NULL), str(str, len) { }
+ explicit MapString(const std::string& str) :
+ alloc(new std::string(str)),
+ str(alloc->data(), alloc->length()) { }
+ MapString(MapString &&rval) :
+ alloc(rval.alloc),
+ str(rval.data(), rval.length()) {
+ rval.alloc = NULL;
+ }
+ explicit MapString(const MapString &rval) :
+ alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+ str(alloc ? alloc->data() : rval.data(), rval.length()) { }
+
+ ~MapString() { if (alloc) delete alloc; }
+};
+
+// Hash for MapString
+template <> struct std::hash<MapString>
+ : public std::unary_function<const MapString&, size_t> {
+ size_t operator()(const MapString& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(std::experimental::string_view(__t));
+ }
+};
typedef std::pair<MapString, MapString> TagFmt;
@@ -53,57 +102,125 @@
// Map
struct EventTagMap {
+# define NUM_MAPS 2
// memory-mapped source file; we get strings from here
- void* mapAddr;
- size_t mapLen;
+ void* mapAddr[NUM_MAPS];
+ size_t mapLen[NUM_MAPS];
private:
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+ std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+ std::unordered_map<MapString, uint32_t> Tag2Idx;
+ // protect unordered sets
+ android::RWLock rwlock;
public:
- EventTagMap() : mapAddr(NULL), mapLen(0) { }
+ EventTagMap() {
+ memset(mapAddr, 0, sizeof(mapAddr));
+ memset(mapLen, 0, sizeof(mapLen));
+ }
~EventTagMap() {
Idx2TagFmt.clear();
- if (mapAddr) {
- munmap(mapAddr, mapLen);
- mapAddr = 0;
+ TagFmt2Idx.clear();
+ Tag2Idx.clear();
+ for (size_t which = 0; which < NUM_MAPS; ++which) {
+ if (mapAddr[which]) {
+ munmap(mapAddr[which], mapLen[which]);
+ mapAddr[which] = 0;
+ }
}
}
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
const TagFmt* find(uint32_t tag) const;
+ int find(TagFmt&& tagfmt) const;
+ int find(MapString&& tag) const;
};
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
- it = Idx2TagFmt.find(tag);
- if (it != Idx2TagFmt.end()) {
- if (verbose) {
- fprintf(stderr,
- OUT_TAG ": duplicate tag entries %" PRIu32
- ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
- it->first,
- (int)it->second.first.length(), it->second.first.data(),
- (int)it->second.second.length(), it->second.second.data(),
- tag,
- (int)tagfmt.first.length(), tagfmt.first.data(),
- (int)tagfmt.second.length(), tagfmt.second.data());
+ bool ret = true;
+ static const char errorFormat[] = OUT_TAG ": duplicate tag entries %" PRIu32
+ ":%.*s:%.*s and %" PRIu32
+ ":%.*s:%.*s)\n";
+ android::RWLock::AutoWLock writeLock(rwlock);
+ {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it != Idx2TagFmt.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->first,
+ (int)it->second.first.length(), it->second.first.data(),
+ (int)it->second.second.length(), it->second.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
}
- return false;
}
- Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
- return true;
+ {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ it = TagFmt2Idx.find(tagfmt);
+ if (it != TagFmt2Idx.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->second,
+ (int)it->first.first.length(), it->first.first.data(),
+ (int)it->first.second.length(), it->first.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+ }
+ }
+
+ {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ it = Tag2Idx.find(tagfmt.first);
+ if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+ Tag2Idx.erase(it);
+ it = Tag2Idx.end();
+ }
+ if (it == Tag2Idx.end()) {
+ Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+ }
+ }
+
+ return ret;
}
const TagFmt* EventTagMap::find(uint32_t tag) const {
std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
it = Idx2TagFmt.find(tag);
if (it == Idx2TagFmt.end()) return NULL;
return &(it->second);
}
+int EventTagMap::find(TagFmt&& tagfmt) const {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = TagFmt2Idx.find(std::move(tagfmt));
+ if (it == TagFmt2Idx.end()) return -1;
+ return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Tag2Idx.find(std::move(tag));
+ if (it == Tag2Idx.end()) return -1;
+ return it->second;
+}
+
// Scan one tag line.
//
// "*pData" should be pointing to the first digit in the tag number. On
@@ -157,6 +274,19 @@
fmtLen = cp - fmt;
}
+ // KISS Only report identicals if they are global
+ // Ideally we want to check if there are identicals
+ // recorded for the same uid, but recording that
+ // unused detail in our database is too burdensome.
+ bool verbose = true;
+ while ((*cp != '#') && (*cp != '\n')) ++cp;
+ if (*cp == '#') {
+ do {
+ ++cp;
+ } while (isspace(*cp) && (*cp != '\n'));
+ verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+ }
+
while (*cp != '\n') ++cp;
#ifdef DEBUG
fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
@@ -164,24 +294,33 @@
*pData = cp;
if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
- MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+ MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
return 0;
}
errno = EMLINK;
return -1;
}
+static const char* eventTagFiles[NUM_MAPS] = {
+ EVENT_TAG_MAP_FILE,
+ "/dev/event-log-tags",
+};
+
// Parse the tags out of the file.
-static int parseMapLines(EventTagMap* map) {
- char* cp = static_cast<char*>(map->mapAddr);
- size_t len = map->mapLen;
+static int parseMapLines(EventTagMap* map, size_t which) {
+ char* cp = static_cast<char*>(map->mapAddr[which]);
+ size_t len = map->mapLen[which];
char* endp = cp + len;
// insist on EOL at EOF; simplifies parsing and null-termination
if (!len || (*(endp - 1) != '\n')) {
#ifdef DEBUG
- fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
+ fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+ which, len);
#endif
+ if (which) { // do not propagate errors for other files
+ return 0;
+ }
errno = EINVAL;
return -1;
}
@@ -199,7 +338,9 @@
} else if (isdigit(*cp)) {
// looks like a tag; scan it out
if (scanTagLine(map, &cp, lineNum) != 0) {
- return -1;
+ if (!which || (errno != EMLINK)) {
+ return -1;
+ }
}
lineNum++; // we eat the '\n'
// leave lineStart==true
@@ -226,57 +367,87 @@
// We create a private mapping because we want to terminate the log tag
// strings with '\0'.
LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
- int save_errno;
+ EventTagMap* newTagMap;
+ off_t end[NUM_MAPS];
+ int save_errno, fd[NUM_MAPS];
+ size_t which;
- const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
- int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
+ memset(fd, -1, sizeof(fd));
+ memset(end, 0, sizeof(end));
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+ if (fd[which] < 0) {
+ if (!which) {
+ save_errno = errno;
+ fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_errno;
+ }
+ continue;
+ }
+ end[which] = lseek(fd[which], 0L, SEEK_END);
save_errno = errno;
- fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
- tagfile, strerror(save_errno));
- errno = save_errno;
- return NULL;
- }
- off_t end = lseek(fd, 0L, SEEK_END);
- save_errno = errno;
- (void)lseek(fd, 0L, SEEK_SET);
- if (end < 0) {
- fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
- tagfile, strerror(save_errno));
- close(fd);
- errno = save_errno;
- return NULL;
+ (void)lseek(fd[which], 0L, SEEK_SET);
+ if (!which && (end[0] < 0)) {
+ fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_close;
+ }
+ if (fileName) break; // Only allow one as specified
}
- EventTagMap* newTagMap = new EventTagMap;
+ newTagMap = new EventTagMap;
if (newTagMap == NULL) {
save_errno = errno;
- close(fd);
- errno = save_errno;
- return NULL;
+ goto fail_close;
}
- newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
- MAP_PRIVATE, fd, 0);
- save_errno = errno;
- close(fd);
- fd = -1;
- if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
- fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
- tagfile, strerror(save_errno));
- delete newTagMap;
- errno = save_errno;
- return NULL;
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (fd[which] >= 0) {
+ newTagMap->mapAddr[which] = mmap(NULL, end[which],
+ which ?
+ PROT_READ :
+ PROT_READ | PROT_WRITE,
+ which ?
+ MAP_SHARED :
+ MAP_PRIVATE,
+ fd[which], 0);
+ save_errno = errno;
+ close(fd[which]);
+ fd[which] = -1;
+ if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+ (newTagMap->mapAddr[which] != NULL)) {
+ newTagMap->mapLen[which] = end[which];
+ } else if (!which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_unmap;
+ }
+ }
}
- newTagMap->mapLen = end;
-
- if (parseMapLines(newTagMap) != 0) {
- delete newTagMap;
- return NULL;
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (parseMapLines(newTagMap, which) != 0) {
+ delete newTagMap;
+ return NULL;
+ }
}
return newTagMap;
+
+fail_unmap:
+ save_errno = EINVAL;
+ delete newTagMap;
+fail_close:
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+fail_errno:
+ errno = save_errno;
+ return NULL;
}
// Close the map.
@@ -320,3 +491,75 @@
if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
return tagStr;
}
+
+// Look up tagname, generate one if necessary, and return a tag
+LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
+ const char* tagname,
+ const char* format,
+ int prio) {
+ size_t len = strlen(tagname);
+ if (!len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+ !__android_log_is_loggable_len(prio, tagname, len,
+ __android_log_is_debuggable() ?
+ ANDROID_LOG_VERBOSE :
+ ANDROID_LOG_DEBUG)) {
+ errno = EPERM;
+ return -1;
+ }
+
+ if (!format) format="";
+ ssize_t fmtLen = strlen(format);
+ int ret = map->find(TagFmt(std::make_pair(MapString(tagname, len),
+ MapString(format, fmtLen))));
+ if (ret != -1) return ret;
+
+ // call event tag service to arrange for a new tag
+ char *buf = NULL;
+ // Can not use android::base::StringPrintf, asprintf + free instead.
+ static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+ ret = asprintf(&buf, command_template, tagname, format);
+ if (ret > 0) {
+ // Add some buffer margin for an estimate of the full return content.
+ char *cp;
+ size_t size = ret - strlen(command_template) +
+ strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+ if (size > (size_t)ret) {
+ cp = static_cast<char*>(realloc(buf, size));
+ if (cp) {
+ buf = cp;
+ } else {
+ size = ret;
+ }
+ } else {
+ size = ret;
+ }
+ // Ask event log tag service for an allocation
+ if (__send_log_msg(buf, size) >= 0) {
+ buf[size - 1] = '\0';
+ unsigned long val = strtoul(buf, &cp, 10); // return size
+ if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+ val = strtoul(cp + 1, &cp, 10); // allocated tag number
+ if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+ free(buf);
+ ret = val;
+ // cache
+ map->emplaceUnique(ret, TagFmt(std::make_pair(
+ MapString(std::string(tagname, len)),
+ MapString(std::string(format, fmtLen)))));
+ return ret;
+ }
+ }
+ }
+ free(buf);
+ }
+
+ // Hail Mary
+ ret = map->find(MapString(tagname, len));
+ if (ret == -1) errno = ESRCH;
+ return ret;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 4939221..1d7a157 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -612,7 +612,12 @@
bail:
unlock();
- return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len;
+ int len = 0;
+ for (i = 0; i < count; ++i) {
+ len += vector[i].iov_len;
+ }
+ return len;
+
error:
unlock();
return -1;
@@ -715,6 +720,12 @@
return redirectWritev(fd, vector, count);
}
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf __unused,
+ size_t buf_size __unused)
+{
+ return -ENODEV;
+}
+
LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
const char *tag __unused,
int def)
diff --git a/liblog/fake_writer.c b/liblog/fake_writer.c
index dab8bc5..2350673 100644
--- a/liblog/fake_writer.c
+++ b/liblog/fake_writer.c
@@ -46,9 +46,19 @@
int i;
for (i = 0; i < LOG_ID_MAX; i++) {
- char buf[sizeof("/dev/log_security")];
+ /*
+ * Known maximum size string, plus an 8 character margin to deal with
+ * possible independent changes to android_log_id_to_name().
+ */
+ char buf[sizeof("/dev/log_security") + 8];
+ if (logFds[i] >= 0) {
+ continue;
+ }
snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
logFds[i] = fakeLogOpen(buf, O_WRONLY);
+ if (logFds[i] < 0) {
+ fprintf(stderr, "fakeLogOpen(%s, O_WRONLY) failed\n", buf);
+ }
}
return 0;
}
@@ -66,16 +76,28 @@
struct iovec *vec, size_t nr)
{
ssize_t ret;
- int logFd;
+ size_t i;
+ int logFd, len;
if (/*(int)log_id >= 0 &&*/ (int)log_id >= (int)LOG_ID_MAX) {
- return -EBADF;
+ return -EINVAL;
+ }
+
+ len = 0;
+ for (i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
}
logFd = logFds[(int)log_id];
ret = TEMP_FAILURE_RETRY(fakeLogWritev(logFd, vec, nr));
if (ret < 0) {
ret = -errno;
+ } else if (ret > len) {
+ ret = len;
}
return ret;
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 22e62ec..e57e47b 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -58,6 +58,12 @@
const char* android_lookupEventFormat_len(const EventTagMap* map,
size_t* len, unsigned int tag);
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+ const char* format, int prio);
+
#ifdef __cplusplus
}
#endif
diff --git a/liblog/include/log/log_frontend.h b/liblog/include/log/log_frontend.h
new file mode 100644
index 0000000..9527779
--- /dev/null
+++ b/liblog/include/log/log_frontend.h
@@ -0,0 +1,34 @@
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** This file is dual licensed. It may be redistributed and/or modified
+** under the terms of the Apache 2.0 License OR version 2 of the GNU
+** General Public License.
+*/
+
+#ifndef _LIBS_LOG_FRONTEND_H
+#define _LIBS_LOG_FRONTEND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Logging frontends, bit mask to select features. Function returns selection.
+ */
+#define LOGGER_DEFAULT 0x0
+#define LOGGER_LOGD 0x1
+#define LOGGER_KERNEL 0x2 /* Reserved/Deprecated */
+#define LOGGER_NULL 0x4 /* Does not release resources of other selections */
+#define LOGGER_LOCAL 0x8 /* logs sent to local memory */
+
+/* Both return the selected frontend flag mask, or negative errno */
+int android_set_log_frontend(int frontend_flag);
+int android_get_log_frontend();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBS_LOG_FRONTEND_H */
diff --git a/liblog/include/log/log_radio.h b/liblog/include/log/log_radio.h
index 30a73f2..430e522 100644
--- a/liblog/include/log/log_radio.h
+++ b/liblog/include/log/log_radio.h
@@ -38,6 +38,10 @@
/* --------------------------------------------------------------------- */
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
/*
* Simplified macro to send a verbose radio log message using current LOG_TAG.
*/
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 5b5eebc..6a44b56 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -251,7 +251,11 @@
#define ANDROID_LOG_WRONLY O_WRONLY
#define ANDROID_LOG_RDWR O_RDWR
#define ANDROID_LOG_ACCMODE O_ACCMODE
+#ifndef O_NONBLOCK
+#define ANDROID_LOG_NONBLOCK 0x00000800
+#else
#define ANDROID_LOG_NONBLOCK O_NONBLOCK
+#endif
#if __ANDROID_USE_LIBLOG_READER_INTERFACE > 2
#define ANDROID_LOG_WRAP 0x40000000 /* Block until buffer about to wrap */
#define ANDROID_LOG_WRAP_DEFAULT_TIMEOUT 7200 /* 2 hour default */
diff --git a/liblog/include/log/log_system.h b/liblog/include/log/log_system.h
index 8c1ec96..394a106 100644
--- a/liblog/include/log/log_system.h
+++ b/liblog/include/log/log_system.h
@@ -36,6 +36,10 @@
#endif
#endif
+#ifndef __predict_false
+#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
+#endif
+
/*
* Simplified macro to send a verbose system log message using current LOG_TAG.
*/
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
new file mode 100644
index 0000000..d504342
--- /dev/null
+++ b/liblog/local_logger.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#if !defined(__MINGW32__)
+#include <pwd.h>
+#endif
+#include <sched.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <log/uio.h>
+
+#include <cutils/list.h> /* template, no library dependency */
+#include <log/log_frontend.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+#include <system/thread_defs.h>
+
+#include "config_read.h"
+#include "config_write.h"
+#include "log_portability.h"
+#include "logger.h"
+
+static const char baseServiceName[] = "android.logd";
+
+static int writeToLocalInit();
+static int writeToLocalAvailable(log_id_t logId);
+static void writeToLocalReset();
+static int writeToLocalWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr);
+
+LIBLOG_HIDDEN struct android_log_transport_write localLoggerWrite = {
+ .node = { &localLoggerWrite.node, &localLoggerWrite.node },
+ .context.private = NULL,
+ .name = "local",
+ .available = writeToLocalAvailable,
+ .open = writeToLocalInit,
+ .close = writeToLocalReset,
+ .write = writeToLocalWrite,
+};
+
+static int writeToLocalVersion(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static int writeToLocalRead(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg);
+static int writeToLocalPoll(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+static void writeToLocalClose(struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp);
+static int writeToLocalClear(struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static ssize_t writeToLocalGetSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+static ssize_t writeToLocalSetSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused,
+ size_t size);
+static ssize_t writeToLocalGetReadbleSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp);
+
+struct android_log_transport_read localLoggerRead = {
+ .node = { &localLoggerRead.node, &localLoggerRead.node },
+ .name = "local",
+ .available = writeToLocalAvailable,
+ .version = writeToLocalVersion,
+ .read = writeToLocalRead,
+ .poll = writeToLocalPoll,
+ .close = writeToLocalClose,
+ .clear = writeToLocalClear,
+ .getSize = writeToLocalGetSize,
+ .setSize = writeToLocalSetSize,
+ .getReadableSize = writeToLocalGetReadbleSize,
+ .getPrune = NULL,
+ .setPrune = NULL,
+ .getStats = NULL,
+};
+
+struct LogBufferElement {
+ struct listnode node;
+ log_id_t logId;
+ pid_t tid;
+ log_time timestamp;
+ unsigned short len;
+ char msg[];
+};
+
+static const size_t MAX_SIZE_DEFAULT = 32768;
+
+/*
+ * Number of log buffers we support with the following assumption:
+ * . . .
+ * LOG_ID_SECURITY = 5, // security logs go to the system logs only
+ * LOG_ID_KERNEL = 6, // place last, third-parties can not use it
+ * LOG_ID_MAX
+ * } log_id_t;
+ *
+ * Confirm the following should <log/log_id.h> be adjusted in the future.
+ */
+#define NUMBER_OF_LOG_BUFFERS ((LOG_ID_SECURITY == (LOG_ID_MAX - 2)) ? \
+ LOG_ID_SECURITY : \
+ LOG_ID_KERNEL)
+#define BLOCK_LOG_BUFFERS(id) (((id) == LOG_ID_SECURITY) || \
+ ((id) == LOG_ID_KERNEL))
+
+static struct LogBuffer {
+ struct listnode head;
+ pthread_rwlock_t listLock;
+ char *serviceName; /* Also indicates ready by having a value */
+ /* Order and proximity important for memset */
+ size_t number[NUMBER_OF_LOG_BUFFERS]; /* clear memset */
+ size_t size[NUMBER_OF_LOG_BUFFERS]; /* clear memset */
+ size_t totalSize[NUMBER_OF_LOG_BUFFERS]; /* init memset */
+ size_t maxSize[NUMBER_OF_LOG_BUFFERS]; /* init MAX_SIZE_DEFAULT */
+ struct listnode *last[NUMBER_OF_LOG_BUFFERS]; /* init &head */
+} logbuf = {
+ .head = { &logbuf.head, &logbuf.head },
+ .listLock = PTHREAD_RWLOCK_INITIALIZER,
+};
+
+static void LogBufferInit(struct LogBuffer *log) {
+ size_t i;
+
+ pthread_rwlock_wrlock(&log->listLock);
+ list_init(&log->head);
+ memset(log->number, 0,
+ sizeof(log->number) + sizeof(log->size) + sizeof(log->totalSize));
+ for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
+ log->maxSize[i] = MAX_SIZE_DEFAULT;
+ log->last[i] = &log->head;
+ }
+#ifdef __BIONIC__
+ asprintf(&log->serviceName, "%s@%d:%d", baseServiceName,
+ __android_log_uid(), getpid());
+#else
+ char buffer[sizeof(baseServiceName) + 1 + 5 + 1 + 5 + 8];
+ snprintf(buffer, sizeof(buffer), "%s@%d:%d", baseServiceName,
+ __android_log_uid(), getpid());
+ log->serviceName = strdup(buffer);
+#endif
+ pthread_rwlock_unlock(&log->listLock);
+}
+
+static void LogBufferClear(struct LogBuffer *log) {
+ size_t i;
+ struct listnode *node;
+
+ pthread_rwlock_wrlock(&log->listLock);
+ memset(log->number, 0, sizeof(log->number) + sizeof(log->size));
+ for (i = 0; i < NUMBER_OF_LOG_BUFFERS; ++i) {
+ log->last[i] = &log->head;
+ }
+ while ((node = list_head(&log->head)) != &log->head) {
+ struct LogBufferElement *element;
+
+ element = node_to_item(node, struct LogBufferElement, node);
+ list_remove(node);
+ free(element);
+ }
+ pthread_rwlock_unlock(&log->listLock);
+}
+
+static inline void LogBufferFree(struct LogBuffer *log) {
+ pthread_rwlock_wrlock(&log->listLock);
+ free(log->serviceName);
+ log->serviceName = NULL;
+ pthread_rwlock_unlock(&log->listLock);
+ LogBufferClear(log);
+}
+
+static int LogBufferLog(struct LogBuffer *log,
+ struct LogBufferElement *element) {
+ log_id_t logId = element->logId;
+
+ pthread_rwlock_wrlock(&log->listLock);
+ log->number[logId]++;
+ log->size[logId] += element->len;
+ log->totalSize[logId] += element->len;
+ /* prune entry(s) until enough space is available */
+ if (log->last[logId] == &log->head) {
+ log->last[logId] = list_tail(&log->head);
+ }
+ while (log->size[logId] > log->maxSize[logId]) {
+ struct listnode *node = log->last[logId];
+ struct LogBufferElement *e;
+ struct android_log_logger_list *logger_list;
+
+ e = node_to_item(node, struct LogBufferElement, node);
+ log->number[logId]--;
+ log->size[logId] -= e->len;
+ logger_list_rdlock();
+ logger_list_for_each(logger_list) {
+ struct android_log_transport_context *transp;
+
+ transport_context_for_each(transp, logger_list) {
+ if ((transp->transport == &localLoggerRead) &&
+ (transp->context.node == node)) {
+ if (node == &log->head) {
+ transp->context.node = &log->head;
+ } else {
+ transp->context.node = node->next;
+ }
+ }
+ }
+ }
+ logger_list_unlock();
+ if (node != &log->head) {
+ log->last[logId] = node->prev;
+ }
+ list_remove(node);
+ free(e);
+ }
+ /* add entry to list */
+ list_add_head(&log->head, &element->node);
+ /* ToDo: wake up all readers */
+ pthread_rwlock_unlock(&log->listLock);
+
+ return element->len;
+}
+
+/*
+ * return zero if permitted to log directly to logd,
+ * return 1 if binder server started and
+ * return negative error number if failed to start binder server.
+ */
+static int writeToLocalInit() {
+ pthread_attr_t attr;
+ struct LogBuffer *log;
+
+ if (writeToLocalAvailable(LOG_ID_MAIN) < 0) {
+ return -EPERM;
+ }
+
+ log = &logbuf;
+ if (!log->serviceName) {
+ LogBufferInit(log);
+ }
+
+ if (!log->serviceName) {
+ LogBufferFree(log);
+ return -ENOMEM;
+ }
+
+ return EPERM; /* successful local-only logging */
+}
+
+static void writeToLocalReset() {
+ LogBufferFree(&logbuf);
+}
+
+static int writeToLocalAvailable(log_id_t logId) {
+#if !defined(__MINGW32__)
+ uid_t uid;
+#endif
+
+ if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+ return -EINVAL;
+ }
+
+ /* Android hard coded permitted, system goes to logd */
+#if !defined(__MINGW32__)
+ if (__android_log_frontend == LOGGER_DEFAULT) {
+ uid = __android_log_uid();
+ if ((uid < AID_APP) && (getpwuid(uid) != NULL)) {
+ return -EPERM;
+ }
+ }
+#endif
+
+ /* ToDo: Ask package manager for LOGD permissions */
+ /* Assume we do _not_ have permissions to go to LOGD, so must go local */
+ return 0;
+}
+
+static int writeToLocalWrite(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr) {
+ size_t len, i;
+ struct LogBufferElement *element;
+
+ if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+ return -EINVAL;
+ }
+
+ len = 0;
+ for (i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+ element = (struct LogBufferElement *)calloc(1,
+ sizeof(struct LogBufferElement) + len + 1);
+ if (!element) {
+ return errno ? -errno : -ENOMEM;
+ }
+ element->timestamp.tv_sec = ts->tv_sec;
+ element->timestamp.tv_nsec = ts->tv_nsec;
+#ifdef __BIONIC__
+ element->tid = gettid();
+#else
+ element->tid = getpid();
+#endif
+ element->logId = logId;
+ element->len = len;
+
+ char *cp = element->msg;
+ for (i = 0; i < nr; ++i) {
+ size_t iov_len = vec[i].iov_len;
+ if (iov_len > len) {
+ iov_len = len;
+ }
+ memcpy(cp, vec[i].iov_base, iov_len);
+ len -= iov_len;
+ if (len == 0) {
+ break;
+ }
+ cp += iov_len;
+ }
+
+ return LogBufferLog(&logbuf, element);
+}
+
+static int writeToLocalVersion(
+ struct android_log_logger *logger __unused,
+ struct android_log_transport_context *transp __unused) {
+ return 3;
+}
+
+/* within reader lock, serviceName already validated */
+static struct listnode *writeToLocalNode(
+ struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp) {
+ struct listnode *node;
+ unsigned logMask;
+ unsigned int tail;
+
+ node = transp->context.node;
+ if (node) {
+ return node;
+ }
+
+ if (!logger_list->tail) {
+ return transp->context.node = &logbuf.head;
+ }
+
+ logMask = transp->logMask;
+ tail = logger_list->tail;
+
+ for (node = list_head(&logbuf.head); node != &logbuf.head; node = node->next) {
+ struct LogBufferElement *element;
+ log_id_t logId;
+
+ element = node_to_item(node, struct LogBufferElement, node);
+ logId = element->logId;
+
+ if ((logMask & (1 << logId)) && !--tail) {
+ node = node->next;
+ break;
+ }
+ }
+ return transp->context.node = node;
+}
+
+static int writeToLocalRead(
+ struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp,
+ struct log_msg *log_msg) {
+ int ret;
+ struct listnode *node;
+ unsigned logMask;
+
+ pthread_rwlock_rdlock(&logbuf.listLock);
+ if (!logbuf.serviceName) {
+ pthread_rwlock_unlock(&logbuf.listLock);
+ return (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
+ }
+
+ logMask = transp->logMask;
+
+ node = writeToLocalNode(logger_list, transp);
+
+ ret = 0;
+
+ while (node != list_head(&logbuf.head)) {
+ struct LogBufferElement *element;
+ log_id_t logId;
+
+ node = node->prev;
+ element = node_to_item(node, struct LogBufferElement, node);
+ logId = element->logId;
+
+ if (logMask & (1 << logId)) {
+ ret = log_msg->entry_v3.len = element->len;
+ log_msg->entry_v3.hdr_size = sizeof(log_msg->entry_v3);
+ log_msg->entry_v3.pid = getpid();
+ log_msg->entry_v3.tid = element->tid;
+ log_msg->entry_v3.sec = element->timestamp.tv_sec;
+ log_msg->entry_v3.nsec = element->timestamp.tv_nsec;
+ log_msg->entry_v3.lid = logId;
+ memcpy(log_msg->entry_v3.msg, element->msg, ret);
+ ret += log_msg->entry_v3.hdr_size;
+ break;
+ }
+ }
+
+ transp->context.node = node;
+
+ /* ToDo: if blocking, and no entry, put reader to sleep */
+ pthread_rwlock_unlock(&logbuf.listLock);
+ return ret;
+}
+
+static int writeToLocalPoll(
+ struct android_log_logger_list *logger_list,
+ struct android_log_transport_context *transp) {
+ int ret = (logger_list->mode & ANDROID_LOG_NONBLOCK) ? -ENODEV : 0;
+
+ pthread_rwlock_rdlock(&logbuf.listLock);
+
+ if (logbuf.serviceName) {
+ unsigned logMask = transp->logMask;
+ struct listnode *node = writeToLocalNode(logger_list, transp);
+
+ ret = (node != list_head(&logbuf.head));
+ if (ret) {
+ do {
+ ret = !!(logMask & (1 << (node_to_item(node->prev,
+ struct LogBufferElement,
+ node))->logId));
+ } while (!ret && ((node = node->prev) != list_head(&logbuf.head)));
+ }
+
+ transp->context.node = node;
+ }
+
+ pthread_rwlock_unlock(&logbuf.listLock);
+
+ return ret;
+}
+
+static void writeToLocalClose(
+ struct android_log_logger_list *logger_list __unused,
+ struct android_log_transport_context *transp) {
+ pthread_rwlock_wrlock(&logbuf.listLock);
+ transp->context.node = list_head(&logbuf.head);
+ pthread_rwlock_unlock(&logbuf.listLock);
+}
+
+static int writeToLocalClear(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *unused __unused) {
+ log_id_t logId = logger->logId;
+ struct listnode *node, *n;
+
+ if ((logId >= NUMBER_OF_LOG_BUFFERS) || BLOCK_LOG_BUFFERS(logId)) {
+ return -EINVAL;
+ }
+
+ pthread_rwlock_wrlock(&logbuf.listLock);
+ logbuf.number[logId] = 0;
+ logbuf.last[logId] = &logbuf.head;
+ list_for_each_safe(node, n, &logbuf.head) {
+ struct LogBufferElement *element;
+ element = node_to_item(node, struct LogBufferElement, node);
+
+ if (logId == element->logId) {
+ struct android_log_logger_list *logger_list;
+
+ logger_list_rdlock();
+ logger_list_for_each(logger_list) {
+ struct android_log_transport_context *transp;
+
+ transport_context_for_each(transp, logger_list) {
+ if ((transp->transport == &localLoggerRead) &&
+ (transp->context.node == node)) {
+ transp->context.node = node->next;
+ }
+ }
+ }
+ logger_list_unlock();
+ list_remove(node);
+ free(element);
+ }
+ }
+
+ pthread_rwlock_unlock(&logbuf.listLock);
+
+ return 0;
+}
+
+static ssize_t writeToLocalGetSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused) {
+ ssize_t ret = -EINVAL;
+ log_id_t logId = logger->logId;
+
+ if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
+ pthread_rwlock_rdlock(&logbuf.listLock);
+ ret = logbuf.maxSize[logId];
+ pthread_rwlock_unlock(&logbuf.listLock);
+ }
+
+ return ret;
+}
+
+static ssize_t writeToLocalSetSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused,
+ size_t size) {
+ ssize_t ret = -EINVAL;
+
+ if ((size > LOGGER_ENTRY_MAX_LEN) || (size < (4 * 1024 * 1024))) {
+ log_id_t logId = logger->logId;
+ if ((logId < NUMBER_OF_LOG_BUFFERS) || !BLOCK_LOG_BUFFERS(logId)) {
+ pthread_rwlock_wrlock(&logbuf.listLock);
+ ret = logbuf.maxSize[logId] = size;
+ pthread_rwlock_unlock(&logbuf.listLock);
+ }
+ }
+
+ return ret;
+}
+
+static ssize_t writeToLocalGetReadbleSize(
+ struct android_log_logger *logger,
+ struct android_log_transport_context *transp __unused) {
+ ssize_t ret = -EINVAL;
+ log_id_t logId = logger->logId;
+
+ if ((logId < NUMBER_OF_LOG_BUFFERS) && !BLOCK_LOG_BUFFERS(logId)) {
+ pthread_rwlock_rdlock(&logbuf.listLock);
+ ret = logbuf.serviceName ? (ssize_t)logbuf.size[logId] : -EBADF;
+ pthread_rwlock_unlock(&logbuf.listLock);
+ }
+
+ return ret;
+}
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 99d7fea..9411f36 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -37,6 +37,7 @@
#include "config_read.h"
#include "log_portability.h"
+#include "logd_reader.h"
#include "logger.h"
/* branchless on many architectures. */
@@ -90,7 +91,7 @@
static int logdAvailable(log_id_t logId)
{
- if (logId > LOG_ID_KERNEL) {
+ if (logId >= LOG_ID_MAX || logId == LOG_ID_KERNEL) {
return -EINVAL;
}
if (logId == LOG_ID_SECURITY) {
@@ -324,6 +325,11 @@
return ret;
}
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size)
+{
+ return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
static int check_log_success(char *buf, ssize_t ret)
{
if (ret < 0) {
diff --git a/bootstat/histogram_logger.h b/liblog/logd_reader.h
similarity index 70%
copy from bootstat/histogram_logger.h
copy to liblog/logd_reader.h
index 60c7776..04c2cf2 100644
--- a/bootstat/histogram_logger.h
+++ b/liblog/logd_reader.h
@@ -14,13 +14,17 @@
* limitations under the License.
*/
-#include <cstdint>
-#include <string>
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
-namespace bootstat {
+#include <unistd.h>
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogHistogram(const std::string& event, int32_t data);
+#include "log_portability.h"
-} // namespace bootstat
\ No newline at end of file
+__BEGIN_DECLS
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size);
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/logger.h b/liblog/logger.h
index 50d1cb4..d94cd14 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -39,14 +39,16 @@
struct android_log_transport_write {
struct listnode node;
- const char *name;
- unsigned logMask; /* cache of available success */
+ const char *name; /* human name to describe the transport */
+ unsigned logMask; /* mask cache of available() success */
union android_log_context context; /* Initialized by static allocation */
- int (*available)(log_id_t logId);
- int (*open)();
- void (*close)();
- int (*write)(log_id_t logId, struct timespec *ts, struct iovec *vec, size_t nr);
+ int (*available)(log_id_t logId); /* Does not cause resources to be taken */
+ int (*open)(); /* can be called multiple times, reusing current resources */
+ void (*close)(); /* free up resources */
+ /* write log to transport, returns number of bytes propagated, or -errno */
+ int (*write)(log_id_t logId, struct timespec *ts,
+ struct iovec *vec, size_t nr);
};
struct android_log_logger_list;
@@ -55,22 +57,23 @@
struct android_log_transport_read {
struct listnode node;
- const char *name;
+ const char *name; /* human name to describe the transport */
+ /* Does not cause resources to be taken */
int (*available)(log_id_t logId);
int (*version)(struct android_log_logger *logger,
struct android_log_transport_context *transp);
+ /* Release resources taken by the following interfaces */
void (*close)(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp);
-
/*
- * Expect all to instantiate open on any call, so we do not have
- * an expicit open call
+ * Expect all to instantiate open automagically on any call,
+ * so we do not have an explicit open call.
*/
int (*read)(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp,
struct log_msg *log_msg);
- /* Assumption is only called if not ANDROID_LOG_NONBLOCK */
+ /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
int (*poll)(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp);
@@ -96,6 +99,7 @@
};
struct android_log_logger_list {
+ struct listnode node;
struct listnode logger;
struct listnode transport;
int mode;
@@ -117,9 +121,9 @@
struct android_log_logger_list *parent;
struct android_log_transport_read *transport;
- unsigned logMask;
- int ret;
- struct log_msg logMsg; /* valid is logMsg.len != 0 */
+ unsigned logMask; /* mask of requested log buffers */
+ int ret; /* return value associated with following data */
+ struct log_msg logMsg; /* peek at upcoming data, valid if logMsg.len != 0 */
};
/* assumes caller has structures read-locked, single threaded, or fenced */
@@ -143,6 +147,41 @@
(logp) = node_to_item((logp)->node.next, \
struct android_log_logger, node))
+/*
+ * Global list of log readers.
+ *
+ * Usage case: search out transport contexts for all readers
+ */
+
+LIBLOG_HIDDEN struct listnode __android_log_readers;
+
+#if defined(_WIN32)
+#define logger_list_rdlock()
+#define logger_list_wrlock()
+#define logger_list_unlock()
+#else
+LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock;
+
+#define logger_list_rdlock() pthread_rwlock_rdlock(&__android_log_readers_lock)
+#define logger_list_wrlock() pthread_rwlock_wrlock(&__android_log_readers_lock)
+#define logger_list_unlock() pthread_rwlock_unlock(&__android_log_readers_lock)
+#endif
+
+/* Must be called with logger_list_rdlock() or logger_list_wrlock() held */
+#define logger_list_for_each(logger_list) \
+ for ((logger_list) = node_to_item(&__android_log_readers, \
+ struct android_log_logger_list, \
+ node); \
+ (logger_list) != node_to_item(&__android_log_readers, \
+ struct android_log_logger_list, \
+ node) && \
+ (logger_list) != node_to_item((logger_list)->node.next, \
+ struct android_log_logger_list, \
+ node); \
+ (logger_list) = node_to_item((logger_list)->node.next, \
+ struct android_log_logger_list, \
+ node))
+
/* OS specific dribs and drabs */
#if defined(_WIN32)
@@ -157,6 +196,8 @@
LIBLOG_HIDDEN int __android_log_trylock();
LIBLOG_HIDDEN void __android_log_unlock();
+LIBLOG_HIDDEN int __android_log_frontend;
+
__END_DECLS
#endif /* _LIBLOG_LOGGER_H__ */
diff --git a/liblog/logger_read.c b/liblog/logger_read.c
index c3cb7ad..7e50a23 100644
--- a/liblog/logger_read.c
+++ b/liblog/logger_read.c
@@ -228,6 +228,13 @@
LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
}
+LIBLOG_HIDDEN struct listnode __android_log_readers =
+ { &__android_log_readers, &__android_log_readers };
+#if !defined(_WIN32)
+LIBLOG_HIDDEN pthread_rwlock_t __android_log_readers_lock =
+ PTHREAD_RWLOCK_INITIALIZER;
+#endif
+
LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
int mode,
unsigned int tail,
@@ -246,6 +253,10 @@
logger_list->tail = tail;
logger_list->pid = pid;
+ logger_list_wrlock();
+ list_add_tail(&__android_log_readers, &logger_list->node);
+ logger_list_unlock();
+
return (struct logger_list *)logger_list;
}
@@ -267,6 +278,10 @@
logger_list->start = start;
logger_list->pid = pid;
+ logger_list_wrlock();
+ list_add_tail(&__android_log_readers, &logger_list->node);
+ logger_list_unlock();
+
return (struct logger_list *)logger_list;
}
@@ -502,6 +517,10 @@
return;
}
+ logger_list_wrlock();
+ list_remove(&logger_list_internal->node);
+ logger_list_unlock();
+
while (!list_empty(&logger_list_internal->transport)) {
struct listnode *node = list_head(&logger_list_internal->transport);
struct android_log_transport_context *transp =
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 1a2d506..e149e68 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -25,9 +25,11 @@
#endif
#include <log/event_tag_map.h>
+#include <log/log_frontend.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "config_read.h" /* __android_log_config_read_close() definition */
#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
@@ -170,6 +172,8 @@
}
}
+ __android_log_config_write_close();
+
#if defined(__ANDROID__)
/*
* Additional risk here somewhat mitigated by immediately unlock flushing
@@ -343,7 +347,7 @@
}
}
/* tag must be nul terminated */
- if (strnlen(tag, len) >= len) {
+ if (tag && strnlen(tag, len) >= len) {
tag = NULL;
}
@@ -514,6 +518,14 @@
strcpy(buf, "Unspecified assertion failed");
}
+ // Log assertion failures to stderr for the benefit of "adb shell" users
+ // and gtests (http://b/23675822).
+ struct iovec iov[2] = {
+ { buf, strlen(buf) },
+ { (char*) "\n", 1 },
+ };
+ TEMP_FAILURE_RETRY(writev(2, iov, 2));
+
__android_log_write(ANDROID_LOG_FATAL, tag, buf);
abort(); /* abort so we have a chance to debug the situation */
/* NOTREACHED */
@@ -610,3 +622,87 @@
return write_to_log(LOG_ID_SECURITY, vec, 4);
}
+
+static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr)
+{
+ size_t len, i;
+
+ if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
+ return -EINVAL;
+ }
+
+ for (len = i = 0; i < nr; ++i) {
+ len += vec[i].iov_len;
+ }
+ if (!len) {
+ return -EINVAL;
+ }
+ return len;
+}
+
+/* Following functions need access to our internal write_to_log status */
+
+LIBLOG_HIDDEN int __android_log_frontend;
+
+LIBLOG_ABI_PUBLIC int android_set_log_frontend(int frontend_flag)
+{
+ int retval;
+
+ if (frontend_flag < 0) {
+ return -EINVAL;
+ }
+
+ retval = LOGGER_NULL;
+
+ __android_log_lock();
+
+ if (frontend_flag & LOGGER_NULL) {
+ write_to_log = __write_to_log_null;
+
+ __android_log_unlock();
+
+ return retval;
+ }
+
+ __android_log_frontend &= LOGGER_LOCAL | LOGGER_LOGD;
+
+ frontend_flag &= LOGGER_LOCAL | LOGGER_LOGD;
+
+ if (__android_log_frontend != frontend_flag) {
+ __android_log_frontend = frontend_flag;
+ __android_log_config_write_close();
+ __android_log_config_read_close();
+
+ write_to_log = __write_to_log_init;
+ /* generically we only expect these two values for write_to_log */
+ } else if ((write_to_log != __write_to_log_init) &&
+ (write_to_log != __write_to_log_daemon)) {
+ write_to_log = __write_to_log_init;
+ }
+
+ retval = __android_log_frontend;
+
+ __android_log_unlock();
+
+ return retval;
+}
+
+LIBLOG_ABI_PUBLIC int android_get_log_frontend()
+{
+ int ret = LOGGER_DEFAULT;
+
+ __android_log_lock();
+ if (write_to_log == __write_to_log_null) {
+ ret = LOGGER_NULL;
+ } else {
+ __android_log_frontend &= LOGGER_LOCAL | LOGGER_LOGD;
+ ret = __android_log_frontend;
+ if ((write_to_log != __write_to_log_init) &&
+ (write_to_log != __write_to_log_daemon)) {
+ ret = -EINVAL;
+ }
+ }
+ __android_log_unlock();
+
+ return ret;
+}
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 18af9de..e61850d 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -16,13 +16,20 @@
*/
#define _GNU_SOURCE /* for asprintf */
+#ifndef __MINGW32__
+#define HAVE_STRSEP
+#endif
-#include <arpa/inet.h>
+//#ifndef __MINGW32__
+//#include <arpa/inet.h>
+//#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
+#ifndef __MINGW32__
#include <pwd.h>
+#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -40,6 +47,10 @@
#define MS_PER_NSEC 1000000
#define US_PER_NSEC 1000
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
typedef struct FilterInfo_t {
char *mTag;
android_LogPriority mPri;
@@ -216,7 +227,11 @@
p_ret->year_output = false;
p_ret->zone_output = false;
p_ret->epoch_output = false;
+#ifdef __ANDROID__
p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
+#else
+ p_ret->monotonic_output = false;
+#endif
p_ret->uid_output = false;
p_ret->descriptive_output = false;
descriptive_output = false;
@@ -322,6 +337,7 @@
else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
else if (strcmp(formatString, "uid") == 0) format = FORMAT_MODIFIER_UID;
else if (strcmp(formatString, "descriptive") == 0) format = FORMAT_MODIFIER_DESCRIPT;
+#ifndef __MINGW32__
else {
extern char *tzname[2];
static const char gmt[] = "GMT";
@@ -353,6 +369,7 @@
}
free(cp);
}
+#endif
return format;
}
@@ -411,7 +428,7 @@
/*
* Presently HAVE_STRNDUP is never defined, so the second case is always taken
- * Darwin doesn't have strnup, everything else does
+ * Darwin doesn't have strndup, everything else does
*/
#ifdef HAVE_STRNDUP
tagName = strndup(filterExpression, tagNameLength);
@@ -433,6 +450,27 @@
return -1;
}
+#ifndef HAVE_STRSEP
+/* KISS replacement helper for below */
+static char* strsep(char** stringp, const char* delim)
+{
+ char* token;
+ char* ret = *stringp;
+
+ if (!ret || !*ret) {
+ return NULL;
+ }
+ token = strpbrk(ret, delim);
+ if (token) {
+ *token = '\0';
+ ++token;
+ } else {
+ token = ret + strlen(ret);
+ }
+ *stringp = token;
+ return ret;
+}
+#endif
/**
* filterString: a comma/whitespace-separated set of filter expressions
@@ -444,7 +482,6 @@
* Assumes single threaded execution
*
*/
-
LIBLOG_ABI_PUBLIC int android_log_addFilterString(
AndroidLogFormat *p_format,
const char *filterString)
@@ -728,6 +765,7 @@
}
}
}
+ outCount = 0;
lval = 0;
switch (type) {
case EVENT_TYPE_INT:
@@ -953,7 +991,7 @@
LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
struct logger_entry *buf,
AndroidLogEntry *entry,
- const EventTagMap *map,
+ const EventTagMap *map __unused,
char *messageBuf, int messageBufLen)
{
size_t inCount;
@@ -995,11 +1033,12 @@
inCount -= 4;
entry->tagLen = 0;
+ entry->tag = NULL;
+#ifdef __ANDROID__
if (map != NULL) {
entry->tag = android_lookupEventTag_len(map, &entry->tagLen, tagIndex);
- } else {
- entry->tag = NULL;
}
+#endif
/*
* If we don't have a map, or didn't find the tag number in the map,
@@ -1024,9 +1063,11 @@
*/
const char* fmtStr = NULL;
size_t fmtLen = 0;
+#ifdef __ANDROID__
if (descriptive_output && map) {
fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
}
+#endif
char* outBuf = messageBuf;
size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
@@ -1250,6 +1291,7 @@
return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
}
+#ifdef __ANDROID__
static void convertMonotonic(struct timespec *result,
const AndroidLogEntry *entry)
{
@@ -1482,6 +1524,7 @@
result->tv_nsec = entry->tv_nsec;
subTimespec(result, result, &convert);
}
+#endif
/**
* Formats a log message into a buffer
@@ -1529,6 +1572,7 @@
*/
now = entry->tv_sec;
nsec = entry->tv_nsec;
+#if __ANDROID__
if (p_format->monotonic_output) {
// prevent convertMonotonic from being called if logd is monotonic
if (android_log_clockid() != CLOCK_MONOTONIC) {
@@ -1538,6 +1582,7 @@
nsec = time.tv_nsec;
}
}
+#endif
if (now < 0) {
nsec = NS_PER_SEC - nsec;
}
@@ -1591,13 +1636,18 @@
* This code is Android specific, bionic guarantees that
* calls to non-reentrant getpwuid() are thread safe.
*/
+#if !defined(__MINGW32__)
+#if (FAKE_LOG_DEVICE == 0)
#ifndef __BIONIC__
#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
#endif
+#endif
struct passwd* pwd = getpwuid(entry->uid);
if (pwd && (strlen(pwd->pw_name) <= 5)) {
snprintf(uid, sizeof(uid), "%5s:", pwd->pw_name);
- } else {
+ } else
+#endif
+ {
// Not worth parsing package list, names all longer than 5
snprintf(uid, sizeof(uid), "%5d:", entry->uid);
}
diff --git a/liblog/pmsg_writer.c b/liblog/pmsg_writer.c
index c1c068e..5e4ff98 100644
--- a/liblog/pmsg_writer.c
+++ b/liblog/pmsg_writer.c
@@ -285,6 +285,7 @@
__android_log_unlock();
} else if (pmsgOpen() < 0) {
__android_log_unlock();
+ free(cp);
return -EBADF;
}
}
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 097befc..cfea452 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -55,7 +55,8 @@
-fno-builtin \
test_src_files := \
- liblog_test.cpp \
+ liblog_test_default.cpp \
+ liblog_test_local.cpp \
log_id_test.cpp \
log_radio_test.cpp \
log_read_test.cpp \
@@ -111,6 +112,7 @@
LOCAL_CXX_STL := libc++
LOCAL_SHARED_LIBRARIES := liblog libcutils libbase
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_LDLIBS_linux := -lrt
include $(BUILD_HOST_NATIVE_TEST)
endif # ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 5420f68..dac84eb 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -15,6 +15,8 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -22,8 +24,10 @@
#include <unordered_set>
+#include <android-base/file.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <log/log_frontend.h>
#include <private/android_logger.h>
#include "benchmark.h"
@@ -77,10 +81,29 @@
}
BENCHMARK(BM_log_maximum);
+static void set_log_null() {
+ android_set_log_frontend(LOGGER_NULL);
+}
+
+static void set_log_default() {
+ android_set_log_frontend(LOGGER_DEFAULT);
+}
+
+static void BM_log_maximum_null(int iters) {
+ set_log_null();
+ BM_log_maximum(iters);
+ set_log_default();
+}
+BENCHMARK(BM_log_maximum_null);
+
/*
- * Measure the time it takes to submit the android logging call using
- * discrete acquisition under light load. Expect this to be a pair of
- * syscall periods (2us).
+ * Measure the time it takes to collect the time using
+ * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load. Expect this to be a syscall period (2us) or
+ * data read time if zero-syscall.
+ *
+ * vdso support in the kernel and the library can allow
+ * clock_gettime to be zero-syscall.
*/
static void BM_clock_overhead(int iters) {
for (int i = 0; i < iters; ++i) {
@@ -465,19 +488,94 @@
BENCHMARK(BM_pmsg_long_unaligned1);
/*
- * Measure the time it takes to submit the android logging call using
- * discrete acquisition under light load. Expect this to be a dozen or so
- * syscall periods (40us).
+ * Measure the time it takes to form sprintf plus time using
+ * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load. Expect this to be a syscall period (2us) or sprintf
+ * time if zero-syscall time.
*/
-static void BM_log_overhead(int iters) {
+/* helper function */
+static void test_print(const char *fmt, ...) {
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+}
+
+#define logd_yield() sched_yield() // allow logd to catch up
+#define logd_sleep() usleep(50) // really allow logd to catch up
+
+/* performance test */
+static void BM_sprintf_overhead(int iters) {
+ for (int i = 0; i < iters; ++i) {
+ StartBenchmarkTiming();
+ test_print("BM_sprintf_overhead:%d", i);
+ StopBenchmarkTiming();
+ logd_yield();
+ }
+}
+BENCHMARK(BM_sprintf_overhead);
+
+/*
+ * Measure the time it takes to submit the android printing logging call
+ * using discrete acquisition discrete acquisition (StartBenchmarkTiming() ->
+ * StopBenchmarkTiming()) under light load. Expect this to be a dozen or so
+ * syscall periods (40us) plus time to run *printf
+ */
+static void BM_log_print_overhead(int iters) {
for (int i = 0; i < iters; ++i) {
StartBenchmarkTiming();
__android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i);
StopBenchmarkTiming();
- usleep(1000);
+ logd_yield();
}
}
-BENCHMARK(BM_log_overhead);
+BENCHMARK(BM_log_print_overhead);
+
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under light load. Expect this to be a dozen or so syscall periods (40us)
+ */
+static void BM_log_event_overhead(int iters) {
+ for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+ StartBenchmarkTiming();
+ __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+ StopBenchmarkTiming();
+ logd_yield();
+ }
+}
+BENCHMARK(BM_log_event_overhead);
+
+static void BM_log_event_overhead_null(int iters) {
+ set_log_null();
+ BM_log_event_overhead(iters);
+ set_log_default();
+}
+BENCHMARK(BM_log_event_overhead_null);
+
+/*
+ * Measure the time it takes to submit the android event logging call
+ * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * under very-light load (<1% CPU utilization).
+ */
+static void BM_log_light_overhead(int iters) {
+ for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+ StartBenchmarkTiming();
+ __android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
+ StopBenchmarkTiming();
+ usleep(10000);
+ }
+}
+BENCHMARK(BM_log_light_overhead);
+
+static void BM_log_light_overhead_null(int iters) {
+ set_log_null();
+ BM_log_light_overhead(iters);
+ set_log_default();
+}
+BENCHMARK(BM_log_light_overhead_null);
static void caught_latency(int /*signum*/)
{
@@ -695,7 +793,7 @@
// Keep maps around for multiple iterations
static std::unordered_set<uint32_t> set;
-static const EventTagMap* map;
+static EventTagMap* map;
static bool prechargeEventMap() {
if (map) return true;
@@ -785,3 +883,142 @@
StopBenchmarkTiming();
}
BENCHMARK(BM_lookupEventFormat);
+
+/*
+ * Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(int iters) {
+
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+ StartBenchmarkTiming();
+ android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+ ANDROID_LOG_UNKNOWN);
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal __send_log_msg.
+static void send_to_control(char *buf, size_t len)
+{
+ int sock = socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock < 0) return;
+ size_t writeLen = strlen(buf) + 1;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+ if (ret <= 0) {
+ close(sock);
+ return;
+ }
+ while ((ret = read(sock, buf, len)) > 0) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {
+ .fd = sock,
+ .events = POLLIN,
+ .revents = 0
+ };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(int iters) {
+ fprintf(stderr, "WARNING: "
+ "This test can cause logd to grow in size and hit DOS limiter\n");
+ // Make copies
+ static const char empty_event_log_tags[] = "# content owned by logd\n";
+ static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+ std::string dev_event_log_tags;
+ if (android::base::ReadFileToString(dev_event_log_tags_path,
+ &dev_event_log_tags) &&
+ (dev_event_log_tags.length() == 0)) {
+ dev_event_log_tags = empty_event_log_tags;
+ }
+ static const char data_event_log_tags_path[] = "/data/misc/logd/event-log-tags";
+ std::string data_event_log_tags;
+ if (android::base::ReadFileToString(data_event_log_tags_path,
+ &data_event_log_tags) &&
+ (data_event_log_tags.length() == 0)) {
+ data_event_log_tags = empty_event_log_tags;
+ }
+
+ for (int i = 0; i < iters; ++i) {
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ }
+
+ // Restore copies (logd still know about them, until crash or reboot)
+ if (dev_event_log_tags.length() &&
+ !android::base::WriteStringToFile(dev_event_log_tags,
+ dev_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", dev_event_log_tags_path);
+ }
+ if (data_event_log_tags.length() &&
+ !android::base::WriteStringToFile(data_event_log_tags,
+ data_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", data_event_log_tags_path);
+ }
+ fprintf(stderr, "WARNING: "
+ "Restarting logd to make it forget what we just did\n");
+ system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(int iters) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"%s\"",
+ Name.c_str(), Format.c_str());
+
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8b9f0f0..bc0ea4c 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -37,9 +37,32 @@
#include <gtest/gtest.h>
#include <log/logprint.h>
#include <log/log_event_list.h>
+#include <log/log_frontend.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#ifndef TEST_PREFIX
+#ifdef __ANDROID__ // make sure we always run code if compiled for android
+#define TEST_PREFIX
+#endif
+#endif
+
+#if (!defined(USING_LOGGER_DEFAULT) || !defined(USING_LOGGER_LOCAL))
+#ifdef liblog // a binary clue that we are overriding the test names
+// Does not support log reading blocking feature yet
+// Does not support LOG_ID_SECURITY (unless we set LOGGER_LOCAL | LOGGER_LOGD)
+// Assume some common aspects are tested by USING_LOGGER_DEFAULT:
+// Does not need to _retest_ pmsg functionality
+// Does not need to _retest_ property handling as it is a higher function
+// Does not need to _retest_ event mapping functionality
+// Does not need to _retest_ ratelimit
+// Does not need to _retest_ logprint
+#define USING_LOGGER_LOCAL
+#else
+#define USING_LOGGER_DEFAULT
+#endif
+#endif
+
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are only using this in the emergency of
// a signal to stuff a terminating code into the logs, we will spin rather
@@ -56,7 +79,9 @@
_rc; })
TEST(liblog, __android_log_btwrite) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
+#endif
int intBuf = 0xDEADBEEF;
EXPECT_LT(0, __android_log_btwrite(0,
EVENT_TYPE_INT,
@@ -71,13 +96,10 @@
EVENT_TYPE_STRING,
Buf, sizeof(Buf) - 1));
usleep(1000);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
-#ifdef __ANDROID__
-std::string popenToString(std::string command) {
+#if (defined(__ANDROID__) && !defined(USING_LOGGER_LOCAL))
+static std::string popenToString(std::string command) {
std::string ret;
FILE* fp = popen(command.c_str(), "r");
@@ -141,11 +163,11 @@
return false;
}
-bool tested__android_log_close;
+static bool tested__android_log_close;
#endif
TEST(liblog, __android_log_btwrite__android_logger_list_read) {
-#ifdef __ANDROID__
+#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -153,9 +175,10 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
- // Check that we can close and reopen the logger
log_time ts(CLOCK_MONOTONIC);
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+#ifndef USING_LOGGER_LOCAL
+ // Check that we can close and reopen the logger
bool pmsgActiveAfter__android_log_btwrite;
bool logdwActiveAfter__android_log_btwrite;
if (getuid() == AID_ROOT) {
@@ -174,15 +197,18 @@
EXPECT_FALSE(pmsgActiveAfter__android_log_close);
EXPECT_FALSE(logdwActiveAfter__android_log_close);
}
+#endif
log_time ts1(CLOCK_MONOTONIC);
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+#ifndef USING_LOGGER_LOCAL
if (getuid() == AID_ROOT) {
pmsgActiveAfter__android_log_btwrite = isPmsgActive();
logdwActiveAfter__android_log_btwrite = isLogdwActive();
EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
}
+#endif
usleep(1000000);
int count = 0;
@@ -225,15 +251,92 @@
#endif
}
-#ifdef __ANDROID__
-static inline int32_t get4LE(const char* src)
-{
+// This test makes little sense standalone, and requires the tests ahead
+// and behind us, to make us whole. We could incorporate a prefix and
+// suffix test to make this standalone, but opted to not complicate this.
+TEST(liblog, android_set_log_frontend) {
+#if (defined(__ANDROID__) || defined(USING_LOGGER_LOCAL))
+#ifdef TEST_PREFIX
+ TEST_PREFIX
+#endif
+
+ int logger = android_get_log_frontend();
+ EXPECT_NE(LOGGER_NULL, logger);
+
+ EXPECT_EQ(LOGGER_NULL, android_set_log_frontend(LOGGER_NULL));
+ EXPECT_EQ(LOGGER_NULL, android_get_log_frontend());
+
+ pid_t pid = getpid();
+
+ struct logger_list *logger_list;
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(CLOCK_MONOTONIC);
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != sizeof(android_log_event_long_t))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ android_log_event_long_t* eventData;
+ eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
+
+ if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
+ continue;
+ }
+
+ log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ android_logger_list_close(logger_list);
+
+ EXPECT_EQ(logger, android_set_log_frontend(logger));
+ EXPECT_EQ(logger, android_get_log_frontend());
+
+ // False negative if liblog.__android_log_btwrite__android_logger_list_read
+ // fails above, so we will likely succeed. But we will have so many
+ // failures elsewhere that it is probably not worthwhile for us to
+ // highlight yet another disappointment.
+ EXPECT_EQ(0, count);
+ // We also expect failures in the following tests if the set does not
+ // react in an appropriate manner internally, yet passes, so we depend
+ // on this test being in the middle of a series of tests performed in
+ // the same process.
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+#ifdef TEST_PREFIX
+static inline uint32_t get4LE(const uint8_t* src) {
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}
+
+static inline uint32_t get4LE(const char* src) {
+ return get4LE(reinterpret_cast<const uint8_t*>(src));
+}
#endif
static void bswrite_test(const char *message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -241,7 +344,11 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+#ifdef __ANDROID__
log_time ts(android_log_clockid());
+#else
+ log_time ts(CLOCK_REALTIME);
+#endif
ASSERT_LT(0, __android_log_bswrite(0, message));
size_t num_lines = 1, size = 0, length = 0, total = 0;
@@ -307,8 +414,11 @@
&log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
EXPECT_EQ((length == total) ? 0 : -1, processBinaryLogBuffer);
if (processBinaryLogBuffer == 0) {
+ size_t line_overhead = 20;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
fflush(stderr);
- EXPECT_EQ((int)((20 * num_lines) + size),
+ EXPECT_EQ((int)((line_overhead * num_lines) + size),
android_log_printLogLine(logformat, fileno(stderr), &entry));
}
android_log_format_free(logformat);
@@ -345,7 +455,8 @@
}
static void buf_write_test(const char *message) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -354,7 +465,11 @@
LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
static const char tag[] = "TEST__android_log_buf_write";
+#ifdef __ANDROID__
log_time ts(android_log_clockid());
+#else
+ log_time ts(CLOCK_REALTIME);
+#endif
EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
tag, message));
@@ -402,8 +517,11 @@
&entry);
EXPECT_EQ(0, processLogBuffer);
if (processLogBuffer == 0) {
+ size_t line_overhead = 11;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
fflush(stderr);
- EXPECT_EQ((int)(((11 + sizeof(tag)) * num_lines) + size),
+ EXPECT_EQ((int)(((line_overhead + sizeof(tag)) * num_lines) + size),
android_log_printLogLine(logformat, fileno(stderr), &entry));
}
android_log_format_free(logformat);
@@ -430,7 +548,8 @@
buf_write_test("\n Hello World \n");
}
-#ifdef __ANDROID__
+#ifndef USING_LOGGER_LOCAL // requires blocking reader functionality
+#ifdef TEST_PREFIX
static unsigned signaled;
static log_time signal_time;
@@ -492,7 +611,8 @@
#endif
TEST(liblog, android_logger_list_read__cpu_signal) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
struct logger_list *logger_list;
unsigned long long v = 0xDEADBEEFA55A0000ULL;
@@ -584,7 +704,7 @@
#endif
}
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
/*
* Strictly, we are not allowed to log messages in a signal context, the
* correct way to handle this is to ensure the messages are constructed in
@@ -618,7 +738,7 @@
return NULL;
}
-int start_thread()
+static int start_thread()
{
sem_init(&thread_trigger, 0, 0);
@@ -650,7 +770,8 @@
#endif
TEST(liblog, android_logger_list_read__cpu_thread) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
struct logger_list *logger_list;
unsigned long long v = 0xDEADBEAFA55A0000ULL;
@@ -742,8 +863,9 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOGGER_LOCAL
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
#define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
sizeof(max_payload_tag) - 1)
@@ -880,7 +1002,8 @@
takes his leave.";
TEST(liblog, max_payload) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
pid_t pid = getpid();
char tag[sizeof(max_payload_tag)];
memcpy(tag, max_payload_tag, sizeof(tag));
@@ -944,7 +1067,8 @@
}
TEST(liblog, __android_log_buf_print__maxtag) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -952,7 +1076,11 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+#ifdef __ANDROID__
log_time ts(android_log_clockid());
+#else
+ log_time ts(CLOCK_REALTIME);
+#endif
EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
max_payload_buf, max_payload_buf));
@@ -1004,7 +1132,8 @@
}
TEST(liblog, too_big_payload) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
pid_t pid = getpid();
static const char big_payload_tag[] = "TEST_big_payload_XXXX";
char tag[sizeof(big_payload_tag)];
@@ -1058,6 +1187,12 @@
EXPECT_LE(LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag),
static_cast<size_t>(max_len));
+ // SLOP: Allow the underlying interface to optionally place a
+ // terminating nul at the LOGGER_ENTRY_MAX_PAYLOAD's last byte
+ // or not.
+ if (ret == (max_len + static_cast<ssize_t>(sizeof(big_payload_tag)) - 1)) {
+ --max_len;
+ }
EXPECT_EQ(ret, max_len + static_cast<ssize_t>(sizeof(big_payload_tag)));
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -1065,17 +1200,29 @@
}
TEST(liblog, dual_reader) {
-#ifdef __ANDROID__
- struct logger_list *logger_list1;
+#ifdef TEST_PREFIX
+ TEST_PREFIX
- // >25 messages due to liblog.__android_log_buf_print__concurrentXX above.
+ static const int num = 25;
+
+ for (int i = 25; i > 0; --i) {
+ static const char fmt[] = "dual_reader %02d";
+ char buffer[sizeof(fmt) + 8];
+ snprintf(buffer, sizeof(buffer), fmt, i);
+ LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_MAIN,
+ ANDROID_LOG_INFO,
+ "liblog", buffer));
+ }
+ usleep(1000000);
+
+ struct logger_list *logger_list1;
ASSERT_TRUE(NULL != (logger_list1 = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 25, 0)));
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num, 0)));
struct logger_list *logger_list2;
if (NULL == (logger_list2 = android_logger_list_open(
- LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 15, 0))) {
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, num - 10, 0))) {
android_logger_list_close(logger_list1);
ASSERT_TRUE(NULL != logger_list2);
}
@@ -1108,22 +1255,20 @@
android_logger_list_close(logger_list1);
android_logger_list_close(logger_list2);
- EXPECT_EQ(25, count1);
- EXPECT_EQ(15, count2);
+ EXPECT_EQ(num, count1);
+ EXPECT_EQ(num - 10, count2);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-#ifdef __ANDROID__
+#ifndef USING_LOGGER_LOCAL // Do not retest logprint
static bool checkPriForTag(AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) {
return android_log_shouldPrintLine(p_format, tag, pri)
&& !android_log_shouldPrintLine(p_format, tag, (android_LogPriority)(pri - 1));
}
-#endif
TEST(liblog, filterRule) {
-#ifdef __ANDROID__
static const char tag[] = "random";
AndroidLogFormat *p_format = android_log_format_new();
@@ -1185,11 +1330,10 @@
#endif
android_log_format_free(p_format);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
+#endif // !USING_LOGGER_LOCAL
+#ifndef USING_LOGGER_LOCAL // Do not retest property handling
TEST(liblog, is_loggable) {
#ifdef __ANDROID__
static const char tag[] = "is_loggable";
@@ -1488,8 +1632,13 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOGGER_LOCAL
-#ifdef __ANDROID__
+// Following tests the specific issues surrounding error handling wrt logd.
+// Kills logd and toss all collected data, equivalent to logcat -b all -c,
+// except we also return errors to the logging callers.
+#ifndef USING_LOGGER_LOCAL
+#ifdef TEST_PREFIX
// helper to liblog.enoent to count end-to-end matching logging messages.
static int count_matching_ts(log_time ts) {
usleep(1000000);
@@ -1531,10 +1680,11 @@
testing::AssertionSuccess() :
(testing::AssertionFailure() << message);
}
-#endif
+#endif // TEST_PREFIX
TEST(liblog, enoent) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
+ TEST_PREFIX
log_time ts(CLOCK_MONOTONIC);
EXPECT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
EXPECT_EQ(1, count_matching_ts(ts));
@@ -1588,9 +1738,12 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOCAL_LOGGER
// Below this point we run risks of setuid(AID_SYSTEM) which may affect others.
+// Do not retest properties, and cannot log into LOG_ID_SECURITY
+#ifndef USING_LOGGER_LOCAL
TEST(liblog, __security) {
#ifdef __ANDROID__
static const char persist_key[] = "persist.logd.security";
@@ -1776,15 +1929,19 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOGGER_LOCAL
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
int UID, const char* payload,
int DATA_LEN, int& count) {
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
+ count = 0;
+
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
@@ -1798,8 +1955,6 @@
sleep(2);
- count = 0;
-
for (;;) {
log_msg log_msg;
if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -1839,7 +1994,7 @@
ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
eventData++;
- int subtag_len = strlen(SUBTAG);
+ unsigned subtag_len = strlen(SUBTAG);
if (subtag_len > 32) subtag_len = 32;
ASSERT_EQ(subtag_len, get4LE(eventData));
eventData += 4;
@@ -1853,7 +2008,7 @@
ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
eventData++;
- ASSERT_EQ(UID, get4LE(eventData));
+ ASSERT_EQ(UID, (int)get4LE(eventData));
eventData += 4;
// Element #3: string type for data
@@ -1883,7 +2038,7 @@
#endif
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteWithInfoLog_helper(
123456781,
@@ -1899,7 +2054,7 @@
}
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteWithInfoLog_helper(
123456782,
@@ -1915,7 +2070,7 @@
}
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteWithInfoLog_helper(
123456783,
@@ -1931,7 +2086,7 @@
}
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteWithInfoLog_helper(
123456784,
@@ -1954,12 +2109,15 @@
buf_write_test(max_payload_buf);
}
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
static void android_errorWriteLog_helper(int TAG, const char *SUBTAG, int& count) {
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
+ count = 0;
+
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
@@ -1972,8 +2130,6 @@
sleep(2);
- count = 0;
-
for (;;) {
log_msg log_msg;
if (android_logger_list_read(logger_list, &log_msg) <= 0) {
@@ -2011,7 +2167,7 @@
ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
eventData++;
- ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
+ ASSERT_EQ(strlen(SUBTAG), get4LE(eventData));
eventData +=4;
if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
@@ -2025,7 +2181,7 @@
#endif
TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteLog_helper(123456785, "test-subtag", count);
EXPECT_EQ(1, count);
@@ -2035,7 +2191,7 @@
}
TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
int count;
android_errorWriteLog_helper(123456786, NULL, count);
EXPECT_EQ(0, count);
@@ -2044,7 +2200,8 @@
#endif
}
-#ifdef __ANDROID__
+// Do not retest logger list handling
+#if (defined(TEST_PREFIX) || !defined(USING_LOGGER_LOCAL))
static int is_real_element(int type) {
return ((type == EVENT_TYPE_INT) ||
(type == EVENT_TYPE_LONG) ||
@@ -2052,8 +2209,8 @@
(type == EVENT_TYPE_FLOAT));
}
-int android_log_buffer_to_string(const char *msg, size_t len,
- char *strOut, size_t strOutLen) {
+static int android_log_buffer_to_string(const char *msg, size_t len,
+ char *strOut, size_t strOutLen) {
android_log_context context = create_android_log_parser(msg, len);
android_log_list_element elem;
bool overflow = false;
@@ -2203,7 +2360,9 @@
return 0;
}
+#endif // TEST_PREFIX || !USING_LOGGER_LOCAL
+#ifdef TEST_PREFIX
static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
android_log_context ctx;
@@ -2459,6 +2618,7 @@
}
static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+ TEST_PREFIX
struct logger_list *logger_list;
pid_t pid = getpid();
@@ -2466,7 +2626,11 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+#ifdef __ANDROID__
log_time ts(android_log_clockid());
+#else
+ log_time ts(CLOCK_REALTIME);
+#endif
size_t expected_len;
const char *expected_string = (*fn)(1005, expected_len);
@@ -2507,18 +2671,23 @@
&log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
EXPECT_EQ(0, processBinaryLogBuffer);
if (processBinaryLogBuffer == 0) {
+ int line_overhead = 20;
+ if (pid > 99999) ++line_overhead;
+ if (pid > 999999) ++line_overhead;
print_barrier();
int printLogLine = android_log_printLogLine(
logformat, fileno(stderr), &entry);
print_barrier();
- EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+ EXPECT_EQ(line_overhead + (int)strlen(expected_string),
+ printLogLine);
}
android_log_format_free(logformat);
// test buffer reading API
int buffer_to_string = -1;
if (eventData) {
- snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+ snprintf(msgBuf, sizeof(msgBuf),
+ "I/[%" PRIu32 "]", get4LE(eventData));
print_barrier();
fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
memset(msgBuf, 0, sizeof(msgBuf));
@@ -2541,7 +2710,7 @@
#endif
TEST(liblog, create_android_logger_int32) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_int32);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2549,7 +2718,7 @@
}
TEST(liblog, create_android_logger_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_int64);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2557,7 +2726,7 @@
}
TEST(liblog, create_android_logger_list_int64) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_list_int64);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2565,7 +2734,7 @@
}
TEST(liblog, create_android_logger_simple_automagic_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_simple_automagic_list);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2573,7 +2742,7 @@
}
TEST(liblog, create_android_logger_list_empty) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_list_empty);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2581,7 +2750,7 @@
}
TEST(liblog, create_android_logger_complex_nested_list) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_complex_nested_list);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2589,7 +2758,7 @@
}
TEST(liblog, create_android_logger_7_level_prefix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_7_level_prefix);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2597,7 +2766,7 @@
}
TEST(liblog, create_android_logger_7_level_suffix) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_7_level_suffix);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2605,7 +2774,7 @@
}
TEST(liblog, create_android_logger_android_log_error_write) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_android_log_error_write);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
@@ -2613,15 +2782,15 @@
}
TEST(liblog, create_android_logger_android_log_error_write_null) {
-#ifdef __ANDROID__
+#ifdef TEST_PREFIX
create_android_logger(event_test_android_log_error_write_null);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#ifndef USING_LOGGER_LOCAL // Do not retest logger list handling
TEST(liblog, create_android_logger_overflow) {
-#ifdef __ANDROID__
android_log_context ctx;
EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
@@ -2646,13 +2815,9 @@
EXPECT_GT(0, android_log_write_list_begin(ctx));
EXPECT_LE(0, android_log_destroy(&ctx));
ASSERT_TRUE(NULL == ctx);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
TEST(liblog, android_log_write_list_buffer) {
-#ifdef __ANDROID__
__android_log_event_list ctx(1005);
ctx << 1005 << "tag_def" << "(tag|1),(name|3),(format|3)";
std::string buffer(ctx);
@@ -2663,11 +2828,10 @@
EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(),
msgBuf, sizeof(msgBuf)), 0);
EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
+#endif // !USING_LOGGER_LOCAL
+#ifndef USING_LOGGER_LOCAL // Do not retest pmsg functionality
#ifdef __ANDROID__
static const char __pmsg_file[] =
"/data/william-shakespeare/MuchAdoAboutNothing.txt";
@@ -2730,8 +2894,8 @@
}
#ifdef __ANDROID__
-ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
- const char *buf, size_t len, void *arg) {
+static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename,
+ const char *buf, size_t len, void *arg) {
EXPECT_TRUE(NULL == arg);
EXPECT_EQ(LOG_ID_CRASH, logId);
EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
@@ -2793,7 +2957,9 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOGGER_LOCAL
+#ifndef USING_LOGGER_LOCAL // Do not retest event mapping functionality
#ifdef __ANDROID__
// must be: '<needle:> 0 kB'
static bool isZero(const std::string &content, std::string::size_type pos,
@@ -2876,7 +3042,7 @@
EXPECT_TRUE(IsOk(private_ok, content));
EXPECT_TRUE(IsOk(anonymous_ok, content));
}
-#endif
+#endif // __ANDROID__
TEST(liblog, event_log_tags) {
#ifdef __ANDROID__
@@ -2897,7 +3063,9 @@
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif // !USING_LOGGER_LOCAL
+#ifndef USING_LOGGER_LOCAL // Do not retest ratelimit
TEST(liblog, __android_log_ratelimit) {
time_t state = 0;
@@ -2929,3 +3097,22 @@
}
// Do not test default seconds, to allow liblog to tune freely
}
+#endif // !USING_LOGGER_LOCAL
+
+#ifndef USING_LOGGER_LOCAL // Do not retest event mapping functionality
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+ EventTagMap* map = android_openEventTagMap(NULL);
+ EXPECT_TRUE(NULL != map);
+ std::string Name = android::base::StringPrintf("a%d", getpid());
+ int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)", ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+ EXPECT_NE(-1, tag);
+ EXPECT_NE(0, tag);
+ EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+#endif // !USING_LOGGER_LOCAL
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
new file mode 100644
index 0000000..079ba07
--- /dev/null
+++ b/liblog/tests/liblog_test_default.cpp
@@ -0,0 +1,5 @@
+#ifdef __ANDROID__
+#include <log/log_frontend.h>
+#define TEST_PREFIX android_set_log_frontend(LOGGER_DEFAULT);
+#endif
+#include "liblog_test.cpp"
diff --git a/liblog/tests/liblog_test_local.cpp b/liblog/tests/liblog_test_local.cpp
new file mode 100644
index 0000000..5f7f645
--- /dev/null
+++ b/liblog/tests/liblog_test_local.cpp
@@ -0,0 +1,4 @@
+#include <log/log_frontend.h>
+#define liblog liblog_local
+#define TEST_PREFIX android_set_log_frontend(LOGGER_LOCAL);
+#include "liblog_test.cpp"
diff --git a/liblog/tests/log_id_test.cpp b/liblog/tests/log_id_test.cpp
index 3241534..b8223f1 100644
--- a/liblog/tests/log_id_test.cpp
+++ b/liblog/tests/log_id_test.cpp
@@ -31,7 +31,6 @@
#endif
TEST(liblog, log_id) {
-#ifdef __ANDROID__
int count = 0;
for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
@@ -44,13 +43,9 @@
fprintf(stderr, "log buffer %s\r", name);
}
ASSERT_EQ(LOG_ID_MAX, count);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
TEST(liblog, __android_log_buf_print) {
-#ifdef __ANDROID__
EXPECT_LT(0, __android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO,
"TEST__android_log_buf_print",
"radio"));
@@ -63,13 +58,9 @@
"TEST__android_log_buf_print",
"main"));
usleep(1000);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
TEST(liblog, __android_log_buf_write) {
-#ifdef __ANDROID__
EXPECT_LT(0, __android_log_buf_write(LOG_ID_RADIO, ANDROID_LOG_INFO,
"TEST__android_log_buf_write",
"radio"));
@@ -82,26 +73,20 @@
"TEST__android_log_buf_write",
"main"));
usleep(1000);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
-#ifdef __ANDROID__
static void* ConcurrentPrintFn(void *arg) {
int ret = __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
"TEST__android_log_print", "Concurrent %" PRIuPTR,
reinterpret_cast<uintptr_t>(arg));
return reinterpret_cast<void*>(ret);
}
-#endif
#define NUM_CONCURRENT 64
#define _concurrent_name(a,n) a##__concurrent##n
#define concurrent_name(a,n) _concurrent_name(a,n)
TEST(liblog, concurrent_name(__android_log_buf_print, NUM_CONCURRENT)) {
-#ifdef __ANDROID__
pthread_t t[NUM_CONCURRENT];
int i;
for (i=0; i < NUM_CONCURRENT; i++) {
@@ -119,7 +104,4 @@
}
}
ASSERT_LT(0, ret);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
diff --git a/liblog/tests/log_radio_test.cpp b/liblog/tests/log_radio_test.cpp
index 591748a..ecba777 100644
--- a/liblog/tests/log_radio_test.cpp
+++ b/liblog/tests/log_radio_test.cpp
@@ -27,7 +27,6 @@
#include <log/log_radio.h>
TEST(liblog, RLOG) {
-#ifdef __ANDROID__
static const char content[] = "log_radio.h";
static const char content_false[] = "log_radio.h false";
@@ -84,6 +83,7 @@
usleep(100000);
RLOGE_IF(false, content_false);
+#ifdef __ANDROID__
// give time for content to long-path through logger
sleep(1);
@@ -112,6 +112,6 @@
#endif
#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
+ GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
#endif
}
diff --git a/liblog/tests/log_system_test.cpp b/liblog/tests/log_system_test.cpp
index b62832e..40e3a63 100644
--- a/liblog/tests/log_system_test.cpp
+++ b/liblog/tests/log_system_test.cpp
@@ -27,7 +27,6 @@
#include <log/log_system.h>
TEST(liblog, SLOG) {
-#ifdef __ANDROID__
static const char content[] = "log_system.h";
static const char content_false[] = "log_system.h false";
@@ -84,6 +83,7 @@
usleep(100000);
SLOGE_IF(false, content_false);
+#ifdef __ANDROID__
// give time for content to long-path through logger
sleep(1);
@@ -112,6 +112,6 @@
#endif
#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
+ GTEST_LOG_(INFO) << "This test does not test end-to-end.\n";
#endif
}
diff --git a/liblog/tests/log_time_test.cpp b/liblog/tests/log_time_test.cpp
index f2601b6..59655ba 100644
--- a/liblog/tests/log_time_test.cpp
+++ b/liblog/tests/log_time_test.cpp
@@ -21,8 +21,6 @@
#include <log/log_time.h>
TEST(liblog, log_time) {
-#ifdef __ANDROID__
-
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
log_time(CLOCK_MONOTONIC);
@@ -36,8 +34,4 @@
EXPECT_EQ(tl, ts);
EXPECT_GE(tl, ts);
EXPECT_LE(tl, ts);
-
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
}
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
new file mode 100644
index 0000000..75eab66
--- /dev/null
+++ b/libmetricslogger/Android.bp
@@ -0,0 +1,64 @@
+// Copyright 2017 The Android Open Source Project
+
+metricslogger_lib_src_files = [
+ "metrics_logger.cpp",
+]
+
+cc_defaults {
+ name: "metricslogger_defaults",
+
+ clang: true,
+ host_supported: true,
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+ shared_libs: ["liblog"],
+ whole_static_libs: ["libgtest_prod"],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+
+ // 524291 corresponds to sysui_histogram, from
+ // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+ "-DHISTOGRAM_LOG_TAG=524291",
+ ],
+}
+
+// metricslogger shared library
+// -----------------------------------------------------------------------------
+cc_library_shared {
+ name: "libmetricslogger",
+ srcs: metricslogger_lib_src_files,
+ defaults: ["metricslogger_defaults"],
+}
+
+// metricslogger shared library, debug
+// -----------------------------------------------------------------------------
+cc_library_shared {
+ name: "libmetricslogger_debug",
+ srcs: metricslogger_lib_src_files,
+ defaults: ["metricslogger_defaults"],
+
+ target: {
+ host: {
+ cflags: ["-UNDEBUG"],
+ },
+ },
+}
+
+// Native tests
+// -----------------------------------------------------------------------------
+cc_test {
+ name: "metricslogger_tests",
+ defaults: ["metricslogger_defaults"],
+ shared_libs: [
+ "libbase",
+ "libmetricslogger_debug",
+ ],
+ static_libs: ["libBionicGtestMain"],
+ srcs: [
+ "metrics_logger_test.cpp",
+ ],
+}
diff --git a/bootstat/histogram_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
similarity index 72%
rename from bootstat/histogram_logger.h
rename to libmetricslogger/include/metricslogger/metrics_logger.h
index 60c7776..d30e56c 100644
--- a/bootstat/histogram_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,10 +17,12 @@
#include <cstdint>
#include <string>
-namespace bootstat {
+namespace android {
+namespace metricslogger {
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
+// Logs a Tron histogram metric named |event| containing |data| to the Tron log
+// buffer.
void LogHistogram(const std::string& event, int32_t data);
-} // namespace bootstat
\ No newline at end of file
+} // namespace metricslogger
+} // namespace android
diff --git a/bootstat/histogram_logger.cpp b/libmetricslogger/metrics_logger.cpp
similarity index 76%
rename from bootstat/histogram_logger.cpp
rename to libmetricslogger/metrics_logger.cpp
index 73f3295..f8e0174 100644
--- a/bootstat/histogram_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,19 @@
* limitations under the License.
*/
-#include "histogram_logger.h"
+#include "metricslogger/metrics_logger.h"
#include <cstdlib>
-#include <android-base/logging.h>
#include <log/log_event_list.h>
-namespace bootstat {
+namespace android {
+namespace metricslogger {
void LogHistogram(const std::string& event, int32_t data) {
- LOG(INFO) << "Logging histogram: " << event << " " << data;
android_log_event_list log(HISTOGRAM_LOG_TAG);
log << event << data << LOG_ID_EVENTS;
}
-} // namespace bootstat
+} // namespace metricslogger
+} // namespace android
diff --git a/bootstat/histogram_logger.h b/libmetricslogger/metrics_logger_test.cpp
similarity index 70%
copy from bootstat/histogram_logger.h
copy to libmetricslogger/metrics_logger_test.cpp
index 60c7776..5a30ad7 100644
--- a/bootstat/histogram_logger.h
+++ b/libmetricslogger/metrics_logger_test.cpp
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-#include <cstdint>
-#include <string>
+#include "metricslogger/metrics_logger.h"
-namespace bootstat {
+#include <gtest/gtest.h>
-// Builds an EventLog buffer named |event| containing |data| and writes
-// the log into the Tron histogram logs.
-void LogHistogram(const std::string& event, int32_t data);
-
-} // namespace bootstat
\ No newline at end of file
+TEST(MetricsLoggerTest, AddSingleBootEvent) {
+ android::metricslogger::LogHistogram("test_event", 42);
+ // TODO(jhawkins): Verify the EventLog is updated.
+}
diff --git a/libnativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h
similarity index 64%
rename from libnativeloader/dlext_namespaces.h
rename to libnativeloader/include/nativeloader/dlext_namespaces.h
index 13a44e2..ac64f71 100644
--- a/libnativeloader/dlext_namespaces.h
+++ b/libnativeloader/include/nativeloader/dlext_namespaces.h
@@ -22,16 +22,15 @@
__BEGIN_DECLS
/*
- * Initializes public and anonymous namespaces. The public_ns_sonames is the list of sonames
- * to be included into public namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
- * The libraries in this list should be loaded prior to this call.
+ * Initializes anonymous namespaces. The shared_libs_sonames is the list of sonames
+ * to be shared by default namespace separated by colon. Example: "libc.so:libm.so:libdl.so".
*
- * The anon_ns_library_path is the search path for anonymous namespace. The anonymous namespace
+ * The library_search_path is the search path for anonymous namespace. The anonymous namespace
* is used in the case when linker cannot identify the caller of dlopen/dlsym. This happens
* for the code not loaded by dynamic linker; for example calls from the mono-compiled code.
*/
-extern bool android_init_namespaces(const char* public_ns_sonames,
- const char* anon_ns_library_path);
+extern bool android_init_anonymous_namespace(const char* shared_libs_sonames,
+ const char* library_search_path);
enum {
@@ -86,6 +85,39 @@
const char* permitted_when_isolated_path,
android_namespace_t* parent);
+/*
+ * Creates a link between namespaces. Every link has list of sonames of
+ * shared libraries. These are the libraries which are accessible from
+ * namespace 'from' but loaded within namespace 'to' context.
+ * When to namespace is nullptr this function establishes a link between
+ * 'from' namespace and the default namespace.
+ *
+ * The lookup order of the libraries in namespaces with links is following:
+ * 1. Look inside current namespace using 'this' namespace search path.
+ * 2. Look in linked namespaces
+ * 2.1. Perform soname check - if library soname is not in the list of shared
+ * libraries sonames skip this link, otherwise
+ * 2.2. Search library using linked namespace search path. Note that this
+ * step will not go deeper into linked namespaces for this library but
+ * will do so for DT_NEEDED libraries.
+ */
+extern bool android_link_namespaces(android_namespace_t* from,
+ android_namespace_t* to,
+ const char* shared_libs_sonames);
+
+/*
+ * Get the default library search path.
+ * The path will be copied into buffer, which must have space for at least
+ * buffer_size chars. Elements are separated with ':', and the path will always
+ * be null-terminated.
+ *
+ * If buffer_size is too small to hold the entire default search path and the
+ * null terminator, this function will abort. There is currently no way to find
+ * out what the required buffer size is. At the time of this writing, PATH_MAX
+ * is sufficient and used by all callers of this function.
+ */
+extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size);
+
__END_DECLS
#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 94c46fc..74f2f1d 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -20,7 +20,7 @@
#include <dlfcn.h>
#ifdef __ANDROID__
#define LOG_TAG "libnativeloader"
-#include "dlext_namespaces.h"
+#include "nativeloader/dlext_namespaces.h"
#include "cutils/properties.h"
#include "log/log.h"
#endif
@@ -165,6 +165,11 @@
return false;
}
+ if (!android_link_namespaces(ns, nullptr, public_libraries_.c_str())) {
+ *error_msg = dlerror();
+ return false;
+ }
+
native_loader_ns = NativeLoaderNamespace(ns);
} else {
native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace",
@@ -246,7 +251,9 @@
// For now we rely on CTS test to catch things like this but
// it should probably be addressed in the future.
for (const auto& soname : sonames) {
- dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
+ "Error preloading public library %s: %s",
+ soname.c_str(), dlerror());
}
public_libraries_ = base::Join(sonames, ':');
@@ -308,8 +315,8 @@
// code is one example) unknown to linker in which case linker uses anonymous
// namespace. The second argument specifies the search path for the anonymous
// namespace which is the library_path of the classloader.
- initialized_ = android_init_namespaces(public_libraries_.c_str(),
- is_native_bridge ? nullptr : library_path);
+ initialized_ = android_init_anonymous_namespace(public_libraries_.c_str(),
+ is_native_bridge ? nullptr : library_path);
if (!initialized_) {
*error_msg = dlerror();
return false;
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index fef801a..79bc888 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -561,10 +561,12 @@
static const char*
has_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)
{
- if ((end-str) >= (ptrdiff_t)prefixlen && !memcmp(str, prefix, prefixlen))
+ if ((end - str) >= (ptrdiff_t)prefixlen &&
+ (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {
return str + prefixlen;
- else
+ } else {
return NULL;
+ }
}
/* Same as strlen(x) for constant string literals ONLY */
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 671200f..e074a92 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -74,12 +74,9 @@
EXPECT_EQ(other[3], 5);
}
-// TODO: gtest isn't capable of parsing Abort messages formatted by
-// Android (fails differently on host and target), so we always need to
-// use an empty error message for death tests.
TEST_F(VectorTest, SetCapacity_Overflow) {
Vector<int> vector;
- EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "");
+ EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "Assertion failed");
}
TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
@@ -95,20 +92,13 @@
ASSERT_EQ(8U, vector.capacity());
}
-// NOTE: All of the tests below are useless because of the "TODO" above.
-// We have no way of knowing *why* the process crashed. Given that we're
-// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need
-// the ability to make assertions on the abort message to make sure we're
-// failing for the right reasons.
TEST_F(VectorTest, _grow_OverflowSize) {
Vector<int> vector;
vector.add(1);
// Checks that the size calculation (not the capacity calculation) doesn't
// overflow : the size here will be (1 + SIZE_MAX).
- //
- // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow");
- EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size overflow");
}
TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
@@ -116,18 +106,14 @@
// This should fail because the calculated capacity will overflow even though
// the size of the vector doesn't.
- //
- // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow");
- EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity overflow");
}
TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
Vector<int> vector;
// This should fail because the capacity * sizeof(int) overflows, even
// though the capacity itself doesn't.
- //
- // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
- EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "");
+ EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
}
TEST_F(VectorTest, editArray_Shared) {
diff --git a/logd/Android.mk b/logd/Android.mk
index 2da9782..9211037 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -21,6 +21,7 @@
libaudit.c \
LogAudit.cpp \
LogKlog.cpp \
+ LogTags.cpp \
event.logtags
LOCAL_SHARED_LIBRARIES := \
@@ -38,12 +39,23 @@
# $(LOCAL_PATH)/$2/event.logtags)
# event_flag := $(call event_logtags,auditd)
# event_flag += $(call event_logtags,logd)
+# event_flag += $(call event_logtags,tag_def)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
event_flag += -DLIBLOG_LOG_TAG=1006
LOCAL_CFLAGS := -Werror $(event_flag)
include $(BUILD_EXECUTABLE)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logtagd.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 52c6742..74e0ea5 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -15,6 +15,7 @@
*/
#include <arpa/inet.h>
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -47,6 +48,7 @@
registerCmd(new GetStatisticsCmd(buf));
registerCmd(new SetPruneListCmd(buf));
registerCmd(new GetPruneListCmd(buf));
+ registerCmd(new GetEventTagCmd(buf));
registerCmd(new ReinitCmd());
registerCmd(new ExitCmd(this));
}
@@ -284,6 +286,41 @@
return 0;
}
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer *buf) :
+ LogCommand("getEventTag"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient *cli,
+ int argc, char ** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ const char *name = NULL;
+ const char *format = NULL;
+ for (int i = 1; i < argc; ++i) {
+ static const char _name[] = "name=";
+ if (!strncmp(argv[i], _name, strlen(_name))) {
+ name = argv[i] + strlen(_name);
+ continue;
+ }
+
+ static const char _format[] = "format=";
+ if (!strncmp(argv[i], _format, strlen(_format))) {
+ format = argv[i] + strlen(_format);
+ continue;
+ }
+ }
+
+ cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
+ name, format)).c_str());
+
+ return 0;
+}
+
CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 5d50177..39de03b 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -61,6 +61,7 @@
LogBufferCmd(GetStatistics);
LogBufferCmd(GetPruneList);
LogBufferCmd(SetPruneList);
+ LogBufferCmd(GetEventTag);
#define LogCmd(name) \
class name##Cmd : public LogCommand { \
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 820ff64..7613c1e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -199,15 +199,13 @@
if (log_id != LOG_ID_SECURITY) {
int prio = ANDROID_LOG_INFO;
const char *tag = NULL;
- size_t len = 0;
if (log_id == LOG_ID_EVENTS) {
- tag = android::tagToName(&len, elem->getTag());
+ tag = tagToName(elem->getTag());
} else {
prio = *msg;
tag = msg + 1;
- len = strlen(tag);
}
- if (!__android_log_is_loggable_len(prio, tag, len, ANDROID_LOG_VERBOSE)) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
pthread_mutex_lock(&mLogElementsLock);
stats.add(elem);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 932d55f..da63e12 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogTags.h"
#include "LogTimes.h"
#include "LogStatistics.h"
#include "LogWhiteBlackList.h"
@@ -99,6 +100,8 @@
bool monotonic;
+ LogTags tags;
+
LogBufferElement* lastLoggedElements[LOG_ID_MAX];
LogBufferElement* droppedElements[LOG_ID_MAX];
void log(LogBufferElement* elem);
@@ -133,6 +136,12 @@
int initPrune(const char *cp) { return mPrune.init(cp); }
std::string formatPrune() { return mPrune.format(); }
+ std::string formatGetEventTag(uid_t uid,
+ const char *name, const char *format) {
+ return tags.formatGetEventTag(uid, name, format);
+ }
+ const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
+
// helper must be protected directly or implicitly by lock()/unlock()
const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 273150e..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -452,12 +452,11 @@
name = android::base::StringPrintf("%7u/%u",
getKey(), uid);
}
- size_t len = 0;
- const char *nameTmp = getName(len);
+ const char *nameTmp = getName();
if (nameTmp) {
name += android::base::StringPrintf(
- "%*s%.*s", (int)std::max(14 - name.length(), (size_t)1),
- "", (int)len, nameTmp);
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+ "", nameTmp);
}
std::string size = android::base::StringPrintf("%zu",
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 7acef6d..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -413,7 +413,7 @@
const uint32_t&getKey() const { return tag; }
const pid_t&getPid() const { return pid; }
const uid_t&getUid() const { return uid; }
- const char*getName(size_t &len) const { return android::tagToName(&len, tag); }
+ const char*getName() const { return android::tagToName(tag); }
inline void add(LogBufferElement *element) {
if (uid != element->getUid()) {
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..a109592
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+ if (!comment) return AID_ROOT;
+
+ if (*comment == '#') ++comment;
+ while ((comment < endp) && (*comment != '\n') && isspace(*comment)) ++comment;
+ static const char uid_str[] = "uid=";
+ if (((comment + strlen(uid_str)) >= endp) ||
+ fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+ !isdigit(comment[strlen(uid_str)])) return AID_ROOT;
+ char* cp;
+ unsigned long Uid = strtoul(comment + 4, &cp, 10);
+ if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+ return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file. Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+ int fd;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ if (tag2total.begin() == tag2total.end()) {
+ return false;
+ }
+
+ file2watermark_const_iterator iwater = file2watermark.find(filename);
+ if (iwater == file2watermark.end()) {
+ return false;
+ }
+
+ struct stat sb;
+ if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+ return false;
+ }
+
+ // dump what we already know back into the file?
+ fd = TEMP_FAILURE_RETRY(open(filename,
+ O_WRONLY | O_TRUNC | O_CLOEXEC |
+ O_NOFOLLOW | O_BINARY));
+ if (fd >= 0) {
+ time_t now = time(NULL);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char timebuf[20];
+ size_t len = strftime(timebuf, sizeof(timebuf),
+ "%Y-%m-%d %H:%M:%S", &tm);
+ android::base::WriteStringToFd(
+ android::base::StringPrintf(
+ "# Rebuilt %.20s, content owned by logd\n", timebuf),
+ fd);
+ for (const auto& it : tag2total) {
+ android::base::WriteStringToFd(formatEntry_locked(it.first,
+ AID_ROOT),
+ fd);
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ }
+ }
+
+ if (warn) {
+ android::prdebug(((fd < 0) ?
+ "%s failed to rebuild" :
+ "%s missing, damaged or truncated; rebuilt"),
+ filename);
+ }
+
+ if (fd >= 0) {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ struct stat sb;
+ if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+ }
+
+ return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name,
+ const std::string& Format,
+ const char* source, bool warn) {
+ std::string Key = Name;
+ if (Format.length()) Key += "+" + Format;
+
+ bool update = !source || !!strcmp(source, system_event_log_tags);
+ bool newOne;
+
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+
+ // unlikely except for dupes, or updates to uid list (more later)
+ if (itot != tag2total.end()) update = false;
+
+ newOne = tag2name.find(tag) == tag2name.end();
+ key2tag[Key] = tag;
+
+ if (Format.length()) {
+ if (key2tag.find(Name) == key2tag.end()) {
+ key2tag[Name] = tag;
+ }
+ tag2format[tag] = Format;
+ }
+ tag2name[tag] = Name;
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut != tag2uid.end()) {
+ if (uid == AID_ROOT) {
+ tag2uid.erase(ut);
+ update = true;
+ } else if (ut->second.find(uid) == ut->second.end()) {
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ update = true;
+ }
+ } else if (newOne && (uid != AID_ROOT)) {
+ tag2uid[tag].emplace(uid);
+ update = true;
+ }
+
+ // updatePersist -> trigger output on modified
+ // content, reset tag2total if available
+ if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+ }
+
+ if (update) {
+ WritePersistEventLogTags(tag, uid, source);
+ } else if (warn && !newOne && source) {
+ // For the files, we want to report dupes.
+ android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag,
+ Name.c_str(), Format.c_str(), source);
+ }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+ bool etc = !strcmp(filename, system_event_log_tags);
+ bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+ if (!etc) {
+ RebuildFileEventLogTags(filename, warn);
+ }
+ std::string content;
+ if (android::base::ReadFileToString(filename, &content)) {
+ char* cp = (char*) content.c_str();
+ char* endp = cp + content.length();
+
+ {
+ android::RWLock::AutoRLock writeLock(rwlock);
+
+ file2watermark[filename] = content.length();
+ }
+
+ char* lineStart = cp;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = cp;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ /* comment; just scan to end */
+ lineStart = NULL;
+ } else if (isdigit(*cp)) {
+ unsigned long Tag = strtoul(cp, &cp, 10);
+ if (warn && (Tag > emptyTag)) {
+ android::prdebug("tag too large %lu", Tag);
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ if (cp >= endp) break;
+ if (*cp == '\n') continue;
+ const char* name = cp;
+ /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+ bool hasAlpha = false;
+ while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+ if (!isdigit(*cp)) hasAlpha = true;
+ ++cp;
+ }
+ std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+ static const size_t maximum_official_tag_name_size = 24;
+ if (warn && (Name.length() > maximum_official_tag_name_size)) {
+ android::prdebug("tag name too long %s", Name.c_str());
+ }
+#endif
+ if (hasAlpha && ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+ if (Tag > emptyTag) {
+ if (*cp != '\n') lineStart = NULL;
+ continue;
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ const char* format = cp;
+ uid_t uid = AID_ROOT;
+ while ((cp < endp) && (*cp != '\n')) {
+ if (*cp == '#') {
+ uid = sniffUid(cp, endp);
+ lineStart = NULL;
+ break;
+ }
+ ++cp;
+ }
+ while ((cp > format) && isspace(cp[-1])) {
+ --cp;
+ lineStart = NULL;
+ }
+ std::string Format(format, cp - format);
+
+ AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+ filename, warn);
+ } else {
+ if (warn) {
+ android::prdebug("tag name invalid %.*s",
+ (int)(cp - name + 1), name);
+ }
+ lineStart = NULL;
+ }
+ } else if (!isspace(*cp)) break;
+ }
+ cp++;
+ }
+ } else if (warn) {
+ android::prdebug("Cannot read %s", filename);
+ }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg)
+{
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags. Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+ struct logger_list* logger_list = android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK,
+ 0, (pid_t)0);
+ if (!logger_list) return;
+
+ struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+ struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+ if (!e && !s) {
+ android_logger_list_free(logger_list);
+ return;
+ }
+
+ for (;;) {
+ struct log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ if (ret <= 0) break;
+
+ const char* msg = log_msg.msg();
+ if (!msg) continue;
+ if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+ uint32_t Tag = get4LE(msg);
+ if (Tag != TAG_DEF_LOG_TAG) continue;
+ uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4)) ?
+ log_msg.entry.uid : AID_ROOT;
+
+ std::string Name;
+ std::string Format;
+ android_log_list_element elem;
+ {
+ android_log_event_list ctx(log_msg);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_LIST) {
+ continue;
+ }
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_INT) {
+ continue;
+ }
+ Tag = elem.data.int32;
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Name = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Format = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ }
+ if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+ AddEventLogTags(Tag, uid, Name, Format);
+ }
+ android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+ ReadFileEventLogTags(system_event_log_tags);
+ // Following will likely fail on boot, but is required if logd restarts
+ ReadFileEventLogTags(dynamic_event_log_tags, false);
+ if (__android_log_is_debuggable()) {
+ ReadFileEventLogTags(debug_event_log_tags, false);
+ }
+ ReadPersistEventLogTags();
+
+ logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+ tag2name_const_iterator it;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ it = tag2name.find(tag);
+ if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+
+ return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant. The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call. If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+ LogTags* me = logtags;
+
+ if (!me) return NULL;
+ me->WritePmsgEventLogTags(tag);
+ return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once. There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet. More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+ LogTags* me = logtags;
+
+ if (me && __android_log_is_debuggable()) {
+ me->ReadFileEventLogTags(me->debug_event_log_tags);
+ }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+ tag2format_const_iterator iform;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ iform = tag2format.find(tag);
+ if (iform == tag2format.end()) return NULL;
+
+ return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+ uint32_t ret = emptyTag;
+
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ key2tag_const_iterator ik = key2tag.find(std::string(name));
+ if (ik != key2tag.end()) ret = ik->second;
+
+ return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock. We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts. If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name,
+ const char* format,
+ bool& unique) {
+ key2tag_const_iterator ik;
+
+ bool write = format != NULL;
+ unique = write;
+
+ if (!write) {
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+ ik = key2tag.find(name);
+ if (ik == key2tag.end()) return emptyTag;
+ return ik->second;
+ }
+
+ std::string Key(name);
+ if (*format) Key += std::string("+") + format;
+
+ ik = key2tag.find(Key);
+ if (ik != key2tag.end()) {
+ unique = false;
+ return ik->second;
+ }
+
+ size_t Hash = key2tag.hash_function()(Key);
+ uint32_t Tag = Hash;
+ // This sets an upper limit on the conflics we are allowed to deal with.
+ for (unsigned i = 0; i < 256; ) {
+ tag2name_const_iterator it = tag2name.find(Tag);
+ if (it == tag2name.end()) return Tag;
+ std::string localKey(it->second);
+ tag2format_const_iterator iform = tag2format.find(Tag);
+ if ((iform == tag2format.end()) && iform->second.length()) {
+ localKey += "+" + iform->second;
+ }
+ unique = !!it->second.compare(localKey);
+ if (!unique) return Tag; // unlikely except in a race
+
+ ++i;
+ // Algorithm to convert hash to next tag
+ if (i < 32) {
+ Tag = (Hash >> i);
+ // size_t is 32 bits, or upper word zero, rotate
+ if ((sizeof(Hash) <= 4) ||
+ ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+ Tag |= Hash << (32 - i);
+ }
+ } else {
+ Tag = Hash + i - 31;
+ }
+ }
+ return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+ int fd = TEMP_FAILURE_RETRY(open(name, mode));
+ if ((fd < 0) && warning) {
+ android::prdebug("Failed open %s (%d)", name, errno);
+ }
+ return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot == tag2total.end()) return; // source is a static entry
+
+ size_t lastTotal = itot->second;
+
+ // Every 16K (half the smallest configurable pmsg buffer size) record
+ static const size_t rate_to_pmsg = 16 * 1024;
+ if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ return;
+ }
+
+ static int pmsg_fd = -1;
+ if (pmsg_fd < 0) {
+ pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ // unlikely, but deal with partners with borken pmsg
+ if (pmsg_fd < 0) return;
+ }
+
+ std::string Name = tag2name[tag];
+ tag2format_const_iterator iform = tag2format.find(tag);
+ std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+ __android_log_event_list ctx(TAG_DEF_LOG_TAG);
+ ctx << tag << Name << Format;
+ std::string buffer(ctx);
+ if (buffer.length() <= 0) return; // unlikely
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_log_header_t header = {
+ .id = LOG_ID_EVENTS,
+ .tid = (uint16_t)gettid(),
+ .realtime.tv_sec = (uint32_t)ts.tv_sec,
+ .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ };
+
+ uint32_t outTag = TAG_DEF_LOG_TAG;
+ outTag = get4LE((const char*)&outTag);
+
+ android_pmsg_log_header_t pmsgHeader = {
+ .magic = LOGGER_MAGIC,
+ .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) +
+ sizeof(outTag) + buffer.length()),
+ .uid = (uint16_t)AID_ROOT,
+ .pid = (uint16_t)getpid(),
+ };
+
+ struct iovec Vec[] = {
+ { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+ { (unsigned char*)&header, sizeof(header) },
+ { (unsigned char*)&outTag, sizeof(outTag) },
+ { (unsigned char*)const_cast<char*>(buffer.data()), buffer.length() }
+ };
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else if (uid != AID_ROOT) {
+ pmsgHeader.uid = (uint16_t)uid;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else {
+ for (auto &it : ut->second) {
+ pmsgHeader.uid = (uint16_t)it;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ }
+ }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ int fd = openFile(dynamic_event_log_tags, mode, true);
+ if (fd < 0) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(dynamic_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ static bool one = true;
+ int fd = openFile(debug_event_log_tags, mode, one);
+ one = fd >= 0;
+ if (!one) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(debug_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag,
+ uid_t uid, const char* source) {
+ // very unlikely
+ bool etc = source && !strcmp(source, system_event_log_tags);
+ if (etc) return;
+
+ bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+ bool debug = (!dynamic &&
+ source &&
+ !strcmp(source, debug_event_log_tags)) ||
+ !__android_log_is_debuggable();
+
+ WritePmsgEventLogTags(tag, uid);
+
+ size_t lastTotal = 0;
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot != tag2total.end()) lastTotal = itot->second;
+ }
+
+ if (lastTotal == 0) { // denotes first time for this one
+ if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+ WriteDynamicEventLogTags(tag, uid);
+ }
+
+ if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+ WriteDebugEventLogTags(tag, uid);
+ }
+ }
+
+ lastTotal = android::sizesTotal();
+ if (!lastTotal) ++lastTotal;
+
+ // record totals for next watermark.
+ android::RWLock::AutoWLock writeLock(rwlock);
+ tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+ std::string Name = std::string(name);
+ bool write = format != NULL;
+ bool updateUid = uid != AID_ROOT;
+ bool updateFormat = format && *format;
+ bool unique;
+ uint32_t Tag;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ Tag = nameToTag_locked(Name, format, unique);
+ if (updateUid && (Tag != emptyTag) && !unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ if ((ut != tag2uid.end()) &&
+ (ut->second.find(uid) == ut->second.end())) {
+ unique = write; // write passthrough to update uid counts
+ if (!write) Tag = emptyTag; // deny read access
+ }
+ } else {
+ unique = write && (ut != tag2uid.end());
+ }
+ }
+ }
+
+ if (Tag == emptyTag) return Tag;
+ WritePmsgEventLogTags(Tag, uid); // record references periodically
+ if (!unique) return Tag;
+
+ bool updateWrite = false;
+ bool updateTag;
+
+ // Special case of AddEventLogTags, checks per-uid counter which makes
+ // no sense there, and is also optimized somewhat to reduce write times.
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ // double check after switch from read lock to write lock for Tag
+ updateTag = tag2name.find(Tag) == tag2name.end();
+ // unlikely, either update, race inviting conflict or multiple uids
+ if (!updateTag) {
+ Tag = nameToTag_locked(Name, format, unique);
+ if (Tag == emptyTag) return Tag;
+ // is it multiple uid's setting this value
+ if (!unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ // Add it to the uid list
+ if ((ut == tag2uid.end()) ||
+ (ut->second.find(uid) != ut->second.end())) return Tag;
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ updateWrite = true;
+ } else {
+ if (ut == tag2uid.end()) return Tag;
+ // (system) adding a global one, erase the uid list
+ tag2uid.erase(ut);
+ updateWrite = true;
+ }
+ }
+ }
+
+ // Update section
+ size_t count;
+ if (updateUid) {
+ count = 0;
+ uid2count_const_iterator ci = uid2count.find(uid);
+ if (ci != uid2count.end()) {
+ count = ci->second;
+ if (count >= max_per_uid) {
+ if (!updateWrite) return emptyTag;
+ // If we are added to the per-Uid perms, leak the Tag
+ // if it already exists.
+ updateUid = false;
+ updateTag = false;
+ updateFormat = false;
+ }
+ }
+ }
+
+ // updateWrite -> trigger output on modified content, reset tag2total
+ // also sets static to dynamic entries if they are alterred,
+ // only occurs if they have a uid, and runtime adds another uid.
+ if (updateWrite) tag2total[Tag] = 0;
+
+ if (updateTag) {
+ // mark as a dynamic entry, but do not upset current total counter
+ tag2total_const_iterator itot = tag2total.find(Tag);
+ if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+ if (*format) {
+ key2tag[Name + "+" + format] = Tag;
+ if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+ } else {
+ key2tag[Name] = Tag;
+ }
+ tag2name[Tag] = Name;
+ }
+ if (updateFormat) tag2format[Tag] = format;
+
+ if (updateUid) {
+ tag2uid[Tag].emplace(uid);
+ uid2count[uid] = count + 1;
+ }
+ }
+
+ if (updateTag || updateFormat || updateWrite) {
+ WritePersistEventLogTags(Tag, uid);
+ }
+
+ return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid,
+ const char* name,
+ const char* format) {
+ if (!format || !format[0]) {
+ return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+ }
+ size_t len = (strlen(name) + 7) / 8;
+ static const char tabs[] = "\t\t\t";
+ if (len > strlen(tabs)) len = strlen(tabs);
+ std::string Uid;
+ if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+ return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n",
+ tag, name, &tabs[len], format,
+ Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate) {
+ const char* name = tag2name[tag].c_str();
+
+ const char* format = "";
+ tag2format_const_iterator iform = tag2format.find(tag);
+ if (iform != tag2format.end()) format = iform->second.c_str();
+
+ // Access permission test, do not report dynamic entries
+ // that do not belong to us.
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ return formatEntry(tag, AID_ROOT, name, format);
+ }
+ if (uid != AID_ROOT) {
+ if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+ return std::string("");
+ }
+ return formatEntry(tag, uid, name, format);
+ }
+
+ // Show all, one for each registered uid (we are group root)
+ std::string ret;
+ for (auto &it : ut->second) {
+ ret += formatEntry(tag, it, name, format);
+ }
+ return ret;
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid,
+ const char* name, const char* format) {
+ bool all = name && (name[0] == '*') && !name[1];
+ bool list = !name || all;
+ std::string ret;
+
+ if (!list) {
+ // switch to read entry only if format == "*"
+ if (format && (format[0] == '*') && !format[1]) format = NULL;
+
+ // WAI: for null format, only works for a single entry, we can have
+ // multiple entries, one for each format, so we find first entry
+ // recorded, or entry with no format associated with it.
+ // We may desire to print all that match the name, but we did not
+ // add a mapping table for that and the cost is too high.
+ uint32_t tag = nameToTag(uid, name, format);
+ if (tag == emptyTag) return std::string("-1 ESRCH");
+ if (uid == AID_ROOT) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ // first uid in list so as to manufacture an accurate reference
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if ((ut != tag2uid.end()) &&
+ (ut->second.begin() != ut->second.end())) {
+ uid = *(ut->second.begin());
+ }
+ }
+ ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+ if (!ret.length()) return std::string("-1 ESRCH");
+ return ret;
+ }
+
+ android::RWLock::AutoRLock readLock(rwlock);
+ if (all) {
+ // everything under the sun
+ for (const auto& it : tag2name) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ } else {
+ // set entries are dynamic
+ for (const auto& it : tag2total) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ }
+ return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 0000000..37a6d96
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+ // This lock protects all the unordered_map accesses below. It
+ // is a reader/writer lock so that contentions are kept to a
+ // minimum since writes are rare, even administratably when
+ // reads are extended. Resist the temptation to use the writer
+ // lock to protect anything outside the following unordered_maps
+ // as that would increase the reader contentions. Use a separate
+ // mutex to protect the other entities.
+ android::RWLock rwlock;
+
+ // key is Name + "+" + Format
+ std::unordered_map<std::string, uint32_t> key2tag;
+ typedef std::unordered_map<std::string, uint32_t>::const_iterator key2tag_const_iterator;
+
+ // Allows us to manage access permissions based on uid registrants
+ // Global entries are specifically erased.
+ typedef std::unordered_set<uid_t> uid_list;
+ std::unordered_map<uint32_t, uid_list> tag2uid;
+ typedef std::unordered_map<uint32_t, uid_list>::const_iterator tag2uid_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2name;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2name_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2format;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2format_const_iterator;
+
+ static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+ std::unordered_map<uid_t, size_t> uid2count;
+ typedef std::unordered_map<uid_t, size_t>::const_iterator uid2count_const_iterator;
+
+ // Dynamic entries are assigned
+ std::unordered_map<uint32_t, size_t> tag2total;
+ typedef std::unordered_map<uint32_t, size_t>::const_iterator tag2total_const_iterator;
+
+ // emplace unique tag
+ uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+ // find unique or associated tag
+ uint32_t nameToTag_locked(const std::string& name, const char* format, bool &unique);
+
+ // Record expected file watermarks to detect corruption.
+ std::unordered_map<std::string, size_t> file2watermark;
+ typedef std::unordered_map<std::string, size_t>::const_iterator file2watermark_const_iterator;
+
+ void ReadPersistEventLogTags();
+
+ // format helpers
+ // format a single entry, does not need object data
+ static std::string formatEntry(uint32_t tag, uid_t uid,
+ const char* name, const char* format);
+ // caller locks, database lookup, authenticate against uid
+ std::string formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate = true);
+
+ bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+ void AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name, const std::string& Format,
+ const char* source = NULL, bool warn = false);
+
+ void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+ void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+ // push tag details to persistent storage
+ void WritePersistEventLogTags(uint32_t tag,
+ uid_t uid = AID_ROOT,
+ const char* source = NULL);
+
+ static const uint32_t emptyTag = uint32_t(-1);
+
+public:
+
+ static const char system_event_log_tags[];
+ static const char dynamic_event_log_tags[];
+ // Only for userdebug and eng
+ static const char debug_event_log_tags[];
+
+ LogTags();
+
+ void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+ void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+ // reverse lookup from tag
+ const char* tagToName(uint32_t tag) const;
+ const char* tagToFormat(uint32_t tag) const;
+ // find associated tag
+ uint32_t nameToTag(const char* name) const;
+
+ // emplace tag if necessary, provide event-log-tag formated output in string
+ std::string formatGetEventTag(uid_t uid,
+ const char* name,
+ const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 70f24e4..f044b27 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -39,8 +39,9 @@
char *pidToName(pid_t pid);
char *tidToName(pid_t tid);
-// Furnished in main.cpp. Thread safe.
-const char *tagToName(size_t *len, uint32_t tag);
+// Furnished in LogTags.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+void ReReadEventLogTags();
// Furnished by LogKlog.cpp.
const char* strnstr(const char* s, size_t len, const char* needle);
diff --git a/logd/event.logtags b/logd/event.logtags
index 0d24df0..39063a9 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -35,3 +35,4 @@
1003 auditd (avc|3)
1004 chatty (dropped|3)
+1005 tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/logd.rc b/logd/logd.rc
index 54349dd..ee89b83 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -14,3 +14,10 @@
user logd
group logd
writepid /dev/cpuset/system-background/tasks
+
+on fs
+ write /dev/event-log-tags "# content owned by logd
+"
+ chown logd logd /dev/event-log-tags
+ chmod 0644 /dev/event-log-tags
+ restorecon /dev/event-log-tags
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 0000000..46aa8c1
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+ mkdir /data/misc/logd 0700 logd log
+ write /data/misc/logd/event-log-tags ""
+ chown logd log /data/misc/logd/event-log-tags
+ chmod 0600 /data/misc/logd/event-log-tags
+ restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index 5878f15..2551f2e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -244,7 +244,7 @@
// anything else, we have even lesser privileges and accept our fate. Not
// worth checking for error returns setting this thread's privileges.
(void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
- (void)setuid(AID_LOGD); // access to everything logd.
+ (void)setuid(AID_LOGD); // access to everything logd, eg /data/misc/logd
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
@@ -271,6 +271,7 @@
logBuf->init();
logBuf->initPrune(NULL);
}
+ android::ReReadEventLogTags();
}
return NULL;
@@ -304,24 +305,6 @@
sem_post(&reinit);
}
-// tagToName converts an events tag into a name
-const char *android::tagToName(size_t *len, uint32_t tag) {
- static const EventTagMap *map;
-
- if (!map) {
- sem_wait(&sem_name);
- if (!map) {
- map = android_openEventTagMap(NULL);
- }
- sem_post(&sem_name);
- if (!map) {
- if (len) len = 0;
- return NULL;
- }
- }
- return android_lookupEventTag_len(map, len, tag);
-}
-
static void readDmesg(LogAudit *al, LogKlog *kl) {
if (!al && !kl) {
return;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 2a6cdc8..adf583b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,12 +39,8 @@
#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
-/*
- * returns statistics
- */
-static void my_android_logger_get_statistics(char *buf, size_t len)
+static void send_to_control(char* buf, size_t len)
{
- snprintf(buf, len, "getStatistics 0 1 2 3 4");
int sock = socket_local_client("logd",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
@@ -52,7 +48,7 @@
if (write(sock, buf, strlen(buf) + 1) > 0) {
ssize_t ret;
while ((ret = read(sock, buf, len)) > 0) {
- if ((size_t)ret == len) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
break;
}
len -= ret;
@@ -74,6 +70,15 @@
}
}
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+ snprintf(buf, len, "getStatistics 0 1 2 3 4");
+ send_to_control(buf, len);
+}
+
static void alloc_statistics(char **buffer, size_t *length)
{
size_t len = 8192;
@@ -816,6 +821,44 @@
close(fd);
}
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 4096);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
+ EXPECT_TRUE(strstr(buffer, name) != NULL);
+ // ToDo: also look for this in /data/misc/logd/event-log-tags and
+ // /dev/event-log-tags.
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
static inline int32_t get4LE(const char* src)
{
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9ab2333..1e5fa50 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -402,6 +402,7 @@
mkdir /data/misc/boottrace 0771 system shell
mkdir /data/misc/update_engine 0700 root root
mkdir /data/misc/trace 0700 root root
+ mkdir /data/misc/reboot 0700 root root
# profile file layout
mkdir /data/misc/profiles 0771 system system
mkdir /data/misc/profiles/cur 0771 system system
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index ed11164..80bb673 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -11,7 +11,7 @@
onrestart restart media
onrestart restart netd
onrestart restart wificond
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 66e7750..36bb443 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -11,7 +11,7 @@
onrestart restart media
onrestart restart netd
onrestart restart wificond
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0633a68..3b64f6d 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -46,7 +46,7 @@
/dev/tty0 0660 root system
/dev/graphics/* 0660 root graphics
/dev/msm_hw3dm 0660 system graphics
-/dev/input/* 0660 root input
+/dev/input/* 0640 system input
/dev/eac 0660 root audio
/dev/cam 0660 root camera
/dev/pmem 0660 system graphics
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e6def6b..1fb315c 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
char idstr[80];
struct input_id id;
- fd = open(device, O_RDWR);
+ fd = open(device, O_RDONLY);
if(fd < 0) {
if(print_flags & PRINT_DEVICE_ERRORS)
fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index fb5c84b..769f0f5 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -30,26 +30,48 @@
#include "android-base/logging.h"
-static const char* BUNDLE_VERSION_FILENAME = "/bundle_version";
-// bundle_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
-// AAA.BBB is the major/minor version of the bundle format (e.g. 001.001),
+// The name of the file containing the distro version information.
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char* DISTRO_VERSION_FILENAME = "/distro_version";
+
+// distro_version is an ASCII file consisting of 17 bytes in the form: AAA.BBB|CCCCC|DDD
+// AAA.BBB is the major/minor version of the distro format (e.g. 001.001),
// CCCCC is the rules version (e.g. 2016g)
-// DDD is the android revision for this rules version to allow for bundle corrections (e.g. 001)
+// DDD is the android revision for this rules version to allow for distro corrections (e.g. 001)
// We only need the first 13 to determine if it is suitable for the device.
-static const int BUNDLE_VERSION_LENGTH = 13;
-// The major version of the bundle format supported by this code as a null-terminated char[].
-static const char REQUIRED_BUNDLE_VERSION[] = "001";
-static const size_t REQUIRED_BUNDLE_VERSION_LEN = sizeof(REQUIRED_BUNDLE_VERSION) - 1; // exclude \0
+static const int DISTRO_VERSION_LENGTH = 13;
+
+// The major version of the distro format supported by this code as a null-terminated char[].
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char SUPPORTED_DISTRO_MAJOR_VERSION[] = "001";
+
+// The length of the distro format major version excluding the \0
+static const size_t SUPPORTED_DISTRO_MAJOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MAJOR_VERSION) - 1;
+
+// The minor version of the distro format supported by this code as a null-terminated char[].
+// See also libcore.tzdata.update2.TimeZoneDistro / libcore.tzdata.update2.DistroVersion.
+static const char SUPPORTED_DISTRO_MINOR_VERSION[] = "001";
+
+// The length of the distro format minor version excluding the \0
+static const size_t SUPPORTED_DISTRO_MINOR_VERSION_LEN = sizeof(SUPPORTED_DISTRO_MINOR_VERSION) - 1;
+
+// The length of the distro format version. e.g. 001.001
+static const size_t SUPPORTED_DISTRO_VERSION_LEN =
+ SUPPORTED_DISTRO_MAJOR_VERSION_LEN + SUPPORTED_DISTRO_MINOR_VERSION_LEN + 1;
+
// The length of the IANA rules version bytes. e.g. 2016a
static const size_t RULES_VERSION_LEN = 5;
-// Bundle version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
-static const size_t BUNDLE_VERSION_RULES_IDX = 8;
+// Distro version bytes are: AAA.BBB|CCCCC - the rules version is CCCCC
+static const size_t DISTRO_VERSION_RULES_IDX = 8;
+
+// See also libcore.tzdata.update2.TimeZoneDistro.
static const char* TZDATA_FILENAME = "/tzdata";
+
// tzdata file header (as much as we need for the version):
// byte[11] tzdata_version -- e.g. "tzdata2012f"
static const int TZ_HEADER_LENGTH = 11;
-// The major version of the bundle format supported by this code as a null-terminated char[].
+
static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
@@ -57,7 +79,7 @@
static void usage() {
std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
"\n"
- "Checks whether any timezone update bundle in DATA_TZ_DIR is compatible with the\n"
+ "Checks whether any timezone update distro in DATA_TZ_DIR is compatible with the\n"
"current Android release and better than or the same as base system timezone rules in\n"
"SYSTEM_TZ_DIR. If the timezone rules in SYSTEM_TZ_DIR are a higher version than the\n"
"one in DATA_TZ_DIR the DATA_TZ_DIR is renamed and then deleted.\n";
@@ -108,8 +130,8 @@
return true;
}
-static bool checkValidBundleVersion(const char* buffer) {
- // See BUNDLE_VERSION_LENGTH comments above for a description of the format.
+static bool checkValidDistroVersion(const char* buffer) {
+ // See DISTRO_VERSION_LENGTH comments above for a description of the format.
size_t i = 0;
if (!checkDigits(buffer, 3, &i)) {
return false;
@@ -246,13 +268,13 @@
}
/*
- * Deletes the timezone update bundle directory.
+ * Deletes the timezone update distro directory.
*/
-static void deleteUpdateBundleDir(std::string& bundleDirName) {
- LOG(INFO) << "Removing: " << bundleDirName;
- bool deleted = deleteDir(bundleDirName);
+static void deleteUpdateDistroDir(std::string& distroDirName) {
+ LOG(INFO) << "Removing: " << distroDirName;
+ bool deleted = deleteDir(distroDirName);
if (!deleted) {
- LOG(WARNING) << "Deletion of bundle dir " << bundleDirName << " was not successful";
+ LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
}
}
@@ -278,60 +300,83 @@
const char* systemZoneInfoDir = argv[1];
const char* dataZoneInfoDir = argv[2];
- // Check the bundle directory exists. If it does not, exit quickly: nothing to do.
+ // Check the distro directory exists. If it does not, exit quickly: nothing to do.
std::string dataCurrentDirName(dataZoneInfoDir);
dataCurrentDirName += "/current";
int dataCurrentDirStatus = checkPath(dataCurrentDirName);
if (dataCurrentDirStatus == NONE) {
- LOG(INFO) << "timezone bundle dir " << dataCurrentDirName
+ LOG(INFO) << "timezone distro dir " << dataCurrentDirName
<< " does not exist. No action required.";
return 0;
}
- // If the bundle directory path is not a directory or we can't stat() the path, exit with a
+ // If the distro directory path is not a directory or we can't stat() the path, exit with a
// warning: either there's a problem accessing storage or the world is not as it should be;
// nothing to do.
if (dataCurrentDirStatus != IS_DIR) {
- LOG(WARNING) << "Current bundle dir " << dataCurrentDirName
+ LOG(WARNING) << "Current distro dir " << dataCurrentDirName
<< " could not be accessed or is not a directory. result=" << dataCurrentDirStatus;
return 2;
}
- // Check the installed bundle version.
- std::string bundleVersionFileName(dataCurrentDirName);
- bundleVersionFileName += BUNDLE_VERSION_FILENAME;
- std::vector<char> bundleVersion;
- bundleVersion.reserve(BUNDLE_VERSION_LENGTH);
- bool bundleVersionReadOk =
- readBytes(bundleVersionFileName, bundleVersion.data(), BUNDLE_VERSION_LENGTH);
- if (!bundleVersionReadOk) {
- LOG(WARNING) << "bundle version file " << bundleVersionFileName
- << " does not exist or is too short. Deleting bundle dir.";
+ // Check the installed distro version.
+ std::string distroVersionFileName(dataCurrentDirName);
+ distroVersionFileName += DISTRO_VERSION_FILENAME;
+ std::vector<char> distroVersion;
+ distroVersion.reserve(DISTRO_VERSION_LENGTH);
+ bool distroVersionReadOk =
+ readBytes(distroVersionFileName, distroVersion.data(), DISTRO_VERSION_LENGTH);
+ if (!distroVersionReadOk) {
+ LOG(WARNING) << "distro version file " << distroVersionFileName
+ << " does not exist or is too short. Deleting distro dir.";
// Implies the contents of the data partition is corrupt in some way. Try to clean up.
deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateBundleDir(dataCurrentDirName);
+ deleteUpdateDistroDir(dataCurrentDirName);
return 3;
}
- if (!checkValidBundleVersion(bundleVersion.data())) {
- LOG(WARNING) << "bundle version file " << bundleVersionFileName
- << " is not valid. Deleting bundle dir.";
+ if (!checkValidDistroVersion(distroVersion.data())) {
+ LOG(WARNING) << "distro version file " << distroVersionFileName
+ << " is not valid. Deleting distro dir.";
// Implies the contents of the data partition is corrupt in some way. Try to clean up.
deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateBundleDir(dataCurrentDirName);
+ deleteUpdateDistroDir(dataCurrentDirName);
return 4;
}
- // Check the first 3 bytes of the bundleVersionHeader: these are the major version (e.g. 001).
- // It must match exactly to be ok. The minor version is currently ignored.
- if (strncmp(&bundleVersion[0], REQUIRED_BUNDLE_VERSION, REQUIRED_BUNDLE_VERSION_LEN) != 0) {
- LOG(INFO) << "bundle version file " << bundleVersionFileName
- << " is not the required version " << REQUIRED_BUNDLE_VERSION
- << ". Deleting bundle dir.";
- // This shouldn't happen with 001, but it in future, this will imply there has been an OTA
- // and the installed bundle is not compatible with the new version of Android. Remove the
- // installed bundle.
+ std::string actualDistroVersion =
+ std::string(distroVersion.data(), SUPPORTED_DISTRO_VERSION_LEN);
+ // Check the first 3 bytes of the distro version: these are the major version (e.g. 001).
+ // It must match the one we support exactly to be ok.
+ if (strncmp(
+ &distroVersion[0],
+ SUPPORTED_DISTRO_MAJOR_VERSION,
+ SUPPORTED_DISTRO_MAJOR_VERSION_LEN) != 0) {
+
+ LOG(INFO) << "distro version file " << distroVersionFileName
+ << " major version is not the required version " << SUPPORTED_DISTRO_MAJOR_VERSION
+ << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
+ // This implies there has been an OTA and the installed distro is not compatible with the
+ // new version of Android. Remove the installed distro.
deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateBundleDir(dataCurrentDirName);
+ deleteUpdateDistroDir(dataCurrentDirName);
+ return 5;
+ }
+
+ // Check the last 3 bytes of the distro version: these are the minor version (e.g. 001).
+ // If the version in the distro is < the minor version required by this device it cannot be
+ // used.
+ if (strncmp(
+ &distroVersion[4],
+ SUPPORTED_DISTRO_MINOR_VERSION,
+ SUPPORTED_DISTRO_MINOR_VERSION_LEN) < 0) {
+
+ LOG(INFO) << "distro version file " << distroVersionFileName
+ << " minor version is not the required version " << SUPPORTED_DISTRO_MINOR_VERSION
+ << ", was \"" << actualDistroVersion << "\". Deleting distro dir.";
+ // This implies there has been an OTA and the installed distro is not compatible with the
+ // new version of Android. Remove the installed distro.
+ deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
+ deleteUpdateDistroDir(dataCurrentDirName);
return 5;
}
@@ -353,22 +398,22 @@
return 7;
}
- // Compare the bundle rules version against the system rules version.
+ // Compare the distro rules version against the system rules version.
if (strncmp(
&systemTzDataHeader[TZ_DATA_HEADER_PREFIX_LEN],
- &bundleVersion[BUNDLE_VERSION_RULES_IDX],
+ &distroVersion[DISTRO_VERSION_RULES_IDX],
RULES_VERSION_LEN) <= 0) {
- LOG(INFO) << "Found an installed bundle but it is valid. No action taken.";
+ LOG(INFO) << "Found an installed distro but it is valid. No action taken.";
// Implies there is an installed update, but it is good.
return 0;
}
// Implies there has been an OTA and the system version of the timezone rules is now newer
- // than the version installed in /data. Remove the installed bundle.
- LOG(INFO) << "timezone bundle in " << dataCurrentDirName << " is older than data in "
+ // than the version installed in /data. Remove the installed distro.
+ LOG(INFO) << "timezone distro in " << dataCurrentDirName << " is older than data in "
<< systemTzDataFileName << "; fixing...";
deleteConfigUpdaterMetadataDir(dataZoneInfoDir);
- deleteUpdateBundleDir(dataCurrentDirName);
+ deleteUpdateDistroDir(dataCurrentDirName);
return 0;
}