Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a1062c72 authored by Mark Salyzyn's avatar Mark Salyzyn
Browse files

logcat: Incorporate liblog reading API

Change-Id: I1e1a55d13ac55350e8d54bfe92b392de07f26a5d
parent a63f927c
Loading
Loading
Loading
Loading
+94 −238
Original line number Diff line number Diff line
// Copyright 2006 The Android Open Source Project
// Copyright 2006-2013 The Android Open Source Project

#include <log/log.h>
#include <log/logger.h>
#include <log/logd.h>
#include <log/logprint.h>
@@ -24,65 +25,27 @@
#define DEFAULT_MAX_ROTATED_LOGS 4

static AndroidLogFormat * g_logformat;
static bool g_nonblock = false;
static int g_tail_lines = 0;

/* logd prefixes records with a length field */
#define RECORD_LENGTH_FIELD_SIZE_BYTES sizeof(uint32_t)

#define LOG_FILE_DIR    "/dev/log/"

struct queued_entry_t {
    union {
        unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
        struct logger_entry entry __attribute__((aligned(4)));
    };
    queued_entry_t* next;

    queued_entry_t() {
        next = NULL;
    }
};

static int cmp(queued_entry_t* a, queued_entry_t* b) {
    int n = a->entry.sec - b->entry.sec;
    if (n != 0) {
        return n;
    }
    return a->entry.nsec - b->entry.nsec;
}

struct log_device_t {
    char* device;
    const char* device;
    bool binary;
    int fd;
    struct logger *logger;
    struct logger_list *logger_list;
    bool printed;
    char label;

    queued_entry_t* queue;
    log_device_t* next;

    log_device_t(char* d, bool b, char l) {
    log_device_t(const char* d, bool b, char l) {
        device = d;
        binary = b;
        label = l;
        queue = NULL;
        next = NULL;
        printed = false;
    }

    void enqueue(queued_entry_t* entry) {
        if (this->queue == NULL) {
            this->queue = entry;
        } else {
            queued_entry_t** e = &this->queue;
            while (*e && cmp(entry, *e) >= 0) {
                e = &((*e)->next);
            }
            entry->next = *e;
            *e = entry;
        }
    }
};

namespace android {
@@ -147,17 +110,14 @@ static void rotateLogs()

}

void printBinary(struct logger_entry *buf)
void printBinary(struct log_msg *buf)
{
    size_t size = sizeof(logger_entry) + buf->len;
    int ret;
    size_t size = buf->len();

    do {
        ret = write(g_outFD, buf, size);
    } while (ret < 0 && errno == EINTR);
    TEMP_FAILURE_RETRY(write(g_outFD, buf, size));
}

static void processBuffer(log_device_t* dev, struct logger_entry *buf)
static void processBuffer(log_device_t* dev, struct log_msg *buf)
{
    int bytesWritten = 0;
    int err;
@@ -165,12 +125,14 @@ static void processBuffer(log_device_t* dev, struct logger_entry *buf)
    char binaryMsgBuf[1024];

    if (dev->binary) {
        err = android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,
                binaryMsgBuf, sizeof(binaryMsgBuf));
        err = android_log_processBinaryLogBuffer(&buf->entry_v1, &entry,
                                                 g_eventTagMap,
                                                 binaryMsgBuf,
                                                 sizeof(binaryMsgBuf));
        //printf(">>> pri=%d len=%d msg='%s'\n",
        //    entry.priority, entry.messageLen, entry.message);
    } else {
        err = android_log_processLogBuffer(buf, &entry);
        err = android_log_processLogBuffer(&buf->entry_v1, &entry);
    }
    if (err < 0) {
        goto error;
@@ -208,20 +170,13 @@ error:
    return;
}

static void chooseFirst(log_device_t* dev, log_device_t** firstdev) {
    for (*firstdev = NULL; dev != NULL; dev = dev->next) {
        if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) {
            *firstdev = dev;
        }
    }
}

static void maybePrintStart(log_device_t* dev) {
    if (!dev->printed) {
        dev->printed = true;
        if (g_devCount > 1 && !g_printBinary) {
            char buf[1024];
            snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device);
            snprintf(buf, sizeof(buf), "--------- beginning of %s\n",
                     dev->device);
            if (write(g_outFD, buf, strlen(buf)) < 0) {
                perror("output error");
                exit(-1);
@@ -230,145 +185,6 @@ static void maybePrintStart(log_device_t* dev) {
    }
}

static void skipNextEntry(log_device_t* dev) {
    maybePrintStart(dev);
    queued_entry_t* entry = dev->queue;
    dev->queue = entry->next;
    delete entry;
}

static void printNextEntry(log_device_t* dev) {
    maybePrintStart(dev);
    if (g_printBinary) {
        printBinary(&dev->queue->entry);
    } else {
        processBuffer(dev, &dev->queue->entry);
    }
    skipNextEntry(dev);
}

static void readLogLines(log_device_t* devices)
{
    log_device_t* dev;
    int max = 0;
    int ret;
    int queued_lines = 0;
    bool sleep = false;

    int result;
    fd_set readset;

    for (dev=devices; dev; dev = dev->next) {
        if (dev->fd > max) {
            max = dev->fd;
        }
    }

    while (1) {
        do {
            timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR.
            FD_ZERO(&readset);
            for (dev=devices; dev; dev = dev->next) {
                FD_SET(dev->fd, &readset);
            }
            result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout);
        } while (result == -1 && errno == EINTR);

        if (result >= 0) {
            for (dev=devices; dev; dev = dev->next) {
                if (FD_ISSET(dev->fd, &readset)) {
                    queued_entry_t* entry = new queued_entry_t();
                    /* NOTE: driver guarantees we read exactly one full entry */
                    ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN);
                    if (ret < 0) {
                        if (errno == EINTR) {
                            delete entry;
                            goto next;
                        }
                        if (errno == EAGAIN) {
                            delete entry;
                            break;
                        }
                        perror("logcat read");
                        exit(EXIT_FAILURE);
                    }
                    else if (!ret) {
                        fprintf(stderr, "read: Unexpected EOF!\n");
                        exit(EXIT_FAILURE);
                    }
                    else if (entry->entry.len != ret - sizeof(struct logger_entry)) {
                        fprintf(stderr, "read: unexpected length. Expected %d, got %d\n",
                                entry->entry.len, ret - sizeof(struct logger_entry));
                        exit(EXIT_FAILURE);
                    }

                    entry->entry.msg[entry->entry.len] = '\0';

                    dev->enqueue(entry);
                    ++queued_lines;
                }
            }

            if (result == 0) {
                // we did our short timeout trick and there's nothing new
                // print everything we have and wait for more data
                sleep = true;
                while (true) {
                    chooseFirst(devices, &dev);
                    if (dev == NULL) {
                        break;
                    }
                    if (g_tail_lines == 0 || queued_lines <= g_tail_lines) {
                        printNextEntry(dev);
                    } else {
                        skipNextEntry(dev);
                    }
                    --queued_lines;
                }

                // the caller requested to just dump the log and exit
                if (g_nonblock) {
                    return;
                }
            } else {
                // print all that aren't the last in their list
                sleep = false;
                while (g_tail_lines == 0 || queued_lines > g_tail_lines) {
                    chooseFirst(devices, &dev);
                    if (dev == NULL || dev->queue->next == NULL) {
                        break;
                    }
                    if (g_tail_lines == 0) {
                        printNextEntry(dev);
                    } else {
                        skipNextEntry(dev);
                    }
                    --queued_lines;
                }
            }
        }
next:
        ;
    }
}

static int clearLog(int logfd)
{
    return ioctl(logfd, LOGGER_FLUSH_LOG);
}

/* returns the total size of the log's ring buffer */
static int getLogSize(int logfd)
{
    return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE);
}

/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */
static int getLogReadableSize(int logfd)
{
    return ioctl(logfd, LOGGER_GET_LOG_LEN);
}

static void setupOutput()
{

@@ -465,6 +281,8 @@ int main(int argc, char **argv)
    log_device_t* devices = NULL;
    log_device_t* dev;
    bool needBinary = false;
    struct logger_list *logger_list;
    int tail_lines = 0;

    g_logformat = android_log_format_new();

@@ -499,12 +317,12 @@ int main(int argc, char **argv)
            break;

            case 'd':
                g_nonblock = true;
                mode = O_RDONLY | O_NDELAY;
            break;

            case 't':
                g_nonblock = true;
                g_tail_lines = atoi(optarg);
                mode = O_RDONLY | O_NDELAY;
                tail_lines = atoi(optarg);
            break;

            case 'g':
@@ -512,10 +330,6 @@ int main(int argc, char **argv)
            break;

            case 'b': {
                char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1);
                strcpy(buf, LOG_FILE_DIR);
                strcat(buf, optarg);

                bool binary = strcmp(optarg, "events") == 0;
                if (binary) {
                    needBinary = true;
@@ -526,9 +340,9 @@ int main(int argc, char **argv)
                    while (dev->next) {
                        dev = dev->next;
                    }
                    dev->next = new log_device_t(buf, binary, optarg[0]);
                    dev->next = new log_device_t(optarg, binary, optarg[0]);
                } else {
                    devices = new log_device_t(buf, binary, optarg[0]);
                    devices = new log_device_t(optarg, binary, optarg[0]);
                }
                android::g_devCount++;
            }
@@ -659,14 +473,10 @@ int main(int argc, char **argv)
    }

    if (!devices) {
        devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm');
        devices = new log_device_t("main", false, 'm');
        android::g_devCount = 1;
        int accessmode =
                  (mode & O_RDONLY) ? R_OK : 0
                | (mode & O_WRONLY) ? W_OK : 0;
        // only add this if it's available
        if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) {
            devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's');
        if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
            devices->next = new log_device_t("system", false, 's');
            android::g_devCount++;
        }
    }
@@ -728,19 +538,21 @@ int main(int argc, char **argv)
    }

    dev = devices;
    logger_list = android_logger_list_alloc(mode, tail_lines, 0);
    while (dev) {
        dev->fd = open(dev->device, mode);
        if (dev->fd < 0) {
            fprintf(stderr, "Unable to open log device '%s': %s\n",
                dev->device, strerror(errno));
        dev->logger_list = logger_list;
        dev->logger = android_logger_open(logger_list,
                                          android_name_to_log_id(dev->device));
        if (!dev->logger) {
            fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
            exit(EXIT_FAILURE);
        }

        if (clearLog) {
            int ret;
            ret = android::clearLog(dev->fd);
            ret = android_logger_clear(dev->logger);
            if (ret) {
                perror("ioctl");
                perror("clearLog");
                exit(EXIT_FAILURE);
            }
        }
@@ -748,15 +560,15 @@ int main(int argc, char **argv)
        if (getLogSize) {
            int size, readable;

            size = android::getLogSize(dev->fd);
            size = android_logger_get_log_size(dev->logger);
            if (size < 0) {
                perror("ioctl");
                perror("getLogSize");
                exit(EXIT_FAILURE);
            }

            readable = android::getLogReadableSize(dev->fd);
            readable = android_logger_get_log_readable_size(dev->logger);
            if (readable < 0) {
                perror("ioctl");
                perror("getLogReadableSize");
                exit(EXIT_FAILURE);
            }

@@ -783,7 +595,51 @@ int main(int argc, char **argv)
    if (needBinary)
        android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE);

    android::readLogLines(devices);
    while (1) {
        struct log_msg log_msg;
        int ret = android_logger_list_read(logger_list, &log_msg);

        if (ret == 0) {
            fprintf(stderr, "read: Unexpected EOF!\n");
            exit(EXIT_FAILURE);
        }

        if (ret < 0) {
            if (ret == -EAGAIN) {
                break;
            }

            if (ret == -EIO) {
                fprintf(stderr, "read: Unexpected EOF!\n");
                exit(EXIT_FAILURE);
            }
            if (ret == -EINVAL) {
                fprintf(stderr, "read: unexpected length.\n");
                exit(EXIT_FAILURE);
            }
            perror("logcat read");
            exit(EXIT_FAILURE);
        }

        for(dev = devices; dev; dev = dev->next) {
            if (android_name_to_log_id(dev->device) == log_msg.id()) {
                break;
            }
        }
        if (!dev) {
            fprintf(stderr, "read: Unexpected log ID!\n");
            exit(EXIT_FAILURE);
        }

        android::maybePrintStart(dev);
        if (android::g_printBinary) {
            android::printBinary(&log_msg);
        } else {
            android::processBuffer(dev, &log_msg);
        }
    }

    android_logger_list_free(logger_list);

    return 0;
}