[Android Software/Hacking General [Developers Only]] llrraa2010:
Logcat tool

system/core/logcat

1. data structure

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;

}

};

struct log_device_t {

char* device;

bool binary;

int fd;

bool printed;

char label;

queued_entry_t* queue;

log_device_t* next;

log_device_t(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;

}

}

};

2. initialization

static void setupOutput()

{

if (g_outputFileName == NULL) {

g_outFD = STDOUT_FILENO;

} else {

struct stat statbuf;

g_outFD = openLogFile (g_outputFileName);

if (g_outFD < 0) {

perror ("couldn't open output file");

exit(-1);

}

fstat(g_outFD, &statbuf);

g_outByteCount = statbuf.st_size;

}

}

dev = devices;

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));

exit(EXIT_FAILURE);

}

if (clearLog) {

int ret;

ret = android::clearLog(dev->fd);

if (ret) {

perror("ioctl");

exit(EXIT_FAILURE);

}

}

if (getLogSize) {

int size, readable;

size = android::getLogSize(dev->fd);

if (size < 0) {

perror("ioctl");

exit(EXIT_FAILURE);

}

readable = android::getLogReadableSize(dev->fd);

if (readable < 0) {

perror("ioctl");

exit(EXIT_FAILURE);

}

printf("%s: ring buffer is %dKb (%dKb consumed), "

"max entry is %db, max payload is %db\n", dev->device,

size / 1024, readable / 1024,

(int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);

}

dev = dev->next;

}

3. read the log

static void readLogLines(log_device_t* devices)

{

log_device_t* dev;

int max = 0;

int ret;

int queued_lines = 0;

bool sleep = true;

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);

}

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) {

exit(0);

}

} 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:

;

}

}

4. output the log

static void printNextEntry(log_device_t* dev) {

maybePrintStart(dev);

if (g_printBinary) {

printBinary(&dev->queue->entry);

} else {

processBuffer(dev, &dev->queue->entry);

}

skipNextEntry(dev);

}

void printBinary(struct logger_entry *buf)

{

size_t size = sizeof(logger_entry) + buf->len;

int ret;

do {

ret = write(g_outFD, buf, size);

} while (ret < 0 && errno == EINTR);

}

...