Merge "There's no longer a limit to property names."
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/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/init/property_service.cpp b/init/property_service.cpp
index 04bcb18..decd644 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,8 @@
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
+ uint64_t start_ns = boot_clock::now().time_since_epoch().count();
+ property_set("ro.boottime.persistent_properties", StringPrintf("%" PRIu64, start_ns).c_str());
}
void load_recovery_id_prop() {
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 75e2492..1915ced 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -165,7 +165,7 @@
/* Support Bluetooth legacy hal accessing /sys/class/rfkill */
{ 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
- "system/bin/hw/android.hardware.bluetooth@1.0-service" },
+ "vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
/* A non-privileged zygote that spawns isolated processes for web rendering. */
{ 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
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/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/logcat.cpp b/logcat/logcat.cpp
index 7f852d4..d67dee5 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -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
@@ -284,6 +284,15 @@
return;
}
context->output = fdopen(context->output_fd, "web");
+ if (context->output == NULL) {
+ 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;
}
@@ -788,6 +797,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 +809,29 @@
"stderr redirection to file %s unsupported, skipping\n",
&argv[i][skip]);
}
+ // Only the first one
+ break;
+ }
+
+ const char* filename = NULL;
+ 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 +842,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 the 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;
}
- // 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);
}
@@ -1351,6 +1384,8 @@
for (int i = 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) {
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