summaryrefslogtreecommitdiff
path: root/logd/LogTags.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'logd/LogTags.cpp')
-rw-r--r--logd/LogTags.cpp903
1 files changed, 903 insertions, 0 deletions
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
new file mode 100644
index 000000000..0cc7886ea
--- /dev/null
+++ b/logd/LogTags.cpp
@@ -0,0 +1,903 @@
+/*
+ * 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/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <log/log_event_list.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+#include "LogTags.h"
+#include "LogUtils.h"
+
+using android::base::make_scope_guard;
+
+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(nullptr);
+ 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);
+ }
+ 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 = nullptr;
+ } 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 = nullptr;
+ 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 = nullptr;
+ break;
+ }
+ ++cp;
+ }
+ while ((cp > format) && isspace(cp[-1])) {
+ --cp;
+ lineStart = nullptr;
+ }
+ 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 = nullptr;
+ }
+ } 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.uid;
+
+ std::string Name;
+ std::string Format;
+ android_log_list_element elem;
+ {
+ auto ctx = create_android_log_parser(log_msg.msg() + sizeof(uint32_t),
+ log_msg.entry.len - sizeof(uint32_t));
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_LIST) {
+ continue;
+ }
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_INT) {
+ continue;
+ }
+ Tag = elem.data.int32;
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Name = std::string(elem.data.string, elem.len);
+ elem = android_log_read_next(ctx);
+ if (elem.type != EVENT_TYPE_STRING) {
+ continue;
+ }
+ Format = std::string(elem.data.string, elem.len);
+ elem = android_log_read_next(ctx);
+ }
+ 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 nullptr;
+
+ 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 nullptr;
+ 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 nullptr;
+
+ 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 != nullptr;
+ 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 : "";
+
+ auto ctx = create_android_logger(TAG_DEF_LOG_TAG);
+ auto guard = make_scope_guard([&ctx]() { android_log_destroy(&ctx); });
+ if (android_log_write_int32(ctx, static_cast<int32_t>(tag) < 0) ||
+ android_log_write_string8_len(ctx, Name.c_str(), Name.size()) < 0 ||
+ android_log_write_string8_len(ctx, Format.c_str(), Format.size()) < 0) {
+ return;
+ }
+
+ const char* cp = nullptr;
+ ssize_t len = android_log_write_list_buffer(ctx, &cp);
+
+ if (len <= 0 || cp == nullptr) {
+ return;
+ }
+
+ std::string buffer(cp, len);
+
+ /*
+ * 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);
+ 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);
+ 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 != nullptr;
+ 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::formatEntry(uint32_t tag, uid_t uid) {
+ android::RWLock::AutoRLock readLock(rwlock);
+ return formatEntry_locked(tag, uid);
+}
+
+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 = nullptr;
+
+ // 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;
+}