Home | History | Annotate | Download | only in wrapsim
      1 /*
      2  * Copyright 2007 The Android Open Source Project
      3  *
      4  * Log devices.  We want to filter and display messages, with separate
      5  * treatment for "debug" and "event" logs.
      6  *
      7  * All messages are just dumped to stderr.
      8  */
      9 #include "Common.h"
     10 
     11 #include "cutils/logd.h"
     12 
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #include <ctype.h>
     16 
     17 #include <fcntl.h>
     18 
     19 #define kMaxTagLen  16      /* from utils/Log.cpp */
     20 
     21 #define kTagSetSize 16      /* arbitrary */
     22 
     23 /* from utils/Log.cpp */
     24 typedef enum {
     25     FORMAT_OFF = 0,
     26     FORMAT_BRIEF,
     27     FORMAT_PROCESS,
     28     FORMAT_TAG,
     29     FORMAT_THREAD,
     30     FORMAT_RAW,
     31     FORMAT_TIME,
     32     FORMAT_LONG
     33 } LogFormat;
     34 
     35 
     36 /*
     37  * Log driver state.
     38  */
     39 typedef struct LogState {
     40     /* nonzero if this is a binary log */
     41     int     isBinary;
     42 
     43     /* global minimum priority */
     44     int     globalMinPriority;
     45 
     46     /* output format */
     47     LogFormat outputFormat;
     48 
     49     /* tags and priorities */
     50     struct {
     51         char    tag[kMaxTagLen];
     52         int     minPriority;
     53     } tagSet[kTagSetSize];
     54 } LogState;
     55 
     56 
     57 /*
     58  * Configure logging based on ANDROID_LOG_TAGS environment variable.  We
     59  * need to parse a string that looks like
     60  *
     61  *  '*:v jdwp:d dalvikvm:d dalvikvm-gc:i dalvikvmi:i
     62  *
     63  * The tag (or '*' for the global level) comes first, followed by a colon
     64  * and a letter indicating the minimum priority level we're expected to log.
     65  * This can be used to reveal or conceal logs with specific tags.
     66  *
     67  * We also want to check ANDROID_PRINTF_LOG to determine how the output
     68  * will look.
     69  */
     70 static void configureInitialState(const char* pathName, LogState* logState)
     71 {
     72     static const int kDevLogLen = 9;    /* strlen("/dev/log/") */
     73 
     74     /* identify binary logs */
     75     if (strcmp(pathName + kDevLogLen, "events") == 0) {
     76         logState->isBinary = 1;
     77     }
     78 
     79     /* global min priority defaults to "info" level */
     80     logState->globalMinPriority = ANDROID_LOG_INFO;
     81 
     82     /*
     83      * This is based on the utils/Log.cpp code.
     84      */
     85     const char* tags = getenv("ANDROID_LOG_TAGS");
     86     wsLog("Found ANDROID_LOG_TAGS='%s'\n", tags);
     87     if (tags != NULL) {
     88         int entry = 0;
     89 
     90         while (*tags != '\0') {
     91             char tagName[kMaxTagLen];
     92             int i, minPrio;
     93 
     94             while (isspace(*tags))
     95                 tags++;
     96 
     97             i = 0;
     98             while (*tags != '\0' && !isspace(*tags) && *tags != ':' &&
     99                 i < kMaxTagLen)
    100             {
    101                 tagName[i++] = *tags++;
    102             }
    103             if (i == kMaxTagLen) {
    104                 wsLog("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1);
    105                 return;
    106             }
    107             tagName[i] = '\0';
    108 
    109             /* default priority, if there's no ":" part; also zero out '*' */
    110             minPrio = ANDROID_LOG_VERBOSE;
    111             if (tagName[0] == '*' && tagName[1] == '\0') {
    112                 minPrio = ANDROID_LOG_DEBUG;
    113                 tagName[0] = '\0';
    114             }
    115 
    116             if (*tags == ':') {
    117                 tags++;
    118                 if (*tags >= '0' && *tags <= '9') {
    119                     if (*tags >= ('0' + ANDROID_LOG_SILENT))
    120                         minPrio = ANDROID_LOG_VERBOSE;
    121                     else
    122                         minPrio = *tags - '\0';
    123                 } else {
    124                     switch (*tags) {
    125                     case 'v':   minPrio = ANDROID_LOG_VERBOSE;  break;
    126                     case 'd':   minPrio = ANDROID_LOG_DEBUG;    break;
    127                     case 'i':   minPrio = ANDROID_LOG_INFO;     break;
    128                     case 'w':   minPrio = ANDROID_LOG_WARN;     break;
    129                     case 'e':   minPrio = ANDROID_LOG_ERROR;    break;
    130                     case 'f':   minPrio = ANDROID_LOG_FATAL;    break;
    131                     case 's':   minPrio = ANDROID_LOG_SILENT;   break;
    132                     default:    minPrio = ANDROID_LOG_DEFAULT;  break;
    133                     }
    134                 }
    135 
    136                 tags++;
    137                 if (*tags != '\0' && !isspace(*tags)) {
    138                     wsLog("ERROR: garbage in tag env; expected whitespace\n");
    139                     wsLog("       env='%s'\n", tags);
    140                     return;
    141                 }
    142             }
    143 
    144             if (tagName[0] == 0) {
    145                 logState->globalMinPriority = minPrio;
    146                 wsLog("+++ global min prio %d\n", logState->globalMinPriority);
    147             } else {
    148                 logState->tagSet[entry].minPriority = minPrio;
    149                 strcpy(logState->tagSet[entry].tag, tagName);
    150                 wsLog("+++ entry %d: %s:%d\n",
    151                     entry,
    152                     logState->tagSet[entry].tag,
    153                     logState->tagSet[entry].minPriority);
    154                 entry++;
    155             }
    156         }
    157     }
    158 
    159 
    160     /*
    161      * Taken from utils/Log.cpp
    162      */
    163     const char* fstr = getenv("ANDROID_PRINTF_LOG");
    164     LogFormat format;
    165     if (fstr == NULL) {
    166         format = FORMAT_BRIEF;
    167     } else {
    168         if (strcmp(fstr, "brief") == 0)
    169             format = FORMAT_BRIEF;
    170         else if (strcmp(fstr, "process") == 0)
    171             format = FORMAT_PROCESS;
    172         else if (strcmp(fstr, "tag") == 0)
    173             format = FORMAT_PROCESS;
    174         else if (strcmp(fstr, "thread") == 0)
    175             format = FORMAT_PROCESS;
    176         else if (strcmp(fstr, "raw") == 0)
    177             format = FORMAT_PROCESS;
    178         else if (strcmp(fstr, "time") == 0)
    179             format = FORMAT_PROCESS;
    180         else if (strcmp(fstr, "long") == 0)
    181             format = FORMAT_PROCESS;
    182         else
    183             format = (LogFormat) atoi(fstr);        // really?!
    184     }
    185 
    186     logState->outputFormat = format;
    187 }
    188 
    189 /*
    190  * Free up the state structure.
    191  */
    192 static void freeState(LogState* logState)
    193 {
    194     free(logState);
    195 }
    196 
    197 /*
    198  * Return a human-readable string for the priority level.  Always returns
    199  * a valid string.
    200  */
    201 static const char* getPriorityString(int priority)
    202 {
    203     /* the first character of each string should be unique */
    204     static const char* priorityStrings[] = {
    205         "Verbose", "Debug", "Info", "Warn", "Error", "Assert"
    206     };
    207     int idx;
    208 
    209     idx = (int) priority - (int) ANDROID_LOG_VERBOSE;
    210     if (idx < 0 ||
    211         idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0])))
    212         return "?unknown?";
    213     return priorityStrings[idx];
    214 }
    215 
    216 /*
    217  * Show a log message.  We write it to stderr and send a copy to the
    218  * simulator front-end for the log window.
    219  *
    220  * Taken from utils/Log.cpp.
    221  */
    222 static void showLog(FakeDev* dev, int logPrio, const char* tag, const char* msg)
    223 {
    224     LogState* state = (LogState*) dev->state;
    225 
    226 #if defined(HAVE_LOCALTIME_R)
    227     struct tm tmBuf;
    228 #endif
    229     struct tm* ptm;
    230     char timeBuf[32];
    231     char prefixBuf[128], suffixBuf[128];
    232     char priChar;
    233     time_t when;
    234     pid_t pid, tid;
    235 
    236     //wsLog("LOG %d: %s %s", logPrio, tag, msg);
    237     wsPostLogMessage(logPrio, tag, msg);
    238 
    239     priChar = getPriorityString(logPrio)[0];
    240     when = time(NULL);
    241     pid = tid = getpid();       // find gettid()?
    242 
    243     /*
    244      * Get the current date/time in pretty form
    245      *
    246      * It's often useful when examining a log with "less" to jump to
    247      * a specific point in the file by searching for the date/time stamp.
    248      * For this reason it's very annoying to have regexp meta characters
    249      * in the time stamp.  Don't use forward slashes, parenthesis,
    250      * brackets, asterisks, or other special chars here.
    251      */
    252 #if defined(HAVE_LOCALTIME_R)
    253     ptm = localtime_r(&when, &tmBuf);
    254 #else
    255     ptm = localtime(&when);
    256 #endif
    257     //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm);
    258     strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm);
    259 
    260     /*
    261      * Construct a buffer containing the log header and log message.
    262      */
    263     size_t prefixLen, suffixLen;
    264 
    265     switch (state->outputFormat) {
    266     case FORMAT_TAG:
    267         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    268             "%c/%-8s: ", priChar, tag);
    269         strcpy(suffixBuf, "\n"); suffixLen = 1;
    270         break;
    271     case FORMAT_PROCESS:
    272         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    273             "%c(%5d) ", priChar, pid);
    274         suffixLen = snprintf(suffixBuf, sizeof(suffixBuf),
    275             "  (%s)\n", tag);
    276         break;
    277     case FORMAT_THREAD:
    278         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    279             "%c(%5d:%p) ", priChar, pid, (void*)tid);
    280         strcpy(suffixBuf, "\n"); suffixLen = 1;
    281         break;
    282     case FORMAT_RAW:
    283         prefixBuf[0] = 0; prefixLen = 0;
    284         strcpy(suffixBuf, "\n"); suffixLen = 1;
    285         break;
    286     case FORMAT_TIME:
    287         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    288             "%s %-8s\n\t", timeBuf, tag);
    289         strcpy(suffixBuf, "\n"); suffixLen = 1;
    290         break;
    291     case FORMAT_LONG:
    292         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    293             "[ %s %5d:%p %c/%-8s ]\n",
    294             timeBuf, pid, (void*)tid, priChar, tag);
    295         strcpy(suffixBuf, "\n\n"); suffixLen = 2;
    296         break;
    297     default:
    298         prefixLen = snprintf(prefixBuf, sizeof(prefixBuf),
    299             "%c/%-8s(%5d): ", priChar, tag, pid);
    300         strcpy(suffixBuf, "\n"); suffixLen = 1;
    301         break;
    302      }
    303 
    304     /*
    305      * Figure out how many lines there will be.
    306      */
    307     const char* end = msg + strlen(msg);
    308     size_t numLines = 0;
    309     const char* p = msg;
    310     while (p < end) {
    311         if (*p++ == '\n') numLines++;
    312     }
    313     if (p > msg && *(p-1) != '\n') numLines++;
    314 
    315     /*
    316      * Create an array of iovecs large enough to write all of
    317      * the lines with a prefix and a suffix.
    318      */
    319     const size_t INLINE_VECS = 6;
    320     struct iovec stackVec[INLINE_VECS];
    321     struct iovec* vec = stackVec;
    322 
    323     numLines *= 3;  // 3 iovecs per line.
    324     if (numLines > INLINE_VECS) {
    325         vec = (struct iovec*)malloc(sizeof(struct iovec)*numLines);
    326         if (vec == NULL) {
    327             msg = "LOG: write failed, no memory";
    328             numLines = 3;
    329         }
    330     }
    331 
    332     /*
    333      * Fill in the iovec pointers.
    334      */
    335     p = msg;
    336     struct iovec* v = vec;
    337     int totalLen = 0;
    338     while (p < end) {
    339         if (prefixLen > 0) {
    340             v->iov_base = prefixBuf;
    341             v->iov_len = prefixLen;
    342             totalLen += prefixLen;
    343             v++;
    344         }
    345         const char* start = p;
    346         while (p < end && *p != '\n') p++;
    347         if ((p-start) > 0) {
    348             v->iov_base = (void*)start;
    349             v->iov_len = p-start;
    350             totalLen += p-start;
    351             v++;
    352         }
    353         if (*p == '\n') p++;
    354         if (suffixLen > 0) {
    355             v->iov_base = suffixBuf;
    356             v->iov_len = suffixLen;
    357             totalLen += suffixLen;
    358             v++;
    359         }
    360     }
    361 
    362     /*
    363      * Write the entire message to the log file with a single writev() call.
    364      * We need to use this rather than a collection of printf()s on a FILE*
    365      * because of multi-threading and multi-process issues.
    366      *
    367      * If the file was not opened with O_APPEND, this will produce interleaved
    368      * output when called on the same file from multiple processes.
    369      *
    370      * If the file descriptor is actually a network socket, the writev()
    371      * call may return with a partial write.  Putting the writev() call in
    372      * a loop can result in interleaved data.  This can be alleviated
    373      * somewhat by wrapping the writev call in the Mutex.
    374      */
    375 
    376     for(;;) {
    377         int cc;
    378 
    379         cc = writev(fileno(stderr), vec, v-vec);
    380         if (cc == totalLen) break;
    381 
    382         if (cc < 0) {
    383             if(errno == EINTR) continue;
    384 
    385                 /* can't really log the failure; for now, throw out a stderr */
    386             fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
    387             break;
    388         } else {
    389                 /* shouldn't happen when writing to file or tty */
    390             fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen);
    391             break;
    392         }
    393     }
    394 
    395     /* if we allocated storage for the iovecs, free it */
    396     if (vec != stackVec)
    397         free(vec);
    398 }
    399 
    400 
    401 /*
    402  * Receive a log message.  We happen to know that "vector" has three parts:
    403  *
    404  *  priority (1 byte)
    405  *  tag (N bytes -- null-terminated ASCII string)
    406  *  message (N bytes -- null-terminated ASCII string)
    407  */
    408 static ssize_t writevLog(FakeDev* dev, int fd, const struct iovec* vector,
    409     int count)
    410 {
    411     LogState* state = (LogState*) dev->state;
    412     int ret = 0;
    413 
    414     if (state->isBinary) {
    415         wsLog("%s: ignoring binary log\n", dev->debugName);
    416         goto bail;
    417     }
    418 
    419     if (count != 3) {
    420         wsLog("%s: writevLog with count=%d not expected\n",
    421             dev->debugName, count);
    422         errno = EINVAL;
    423         return -1;
    424     }
    425 
    426     /* pull out the three fields */
    427     int logPrio = *(const char*)vector[0].iov_base;
    428     const char* tag = (const char*) vector[1].iov_base;
    429     const char* msg = (const char*) vector[2].iov_base;
    430 
    431     /* see if this log tag is configured */
    432     int i;
    433     int minPrio = state->globalMinPriority;
    434     for (i = 0; i < kTagSetSize; i++) {
    435         if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN)
    436             break;      /* reached end of configured values */
    437 
    438         if (strcmp(state->tagSet[i].tag, tag) == 0) {
    439             //wsLog("MATCH tag '%s'\n", tag);
    440             minPrio = state->tagSet[i].minPriority;
    441             break;
    442         }
    443     }
    444 
    445     if (logPrio >= minPrio) {
    446         showLog(dev, logPrio, tag, msg);
    447     } else {
    448         //wsLog("+++ NOLOG(%d): %s %s", logPrio, tag, msg);
    449     }
    450 
    451 bail:
    452     for (i = 0; i < count; i++)
    453         ret += vector[i].iov_len;
    454     return ret;
    455 }
    456 
    457 /*
    458  * Free up our state before closing down the fake descriptor.
    459  */
    460 static int closeLog(FakeDev* dev, int fd)
    461 {
    462     freeState((LogState*)dev->state);
    463     dev->state = NULL;
    464     return 0;
    465 }
    466 
    467 /*
    468  * Open a log output device.
    469  */
    470 FakeDev* wsOpenDevLog(const char* pathName, int flags)
    471 {
    472     FakeDev* newDev = wsCreateFakeDev(pathName);
    473     if (newDev != NULL) {
    474         newDev->writev = writevLog;
    475         newDev->close = closeLog;
    476 
    477         LogState* logState = calloc(1, sizeof(LogState));
    478 
    479         configureInitialState(pathName, logState);
    480         newDev->state = logState;
    481     }
    482 
    483     return newDev;
    484 }
    485 
    486