blob: e70754e17ce05e5d5da5a457a1a1f683e5de436b [file] [log] [blame]
/*
* 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);
}
}