M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,14 @@
lib_LIBRARIES := liblog.a
lib_LTLIBRARIES := liblog.la
liblog_a_CPPFLAGS := -I../include
liblog_a_CPPFLAGS += -DHAVE_PTHREADS
liblog_a_CPPFLAGS += -DHAVE_SYS_UIO_H
liblog_a_CPPFLAGS += -include ../include/arch/linux-arm/OEConfig.h
liblog_la_CPPFLAGS = $(liblog_a_CPPFLAGS)
requiredlibs = -lpthread
liblog_a_SOURCES = logd_write.c logprint.c event_tag_map.c
liblog_la_SOURCES = $(liblog_a_SOURCES)

190
system/core/liblog/NOTICE Normal file
View File

@ -0,0 +1,190 @@
Copyright (c) 2005-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.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -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 <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <assert.h>
#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);
}
}

View File

@ -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 <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#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)*numVecs);
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);
}

View File

@ -0,0 +1,269 @@
/*
* Copyright (C) 2007 The Android Open Source Project
* Copyright (c) 2009, The Linux Foundation. All rights reserved.
*
* 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 <time.h>
#include <stdio.h>
#ifdef HAVE_PTHREADS
#include <pthread.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <cutils/logger.h>
#include <cutils/logd.h>
#include <cutils/log.h>
#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
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, -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);
log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, 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;
}
if (log_fds[LOG_ID_SYSTEM] < 0) {
log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
}
}
#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") ||
!strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
!strcmp(tag, "AT") ||
!strcmp(tag, "GSM") ||
!strcmp(tag, "STK") ||
!strcmp(tag, "CDMA") ||
!strcmp(tag, "PHONE") ||
!strcmp(tag, "SMS"))
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_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
struct iovec vec[3];
if (!tag)
tag = "";
/* XXX: This needs to go! */
if (!strcmp(tag, "HTC_RIL") ||
!strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
!strcmp(tag, "AT") ||
!strcmp(tag, "GSM") ||
!strcmp(tag, "STK") ||
!strcmp(tag, "CDMA") ||
!strcmp(tag, "PHONE") ||
!strcmp(tag, "SMS"))
bufID = 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(bufID, 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);
}
int __android_log_buf_print(int bufID, 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_buf_write(bufID, 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);
}

View File

@ -0,0 +1,976 @@
/* //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 <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <arpa/inet.h>
#include <cutils/logd.h>
#include <cutils/logprint.h>
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;
}
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;
}
/* snprintf has a weird return value. It returns what would have been
* written given a large enough buffer. In the case that the prefix is
* longer then our buffer(128), it messes up the calculations below
* possibly causing heap corruption. To avoid this we double check and
* set the length at the maximum (size minus null byte)
*/
if(prefixLen >= sizeof(prefixBuf))
prefixLen = sizeof(prefixBuf) - 1;
if(suffixLen >= sizeof(suffixBuf))
suffixLen = sizeof(suffixBuf) - 1;
/* 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_printLogLine(
AndroidLogFormat *p_format,
int fd,
const AndroidLogEntry *entry)
{
int ret;
char defaultBuffer[512];
char *outBuffer = NULL;
size_t totalLen;
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
}