Merge "bootstat: Log bootloader timing metrics to Tron."
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 032ae31..9069baa 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -302,13 +302,6 @@
// So, we have a device, finally. Grab its vitals.
-
- kr = (*dev)->USBDeviceOpen(dev);
- if (kr != 0) {
- WARN("USBDeviceOpen");
- goto out;
- }
-
kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
if (kr != 0) {
ERR("GetDeviceVendor");
@@ -381,16 +374,12 @@
goto error;
}
- out:
-
- (*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
return 0;
error:
if (dev != NULL) {
- (*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index c53ea2c..1f08eb4 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,15 +27,63 @@
#include <experimental/string_view>
#include <functional>
+#include <string>
#include <unordered_map>
#include <log/event_tag_map.h>
+#include <utils/FastStrcmp.h>
+#include <utils/RWLock.h>
+#include <private/android_logger.h>
#include "log_portability.h"
+#include "logd_reader.h"
#define OUT_TAG "EventTagMap"
-typedef std::experimental::string_view MapString;
+class MapString {
+private:
+ const std::string* alloc; // HAS-AN
+ const std::experimental::string_view str; // HAS-A
+
+public:
+ operator const std::experimental::string_view() const { return str; }
+
+ const char* data() const { return str.data(); }
+ size_t length() const { return str.length(); }
+
+ bool operator== (const MapString& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!= (const MapString& rval) const {
+ return !(*this == rval);
+ }
+
+ MapString(const char* str, size_t len) : alloc(NULL), str(str, len) { }
+ explicit MapString(const std::string& str) :
+ alloc(new std::string(str)),
+ str(alloc->data(), alloc->length()) { }
+ MapString(MapString &&rval) :
+ alloc(rval.alloc),
+ str(rval.data(), rval.length()) {
+ rval.alloc = NULL;
+ }
+ explicit MapString(const MapString &rval) :
+ alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
+ str(alloc ? alloc->data() : rval.data(), rval.length()) { }
+
+ ~MapString() { if (alloc) delete alloc; }
+};
+
+// Hash for MapString
+template <> struct std::hash<MapString>
+ : public std::unary_function<const MapString&, size_t> {
+ size_t operator()(const MapString& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(std::experimental::string_view(__t));
+ }
+};
typedef std::pair<MapString, MapString> TagFmt;
@@ -53,57 +102,125 @@
// Map
struct EventTagMap {
+# define NUM_MAPS 2
// memory-mapped source file; we get strings from here
- void* mapAddr;
- size_t mapLen;
+ void* mapAddr[NUM_MAPS];
+ size_t mapLen[NUM_MAPS];
private:
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
+ std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
+ std::unordered_map<MapString, uint32_t> Tag2Idx;
+ // protect unordered sets
+ android::RWLock rwlock;
public:
- EventTagMap() : mapAddr(NULL), mapLen(0) { }
+ EventTagMap() {
+ memset(mapAddr, 0, sizeof(mapAddr));
+ memset(mapLen, 0, sizeof(mapLen));
+ }
~EventTagMap() {
Idx2TagFmt.clear();
- if (mapAddr) {
- munmap(mapAddr, mapLen);
- mapAddr = 0;
+ TagFmt2Idx.clear();
+ Tag2Idx.clear();
+ for (size_t which = 0; which < NUM_MAPS; ++which) {
+ if (mapAddr[which]) {
+ munmap(mapAddr[which], mapLen[which]);
+ mapAddr[which] = 0;
+ }
}
}
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
const TagFmt* find(uint32_t tag) const;
+ int find(TagFmt&& tagfmt) const;
+ int find(MapString&& tag) const;
};
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
- it = Idx2TagFmt.find(tag);
- if (it != Idx2TagFmt.end()) {
- if (verbose) {
- fprintf(stderr,
- OUT_TAG ": duplicate tag entries %" PRIu32
- ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
- it->first,
- (int)it->second.first.length(), it->second.first.data(),
- (int)it->second.second.length(), it->second.second.data(),
- tag,
- (int)tagfmt.first.length(), tagfmt.first.data(),
- (int)tagfmt.second.length(), tagfmt.second.data());
+ bool ret = true;
+ static const char errorFormat[] = OUT_TAG ": duplicate tag entries %" PRIu32
+ ":%.*s:%.*s and %" PRIu32
+ ":%.*s:%.*s)\n";
+ android::RWLock::AutoWLock writeLock(rwlock);
+ {
+ std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ it = Idx2TagFmt.find(tag);
+ if (it != Idx2TagFmt.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->first,
+ (int)it->second.first.length(), it->second.first.data(),
+ (int)it->second.second.length(), it->second.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
}
- return false;
}
- Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
- return true;
+ {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ it = TagFmt2Idx.find(tagfmt);
+ if (it != TagFmt2Idx.end()) {
+ if (verbose) {
+ fprintf(stderr, errorFormat,
+ it->second,
+ (int)it->first.first.length(), it->first.first.data(),
+ (int)it->first.second.length(), it->first.second.data(),
+ tag,
+ (int)tagfmt.first.length(), tagfmt.first.data(),
+ (int)tagfmt.second.length(), tagfmt.second.data());
+ }
+ ret = false;
+ } else {
+ TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
+ }
+ }
+
+ {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ it = Tag2Idx.find(tagfmt.first);
+ if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
+ Tag2Idx.erase(it);
+ it = Tag2Idx.end();
+ }
+ if (it == Tag2Idx.end()) {
+ Tag2Idx.emplace(std::make_pair(tagfmt.first, tag));
+ }
+ }
+
+ return ret;
}
const TagFmt* EventTagMap::find(uint32_t tag) const {
std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
it = Idx2TagFmt.find(tag);
if (it == Idx2TagFmt.end()) return NULL;
return &(it->second);
}
+int EventTagMap::find(TagFmt&& tagfmt) const {
+ std::unordered_map<TagFmt, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = TagFmt2Idx.find(std::move(tagfmt));
+ if (it == TagFmt2Idx.end()) return -1;
+ return it->second;
+}
+
+int EventTagMap::find(MapString&& tag) const {
+ std::unordered_map<MapString, uint32_t>::const_iterator it;
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+ it = Tag2Idx.find(std::move(tag));
+ if (it == Tag2Idx.end()) return -1;
+ return it->second;
+}
+
// Scan one tag line.
//
// "*pData" should be pointing to the first digit in the tag number. On
@@ -157,6 +274,19 @@
fmtLen = cp - fmt;
}
+ // KISS Only report identicals if they are global
+ // Ideally we want to check if there are identicals
+ // recorded for the same uid, but recording that
+ // unused detail in our database is too burdensome.
+ bool verbose = true;
+ while ((*cp != '#') && (*cp != '\n')) ++cp;
+ if (*cp == '#') {
+ do {
+ ++cp;
+ } while (isspace(*cp) && (*cp != '\n'));
+ verbose = !!fastcmp<strncmp>(cp, "uid=", strlen("uid="));
+ }
+
while (*cp != '\n') ++cp;
#ifdef DEBUG
fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
@@ -164,24 +294,33 @@
*pData = cp;
if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
- MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+ MapString(tag, tagLen), MapString(fmt, fmtLen))), verbose)) {
return 0;
}
errno = EMLINK;
return -1;
}
+static const char* eventTagFiles[NUM_MAPS] = {
+ EVENT_TAG_MAP_FILE,
+ "/dev/event-log-tags",
+};
+
// Parse the tags out of the file.
-static int parseMapLines(EventTagMap* map) {
- char* cp = static_cast<char*>(map->mapAddr);
- size_t len = map->mapLen;
+static int parseMapLines(EventTagMap* map, size_t which) {
+ char* cp = static_cast<char*>(map->mapAddr[which]);
+ size_t len = map->mapLen[which];
char* endp = cp + len;
// insist on EOL at EOF; simplifies parsing and null-termination
if (!len || (*(endp - 1) != '\n')) {
#ifdef DEBUG
- fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
+ fprintf(stderr, OUT_TAG ": map file %zu[%zu] missing EOL on last line\n",
+ which, len);
#endif
+ if (which) { // do not propagate errors for other files
+ return 0;
+ }
errno = EINVAL;
return -1;
}
@@ -199,7 +338,9 @@
} else if (isdigit(*cp)) {
// looks like a tag; scan it out
if (scanTagLine(map, &cp, lineNum) != 0) {
- return -1;
+ if (!which || (errno != EMLINK)) {
+ return -1;
+ }
}
lineNum++; // we eat the '\n'
// leave lineStart==true
@@ -226,57 +367,87 @@
// We create a private mapping because we want to terminate the log tag
// strings with '\0'.
LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
- int save_errno;
+ EventTagMap* newTagMap;
+ off_t end[NUM_MAPS];
+ int save_errno, fd[NUM_MAPS];
+ size_t which;
- const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
- int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
+ memset(fd, -1, sizeof(fd));
+ memset(end, 0, sizeof(end));
+
+ for (which = 0; which < NUM_MAPS; ++which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fd[which] = open(tagfile, O_RDONLY | O_CLOEXEC);
+ if (fd[which] < 0) {
+ if (!which) {
+ save_errno = errno;
+ fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_errno;
+ }
+ continue;
+ }
+ end[which] = lseek(fd[which], 0L, SEEK_END);
save_errno = errno;
- fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
- tagfile, strerror(save_errno));
- errno = save_errno;
- return NULL;
- }
- off_t end = lseek(fd, 0L, SEEK_END);
- save_errno = errno;
- (void)lseek(fd, 0L, SEEK_SET);
- if (end < 0) {
- fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
- tagfile, strerror(save_errno));
- close(fd);
- errno = save_errno;
- return NULL;
+ (void)lseek(fd[which], 0L, SEEK_SET);
+ if (!which && (end[0] < 0)) {
+ fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_close;
+ }
+ if (fileName) break; // Only allow one as specified
}
- EventTagMap* newTagMap = new EventTagMap;
+ newTagMap = new EventTagMap;
if (newTagMap == NULL) {
save_errno = errno;
- close(fd);
- errno = save_errno;
- return NULL;
+ goto fail_close;
}
- newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
- MAP_PRIVATE, fd, 0);
- save_errno = errno;
- close(fd);
- fd = -1;
- if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
- fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
- tagfile, strerror(save_errno));
- delete newTagMap;
- errno = save_errno;
- return NULL;
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (fd[which] >= 0) {
+ newTagMap->mapAddr[which] = mmap(NULL, end[which],
+ which ?
+ PROT_READ :
+ PROT_READ | PROT_WRITE,
+ which ?
+ MAP_SHARED :
+ MAP_PRIVATE,
+ fd[which], 0);
+ save_errno = errno;
+ close(fd[which]);
+ fd[which] = -1;
+ if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
+ (newTagMap->mapAddr[which] != NULL)) {
+ newTagMap->mapLen[which] = end[which];
+ } else if (!which) {
+ const char* tagfile = fileName ? fileName : eventTagFiles[which];
+
+ fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+ tagfile, strerror(save_errno));
+ goto fail_unmap;
+ }
+ }
}
- newTagMap->mapLen = end;
-
- if (parseMapLines(newTagMap) != 0) {
- delete newTagMap;
- return NULL;
+ for (which = 0; which < NUM_MAPS; ++which) {
+ if (parseMapLines(newTagMap, which) != 0) {
+ delete newTagMap;
+ return NULL;
+ }
}
return newTagMap;
+
+fail_unmap:
+ save_errno = EINVAL;
+ delete newTagMap;
+fail_close:
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+fail_errno:
+ errno = save_errno;
+ return NULL;
}
// Close the map.
@@ -320,3 +491,75 @@
if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
return tagStr;
}
+
+// Look up tagname, generate one if necessary, and return a tag
+LIBLOG_ABI_PUBLIC int android_lookupEventTagNum(EventTagMap* map,
+ const char* tagname,
+ const char* format,
+ int prio) {
+ size_t len = strlen(tagname);
+ if (!len) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
+ !__android_log_is_loggable_len(prio, tagname, len,
+ __android_log_is_debuggable() ?
+ ANDROID_LOG_VERBOSE :
+ ANDROID_LOG_DEBUG)) {
+ errno = EPERM;
+ return -1;
+ }
+
+ if (!format) format="";
+ ssize_t fmtLen = strlen(format);
+ int ret = map->find(TagFmt(std::make_pair(MapString(tagname, len),
+ MapString(format, fmtLen))));
+ if (ret != -1) return ret;
+
+ // call event tag service to arrange for a new tag
+ char *buf = NULL;
+ // Can not use android::base::StringPrintf, asprintf + free instead.
+ static const char command_template[] = "getEventTag name=%s format=\"%s\"";
+ ret = asprintf(&buf, command_template, tagname, format);
+ if (ret > 0) {
+ // Add some buffer margin for an estimate of the full return content.
+ char *cp;
+ size_t size = ret - strlen(command_template) +
+ strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
+ if (size > (size_t)ret) {
+ cp = static_cast<char*>(realloc(buf, size));
+ if (cp) {
+ buf = cp;
+ } else {
+ size = ret;
+ }
+ } else {
+ size = ret;
+ }
+ // Ask event log tag service for an allocation
+ if (__send_log_msg(buf, size) >= 0) {
+ buf[size - 1] = '\0';
+ unsigned long val = strtoul(buf, &cp, 10); // return size
+ if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
+ val = strtoul(cp + 1, &cp, 10); // allocated tag number
+ if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
+ free(buf);
+ ret = val;
+ // cache
+ map->emplaceUnique(ret, TagFmt(std::make_pair(
+ MapString(std::string(tagname, len)),
+ MapString(std::string(format, fmtLen)))));
+ return ret;
+ }
+ }
+ }
+ free(buf);
+ }
+
+ // Hail Mary
+ ret = map->find(MapString(tagname, len));
+ if (ret == -1) errno = ESRCH;
+ return ret;
+}
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index 4939221..957129e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -715,6 +715,12 @@
return redirectWritev(fd, vector, count);
}
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf __unused,
+ size_t buf_size __unused)
+{
+ return -ENODEV;
+}
+
LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
const char *tag __unused,
int def)
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index 22e62ec..e57e47b 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -58,6 +58,12 @@
const char* android_lookupEventFormat_len(const EventTagMap* map,
size_t* len, unsigned int tag);
+/*
+ * Look up tagname, generate one if necessary, and return a tag
+ */
+int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
+ const char* format, int prio);
+
#ifdef __cplusplus
}
#endif
diff --git a/liblog/logd_reader.c b/liblog/logd_reader.c
index 99d7fea..ccc7da8 100644
--- a/liblog/logd_reader.c
+++ b/liblog/logd_reader.c
@@ -37,6 +37,7 @@
#include "config_read.h"
#include "log_portability.h"
+#include "logd_reader.h"
#include "logger.h"
/* branchless on many architectures. */
@@ -324,6 +325,11 @@
return ret;
}
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size)
+{
+ return send_log_msg(NULL, NULL, buf, buf_size);
+}
+
static int check_log_success(char *buf, ssize_t ret)
{
if (ret < 0) {
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
new file mode 100644
index 0000000..04c2cf2
--- /dev/null
+++ b/liblog/logd_reader.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLOG_LOGD_READER_H__
+#define _LIBLOG_LOGD_READER_H__
+
+#include <unistd.h>
+
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+LIBLOG_HIDDEN ssize_t __send_log_msg(char *buf, size_t buf_size);
+
+__END_DECLS
+
+#endif /* _LIBLOG_LOGD_READER_H__ */
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 5420f68..dc411c3 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -15,6 +15,8 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -22,6 +24,7 @@
#include <unordered_set>
+#include <android-base/file.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <private/android_logger.h>
@@ -695,7 +698,7 @@
// Keep maps around for multiple iterations
static std::unordered_set<uint32_t> set;
-static const EventTagMap* map;
+static EventTagMap* map;
static bool prechargeEventMap() {
if (map) return true;
@@ -785,3 +788,142 @@
StopBenchmarkTiming();
}
BENCHMARK(BM_lookupEventFormat);
+
+/*
+ * Measure the time it takes for android_lookupEventTagNum plus above
+ */
+static void BM_lookupEventTagNum(int iters) {
+
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+ StartBenchmarkTiming();
+ android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
+ ANDROID_LOG_UNKNOWN);
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+
+}
+BENCHMARK(BM_lookupEventTagNum);
+
+// Must be functionally identical to liblog internal __send_log_msg.
+static void send_to_control(char *buf, size_t len)
+{
+ int sock = socket_local_client("logd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (sock < 0) return;
+ size_t writeLen = strlen(buf) + 1;
+
+ ssize_t ret = TEMP_FAILURE_RETRY(write(sock, buf, writeLen));
+ if (ret <= 0) {
+ close(sock);
+ return;
+ }
+ while ((ret = read(sock, buf, len)) > 0) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
+ break;
+ }
+ len -= ret;
+ buf += ret;
+
+ struct pollfd p = {
+ .fd = sock,
+ .events = POLLIN,
+ .revents = 0
+ };
+
+ ret = poll(&p, 1, 20);
+ if ((ret <= 0) || !(p.revents & POLLIN)) {
+ break;
+ }
+ }
+ close(sock);
+}
+
+static void BM_lookupEventTagNum_logd_new(int iters) {
+ fprintf(stderr, "WARNING: "
+ "This test can cause logd to grow in size and hit DOS limiter\n");
+ // Make copies
+ static const char empty_event_log_tags[] = "# content owned by logd\n";
+ static const char dev_event_log_tags_path[] = "/dev/event-log-tags";
+ std::string dev_event_log_tags;
+ if (android::base::ReadFileToString(dev_event_log_tags_path,
+ &dev_event_log_tags) &&
+ (dev_event_log_tags.length() == 0)) {
+ dev_event_log_tags = empty_event_log_tags;
+ }
+ static const char data_event_log_tags_path[] = "/data/misc/logd/event-log-tags";
+ std::string data_event_log_tags;
+ if (android::base::ReadFileToString(data_event_log_tags_path,
+ &data_event_log_tags) &&
+ (data_event_log_tags.length() == 0)) {
+ data_event_log_tags = empty_event_log_tags;
+ }
+
+ for (int i = 0; i < iters; ++i) {
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ }
+
+ // Restore copies (logd still know about them, until crash or reboot)
+ if (dev_event_log_tags.length() &&
+ !android::base::WriteStringToFile(dev_event_log_tags,
+ dev_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", dev_event_log_tags_path);
+ }
+ if (data_event_log_tags.length() &&
+ !android::base::WriteStringToFile(data_event_log_tags,
+ data_event_log_tags_path)) {
+ fprintf(stderr, "WARNING: "
+ "failed to restore %s\n", data_event_log_tags_path);
+ }
+ fprintf(stderr, "WARNING: "
+ "Restarting logd to make it forget what we just did\n");
+ system("stop logd ; start logd");
+}
+BENCHMARK(BM_lookupEventTagNum_logd_new);
+
+static void BM_lookupEventTagNum_logd_existing(int iters) {
+ prechargeEventMap();
+
+ std::unordered_set<uint32_t>::const_iterator it = set.begin();
+
+ for (int i = 0; i < iters; ++i) {
+ size_t len;
+ const char* name = android_lookupEventTag_len(map, &len, (*it));
+ std::string Name(name, len);
+ const char* format = android_lookupEventFormat_len(map, &len, (*it));
+ std::string Format(format, len);
+
+ char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"%s\"",
+ Name.c_str(), Format.c_str());
+
+ StartBenchmarkTiming();
+ send_to_control(buffer, sizeof(buffer));
+ StopBenchmarkTiming();
+ ++it;
+ if (it == set.end()) it = set.begin();
+ }
+}
+BENCHMARK(BM_lookupEventTagNum_logd_existing);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8b9f0f0..5faf8e1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2929,3 +2929,19 @@
}
// Do not test default seconds, to allow liblog to tune freely
}
+
+TEST(liblog, android_lookupEventTagNum) {
+#ifdef __ANDROID__
+ EventTagMap* map = android_openEventTagMap(NULL);
+ EXPECT_TRUE(NULL != map);
+ std::string Name = android::base::StringPrintf("a%d", getpid());
+ int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)", ANDROID_LOG_UNKNOWN);
+ android_closeEventTagMap(map);
+ if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
+ EXPECT_NE(-1, tag);
+ EXPECT_NE(0, tag);
+ EXPECT_GT(UINT32_MAX, (unsigned)tag);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 2da9782..9211037 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -21,6 +21,7 @@
libaudit.c \
LogAudit.cpp \
LogKlog.cpp \
+ LogTags.cpp \
event.logtags
LOCAL_SHARED_LIBRARIES := \
@@ -38,12 +39,23 @@
# $(LOCAL_PATH)/$2/event.logtags)
# event_flag := $(call event_logtags,auditd)
# event_flag += $(call event_logtags,logd)
+# event_flag += $(call event_logtags,tag_def)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004 -DTAG_DEF_LOG_TAG=1005
event_flag += -DLIBLOG_LOG_TAG=1006
LOCAL_CFLAGS := -Werror $(event_flag)
include $(BUILD_EXECUTABLE)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logtagd.rc
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
+
+include $(BUILD_PREBUILT)
+
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 52c6742..74e0ea5 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -15,6 +15,7 @@
*/
#include <arpa/inet.h>
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -47,6 +48,7 @@
registerCmd(new GetStatisticsCmd(buf));
registerCmd(new SetPruneListCmd(buf));
registerCmd(new GetPruneListCmd(buf));
+ registerCmd(new GetEventTagCmd(buf));
registerCmd(new ReinitCmd());
registerCmd(new ExitCmd(this));
}
@@ -284,6 +286,41 @@
return 0;
}
+CommandListener::GetEventTagCmd::GetEventTagCmd(LogBuffer *buf) :
+ LogCommand("getEventTag"),
+ mBuf(*buf) {
+}
+
+int CommandListener::GetEventTagCmd::runCommand(SocketClient *cli,
+ int argc, char ** argv) {
+ setname();
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
+ }
+
+ const char *name = NULL;
+ const char *format = NULL;
+ for (int i = 1; i < argc; ++i) {
+ static const char _name[] = "name=";
+ if (!strncmp(argv[i], _name, strlen(_name))) {
+ name = argv[i] + strlen(_name);
+ continue;
+ }
+
+ static const char _format[] = "format=";
+ if (!strncmp(argv[i], _format, strlen(_format))) {
+ format = argv[i] + strlen(_format);
+ continue;
+ }
+ }
+
+ cli->sendMsg(package_string(mBuf.formatGetEventTag(uid,
+ name, format)).c_str());
+
+ return 0;
+}
+
CommandListener::ReinitCmd::ReinitCmd() : LogCommand("reinit") {
}
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index 5d50177..39de03b 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -61,6 +61,7 @@
LogBufferCmd(GetStatistics);
LogBufferCmd(GetPruneList);
LogBufferCmd(SetPruneList);
+ LogBufferCmd(GetEventTag);
#define LogCmd(name) \
class name##Cmd : public LogCommand { \
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 820ff64..7613c1e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -199,15 +199,13 @@
if (log_id != LOG_ID_SECURITY) {
int prio = ANDROID_LOG_INFO;
const char *tag = NULL;
- size_t len = 0;
if (log_id == LOG_ID_EVENTS) {
- tag = android::tagToName(&len, elem->getTag());
+ tag = tagToName(elem->getTag());
} else {
prio = *msg;
tag = msg + 1;
- len = strlen(tag);
}
- if (!__android_log_is_loggable_len(prio, tag, len, ANDROID_LOG_VERBOSE)) {
+ if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
pthread_mutex_lock(&mLogElementsLock);
stats.add(elem);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 932d55f..da63e12 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -27,6 +27,7 @@
#include <sysutils/SocketClient.h>
#include "LogBufferElement.h"
+#include "LogTags.h"
#include "LogTimes.h"
#include "LogStatistics.h"
#include "LogWhiteBlackList.h"
@@ -99,6 +100,8 @@
bool monotonic;
+ LogTags tags;
+
LogBufferElement* lastLoggedElements[LOG_ID_MAX];
LogBufferElement* droppedElements[LOG_ID_MAX];
void log(LogBufferElement* elem);
@@ -133,6 +136,12 @@
int initPrune(const char *cp) { return mPrune.init(cp); }
std::string formatPrune() { return mPrune.format(); }
+ std::string formatGetEventTag(uid_t uid,
+ const char *name, const char *format) {
+ return tags.formatGetEventTag(uid, name, format);
+ }
+ const char *tagToName(uint32_t tag) { return tags.tagToName(tag); }
+
// helper must be protected directly or implicitly by lock()/unlock()
const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 273150e..7e0a6b7 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -452,12 +452,11 @@
name = android::base::StringPrintf("%7u/%u",
getKey(), uid);
}
- size_t len = 0;
- const char *nameTmp = getName(len);
+ const char *nameTmp = getName();
if (nameTmp) {
name += android::base::StringPrintf(
- "%*s%.*s", (int)std::max(14 - name.length(), (size_t)1),
- "", (int)len, nameTmp);
+ "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+ "", nameTmp);
}
std::string size = android::base::StringPrintf("%zu",
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 7acef6d..777dc33 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -413,7 +413,7 @@
const uint32_t&getKey() const { return tag; }
const pid_t&getPid() const { return pid; }
const uid_t&getUid() const { return uid; }
- const char*getName(size_t &len) const { return android::tagToName(&len, tag); }
+ const char*getName() const { return android::tagToName(tag); }
inline void add(LogBufferElement *element) {
if (uid != element->getUid()) {
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 0000000..a109592
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,884 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <private/android_filesystem_config.h>
+#include <private/android_logger.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+static LogTags* logtags;
+
+const char LogTags::system_event_log_tags[] = "/system/etc/event-log-tags";
+const char LogTags::dynamic_event_log_tags[] = "/dev/event-log-tags";
+// Only for debug
+const char LogTags::debug_event_log_tags[] = "/data/misc/logd/event-log-tags";
+
+// Sniff for first uid=%d in utf8z comment string
+static uid_t sniffUid(const char* comment, const char* endp) {
+ if (!comment) return AID_ROOT;
+
+ if (*comment == '#') ++comment;
+ while ((comment < endp) && (*comment != '\n') && isspace(*comment)) ++comment;
+ static const char uid_str[] = "uid=";
+ if (((comment + strlen(uid_str)) >= endp) ||
+ fastcmp<strncmp>(comment, uid_str, strlen(uid_str)) ||
+ !isdigit(comment[strlen(uid_str)])) return AID_ROOT;
+ char* cp;
+ unsigned long Uid = strtoul(comment + 4, &cp, 10);
+ if ((cp > endp) || (Uid >= INT_MAX)) return AID_ROOT;
+
+ return Uid;
+}
+
+// Checks for file corruption, and report false if there was no need
+// to rebuild the referenced file. Failure to rebuild is only logged,
+// does not cause a return value of false.
+bool LogTags::RebuildFileEventLogTags(const char* filename, bool warn) {
+ int fd;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ if (tag2total.begin() == tag2total.end()) {
+ return false;
+ }
+
+ file2watermark_const_iterator iwater = file2watermark.find(filename);
+ if (iwater == file2watermark.end()) {
+ return false;
+ }
+
+ struct stat sb;
+ if (!stat(filename, &sb) && ((size_t)sb.st_size >= iwater->second)) {
+ return false;
+ }
+
+ // dump what we already know back into the file?
+ fd = TEMP_FAILURE_RETRY(open(filename,
+ O_WRONLY | O_TRUNC | O_CLOEXEC |
+ O_NOFOLLOW | O_BINARY));
+ if (fd >= 0) {
+ time_t now = time(NULL);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char timebuf[20];
+ size_t len = strftime(timebuf, sizeof(timebuf),
+ "%Y-%m-%d %H:%M:%S", &tm);
+ android::base::WriteStringToFd(
+ android::base::StringPrintf(
+ "# Rebuilt %.20s, content owned by logd\n", timebuf),
+ fd);
+ for (const auto& it : tag2total) {
+ android::base::WriteStringToFd(formatEntry_locked(it.first,
+ AID_ROOT),
+ fd);
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+ }
+ }
+
+ if (warn) {
+ android::prdebug(((fd < 0) ?
+ "%s failed to rebuild" :
+ "%s missing, damaged or truncated; rebuilt"),
+ filename);
+ }
+
+ if (fd >= 0) {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ struct stat sb;
+ if (!stat(filename, &sb)) file2watermark[filename] = sb.st_size;
+ }
+
+ return true;
+}
+
+void LogTags::AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name,
+ const std::string& Format,
+ const char* source, bool warn) {
+ std::string Key = Name;
+ if (Format.length()) Key += "+" + Format;
+
+ bool update = !source || !!strcmp(source, system_event_log_tags);
+ bool newOne;
+
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+
+ // unlikely except for dupes, or updates to uid list (more later)
+ if (itot != tag2total.end()) update = false;
+
+ newOne = tag2name.find(tag) == tag2name.end();
+ key2tag[Key] = tag;
+
+ if (Format.length()) {
+ if (key2tag.find(Name) == key2tag.end()) {
+ key2tag[Name] = tag;
+ }
+ tag2format[tag] = Format;
+ }
+ tag2name[tag] = Name;
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut != tag2uid.end()) {
+ if (uid == AID_ROOT) {
+ tag2uid.erase(ut);
+ update = true;
+ } else if (ut->second.find(uid) == ut->second.end()) {
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ update = true;
+ }
+ } else if (newOne && (uid != AID_ROOT)) {
+ tag2uid[tag].emplace(uid);
+ update = true;
+ }
+
+ // updatePersist -> trigger output on modified
+ // content, reset tag2total if available
+ if (update && (itot != tag2total.end())) tag2total[tag] = 0;
+ }
+
+ if (update) {
+ WritePersistEventLogTags(tag, uid, source);
+ } else if (warn && !newOne && source) {
+ // For the files, we want to report dupes.
+ android::prdebug("Multiple tag %" PRIu32 " %s %s %s", tag,
+ Name.c_str(), Format.c_str(), source);
+ }
+}
+
+// Read the event log tags file, and build up our internal database
+void LogTags::ReadFileEventLogTags(const char* filename, bool warn) {
+ bool etc = !strcmp(filename, system_event_log_tags);
+ bool debug = !etc && !strcmp(filename, debug_event_log_tags);
+
+ if (!etc) {
+ RebuildFileEventLogTags(filename, warn);
+ }
+ std::string content;
+ if (android::base::ReadFileToString(filename, &content)) {
+ char* cp = (char*) content.c_str();
+ char* endp = cp + content.length();
+
+ {
+ android::RWLock::AutoRLock writeLock(rwlock);
+
+ file2watermark[filename] = content.length();
+ }
+
+ char* lineStart = cp;
+ while (cp < endp) {
+ if (*cp == '\n') {
+ lineStart = cp;
+ } else if (lineStart) {
+ if (*cp == '#') {
+ /* comment; just scan to end */
+ lineStart = NULL;
+ } else if (isdigit(*cp)) {
+ unsigned long Tag = strtoul(cp, &cp, 10);
+ if (warn && (Tag > emptyTag)) {
+ android::prdebug("tag too large %lu", Tag);
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ if (cp >= endp) break;
+ if (*cp == '\n') continue;
+ const char* name = cp;
+ /* Determine whether it is a valid tag name [a-zA-Z0-9_] */
+ bool hasAlpha = false;
+ while ((cp < endp) && (isalnum(*cp) || (*cp == '_'))) {
+ if (!isdigit(*cp)) hasAlpha = true;
+ ++cp;
+ }
+ std::string Name(name, cp - name);
+#ifdef ALLOW_NOISY_LOGGING_OF_PROBLEM_WITH_LOTS_OF_TECHNICAL_DEBT
+ static const size_t maximum_official_tag_name_size = 24;
+ if (warn && (Name.length() > maximum_official_tag_name_size)) {
+ android::prdebug("tag name too long %s", Name.c_str());
+ }
+#endif
+ if (hasAlpha && ((cp >= endp) || (*cp == '#') || isspace(*cp))) {
+ if (Tag > emptyTag) {
+ if (*cp != '\n') lineStart = NULL;
+ continue;
+ }
+ while ((cp < endp) && (*cp != '\n') && isspace(*cp)) ++cp;
+ const char* format = cp;
+ uid_t uid = AID_ROOT;
+ while ((cp < endp) && (*cp != '\n')) {
+ if (*cp == '#') {
+ uid = sniffUid(cp, endp);
+ lineStart = NULL;
+ break;
+ }
+ ++cp;
+ }
+ while ((cp > format) && isspace(cp[-1])) {
+ --cp;
+ lineStart = NULL;
+ }
+ std::string Format(format, cp - format);
+
+ AddEventLogTags((uint32_t)Tag, uid, Name, Format,
+ filename, warn);
+ } else {
+ if (warn) {
+ android::prdebug("tag name invalid %.*s",
+ (int)(cp - name + 1), name);
+ }
+ lineStart = NULL;
+ }
+ } else if (!isspace(*cp)) break;
+ }
+ cp++;
+ }
+ } else if (warn) {
+ android::prdebug("Cannot read %s", filename);
+ }
+}
+
+// Extract a 4-byte value from a byte stream.
+static inline uint32_t get4LE(const char* msg)
+{
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(msg);
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+// Additional persistent sources for invented log tags. Read the
+// special pmsg event for log tags, and build up our internal
+// database with any found.
+void LogTags::ReadPersistEventLogTags() {
+ struct logger_list* logger_list = android_logger_list_alloc(
+ ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK,
+ 0, (pid_t)0);
+ if (!logger_list) return;
+
+ struct logger* e = android_logger_open(logger_list, LOG_ID_EVENTS);
+ struct logger* s = android_logger_open(logger_list, LOG_ID_SECURITY);
+ if (!e && !s) {
+ android_logger_list_free(logger_list);
+ return;
+ }
+
+ for (;;) {
+ struct log_msg log_msg;
+ int ret = android_logger_list_read(logger_list, &log_msg);
+ if (ret <= 0) break;
+
+ const char* msg = log_msg.msg();
+ if (!msg) continue;
+ if (log_msg.entry.len <= sizeof(uint32_t)) continue;
+ uint32_t Tag = get4LE(msg);
+ if (Tag != TAG_DEF_LOG_TAG) continue;
+ uid_t uid = (log_msg.entry.hdr_size >= sizeof(logger_entry_v4)) ?
+ log_msg.entry.uid : AID_ROOT;
+
+ std::string Name;
+ std::string Format;
+ android_log_list_element elem;
+ {
+ android_log_event_list ctx(log_msg);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_LIST) {
+ continue;
+ }
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_INT) {
+ continue;
+ }
+ Tag = elem.data.int32;
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Name = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Format = std::string(elem.data.string, elem.len);
+ elem = ctx.read();
+ }
+ if ((elem.type != EVENT_TYPE_LIST_STOP) || !elem.complete) continue;
+
+ AddEventLogTags(Tag, uid, Name, Format);
+ }
+ android_logger_list_free(logger_list);
+}
+
+LogTags::LogTags() {
+ ReadFileEventLogTags(system_event_log_tags);
+ // Following will likely fail on boot, but is required if logd restarts
+ ReadFileEventLogTags(dynamic_event_log_tags, false);
+ if (__android_log_is_debuggable()) {
+ ReadFileEventLogTags(debug_event_log_tags, false);
+ }
+ ReadPersistEventLogTags();
+
+ logtags = this;
+}
+
+// Converts an event tag into a name
+const char* LogTags::tagToName(uint32_t tag) const {
+ tag2name_const_iterator it;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ it = tag2name.find(tag);
+ if ((it == tag2name.end()) || (it->second.length() == 0)) return NULL;
+
+ return it->second.c_str();
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This must be a pure reader to our database, as everything else is
+// guaranteed single-threaded except this access point which is
+// asynchonous and can be multithreaded and thus rentrant. The
+// object's rwlock is only used to guarantee atomic access to the
+// unordered_map to prevent corruption, with a requirement to be a
+// low chance of contention for this call. If we end up changing
+// this algorithm resulting in write, then we should use a different
+// lock than the object's rwlock to protect groups of associated
+// actions.
+const char* android::tagToName(uint32_t tag) {
+ LogTags* me = logtags;
+
+ if (!me) return NULL;
+ me->WritePmsgEventLogTags(tag);
+ return me->tagToName(tag);
+}
+
+// Prototype in LogUtils.h allowing external access to our database.
+//
+// This only works on userdebug and eng devices to re-read the
+// /data/misc/logd/event-log-tags file right after /data is mounted.
+// The operation is near to boot and should only happen once. There
+// are races associated with its use since it can trigger a Rebuild
+// of the file, but that is a can-not-happen since the file was not
+// read yet. More dangerous if called later, but if all is well it
+// should just skip over everything and not write any new entries.
+void android::ReReadEventLogTags() {
+ LogTags* me = logtags;
+
+ if (me && __android_log_is_debuggable()) {
+ me->ReadFileEventLogTags(me->debug_event_log_tags);
+ }
+}
+
+// converts an event tag into a format
+const char* LogTags::tagToFormat(uint32_t tag) const {
+ tag2format_const_iterator iform;
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ iform = tag2format.find(tag);
+ if (iform == tag2format.end()) return NULL;
+
+ return iform->second.c_str();
+}
+
+// converts a name into an event tag
+uint32_t LogTags::nameToTag(const char* name) const {
+ uint32_t ret = emptyTag;
+
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+
+ android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
+
+ key2tag_const_iterator ik = key2tag.find(std::string(name));
+ if (ik != key2tag.end()) ret = ik->second;
+
+ return ret;
+}
+
+// Caller must perform locks, can be under reader (for pre-check) or
+// writer lock. We use this call to invent a new deterministically
+// random tag, unique is cleared if no conflicts. If format is NULL,
+// we are in readonly mode.
+uint32_t LogTags::nameToTag_locked(const std::string& name,
+ const char* format,
+ bool& unique) {
+ key2tag_const_iterator ik;
+
+ bool write = format != NULL;
+ unique = write;
+
+ if (!write) {
+ // Bug: Only works for a single entry, we can have multiple entries,
+ // one for each format, so we find first entry recorded, or entry with
+ // no format associated with it.
+ ik = key2tag.find(name);
+ if (ik == key2tag.end()) return emptyTag;
+ return ik->second;
+ }
+
+ std::string Key(name);
+ if (*format) Key += std::string("+") + format;
+
+ ik = key2tag.find(Key);
+ if (ik != key2tag.end()) {
+ unique = false;
+ return ik->second;
+ }
+
+ size_t Hash = key2tag.hash_function()(Key);
+ uint32_t Tag = Hash;
+ // This sets an upper limit on the conflics we are allowed to deal with.
+ for (unsigned i = 0; i < 256; ) {
+ tag2name_const_iterator it = tag2name.find(Tag);
+ if (it == tag2name.end()) return Tag;
+ std::string localKey(it->second);
+ tag2format_const_iterator iform = tag2format.find(Tag);
+ if ((iform == tag2format.end()) && iform->second.length()) {
+ localKey += "+" + iform->second;
+ }
+ unique = !!it->second.compare(localKey);
+ if (!unique) return Tag; // unlikely except in a race
+
+ ++i;
+ // Algorithm to convert hash to next tag
+ if (i < 32) {
+ Tag = (Hash >> i);
+ // size_t is 32 bits, or upper word zero, rotate
+ if ((sizeof(Hash) <= 4) ||
+ ((Hash & (uint64_t(-1LL) << 32)) == 0)) {
+ Tag |= Hash << (32 - i);
+ }
+ } else {
+ Tag = Hash + i - 31;
+ }
+ }
+ return emptyTag;
+}
+
+static int openFile(const char* name, int mode, bool warning) {
+ int fd = TEMP_FAILURE_RETRY(open(name, mode));
+ if ((fd < 0) && warning) {
+ android::prdebug("Failed open %s (%d)", name, errno);
+ }
+ return fd;
+}
+
+void LogTags::WritePmsgEventLogTags(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot == tag2total.end()) return; // source is a static entry
+
+ size_t lastTotal = itot->second;
+
+ // Every 16K (half the smallest configurable pmsg buffer size) record
+ static const size_t rate_to_pmsg = 16 * 1024;
+ if (lastTotal && ((android::sizesTotal() - lastTotal) < rate_to_pmsg)) {
+ return;
+ }
+
+ static int pmsg_fd = -1;
+ if (pmsg_fd < 0) {
+ pmsg_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC));
+ // unlikely, but deal with partners with borken pmsg
+ if (pmsg_fd < 0) return;
+ }
+
+ std::string Name = tag2name[tag];
+ tag2format_const_iterator iform = tag2format.find(tag);
+ std::string Format = (iform != tag2format.end()) ? iform->second : "";
+
+ __android_log_event_list ctx(TAG_DEF_LOG_TAG);
+ ctx << tag << Name << Format;
+ std::string buffer(ctx);
+ if (buffer.length() <= 0) return; // unlikely
+
+ /*
+ * struct {
+ * // what we provide to pstore
+ * android_pmsg_log_header_t pmsgHeader;
+ * // what we provide to file
+ * android_log_header_t header;
+ * // caller provides
+ * union {
+ * struct {
+ * char prio;
+ * char payload[];
+ * } string;
+ * struct {
+ * uint32_t tag
+ * char payload[];
+ * } binary;
+ * };
+ * };
+ */
+
+ struct timespec ts;
+ clock_gettime(android_log_clockid(), &ts);
+
+ android_log_header_t header = {
+ .id = LOG_ID_EVENTS,
+ .tid = (uint16_t)gettid(),
+ .realtime.tv_sec = (uint32_t)ts.tv_sec,
+ .realtime.tv_nsec = (uint32_t)ts.tv_nsec,
+ };
+
+ uint32_t outTag = TAG_DEF_LOG_TAG;
+ outTag = get4LE((const char*)&outTag);
+
+ android_pmsg_log_header_t pmsgHeader = {
+ .magic = LOGGER_MAGIC,
+ .len = (uint16_t)(sizeof(pmsgHeader) + sizeof(header) +
+ sizeof(outTag) + buffer.length()),
+ .uid = (uint16_t)AID_ROOT,
+ .pid = (uint16_t)getpid(),
+ };
+
+ struct iovec Vec[] = {
+ { (unsigned char*)&pmsgHeader, sizeof(pmsgHeader) },
+ { (unsigned char*)&header, sizeof(header) },
+ { (unsigned char*)&outTag, sizeof(outTag) },
+ { (unsigned char*)const_cast<char*>(buffer.data()), buffer.length() }
+ };
+
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else if (uid != AID_ROOT) {
+ pmsgHeader.uid = (uint16_t)uid;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ } else {
+ for (auto &it : ut->second) {
+ pmsgHeader.uid = (uint16_t)it;
+ TEMP_FAILURE_RETRY(writev(pmsg_fd, Vec, arraysize(Vec)));
+ }
+ }
+}
+
+void LogTags::WriteDynamicEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ int fd = openFile(dynamic_event_log_tags, mode, true);
+ if (fd < 0) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(dynamic_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[dynamic_event_log_tags] = size + ret.length();
+}
+
+void LogTags::WriteDebugEventLogTags(uint32_t tag, uid_t uid) {
+ static const int mode = O_WRONLY | O_APPEND |
+ O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+
+ static bool one = true;
+ int fd = openFile(debug_event_log_tags, mode, one);
+ one = fd >= 0;
+ if (!one) return;
+
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ std::string ret = formatEntry_locked(tag, uid, false);
+ android::base::WriteStringToFd(ret, fd);
+ TEMP_FAILURE_RETRY(close(fd));
+
+ size_t size = 0;
+ file2watermark_const_iterator iwater;
+
+ iwater = file2watermark.find(debug_event_log_tags);
+ if (iwater != file2watermark.end()) size = iwater->second;
+
+ file2watermark[debug_event_log_tags] = size + ret.length();
+}
+
+// How we maintain some runtime or reboot stickiness
+void LogTags::WritePersistEventLogTags(uint32_t tag,
+ uid_t uid, const char* source) {
+ // very unlikely
+ bool etc = source && !strcmp(source, system_event_log_tags);
+ if (etc) return;
+
+ bool dynamic = source && !strcmp(source, dynamic_event_log_tags);
+ bool debug = (!dynamic &&
+ source &&
+ !strcmp(source, debug_event_log_tags)) ||
+ !__android_log_is_debuggable();
+
+ WritePmsgEventLogTags(tag, uid);
+
+ size_t lastTotal = 0;
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ tag2total_const_iterator itot = tag2total.find(tag);
+ if (itot != tag2total.end()) lastTotal = itot->second;
+ }
+
+ if (lastTotal == 0) { // denotes first time for this one
+ if (!dynamic || !RebuildFileEventLogTags(dynamic_event_log_tags)) {
+ WriteDynamicEventLogTags(tag, uid);
+ }
+
+ if (!debug && !RebuildFileEventLogTags(debug_event_log_tags)) {
+ WriteDebugEventLogTags(tag, uid);
+ }
+ }
+
+ lastTotal = android::sizesTotal();
+ if (!lastTotal) ++lastTotal;
+
+ // record totals for next watermark.
+ android::RWLock::AutoWLock writeLock(rwlock);
+ tag2total[tag] = lastTotal;
+}
+
+// nameToTag converts a name into an event tag. If format is NULL, then we
+// are in readonly mode.
+uint32_t LogTags::nameToTag(uid_t uid, const char* name, const char* format) {
+ std::string Name = std::string(name);
+ bool write = format != NULL;
+ bool updateUid = uid != AID_ROOT;
+ bool updateFormat = format && *format;
+ bool unique;
+ uint32_t Tag;
+
+ {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ Tag = nameToTag_locked(Name, format, unique);
+ if (updateUid && (Tag != emptyTag) && !unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ if ((ut != tag2uid.end()) &&
+ (ut->second.find(uid) == ut->second.end())) {
+ unique = write; // write passthrough to update uid counts
+ if (!write) Tag = emptyTag; // deny read access
+ }
+ } else {
+ unique = write && (ut != tag2uid.end());
+ }
+ }
+ }
+
+ if (Tag == emptyTag) return Tag;
+ WritePmsgEventLogTags(Tag, uid); // record references periodically
+ if (!unique) return Tag;
+
+ bool updateWrite = false;
+ bool updateTag;
+
+ // Special case of AddEventLogTags, checks per-uid counter which makes
+ // no sense there, and is also optimized somewhat to reduce write times.
+ {
+ android::RWLock::AutoWLock writeLock(rwlock);
+
+ // double check after switch from read lock to write lock for Tag
+ updateTag = tag2name.find(Tag) == tag2name.end();
+ // unlikely, either update, race inviting conflict or multiple uids
+ if (!updateTag) {
+ Tag = nameToTag_locked(Name, format, unique);
+ if (Tag == emptyTag) return Tag;
+ // is it multiple uid's setting this value
+ if (!unique) {
+ tag2uid_const_iterator ut = tag2uid.find(Tag);
+ if (updateUid) {
+ // Add it to the uid list
+ if ((ut == tag2uid.end()) ||
+ (ut->second.find(uid) != ut->second.end())) return Tag;
+ const_cast<uid_list&>(ut->second).emplace(uid);
+ updateWrite = true;
+ } else {
+ if (ut == tag2uid.end()) return Tag;
+ // (system) adding a global one, erase the uid list
+ tag2uid.erase(ut);
+ updateWrite = true;
+ }
+ }
+ }
+
+ // Update section
+ size_t count;
+ if (updateUid) {
+ count = 0;
+ uid2count_const_iterator ci = uid2count.find(uid);
+ if (ci != uid2count.end()) {
+ count = ci->second;
+ if (count >= max_per_uid) {
+ if (!updateWrite) return emptyTag;
+ // If we are added to the per-Uid perms, leak the Tag
+ // if it already exists.
+ updateUid = false;
+ updateTag = false;
+ updateFormat = false;
+ }
+ }
+ }
+
+ // updateWrite -> trigger output on modified content, reset tag2total
+ // also sets static to dynamic entries if they are alterred,
+ // only occurs if they have a uid, and runtime adds another uid.
+ if (updateWrite) tag2total[Tag] = 0;
+
+ if (updateTag) {
+ // mark as a dynamic entry, but do not upset current total counter
+ tag2total_const_iterator itot = tag2total.find(Tag);
+ if (itot == tag2total.end()) tag2total[Tag] = 0;
+
+ if (*format) {
+ key2tag[Name + "+" + format] = Tag;
+ if (key2tag.find(Name) == key2tag.end()) key2tag[Name] = Tag;
+ } else {
+ key2tag[Name] = Tag;
+ }
+ tag2name[Tag] = Name;
+ }
+ if (updateFormat) tag2format[Tag] = format;
+
+ if (updateUid) {
+ tag2uid[Tag].emplace(uid);
+ uid2count[uid] = count + 1;
+ }
+ }
+
+ if (updateTag || updateFormat || updateWrite) {
+ WritePersistEventLogTags(Tag, uid);
+ }
+
+ return Tag;
+}
+
+std::string LogTags::formatEntry(uint32_t tag, uid_t uid,
+ const char* name,
+ const char* format) {
+ if (!format || !format[0]) {
+ return android::base::StringPrintf("%" PRIu32 "\t%s\n", tag, name);
+ }
+ size_t len = (strlen(name) + 7) / 8;
+ static const char tabs[] = "\t\t\t";
+ if (len > strlen(tabs)) len = strlen(tabs);
+ std::string Uid;
+ if (uid != AID_ROOT) Uid = android::base::StringPrintf(" # uid=%u", uid);
+ return android::base::StringPrintf("%" PRIu32 "\t%s%s\t%s%s\n",
+ tag, name, &tabs[len], format,
+ Uid.c_str());
+}
+
+std::string LogTags::formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate) {
+ const char* name = tag2name[tag].c_str();
+
+ const char* format = "";
+ tag2format_const_iterator iform = tag2format.find(tag);
+ if (iform != tag2format.end()) format = iform->second.c_str();
+
+ // Access permission test, do not report dynamic entries
+ // that do not belong to us.
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if (ut == tag2uid.end()) {
+ return formatEntry(tag, AID_ROOT, name, format);
+ }
+ if (uid != AID_ROOT) {
+ if (authenticate && (ut->second.find(uid) == ut->second.end())) {
+ return std::string("");
+ }
+ return formatEntry(tag, uid, name, format);
+ }
+
+ // Show all, one for each registered uid (we are group root)
+ std::string ret;
+ for (auto &it : ut->second) {
+ ret += formatEntry(tag, it, name, format);
+ }
+ return ret;
+}
+
+std::string LogTags::formatGetEventTag(uid_t uid,
+ const char* name, const char* format) {
+ bool all = name && (name[0] == '*') && !name[1];
+ bool list = !name || all;
+ std::string ret;
+
+ if (!list) {
+ // switch to read entry only if format == "*"
+ if (format && (format[0] == '*') && !format[1]) format = NULL;
+
+ // WAI: for null format, only works for a single entry, we can have
+ // multiple entries, one for each format, so we find first entry
+ // recorded, or entry with no format associated with it.
+ // We may desire to print all that match the name, but we did not
+ // add a mapping table for that and the cost is too high.
+ uint32_t tag = nameToTag(uid, name, format);
+ if (tag == emptyTag) return std::string("-1 ESRCH");
+ if (uid == AID_ROOT) {
+ android::RWLock::AutoRLock readLock(rwlock);
+
+ // first uid in list so as to manufacture an accurate reference
+ tag2uid_const_iterator ut = tag2uid.find(tag);
+ if ((ut != tag2uid.end()) &&
+ (ut->second.begin() != ut->second.end())) {
+ uid = *(ut->second.begin());
+ }
+ }
+ ret = formatEntry(tag, uid, name, format ?: tagToFormat(tag));
+ if (!ret.length()) return std::string("-1 ESRCH");
+ return ret;
+ }
+
+ android::RWLock::AutoRLock readLock(rwlock);
+ if (all) {
+ // everything under the sun
+ for (const auto& it : tag2name) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ } else {
+ // set entries are dynamic
+ for (const auto& it : tag2total) {
+ ret += formatEntry_locked(it.first, uid);
+ }
+ }
+ return ret;
+}
diff --git a/logd/LogTags.h b/logd/LogTags.h
new file mode 100644
index 0000000..37a6d96
--- /dev/null
+++ b/logd/LogTags.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOGD_LOG_TAGS_H__
+#define _LOGD_LOG_TAGS_H__
+
+#include <unordered_map>
+#include <unordered_set>
+#include <string>
+
+#include <utils/RWLock.h>
+
+class LogTags {
+ // This lock protects all the unordered_map accesses below. It
+ // is a reader/writer lock so that contentions are kept to a
+ // minimum since writes are rare, even administratably when
+ // reads are extended. Resist the temptation to use the writer
+ // lock to protect anything outside the following unordered_maps
+ // as that would increase the reader contentions. Use a separate
+ // mutex to protect the other entities.
+ android::RWLock rwlock;
+
+ // key is Name + "+" + Format
+ std::unordered_map<std::string, uint32_t> key2tag;
+ typedef std::unordered_map<std::string, uint32_t>::const_iterator key2tag_const_iterator;
+
+ // Allows us to manage access permissions based on uid registrants
+ // Global entries are specifically erased.
+ typedef std::unordered_set<uid_t> uid_list;
+ std::unordered_map<uint32_t, uid_list> tag2uid;
+ typedef std::unordered_map<uint32_t, uid_list>::const_iterator tag2uid_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2name;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2name_const_iterator;
+
+ std::unordered_map<uint32_t, std::string> tag2format;
+ typedef std::unordered_map<uint32_t, std::string>::const_iterator tag2format_const_iterator;
+
+ static const size_t max_per_uid = 256; // Put a cap on the tags per uid
+ std::unordered_map<uid_t, size_t> uid2count;
+ typedef std::unordered_map<uid_t, size_t>::const_iterator uid2count_const_iterator;
+
+ // Dynamic entries are assigned
+ std::unordered_map<uint32_t, size_t> tag2total;
+ typedef std::unordered_map<uint32_t, size_t>::const_iterator tag2total_const_iterator;
+
+ // emplace unique tag
+ uint32_t nameToTag(uid_t uid, const char* name, const char* format);
+ // find unique or associated tag
+ uint32_t nameToTag_locked(const std::string& name, const char* format, bool &unique);
+
+ // Record expected file watermarks to detect corruption.
+ std::unordered_map<std::string, size_t> file2watermark;
+ typedef std::unordered_map<std::string, size_t>::const_iterator file2watermark_const_iterator;
+
+ void ReadPersistEventLogTags();
+
+ // format helpers
+ // format a single entry, does not need object data
+ static std::string formatEntry(uint32_t tag, uid_t uid,
+ const char* name, const char* format);
+ // caller locks, database lookup, authenticate against uid
+ std::string formatEntry_locked(uint32_t tag, uid_t uid,
+ bool authenticate = true);
+
+ bool RebuildFileEventLogTags(const char* filename, bool warn = true);
+
+ void AddEventLogTags(uint32_t tag, uid_t uid,
+ const std::string& Name, const std::string& Format,
+ const char* source = NULL, bool warn = false);
+
+ void WriteDynamicEventLogTags(uint32_t tag, uid_t uid);
+ void WriteDebugEventLogTags(uint32_t tag, uid_t uid);
+ // push tag details to persistent storage
+ void WritePersistEventLogTags(uint32_t tag,
+ uid_t uid = AID_ROOT,
+ const char* source = NULL);
+
+ static const uint32_t emptyTag = uint32_t(-1);
+
+public:
+
+ static const char system_event_log_tags[];
+ static const char dynamic_event_log_tags[];
+ // Only for userdebug and eng
+ static const char debug_event_log_tags[];
+
+ LogTags();
+
+ void WritePmsgEventLogTags(uint32_t tag, uid_t uid = AID_ROOT);
+ void ReadFileEventLogTags(const char* filename, bool warn = true);
+
+ // reverse lookup from tag
+ const char* tagToName(uint32_t tag) const;
+ const char* tagToFormat(uint32_t tag) const;
+ // find associated tag
+ uint32_t nameToTag(const char* name) const;
+
+ // emplace tag if necessary, provide event-log-tag formated output in string
+ std::string formatGetEventTag(uid_t uid,
+ const char* name,
+ const char* format);
+};
+
+#endif // _LOGD_LOG_TAGS_H__
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 70f24e4..f044b27 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -39,8 +39,9 @@
char *pidToName(pid_t pid);
char *tidToName(pid_t tid);
-// Furnished in main.cpp. Thread safe.
-const char *tagToName(size_t *len, uint32_t tag);
+// Furnished in LogTags.cpp. Thread safe.
+const char *tagToName(uint32_t tag);
+void ReReadEventLogTags();
// Furnished by LogKlog.cpp.
const char* strnstr(const char* s, size_t len, const char* needle);
diff --git a/logd/event.logtags b/logd/event.logtags
index 0d24df0..39063a9 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -35,3 +35,4 @@
1003 auditd (avc|3)
1004 chatty (dropped|3)
+1005 tag_def (tag|1),(name|3),(format|3)
diff --git a/logd/logd.rc b/logd/logd.rc
index 54349dd..ee89b83 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -14,3 +14,10 @@
user logd
group logd
writepid /dev/cpuset/system-background/tasks
+
+on fs
+ write /dev/event-log-tags "# content owned by logd
+"
+ chown logd logd /dev/event-log-tags
+ chmod 0644 /dev/event-log-tags
+ restorecon /dev/event-log-tags
diff --git a/logd/logtagd.rc b/logd/logtagd.rc
new file mode 100644
index 0000000..46aa8c1
--- /dev/null
+++ b/logd/logtagd.rc
@@ -0,0 +1,9 @@
+#
+# logtagd event log tag service (debug only)
+#
+on post-fs-data
+ mkdir /data/misc/logd 0700 logd log
+ write /data/misc/logd/event-log-tags ""
+ chown logd log /data/misc/logd/event-log-tags
+ chmod 0600 /data/misc/logd/event-log-tags
+ restorecon /data/misc/logd/event-log-tags
diff --git a/logd/main.cpp b/logd/main.cpp
index 5878f15..2551f2e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -244,7 +244,7 @@
// anything else, we have even lesser privileges and accept our fate. Not
// worth checking for error returns setting this thread's privileges.
(void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
- (void)setuid(AID_LOGD); // access to everything logd.
+ (void)setuid(AID_LOGD); // access to everything logd, eg /data/misc/logd
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
@@ -271,6 +271,7 @@
logBuf->init();
logBuf->initPrune(NULL);
}
+ android::ReReadEventLogTags();
}
return NULL;
@@ -304,24 +305,6 @@
sem_post(&reinit);
}
-// tagToName converts an events tag into a name
-const char *android::tagToName(size_t *len, uint32_t tag) {
- static const EventTagMap *map;
-
- if (!map) {
- sem_wait(&sem_name);
- if (!map) {
- map = android_openEventTagMap(NULL);
- }
- sem_post(&sem_name);
- if (!map) {
- if (len) len = 0;
- return NULL;
- }
- }
- return android_lookupEventTag_len(map, len, tag);
-}
-
static void readDmesg(LogAudit *al, LogKlog *kl) {
if (!al && !kl) {
return;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 2a6cdc8..adf583b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -39,12 +39,8 @@
#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
#include "../LogReader.h" // pickup LOGD_SNDTIMEO
-/*
- * returns statistics
- */
-static void my_android_logger_get_statistics(char *buf, size_t len)
+static void send_to_control(char* buf, size_t len)
{
- snprintf(buf, len, "getStatistics 0 1 2 3 4");
int sock = socket_local_client("logd",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
@@ -52,7 +48,7 @@
if (write(sock, buf, strlen(buf) + 1) > 0) {
ssize_t ret;
while ((ret = read(sock, buf, len)) > 0) {
- if ((size_t)ret == len) {
+ if (((size_t)ret == len) || (len < PAGE_SIZE)) {
break;
}
len -= ret;
@@ -74,6 +70,15 @@
}
}
+/*
+ * returns statistics
+ */
+static void my_android_logger_get_statistics(char *buf, size_t len)
+{
+ snprintf(buf, len, "getStatistics 0 1 2 3 4");
+ send_to_control(buf, len);
+}
+
static void alloc_statistics(char **buffer, size_t *length)
{
size_t len = 8192;
@@ -816,6 +821,44 @@
close(fd);
}
+TEST(logd, getEventTag_list) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ snprintf(buffer, sizeof(buffer), "getEventTag name=*");
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 4096);
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, getEventTag_newentry) {
+#ifdef __ANDROID__
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ log_time now(CLOCK_MONOTONIC);
+ char name[64];
+ snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
+ snprintf(buffer, sizeof(buffer),
+ "getEventTag name=%s format=\"(new|1)\"", name);
+ send_to_control(buffer, sizeof(buffer));
+ buffer[sizeof(buffer) - 1] = '\0';
+ char *cp;
+ long ret = strtol(buffer, &cp, 10);
+ EXPECT_GT(ret, 16);
+ EXPECT_TRUE(strstr(buffer, "\t(new|1)") != NULL);
+ EXPECT_TRUE(strstr(buffer, name) != NULL);
+ // ToDo: also look for this in /data/misc/logd/event-log-tags and
+ // /dev/event-log-tags.
+#else
+ GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
static inline int32_t get4LE(const char* src)
{
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);