Merge changes If9782396,If033c815
* changes:
DO NOT MERGE ANYWHERE Qemu: make the qemu_pipe_open compatible with old apis
DO NOT MERGE ANYWHERE Emulator: Enhance qemu_pipe.h to handle partial read and write
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7afa616..7058acb 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -19,16 +19,15 @@
#include "adb_utils.h"
#include "adb_unique_fd.h"
-#include <libgen.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
-#include <mutex>
#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
@@ -100,52 +99,6 @@
return result;
}
-std::string adb_basename(const std::string& path) {
- static std::mutex& basename_lock = *new std::mutex();
-
- // Copy path because basename may modify the string passed in.
- std::string result(path);
-
- // Use lock because basename() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to basename in the process also grab this same lock.
- std::lock_guard<std::mutex> lock(basename_lock);
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* name = basename(&result[0]);
-
- // In case dirname returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(name);
-
- return result;
-}
-
-std::string adb_dirname(const std::string& path) {
- static std::mutex& dirname_lock = *new std::mutex();
-
- // Copy path because dirname may modify the string passed in.
- std::string result(path);
-
- // Use lock because dirname() may write to a process global and return a
- // pointer to that. Note that this locking strategy only works if all other
- // callers to dirname in the process also grab this same lock.
- std::lock_guard<std::mutex> lock(dirname_lock);
-
- // Note that if std::string uses copy-on-write strings, &str[0] will cause
- // the copy to be made, so there is no chance of us accidentally writing to
- // the storage for 'path'.
- char* parent = dirname(&result[0]);
-
- // In case dirname returned a pointer to a process global, copy that string
- // before leaving the lock.
- result.assign(parent);
-
- return result;
-}
-
// Given a relative or absolute filepath, create the directory hierarchy
// as needed. Returns true if the hierarchy is/was setup.
bool mkdirs(const std::string& path) {
@@ -171,7 +124,7 @@
return true;
}
- const std::string parent(adb_dirname(path));
+ const std::string parent(android::base::Dirname(path));
// If dirname returned the same path as what we passed in, don't go recursive.
// This can happen on Windows when walking up the directory hierarchy and not
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 2b59034..e0ad103 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -28,11 +28,6 @@
bool getcwd(std::string* cwd);
bool directory_exists(const std::string& path);
-// Like the regular basename and dirname, but thread-safe on all
-// platforms and capable of correctly handling exotic Windows paths.
-std::string adb_basename(const std::string& path);
-std::string adb_dirname(const std::string& path);
-
// Return the user's home directory.
std::string adb_get_homedir_path();
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4cac485..a3bc445 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -109,18 +109,6 @@
ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
}
-TEST(adb_utils, adb_basename) {
- EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
- EXPECT_EQ("sh", adb_basename("sh"));
- EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
-}
-
-TEST(adb_utils, adb_dirname) {
- EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
- EXPECT_EQ(".", adb_dirname("sh"));
- EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
-}
-
void test_mkdirs(const std::string& basepath) {
// Test creating a directory hierarchy.
ASSERT_TRUE(mkdirs(basepath));
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index b7e76a6..a5c312b 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -21,6 +21,7 @@
#include <string>
#include <vector>
+#include <android-base/file.h>
#include <android-base/strings.h>
#include "sysdeps.h"
@@ -113,14 +114,14 @@
private:
void SetLineMessage(const std::string& action) {
- line_message_ = action + " " + adb_basename(dest_file_);
+ line_message_ = action + " " + android::base::Basename(dest_file_);
}
void SetSrcFile(const std::string path) {
src_file_ = path;
if (!dest_dir_.empty()) {
// Only uses device-provided name when user passed a directory.
- dest_file_ = adb_basename(path);
+ dest_file_ = android::base::Basename(path);
SetLineMessage("generating");
}
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 5a2206f..3de7be6 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -2115,7 +2115,8 @@
std::string cmd = android::base::StringPrintf(
"%s install-write -S %" PRIu64 " %d %d_%s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+ install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
+ android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
@@ -2233,7 +2234,7 @@
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(
- where, adb_basename(argv[last_apk]).c_str());
+ where, android::base::Basename(argv[last_apk]).c_str());
if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
result = pm_command(transport, serial, argc, argv);
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 271943d..22bd2f2 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -844,7 +844,8 @@
// TODO(b/25566053): Make pushing empty directories work.
// TODO(b/25457350): We don't preserve permissions on directories.
sc.Warning("skipping empty directory '%s'", lpath.c_str());
- copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+ android::base::Basename(lpath), S_IFDIR);
ci.skip = true;
file_list->push_back(ci);
return true;
@@ -977,7 +978,7 @@
if (dst_dir.back() != '/') {
dst_dir.push_back('/');
}
- dst_dir.append(adb_basename(src_path));
+ dst_dir.append(android::base::Basename(src_path));
}
success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(), false, false);
@@ -995,7 +996,7 @@
if (path_holder.back() != '/') {
path_holder.push_back('/');
}
- path_holder += adb_basename(src_path);
+ path_holder += android::base::Basename(src_path);
dst_path = path_holder.c_str();
}
@@ -1015,7 +1016,8 @@
std::vector<copyinfo> linklist;
// Add an entry for the current directory to ensure it gets created before pulling its contents.
- copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
+ copyinfo ci(android::base::Dirname(lpath), android::base::Dirname(rpath),
+ android::base::Basename(lpath), S_IFDIR);
file_list->push_back(ci);
// Put the files/dirs in rpath on the lists.
@@ -1149,7 +1151,7 @@
if (srcs.size() == 1 && errno == ENOENT) {
// However, its parent must exist.
struct stat parent_st;
- if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+ if (stat(android::base::Dirname(dst).c_str(), &parent_st) == -1) {
sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
return false;
}
@@ -1204,7 +1206,7 @@
if (!adb_is_separator(dst_dir.back())) {
dst_dir.push_back(OS_PATH_SEPARATOR);
}
- dst_dir.append(adb_basename(src_path));
+ dst_dir.append(android::base::Basename(src_path));
}
success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
@@ -1220,7 +1222,7 @@
// really want to copy to local_dir + OS_PATH_SEPARATOR +
// basename(remote).
path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
- adb_basename(src_path).c_str());
+ android::base::Basename(src_path).c_str());
dst_path = path_holder.c_str();
}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index e667bf8..2acf661 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -31,6 +31,7 @@
#include <unistd.h>
#include <utime.h>
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
@@ -206,7 +207,7 @@
int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
if (fd < 0 && errno == ENOENT) {
- if (!secure_mkdirs(adb_dirname(path))) {
+ if (!secure_mkdirs(android::base::Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
goto fail;
}
@@ -333,7 +334,7 @@
ret = symlink(&buffer[0], path.c_str());
if (ret && errno == ENOENT) {
- if (!secure_mkdirs(adb_dirname(path))) {
+ if (!secure_mkdirs(android::base::Dirname(path))) {
SendSyncFailErrno(s, "secure_mkdirs failed");
return false;
}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index e16cf12..516b4f2 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -102,7 +102,7 @@
#if defined(_WIN32) || !ADB_HOST
return false;
#else
- static bool enable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "1") == 0;
- return enable;
+ static bool disable = getenv("ADB_LIBUSB") && strcmp(getenv("ADB_LIBUSB"), "0") == 0;
+ return !disable;
#endif
}
diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp
index a97862a..63c0f75 100644
--- a/adf/libadfhwc/adfhwc.cpp
+++ b/adf/libadfhwc/adfhwc.cpp
@@ -166,6 +166,71 @@
return 0;
}
+static int32_t adf_display_attribute_hwc2(const adf_interface_data &data,
+ const drm_mode_modeinfo &mode, const uint32_t attribute)
+{
+ switch (attribute) {
+ case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+ if (mode.vrefresh)
+ return 1000000000 / mode.vrefresh;
+ return 0;
+
+ case HWC2_ATTRIBUTE_WIDTH:
+ return mode.hdisplay;
+
+ case HWC2_ATTRIBUTE_HEIGHT:
+ return mode.vdisplay;
+
+ case HWC2_ATTRIBUTE_DPI_X:
+ return dpi(mode.hdisplay, data.width_mm);
+
+ case HWC2_ATTRIBUTE_DPI_Y:
+ return dpi(mode.vdisplay, data.height_mm);
+
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ adf_interface_data data;
+ int err = adf_get_interface_data(dev->intf_fds[disp], &data);
+ if (err < 0) {
+ ALOGE("failed to get ADF interface data: %s", strerror(err));
+ return err;
+ }
+
+ for (int i = 0; attributes[i] != HWC2_ATTRIBUTE_INVALID; i++)
+ values[i] = adf_display_attribute_hwc2(data,
+ dev->display_configs[config], attributes[i]);
+
+ adf_free_interface_data(&data);
+ return 0;
+}
+
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config)
+{
+ if ((size_t)disp >= dev->intf_fds.size())
+ return -EINVAL;
+
+ if (config >= dev->display_configs.size())
+ return -EINVAL;
+
+ struct drm_mode_modeinfo mode = dev->display_configs[config];
+
+ return adf_interface_set_mode(dev->intf_fds[disp], &mode);
+}
+
static void handle_adf_event(struct adf_hwc_helper *dev, int disp)
{
adf_event *event;
@@ -208,28 +273,39 @@
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
- pollfd *fds = new pollfd[dev->intf_fds.size()];
+ struct sigaction action = { };
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ action.sa_handler = [](int) { pthread_exit(0); };
+
+ if (sigaction(SIGUSR2, &action, NULL) < 0) {
+ ALOGE("failed to set thread exit action %s", strerror(errno));
+ return NULL;
+ }
+
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGUSR2);
+
+ pthread_sigmask(SIG_UNBLOCK, &signal_set, NULL);
+
+ pollfd fds[dev->intf_fds.size()];
for (size_t i = 0; i < dev->intf_fds.size(); i++) {
fds[i].fd = dev->intf_fds[i];
fds[i].events = POLLIN | POLLPRI;
}
while (true) {
- int err = poll(fds, dev->intf_fds.size(), -1);
-
- if (err > 0) {
- for (size_t i = 0; i < dev->intf_fds.size(); i++)
- if (fds[i].revents & (POLLIN | POLLPRI))
- handle_adf_event(dev, i);
- }
- else if (err == -1) {
- if (errno == EINTR)
- break;
+ if (TEMP_FAILURE_RETRY(poll(fds, dev->intf_fds.size(), -1)) < 0) {
ALOGE("error in event thread: %s", strerror(errno));
+ break;
}
+
+ for (size_t i = 0; i < dev->intf_fds.size(); i++)
+ if (fds[i].revents & (POLLIN | POLLPRI))
+ handle_adf_event(dev, i);
}
- delete [] fds;
return NULL;
}
@@ -264,6 +340,12 @@
}
}
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGUSR2);
+
+ pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
+
ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread,
dev_ret);
if (ret) {
@@ -284,7 +366,7 @@
void adf_hwc_close(struct adf_hwc_helper *dev)
{
- pthread_kill(dev->event_thread, SIGTERM);
+ pthread_kill(dev->event_thread, SIGUSR2);
pthread_join(dev->event_thread, NULL);
for (size_t i = 0; i < dev->intf_fds.size(); i++)
diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h
index 71e7624..4f70925 100644
--- a/adf/libadfhwc/include/adfhwc/adfhwc.h
+++ b/adf/libadfhwc/include/adfhwc/adfhwc.h
@@ -23,6 +23,7 @@
#include <video/adf.h>
#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer2.h>
struct adf_hwc_helper;
@@ -123,6 +124,16 @@
uint32_t *configs, size_t *numConfigs);
int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp,
uint32_t config, const uint32_t *attributes, int32_t *values);
+/**
+ * Generic implementation of common HWC2 functions.
+ *
+ * The HWC2 should not return these functions directly through getFunction.
+ * Instead, the HWC2 should return stub functions which call these helpers.
+ */
+int adf_getDisplayAttributes_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config, const uint32_t *attributes, int32_t *values);
+int adf_set_active_config_hwc2(struct adf_hwc_helper *dev, int disp,
+ uint32_t config);
__END_DECLS
diff --git a/base/file.cpp b/base/file.cpp
index 6284b04..81b04d7 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -18,11 +18,13 @@
#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
+#include <mutex>
#include <string>
#include <vector>
@@ -236,5 +238,59 @@
#endif
}
+std::string GetExecutableDirectory() {
+ return Dirname(GetExecutablePath());
+}
+
+std::string Basename(const std::string& path) {
+ // Copy path because basename may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because basename() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to basename in the process also grab this same lock, but its
+ // better than nothing. Bionic's basename returns a thread-local buffer.
+ static std::mutex& basename_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(basename_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* name = basename(&result[0]);
+
+ // In case basename returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(name);
+
+ return result;
+}
+
+std::string Dirname(const std::string& path) {
+ // Copy path because dirname may modify the string passed in.
+ std::string result(path);
+
+#if !defined(__BIONIC__)
+ // Use lock because dirname() may write to a process global and return a
+ // pointer to that. Note that this locking strategy only works if all other
+ // callers to dirname in the process also grab this same lock, but its
+ // better than nothing. Bionic's dirname returns a thread-local buffer.
+ static std::mutex& dirname_lock = *new std::mutex();
+ std::lock_guard<std::mutex> lock(dirname_lock);
+#endif
+
+ // Note that if std::string uses copy-on-write strings, &str[0] will cause
+ // the copy to be made, so there is no chance of us accidentally writing to
+ // the storage for 'path'.
+ char* parent = dirname(&result[0]);
+
+ // In case dirname returned a pointer to a process global, copy that string
+ // before leaving the lock.
+ result.assign(parent);
+
+ return result;
+}
+
} // namespace base
} // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index ed39ce9..1021326 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -159,6 +159,26 @@
#endif
}
+TEST(file, GetExecutableDirectory) {
+ std::string path = android::base::GetExecutableDirectory();
+ ASSERT_NE("", path);
+ ASSERT_NE(android::base::GetExecutablePath(), path);
+ ASSERT_EQ('/', path[0]);
+ ASSERT_NE('/', path[path.size() - 1]);
+}
+
TEST(file, GetExecutablePath) {
ASSERT_NE("", android::base::GetExecutablePath());
}
+
+TEST(file, Basename) {
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh"));
+ EXPECT_EQ("sh", android::base::Basename("sh"));
+ EXPECT_EQ("sh", android::base::Basename("/system/bin/sh/"));
+}
+
+TEST(file, Dirname) {
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh"));
+ EXPECT_EQ(".", android::base::Dirname("sh"));
+ EXPECT_EQ("/system/bin", android::base::Dirname("/system/bin/sh/"));
+}
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index cd64262..33d1ab3 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -51,6 +51,12 @@
#endif
std::string GetExecutablePath();
+std::string GetExecutableDirectory();
+
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string Basename(const std::string& path);
+std::string Dirname(const std::string& path);
} // namespace base
} // namespace android
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index e2324b7..4de5e57 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -66,6 +66,12 @@
const std::string& expected_value,
std::chrono::milliseconds relative_timeout);
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Returns true on success, false on timeout.
+bool WaitForPropertyCreation(const std::string& key,
+ std::chrono::milliseconds relative_timeout);
+
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index acd6c0f..32c0128 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -108,8 +108,10 @@
ts.tv_nsec = ns.count();
}
+using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
+
static void UpdateTimeSpec(timespec& ts,
- const std::chrono::time_point<std::chrono::steady_clock>& timeout) {
+ const AbsTime& timeout) {
auto now = std::chrono::steady_clock::now();
auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
if (remaining_timeout < 0ns) {
@@ -119,13 +121,16 @@
}
}
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
- std::chrono::milliseconds relative_timeout) {
+// Waits for the system property `key` to be created.
+// Times out after `relative_timeout`.
+// Sets absolute_timeout which represents absolute time for the timeout.
+// Returns nullptr on timeout.
+static const prop_info* WaitForPropertyCreation(const std::string& key,
+ const std::chrono::milliseconds& relative_timeout,
+ AbsTime& absolute_timeout) {
// TODO: boot_clock?
auto now = std::chrono::steady_clock::now();
- std::chrono::time_point<std::chrono::steady_clock> absolute_timeout = now + relative_timeout;
- timespec ts;
+ absolute_timeout = now + relative_timeout;
// Find the property's prop_info*.
const prop_info* pi;
@@ -133,14 +138,25 @@
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.
+ timespec ts;
UpdateTimeSpec(ts, absolute_timeout);
- if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return false;
+ if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
}
+ return pi;
+}
+
+bool WaitForProperty(const std::string& key,
+ const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout) {
+ AbsTime absolute_timeout;
+ const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+ if (pi == nullptr) return false;
WaitForPropertyData data;
data.expected_value = &expected_value;
data.done = false;
while (true) {
+ timespec ts;
// Check whether the property has the value we're looking for?
__system_property_read_callback(pi, WaitForPropertyCallback, &data);
if (data.done) return true;
@@ -152,5 +168,11 @@
}
}
+bool WaitForPropertyCreation(const std::string& key,
+ std::chrono::milliseconds relative_timeout) {
+ AbsTime absolute_timeout;
+ return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+}
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index c68c2f8..1bbe482 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -150,3 +150,25 @@
// Upper bounds on timing are inherently flaky, but let's try...
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
}
+
+TEST(properties, WaitForPropertyCreation) {
+ std::thread thread([&]() {
+ std::this_thread::sleep_for(100ms);
+ android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a");
+ });
+
+ ASSERT_TRUE(android::base::WaitForPropertyCreation(
+ "debug.libbase.WaitForPropertyCreation_test", 1s));
+ thread.join();
+}
+
+TEST(properties, WaitForPropertyCreation_timeout) {
+ auto t0 = std::chrono::steady_clock::now();
+ ASSERT_FALSE(android::base::WaitForPropertyCreation(
+ "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms));
+ auto t1 = std::chrono::steady_clock::now();
+
+ ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
+ // Upper bounds on timing are inherently flaky, but let's try...
+ ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
+}
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 81d70df..f2975d1 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -45,42 +45,6 @@
return true;
}
-static bool check_dumpable(pid_t pid) {
- // /proc/<pid> is owned by the effective UID of the process.
- // Ownership of most of the other files in /proc/<pid> varies based on PR_SET_DUMPABLE.
- // If PR_GET_DUMPABLE would return 0, they're owned by root, instead.
- std::string proc_pid_path = android::base::StringPrintf("/proc/%d/", pid);
- std::string proc_pid_status_path = proc_pid_path + "/status";
-
- unique_fd proc_pid_fd(open(proc_pid_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC));
- if (proc_pid_fd == -1) {
- return false;
- }
- unique_fd proc_pid_status_fd(openat(proc_pid_fd, "status", O_RDONLY | O_CLOEXEC));
- if (proc_pid_status_fd == -1) {
- return false;
- }
-
- struct stat proc_pid_st;
- struct stat proc_pid_status_st;
- if (fstat(proc_pid_fd.get(), &proc_pid_st) != 0 ||
- fstat(proc_pid_status_fd.get(), &proc_pid_status_st) != 0) {
- return false;
- }
-
- // We can't figure out if a process is dumpable if its effective UID is root, but that's fine
- // because being root bypasses the PR_SET_DUMPABLE check for ptrace.
- if (proc_pid_st.st_uid == 0) {
- return true;
- }
-
- if (proc_pid_status_st.st_uid == 0) {
- return false;
- }
-
- return true;
-}
-
bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
int timeout_ms) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
@@ -114,11 +78,6 @@
return true;
};
- if (!check_dumpable(pid)) {
- dprintf(output_fd.get(), "target pid %d is not dumpable\n", pid);
- return true;
- }
-
sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
if (sockfd == -1) {
PLOG(ERROR) << "libdebugger_client: failed to create socket";
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 4727894..f73f672 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -48,6 +48,7 @@
shared_libs: [
"libbase",
"liblog",
+ "libseccomp_policy",
],
multilib: {
lib32: {
@@ -69,6 +70,7 @@
"libdebuggerd_handler",
"libbase",
"liblog",
+ "libseccomp_policy",
],
multilib: {
lib32: {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 1c01e3e..6970201 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -33,6 +33,8 @@
#include <android-base/logging.h>
#include <log/log.h>
+#include "seccomp_policy.h"
+
#if defined(STATIC_CRASHER)
#include "debuggerd/handler.h"
#endif
@@ -269,6 +271,7 @@
munmap(map, sizeof(int));
map[0] = '8';
} else if (!strcasecmp(arg, "seccomp")) {
+ set_seccomp_filter();
syscall(99999);
#if defined(__arm__)
} else if (!strcasecmp(arg, "kuser_helper_version")) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 0c5d3cf..492e9f0 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -34,6 +34,8 @@
static void usage(int exit_code) {
fprintf(stderr, "usage: debuggerd [-b] PID\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "-b, --backtrace just a backtrace rather than a full tombstone\n");
_exit(exit_code);
}
@@ -56,7 +58,8 @@
int main(int argc, char* argv[]) {
if (argc <= 1) usage(0);
if (argc > 3) usage(1);
- if (argc == 3 && strcmp(argv[1], "-b") != 0) usage(1);
+ if (argc == 3 && strcmp(argv[1], "-b") != 0 && strcmp(argv[1], "--backtrace") != 0) usage(1);
+ bool backtrace_only = argc == 3;
pid_t pid;
if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {
@@ -69,9 +72,8 @@
}
std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
- bool backtrace = argc == 3;
if (!debuggerd_trigger_dump(pid, std::move(pipewrite),
- backtrace ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
+ backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) {
redirect_thread.join();
errx(1, "failed to dump process %d", pid);
}
diff --git a/debuggerd/libdebuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
index 9d44345..1f4f58a 100644
--- a/debuggerd/libdebuggerd/test/sys/system_properties.h
+++ b/debuggerd/libdebuggerd/test/sys/system_properties.h
@@ -32,7 +32,6 @@
// This is just enough to get the property code to compile on
// the host.
-#define PROP_NAME_MAX 32
#define PROP_VALUE_MAX 92
#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 3166bfc..4686bfd 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -214,7 +214,8 @@
cause = "call to kuser_cmpxchg64";
}
} else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) {
- cause = StringPrintf("seccomp prevented call to disallowed system call %d", si.si_syscall);
+ cause = StringPrintf("seccomp prevented call to disallowed %s system call %d",
+ ABI_STRING, si.si_syscall);
}
if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 8705ece..6754508 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -34,6 +34,7 @@
#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
+#include "debuggerd/handler.h"
#include "debuggerd/protocol.h"
#include "debuggerd/util.h"
@@ -116,7 +117,7 @@
}
result.reset(
- openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0700));
+ openat(tombstone_directory_fd, buf, O_CREAT | O_EXCL | O_WRONLY | O_APPEND | O_CLOEXEC, 0640));
if (result == -1) {
PLOG(FATAL) << "failed to create tombstone at " << kTombstoneDirectory << buf;
}
@@ -254,6 +255,16 @@
}
int main(int, char* []) {
+ umask(0137);
+
+ // Don't try to connect to ourselves if we crash.
+ struct sigaction action = {};
+ action.sa_handler = [](int signal) {
+ LOG(ERROR) << "received fatal signal " << signal;
+ _exit(1);
+ };
+ debuggerd_register_handlers(&action);
+
tombstone_directory_fd = open(kTombstoneDirectory, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
if (tombstone_directory_fd == -1) {
PLOG(FATAL) << "failed to open tombstone directory";
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index e8ed6a2..54a6f71 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -30,6 +30,7 @@
#include <unistd.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <crypto_utils/android_pubkey.h>
@@ -874,6 +875,11 @@
// read verity metadata
if (fec_verity_get_metadata(f, &verity) < 0) {
PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
+ // Allow verity disabled when the device is unlocked without metadata
+ if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
+ retval = FS_MGR_SETUP_VERITY_DISABLED;
+ LWARNING << "Allow invalid metadata when the device is unlocked";
+ }
goto out;
}
diff --git a/init/Android.mk b/init/Android.mk
index 18cbedc..2fc6f19 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -107,6 +107,24 @@
libnl \
libavb
+# Include SELinux policy. We do this here because different modules
+# need to be included based on the value of PRODUCT_FULL_TREBLE. This
+# type of conditional inclusion cannot be done in top-level files such
+# as build/target/product/embedded.mk.
+# This conditional inclusion closely mimics the conditional logic
+# inside init/init.cpp for loading SELinux policy from files.
+ifeq ($(PRODUCT_FULL_TREBLE),true)
+# Use split SELinux policy
+LOCAL_REQUIRED_MODULES += \
+ mapping_sepolicy.cil \
+ nonplat_sepolicy.cil \
+ plat_sepolicy.cil \
+ secilc
+else
+# Use monolithic SELinux policy
+LOCAL_REQUIRED_MODULES += sepolicy
+endif
+
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 6dd1cbb..9e3489e 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -604,20 +604,27 @@
int mount_mode = MOUNT_MODE_DEFAULT;
const char* fstabfile = args[1].c_str();
std::size_t path_arg_end = args.size();
+ const char* prop_post_fix = "default";
for (na = args.size() - 1; na > 1; --na) {
if (args[na] == "--early") {
path_arg_end = na;
queue_event = false;
mount_mode = MOUNT_MODE_EARLY;
+ prop_post_fix = "early";
} else if (args[na] == "--late") {
path_arg_end = na;
import_rc = false;
mount_mode = MOUNT_MODE_LATE;
+ prop_post_fix = "late";
}
}
+ std::string prop_name = android::base::StringPrintf("ro.boottime.init.mount_all.%s",
+ prop_post_fix);
+ Timer t;
int ret = mount_fstab(fstabfile, mount_mode);
+ property_set(prop_name.c_str(), std::to_string(t.duration_ms()).c_str());
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
diff --git a/init/init.cpp b/init/init.cpp
index bddf005..81f228c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -43,6 +43,7 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/iosched_policy.h>
#include <cutils/list.h>
@@ -618,6 +619,180 @@
return 0;
}
+/*
+ * Forks, executes the provided program in the child, and waits for the completion in the parent.
+ * Child's stderr is captured and logged using LOG(ERROR).
+ *
+ * Returns true if the child exited with status code 0, returns false otherwise.
+ */
+static bool fork_execve_and_wait_for_completion(const char* filename, char* const argv[],
+ char* const envp[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[0]));
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ if (execve(filename, argv, envp) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ TEMP_FAILURE_RETRY(close(pipe_fds[1]));
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ TEMP_FAILURE_RETRY(close(child_out_fd));
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
+static constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
+
+static bool selinux_is_split_policy_device() { return access(plat_policy_cil_file, R_OK) != -1; }
+
+/*
+ * Loads SELinux policy split across platform/system and non-platform/vendor files.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_split_policy() {
+ // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
+ // * platform -- policy needed due to logic contained in the system image,
+ // * non-platform -- policy needed due to logic contained in the vendor image,
+ // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
+ // with newer versions of platform policy.
+ //
+ // secilc is invoked to compile the above three policy files into a single monolithic policy
+ // file. This file is then loaded into the kernel.
+
+ LOG(INFO) << "Compiling SELinux policy";
+
+ // We store the output of the compilation on /dev because this is the most convenient tmpfs
+ // storage mount available this early in the boot sequence.
+ char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
+ android::base::unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
+ if (compiled_sepolicy_fd < 0) {
+ PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
+ return false;
+ }
+
+ const char* compile_args[] = {"/system/bin/secilc", plat_policy_cil_file, "-M", "true", "-c",
+ "30", // TODO: pass in SELinux policy version from build system
+ "/vendor/etc/selinux/mapping_sepolicy.cil",
+ "/vendor/etc/selinux/nonplat_sepolicy.cil", "-o",
+ compiled_sepolicy,
+ // We don't care about file_contexts output by the compiler
+ "-f", "/sys/fs/selinux/null", // /dev/null is not yet available
+ nullptr};
+
+ if (!fork_execve_and_wait_for_completion(compile_args[0], (char**)compile_args, (char**)ENV)) {
+ unlink(compiled_sepolicy);
+ return false;
+ }
+ unlink(compiled_sepolicy);
+
+ LOG(INFO) << "Loading compiled SELinux policy";
+ if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
+ LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Loads SELinux policy from a monolithic file.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_monolithic_policy() {
+ LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
+ if (selinux_android_load_policy() < 0) {
+ PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Loads SELinux policy into the kernel.
+ *
+ * Returns true upon success, false otherwise (failure cause is logged).
+ */
+static bool selinux_load_policy() {
+ return selinux_is_split_policy_device() ? selinux_load_split_policy()
+ : selinux_load_monolithic_policy();
+}
+
static void selinux_initialize(bool in_kernel_domain) {
Timer t;
@@ -628,10 +803,9 @@
selinux_set_callback(SELINUX_CB_AUDIT, cb);
if (in_kernel_domain) {
- LOG(INFO) << "Loading SELinux policy...";
- if (selinux_android_load_policy() < 0) {
- PLOG(ERROR) << "failed to load policy";
- security_failure();
+ LOG(INFO) << "Loading SELinux policy";
+ if (!selinux_load_policy()) {
+ panic();
}
bool kernel_enforcing = (security_getenforce() == 1);
@@ -790,6 +964,12 @@
/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
static bool early_mount() {
+ // skip early mount if we're in recovery mode
+ if (access("/sbin/recovery", F_OK) == 0) {
+ LOG(INFO) << "Early mount skipped (recovery mode)";
+ return true;
+ }
+
// first check if device tree fstab entries are compatible
if (!is_dt_fstab_compatible()) {
LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 04bcb18..d88b72e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -49,6 +50,7 @@
#include <fs_mgr.h>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "bootimg.h"
@@ -57,6 +59,8 @@
#include "util.h"
#include "log.h"
+using android::base::StringPrintf;
+
#define PERSISTENT_PROPERTY_DIR "/data/property"
#define FSTAB_PREFIX "/fstab."
#define RECOVERY_MOUNT_POINT "/recovery"
@@ -605,6 +609,7 @@
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
+ property_set("ro.persistent_properties.ready", "true");
}
void load_recovery_id_prop() {
diff --git a/init/service.cpp b/init/service.cpp
index e186f27..ba901fd 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -180,12 +180,6 @@
}
std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
- if (prop_name.length() >= PROP_NAME_MAX) {
- // If the property name would be too long, we can't set it.
- LOG(ERROR) << "Property name \"init.svc." << name_ << "\" too long; not setting to " << new_state;
- return;
- }
-
property_set(prop_name.c_str(), new_state.c_str());
if (new_state == "running") {
@@ -1040,5 +1034,9 @@
}
bool ServiceParser::IsValidName(const std::string& name) const {
- return is_legal_property_name("init.svc." + name);
+ // Property names can be any length, but may only contain certain characters.
+ // Property values can contain any characters, but may only be a certain length.
+ // (The latter restriction is needed because `start` and `stop` work by writing
+ // the service name to the "ctl.start" and "ctl.stop" properties.)
+ return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
}
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
index b38d262..c768373 100644
--- a/libbinderwrapper/Android.mk
+++ b/libbinderwrapper/Android.mk
@@ -41,7 +41,7 @@
include $(BUILD_SHARED_LIBRARY)
-# libbinderwrapper_test_support shared library
+# libbinderwrapper_test_support static library
# ========================================================
include $(CLEAR_VARS)
@@ -59,4 +59,4 @@
binder_test_base.cc \
stub_binder_wrapper.cc \
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 1f08eb4..42f0f37 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -227,19 +227,30 @@
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
+// lineNum = 0 removes verbose comments and requires us to cache the
+// content rather than make direct raw references since the content
+// will disappear after the call. A non-zero lineNum means we own the
+// data and it will outlive the call.
+//
// Returns 0 on success, nonzero on failure.
static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
char* cp;
unsigned long val = strtoul(*pData, &cp, 10);
if (cp == *pData) {
- fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n",
+ lineNum);
+ }
errno = EINVAL;
return -1;
}
uint32_t tagIndex = val;
if (tagIndex != val) {
- fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n",
+ lineNum);
+ }
errno = ERANGE;
return -1;
}
@@ -248,7 +259,10 @@
}
if (*cp == '\n') {
- fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n",
+ lineNum);
+ }
errno = EINVAL;
return -1;
}
@@ -259,7 +273,10 @@
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
- fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
+ if (lineNum) {
+ fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n",
+ lineNum);
+ }
errno = EINVAL;
return -1;
}
@@ -293,9 +310,18 @@
#endif
*pData = cp;
- if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
- MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
- return 0;
+ if (lineNum) {
+ if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+ MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
+ return 0;
+ }
+ } else {
+ // cache
+ if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+ MapString(std::string(tag, tagLen)),
+ MapString(std::string(fmt, fmtLen)))))) {
+ return 0;
+ }
}
errno = EMLINK;
return -1;
@@ -455,12 +481,55 @@
if (map) delete map;
}
+// Cache miss, go to logd to acquire a public reference.
+// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
+static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+ // 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 id=%u";
+ int ret = asprintf(&buf, command_template, tag);
+ 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 existing entry
+ 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
+ ++cp;
+ if (!scanTagLine(map, &cp, 0)) {
+ free(buf);
+ return map->find(tag);
+ }
+ }
+ }
+ free(buf);
+ }
+ return NULL;
+}
+
// Look up an entry in the map.
LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
size_t *len,
unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
+ if (!str) {
+ str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+ }
if (!str) return NULL;
if (len) *len = str->first.length();
return str->first.data();
@@ -471,6 +540,9 @@
const EventTagMap* map, size_t *len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
+ if (!str) {
+ str = __getEventTag(const_cast<EventTagMap*>(map), tag);
+ }
if (!str) return NULL;
if (len) *len = str->second.length();
return str->second.data();
diff --git a/liblog/logprint.c b/liblog/logprint.c
index af52528..4421f83 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -20,9 +20,6 @@
#define HAVE_STRSEP
#endif
-//#ifndef __MINGW32__
-//#include <arpa/inet.h>
-//#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -117,7 +114,7 @@
c = tolower(c);
if (c >= '0' && c <= '9') {
- if (c >= ('0'+ANDROID_LOG_SILENT)) {
+ if (c >= ('0' + ANDROID_LOG_SILENT)) {
pri = ANDROID_LOG_VERBOSE;
} else {
pri = (android_LogPriority)(c - '0');
@@ -397,7 +394,7 @@
}
if(filterExpression[tagNameLength] == ':') {
- pri = filterCharToPri(filterExpression[tagNameLength+1]);
+ pri = filterCharToPri(filterExpression[tagNameLength + 1]);
if (pri == ANDROID_LOG_UNKNOWN) {
goto error;
@@ -521,6 +518,9 @@
struct logger_entry *buf,
AndroidLogEntry *entry)
{
+ entry->message = NULL;
+ entry->messageLen = 0;
+
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
entry->uid = -1;
@@ -621,7 +621,7 @@
low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
- return ((uint64_t) high << 32) | (uint64_t) low;
+ return ((uint64_t)high << 32) | (uint64_t)low;
}
static bool findChar(const char** cp, size_t* len, int c) {
@@ -829,7 +829,10 @@
eventData += 4;
eventDataLen -= 4;
- if (eventDataLen < strLen) return -1;
+ if (eventDataLen < strLen) {
+ result = -1; /* mark truncated */
+ strLen = eventDataLen;
+ }
if (cp && (strLen == 0)) {
/* reset the format if no content */
@@ -840,15 +843,18 @@
memcpy(outBuf, eventData, strLen);
outBuf += strLen;
outBufLen -= strLen;
- } else if (outBufLen > 0) {
- /* copy what we can */
- memcpy(outBuf, eventData, outBufLen);
- outBuf += outBufLen;
- outBufLen -= outBufLen;
- goto no_room;
+ } else {
+ if (outBufLen > 0) {
+ /* copy what we can */
+ memcpy(outBuf, eventData, outBufLen);
+ outBuf += outBufLen;
+ outBufLen -= outBufLen;
+ }
+ if (!result) result = 1; /* if not truncated, return no room */
}
eventData += strLen;
eventDataLen -= strLen;
+ if (result != 0) goto bail;
break;
}
case EVENT_TYPE_LIST:
@@ -991,13 +997,16 @@
LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
struct logger_entry *buf,
AndroidLogEntry *entry,
- const EventTagMap *map __unused, // only on !__ANDROID__
+ const EventTagMap *map __unused, /* only on !__ANDROID__ */
char *messageBuf, int messageBufLen)
{
size_t inCount;
uint32_t tagIndex;
const unsigned char* eventData;
+ entry->message = NULL;
+ entry->messageLen = 0;
+
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
entry->priority = ANDROID_LOG_INFO;
@@ -1009,7 +1018,7 @@
* Pull the tag out, fill in some additional details based on incoming
* buffer version (v3 adds lid, v4 adds uid).
*/
- eventData = (const unsigned char*) buf->msg;
+ eventData = (const unsigned char*)buf->msg;
struct logger_entry_v2 *buf2 = (struct logger_entry_v2 *)buf;
if (buf2->hdr_size) {
if ((buf2->hdr_size < sizeof(((struct log_msg *)NULL)->entry_v1)) ||
@@ -1122,7 +1131,7 @@
*/
*outBuf = '\0';
entry->messageLen = outBuf - messageBuf;
- assert(entry->messageLen == (messageBufLen-1) - outRemaining);
+ assert(entry->messageLen == (messageBufLen - 1) - outRemaining);
entry->message = messageBuf;
@@ -1217,7 +1226,7 @@
} else if (*message == '\b') {
strcpy(buf, "\\b");
} else if (*message == '\t') {
- strcpy(buf, "\t"); // Do not escape tabs
+ strcpy(buf, "\t"); /* Do not escape tabs */
} else if (*message == '\v') {
strcpy(buf, "\\v");
} else if (*message == '\f') {
@@ -1574,7 +1583,7 @@
nsec = entry->tv_nsec;
#if __ANDROID__
if (p_format->monotonic_output) {
- // prevent convertMonotonic from being called if logd is monotonic
+ /* prevent convertMonotonic from being called if logd is monotonic */
if (android_log_clockid() != CLOCK_MONOTONIC) {
struct timespec time;
convertMonotonic(&time, entry);
@@ -1648,7 +1657,7 @@
} else
#endif
{
- // Not worth parsing package list, names all longer than 5
+ /* Not worth parsing package list, names all longer than 5 */
snprintf(uid, sizeof(uid), "%5d:", entry->uid);
}
} else {
@@ -1758,7 +1767,7 @@
if (*pm++ == '\n') numLines++;
}
/* plus one line for anything not newline-terminated at the end */
- if (pm > entry->message && *(pm-1) != '\n') numLines++;
+ if (pm > entry->message && *(pm - 1) != '\n') numLines++;
}
/*
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 330d6cb..584e5a2 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -17,6 +17,7 @@
LOCAL_CFLAGS := -Werror
LOCAL_SHARED_LIBRARIES := \
+ libbase \
libcutils \
liblog \
libnl
@@ -24,4 +25,3 @@
LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
include $(BUILD_SHARED_LIBRARY)
-
diff --git a/libsysutils/src/ServiceManager.cpp b/libsysutils/src/ServiceManager.cpp
index 13bac09..c7aa1f7 100644
--- a/libsysutils/src/ServiceManager.cpp
+++ b/libsysutils/src/ServiceManager.cpp
@@ -19,34 +19,23 @@
#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <sys/system_properties.h>
#include <unistd.h>
-#include <cutils/properties.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <log/log.h>
#include <sysutils/ServiceManager.h>
ServiceManager::ServiceManager() {
}
-/* The service name should not exceed SERVICE_NAME_MAX to avoid
- * some weird things. This is due to the fact that:
- *
- * - Starting a service is done by writing its name to the "ctl.start"
- * system property. This triggers the init daemon to actually start
- * the service for us.
- *
- * - Stopping the service is done by writing its name to "ctl.stop"
- * in a similar way.
- *
- * - Reading the status of a service is done by reading the property
- * named "init.svc.<name>"
- *
- * If strlen(<name>) > (PROPERTY_KEY_MAX-1)-9, then you can start/stop
- * the service by writing to ctl.start/stop, but you won't be able to
- * read its state due to the truncation of "init.svc.<name>" into a
- * zero-terminated buffer of PROPERTY_KEY_MAX characters.
- */
-#define SERVICE_NAME_MAX (PROPERTY_KEY_MAX-10)
+// The length of a service name should not exceed SERVICE_NAME_MAX. Starting
+// a service is done by writing its name to the "ctl.start" system property
+// and stopping a service is done by writing its name to "ctl.stop". If a
+// service name is too long to fit in a property, you won't be able to start
+// or stop it.
+static constexpr size_t SERVICE_NAME_MAX = PROP_VALUE_MAX;
/* The maximum amount of time to wait for a service to start or stop,
* in micro-seconds (really an approximation) */
@@ -61,13 +50,14 @@
SLOGE("Service name '%s' is too long", name);
return 0;
}
+
if (isRunning(name)) {
SLOGW("Service '%s' is already running", name);
return 0;
}
SLOGD("Starting service '%s'", name);
- property_set("ctl.start", name);
+ android::base::SetProperty("ctl.start", name);
int count = SLEEP_MAX_USEC;
while(count > 0) {
@@ -90,13 +80,14 @@
SLOGE("Service name '%s' is too long", name);
return 0;
}
+
if (!isRunning(name)) {
SLOGW("Service '%s' is already stopped", name);
return 0;
}
SLOGD("Stopping service '%s'", name);
- property_set("ctl.stop", name);
+ android::base::SetProperty("ctl.stop", name);
int count = SLEEP_MAX_USEC;
while(count > 0) {
@@ -116,19 +107,6 @@
}
bool ServiceManager::isRunning(const char *name) {
- char propVal[PROPERTY_VALUE_MAX];
- char propName[PROPERTY_KEY_MAX];
- int ret;
-
- ret = snprintf(propName, sizeof(propName), "init.svc.%s", name);
- if (ret > (int)sizeof(propName)-1) {
- SLOGD("Service name '%s' is too long", name);
- return false;
- }
-
- if (property_get(propName, propVal, NULL)) {
- if (!strcmp(propVal, "running"))
- return true;
- }
- return false;
+ std::string property_name = android::base::StringPrintf("init.svc.%s", name);
+ return (android::base::GetProperty(property_name, "") == "running");
}
diff --git a/libutils/include/utils/Condition.h b/libutils/include/utils/Condition.h
index 2c80acd..3019a21 100644
--- a/libutils/include/utils/Condition.h
+++ b/libutils/include/utils/Condition.h
@@ -41,6 +41,11 @@
* call wait(), then either re-wait() if things aren't quite what you want,
* or unlock the mutex and continue. All threads calling wait() must
* use the same mutex for a given Condition.
+ *
+ * On Android and Apple platforms, these are implemented as a simple wrapper
+ * around pthread condition variables. Care must be taken to abide by
+ * the pthreads semantics, in particular, a boolean predicate must
+ * be re-evaluated after a wake-up, as spurious wake-ups may happen.
*/
class Condition {
public:
@@ -58,10 +63,11 @@
explicit Condition(int type);
~Condition();
// Wait on the condition variable. Lock the mutex before calling.
+ // Note that spurious wake-ups may happen.
status_t wait(Mutex& mutex);
// same with relative timeout
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
- // Signal the condition variable, allowing exactly one thread to continue.
+ // Signal the condition variable, allowing one thread to continue.
void signal();
// Signal the condition variable, allowing one or all threads to continue.
void signal(WakeUpType type) {
@@ -142,17 +148,6 @@
return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
}
inline void Condition::signal() {
- /*
- * POSIX says pthread_cond_signal wakes up "one or more" waiting threads.
- * However bionic follows the glibc guarantee which wakes up "exactly one"
- * waiting thread.
- *
- * man 3 pthread_cond_signal
- * pthread_cond_signal restarts one of the threads that are waiting on
- * the condition variable cond. If no threads are waiting on cond,
- * nothing happens. If several threads are waiting on cond, exactly one
- * is restarted, but it is not specified which.
- */
pthread_cond_signal(&mCond);
}
inline void Condition::broadcast() {
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 70ada72..ea606a1 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -23,6 +23,7 @@
srcs: [
"BitSet_test.cpp",
"LruCache_test.cpp",
+ "Singleton_test.cpp",
"String8_test.cpp",
"StrongPointer_test.cpp",
"Unicode_test.cpp",
@@ -42,6 +43,8 @@
"liblog",
"libcutils",
"libutils",
+ "libbase",
+ "libdl",
],
},
linux: {
@@ -54,13 +57,35 @@
static_libs: [
"libutils",
"liblog",
+ "libbase",
],
+ host_ldlibs: ["-ldl"],
},
},
+ required: [
+ "libutils_tests_singleton1",
+ "libutils_tests_singleton2",
+ ],
+
cflags: [
"-Wall",
"-Wextra",
"-Werror",
],
}
+
+cc_test_library {
+ name: "libutils_tests_singleton1",
+ host_supported: true,
+ relative_install_path: "libutils_tests",
+ srcs: ["Singleton_test1.cpp"],
+}
+
+cc_test_library {
+ name: "libutils_tests_singleton2",
+ host_supported: true,
+ relative_install_path: "libutils_tests",
+ srcs: ["Singleton_test2.cpp"],
+ shared_libs: ["libutils_tests_singleton1"],
+}
diff --git a/libutils/tests/Singleton_test.cpp b/libutils/tests/Singleton_test.cpp
new file mode 100644
index 0000000..9acd3c3
--- /dev/null
+++ b/libutils/tests/Singleton_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Singleton_test"
+
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <utils/Singleton.h>
+
+#include <gtest/gtest.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+TEST(SingletonTest, bug35674422) {
+ std::string path = android::base::GetExecutableDirectory();
+ // libutils_tests_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE
+ // definition of SingletonTestData, load it first.
+ std::string lib = android::base::StringPrintf("%s/libutils_tests_singleton1.so", path.c_str());
+ void* handle1 = dlopen(lib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+ // libutils_tests_singleton2.so references SingletonTestData but should not
+ // have a definition
+ lib = android::base::StringPrintf("%s/libutils_tests_singleton2.so", path.c_str());
+ void* handle2 = dlopen(lib.c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+ using has_fn_t = decltype(&singletonHasInstance);
+ using get_fn_t = decltype(&singletonGetInstanceContents);
+ using set_fn_t = decltype(&singletonSetInstanceContents);
+
+ has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, "singletonHasInstance"));
+ ASSERT_TRUE(has1 != nullptr) << dlerror();
+ has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, "singletonHasInstance"));
+ ASSERT_TRUE(has2 != nullptr) << dlerror();
+ get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, "singletonGetInstanceContents"));
+ ASSERT_TRUE(get1 != nullptr) << dlerror();
+ get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, "singletonGetInstanceContents"));
+ ASSERT_TRUE(get2 != nullptr) << dlerror();
+ set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, "singletonSetInstanceContents"));
+ ASSERT_TRUE(set1 != nullptr) << dlerror();
+
+ EXPECT_FALSE(has1());
+ EXPECT_FALSE(has2());
+ set1(12345678U);
+ EXPECT_TRUE(has1());
+ EXPECT_TRUE(has2());
+ EXPECT_EQ(12345678U, get1());
+ EXPECT_EQ(12345678U, get2());
+}
+
+}
diff --git a/libutils/tests/Singleton_test.h b/libutils/tests/Singleton_test.h
new file mode 100644
index 0000000..c77d9ff
--- /dev/null
+++ b/libutils/tests/Singleton_test.h
@@ -0,0 +1,41 @@
+/*
+ * 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 ANDROID_UTILS_SINGLETON_TEST_H
+#define ANDROID_UTILS_SINGLETON_TEST_H
+
+#include <sys/cdefs.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+struct SingletonTestData : Singleton<SingletonTestData> {
+ unsigned int contents;
+};
+
+__BEGIN_DECLS
+
+unsigned int singletonGetInstanceContents();
+void singletonSetInstanceContents(unsigned int);
+bool singletonHasInstance();
+
+__END_DECLS
+
+}
+
+#endif // ANDROID_UTILS_SINGLETON_TEST_H
+
diff --git a/libutils/tests/Singleton_test1.cpp b/libutils/tests/Singleton_test1.cpp
new file mode 100644
index 0000000..4a91ec0
--- /dev/null
+++ b/libutils/tests/Singleton_test1.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+ANDROID_SINGLETON_STATIC_INSTANCE(SingletonTestData);
+
+void singletonSetInstanceContents(unsigned int contents) {
+ SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+ return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+ return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/libutils/tests/Singleton_test2.cpp b/libutils/tests/Singleton_test2.cpp
new file mode 100644
index 0000000..eb2a9df
--- /dev/null
+++ b/libutils/tests/Singleton_test2.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <utils/Singleton.h>
+
+#include "Singleton_test.h"
+
+namespace android {
+
+// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and
+// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.
+
+void singletonSetInstanceContents(unsigned int contents) {
+ SingletonTestData::getInstance().contents = contents;
+}
+
+unsigned int singletonGetInstanceContents() {
+ return SingletonTestData::getInstance().contents;
+}
+
+bool singletonHasInstance() {
+ return SingletonTestData::hasInstance();
+}
+
+}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index 723c35e..f564f0f 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -16,7 +16,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := liblogcat
-LOCAL_SRC_FILES := logcat.cpp logcat_system.cpp
+LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
LOCAL_SHARED_LIBRARIES := $(logcatLibs)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/logcat/getopt_long.cpp b/logcat/getopt_long.cpp
new file mode 100644
index 0000000..5f8dd66
--- /dev/null
+++ b/logcat/getopt_long.cpp
@@ -0,0 +1,402 @@
+/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */
+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <log/getopt.h>
+
+#define PRINT_ERROR ((context->opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE 0x01 // permute non-options to the end of argv
+#define FLAG_ALLARGS 0x02 // treat non-options as args to option "-1"
+
+// return values
+#define BADCH (int)'?'
+#define BADARG ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER (int)1
+
+#define D_PREFIX 0
+#define DD_PREFIX 1
+#define W_PREFIX 2
+
+// Compute the greatest common divisor of a and b.
+static int gcd(int a, int b) {
+ int c = a % b;
+ while (c) {
+ a = b;
+ b = c;
+ c = a % b;
+ }
+ return b;
+}
+
+// Exchange the block from nonopt_start to nonopt_end with the block from
+// nonopt_end to opt_end (keeping the same order of arguments in each block).
+// Returns optind - (nonopt_end - nonopt_start) for convenience.
+static int permute_args(getopt_context* context, char* const* nargv) {
+ // compute lengths of blocks and number and size of cycles
+ int nnonopts = context->nonopt_end - context->nonopt_start;
+ int nopts = context->optind - context->nonopt_end;
+ int ncycle = gcd(nnonopts, nopts);
+ int cyclelen = (context->optind - context->nonopt_start) / ncycle;
+
+ for (int i = 0; i < ncycle; i++) {
+ int cstart = context->nonopt_end + i;
+ int pos = cstart;
+ for (int j = 0; j < cyclelen; j++) {
+ if (pos >= context->nonopt_end) {
+ pos -= nnonopts;
+ } else {
+ pos += nopts;
+ }
+ char* swap = nargv[pos];
+ const_cast<char**>(nargv)[pos] = nargv[cstart];
+ const_cast<char**>(nargv)[cstart] = swap;
+ }
+ }
+ return context->optind - (context->nonopt_end - context->nonopt_start);
+}
+
+// parse_long_options_r --
+// Parse long options in argc/argv argument vector.
+// Returns -1 if short_too is set and the option does not match long_options.
+static int parse_long_options_r(char* const* nargv, const char* options,
+ const struct option* long_options, int* idx,
+ bool short_too, struct getopt_context* context) {
+ const char* current_argv = context->place;
+ const char* current_dash;
+ switch (context->dash_prefix) {
+ case D_PREFIX:
+ current_dash = "-";
+ break;
+ case DD_PREFIX:
+ current_dash = "--";
+ break;
+ case W_PREFIX:
+ current_dash = "-W ";
+ break;
+ default:
+ current_dash = "";
+ break;
+ }
+ context->optind++;
+
+ const char* has_equal;
+ size_t current_argv_len;
+ if (!!(has_equal = strchr(current_argv, '='))) {
+ // argument found (--option=arg)
+ current_argv_len = has_equal - current_argv;
+ has_equal++;
+ } else {
+ current_argv_len = strlen(current_argv);
+ }
+
+ int match = -1;
+ bool exact_match = false;
+ bool second_partial_match = false;
+ for (int i = 0; long_options[i].name; i++) {
+ // find matching long option
+ if (strncmp(current_argv, long_options[i].name, current_argv_len)) {
+ continue;
+ }
+
+ if (strlen(long_options[i].name) == current_argv_len) {
+ // exact match
+ match = i;
+ exact_match = true;
+ break;
+ }
+ // If this is a known short option, don't allow
+ // a partial match of a single character.
+ if (short_too && current_argv_len == 1) continue;
+
+ if (match == -1) { // first partial match
+ match = i;
+ } else if (long_options[i].has_arg != long_options[match].has_arg ||
+ long_options[i].flag != long_options[match].flag ||
+ long_options[i].val != long_options[match].val) {
+ second_partial_match = true;
+ }
+ }
+ if (!exact_match && second_partial_match) {
+ // ambiguous abbreviation
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr,
+ "option `%s%.*s' is ambiguous", current_dash,
+ (int)current_argv_len, current_argv);
+ }
+ context->optopt = 0;
+ return BADCH;
+ }
+ if (match != -1) { // option found
+ if (long_options[match].has_arg == no_argument && has_equal) {
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr,
+ "option `%s%.*s' doesn't allow an argument",
+ current_dash, (int)current_argv_len, current_argv);
+ }
+ // XXX: GNU sets optopt to val regardless of flag
+ context->optopt =
+ long_options[match].flag ? 0 : long_options[match].val;
+ return BADCH;
+ }
+ if (long_options[match].has_arg == required_argument ||
+ long_options[match].has_arg == optional_argument) {
+ if (has_equal) {
+ context->optarg = has_equal;
+ } else if (long_options[match].has_arg == required_argument) {
+ // optional argument doesn't use next nargv
+ context->optarg = nargv[context->optind++];
+ }
+ }
+ if ((long_options[match].has_arg == required_argument) &&
+ !context->optarg) {
+ // Missing argument; leading ':' indicates no error
+ // should be generated.
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr,
+ "option `%s%s' requires an argument", current_dash,
+ current_argv);
+ }
+ // XXX: GNU sets optopt to val regardless of flag
+ context->optopt =
+ long_options[match].flag ? 0 : long_options[match].val;
+ context->optind--;
+ return BADARG;
+ }
+ } else { // unknown option
+ if (short_too) {
+ context->optind--;
+ return -1;
+ }
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr, "unrecognized option `%s%s'",
+ current_dash, current_argv);
+ }
+ context->optopt = 0;
+ return BADCH;
+ }
+ if (idx) *idx = match;
+ if (long_options[match].flag) {
+ *long_options[match].flag = long_options[match].val;
+ return 0;
+ }
+ return long_options[match].val;
+}
+
+// getopt_long_r --
+// Parse argc/argv argument vector.
+int getopt_long_r(int nargc, char* const* nargv, const char* options,
+ const struct option* long_options, int* idx,
+ struct getopt_context* context) {
+ if (!options) return -1;
+
+ // XXX Some GNU programs (like cvs) set optind to 0 instead of
+ // XXX using optreset. Work around this braindamage.
+ if (!context->optind) context->optind = context->optreset = 1;
+
+ // Disable GNU extensions if options string begins with a '+'.
+ int flags = FLAG_PERMUTE;
+ if (*options == '-') {
+ flags |= FLAG_ALLARGS;
+ } else if (*options == '+') {
+ flags &= ~FLAG_PERMUTE;
+ }
+ if (*options == '+' || *options == '-') options++;
+
+ context->optarg = nullptr;
+ if (context->optreset) context->nonopt_start = context->nonopt_end = -1;
+start:
+ if (context->optreset || !*context->place) { // update scanning pointer
+ context->optreset = 0;
+ if (context->optind >= nargc) { // end of argument vector
+ context->place = EMSG;
+ if (context->nonopt_end != -1) {
+ // do permutation, if we have to
+ context->optind = permute_args(context, nargv);
+ } else if (context->nonopt_start != -1) {
+ // If we skipped non-options, set optind to the first of them.
+ context->optind = context->nonopt_start;
+ }
+ context->nonopt_start = context->nonopt_end = -1;
+ return -1;
+ }
+ if (*(context->place = nargv[context->optind]) != '-' ||
+ context->place[1] == '\0') {
+ context->place = EMSG; // found non-option
+ if (flags & FLAG_ALLARGS) {
+ // GNU extension: return non-option as argument to option 1
+ context->optarg = nargv[context->optind++];
+ return INORDER;
+ }
+ if (!(flags & FLAG_PERMUTE)) {
+ // If no permutation wanted, stop parsing at first non-option.
+ return -1;
+ }
+ // do permutation
+ if (context->nonopt_start == -1) {
+ context->nonopt_start = context->optind;
+ } else if (context->nonopt_end != -1) {
+ context->nonopt_start = permute_args(context, nargv);
+ context->nonopt_end = -1;
+ }
+ context->optind++;
+ // process next argument
+ goto start;
+ }
+ if (context->nonopt_start != -1 && context->nonopt_end == -1) {
+ context->nonopt_end = context->optind;
+ }
+
+ // If we have "-" do nothing, if "--" we are done.
+ if (context->place[1] != '\0' && *++(context->place) == '-' &&
+ context->place[1] == '\0') {
+ context->optind++;
+ context->place = EMSG;
+ // We found an option (--), so if we skipped
+ // non-options, we have to permute.
+ if (context->nonopt_end != -1) {
+ context->optind = permute_args(context, nargv);
+ }
+ context->nonopt_start = context->nonopt_end = -1;
+ return -1;
+ }
+ }
+
+ int optchar;
+ // Check long options if:
+ // 1) we were passed some
+ // 2) the arg is not just "-"
+ // 3) either the arg starts with -- we are getopt_long_only()
+ if (long_options && context->place != nargv[context->optind] &&
+ (*context->place == '-')) {
+ bool short_too = false;
+ context->dash_prefix = D_PREFIX;
+ if (*context->place == '-') {
+ context->place++; // --foo long option
+ context->dash_prefix = DD_PREFIX;
+ } else if (*context->place != ':' && strchr(options, *context->place)) {
+ short_too = true; // could be short option too
+ }
+
+ optchar = parse_long_options_r(nargv, options, long_options, idx,
+ short_too, context);
+ if (optchar != -1) {
+ context->place = EMSG;
+ return optchar;
+ }
+ }
+
+ const char* oli; // option letter list index
+ if ((optchar = (int)*(context->place)++) == (int)':' ||
+ (optchar == (int)'-' && *context->place != '\0') ||
+ !(oli = strchr(options, optchar))) {
+ // If the user specified "-" and '-' isn't listed in
+ // options, return -1 (non-option) as per POSIX.
+ // Otherwise, it is an unknown option character (or ':').
+ if (optchar == (int)'-' && *context->place == '\0') return -1;
+ if (!*context->place) context->optind++;
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr, "invalid option -- %c",
+ optchar);
+ }
+ context->optopt = optchar;
+ return BADCH;
+ }
+
+ static const char recargchar[] = "option requires an argument -- %c";
+ if (long_options && optchar == 'W' && oli[1] == ';') {
+ // -W long-option
+ if (*context->place) { // no space
+ ; // NOTHING
+ } else if (++(context->optind) >= nargc) { // no arg
+ context->place = EMSG;
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr, recargchar, optchar);
+ }
+ context->optopt = optchar;
+ return BADARG;
+ } else { // white space
+ context->place = nargv[context->optind];
+ }
+ context->dash_prefix = W_PREFIX;
+ optchar = parse_long_options_r(nargv, options, long_options, idx, false,
+ context);
+ context->place = EMSG;
+ return optchar;
+ }
+ if (*++oli != ':') { // doesn't take argument
+ if (!*context->place) context->optind++;
+ } else { // takes (optional) argument
+ context->optarg = nullptr;
+ if (*context->place) { // no white space
+ context->optarg = context->place;
+ } else if (oli[1] != ':') { // arg not optional
+ if (++(context->optind) >= nargc) { // no arg
+ context->place = EMSG;
+ if (PRINT_ERROR) {
+ fprintf(context->optstderr ?: stderr, recargchar, optchar);
+ }
+ context->optopt = optchar;
+ return BADARG;
+ }
+ context->optarg = nargv[context->optind];
+ }
+ context->place = EMSG;
+ context->optind++;
+ }
+ // dump back option letter
+ return optchar;
+}
diff --git a/logcat/include/log/getopt.h b/logcat/include/log/getopt.h
new file mode 100644
index 0000000..0da2b10
--- /dev/null
+++ b/logcat/include/log/getopt.h
@@ -0,0 +1,67 @@
+/*
+ * 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 _LOG_GETOPT_H_
+#define _LOG_GETOPT_H_
+
+#ifndef __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#elif __ANDROID_API__ > 24 /* > Nougat */
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE
+
+#include <getopt.h>
+#include <sys/cdefs.h>
+
+struct getopt_context {
+ int opterr;
+ int optind;
+ int optopt;
+ int optreset;
+ const char* optarg;
+ FILE* optstderr; /* NULL defaults to stderr */
+ /* private */
+ const char* place;
+ int nonopt_start;
+ int nonopt_end;
+ int dash_prefix;
+ /* expansion space */
+ int __extra__;
+ void* __stuff__;
+};
+
+#define EMSG ""
+#define NO_PREFIX (-1)
+
+#define INIT_GETOPT_CONTEXT(context) \
+ context = { 1, 1, '?', 0, NULL, NULL, EMSG, -1, -1, NO_PREFIX, 0, NULL }
+
+__BEGIN_DECLS
+int getopt_long_r(int nargc, char* const* nargv, const char* options,
+ const struct option* long_options, int* idx,
+ struct getopt_context* context);
+
+__END_DECLS
+
+#endif /* __ANDROID_USE_LIBLOG_LOGCAT_INTERFACE */
+
+#endif /* !_LOG_GETOPT_H_ */
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 7f852d4..3cd36f9 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -20,7 +20,6 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <getopt.h>
#include <math.h>
#include <pthread.h>
#include <sched.h>
@@ -47,6 +46,7 @@
#include <cutils/sched_policy.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
+#include <log/getopt.h>
#include <log/logcat.h>
#include <log/logprint.h>
#include <private/android_logger.h>
@@ -67,8 +67,8 @@
std::vector<const char*> argv_hold;
std::vector<std::string> envs;
std::vector<const char*> envp_hold;
- int output_fd;
- int error_fd;
+ int output_fd; // duplication of fileno(output) (below)
+ int error_fd; // duplication of fileno(error) (below)
// library
int fds[2]; // From popen call
@@ -108,7 +108,7 @@
context = (android_logcat_context_internal*)calloc(
1, sizeof(android_logcat_context_internal));
- if (!context) return NULL;
+ if (!context) return nullptr;
context->fds[0] = -1;
context->fds[1] = -1;
@@ -139,10 +139,10 @@
log_device_t(const char* d, bool b) {
device = d;
binary = b;
- next = NULL;
+ next = nullptr;
printed = false;
- logger = NULL;
- logger_list = NULL;
+ logger = nullptr;
+ logger_list = nullptr;
}
};
@@ -162,7 +162,7 @@
static void close_output(android_logcat_context_internal* context) {
// split output_from_error
if (context->error == context->output) {
- context->output = NULL;
+ context->output = nullptr;
context->output_fd = -1;
}
if (context->error && (context->output_fd == fileno(context->error))) {
@@ -182,7 +182,7 @@
}
fclose(context->output);
}
- context->output = NULL;
+ context->output = nullptr;
}
if (context->output_fd >= 0) {
if (context->output_fd != fileno(stdout)) {
@@ -198,7 +198,7 @@
static void close_error(android_logcat_context_internal* context) {
// split error_from_output
if (context->output == context->error) {
- context->error = NULL;
+ context->error = nullptr;
context->error_fd = -1;
}
if (context->output && (context->error_fd == fileno(context->output))) {
@@ -218,14 +218,12 @@
}
fclose(context->error);
}
- context->error = NULL;
+ context->error = nullptr;
}
if (context->error_fd >= 0) {
if ((context->error_fd != fileno(stdout)) &&
(context->error_fd != fileno(stderr))) {
- if (context->fds[1] == context->error_fd) {
- context->fds[1] = -1;
- }
+ if (context->fds[1] == context->error_fd) context->fds[1] = -1;
close(context->error_fd);
}
context->error_fd = -1;
@@ -236,9 +234,7 @@
int err;
// Can't rotate logs if we're not outputting to a file
- if (context->outputFileName == NULL) {
- return;
- }
+ if (!context->outputFileName) return;
close_output(context);
@@ -257,7 +253,7 @@
"%s.%.*d", context->outputFileName, maxRotationCountDigits, i);
std::string file0;
- if (i - 1 == 0) {
+ if (!(i - 1)) {
file0 = android::base::StringPrintf("%s", context->outputFileName);
} else {
file0 =
@@ -265,7 +261,7 @@
maxRotationCountDigits, i - 1);
}
- if ((file0.length() == 0) || (file1.length() == 0)) {
+ if (!file0.length() || !file1.length()) {
perror("while rotating log files");
break;
}
@@ -284,6 +280,15 @@
return;
}
context->output = fdopen(context->output_fd, "web");
+ if (!context->output) {
+ logcat_panic(context, HELP_FALSE, "couldn't fdopen output file");
+ return;
+ }
+ if (context->stderr_stdout) {
+ close_error(context);
+ context->error = context->output;
+ context->error_fd = context->output_fd;
+ }
context->outByteCount = 0;
}
@@ -296,9 +301,7 @@
static bool regexOk(android_logcat_context_internal* context,
const AndroidLogEntry& entry) {
- if (!context->regex) {
- return true;
- }
+ if (!context->regex) return true;
std::string messageString(entry.message, entry.messageLen);
@@ -314,7 +317,7 @@
if (dev->binary) {
if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
- context->eventTagMap = android_openEventTagMap(NULL);
+ context->eventTagMap = android_openEventTagMap(nullptr);
context->hasOpenedEventTagMap = true;
}
err = android_log_processBinaryLogBuffer(
@@ -325,9 +328,7 @@
} else {
err = android_log_processLogBuffer(&buf->entry_v1, &entry);
}
- if ((err < 0) && !context->debug) {
- return;
- }
+ if ((err < 0) && !context->debug) return;
if (android_log_shouldPrintLine(
context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
@@ -372,7 +373,7 @@
static void setupOutputAndSchedulingPolicy(
android_logcat_context_internal* context, bool blocking) {
- if (context->outputFileName == NULL) return;
+ if (!context->outputFileName) return;
if (blocking) {
// Lower priority and set to batch scheduling if we are saving
@@ -445,6 +446,8 @@
" and individually flagged modifying adverbs can be added:\n"
" color descriptive epoch monotonic printable uid\n"
" usec UTC year zone\n"
+ " Multiple -v parameters or comma separated list of format and\n"
+ " format modifiers are allowed.\n"
// private and undocumented nsec, no signal, too much noise
// useful for -T or -t <timestamp> accurate testing though.
" -D, --dividers Print dividers between each log buffer\n"
@@ -553,10 +556,8 @@
format = android_log_formatFromString(formatString);
- if (format == FORMAT_OFF) {
- // FORMAT_OFF means invalid string
- return -1;
- }
+ // invalid string?
+ if (format == FORMAT_OFF) return -1;
return android_log_setPrintFormat(context->logformat, format);
}
@@ -583,21 +584,15 @@
// String to unsigned int, returns -1 if it fails
static bool getSizeTArg(const char* ptr, size_t* val, size_t min = 0,
size_t max = SIZE_MAX) {
- if (!ptr) {
- return false;
- }
+ if (!ptr) return false;
char* endp;
errno = 0;
size_t ret = (size_t)strtoll(ptr, &endp, 0);
- if (endp[0] || errno) {
- return false;
- }
+ if (endp[0] || errno) return false;
- if ((ret > max) || (ret < min)) {
- return false;
- }
+ if ((ret > max) || (ret < min)) return false;
*val = ret;
return true;
@@ -633,40 +628,30 @@
static char* parseTime(log_time& t, const char* cp) {
char* ep = t.strptime(cp, "%m-%d %H:%M:%S.%q");
- if (ep) {
- return ep;
- }
+ if (ep) return ep;
ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
- if (ep) {
- return ep;
- }
+ if (ep) return ep;
return t.strptime(cp, "%s.%q");
}
// Find last logged line in <outputFileName>, or <outputFileName>.1
-static log_time lastLogTime(char* outputFileName) {
+static log_time lastLogTime(const char* outputFileName) {
log_time retval(log_time::EPOCH);
- if (!outputFileName) {
- return retval;
- }
+ if (!outputFileName) return retval;
std::string directory;
- char* file = strrchr(outputFileName, '/');
+ const char* file = strrchr(outputFileName, '/');
if (!file) {
directory = ".";
file = outputFileName;
} else {
- *file = '\0';
- directory = outputFileName;
- *file = '/';
+ directory = std::string(outputFileName, file - outputFileName);
++file;
}
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(directory.c_str()),
closedir);
- if (!dir.get()) {
- return retval;
- }
+ if (!dir.get()) return retval;
log_time now(android_log_clockid());
@@ -674,10 +659,10 @@
log_time modulo(0, NS_PER_SEC);
struct dirent* dp;
- while ((dp = readdir(dir.get())) != NULL) {
- if ((dp->d_type != DT_REG) || (strncmp(dp->d_name, file, len) != 0) ||
+ while (!!(dp = readdir(dir.get()))) {
+ if ((dp->d_type != DT_REG) || !!strncmp(dp->d_name, file, len) ||
(dp->d_name[len] && ((dp->d_name[len] != '.') ||
- (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
+ (strtoll(dp->d_name + 1, nullptr, 10) != 1)))) {
continue;
}
@@ -685,17 +670,13 @@
file_name += "/";
file_name += dp->d_name;
std::string file;
- if (!android::base::ReadFileToString(file_name, &file)) {
- continue;
- }
+ if (!android::base::ReadFileToString(file_name, &file)) continue;
bool found = false;
for (const auto& line : android::base::Split(file, "\n")) {
log_time t(log_time::EPOCH);
char* ep = parseTime(t, line.c_str());
- if (!ep || (*ep != ' ')) {
- continue;
- }
+ if (!ep || (*ep != ' ')) continue;
// determine the time precision of the logs (eg: msec or usec)
for (unsigned long mod = 1UL; mod < modulo.tv_nsec; mod *= 10) {
if (t.tv_nsec % (mod * 10)) {
@@ -713,13 +694,9 @@
}
}
// We count on the basename file to be the definitive end, so stop here.
- if (!dp->d_name[len] && found) {
- break;
- }
+ if (!dp->d_name[len] && found) break;
}
- if (retval == log_time::EPOCH) {
- return retval;
- }
+ if (retval == log_time::EPOCH) return retval;
// tail_time prints matching or higher, round up by the modulo to prevent
// a replay of the last entry we have just checked.
retval += modulo;
@@ -727,32 +704,29 @@
}
const char* getenv(android_logcat_context_internal* context, const char* name) {
- if (!context->envp || !name || !*name) return NULL;
+ if (!context->envp || !name || !*name) return nullptr;
for (size_t len = strlen(name), i = 0; context->envp[i]; ++i) {
if (strncmp(context->envp[i], name, len)) continue;
if (context->envp[i][len] == '=') return &context->envp[i][len + 1];
}
- return NULL;
+ return nullptr;
}
} // namespace android
void reportErrorName(const char** current, const char* name,
bool blockSecurity) {
- if (*current) {
- return;
+ if (*current) return;
+ if (!blockSecurity || (android_name_to_log_id(name) != LOG_ID_SECURITY)) {
+ *current = name;
}
- if (blockSecurity && (android_name_to_log_id(name) == LOG_ID_SECURITY)) {
- return;
- }
- *current = name;
}
static int __logcat(android_logcat_context_internal* context) {
using namespace android;
int err;
- int hasSetLogFormat = 0;
+ bool hasSetLogFormat = false;
bool clearLog = false;
bool allSelected = false;
bool getLogSize = false;
@@ -760,11 +734,11 @@
bool printStatistics = false;
bool printDividers = false;
unsigned long setLogSize = 0;
- char* setPruneList = NULL;
- char* setId = NULL;
+ const char* setPruneList = nullptr;
+ const char* setId = nullptr;
int mode = ANDROID_LOG_RDONLY;
std::string forceFilters;
- log_device_t* devices = NULL;
+ log_device_t* devices = nullptr;
log_device_t* dev;
struct logger_list* logger_list;
size_t tail_lines = 0;
@@ -774,10 +748,10 @@
// object instantiations before goto's can happen
log_device_t unexpected("unexpected", false);
- const char* openDeviceFail = NULL;
- const char* clearFail = NULL;
- const char* setSizeFail = NULL;
- const char* getSizeFail = NULL;
+ const char* openDeviceFail = nullptr;
+ const char* clearFail = nullptr;
+ const char* setSizeFail = nullptr;
+ const char* getSizeFail = nullptr;
int argc = context->argc;
char* const* argv = context->argv;
@@ -788,6 +762,7 @@
// Simulate shell stderr redirect parsing
if ((argv[i][0] != '2') || (argv[i][1] != '>')) continue;
+ // Append to file not implemented, just open file
size_t skip = (argv[i][2] == '>') + 2;
if (!strcmp(&argv[i][skip], "/dev/null")) {
context->stderr_null = true;
@@ -799,16 +774,29 @@
"stderr redirection to file %s unsupported, skipping\n",
&argv[i][skip]);
}
+ // Only the first one
+ break;
+ }
+
+ const char* filename = nullptr;
+ for (int i = 0; i < argc; ++i) {
+ // Simulate shell stdout redirect parsing
+ if (argv[i][0] != '>') continue;
+
+ // Append to file not implemented, just open file
+ filename = &argv[i][(argv[i][1] == '>') + 1];
+ // Only the first one
+ break;
}
// Deal with setting up file descriptors and FILE pointers
- if (context->error_fd >= 0) {
+ if (context->error_fd >= 0) { // Is an error file descriptor supplied?
if (context->error_fd == context->output_fd) {
context->stderr_stdout = true;
- } else if (context->stderr_null) {
+ } else if (context->stderr_null) { // redirection told us to close it
close(context->error_fd);
context->error_fd = -1;
- } else {
+ } else { // All Ok, convert error to a FILE pointer
context->error = fdopen(context->error_fd, "web");
if (!context->error) {
context->retval = -errno;
@@ -819,22 +807,32 @@
}
}
}
- if (context->output_fd >= 0) {
- context->output = fdopen(context->output_fd, "web");
- if (!context->output) {
- context->retval = -errno;
- fprintf(context->stderr_stdout ? stdout : context->error,
- "Failed to fdopen(output_fd=%d) %s\n", context->output_fd,
- strerror(errno));
- goto exit;
+ if (context->output_fd >= 0) { // Is an output file descriptor supplied?
+ if (filename) { // redirect to file, close supplied file descriptor.
+ close(context->output_fd);
+ context->output_fd = -1;
+ } else { // All Ok, convert output to a FILE pointer
+ context->output = fdopen(context->output_fd, "web");
+ if (!context->output) {
+ context->retval = -errno;
+ fprintf(context->stderr_stdout ? stdout : context->error,
+ "Failed to fdopen(output_fd=%d) %s\n",
+ context->output_fd, strerror(errno));
+ goto exit;
+ }
}
}
+ if (filename) { // We supplied an output file redirected in command line
+ context->output = fopen(filename, "web");
+ }
+ // Deal with 2>&1
if (context->stderr_stdout) context->error = context->output;
+ // Deal with 2>/dev/null
if (context->stderr_null) {
context->error_fd = -1;
- context->error = NULL;
+ context->error = nullptr;
}
- // Only happens if output=stdout
+ // Only happens if output=stdout or output=filename
if ((context->output_fd < 0) && context->output) {
context->output_fd = fileno(context->output);
}
@@ -845,14 +843,21 @@
context->logformat = android_log_format_new();
- if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
+ if (argc == 2 && !strcmp(argv[1], "--help")) {
show_help(context);
context->retval = EXIT_SUCCESS;
goto exit;
}
- // danger: getopt is _not_ reentrant
- optind = 1;
+ // meant to catch comma-delimited values, but cast a wider
+ // net for stability dealing with possible mistaken inputs.
+ static const char delimiters[] = ",:; \t\n\r\f";
+
+ struct getopt_context optctx;
+ INIT_GETOPT_CONTEXT(optctx);
+ optctx.opterr = !!context->error;
+ optctx.optstderr = context->error;
+
for (;;) {
int ret;
@@ -865,52 +870,50 @@
static const char print_str[] = "print";
// clang-format off
static const struct option long_options[] = {
- { "binary", no_argument, NULL, 'B' },
- { "buffer", required_argument, NULL, 'b' },
- { "buffer-size", optional_argument, NULL, 'g' },
- { "clear", no_argument, NULL, 'c' },
- { debug_str, no_argument, NULL, 0 },
- { "dividers", no_argument, NULL, 'D' },
- { "file", required_argument, NULL, 'f' },
- { "format", required_argument, NULL, 'v' },
+ { "binary", no_argument, nullptr, 'B' },
+ { "buffer", required_argument, nullptr, 'b' },
+ { "buffer-size", optional_argument, nullptr, 'g' },
+ { "clear", no_argument, nullptr, 'c' },
+ { debug_str, no_argument, nullptr, 0 },
+ { "dividers", no_argument, nullptr, 'D' },
+ { "file", required_argument, nullptr, 'f' },
+ { "format", required_argument, nullptr, 'v' },
// hidden and undocumented reserved alias for --regex
- { "grep", required_argument, NULL, 'e' },
+ { "grep", required_argument, nullptr, 'e' },
// hidden and undocumented reserved alias for --max-count
- { "head", required_argument, NULL, 'm' },
- { id_str, required_argument, NULL, 0 },
- { "last", no_argument, NULL, 'L' },
- { "max-count", required_argument, NULL, 'm' },
- { pid_str, required_argument, NULL, 0 },
- { print_str, no_argument, NULL, 0 },
- { "prune", optional_argument, NULL, 'p' },
- { "regex", required_argument, NULL, 'e' },
- { "rotate-count", required_argument, NULL, 'n' },
- { "rotate-kbytes", required_argument, NULL, 'r' },
- { "statistics", no_argument, NULL, 'S' },
+ { "head", required_argument, nullptr, 'm' },
+ { id_str, required_argument, nullptr, 0 },
+ { "last", no_argument, nullptr, 'L' },
+ { "max-count", required_argument, nullptr, 'm' },
+ { pid_str, required_argument, nullptr, 0 },
+ { print_str, no_argument, nullptr, 0 },
+ { "prune", optional_argument, nullptr, 'p' },
+ { "regex", required_argument, nullptr, 'e' },
+ { "rotate-count", required_argument, nullptr, 'n' },
+ { "rotate-kbytes", required_argument, nullptr, 'r' },
+ { "statistics", no_argument, nullptr, 'S' },
// hidden and undocumented reserved alias for -t
- { "tail", required_argument, NULL, 't' },
+ { "tail", required_argument, nullptr, 't' },
// support, but ignore and do not document, the optional argument
- { wrap_str, optional_argument, NULL, 0 },
- { NULL, 0, NULL, 0 }
+ { wrap_str, optional_argument, nullptr, 0 },
+ { nullptr, 0, nullptr, 0 }
};
// clang-format on
- ret = getopt_long(argc, argv,
- ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
- &option_index);
-
- if (ret < 0) {
- break;
- }
+ ret = getopt_long_r(argc, argv,
+ ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
+ &option_index, &optctx);
+ if (ret < 0) break;
switch (ret) {
case 0:
// only long options
if (long_options[option_index].name == pid_str) {
// ToDo: determine runtime PID_MAX?
- if (!getSizeTArg(optarg, &pid, 1)) {
+ if (!getSizeTArg(optctx.optarg, &pid, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name, optarg);
+ long_options[option_index].name,
+ optctx.optarg);
goto exit;
}
break;
@@ -920,9 +923,11 @@
ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
+ if (optctx.optarg &&
+ !getSizeTArg(optctx.optarg, &dummy, 1)) {
logcat_panic(context, HELP_TRUE, "%s %s out of range\n",
- long_options[option_index].name, optarg);
+ long_options[option_index].name,
+ optctx.optarg);
goto exit;
}
if ((dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) &&
@@ -943,8 +948,8 @@
break;
}
if (long_options[option_index].name == id_str) {
- setId = optarg && optarg[0] ? optarg : NULL;
- break;
+ setId = (optctx.optarg && optctx.optarg[0]) ? optctx.optarg
+ : nullptr;
}
break;
@@ -972,12 +977,13 @@
mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
// FALLTHRU
case 'T':
- if (strspn(optarg, "0123456789") != strlen(optarg)) {
- char* cp = parseTime(tail_time, optarg);
+ if (strspn(optctx.optarg, "0123456789") !=
+ strlen(optctx.optarg)) {
+ char* cp = parseTime(tail_time, optctx.optarg);
if (!cp) {
logcat_panic(context, HELP_FALSE,
"-%c \"%s\" not in time format\n", ret,
- optarg);
+ optctx.optarg);
goto exit;
}
if (*cp) {
@@ -987,16 +993,16 @@
fprintf(
context->error,
"WARNING: -%c \"%s\"\"%c%s\" time truncated\n",
- ret, optarg, c, cp + 1);
+ ret, optctx.optarg, c, cp + 1);
}
*cp = c;
}
} else {
- if (!getSizeTArg(optarg, &tail_lines, 1)) {
+ if (!getSizeTArg(optctx.optarg, &tail_lines, 1)) {
if (context->error) {
fprintf(context->error,
"WARNING: -%c %s invalid, setting to 1\n",
- ret, optarg);
+ ret, optctx.optarg);
}
tail_lines = 1;
}
@@ -1008,22 +1014,22 @@
break;
case 'e':
- context->regex = new pcrecpp::RE(optarg);
+ context->regex = new pcrecpp::RE(optctx.optarg);
break;
case 'm': {
- char* end = NULL;
- if (!getSizeTArg(optarg, &context->maxCount)) {
+ char* end = nullptr;
+ if (!getSizeTArg(optctx.optarg, &context->maxCount)) {
logcat_panic(context, HELP_FALSE,
"-%c \"%s\" isn't an "
"integer greater than zero\n",
- ret, optarg);
+ ret, optctx.optarg);
goto exit;
}
} break;
case 'g':
- if (!optarg) {
+ if (!optctx.optarg) {
getLogSize = true;
break;
}
@@ -1031,8 +1037,8 @@
case 'G': {
char* cp;
- if (strtoll(optarg, &cp, 0) > 0) {
- setLogSize = strtoll(optarg, &cp, 0);
+ if (strtoll(optctx.optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optctx.optarg, &cp, 0);
} else {
setLogSize = 0;
}
@@ -1065,50 +1071,50 @@
} break;
case 'p':
- if (!optarg) {
+ if (!optctx.optarg) {
getPruneList = true;
break;
}
// FALLTHRU
case 'P':
- setPruneList = optarg;
+ setPruneList = optctx.optarg;
break;
case 'b': {
+ std::unique_ptr<char, void (*)(void*)> buffers(
+ strdup(optctx.optarg), free);
+ char* arg = buffers.get();
unsigned idMask = 0;
- while ((optarg = strtok(optarg, ",:; \t\n\r\f")) != NULL) {
- if (strcmp(optarg, "default") == 0) {
+ char* sv = nullptr; // protect against -ENOMEM above
+ while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+ if (!strcmp(arg, "default")) {
idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) |
(1 << LOG_ID_CRASH);
- } else if (strcmp(optarg, "all") == 0) {
+ } else if (!strcmp(arg, "all")) {
allSelected = true;
idMask = (unsigned)-1;
} else {
- log_id_t log_id = android_name_to_log_id(optarg);
+ log_id_t log_id = android_name_to_log_id(arg);
const char* name = android_log_id_to_name(log_id);
- if (strcmp(name, optarg) != 0) {
+ if (!!strcmp(name, arg)) {
logcat_panic(context, HELP_TRUE,
- "unknown buffer %s\n", optarg);
+ "unknown buffer %s\n", arg);
goto exit;
}
if (log_id == LOG_ID_SECURITY) allSelected = false;
idMask |= (1 << log_id);
}
- optarg = NULL;
+ arg = nullptr;
}
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
const char* name = android_log_id_to_name((log_id_t)i);
log_id_t log_id = android_name_to_log_id(name);
- if (log_id != (log_id_t)i) {
- continue;
- }
- if ((idMask & (1 << i)) == 0) {
- continue;
- }
+ if (log_id != (log_id_t)i) continue;
+ if (!(idMask & (1 << i))) continue;
bool found = false;
for (dev = devices; dev; dev = dev->next) {
@@ -1116,13 +1122,9 @@
found = true;
break;
}
- if (!dev->next) {
- break;
- }
+ if (!dev->next) break;
}
- if (found) {
- continue;
- }
+ if (found) continue;
bool binary =
!strcmp(name, "events") || !strcmp(name, "security");
@@ -1143,43 +1145,55 @@
break;
case 'f':
- if ((tail_time == log_time::EPOCH) && (tail_lines == 0)) {
- tail_time = lastLogTime(optarg);
+ if ((tail_time == log_time::EPOCH) && !tail_lines) {
+ tail_time = lastLogTime(optctx.optarg);
}
// redirect output to a file
- context->outputFileName = optarg;
+ context->outputFileName = optctx.optarg;
break;
case 'r':
- if (!getSizeTArg(optarg, &context->logRotateSizeKBytes, 1)) {
+ if (!getSizeTArg(optctx.optarg, &context->logRotateSizeKBytes,
+ 1)) {
logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -r\n", optarg);
+ "Invalid parameter \"%s\" to -r\n",
+ optctx.optarg);
goto exit;
}
break;
case 'n':
- if (!getSizeTArg(optarg, &context->maxRotatedLogs, 1)) {
+ if (!getSizeTArg(optctx.optarg, &context->maxRotatedLogs, 1)) {
logcat_panic(context, HELP_TRUE,
- "Invalid parameter \"%s\" to -n\n", optarg);
+ "Invalid parameter \"%s\" to -n\n",
+ optctx.optarg);
goto exit;
}
break;
- case 'v':
- if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
+ case 'v': {
+ if (!strcmp(optctx.optarg, "help") ||
+ !strcmp(optctx.optarg, "--help")) {
show_format_help(context);
context->retval = EXIT_SUCCESS;
goto exit;
}
- err = setLogFormat(context, optarg);
- if (err < 0) {
- logcat_panic(context, HELP_FORMAT,
- "Invalid parameter \"%s\" to -v\n", optarg);
- goto exit;
+ std::unique_ptr<char, void (*)(void*)> formats(
+ strdup(optctx.optarg), free);
+ char* arg = formats.get();
+ unsigned idMask = 0;
+ char* sv = nullptr; // protect against -ENOMEM above
+ while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+ err = setLogFormat(context, arg);
+ if (err < 0) {
+ logcat_panic(context, HELP_FORMAT,
+ "Invalid parameter \"%s\" to -v\n", arg);
+ goto exit;
+ }
+ arg = nullptr;
+ if (err) hasSetLogFormat = true;
}
- hasSetLogFormat |= err;
- break;
+ } break;
case 'Q':
#define KERNEL_OPTION "androidboot.logcat="
@@ -1245,12 +1259,12 @@
case ':':
logcat_panic(context, HELP_TRUE,
- "Option -%c needs an argument\n", optopt);
+ "Option -%c needs an argument\n", optctx.optopt);
goto exit;
default:
logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
- optopt);
+ optctx.optopt);
goto exit;
}
}
@@ -1286,13 +1300,13 @@
}
}
- if (context->logRotateSizeKBytes != 0 && context->outputFileName == NULL) {
+ if (!!context->logRotateSizeKBytes && !context->outputFileName) {
logcat_panic(context, HELP_TRUE, "-r requires -f as well\n");
goto exit;
}
- if (setId != NULL) {
- if (context->outputFileName == NULL) {
+ if (!!setId) {
+ if (!context->outputFileName) {
logcat_panic(context, HELP_TRUE,
"--id='%s' requires -f as well\n", setId);
goto exit;
@@ -1304,22 +1318,29 @@
bool file_ok = android::base::ReadFileToString(file_name, &file);
android::base::WriteStringToFile(setId, file_name, S_IRUSR | S_IWUSR,
getuid(), getgid());
- if (!file_ok || (file.compare(setId) == 0)) {
- setId = NULL;
- }
+ if (!file_ok || !file.compare(setId)) setId = nullptr;
}
- if (hasSetLogFormat == 0) {
+ if (!hasSetLogFormat) {
const char* logFormat = android::getenv(context, "ANDROID_PRINTF_LOG");
- if (logFormat != NULL) {
- err = setLogFormat(context, logFormat);
- if ((err < 0) && context->error) {
- fprintf(context->error,
- "invalid format in ANDROID_PRINTF_LOG '%s'\n",
- logFormat);
+ if (!!logFormat) {
+ std::unique_ptr<char, void (*)(void*)> formats(strdup(logFormat),
+ free);
+ char* sv = nullptr; // protect against -ENOMEM above
+ char* arg = formats.get();
+ while (!!(arg = strtok_r(arg, delimiters, &sv))) {
+ err = setLogFormat(context, arg);
+ // environment should not cause crash of logcat
+ if ((err < 0) && context->error) {
+ fprintf(context->error,
+ "invalid format in ANDROID_PRINTF_LOG '%s'\n", arg);
+ }
+ arg = nullptr;
+ if (err > 0) hasSetLogFormat = true;
}
- } else {
+ }
+ if (!hasSetLogFormat) {
setLogFormat(context, "threadtime");
}
}
@@ -1332,11 +1353,11 @@
"Invalid filter expression in logcat args\n");
goto exit;
}
- } else if (argc == optind) {
+ } else if (argc == optctx.optind) {
// Add from environment variable
const char* env_tags_orig = android::getenv(context, "ANDROID_LOG_TAGS");
- if (env_tags_orig != NULL) {
+ if (!!env_tags_orig) {
err = android_log_addFilterString(context->logformat,
env_tags_orig);
@@ -1348,9 +1369,11 @@
}
} else {
// Add from commandline
- for (int i = optind ; i < argc ; i++) {
+ for (int i = optctx.optind ; i < argc ; i++) {
// skip stderr redirections of _all_ kinds
if ((argv[i][0] == '2') && (argv[i][1] == '>')) continue;
+ // skip stdout redirections of _all_ kinds
+ if (argv[i][0] == '>') continue;
err = android_log_addFilterString(context->logformat, argv[i]);
if (err < 0) {
@@ -1389,7 +1412,7 @@
for (int i = context->maxRotatedLogs ; i >= 0 ; --i) {
std::string file;
- if (i == 0) {
+ if (!i) {
file = android::base::StringPrintf(
"%s", context->outputFileName);
} else {
@@ -1397,7 +1420,7 @@
context->outputFileName, maxRotationCountDigits, i);
}
- if (file.length() == 0) {
+ if (!file.length()) {
perror("while clearing log files");
reportErrorName(&clearFail, dev->device, allSelected);
break;
@@ -1405,7 +1428,7 @@
err = unlink(file.c_str());
- if (err < 0 && errno != ENOENT && clearFail == NULL) {
+ if (err < 0 && errno != ENOENT && !clearFail) {
perror("while clearing log files");
reportErrorName(&clearFail, dev->device, allSelected);
}
@@ -1472,7 +1495,7 @@
size_t len = strlen(setPruneList);
// extra 32 bytes are needed by android_logger_set_prune_list
size_t bLen = len + 32;
- char* buf = NULL;
+ char* buf = nullptr;
if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
buf[len] = '\0';
if (android_logger_set_prune_list(logger_list, buf, bLen)) {
@@ -1492,7 +1515,7 @@
char* buf;
for (int retry = 32; (retry >= 0) && ((buf = new char[len]));
- delete[] buf, buf = NULL, --retry) {
+ delete[] buf, buf = nullptr, --retry) {
if (getPruneList) {
android_logger_get_prune_list(logger_list, buf, len);
} else {
@@ -1501,7 +1524,7 @@
buf[len - 1] = '\0';
if (atol(buf) < 3) {
delete[] buf;
- buf = NULL;
+ buf = nullptr;
break;
}
size_t ret = atol(buf) + 1;
@@ -1521,19 +1544,13 @@
char* cp = buf + len - 1;
*cp = '\0';
bool truncated = *--cp != '\f';
- if (!truncated) {
- *cp = '\0';
- }
+ if (!truncated) *cp = '\0';
// squash out the byte count
cp = buf;
if (!truncated) {
- while (isdigit(*cp)) {
- ++cp;
- }
- if (*cp == '\n') {
- ++cp;
- }
+ while (isdigit(*cp)) ++cp;
+ if (*cp == '\n') ++cp;
}
len = strlen(cp);
@@ -1542,32 +1559,28 @@
goto close;
}
- if (getLogSize || setLogSize || clearLog) {
- goto close;
- }
+ if (getLogSize || setLogSize || clearLog) goto close;
- setupOutputAndSchedulingPolicy(context, (mode & ANDROID_LOG_NONBLOCK) == 0);
+ setupOutputAndSchedulingPolicy(context, !(mode & ANDROID_LOG_NONBLOCK));
if (context->stop) goto close;
// LOG_EVENT_INT(10, 12345);
// LOG_EVENT_LONG(11, 0x1122334455667788LL);
// LOG_EVENT_STRING(0, "whassup, doc?");
- dev = NULL;
+ dev = nullptr;
while (!context->stop &&
(!context->maxCount || (context->printCount < context->maxCount))) {
struct log_msg log_msg;
int ret = android_logger_list_read(logger_list, &log_msg);
- if (ret == 0) {
+ if (!ret) {
logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
break;
}
if (ret < 0) {
- if (ret == -EAGAIN) {
- break;
- }
+ if (ret == -EAGAIN) break;
if (ret == -EIO) {
logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
@@ -1583,9 +1596,7 @@
log_device_t* d;
for (d = devices; d; d = d->next) {
- if (android_name_to_log_id(d->device) == log_msg.id()) {
- break;
- }
+ if (android_name_to_log_id(d->device) == log_msg.id()) break;
}
if (!d) {
context->devCount = 2; // set to Multiple
@@ -1651,9 +1662,7 @@
android_logcat_context_internal* context = ctx;
int save_errno = EBUSY;
- if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
- goto exit;
- }
+ if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) goto exit;
if (pipe(context->fds) < 0) {
save_errno = errno;
@@ -1690,11 +1699,11 @@
for (auto& str : context->args) {
context->argv_hold.push_back(str.c_str());
}
- context->argv_hold.push_back(NULL);
+ context->argv_hold.push_back(nullptr);
for (auto& str : context->envs) {
context->envp_hold.push_back(str.c_str());
}
- context->envp_hold.push_back(NULL);
+ context->envp_hold.push_back(nullptr);
context->argc = context->argv_hold.size() - 1;
context->argv = (char* const*)&context->argv_hold[0];
@@ -1703,7 +1712,7 @@
#ifdef DEBUG
fprintf(stderr, "argv[%d] = {", context->argc);
for (auto str : context->argv_hold) {
- fprintf(stderr, " \"%s\"", str ?: "NULL");
+ fprintf(stderr, " \"%s\"", str ?: "nullptr");
}
fprintf(stderr, " }\n");
fflush(stderr);
@@ -1749,11 +1758,14 @@
int android_logcat_destroy(android_logcat_context* ctx) {
android_logcat_context_internal* context = *ctx;
- *ctx = NULL;
+ if (!context) return -EBADF;
+
+ *ctx = nullptr;
context->stop = true;
while (context->thread_stopped == false) {
+ // Makes me sad, replace thread_stopped with semaphore. Short lived.
sched_yield();
}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 1c3feba..22aca17 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -27,11 +27,12 @@
-fno-builtin \
# -----------------------------------------------------------------------------
-# Benchmarks (actually a gTest where the result code does not matter)
+# Benchmarks
# ----------------------------------------------------------------------------
benchmark_src_files := \
- logcat_benchmark.cpp
+ logcat_benchmark.cpp \
+ exec_benchmark.cpp \
# Build benchmarks for the device. Run with:
# adb shell /data/nativetest/logcat-benchmarks/logcat-benchmarks
@@ -40,7 +41,8 @@
LOCAL_MODULE_TAGS := $(test_tags)
LOCAL_CFLAGS += $(test_c_flags)
LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+LOCAL_SHARED_LIBRARIES := libbase liblogcat
+include $(BUILD_NATIVE_BENCHMARK)
# -----------------------------------------------------------------------------
# Unit tests.
diff --git a/logcat/tests/exec_benchmark.cpp b/logcat/tests/exec_benchmark.cpp
new file mode 100644
index 0000000..c30a5f5
--- /dev/null
+++ b/logcat/tests/exec_benchmark.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <stdio.h>
+
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <log/logcat.h>
+
+// Dump the statistics and report results
+
+static void logcat_popen_libc(benchmark::State& state, const char* cmd) {
+ while (state.KeepRunning()) {
+ FILE* fp = popen(cmd, "r");
+ std::string ret;
+ android::base::ReadFdToString(fileno(fp), &ret);
+ pclose(fp);
+ }
+}
+
+static void BM_logcat_stat_popen_libc(benchmark::State& state) {
+ logcat_popen_libc(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_libc);
+
+static void logcat_popen_liblogcat(benchmark::State& state, const char* cmd) {
+ while (state.KeepRunning()) {
+ android_logcat_context ctx;
+ FILE* fp = android_logcat_popen(&ctx, cmd);
+ std::string ret;
+ android::base::ReadFdToString(fileno(fp), &ret);
+ android_logcat_pclose(&ctx, fp);
+ }
+}
+
+static void BM_logcat_stat_popen_liblogcat(benchmark::State& state) {
+ logcat_popen_liblogcat(state, "logcat -b all -S");
+}
+BENCHMARK(BM_logcat_stat_popen_liblogcat);
+
+static void logcat_system_libc(benchmark::State& state, const char* cmd) {
+ while (state.KeepRunning()) {
+ system(cmd);
+ }
+}
+
+static void BM_logcat_stat_system_libc(benchmark::State& state) {
+ logcat_system_libc(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_libc);
+
+static void logcat_system_liblogcat(benchmark::State& state, const char* cmd) {
+ while (state.KeepRunning()) {
+ android_logcat_system(cmd);
+ }
+}
+
+static void BM_logcat_stat_system_liblogcat(benchmark::State& state) {
+ logcat_system_liblogcat(state, "logcat -b all -S >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_stat_system_liblogcat);
+
+// Dump the logs and report results
+
+static void BM_logcat_dump_popen_libc(benchmark::State& state) {
+ logcat_popen_libc(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_libc);
+
+static void BM_logcat_dump_popen_liblogcat(benchmark::State& state) {
+ logcat_popen_liblogcat(state, "logcat -b all -d");
+}
+BENCHMARK(BM_logcat_dump_popen_liblogcat);
+
+static void BM_logcat_dump_system_libc(benchmark::State& state) {
+ logcat_system_libc(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_libc);
+
+static void BM_logcat_dump_system_liblogcat(benchmark::State& state) {
+ logcat_system_liblogcat(state, "logcat -b all -d >/dev/null 2>/dev/null");
+}
+BENCHMARK(BM_logcat_dump_system_liblogcat);
diff --git a/logcat/tests/logcat_benchmark.cpp b/logcat/tests/logcat_benchmark.cpp
index dd85164..8d88628 100644
--- a/logcat/tests/logcat_benchmark.cpp
+++ b/logcat/tests/logcat_benchmark.cpp
@@ -18,19 +18,22 @@
#include <stdlib.h>
#include <string.h>
-#include <gtest/gtest.h>
+#include <benchmark/benchmark.h>
static const char begin[] = "--------- beginning of ";
-TEST(logcat, sorted_order) {
- FILE *fp;
+static void BM_logcat_sorted_order(benchmark::State& state) {
+ FILE* fp;
- ASSERT_TRUE(NULL != (fp = popen(
- "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
- "r")));
+ if (!state.KeepRunning()) return;
+
+ fp = popen(
+ "logcat -v time -b radio -b events -b system -b main -d 2>/dev/null",
+ "r");
+ if (!fp) return;
class timestamp {
- private:
+ private:
int month;
int day;
int hour;
@@ -39,44 +42,39 @@
int millisecond;
bool ok;
- public:
- void init(const char *buffer)
- {
+ public:
+ void init(const char* buffer) {
ok = false;
if (buffer != NULL) {
- ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ",
- &month, &day, &hour, &minute, &second, &millisecond) == 6;
+ ok = sscanf(buffer, "%d-%d %d:%d:%d.%d ", &month, &day, &hour,
+ &minute, &second, &millisecond) == 6;
}
}
- explicit timestamp(const char *buffer)
- {
+ explicit timestamp(const char* buffer) {
init(buffer);
}
- bool operator< (timestamp &T)
- {
- return !ok || !T.ok
- || (month < T.month)
- || ((month == T.month)
- && ((day < T.day)
- || ((day == T.day)
- && ((hour < T.hour)
- || ((hour == T.hour)
- && ((minute < T.minute)
- || ((minute == T.minute)
- && ((second < T.second)
- || ((second == T.second)
- && (millisecond < T.millisecond))))))))));
+ bool operator<(timestamp& T) {
+ return !ok || !T.ok || (month < T.month) ||
+ ((month == T.month) &&
+ ((day < T.day) ||
+ ((day == T.day) &&
+ ((hour < T.hour) ||
+ ((hour == T.hour) &&
+ ((minute < T.minute) ||
+ ((minute == T.minute) &&
+ ((second < T.second) ||
+ ((second == T.second) &&
+ (millisecond < T.millisecond))))))))));
}
- bool valid(void)
- {
+ bool valid(void) {
return ok;
}
} last(NULL);
- char *last_buffer = NULL;
+ char* last_buffer = NULL;
char buffer[5120];
int count = 0;
@@ -114,15 +112,22 @@
// Allow few fails, happens with readers active
fprintf(stderr, "%s: %d/%d out of order entries\n",
- (next_lt_last)
- ? ((next_lt_last <= max_ok)
- ? "WARNING"
- : "ERROR")
- : "INFO",
+ (next_lt_last) ? ((next_lt_last <= max_ok) ? "WARNING" : "ERROR")
+ : "INFO",
next_lt_last, count);
- EXPECT_GE(max_ok, next_lt_last);
+ if (next_lt_last > max_ok) {
+ fprintf(stderr, "EXPECT_GE(max_ok=%d, next_lt_last=%d)\n", max_ok,
+ next_lt_last);
+ }
// sample statistically too small
- EXPECT_LT(100, count);
+ if (count < 100) {
+ fprintf(stderr, "EXPECT_LT(100, count=%d)\n", count);
+ }
+
+ state.KeepRunning();
}
+BENCHMARK(BM_logcat_sorted_order);
+
+BENCHMARK_MAIN();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7d0c87d..03f878a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -116,6 +116,12 @@
EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
endif
+
+EXPORT_GLOBAL_GCOV_OPTIONS :=
+ifeq ($(NATIVE_COVERAGE),true)
+ EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/gcov
+endif
+
# Put it here instead of in init.rc module definition,
# because init.rc is conditionally included.
#
@@ -163,6 +169,7 @@
$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
+ $(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
bcp_md5 :=
bcp_dep :=
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 32817fa..2e2ab74 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,3 +10,4 @@
export BOOTCLASSPATH %BOOTCLASSPATH%
export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
%EXPORT_GLOBAL_ASAN_OPTIONS%
+ %EXPORT_GLOBAL_GCOV_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1e5fa50..757e1ff 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -408,6 +408,7 @@
mkdir /data/misc/profiles/cur 0771 system system
mkdir /data/misc/profiles/ref 0771 system system
mkdir /data/misc/profman 0770 system shell
+ mkdir /data/misc/gcov 0770 root root
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp