From 1b8e5a6b14ca850920e19b3dfae41e6494475c1a Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Fri, 13 Feb 2009 12:57:54 -0800 Subject: auto import from //branches/cupcake/...@131421 --- liblog/logd_write.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'liblog') diff --git a/liblog/logd_write.c b/liblog/logd_write.c index e40d2ce..80867d1 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #define LOG_BUF_SIZE 1024 -- cgit v1.1 From e54eebbf1a908d65ee8cf80bab62821c05666d70 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:29:04 -0800 Subject: auto import from //depot/cupcake/@135843 --- liblog/Android.mk | 72 ---- liblog/event_tag_map.c | 438 --------------------- liblog/fake_log_device.c | 677 --------------------------------- liblog/logd_write.c | 230 ----------- liblog/logprint.c | 972 ----------------------------------------------- 5 files changed, 2389 deletions(-) delete mode 100644 liblog/Android.mk delete mode 100644 liblog/event_tag_map.c delete mode 100644 liblog/fake_log_device.c delete mode 100644 liblog/logd_write.c delete mode 100644 liblog/logprint.c (limited to 'liblog') diff --git a/liblog/Android.mk b/liblog/Android.mk deleted file mode 100644 index 0eec87f..0000000 --- a/liblog/Android.mk +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright (C) 2008 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(my-dir) -include $(CLEAR_VARS) - -liblog_sources := logd_write.c - -# some files must not be compiled when building against Mingw -# they correspond to features not used by our host development tools -# which are also hard or even impossible to port to native Win32 -WITH_MINGW := -ifeq ($(HOST_OS),windows) - ifeq ($(strip $(USE_CYGWIN)),) - WITH_MINGW := true - endif -endif -# USE_MINGW is defined when we build against Mingw on Linux -ifneq ($(strip $(USE_MINGW)),) - WITH_MINGW := true -endif - -ifndef WITH_MINGW - liblog_sources += \ - logprint.c \ - event_tag_map.c -endif - -liblog_host_sources := $(liblog_sources) fake_log_device.c - -# Static library for host -# ======================================================== -LOCAL_MODULE := liblog -LOCAL_SRC_FILES := $(liblog_host_sources) -LOCAL_LDLIBS := -lpthread -LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -include $(BUILD_HOST_STATIC_LIBRARY) - -ifeq ($(TARGET_SIMULATOR),true) - # Shared library for simulator - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_host_sources) - LOCAL_LDLIBS := -lpthread - LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 - include $(BUILD_SHARED_LIBRARY) -else # !sim - # Shared and static library for target - # ======================================================== - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_SRC_FILES := $(liblog_sources) - include $(BUILD_STATIC_LIBRARY) - - include $(CLEAR_VARS) - LOCAL_MODULE := liblog - LOCAL_WHOLE_STATIC_LIBRARIES := liblog - include $(BUILD_SHARED_LIBRARY) -endif # !sim diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c deleted file mode 100644 index e70754e..0000000 --- a/liblog/event_tag_map.c +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2007 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 "cutils/event_tag_map.h" -#include "cutils/log.h" - -#include -#include -#include -#include -#include -#include - -#define OUT_TAG "EventTagMap" - -/* - * Single entry. - */ -typedef struct EventTag { - unsigned int tagIndex; - const char* tagStr; -} EventTag; - -/* - * Map. - */ -struct EventTagMap { - /* memory-mapped source file; we get strings from here */ - void* mapAddr; - size_t mapLen; - - /* array of event tags, sorted numerically by tag index */ - EventTag* tagArray; - int numTags; -}; - -/* fwd */ -static int processFile(EventTagMap* map); -static int countMapLines(const EventTagMap* map); -static int parseMapLines(EventTagMap* map); -static int scanTagLine(char** pData, EventTag* tag, int lineNum); -static int sortTags(EventTagMap* map); -static void dumpTags(const EventTagMap* map); - - -/* - * Open the map file and allocate a structure to manage it. - * - * We create a private mapping because we want to terminate the log tag - * strings with '\0'. - */ -EventTagMap* android_openEventTagMap(const char* fileName) -{ - EventTagMap* newTagMap; - off_t end; - int fd = -1; - - newTagMap = calloc(1, sizeof(EventTagMap)); - if (newTagMap == NULL) - return NULL; - - fd = open(fileName, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "%s: unable to open map '%s': %s\n", - OUT_TAG, fileName, strerror(errno)); - goto fail; - } - - end = lseek(fd, 0L, SEEK_END); - (void) lseek(fd, 0L, SEEK_SET); - if (end < 0) { - fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName); - goto fail; - } - - newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, - fd, 0); - if (newTagMap->mapAddr == MAP_FAILED) { - fprintf(stderr, "%s: mmap(%s) failed: %s\n", - OUT_TAG, fileName, strerror(errno)); - goto fail; - } - newTagMap->mapLen = end; - - if (processFile(newTagMap) != 0) - goto fail; - - return newTagMap; - -fail: - android_closeEventTagMap(newTagMap); - if (fd >= 0) - close(fd); - return NULL; -} - -/* - * Close the map. - */ -void android_closeEventTagMap(EventTagMap* map) -{ - if (map == NULL) - return; - - munmap(map->mapAddr, map->mapLen); - free(map); -} - -/* - * Look up an entry in the map. - * - * The entries are sorted by tag number, so we can do a binary search. - */ -const char* android_lookupEventTag(const EventTagMap* map, int tag) -{ - int hi, lo, mid; - - lo = 0; - hi = map->numTags-1; - - while (lo <= hi) { - int cmp; - - mid = (lo+hi)/2; - cmp = map->tagArray[mid].tagIndex - tag; - if (cmp < 0) { - /* tag is bigger */ - lo = mid + 1; - } else if (cmp > 0) { - /* tag is smaller */ - hi = mid - 1; - } else { - /* found */ - return map->tagArray[mid].tagStr; - } - } - - return NULL; -} - - - -/* - * Determine whether "c" is a whitespace char. - */ -static inline int isCharWhitespace(char c) -{ - return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); -} - -/* - * Determine whether "c" is a valid tag char. - */ -static inline int isCharValidTag(char c) -{ - return ((c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || - (c == '_')); -} - -/* - * Determine whether "c" is a valid decimal digit. - */ -static inline int isCharDigit(char c) -{ - return (c >= '0' && c <= '9'); -} - - -/* - * Crunch through the file, parsing the contents and creating a tag index. - */ -static int processFile(EventTagMap* map) -{ - EventTag* tagArray = NULL; - - /* get a tag count */ - map->numTags = countMapLines(map); - if (map->numTags < 0) - return -1; - - //printf("+++ found %d tags\n", map->numTags); - - /* allocate storage for the tag index array */ - map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); - if (map->tagArray == NULL) - return -1; - - /* parse the file, null-terminating tag strings */ - if (parseMapLines(map) != 0) { - fprintf(stderr, "%s: file parse failed\n", OUT_TAG); - return -1; - } - - /* sort the tags and check for duplicates */ - if (sortTags(map) != 0) - return -1; - - return 0; -} - -/* - * Run through all lines in the file, determining whether they're blank, - * comments, or possibly have a tag entry. - * - * This is a very "loose" scan. We don't try to detect syntax errors here. - * The later pass is more careful, but the number of tags found there must - * match the number of tags found here. - * - * Returns the number of potential tag entries found. - */ -static int countMapLines(const EventTagMap* map) -{ - int numTags, unknown; - const char* cp; - const char* endp; - - cp = (const char*) map->mapAddr; - endp = cp + map->mapLen; - - numTags = 0; - unknown = 1; - while (cp < endp) { - if (*cp == '\n') { - unknown = 1; - } else if (unknown) { - if (isCharDigit(*cp)) { - /* looks like a tag to me */ - numTags++; - unknown = 0; - } else if (isCharWhitespace(*cp)) { - /* might be leading whitespace before tag num, keep going */ - } else { - /* assume comment; second pass can complain in detail */ - unknown = 0; - } - } else { - /* we've made up our mind; just scan to end of line */ - } - cp++; - } - - return numTags; -} - -/* - * Parse the tags out of the file. - */ -static int parseMapLines(EventTagMap* map) -{ - int tagNum, lineStart, lineNum; - char* cp; - char* endp; - - cp = (char*) map->mapAddr; - endp = cp + map->mapLen; - - /* insist on EOL at EOF; simplifies parsing and null-termination */ - if (*(endp-1) != '\n') { - fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG); - return -1; - } - - tagNum = 0; - lineStart = 1; - lineNum = 1; - while (cp < endp) { - //printf("{%02x}", *cp); fflush(stdout); - if (*cp == '\n') { - lineStart = 1; - lineNum++; - } else if (lineStart) { - if (*cp == '#') { - /* comment; just scan to end */ - lineStart = 0; - } else if (isCharDigit(*cp)) { - /* looks like a tag; scan it out */ - if (tagNum >= map->numTags) { - fprintf(stderr, - "%s: more tags than expected (%d)\n", OUT_TAG, tagNum); - return -1; - } - if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) - return -1; - tagNum++; - lineNum++; // we eat the '\n' - /* leave lineStart==1 */ - } else if (isCharWhitespace(*cp)) { - /* looks like leading whitespace; keep scanning */ - } else { - fprintf(stderr, - "%s: unexpected chars (0x%02x) in tag number on line %d\n", - OUT_TAG, *cp, lineNum); - return -1; - } - } else { - /* this is a blank or comment line */ - } - cp++; - } - - if (tagNum != map->numTags) { - fprintf(stderr, "%s: parsed %d tags, expected %d\n", - OUT_TAG, tagNum, map->numTags); - return -1; - } - - return 0; -} - -/* - * Scan one tag line. - * - * "*pData" should be pointing to the first digit in the tag number. On - * 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). - * - * Returns 0 on success, nonzero on failure. - */ -static int scanTagLine(char** pData, EventTag* tag, int lineNum) -{ - char* cp = *pData; - char* startp; - char* endp; - unsigned long val; - - startp = cp; - while (isCharDigit(*++cp)) - ; - *cp = '\0'; - - val = strtoul(startp, &endp, 10); - assert(endp == cp); - if (endp != cp) - fprintf(stderr, "ARRRRGH\n"); - - tag->tagIndex = val; - - while (*++cp != '\n' && isCharWhitespace(*cp)) - ; - - if (*cp == '\n') { - fprintf(stderr, - "%s: missing tag string on line %d\n", OUT_TAG, lineNum); - return -1; - } - - tag->tagStr = cp; - - while (isCharValidTag(*++cp)) - ; - - if (*cp == '\n') { - /* null terminate and return */ - *cp = '\0'; - } else if (isCharWhitespace(*cp)) { - /* CRLF or trailin spaces; zap this char, then scan for the '\n' */ - *cp = '\0'; - - /* just ignore the rest of the line till \n - TODO: read the tag description that follows the tag name - */ - while (*++cp != '\n') { - } - } else { - fprintf(stderr, - "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum); - return -1; - } - - *pData = cp; - - //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr); - return 0; -} - -/* - * Compare two EventTags. - */ -static int compareEventTags(const void* v1, const void* v2) -{ - const EventTag* tag1 = (const EventTag*) v1; - const EventTag* tag2 = (const EventTag*) v2; - - return tag1->tagIndex - tag2->tagIndex; -} - -/* - * Sort the EventTag array so we can do fast lookups by tag index. After - * the sort we do a quick check for duplicate tag indices. - * - * Returns 0 on success. - */ -static int sortTags(EventTagMap* map) -{ - int i; - - qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags); - - for (i = 1; i < map->numTags; i++) { - if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) { - fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n", - OUT_TAG, - map->tagArray[i].tagIndex, map->tagArray[i].tagStr, - map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); - return -1; - } - } - - return 0; -} - -/* - * Dump the tag array for debugging. - */ -static void dumpTags(const EventTagMap* map) -{ - int i; - - for (i = 0; i < map->numTags; i++) { - const EventTag* tag = &map->tagArray[i]; - printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); - } -} - diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c deleted file mode 100644 index d9d67b4..0000000 --- a/liblog/fake_log_device.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ -/* - * Intercepts log messages intended for the Android log device. - * When running in the context of the simulator, the messages are - * passed on to the underlying (fake) log device. When not in the - * simulator, messages are printed to stderr. - */ -#include "cutils/logd.h" - -#include -#include -#include -#include -#include - -#ifdef HAVE_PTHREADS -#include -#endif - -#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ - -#define kTagSetSize 16 /* arbitrary */ - -#if 0 -#define TRACE(...) printf("fake_log_device: " __VA_ARGS__) -#else -#define TRACE(...) ((void)0) -#endif - -/* from the long-dead utils/Log.cpp */ -typedef enum { - FORMAT_OFF = 0, - FORMAT_BRIEF, - FORMAT_PROCESS, - FORMAT_TAG, - FORMAT_THREAD, - FORMAT_RAW, - FORMAT_TIME, - FORMAT_THREADTIME, - FORMAT_LONG -} LogFormat; - - -/* - * Log driver state. - */ -typedef struct LogState { - /* the fake fd that's seen by the user */ - int fakeFd; - - /* a printable name for this fake device */ - char *debugName; - - /* nonzero if this is a binary log */ - int isBinary; - - /* global minimum priority */ - int globalMinPriority; - - /* output format */ - LogFormat outputFormat; - - /* tags and priorities */ - struct { - char tag[kMaxTagLen]; - int minPriority; - } tagSet[kTagSetSize]; -} LogState; - - -#ifdef HAVE_PTHREADS -/* - * Locking. Since we're emulating a device, we need to be prepared - * to have multiple callers at the same time. This lock is used - * to both protect the fd list and to prevent LogStates from being - * freed out from under a user. - */ -static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; - -static void lock() -{ - pthread_mutex_lock(&fakeLogDeviceLock); -} - -static void unlock() -{ - pthread_mutex_unlock(&fakeLogDeviceLock); -} -#else // !HAVE_PTHREADS -#define lock() ((void)0) -#define unlock() ((void)0) -#endif // !HAVE_PTHREADS - - -/* - * File descriptor management. - */ -#define FAKE_FD_BASE 10000 -#define MAX_OPEN_LOGS 16 -static LogState *openLogTable[MAX_OPEN_LOGS]; - -/* - * Allocate an fd and associate a new LogState with it. - * The fd is available via the fakeFd field of the return value. - */ -static LogState *createLogState() -{ - size_t i; - - for (i = 0; i < sizeof(openLogTable); i++) { - if (openLogTable[i] == NULL) { - openLogTable[i] = calloc(1, sizeof(LogState)); - openLogTable[i]->fakeFd = FAKE_FD_BASE + i; - return openLogTable[i]; - } - } - return NULL; -} - -/* - * Translate an fd to a LogState. - */ -static LogState *fdToLogState(int fd) -{ - if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) { - return openLogTable[fd - FAKE_FD_BASE]; - } - return NULL; -} - -/* - * Unregister the fake fd and free the memory it pointed to. - */ -static void deleteFakeFd(int fd) -{ - LogState *ls; - - lock(); - - ls = fdToLogState(fd); - if (ls != NULL) { - openLogTable[fd - FAKE_FD_BASE] = NULL; - free(ls->debugName); - free(ls); - } - - unlock(); -} - -/* - * Configure logging based on ANDROID_LOG_TAGS environment variable. We - * need to parse a string that looks like - * - * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i - * - * The tag (or '*' for the global level) comes first, followed by a colon - * and a letter indicating the minimum priority level we're expected to log. - * This can be used to reveal or conceal logs with specific tags. - * - * We also want to check ANDROID_PRINTF_LOG to determine how the output - * will look. - */ -static void configureInitialState(const char* pathName, LogState* logState) -{ - static const int kDevLogLen = sizeof("/dev/log/") - 1; - - logState->debugName = strdup(pathName); - - /* identify binary logs */ - if (strcmp(pathName + kDevLogLen, "events") == 0) { - logState->isBinary = 1; - } - - /* global min priority defaults to "info" level */ - logState->globalMinPriority = ANDROID_LOG_INFO; - - /* - * This is based on the the long-dead utils/Log.cpp code. - */ - const char* tags = getenv("ANDROID_LOG_TAGS"); - TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); - if (tags != NULL) { - int entry = 0; - - while (*tags != '\0') { - char tagName[kMaxTagLen]; - int i, minPrio; - - while (isspace(*tags)) - tags++; - - i = 0; - while (*tags != '\0' && !isspace(*tags) && *tags != ':' && - i < kMaxTagLen) - { - tagName[i++] = *tags++; - } - if (i == kMaxTagLen) { - TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1); - return; - } - tagName[i] = '\0'; - - /* default priority, if there's no ":" part; also zero out '*' */ - minPrio = ANDROID_LOG_VERBOSE; - if (tagName[0] == '*' && tagName[1] == '\0') { - minPrio = ANDROID_LOG_DEBUG; - tagName[0] = '\0'; - } - - if (*tags == ':') { - tags++; - if (*tags >= '0' && *tags <= '9') { - if (*tags >= ('0' + ANDROID_LOG_SILENT)) - minPrio = ANDROID_LOG_VERBOSE; - else - minPrio = *tags - '\0'; - } else { - switch (*tags) { - case 'v': minPrio = ANDROID_LOG_VERBOSE; break; - case 'd': minPrio = ANDROID_LOG_DEBUG; break; - case 'i': minPrio = ANDROID_LOG_INFO; break; - case 'w': minPrio = ANDROID_LOG_WARN; break; - case 'e': minPrio = ANDROID_LOG_ERROR; break; - case 'f': minPrio = ANDROID_LOG_FATAL; break; - case 's': minPrio = ANDROID_LOG_SILENT; break; - default: minPrio = ANDROID_LOG_DEFAULT; break; - } - } - - tags++; - if (*tags != '\0' && !isspace(*tags)) { - TRACE("ERROR: garbage in tag env; expected whitespace\n"); - TRACE(" env='%s'\n", tags); - return; - } - } - - if (tagName[0] == 0) { - logState->globalMinPriority = minPrio; - TRACE("+++ global min prio %d\n", logState->globalMinPriority); - } else { - logState->tagSet[entry].minPriority = minPrio; - strcpy(logState->tagSet[entry].tag, tagName); - TRACE("+++ entry %d: %s:%d\n", - entry, - logState->tagSet[entry].tag, - logState->tagSet[entry].minPriority); - entry++; - } - } - } - - - /* - * Taken from the long-dead utils/Log.cpp - */ - const char* fstr = getenv("ANDROID_PRINTF_LOG"); - LogFormat format; - if (fstr == NULL) { - format = FORMAT_BRIEF; - } else { - if (strcmp(fstr, "brief") == 0) - format = FORMAT_BRIEF; - else if (strcmp(fstr, "process") == 0) - format = FORMAT_PROCESS; - else if (strcmp(fstr, "tag") == 0) - format = FORMAT_PROCESS; - else if (strcmp(fstr, "thread") == 0) - format = FORMAT_PROCESS; - else if (strcmp(fstr, "raw") == 0) - format = FORMAT_PROCESS; - else if (strcmp(fstr, "time") == 0) - format = FORMAT_PROCESS; - else if (strcmp(fstr, "long") == 0) - format = FORMAT_PROCESS; - else - format = (LogFormat) atoi(fstr); // really?! - } - - logState->outputFormat = format; -} - -/* - * Return a human-readable string for the priority level. Always returns - * a valid string. - */ -static const char* getPriorityString(int priority) -{ - /* the first character of each string should be unique */ - static const char* priorityStrings[] = { - "Verbose", "Debug", "Info", "Warn", "Error", "Assert" - }; - int idx; - - idx = (int) priority - (int) ANDROID_LOG_VERBOSE; - if (idx < 0 || - idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0]))) - return "?unknown?"; - return priorityStrings[idx]; -} - -#ifndef HAVE_WRITEV -/* - * Some platforms like WIN32 do not have writev(). - * Make up something to replace it. - */ -static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { - int result = 0; - struct iovec* end = iov + iovcnt; - for (; iov < end; iov++) { - int w = write(fd, iov->iov_base, iov->iov_len); - if (w != iov->iov_len) { - if (w < 0) - return w; - return result + w; - } - result += w; - } - return result; -} - -#define writev fake_writev -#endif - - -/* - * Write a filtered log message to stderr. - * - * Log format parsing taken from the long-dead utils/Log.cpp. - */ -static void showLog(LogState *state, - int logPrio, const char* tag, const char* msg) -{ -#if defined(HAVE_LOCALTIME_R) - struct tm tmBuf; -#endif - struct tm* ptm; - char timeBuf[32]; - char prefixBuf[128], suffixBuf[128]; - char priChar; - time_t when; - pid_t pid, tid; - - TRACE("LOG %d: %s %s", logPrio, tag, msg); - - priChar = getPriorityString(logPrio)[0]; - when = time(NULL); - pid = tid = getpid(); // find gettid()? - - /* - * Get the current date/time in pretty form - * - * It's often useful when examining a log with "less" to jump to - * a specific point in the file by searching for the date/time stamp. - * For this reason it's very annoying to have regexp meta characters - * in the time stamp. Don't use forward slashes, parenthesis, - * brackets, asterisks, or other special chars here. - */ -#if defined(HAVE_LOCALTIME_R) - ptm = localtime_r(&when, &tmBuf); -#else - ptm = localtime(&when); -#endif - //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - - /* - * Construct a buffer containing the log header and log message. - */ - size_t prefixLen, suffixLen; - - switch (state->outputFormat) { - case FORMAT_TAG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c/%-8s: ", priChar, tag); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_PROCESS: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d) ", priChar, pid); - suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), - " (%s)\n", tag); - break; - case FORMAT_THREAD: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d:%p) ", priChar, pid, (void*)tid); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_RAW: - prefixBuf[0] = 0; prefixLen = 0; - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_TIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%s %-8s\n\t", timeBuf, tag); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_THREADTIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_LONG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "[ %s %5d:%p %c/%-8s ]\n", - timeBuf, pid, (void*)tid, priChar, tag); - strcpy(suffixBuf, "\n\n"); suffixLen = 2; - break; - default: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c/%-8s(%5d): ", priChar, tag, pid); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - } - - /* - * Figure out how many lines there will be. - */ - const char* end = msg + strlen(msg); - size_t numLines = 0; - const char* p = msg; - while (p < end) { - if (*p++ == '\n') numLines++; - } - if (p > msg && *(p-1) != '\n') numLines++; - - /* - * Create an array of iovecs large enough to write all of - * the lines with a prefix and a suffix. - */ - const size_t INLINE_VECS = 6; - struct iovec stackVec[INLINE_VECS]; - struct iovec* vec = stackVec; - - numLines *= 3; // 3 iovecs per line. - if (numLines > INLINE_VECS) { - vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines); - if (vec == NULL) { - msg = "LOG: write failed, no memory"; - numLines = 3; - } - } - - /* - * Fill in the iovec pointers. - */ - p = msg; - struct iovec* v = vec; - int totalLen = 0; - while (p < end) { - if (prefixLen > 0) { - v->iov_base = prefixBuf; - v->iov_len = prefixLen; - totalLen += prefixLen; - v++; - } - const char* start = p; - while (p < end && *p != '\n') p++; - if ((p-start) > 0) { - v->iov_base = (void*)start; - v->iov_len = p-start; - totalLen += p-start; - v++; - } - if (*p == '\n') p++; - if (suffixLen > 0) { - v->iov_base = suffixBuf; - v->iov_len = suffixLen; - totalLen += suffixLen; - v++; - } - } - - /* - * Write the entire message to the log file with a single writev() call. - * We need to use this rather than a collection of printf()s on a FILE* - * because of multi-threading and multi-process issues. - * - * If the file was not opened with O_APPEND, this will produce interleaved - * output when called on the same file from multiple processes. - * - * If the file descriptor is actually a network socket, the writev() - * call may return with a partial write. Putting the writev() call in - * a loop can result in interleaved data. This can be alleviated - * somewhat by wrapping the writev call in the Mutex. - */ - - for(;;) { - int cc = writev(fileno(stderr), vec, v-vec); - - if (cc == totalLen) break; - - if (cc < 0) { - if(errno == EINTR) continue; - - /* can't really log the failure; for now, throw out a stderr */ - fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); - break; - } else { - /* shouldn't happen when writing to file or tty */ - fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen); - break; - } - } - - /* if we allocated storage for the iovecs, free it */ - if (vec != stackVec) - free(vec); -} - - -/* - * Receive a log message. We happen to know that "vector" has three parts: - * - * priority (1 byte) - * tag (N bytes -- null-terminated ASCII string) - * message (N bytes -- null-terminated ASCII string) - */ -static ssize_t logWritev(int fd, const struct iovec* vector, int count) -{ - LogState* state; - - /* Make sure that no-one frees the LogState while we're using it. - * Also guarantees that only one thread is in showLog() at a given - * time (if it matters). - */ - lock(); - - state = fdToLogState(fd); - if (state == NULL) { - errno = EBADF; - goto error; - } - - if (state->isBinary) { - TRACE("%s: ignoring binary log\n", state->debugName); - goto bail; - } - - if (count != 3) { - TRACE("%s: writevLog with count=%d not expected\n", - state->debugName, count); - goto error; - } - - /* pull out the three fields */ - int logPrio = *(const char*)vector[0].iov_base; - const char* tag = (const char*) vector[1].iov_base; - const char* msg = (const char*) vector[2].iov_base; - - /* see if this log tag is configured */ - int i; - int minPrio = state->globalMinPriority; - for (i = 0; i < kTagSetSize; i++) { - if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN) - break; /* reached end of configured values */ - - if (strcmp(state->tagSet[i].tag, tag) == 0) { - //TRACE("MATCH tag '%s'\n", tag); - minPrio = state->tagSet[i].minPriority; - break; - } - } - - if (logPrio >= minPrio) { - showLog(state, logPrio, tag, msg); - } else { - //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); - } - -bail: - unlock(); - return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; -error: - unlock(); - return -1; -} - -/* - * Free up our state and close the fake descriptor. - */ -static int logClose(int fd) -{ - deleteFakeFd(fd); - return 0; -} - -/* - * Open a log output device and return a fake fd. - */ -static int logOpen(const char* pathName, int flags) -{ - LogState *logState; - int fd = -1; - - lock(); - - logState = createLogState(); - if (logState != NULL) { - configureInitialState(pathName, logState); - fd = logState->fakeFd; - } else { - errno = ENFILE; - } - - unlock(); - - return fd; -} - - -/* - * Runtime redirection. If this binary is running in the simulator, - * just pass log messages to the emulated device. If it's running - * outside of the simulator, write the log messages to stderr. - */ - -static int (*redirectOpen)(const char *pathName, int flags) = NULL; -static int (*redirectClose)(int fd) = NULL; -static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count) - = NULL; - -static void setRedirects() -{ - const char *ws; - - /* Wrapsim sets this environment variable on children that it's - * created using its LD_PRELOAD wrapper. - */ - ws = getenv("ANDROID_WRAPSIM"); - if (ws != NULL && strcmp(ws, "1") == 0) { - /* We're running inside wrapsim, so we can just write to the device. */ - redirectOpen = (int (*)(const char *pathName, int flags))open; - redirectClose = close; - redirectWritev = writev; - } else { - /* There's no device to delegate to; handle the logging ourselves. */ - redirectOpen = logOpen; - redirectClose = logClose; - redirectWritev = logWritev; - } -} - -int fakeLogOpen(const char *pathName, int flags) -{ - if (redirectOpen == NULL) { - setRedirects(); - } - return redirectOpen(pathName, flags); -} - -int fakeLogClose(int fd) -{ - /* Assume that open() was called first. */ - return redirectClose(fd); -} - -ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) -{ - /* Assume that open() was called first. */ - return redirectWritev(fd, vector, count); -} diff --git a/liblog/logd_write.c b/liblog/logd_write.c deleted file mode 100644 index 80867d1..0000000 --- a/liblog/logd_write.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2007 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 -#include -#ifdef HAVE_PTHREADS -#include -#endif -#include -#include -#include -#include -#include -#include - -#include -#include - -#define LOG_BUF_SIZE 1024 - -#if FAKE_LOG_DEVICE -// This will be defined when building for the host. -#define log_open(pathname, flags) fakeLogOpen(pathname, flags) -#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) -#define log_close(filedes) fakeLogClose(filedes) -#else -#define log_open(pathname, flags) open(pathname, flags) -#define log_writev(filedes, vector, count) writev(filedes, vector, count) -#define log_close(filedes) close(filedes) -#endif - -typedef enum { - LOG_ID_MAIN = 0, - LOG_ID_RADIO, - LOG_ID_EVENTS, - LOG_ID_MAX -} log_id_t; - -static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); -static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = - __write_to_log_init; -#ifdef HAVE_PTHREADS -static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; -#endif - -static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 }; - -/* - * This is used by the C++ code to decide if it should write logs through - * the C code. Basically, if /dev/log/... is available, we're running in - * the simulator rather than a desktop tool and want to use the device. - */ -static enum { - kLogUninitialized, kLogNotAvailable, kLogAvailable -} g_log_status = kLogUninitialized; -int __android_log_dev_available(void) -{ - if (g_log_status == kLogUninitialized) { - if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) - g_log_status = kLogAvailable; - else - g_log_status = kLogNotAvailable; - } - - return (g_log_status == kLogAvailable); -} - -static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) -{ - return -1; -} - -static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) -{ - ssize_t ret; - int log_fd; - - if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { - log_fd = log_fds[(int)log_id]; - } else { - return EBADF; - } - - do { - ret = log_writev(log_fd, vec, nr); - } while (ret < 0 && errno == EINTR); - - return ret; -} - -static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) -{ -#ifdef HAVE_PTHREADS - pthread_mutex_lock(&log_init_lock); -#endif - - if (write_to_log == __write_to_log_init) { - log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); - log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); - log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); - - write_to_log = __write_to_log_kernel; - - if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || - log_fds[LOG_ID_EVENTS] < 0) { - log_close(log_fds[LOG_ID_MAIN]); - log_close(log_fds[LOG_ID_RADIO]); - log_close(log_fds[LOG_ID_EVENTS]); - log_fds[LOG_ID_MAIN] = -1; - log_fds[LOG_ID_RADIO] = -1; - log_fds[LOG_ID_EVENTS] = -1; - write_to_log = __write_to_log_null; - } - } - -#ifdef HAVE_PTHREADS - pthread_mutex_unlock(&log_init_lock); -#endif - - return write_to_log(log_id, vec, nr); -} - -int __android_log_write(int prio, const char *tag, const char *msg) -{ - struct iovec vec[3]; - log_id_t log_id = LOG_ID_MAIN; - - if (!tag) - tag = ""; - - /* XXX: This needs to go! */ - if (!strcmp(tag, "HTC_RIL") || - !strcmp(tag, "RILJ") || - !strcmp(tag, "RILC") || - !strcmp(tag, "RILD") || - !strcmp(tag, "RIL") || - !strcmp(tag, "AT") || - !strcmp(tag, "GSM") || - !strcmp(tag, "STK")) - log_id = LOG_ID_RADIO; - - vec[0].iov_base = (unsigned char *) &prio; - vec[0].iov_len = 1; - vec[1].iov_base = (void *) tag; - vec[1].iov_len = strlen(tag) + 1; - vec[2].iov_base = (void *) msg; - vec[2].iov_len = strlen(msg) + 1; - - return write_to_log(log_id, vec, 3); -} - -int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) -{ - char buf[LOG_BUF_SIZE]; - - vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); - - return __android_log_write(prio, tag, buf); -} - -int __android_log_print(int prio, const char *tag, const char *fmt, ...) -{ - va_list ap; - char buf[LOG_BUF_SIZE]; - - va_start(ap, fmt); - vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); - va_end(ap); - - return __android_log_write(prio, tag, buf); -} - -void __android_log_assert(const char *cond, const char *tag, - const char *fmt, ...) -{ - va_list ap; - char buf[LOG_BUF_SIZE]; - - va_start(ap, fmt); - vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); - va_end(ap); - - __android_log_write(ANDROID_LOG_FATAL, tag, buf); - - __builtin_trap(); /* trap so we have a chance to debug the situation */ -} - -int __android_log_bwrite(int32_t tag, const void *payload, size_t len) -{ - struct iovec vec[2]; - - vec[0].iov_base = &tag; - vec[0].iov_len = sizeof(tag); - vec[1].iov_base = (void*)payload; - vec[1].iov_len = len; - - return write_to_log(LOG_ID_EVENTS, vec, 2); -} - -/* - * Like __android_log_bwrite, but takes the type as well. Doesn't work - * for the general case where we're generating lists of stuff, but very - * handy if we just want to dump an integer into the log. - */ -int __android_log_btwrite(int32_t tag, char type, const void *payload, - size_t len) -{ - struct iovec vec[3]; - - vec[0].iov_base = &tag; - vec[0].iov_len = sizeof(tag); - vec[1].iov_base = &type; - vec[1].iov_len = sizeof(type); - vec[2].iov_base = (void*)payload; - vec[2].iov_len = len; - - return write_to_log(LOG_ID_EVENTS, vec, 3); -} diff --git a/liblog/logprint.c b/liblog/logprint.c deleted file mode 100644 index 2cf1254..0000000 --- a/liblog/logprint.c +++ /dev/null @@ -1,972 +0,0 @@ -/* //device/libs/cutils/logprint.c -** -** Copyright 2006, 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 _GNU_SOURCE /* for asprintf */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -typedef struct FilterInfo_t { - char *mTag; - android_LogPriority mPri; - struct FilterInfo_t *p_next; -} FilterInfo; - -struct AndroidLogFormat_t { - android_LogPriority global_pri; - FilterInfo *filters; - AndroidLogPrintFormat format; -}; - -static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) -{ - FilterInfo *p_ret; - - p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); - p_ret->mTag = strdup(tag); - p_ret->mPri = pri; - - return p_ret; -} - -static void filterinfo_free(FilterInfo *p_info) -{ - if (p_info == NULL) { - return; - } - - free(p_info->mTag); - p_info->mTag = NULL; -} - -/* - * Note: also accepts 0-9 priorities - * returns ANDROID_LOG_UNKNOWN if the character is unrecognized - */ -static android_LogPriority filterCharToPri (char c) -{ - android_LogPriority pri; - - c = tolower(c); - - if (c >= '0' && c <= '9') { - if (c >= ('0'+ANDROID_LOG_SILENT)) { - pri = ANDROID_LOG_VERBOSE; - } else { - pri = (android_LogPriority)(c - '0'); - } - } else if (c == 'v') { - pri = ANDROID_LOG_VERBOSE; - } else if (c == 'd') { - pri = ANDROID_LOG_DEBUG; - } else if (c == 'i') { - pri = ANDROID_LOG_INFO; - } else if (c == 'w') { - pri = ANDROID_LOG_WARN; - } else if (c == 'e') { - pri = ANDROID_LOG_ERROR; - } else if (c == 'f') { - pri = ANDROID_LOG_FATAL; - } else if (c == 's') { - pri = ANDROID_LOG_SILENT; - } else if (c == '*') { - pri = ANDROID_LOG_DEFAULT; - } else { - pri = ANDROID_LOG_UNKNOWN; - } - - return pri; -} - -static char filterPriToChar (android_LogPriority pri) -{ - switch (pri) { - case ANDROID_LOG_VERBOSE: return 'V'; - case ANDROID_LOG_DEBUG: return 'D'; - case ANDROID_LOG_INFO: return 'I'; - case ANDROID_LOG_WARN: return 'W'; - case ANDROID_LOG_ERROR: return 'E'; - case ANDROID_LOG_FATAL: return 'F'; - case ANDROID_LOG_SILENT: return 'S'; - - case ANDROID_LOG_DEFAULT: - case ANDROID_LOG_UNKNOWN: - default: return '?'; - } -} - -static android_LogPriority filterPriForTag( - AndroidLogFormat *p_format, const char *tag) -{ - FilterInfo *p_curFilter; - - for (p_curFilter = p_format->filters - ; p_curFilter != NULL - ; p_curFilter = p_curFilter->p_next - ) { - if (0 == strcmp(tag, p_curFilter->mTag)) { - if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) { - return p_format->global_pri; - } else { - return p_curFilter->mPri; - } - } - } - - return p_format->global_pri; -} - -/** for debugging */ -static void dumpFilters(AndroidLogFormat *p_format) -{ - FilterInfo *p_fi; - - for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { - char cPri = filterPriToChar(p_fi->mPri); - if (p_fi->mPri == ANDROID_LOG_DEFAULT) { - cPri = filterPriToChar(p_format->global_pri); - } - fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); - } - - fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); - -} - -/** - * returns 1 if this log line should be printed based on its priority - * and tag, and 0 if it should not - */ -int android_log_shouldPrintLine ( - AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) -{ - return pri >= filterPriForTag(p_format, tag); -} - -AndroidLogFormat *android_log_format_new() -{ - AndroidLogFormat *p_ret; - - p_ret = calloc(1, sizeof(AndroidLogFormat)); - - p_ret->global_pri = ANDROID_LOG_VERBOSE; - p_ret->format = FORMAT_BRIEF; - - return p_ret; -} - -void android_log_format_free(AndroidLogFormat *p_format) -{ - FilterInfo *p_info, *p_info_old; - - p_info = p_format->filters; - - while (p_info != NULL) { - p_info_old = p_info; - p_info = p_info->p_next; - - free(p_info_old); - } - - free(p_format); -} - - - -void android_log_setPrintFormat(AndroidLogFormat *p_format, - AndroidLogPrintFormat format) -{ - p_format->format=format; -} - -/** - * Returns FORMAT_OFF on invalid string - */ -AndroidLogPrintFormat android_log_formatFromString(const char * formatString) -{ - static AndroidLogPrintFormat format; - - if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; - else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; - else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; - else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; - else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; - else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; - else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; - else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; - else format = FORMAT_OFF; - - return format; -} - -/** - * filterExpression: a single filter expression - * eg "AT:d" - * - * returns 0 on success and -1 on invalid expression - * - * Assumes single threaded execution - */ - -int android_log_addFilterRule(AndroidLogFormat *p_format, - const char *filterExpression) -{ - size_t i=0; - size_t tagNameLength; - android_LogPriority pri = ANDROID_LOG_DEFAULT; - - tagNameLength = strcspn(filterExpression, ":"); - - if (tagNameLength == 0) { - goto error; - } - - if(filterExpression[tagNameLength] == ':') { - pri = filterCharToPri(filterExpression[tagNameLength+1]); - - if (pri == ANDROID_LOG_UNKNOWN) { - goto error; - } - } - - if(0 == strncmp("*", filterExpression, tagNameLength)) { - // This filter expression refers to the global filter - // The default level for this is DEBUG if the priority - // is unspecified - if (pri == ANDROID_LOG_DEFAULT) { - pri = ANDROID_LOG_DEBUG; - } - - p_format->global_pri = pri; - } else { - // for filter expressions that don't refer to the global - // filter, the default is verbose if the priority is unspecified - if (pri == ANDROID_LOG_DEFAULT) { - pri = ANDROID_LOG_VERBOSE; - } - - char *tagName; - -// Presently HAVE_STRNDUP is never defined, so the second case is always taken -// Darwin doesn't have strnup, everything else does -#ifdef HAVE_STRNDUP - tagName = strndup(filterExpression, tagNameLength); -#else - //a few extra bytes copied... - tagName = strdup(filterExpression); - tagName[tagNameLength] = '\0'; -#endif /*HAVE_STRNDUP*/ - - FilterInfo *p_fi = filterinfo_new(tagName, pri); - free(tagName); - - p_fi->p_next = p_format->filters; - p_format->filters = p_fi; - } - - return 0; -error: - return -1; -} - - -/** - * filterString: a comma/whitespace-separated set of filter expressions - * - * eg "AT:d *:i" - * - * returns 0 on success and -1 on invalid expression - * - * Assumes single threaded execution - * - */ - -int android_log_addFilterString(AndroidLogFormat *p_format, - const char *filterString) -{ - char *filterStringCopy = strdup (filterString); - char *p_cur = filterStringCopy; - char *p_ret; - int err; - - // Yes, I'm using strsep - while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { - // ignore whitespace-only entries - if(p_ret[0] != '\0') { - err = android_log_addFilterRule(p_format, p_ret); - - if (err < 0) { - goto error; - } - } - } - - free (filterStringCopy); - return 0; -error: - free (filterStringCopy); - return -1; -} - -static inline char * strip_end(char *str) -{ - char *end = str + strlen(str) - 1; - - while (end >= str && isspace(*end)) - *end-- = '\0'; - return str; -} - -/** - * Splits a wire-format buffer into an AndroidLogEntry - * entry allocated by caller. Pointers will point directly into buf - * - * Returns 0 on success and -1 on invalid wire format (entry will be - * in unspecified state) - */ -int android_log_processLogBuffer(struct logger_entry *buf, - AndroidLogEntry *entry) -{ - size_t tag_len; - - entry->tv_sec = buf->sec; - entry->tv_nsec = buf->nsec; - entry->priority = buf->msg[0]; - entry->pid = buf->pid; - entry->tid = buf->tid; - entry->tag = buf->msg + 1; - tag_len = strlen(entry->tag); - entry->messageLen = buf->len - tag_len - 3; - entry->message = entry->tag + tag_len + 1; - - return 0; -} - -/* - * Extract a 4-byte value from a byte stream. - */ -static inline uint32_t get4LE(const uint8_t* src) -{ - return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); -} - -/* - * Extract an 8-byte value from a byte stream. - */ -static inline uint64_t get8LE(const uint8_t* src) -{ - uint32_t low, high; - - low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); - high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); - return ((long long) high << 32) | (long long) low; -} - - -/* - * Recursively convert binary log data to printable form. - * - * This needs to be recursive because you can have lists of lists. - * - * If we run out of room, we stop processing immediately. It's important - * for us to check for space on every output element to avoid producing - * garbled output. - * - * Returns 0 on success, 1 on buffer full, -1 on failure. - */ -static int android_log_printBinaryEvent(const unsigned char** pEventData, - size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) -{ - const unsigned char* eventData = *pEventData; - size_t eventDataLen = *pEventDataLen; - char* outBuf = *pOutBuf; - size_t outBufLen = *pOutBufLen; - unsigned char type; - size_t outCount; - int result = 0; - - if (eventDataLen < 1) - return -1; - type = *eventData++; - eventDataLen--; - - //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); - - switch (type) { - case EVENT_TYPE_INT: - /* 32-bit signed int */ - { - int ival; - - if (eventDataLen < 4) - return -1; - ival = get4LE(eventData); - eventData += 4; - eventDataLen -= 4; - - outCount = snprintf(outBuf, outBufLen, "%d", ival); - if (outCount < outBufLen) { - outBuf += outCount; - outBufLen -= outCount; - } else { - /* halt output */ - goto no_room; - } - } - break; - case EVENT_TYPE_LONG: - /* 64-bit signed long */ - { - long long lval; - - if (eventDataLen < 8) - return -1; - lval = get8LE(eventData); - eventData += 8; - eventDataLen -= 8; - - outCount = snprintf(outBuf, outBufLen, "%lld", lval); - if (outCount < outBufLen) { - outBuf += outCount; - outBufLen -= outCount; - } else { - /* halt output */ - goto no_room; - } - } - break; - case EVENT_TYPE_STRING: - /* UTF-8 chars, not NULL-terminated */ - { - unsigned int strLen; - - if (eventDataLen < 4) - return -1; - strLen = get4LE(eventData); - eventData += 4; - eventDataLen -= 4; - - if (eventDataLen < strLen) - return -1; - - if (strLen < outBufLen) { - memcpy(outBuf, eventData, strLen); - outBuf += strLen; - outBufLen -= strLen; - } else if (outBufLen > 0) { - /* copy what we can */ - memcpy(outBuf, eventData, outBufLen); - outBuf += outBufLen; - outBufLen -= outBufLen; - goto no_room; - } - eventData += strLen; - eventDataLen -= strLen; - break; - } - case EVENT_TYPE_LIST: - /* N items, all different types */ - { - unsigned char count; - int i; - - if (eventDataLen < 1) - return -1; - - count = *eventData++; - eventDataLen--; - - if (outBufLen > 0) { - *outBuf++ = '['; - outBufLen--; - } else { - goto no_room; - } - - for (i = 0; i < count; i++) { - result = android_log_printBinaryEvent(&eventData, &eventDataLen, - &outBuf, &outBufLen); - if (result != 0) - goto bail; - - if (i < count-1) { - if (outBufLen > 0) { - *outBuf++ = ','; - outBufLen--; - } else { - goto no_room; - } - } - } - - if (outBufLen > 0) { - *outBuf++ = ']'; - outBufLen--; - } else { - goto no_room; - } - } - break; - default: - fprintf(stderr, "Unknown binary event type %d\n", type); - return -1; - } - -bail: - *pEventData = eventData; - *pEventDataLen = eventDataLen; - *pOutBuf = outBuf; - *pOutBufLen = outBufLen; - return result; - -no_room: - result = 1; - goto bail; -} - -/** - * Convert a binary log entry to ASCII form. - * - * For convenience we mimic the processLogBuffer API. There is no - * pre-defined output length for the binary data, since we're free to format - * it however we choose, which means we can't really use a fixed-size buffer - * here. - */ -int android_log_processBinaryLogBuffer(struct logger_entry *buf, - AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, - int messageBufLen) -{ - size_t inCount; - unsigned int tagIndex; - const unsigned char* eventData; - - entry->tv_sec = buf->sec; - entry->tv_nsec = buf->nsec; - entry->priority = ANDROID_LOG_INFO; - entry->pid = buf->pid; - entry->tid = buf->tid; - - /* - * Pull the tag out. - */ - eventData = (const unsigned char*) buf->msg; - inCount = buf->len; - if (inCount < 4) - return -1; - tagIndex = get4LE(eventData); - eventData += 4; - inCount -= 4; - - if (map != NULL) { - entry->tag = android_lookupEventTag(map, tagIndex); - } else { - entry->tag = NULL; - } - - /* - * If we don't have a map, or didn't find the tag number in the map, - * stuff a generated tag value into the start of the output buffer and - * shift the buffer pointers down. - */ - if (entry->tag == NULL) { - int tagLen; - - tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); - entry->tag = messageBuf; - messageBuf += tagLen+1; - messageBufLen -= tagLen+1; - } - - /* - * Format the event log data into the buffer. - */ - char* outBuf = messageBuf; - size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ - int result; - result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, - &outRemaining); - if (result < 0) { - fprintf(stderr, "Binary log entry conversion failed\n"); - return -1; - } else if (result == 1) { - if (outBuf > messageBuf) { - /* leave an indicator */ - *(outBuf-1) = '!'; - } else { - /* no room to output anything at all */ - *outBuf++ = '!'; - outRemaining--; - } - /* pretend we ate all the data */ - inCount = 0; - } - - /* eat the silly terminating '\n' */ - if (inCount == 1 && *eventData == '\n') { - eventData++; - inCount--; - } - - if (inCount != 0) { - fprintf(stderr, - "Warning: leftover binary log data (%d bytes)\n", inCount); - } - - /* - * Terminate the buffer. The NUL byte does not count as part of - * entry->messageLen. - */ - *outBuf = '\0'; - entry->messageLen = outBuf - messageBuf; - assert(entry->messageLen == (messageBufLen-1) - outRemaining); - - entry->message = messageBuf; - - return 0; -} - -/** - * Formats a log message into a buffer - * - * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer - * If return value != defaultBuffer, caller must call free() - * Returns NULL on malloc error - */ - -char *android_log_formatLogLine ( - AndroidLogFormat *p_format, - char *defaultBuffer, - size_t defaultBufferSize, - const AndroidLogEntry *entry, - size_t *p_outLength) -{ -#if defined(HAVE_LOCALTIME_R) - struct tm tmBuf; -#endif - struct tm* ptm; - char timeBuf[32]; - char headerBuf[128]; - char prefixBuf[128], suffixBuf[128]; - char priChar; - int prefixSuffixIsHeaderFooter = 0; - char * ret = NULL; - - priChar = filterPriToChar(entry->priority); - - /* - * Get the current date/time in pretty form - * - * It's often useful when examining a log with "less" to jump to - * a specific point in the file by searching for the date/time stamp. - * For this reason it's very annoying to have regexp meta characters - * in the time stamp. Don't use forward slashes, parenthesis, - * brackets, asterisks, or other special chars here. - */ -#if defined(HAVE_LOCALTIME_R) - ptm = localtime_r(&(entry->tv_sec), &tmBuf); -#else - ptm = localtime(&(entry->tv_sec)); -#endif - //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); - strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); - - /* - * Construct a buffer containing the log header and log message. - */ - size_t prefixLen, suffixLen; - - switch (p_format->format) { - case FORMAT_TAG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c/%-8s: ", priChar, entry->tag); - strcpy(suffixBuf, "\n"); suffixLen = 1; - break; - case FORMAT_PROCESS: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d) ", priChar, entry->pid); - suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), - " (%s)\n", entry->tag); - break; - case FORMAT_THREAD: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; - break; - case FORMAT_RAW: - prefixBuf[0] = 0; - prefixLen = 0; - strcpy(suffixBuf, "\n"); - suffixLen = 1; - break; - case FORMAT_TIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, - priChar, entry->tag, entry->pid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; - break; - case FORMAT_THREADTIME: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, - (int)entry->pid, (int)entry->tid, priChar, entry->tag); - strcpy(suffixBuf, "\n"); - suffixLen = 1; - break; - case FORMAT_LONG: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "[ %s.%03ld %5d:%p %c/%-8s ]\n", - timeBuf, entry->tv_nsec / 1000000, entry->pid, - (void*)entry->tid, priChar, entry->tag); - strcpy(suffixBuf, "\n\n"); - suffixLen = 2; - prefixSuffixIsHeaderFooter = 1; - break; - case FORMAT_BRIEF: - default: - prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), - "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); - strcpy(suffixBuf, "\n"); - suffixLen = 1; - break; - } - - /* the following code is tragically unreadable */ - - size_t numLines; - size_t i; - char *p; - size_t bufferSize; - const char *pm; - - if (prefixSuffixIsHeaderFooter) { - // we're just wrapping message with a header/footer - numLines = 1; - } else { - pm = entry->message; - numLines = 0; - - // The line-end finding here must match the line-end finding - // in for ( ... numLines...) loop below - while (pm < (entry->message + entry->messageLen)) { - if (*pm++ == '\n') numLines++; - } - // plus one line for anything not newline-terminated at the end - if (pm > entry->message && *(pm-1) != '\n') numLines++; - } - - // this is an upper bound--newlines in message may be counted - // extraneously - bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; - - if (defaultBufferSize >= bufferSize) { - ret = defaultBuffer; - } else { - ret = (char *)malloc(bufferSize); - - if (ret == NULL) { - return ret; - } - } - - ret[0] = '\0'; /* to start strcat off */ - - p = ret; - pm = entry->message; - - if (prefixSuffixIsHeaderFooter) { - strcat(p, prefixBuf); - p += prefixLen; - strncat(p, entry->message, entry->messageLen); - p += entry->messageLen; - strcat(p, suffixBuf); - p += suffixLen; - } else { - while(pm < (entry->message + entry->messageLen)) { - const char *lineStart; - size_t lineLen; - - lineStart = pm; - - // Find the next end-of-line in message - while (pm < (entry->message + entry->messageLen) - && *pm != '\n') pm++; - lineLen = pm - lineStart; - - strcat(p, prefixBuf); - p += prefixLen; - strncat(p, lineStart, lineLen); - p += lineLen; - strcat(p, suffixBuf); - p += suffixLen; - - if (*pm == '\n') pm++; - } - } - - if (p_outLength != NULL) { - *p_outLength = p - ret; - } - - return ret; -} - -/** - * Either print or do not print log line, based on filter - * - * Returns count bytes written - */ - -int android_log_filterAndPrintLogLine( - AndroidLogFormat *p_format, - int fd, - const AndroidLogEntry *entry) -{ - int ret; - char defaultBuffer[512]; - char *outBuffer = NULL; - size_t totalLen; - - if (0 == android_log_shouldPrintLine(p_format, entry->tag, - entry->priority)) { - return 0; - } - - outBuffer = android_log_formatLogLine(p_format, defaultBuffer, - sizeof(defaultBuffer), entry, &totalLen); - - if (!outBuffer) - return -1; - - do { - ret = write(fd, outBuffer, totalLen); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); - ret = 0; - goto done; - } - - if (((size_t)ret) < totalLen) { - fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, - (int)totalLen); - goto done; - } - -done: - if (outBuffer != defaultBuffer) { - free(outBuffer); - } - - return ret; -} - - - -void logprint_run_tests() -{ -#if 0 - - fprintf(stderr, "tests disabled\n"); - -#else - - int err; - const char *tag; - AndroidLogFormat *p_format; - - p_format = android_log_format_new(); - - fprintf(stderr, "running tests\n"); - - tag = "random"; - - android_log_addFilterRule(p_format,"*:i"); - - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - android_log_addFilterRule(p_format, "*"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "*:i"); - assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "random"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:v"); - assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:d"); - assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); - android_log_addFilterRule(p_format, "random:w"); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - android_log_addFilterRule(p_format, "crap:*"); - assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); - assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); - - // invalid expression - err = android_log_addFilterRule(p_format, "random:z"); - assert (err < 0); - assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); - - // Issue #550946 - err = android_log_addFilterString(p_format, " "); - assert(err == 0); - assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); - - // note trailing space - err = android_log_addFilterString(p_format, "*:s random:d "); - assert(err == 0); - assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); - - err = android_log_addFilterString(p_format, "*:s random:z"); - assert(err < 0); - - -#if 0 - char *ret; - char defaultBuffer[512]; - - ret = android_log_formatLogLine(p_format, - defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, - 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); -#endif - - - fprintf(stderr, "tests complete\n"); -#endif -} -- cgit v1.1 From dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:32:55 -0800 Subject: auto import from //depot/cupcake/@135843 --- liblog/Android.mk | 72 ++++ liblog/event_tag_map.c | 438 +++++++++++++++++++++ liblog/fake_log_device.c | 685 +++++++++++++++++++++++++++++++++ liblog/logd_write.c | 230 +++++++++++ liblog/logprint.c | 972 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 2397 insertions(+) create mode 100644 liblog/Android.mk create mode 100644 liblog/event_tag_map.c create mode 100644 liblog/fake_log_device.c create mode 100644 liblog/logd_write.c create mode 100644 liblog/logprint.c (limited to 'liblog') diff --git a/liblog/Android.mk b/liblog/Android.mk new file mode 100644 index 0000000..0eec87f --- /dev/null +++ b/liblog/Android.mk @@ -0,0 +1,72 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(my-dir) +include $(CLEAR_VARS) + +liblog_sources := logd_write.c + +# some files must not be compiled when building against Mingw +# they correspond to features not used by our host development tools +# which are also hard or even impossible to port to native Win32 +WITH_MINGW := +ifeq ($(HOST_OS),windows) + ifeq ($(strip $(USE_CYGWIN)),) + WITH_MINGW := true + endif +endif +# USE_MINGW is defined when we build against Mingw on Linux +ifneq ($(strip $(USE_MINGW)),) + WITH_MINGW := true +endif + +ifndef WITH_MINGW + liblog_sources += \ + logprint.c \ + event_tag_map.c +endif + +liblog_host_sources := $(liblog_sources) fake_log_device.c + +# Static library for host +# ======================================================== +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_host_sources) +LOCAL_LDLIBS := -lpthread +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 +include $(BUILD_HOST_STATIC_LIBRARY) + +ifeq ($(TARGET_SIMULATOR),true) + # Shared library for simulator + # ======================================================== + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_SRC_FILES := $(liblog_host_sources) + LOCAL_LDLIBS := -lpthread + LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 + include $(BUILD_SHARED_LIBRARY) +else # !sim + # Shared and static library for target + # ======================================================== + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_SRC_FILES := $(liblog_sources) + include $(BUILD_STATIC_LIBRARY) + + include $(CLEAR_VARS) + LOCAL_MODULE := liblog + LOCAL_WHOLE_STATIC_LIBRARIES := liblog + include $(BUILD_SHARED_LIBRARY) +endif # !sim diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c new file mode 100644 index 0000000..e70754e --- /dev/null +++ b/liblog/event_tag_map.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2007 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 "cutils/event_tag_map.h" +#include "cutils/log.h" + +#include +#include +#include +#include +#include +#include + +#define OUT_TAG "EventTagMap" + +/* + * Single entry. + */ +typedef struct EventTag { + unsigned int tagIndex; + const char* tagStr; +} EventTag; + +/* + * Map. + */ +struct EventTagMap { + /* memory-mapped source file; we get strings from here */ + void* mapAddr; + size_t mapLen; + + /* array of event tags, sorted numerically by tag index */ + EventTag* tagArray; + int numTags; +}; + +/* fwd */ +static int processFile(EventTagMap* map); +static int countMapLines(const EventTagMap* map); +static int parseMapLines(EventTagMap* map); +static int scanTagLine(char** pData, EventTag* tag, int lineNum); +static int sortTags(EventTagMap* map); +static void dumpTags(const EventTagMap* map); + + +/* + * Open the map file and allocate a structure to manage it. + * + * We create a private mapping because we want to terminate the log tag + * strings with '\0'. + */ +EventTagMap* android_openEventTagMap(const char* fileName) +{ + EventTagMap* newTagMap; + off_t end; + int fd = -1; + + newTagMap = calloc(1, sizeof(EventTagMap)); + if (newTagMap == NULL) + return NULL; + + fd = open(fileName, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s: unable to open map '%s': %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + + end = lseek(fd, 0L, SEEK_END); + (void) lseek(fd, 0L, SEEK_SET); + if (end < 0) { + fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName); + goto fail; + } + + newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, + fd, 0); + if (newTagMap->mapAddr == MAP_FAILED) { + fprintf(stderr, "%s: mmap(%s) failed: %s\n", + OUT_TAG, fileName, strerror(errno)); + goto fail; + } + newTagMap->mapLen = end; + + if (processFile(newTagMap) != 0) + goto fail; + + return newTagMap; + +fail: + android_closeEventTagMap(newTagMap); + if (fd >= 0) + close(fd); + return NULL; +} + +/* + * Close the map. + */ +void android_closeEventTagMap(EventTagMap* map) +{ + if (map == NULL) + return; + + munmap(map->mapAddr, map->mapLen); + free(map); +} + +/* + * Look up an entry in the map. + * + * The entries are sorted by tag number, so we can do a binary search. + */ +const char* android_lookupEventTag(const EventTagMap* map, int tag) +{ + int hi, lo, mid; + + lo = 0; + hi = map->numTags-1; + + while (lo <= hi) { + int cmp; + + mid = (lo+hi)/2; + cmp = map->tagArray[mid].tagIndex - tag; + if (cmp < 0) { + /* tag is bigger */ + lo = mid + 1; + } else if (cmp > 0) { + /* tag is smaller */ + hi = mid - 1; + } else { + /* found */ + return map->tagArray[mid].tagStr; + } + } + + return NULL; +} + + + +/* + * Determine whether "c" is a whitespace char. + */ +static inline int isCharWhitespace(char c) +{ + return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); +} + +/* + * Determine whether "c" is a valid tag char. + */ +static inline int isCharValidTag(char c) +{ + return ((c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '_')); +} + +/* + * Determine whether "c" is a valid decimal digit. + */ +static inline int isCharDigit(char c) +{ + return (c >= '0' && c <= '9'); +} + + +/* + * Crunch through the file, parsing the contents and creating a tag index. + */ +static int processFile(EventTagMap* map) +{ + EventTag* tagArray = NULL; + + /* get a tag count */ + map->numTags = countMapLines(map); + if (map->numTags < 0) + return -1; + + //printf("+++ found %d tags\n", map->numTags); + + /* allocate storage for the tag index array */ + map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); + if (map->tagArray == NULL) + return -1; + + /* parse the file, null-terminating tag strings */ + if (parseMapLines(map) != 0) { + fprintf(stderr, "%s: file parse failed\n", OUT_TAG); + return -1; + } + + /* sort the tags and check for duplicates */ + if (sortTags(map) != 0) + return -1; + + return 0; +} + +/* + * Run through all lines in the file, determining whether they're blank, + * comments, or possibly have a tag entry. + * + * This is a very "loose" scan. We don't try to detect syntax errors here. + * The later pass is more careful, but the number of tags found there must + * match the number of tags found here. + * + * Returns the number of potential tag entries found. + */ +static int countMapLines(const EventTagMap* map) +{ + int numTags, unknown; + const char* cp; + const char* endp; + + cp = (const char*) map->mapAddr; + endp = cp + map->mapLen; + + numTags = 0; + unknown = 1; + while (cp < endp) { + if (*cp == '\n') { + unknown = 1; + } else if (unknown) { + if (isCharDigit(*cp)) { + /* looks like a tag to me */ + numTags++; + unknown = 0; + } else if (isCharWhitespace(*cp)) { + /* might be leading whitespace before tag num, keep going */ + } else { + /* assume comment; second pass can complain in detail */ + unknown = 0; + } + } else { + /* we've made up our mind; just scan to end of line */ + } + cp++; + } + + return numTags; +} + +/* + * Parse the tags out of the file. + */ +static int parseMapLines(EventTagMap* map) +{ + int tagNum, lineStart, lineNum; + char* cp; + char* endp; + + cp = (char*) map->mapAddr; + endp = cp + map->mapLen; + + /* insist on EOL at EOF; simplifies parsing and null-termination */ + if (*(endp-1) != '\n') { + fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG); + return -1; + } + + tagNum = 0; + lineStart = 1; + lineNum = 1; + while (cp < endp) { + //printf("{%02x}", *cp); fflush(stdout); + if (*cp == '\n') { + lineStart = 1; + lineNum++; + } else if (lineStart) { + if (*cp == '#') { + /* comment; just scan to end */ + lineStart = 0; + } else if (isCharDigit(*cp)) { + /* looks like a tag; scan it out */ + if (tagNum >= map->numTags) { + fprintf(stderr, + "%s: more tags than expected (%d)\n", OUT_TAG, tagNum); + return -1; + } + if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) + return -1; + tagNum++; + lineNum++; // we eat the '\n' + /* leave lineStart==1 */ + } else if (isCharWhitespace(*cp)) { + /* looks like leading whitespace; keep scanning */ + } else { + fprintf(stderr, + "%s: unexpected chars (0x%02x) in tag number on line %d\n", + OUT_TAG, *cp, lineNum); + return -1; + } + } else { + /* this is a blank or comment line */ + } + cp++; + } + + if (tagNum != map->numTags) { + fprintf(stderr, "%s: parsed %d tags, expected %d\n", + OUT_TAG, tagNum, map->numTags); + return -1; + } + + return 0; +} + +/* + * Scan one tag line. + * + * "*pData" should be pointing to the first digit in the tag number. On + * 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). + * + * Returns 0 on success, nonzero on failure. + */ +static int scanTagLine(char** pData, EventTag* tag, int lineNum) +{ + char* cp = *pData; + char* startp; + char* endp; + unsigned long val; + + startp = cp; + while (isCharDigit(*++cp)) + ; + *cp = '\0'; + + val = strtoul(startp, &endp, 10); + assert(endp == cp); + if (endp != cp) + fprintf(stderr, "ARRRRGH\n"); + + tag->tagIndex = val; + + while (*++cp != '\n' && isCharWhitespace(*cp)) + ; + + if (*cp == '\n') { + fprintf(stderr, + "%s: missing tag string on line %d\n", OUT_TAG, lineNum); + return -1; + } + + tag->tagStr = cp; + + while (isCharValidTag(*++cp)) + ; + + if (*cp == '\n') { + /* null terminate and return */ + *cp = '\0'; + } else if (isCharWhitespace(*cp)) { + /* CRLF or trailin spaces; zap this char, then scan for the '\n' */ + *cp = '\0'; + + /* just ignore the rest of the line till \n + TODO: read the tag description that follows the tag name + */ + while (*++cp != '\n') { + } + } else { + fprintf(stderr, + "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum); + return -1; + } + + *pData = cp; + + //printf("+++ Line %d: got %d '%s'\n", lineNum, tag->tagIndex, tag->tagStr); + return 0; +} + +/* + * Compare two EventTags. + */ +static int compareEventTags(const void* v1, const void* v2) +{ + const EventTag* tag1 = (const EventTag*) v1; + const EventTag* tag2 = (const EventTag*) v2; + + return tag1->tagIndex - tag2->tagIndex; +} + +/* + * Sort the EventTag array so we can do fast lookups by tag index. After + * the sort we do a quick check for duplicate tag indices. + * + * Returns 0 on success. + */ +static int sortTags(EventTagMap* map) +{ + int i; + + qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags); + + for (i = 1; i < map->numTags; i++) { + if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) { + fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n", + OUT_TAG, + map->tagArray[i].tagIndex, map->tagArray[i].tagStr, + map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); + return -1; + } + } + + return 0; +} + +/* + * Dump the tag array for debugging. + */ +static void dumpTags(const EventTagMap* map) +{ + int i; + + for (i = 0; i < map->numTags; i++) { + const EventTag* tag = &map->tagArray[i]; + printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); + } +} + diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c new file mode 100644 index 0000000..ed9d699 --- /dev/null +++ b/liblog/fake_log_device.c @@ -0,0 +1,685 @@ +/* + * Copyright (C) 2008 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. + */ +/* + * Intercepts log messages intended for the Android log device. + * When running in the context of the simulator, the messages are + * passed on to the underlying (fake) log device. When not in the + * simulator, messages are printed to stderr. + */ +#include "cutils/logd.h" + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#endif + +#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ + +#define kTagSetSize 16 /* arbitrary */ + +#if 0 +#define TRACE(...) printf("fake_log_device: " __VA_ARGS__) +#else +#define TRACE(...) ((void)0) +#endif + +/* from the long-dead utils/Log.cpp */ +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG +} LogFormat; + + +/* + * Log driver state. + */ +typedef struct LogState { + /* the fake fd that's seen by the user */ + int fakeFd; + + /* a printable name for this fake device */ + char *debugName; + + /* nonzero if this is a binary log */ + int isBinary; + + /* global minimum priority */ + int globalMinPriority; + + /* output format */ + LogFormat outputFormat; + + /* tags and priorities */ + struct { + char tag[kMaxTagLen]; + int minPriority; + } tagSet[kTagSetSize]; +} LogState; + + +#ifdef HAVE_PTHREADS +/* + * Locking. Since we're emulating a device, we need to be prepared + * to have multiple callers at the same time. This lock is used + * to both protect the fd list and to prevent LogStates from being + * freed out from under a user. + */ +static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; + +static void lock() +{ + pthread_mutex_lock(&fakeLogDeviceLock); +} + +static void unlock() +{ + pthread_mutex_unlock(&fakeLogDeviceLock); +} +#else // !HAVE_PTHREADS +#define lock() ((void)0) +#define unlock() ((void)0) +#endif // !HAVE_PTHREADS + + +/* + * File descriptor management. + */ +#define FAKE_FD_BASE 10000 +#define MAX_OPEN_LOGS 16 +static LogState *openLogTable[MAX_OPEN_LOGS]; + +/* + * Allocate an fd and associate a new LogState with it. + * The fd is available via the fakeFd field of the return value. + */ +static LogState *createLogState() +{ + size_t i; + + for (i = 0; i < sizeof(openLogTable); i++) { + if (openLogTable[i] == NULL) { + openLogTable[i] = calloc(1, sizeof(LogState)); + openLogTable[i]->fakeFd = FAKE_FD_BASE + i; + return openLogTable[i]; + } + } + return NULL; +} + +/* + * Translate an fd to a LogState. + */ +static LogState *fdToLogState(int fd) +{ + if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) { + return openLogTable[fd - FAKE_FD_BASE]; + } + return NULL; +} + +/* + * Unregister the fake fd and free the memory it pointed to. + */ +static void deleteFakeFd(int fd) +{ + LogState *ls; + + lock(); + + ls = fdToLogState(fd); + if (ls != NULL) { + openLogTable[fd - FAKE_FD_BASE] = NULL; + free(ls->debugName); + free(ls); + } + + unlock(); +} + +/* + * Configure logging based on ANDROID_LOG_TAGS environment variable. We + * need to parse a string that looks like + * + * *:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i + * + * The tag (or '*' for the global level) comes first, followed by a colon + * and a letter indicating the minimum priority level we're expected to log. + * This can be used to reveal or conceal logs with specific tags. + * + * We also want to check ANDROID_PRINTF_LOG to determine how the output + * will look. + */ +static void configureInitialState(const char* pathName, LogState* logState) +{ + static const int kDevLogLen = sizeof("/dev/log/") - 1; + + logState->debugName = strdup(pathName); + + /* identify binary logs */ + if (strcmp(pathName + kDevLogLen, "events") == 0) { + logState->isBinary = 1; + } + + /* global min priority defaults to "info" level */ + logState->globalMinPriority = ANDROID_LOG_INFO; + + /* + * This is based on the the long-dead utils/Log.cpp code. + */ + const char* tags = getenv("ANDROID_LOG_TAGS"); + TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); + if (tags != NULL) { + int entry = 0; + + while (*tags != '\0') { + char tagName[kMaxTagLen]; + int i, minPrio; + + while (isspace(*tags)) + tags++; + + i = 0; + while (*tags != '\0' && !isspace(*tags) && *tags != ':' && + i < kMaxTagLen) + { + tagName[i++] = *tags++; + } + if (i == kMaxTagLen) { + TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1); + return; + } + tagName[i] = '\0'; + + /* default priority, if there's no ":" part; also zero out '*' */ + minPrio = ANDROID_LOG_VERBOSE; + if (tagName[0] == '*' && tagName[1] == '\0') { + minPrio = ANDROID_LOG_DEBUG; + tagName[0] = '\0'; + } + + if (*tags == ':') { + tags++; + if (*tags >= '0' && *tags <= '9') { + if (*tags >= ('0' + ANDROID_LOG_SILENT)) + minPrio = ANDROID_LOG_VERBOSE; + else + minPrio = *tags - '\0'; + } else { + switch (*tags) { + case 'v': minPrio = ANDROID_LOG_VERBOSE; break; + case 'd': minPrio = ANDROID_LOG_DEBUG; break; + case 'i': minPrio = ANDROID_LOG_INFO; break; + case 'w': minPrio = ANDROID_LOG_WARN; break; + case 'e': minPrio = ANDROID_LOG_ERROR; break; + case 'f': minPrio = ANDROID_LOG_FATAL; break; + case 's': minPrio = ANDROID_LOG_SILENT; break; + default: minPrio = ANDROID_LOG_DEFAULT; break; + } + } + + tags++; + if (*tags != '\0' && !isspace(*tags)) { + TRACE("ERROR: garbage in tag env; expected whitespace\n"); + TRACE(" env='%s'\n", tags); + return; + } + } + + if (tagName[0] == 0) { + logState->globalMinPriority = minPrio; + TRACE("+++ global min prio %d\n", logState->globalMinPriority); + } else { + logState->tagSet[entry].minPriority = minPrio; + strcpy(logState->tagSet[entry].tag, tagName); + TRACE("+++ entry %d: %s:%d\n", + entry, + logState->tagSet[entry].tag, + logState->tagSet[entry].minPriority); + entry++; + } + } + } + + + /* + * Taken from the long-dead utils/Log.cpp + */ + const char* fstr = getenv("ANDROID_PRINTF_LOG"); + LogFormat format; + if (fstr == NULL) { + format = FORMAT_BRIEF; + } else { + if (strcmp(fstr, "brief") == 0) + format = FORMAT_BRIEF; + else if (strcmp(fstr, "process") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "tag") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "thread") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "raw") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "time") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "long") == 0) + format = FORMAT_PROCESS; + else + format = (LogFormat) atoi(fstr); // really?! + } + + logState->outputFormat = format; +} + +/* + * Return a human-readable string for the priority level. Always returns + * a valid string. + */ +static const char* getPriorityString(int priority) +{ + /* the first character of each string should be unique */ + static const char* priorityStrings[] = { + "Verbose", "Debug", "Info", "Warn", "Error", "Assert" + }; + int idx; + + idx = (int) priority - (int) ANDROID_LOG_VERBOSE; + if (idx < 0 || + idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0]))) + return "?unknown?"; + return priorityStrings[idx]; +} + +#ifndef HAVE_WRITEV +/* + * Some platforms like WIN32 do not have writev(). + * Make up something to replace it. + */ +static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { + int result = 0; + struct iovec* end = iov + iovcnt; + for (; iov < end; iov++) { + int w = write(fd, iov->iov_base, iov->iov_len); + if (w != iov->iov_len) { + if (w < 0) + return w; + return result + w; + } + result += w; + } + return result; +} + +#define writev fake_writev +#endif + + +/* + * Write a filtered log message to stderr. + * + * Log format parsing taken from the long-dead utils/Log.cpp. + */ +static void showLog(LogState *state, + int logPrio, const char* tag, const char* msg) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + time_t when; + pid_t pid, tid; + + TRACE("LOG %d: %s %s", logPrio, tag, msg); + + priChar = getPriorityString(logPrio)[0]; + when = time(NULL); + pid = tid = getpid(); // find gettid()? + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&when, &tmBuf); +#else + ptm = localtime(&when); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (state->outputFormat) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%p) ", priChar, pid, (void*)tid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; prefixLen = 0; + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %-8s\n\t", timeBuf, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s %5d:%p %c/%-8s ]\n", + timeBuf, pid, (void*)tid, priChar, tag); + strcpy(suffixBuf, "\n\n"); suffixLen = 2; + break; + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, tag, pid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + } + + /* + * Figure out how many lines there will be. + */ + const char* end = msg + strlen(msg); + size_t numLines = 0; + const char* p = msg; + while (p < end) { + if (*p++ == '\n') numLines++; + } + if (p > msg && *(p-1) != '\n') numLines++; + + /* + * Create an array of iovecs large enough to write all of + * the lines with a prefix and a suffix. + */ + const size_t INLINE_VECS = 6; + const size_t MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*)); + struct iovec stackVec[INLINE_VECS]; + struct iovec* vec = stackVec; + size_t numVecs; + + if (numLines > MAX_LINES) + numLines = MAX_LINES; + + numVecs = numLines*3; // 3 iovecs per line. + if (numVecs > INLINE_VECS) { + vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines); + if (vec == NULL) { + msg = "LOG: write failed, no memory"; + numVecs = 3; + numLines = 1; + vec = stackVec; + } + } + + /* + * Fill in the iovec pointers. + */ + p = msg; + struct iovec* v = vec; + int totalLen = 0; + while (numLines > 0 && p < end) { + if (prefixLen > 0) { + v->iov_base = prefixBuf; + v->iov_len = prefixLen; + totalLen += prefixLen; + v++; + } + const char* start = p; + while (p < end && *p != '\n') p++; + if ((p-start) > 0) { + v->iov_base = (void*)start; + v->iov_len = p-start; + totalLen += p-start; + v++; + } + if (*p == '\n') p++; + if (suffixLen > 0) { + v->iov_base = suffixBuf; + v->iov_len = suffixLen; + totalLen += suffixLen; + v++; + } + numLines -= 1; + } + + /* + * Write the entire message to the log file with a single writev() call. + * We need to use this rather than a collection of printf()s on a FILE* + * because of multi-threading and multi-process issues. + * + * If the file was not opened with O_APPEND, this will produce interleaved + * output when called on the same file from multiple processes. + * + * If the file descriptor is actually a network socket, the writev() + * call may return with a partial write. Putting the writev() call in + * a loop can result in interleaved data. This can be alleviated + * somewhat by wrapping the writev call in the Mutex. + */ + + for(;;) { + int cc = writev(fileno(stderr), vec, v-vec); + + if (cc == totalLen) break; + + if (cc < 0) { + if(errno == EINTR) continue; + + /* can't really log the failure; for now, throw out a stderr */ + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + break; + } else { + /* shouldn't happen when writing to file or tty */ + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen); + break; + } + } + + /* if we allocated storage for the iovecs, free it */ + if (vec != stackVec) + free(vec); +} + + +/* + * Receive a log message. We happen to know that "vector" has three parts: + * + * priority (1 byte) + * tag (N bytes -- null-terminated ASCII string) + * message (N bytes -- null-terminated ASCII string) + */ +static ssize_t logWritev(int fd, const struct iovec* vector, int count) +{ + LogState* state; + + /* Make sure that no-one frees the LogState while we're using it. + * Also guarantees that only one thread is in showLog() at a given + * time (if it matters). + */ + lock(); + + state = fdToLogState(fd); + if (state == NULL) { + errno = EBADF; + goto error; + } + + if (state->isBinary) { + TRACE("%s: ignoring binary log\n", state->debugName); + goto bail; + } + + if (count != 3) { + TRACE("%s: writevLog with count=%d not expected\n", + state->debugName, count); + goto error; + } + + /* pull out the three fields */ + int logPrio = *(const char*)vector[0].iov_base; + const char* tag = (const char*) vector[1].iov_base; + const char* msg = (const char*) vector[2].iov_base; + + /* see if this log tag is configured */ + int i; + int minPrio = state->globalMinPriority; + for (i = 0; i < kTagSetSize; i++) { + if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN) + break; /* reached end of configured values */ + + if (strcmp(state->tagSet[i].tag, tag) == 0) { + //TRACE("MATCH tag '%s'\n", tag); + minPrio = state->tagSet[i].minPriority; + break; + } + } + + if (logPrio >= minPrio) { + showLog(state, logPrio, tag, msg); + } else { + //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); + } + +bail: + unlock(); + return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; +error: + unlock(); + return -1; +} + +/* + * Free up our state and close the fake descriptor. + */ +static int logClose(int fd) +{ + deleteFakeFd(fd); + return 0; +} + +/* + * Open a log output device and return a fake fd. + */ +static int logOpen(const char* pathName, int flags) +{ + LogState *logState; + int fd = -1; + + lock(); + + logState = createLogState(); + if (logState != NULL) { + configureInitialState(pathName, logState); + fd = logState->fakeFd; + } else { + errno = ENFILE; + } + + unlock(); + + return fd; +} + + +/* + * Runtime redirection. If this binary is running in the simulator, + * just pass log messages to the emulated device. If it's running + * outside of the simulator, write the log messages to stderr. + */ + +static int (*redirectOpen)(const char *pathName, int flags) = NULL; +static int (*redirectClose)(int fd) = NULL; +static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count) + = NULL; + +static void setRedirects() +{ + const char *ws; + + /* Wrapsim sets this environment variable on children that it's + * created using its LD_PRELOAD wrapper. + */ + ws = getenv("ANDROID_WRAPSIM"); + if (ws != NULL && strcmp(ws, "1") == 0) { + /* We're running inside wrapsim, so we can just write to the device. */ + redirectOpen = (int (*)(const char *pathName, int flags))open; + redirectClose = close; + redirectWritev = writev; + } else { + /* There's no device to delegate to; handle the logging ourselves. */ + redirectOpen = logOpen; + redirectClose = logClose; + redirectWritev = logWritev; + } +} + +int fakeLogOpen(const char *pathName, int flags) +{ + if (redirectOpen == NULL) { + setRedirects(); + } + return redirectOpen(pathName, flags); +} + +int fakeLogClose(int fd) +{ + /* Assume that open() was called first. */ + return redirectClose(fd); +} + +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) +{ + /* Assume that open() was called first. */ + return redirectWritev(fd, vector, count); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c new file mode 100644 index 0000000..80867d1 --- /dev/null +++ b/liblog/logd_write.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2007 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 +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_BUF_SIZE 1024 + +#if FAKE_LOG_DEVICE +// This will be defined when building for the host. +#define log_open(pathname, flags) fakeLogOpen(pathname, flags) +#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +#define log_close(filedes) fakeLogClose(filedes) +#else +#define log_open(pathname, flags) open(pathname, flags) +#define log_writev(filedes, vector, count) writev(filedes, vector, count) +#define log_close(filedes) close(filedes) +#endif + +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO, + LOG_ID_EVENTS, + LOG_ID_MAX +} log_id_t; + +static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr); +static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = + __write_to_log_init; +#ifdef HAVE_PTHREADS +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... is available, we're running in + * the simulator rather than a desktop tool and want to use the device. + */ +static enum { + kLogUninitialized, kLogNotAvailable, kLogAvailable +} g_log_status = kLogUninitialized; +int __android_log_dev_available(void) +{ + if (g_log_status == kLogUninitialized) { + if (access("/dev/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + int log_fd; + + if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) { + log_fd = log_fds[(int)log_id]; + } else { + return EBADF; + } + + do { + ret = log_writev(log_fd, vec, nr); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) +{ +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + + if (write_to_log == __write_to_log_init) { + log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + } + +#ifdef HAVE_PTHREADS + pthread_mutex_unlock(&log_init_lock); +#endif + + return write_to_log(log_id, vec, nr); +} + +int __android_log_write(int prio, const char *tag, const char *msg) +{ + struct iovec vec[3]; + log_id_t log_id = LOG_ID_MAIN; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!strcmp(tag, "HTC_RIL") || + !strcmp(tag, "RILJ") || + !strcmp(tag, "RILC") || + !strcmp(tag, "RILD") || + !strcmp(tag, "RIL") || + !strcmp(tag, "AT") || + !strcmp(tag, "GSM") || + !strcmp(tag, "STK")) + log_id = LOG_ID_RADIO; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + return write_to_log(log_id, vec, 3); +} + +int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap) +{ + char buf[LOG_BUF_SIZE]; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + + return __android_log_write(prio, tag, buf); +} + +int __android_log_print(int prio, const char *tag, const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + return __android_log_write(prio, tag, buf); +} + +void __android_log_assert(const char *cond, const char *tag, + const char *fmt, ...) +{ + va_list ap; + char buf[LOG_BUF_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + __android_log_write(ANDROID_LOG_FATAL, tag, buf); + + __builtin_trap(); /* trap so we have a chance to debug the situation */ +} + +int __android_log_bwrite(int32_t tag, const void *payload, size_t len) +{ + struct iovec vec[2]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = (void*)payload; + vec[1].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 2); +} + +/* + * Like __android_log_bwrite, but takes the type as well. Doesn't work + * for the general case where we're generating lists of stuff, but very + * handy if we just want to dump an integer into the log. + */ +int __android_log_btwrite(int32_t tag, char type, const void *payload, + size_t len) +{ + struct iovec vec[3]; + + vec[0].iov_base = &tag; + vec[0].iov_len = sizeof(tag); + vec[1].iov_base = &type; + vec[1].iov_len = sizeof(type); + vec[2].iov_base = (void*)payload; + vec[2].iov_len = len; + + return write_to_log(LOG_ID_EVENTS, vec, 3); +} diff --git a/liblog/logprint.c b/liblog/logprint.c new file mode 100644 index 0000000..2cf1254 --- /dev/null +++ b/liblog/logprint.c @@ -0,0 +1,972 @@ +/* //device/libs/cutils/logprint.c +** +** Copyright 2006, 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 _GNU_SOURCE /* for asprintf */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef struct FilterInfo_t { + char *mTag; + android_LogPriority mPri; + struct FilterInfo_t *p_next; +} FilterInfo; + +struct AndroidLogFormat_t { + android_LogPriority global_pri; + FilterInfo *filters; + AndroidLogPrintFormat format; +}; + +static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) +{ + FilterInfo *p_ret; + + p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); + p_ret->mTag = strdup(tag); + p_ret->mPri = pri; + + return p_ret; +} + +static void filterinfo_free(FilterInfo *p_info) +{ + if (p_info == NULL) { + return; + } + + free(p_info->mTag); + p_info->mTag = NULL; +} + +/* + * Note: also accepts 0-9 priorities + * returns ANDROID_LOG_UNKNOWN if the character is unrecognized + */ +static android_LogPriority filterCharToPri (char c) +{ + android_LogPriority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+ANDROID_LOG_SILENT)) { + pri = ANDROID_LOG_VERBOSE; + } else { + pri = (android_LogPriority)(c - '0'); + } + } else if (c == 'v') { + pri = ANDROID_LOG_VERBOSE; + } else if (c == 'd') { + pri = ANDROID_LOG_DEBUG; + } else if (c == 'i') { + pri = ANDROID_LOG_INFO; + } else if (c == 'w') { + pri = ANDROID_LOG_WARN; + } else if (c == 'e') { + pri = ANDROID_LOG_ERROR; + } else if (c == 'f') { + pri = ANDROID_LOG_FATAL; + } else if (c == 's') { + pri = ANDROID_LOG_SILENT; + } else if (c == '*') { + pri = ANDROID_LOG_DEFAULT; + } else { + pri = ANDROID_LOG_UNKNOWN; + } + + return pri; +} + +static char filterPriToChar (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return 'V'; + case ANDROID_LOG_DEBUG: return 'D'; + case ANDROID_LOG_INFO: return 'I'; + case ANDROID_LOG_WARN: return 'W'; + case ANDROID_LOG_ERROR: return 'E'; + case ANDROID_LOG_FATAL: return 'F'; + case ANDROID_LOG_SILENT: return 'S'; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return '?'; + } +} + +static android_LogPriority filterPriForTag( + AndroidLogFormat *p_format, const char *tag) +{ + FilterInfo *p_curFilter; + + for (p_curFilter = p_format->filters + ; p_curFilter != NULL + ; p_curFilter = p_curFilter->p_next + ) { + if (0 == strcmp(tag, p_curFilter->mTag)) { + if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) { + return p_format->global_pri; + } else { + return p_curFilter->mPri; + } + } + } + + return p_format->global_pri; +} + +/** for debugging */ +static void dumpFilters(AndroidLogFormat *p_format) +{ + FilterInfo *p_fi; + + for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { + char cPri = filterPriToChar(p_fi->mPri); + if (p_fi->mPri == ANDROID_LOG_DEFAULT) { + cPri = filterPriToChar(p_format->global_pri); + } + fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); + } + + fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); + +} + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int android_log_shouldPrintLine ( + AndroidLogFormat *p_format, const char *tag, android_LogPriority pri) +{ + return pri >= filterPriForTag(p_format, tag); +} + +AndroidLogFormat *android_log_format_new() +{ + AndroidLogFormat *p_ret; + + p_ret = calloc(1, sizeof(AndroidLogFormat)); + + p_ret->global_pri = ANDROID_LOG_VERBOSE; + p_ret->format = FORMAT_BRIEF; + + return p_ret; +} + +void android_log_format_free(AndroidLogFormat *p_format) +{ + FilterInfo *p_info, *p_info_old; + + p_info = p_format->filters; + + while (p_info != NULL) { + p_info_old = p_info; + p_info = p_info->p_next; + + free(p_info_old); + } + + free(p_format); +} + + + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format) +{ + p_format->format=format; +} + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char * formatString) +{ + static AndroidLogPrintFormat format; + + if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; + else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; + else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; + else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; + else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; + else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; + else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; + else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; + else format = FORMAT_OFF; + + return format; +} + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + */ + +int android_log_addFilterRule(AndroidLogFormat *p_format, + const char *filterExpression) +{ + size_t i=0; + size_t tagNameLength; + android_LogPriority pri = ANDROID_LOG_DEFAULT; + + tagNameLength = strcspn(filterExpression, ":"); + + if (tagNameLength == 0) { + goto error; + } + + if(filterExpression[tagNameLength] == ':') { + pri = filterCharToPri(filterExpression[tagNameLength+1]); + + if (pri == ANDROID_LOG_UNKNOWN) { + goto error; + } + } + + if(0 == strncmp("*", filterExpression, tagNameLength)) { + // This filter expression refers to the global filter + // The default level for this is DEBUG if the priority + // is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_DEBUG; + } + + p_format->global_pri = pri; + } else { + // for filter expressions that don't refer to the global + // filter, the default is verbose if the priority is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_VERBOSE; + } + + char *tagName; + +// Presently HAVE_STRNDUP is never defined, so the second case is always taken +// Darwin doesn't have strnup, everything else does +#ifdef HAVE_STRNDUP + tagName = strndup(filterExpression, tagNameLength); +#else + //a few extra bytes copied... + tagName = strdup(filterExpression); + tagName[tagNameLength] = '\0'; +#endif /*HAVE_STRNDUP*/ + + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; + } + + return 0; +error: + return -1; +} + + +/** + * filterString: a comma/whitespace-separated set of filter expressions + * + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int android_log_addFilterString(AndroidLogFormat *p_format, + const char *filterString) +{ + char *filterStringCopy = strdup (filterString); + char *p_cur = filterStringCopy; + char *p_ret; + int err; + + // Yes, I'm using strsep + while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { + // ignore whitespace-only entries + if(p_ret[0] != '\0') { + err = android_log_addFilterRule(p_format, p_ret); + + if (err < 0) { + goto error; + } + } + } + + free (filterStringCopy); + return 0; +error: + free (filterStringCopy); + return -1; +} + +static inline char * strip_end(char *str) +{ + char *end = str + strlen(str) - 1; + + while (end >= str && isspace(*end)) + *end-- = '\0'; + return str; +} + +/** + * Splits a wire-format buffer into an AndroidLogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int android_log_processLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry) +{ + size_t tag_len; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = buf->msg[0]; + entry->pid = buf->pid; + entry->tid = buf->tid; + entry->tag = buf->msg + 1; + tag_len = strlen(entry->tag); + entry->messageLen = buf->len - tag_len - 3; + entry->message = entry->tag + tag_len + 1; + + return 0; +} + +/* + * Extract a 4-byte value from a byte stream. + */ +static inline uint32_t get4LE(const uint8_t* src) +{ + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +/* + * Extract an 8-byte value from a byte stream. + */ +static inline uint64_t get8LE(const uint8_t* src) +{ + uint32_t low, high; + + low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); + return ((long long) high << 32) | (long long) low; +} + + +/* + * Recursively convert binary log data to printable form. + * + * This needs to be recursive because you can have lists of lists. + * + * If we run out of room, we stop processing immediately. It's important + * for us to check for space on every output element to avoid producing + * garbled output. + * + * Returns 0 on success, 1 on buffer full, -1 on failure. + */ +static int android_log_printBinaryEvent(const unsigned char** pEventData, + size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) +{ + const unsigned char* eventData = *pEventData; + size_t eventDataLen = *pEventDataLen; + char* outBuf = *pOutBuf; + size_t outBufLen = *pOutBufLen; + unsigned char type; + size_t outCount; + int result = 0; + + if (eventDataLen < 1) + return -1; + type = *eventData++; + eventDataLen--; + + //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); + + switch (type) { + case EVENT_TYPE_INT: + /* 32-bit signed int */ + { + int ival; + + if (eventDataLen < 4) + return -1; + ival = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + outCount = snprintf(outBuf, outBufLen, "%d", ival); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_LONG: + /* 64-bit signed long */ + { + long long lval; + + if (eventDataLen < 8) + return -1; + lval = get8LE(eventData); + eventData += 8; + eventDataLen -= 8; + + outCount = snprintf(outBuf, outBufLen, "%lld", lval); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_STRING: + /* UTF-8 chars, not NULL-terminated */ + { + unsigned int strLen; + + if (eventDataLen < 4) + return -1; + strLen = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + if (eventDataLen < strLen) + return -1; + + if (strLen < outBufLen) { + memcpy(outBuf, eventData, strLen); + outBuf += strLen; + outBufLen -= strLen; + } else if (outBufLen > 0) { + /* copy what we can */ + memcpy(outBuf, eventData, outBufLen); + outBuf += outBufLen; + outBufLen -= outBufLen; + goto no_room; + } + eventData += strLen; + eventDataLen -= strLen; + break; + } + case EVENT_TYPE_LIST: + /* N items, all different types */ + { + unsigned char count; + int i; + + if (eventDataLen < 1) + return -1; + + count = *eventData++; + eventDataLen--; + + if (outBufLen > 0) { + *outBuf++ = '['; + outBufLen--; + } else { + goto no_room; + } + + for (i = 0; i < count; i++) { + result = android_log_printBinaryEvent(&eventData, &eventDataLen, + &outBuf, &outBufLen); + if (result != 0) + goto bail; + + if (i < count-1) { + if (outBufLen > 0) { + *outBuf++ = ','; + outBufLen--; + } else { + goto no_room; + } + } + } + + if (outBufLen > 0) { + *outBuf++ = ']'; + outBufLen--; + } else { + goto no_room; + } + } + break; + default: + fprintf(stderr, "Unknown binary event type %d\n", type); + return -1; + } + +bail: + *pEventData = eventData; + *pEventDataLen = eventDataLen; + *pOutBuf = outBuf; + *pOutBufLen = outBufLen; + return result; + +no_room: + result = 1; + goto bail; +} + +/** + * Convert a binary log entry to ASCII form. + * + * For convenience we mimic the processLogBuffer API. There is no + * pre-defined output length for the binary data, since we're free to format + * it however we choose, which means we can't really use a fixed-size buffer + * here. + */ +int android_log_processBinaryLogBuffer(struct logger_entry *buf, + AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf, + int messageBufLen) +{ + size_t inCount; + unsigned int tagIndex; + const unsigned char* eventData; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = ANDROID_LOG_INFO; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * Pull the tag out. + */ + eventData = (const unsigned char*) buf->msg; + inCount = buf->len; + if (inCount < 4) + return -1; + tagIndex = get4LE(eventData); + eventData += 4; + inCount -= 4; + + if (map != NULL) { + entry->tag = android_lookupEventTag(map, tagIndex); + } else { + entry->tag = NULL; + } + + /* + * If we don't have a map, or didn't find the tag number in the map, + * stuff a generated tag value into the start of the output buffer and + * shift the buffer pointers down. + */ + if (entry->tag == NULL) { + int tagLen; + + tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); + entry->tag = messageBuf; + messageBuf += tagLen+1; + messageBufLen -= tagLen+1; + } + + /* + * Format the event log data into the buffer. + */ + char* outBuf = messageBuf; + size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ + int result; + result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, + &outRemaining); + if (result < 0) { + fprintf(stderr, "Binary log entry conversion failed\n"); + return -1; + } else if (result == 1) { + if (outBuf > messageBuf) { + /* leave an indicator */ + *(outBuf-1) = '!'; + } else { + /* no room to output anything at all */ + *outBuf++ = '!'; + outRemaining--; + } + /* pretend we ate all the data */ + inCount = 0; + } + + /* eat the silly terminating '\n' */ + if (inCount == 1 && *eventData == '\n') { + eventData++; + inCount--; + } + + if (inCount != 0) { + fprintf(stderr, + "Warning: leftover binary log data (%d bytes)\n", inCount); + } + + /* + * Terminate the buffer. The NUL byte does not count as part of + * entry->messageLen. + */ + *outBuf = '\0'; + entry->messageLen = outBuf - messageBuf; + assert(entry->messageLen == (messageBufLen-1) - outRemaining); + + entry->message = messageBuf; + + return 0; +} + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *android_log_formatLogLine ( + AndroidLogFormat *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const AndroidLogEntry *entry, + size_t *p_outLength) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char headerBuf[128]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + int prefixSuffixIsHeaderFooter = 0; + char * ret = NULL; + + priChar = filterPriToChar(entry->priority); + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&(entry->tv_sec), &tmBuf); +#else + ptm = localtime(&(entry->tv_sec)); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (p_format->format) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, entry->tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, entry->pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", entry->tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%p) ", priChar, entry->pid, (void*)entry->tid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; + prefixLen = 0; + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, + priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + (int)entry->pid, (int)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s.%03ld %5d:%p %c/%-8s ]\n", + timeBuf, entry->tv_nsec / 1000000, entry->pid, + (void*)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n\n"); + suffixLen = 2; + prefixSuffixIsHeaderFooter = 1; + break; + case FORMAT_BRIEF: + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + } + + /* the following code is tragically unreadable */ + + size_t numLines; + size_t i; + char *p; + size_t bufferSize; + const char *pm; + + if (prefixSuffixIsHeaderFooter) { + // we're just wrapping message with a header/footer + numLines = 1; + } else { + pm = entry->message; + numLines = 0; + + // The line-end finding here must match the line-end finding + // in for ( ... numLines...) loop below + while (pm < (entry->message + entry->messageLen)) { + if (*pm++ == '\n') numLines++; + } + // plus one line for anything not newline-terminated at the end + if (pm > entry->message && *(pm-1) != '\n') numLines++; + } + + // this is an upper bound--newlines in message may be counted + // extraneously + bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + + if (defaultBufferSize >= bufferSize) { + ret = defaultBuffer; + } else { + ret = (char *)malloc(bufferSize); + + if (ret == NULL) { + return ret; + } + } + + ret[0] = '\0'; /* to start strcat off */ + + p = ret; + pm = entry->message; + + if (prefixSuffixIsHeaderFooter) { + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + strcat(p, suffixBuf); + p += suffixLen; + } else { + while(pm < (entry->message + entry->messageLen)) { + const char *lineStart; + size_t lineLen; + + lineStart = pm; + + // Find the next end-of-line in message + while (pm < (entry->message + entry->messageLen) + && *pm != '\n') pm++; + lineLen = pm - lineStart; + + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, lineStart, lineLen); + p += lineLen; + strcat(p, suffixBuf); + p += suffixLen; + + if (*pm == '\n') pm++; + } + } + + if (p_outLength != NULL) { + *p_outLength = p - ret; + } + + return ret; +} + +/** + * Either print or do not print log line, based on filter + * + * Returns count bytes written + */ + +int android_log_filterAndPrintLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry) +{ + int ret; + char defaultBuffer[512]; + char *outBuffer = NULL; + size_t totalLen; + + if (0 == android_log_shouldPrintLine(p_format, entry->tag, + entry->priority)) { + return 0; + } + + outBuffer = android_log_formatLogLine(p_format, defaultBuffer, + sizeof(defaultBuffer), entry, &totalLen); + + if (!outBuffer) + return -1; + + do { + ret = write(fd, outBuffer, totalLen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + ret = 0; + goto done; + } + + if (((size_t)ret) < totalLen) { + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, + (int)totalLen); + goto done; + } + +done: + if (outBuffer != defaultBuffer) { + free(outBuffer); + } + + return ret; +} + + + +void logprint_run_tests() +{ +#if 0 + + fprintf(stderr, "tests disabled\n"); + +#else + + int err; + const char *tag; + AndroidLogFormat *p_format; + + p_format = android_log_format_new(); + + fprintf(stderr, "running tests\n"); + + tag = "random"; + + android_log_addFilterRule(p_format,"*:i"); + + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "random"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); + assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + err = android_log_addFilterRule(p_format, "random:z"); + assert (err < 0); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + err = android_log_addFilterString(p_format, " "); + assert(err == 0); + assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + + // note trailing space + err = android_log_addFilterString(p_format, "*:s random:d "); + assert(err == 0); + assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + + err = android_log_addFilterString(p_format, "*:s random:z"); + assert(err < 0); + + +#if 0 + char *ret; + char defaultBuffer[512]; + + ret = android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); +#endif + + + fprintf(stderr, "tests complete\n"); +#endif +} -- cgit v1.1