Home | History | Annotate | Download | only in libnbaio
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "NBLog"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <climits>
     21 #include <stdarg.h>
     22 #include <stdint.h>
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <sys/prctl.h>
     26 #include <time.h>
     27 #include <new>
     28 #include <audio_utils/roundup.h>
     29 #include <media/nbaio/NBLog.h>
     30 #include <utils/Log.h>
     31 #include <utils/String8.h>
     32 
     33 #include <queue>
     34 
     35 namespace android {
     36 
     37 int NBLog::Entry::readAt(size_t offset) const
     38 {
     39     // FIXME This is too slow, despite the name it is used during writing
     40     if (offset == 0)
     41         return mEvent;
     42     else if (offset == 1)
     43         return mLength;
     44     else if (offset < (size_t) (mLength + 2))
     45         return ((char *) mData)[offset - 2];
     46     else if (offset == (size_t) (mLength + 2))
     47         return mLength;
     48     else
     49         return 0;
     50 }
     51 
     52 // ---------------------------------------------------------------------------
     53 
     54 NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
     55     ALOGW_IF(entry[offsetof(struct entry, type)] != EVENT_START_FMT,
     56         "Created format entry with invalid event type %d", entry[offsetof(struct entry, type)]);
     57 }
     58 
     59 NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
     60 
     61 const char *NBLog::FormatEntry::formatString() const {
     62     return (const char*) mEntry + offsetof(entry, data);
     63 }
     64 
     65 size_t NBLog::FormatEntry::formatStringLength() const {
     66     return mEntry[offsetof(entry, length)];
     67 }
     68 
     69 NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
     70     auto it = begin();
     71     // skip start fmt
     72     ++it;
     73     // skip timestamp
     74     ++it;
     75     // Skip author if present
     76     if (it->type == EVENT_AUTHOR) {
     77         ++it;
     78     }
     79     return it;
     80 }
     81 
     82 timespec NBLog::FormatEntry::timestamp() const {
     83     auto it = begin();
     84     // skip start fmt
     85     ++it;
     86     return it.payload<timespec>();
     87 }
     88 
     89 pid_t NBLog::FormatEntry::author() const {
     90     auto it = begin();
     91     // skip start fmt
     92     ++it;
     93     // skip timestamp
     94     ++it;
     95     // if there is an author entry, return it, return -1 otherwise
     96     if (it->type == EVENT_AUTHOR) {
     97         return it.payload<int>();
     98     }
     99     return -1;
    100 }
    101 
    102 NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
    103         std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
    104     auto it = begin();
    105     // copy fmt start entry
    106     it.copyTo(dst);
    107     // copy timestamp
    108     (++it).copyTo(dst);
    109     // insert author entry
    110     size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
    111     uint8_t authorEntry[authorEntrySize];
    112     authorEntry[offsetof(entry, type)] = EVENT_AUTHOR;
    113     authorEntry[offsetof(entry, length)] =
    114         authorEntry[authorEntrySize + NBLog::Entry::kPreviousLengthOffset] =
    115         sizeof(author);
    116     *(int*) (&authorEntry[offsetof(entry, data)]) = author;
    117     dst->write(authorEntry, authorEntrySize);
    118     // copy rest of entries
    119     while ((++it)->type != EVENT_END_FMT) {
    120         it.copyTo(dst);
    121     }
    122     it.copyTo(dst);
    123     ++it;
    124     return it;
    125 }
    126 
    127 void NBLog::FormatEntry::iterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
    128     size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
    129     dst->write(ptr, length);
    130 }
    131 
    132 void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
    133     memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
    134 }
    135 
    136 NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
    137     return iterator(mEntry);
    138 }
    139 
    140 NBLog::FormatEntry::iterator::iterator()
    141     : ptr(nullptr) {}
    142 
    143 NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
    144     : ptr(entry) {}
    145 
    146 NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
    147     : ptr(other.ptr) {}
    148 
    149 const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
    150     return *(entry*) ptr;
    151 }
    152 
    153 const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
    154     return (entry*) ptr;
    155 }
    156 
    157 NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
    158     ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
    159     return *this;
    160 }
    161 
    162 NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
    163     ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
    164     return *this;
    165 }
    166 
    167 NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
    168     iterator aux(*this);
    169     return ++aux;
    170 }
    171 
    172 NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
    173     iterator aux(*this);
    174     return --aux;
    175 }
    176 
    177 int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
    178     return ptr - other.ptr;
    179 }
    180 
    181 bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
    182     return ptr != other.ptr;
    183 }
    184 
    185 bool NBLog::FormatEntry::iterator::hasConsistentLength() const {
    186     return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] +
    187         NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset];
    188 }
    189 
    190 // ---------------------------------------------------------------------------
    191 
    192 #if 0   // FIXME see note in NBLog.h
    193 NBLog::Timeline::Timeline(size_t size, void *shared)
    194     : mSize(roundup(size)), mOwn(shared == NULL),
    195       mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared))
    196 {
    197     new (mShared) Shared;
    198 }
    199 
    200 NBLog::Timeline::~Timeline()
    201 {
    202     mShared->~Shared();
    203     if (mOwn) {
    204         delete[] (char *) mShared;
    205     }
    206 }
    207 #endif
    208 
    209 /*static*/
    210 size_t NBLog::Timeline::sharedSize(size_t size)
    211 {
    212     // TODO fifo now supports non-power-of-2 buffer sizes, so could remove the roundup
    213     return sizeof(Shared) + roundup(size);
    214 }
    215 
    216 // ---------------------------------------------------------------------------
    217 
    218 NBLog::Writer::Writer()
    219     : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false), mPidTag(NULL), mPidTagSize(0)
    220 {
    221 }
    222 
    223 NBLog::Writer::Writer(void *shared, size_t size)
    224     : mShared((Shared *) shared),
    225       mFifo(mShared != NULL ?
    226         new audio_utils_fifo(size, sizeof(uint8_t),
    227             mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
    228       mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL),
    229       mEnabled(mFifoWriter != NULL)
    230 {
    231     // caching pid and process name
    232     pid_t id = ::getpid();
    233     char procName[16];
    234     int status = prctl(PR_GET_NAME, procName);
    235     if (status) {  // error getting process name
    236         procName[0] = '\0';
    237     }
    238     size_t length = strlen(procName);
    239     mPidTagSize = length + sizeof(pid_t);
    240     mPidTag = new char[mPidTagSize];
    241     memcpy(mPidTag, &id, sizeof(pid_t));
    242     memcpy(mPidTag + sizeof(pid_t), procName, length);
    243 }
    244 
    245 NBLog::Writer::Writer(const sp<IMemory>& iMemory, size_t size)
    246     : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
    247 {
    248     mIMemory = iMemory;
    249 }
    250 
    251 NBLog::Writer::~Writer()
    252 {
    253     delete mFifoWriter;
    254     delete mFifo;
    255     delete[] mPidTag;
    256 }
    257 
    258 void NBLog::Writer::log(const char *string)
    259 {
    260     if (!mEnabled) {
    261         return;
    262     }
    263     LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string");
    264     size_t length = strlen(string);
    265     if (length > Entry::kMaxLength) {
    266         length = Entry::kMaxLength;
    267     }
    268     log(EVENT_STRING, string, length);
    269 }
    270 
    271 void NBLog::Writer::logf(const char *fmt, ...)
    272 {
    273     if (!mEnabled) {
    274         return;
    275     }
    276     va_list ap;
    277     va_start(ap, fmt);
    278     Writer::logvf(fmt, ap);     // the Writer:: is needed to avoid virtual dispatch for LockedWriter
    279     va_end(ap);
    280 }
    281 
    282 void NBLog::Writer::logvf(const char *fmt, va_list ap)
    283 {
    284     if (!mEnabled) {
    285         return;
    286     }
    287     char buffer[Entry::kMaxLength + 1 /*NUL*/];
    288     int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
    289     if (length >= (int) sizeof(buffer)) {
    290         length = sizeof(buffer) - 1;
    291         // NUL termination is not required
    292         // buffer[length] = '\0';
    293     }
    294     if (length >= 0) {
    295         log(EVENT_STRING, buffer, length);
    296     }
    297 }
    298 
    299 void NBLog::Writer::logTimestamp()
    300 {
    301     if (!mEnabled) {
    302         return;
    303     }
    304     struct timespec ts;
    305     if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
    306         log(EVENT_TIMESTAMP, &ts, sizeof(ts));
    307     }
    308 }
    309 
    310 void NBLog::Writer::logTimestamp(const struct timespec &ts)
    311 {
    312     if (!mEnabled) {
    313         return;
    314     }
    315     log(EVENT_TIMESTAMP, &ts, sizeof(ts));
    316 }
    317 
    318 void NBLog::Writer::logInteger(const int x)
    319 {
    320     if (!mEnabled) {
    321         return;
    322     }
    323     log(EVENT_INTEGER, &x, sizeof(x));
    324 }
    325 
    326 void NBLog::Writer::logFloat(const float x)
    327 {
    328     if (!mEnabled) {
    329         return;
    330     }
    331     log(EVENT_FLOAT, &x, sizeof(x));
    332 }
    333 
    334 void NBLog::Writer::logPID()
    335 {
    336     if (!mEnabled) {
    337         return;
    338     }
    339     log(EVENT_PID, mPidTag, mPidTagSize);
    340 }
    341 
    342 void NBLog::Writer::logStart(const char *fmt)
    343 {
    344     if (!mEnabled) {
    345         return;
    346     }
    347     size_t length = strlen(fmt);
    348     if (length > Entry::kMaxLength) {
    349         length = Entry::kMaxLength;
    350     }
    351     log(EVENT_START_FMT, fmt, length);
    352 }
    353 
    354 void NBLog::Writer::logEnd()
    355 {
    356     if (!mEnabled) {
    357         return;
    358     }
    359     Entry entry = Entry(EVENT_END_FMT, NULL, 0);
    360     log(&entry, true);
    361 }
    362 
    363 void NBLog::Writer::logFormat(const char *fmt, ...)
    364 {
    365     if (!mEnabled) {
    366         return;
    367     }
    368 
    369     va_list ap;
    370     va_start(ap, fmt);
    371     Writer::logVFormat(fmt, ap);
    372     va_end(ap);
    373 }
    374 
    375 void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
    376 {
    377     if (!mEnabled) {
    378         return;
    379     }
    380     Writer::logStart(fmt);
    381     int i;
    382     double f;
    383     char* s;
    384     struct timespec t;
    385     Writer::logTimestamp();
    386     for (const char *p = fmt; *p != '\0'; p++) {
    387         // TODO: implement more complex formatting such as %.3f
    388         if (*p != '%') {
    389             continue;
    390         }
    391         switch(*++p) {
    392         case 's': // string
    393             s = va_arg(argp, char *);
    394             Writer::log(s);
    395             break;
    396 
    397         case 't': // timestamp
    398             t = va_arg(argp, struct timespec);
    399             Writer::logTimestamp(t);
    400             break;
    401 
    402         case 'd': // integer
    403             i = va_arg(argp, int);
    404             Writer::logInteger(i);
    405             break;
    406 
    407         case 'f': // float
    408             f = va_arg(argp, double); // float arguments are promoted to double in vararg lists
    409             Writer::logFloat((float)f);
    410             break;
    411 
    412         case 'p': // pid
    413             Writer::logPID();
    414             break;
    415 
    416         // the "%\0" case finishes parsing
    417         case '\0':
    418             --p;
    419             break;
    420 
    421         case '%':
    422             break;
    423 
    424         default:
    425             ALOGW("NBLog Writer parsed invalid format specifier: %c", *p);
    426             break;
    427         }
    428     }
    429     Writer::logEnd();
    430 }
    431 
    432 void NBLog::Writer::log(Event event, const void *data, size_t length)
    433 {
    434     if (!mEnabled) {
    435         return;
    436     }
    437     if (data == NULL || length > Entry::kMaxLength) {
    438         // TODO Perhaps it makes sense to display truncated data or at least a
    439         //      message that the data is too long?  The current behavior can create
    440         //      a confusion for a programmer debugging their code.
    441         return;
    442     }
    443     switch (event) {
    444     case EVENT_STRING:
    445     case EVENT_TIMESTAMP:
    446     case EVENT_INTEGER:
    447     case EVENT_FLOAT:
    448     case EVENT_PID:
    449     case EVENT_START_FMT:
    450         break;
    451     case EVENT_RESERVED:
    452     default:
    453         return;
    454     }
    455     Entry entry(event, data, length);
    456     log(&entry, true /*trusted*/);
    457 }
    458 
    459 void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
    460 {
    461     if (!mEnabled) {
    462         return;
    463     }
    464     if (!trusted) {
    465         log(entry->mEvent, entry->mData, entry->mLength);
    466         return;
    467     }
    468     size_t need = entry->mLength + Entry::kOverhead;    // mEvent, mLength, data[length], mLength
    469                                                         // need = number of bytes remaining to write
    470 
    471     // FIXME optimize this using memcpy for the data part of the Entry.
    472     // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
    473     uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
    474     for (size_t i = 0; i < need; i++) {
    475         temp[i] = entry->readAt(i);
    476     }
    477     mFifoWriter->write(temp, need);
    478 }
    479 
    480 bool NBLog::Writer::isEnabled() const
    481 {
    482     return mEnabled;
    483 }
    484 
    485 bool NBLog::Writer::setEnabled(bool enabled)
    486 {
    487     bool old = mEnabled;
    488     mEnabled = enabled && mShared != NULL;
    489     return old;
    490 }
    491 
    492 // ---------------------------------------------------------------------------
    493 
    494 NBLog::LockedWriter::LockedWriter()
    495     : Writer()
    496 {
    497 }
    498 
    499 NBLog::LockedWriter::LockedWriter(void *shared, size_t size)
    500     : Writer(shared, size)
    501 {
    502 }
    503 
    504 void NBLog::LockedWriter::log(const char *string)
    505 {
    506     Mutex::Autolock _l(mLock);
    507     Writer::log(string);
    508 }
    509 
    510 void NBLog::LockedWriter::logf(const char *fmt, ...)
    511 {
    512     // FIXME should not take the lock until after formatting is done
    513     Mutex::Autolock _l(mLock);
    514     va_list ap;
    515     va_start(ap, fmt);
    516     Writer::logvf(fmt, ap);
    517     va_end(ap);
    518 }
    519 
    520 void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
    521 {
    522     // FIXME should not take the lock until after formatting is done
    523     Mutex::Autolock _l(mLock);
    524     Writer::logvf(fmt, ap);
    525 }
    526 
    527 void NBLog::LockedWriter::logTimestamp()
    528 {
    529     // FIXME should not take the lock until after the clock_gettime() syscall
    530     Mutex::Autolock _l(mLock);
    531     Writer::logTimestamp();
    532 }
    533 
    534 void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
    535 {
    536     Mutex::Autolock _l(mLock);
    537     Writer::logTimestamp(ts);
    538 }
    539 
    540 void NBLog::LockedWriter::logInteger(const int x)
    541 {
    542     Mutex::Autolock _l(mLock);
    543     Writer::logInteger(x);
    544 }
    545 
    546 void NBLog::LockedWriter::logFloat(const float x)
    547 {
    548     Mutex::Autolock _l(mLock);
    549     Writer::logFloat(x);
    550 }
    551 
    552 void NBLog::LockedWriter::logPID()
    553 {
    554     Mutex::Autolock _l(mLock);
    555     Writer::logPID();
    556 }
    557 
    558 void NBLog::LockedWriter::logStart(const char *fmt)
    559 {
    560     Mutex::Autolock _l(mLock);
    561     Writer::logStart(fmt);
    562 }
    563 
    564 
    565 void NBLog::LockedWriter::logEnd()
    566 {
    567     Mutex::Autolock _l(mLock);
    568     Writer::logEnd();
    569 }
    570 
    571 bool NBLog::LockedWriter::isEnabled() const
    572 {
    573     Mutex::Autolock _l(mLock);
    574     return Writer::isEnabled();
    575 }
    576 
    577 bool NBLog::LockedWriter::setEnabled(bool enabled)
    578 {
    579     Mutex::Autolock _l(mLock);
    580     return Writer::setEnabled(enabled);
    581 }
    582 
    583 // ---------------------------------------------------------------------------
    584 
    585 NBLog::Reader::Reader(const void *shared, size_t size)
    586     : mShared((/*const*/ Shared *) shared), /*mIMemory*/
    587       mFd(-1), mIndent(0),
    588       mFifo(mShared != NULL ?
    589         new audio_utils_fifo(size, sizeof(uint8_t),
    590             mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
    591       mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL)
    592 {
    593 }
    594 
    595 NBLog::Reader::Reader(const sp<IMemory>& iMemory, size_t size)
    596     : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
    597 {
    598     mIMemory = iMemory;
    599 }
    600 
    601 NBLog::Reader::~Reader()
    602 {
    603     delete mFifoReader;
    604     delete mFifo;
    605 }
    606 
    607 uint8_t *NBLog::Reader::findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type) {
    608     while (back + Entry::kPreviousLengthOffset >= front) {
    609         uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
    610         if (prev < front || prev + prev[offsetof(FormatEntry::entry, length)] +
    611                             Entry::kOverhead != back) {
    612 
    613             // prev points to an out of limits or inconsistent entry
    614             return nullptr;
    615         }
    616         if (prev[offsetof(FormatEntry::entry, type)] == type) {
    617             return prev;
    618         }
    619         back = prev;
    620     }
    621     return nullptr; // no entry found
    622 }
    623 
    624 std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
    625 {
    626     if (mFifoReader == NULL) {
    627         return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot());
    628     }
    629     // make a copy to avoid race condition with writer
    630     size_t capacity = mFifo->capacity();
    631 
    632     // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the
    633     // reader index. The index is incremented after handling corruption, to after the last complete
    634     // entry of the buffer
    635     size_t lost;
    636     audio_utils_iovec iovec[2];
    637     ssize_t availToRead = mFifoReader->obtain(iovec, capacity, NULL /*timeout*/, &lost);
    638     if (availToRead <= 0) {
    639         return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot());
    640     }
    641 
    642     std::unique_ptr<Snapshot> snapshot(new Snapshot(availToRead));
    643     memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength);
    644     if (iovec[1].mLength > 0) {
    645         memcpy(snapshot->mData + (iovec[0].mLength),
    646             (const char *) mFifo->buffer() + iovec[1].mOffset, iovec[1].mLength);
    647     }
    648 
    649     // Handle corrupted buffer
    650     // Potentially, a buffer has corrupted data on both beginning (due to overflow) and end
    651     // (due to incomplete format entry). But even if the end format entry is incomplete,
    652     // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
    653     // TODO: handle client corruption (in the middle of a buffer)
    654 
    655     uint8_t *back = snapshot->mData + availToRead;
    656     uint8_t *front = snapshot->mData;
    657 
    658     // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
    659     // We go backwards until we find an EVENT_END_FMT.
    660     uint8_t *lastEnd = findLastEntryOfType(front, back, EVENT_END_FMT);
    661     if (lastEnd == nullptr) {
    662         snapshot->mEnd = snapshot->mBegin = FormatEntry::iterator(front);
    663     } else {
    664         // end of snapshot points to after last END_FMT entry
    665         snapshot->mEnd = FormatEntry::iterator(lastEnd + Entry::kOverhead);
    666         // find first START_FMT
    667         uint8_t *firstStart = nullptr;
    668         uint8_t *firstStartTmp = lastEnd;
    669         while ((firstStartTmp = findLastEntryOfType(front, firstStartTmp, EVENT_START_FMT))
    670                 != nullptr) {
    671             firstStart = firstStartTmp;
    672         }
    673         // firstStart is null if no START_FMT entry was found before lastEnd
    674         if (firstStart == nullptr) {
    675             snapshot->mBegin = snapshot->mEnd;
    676         } else {
    677             snapshot->mBegin = FormatEntry::iterator(firstStart);
    678         }
    679     }
    680 
    681     // advance fifo reader index to after last entry read.
    682     mFifoReader->release(snapshot->mEnd - front);
    683 
    684     snapshot->mLost = lost;
    685     return snapshot;
    686 
    687 }
    688 
    689 void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
    690 {
    691 #if 0
    692     struct timespec ts;
    693     time_t maxSec = -1;
    694     while (entry - start >= (int) Entry::kOverhead) {
    695         if (prevEntry - start < 0 || !prevEntry.hasConsistentLength()) {
    696             break;
    697         }
    698         if (prevEntry->type == EVENT_TIMESTAMP) {
    699             if (prevEntry->length != sizeof(struct timespec)) {
    700                 // corrupt
    701                 break;
    702             }
    703             prevEntry.copyData((uint8_t*) &ts);
    704             if (ts.tv_sec > maxSec) {
    705                 maxSec = ts.tv_sec;
    706             }
    707         }
    708         --entry;
    709         --prevEntry;
    710     }
    711 #endif
    712     mFd = fd;
    713     mIndent = indent;
    714     String8 timestamp, body;
    715     size_t lost = snapshot.lost() + (snapshot.begin() - FormatEntry::iterator(snapshot.data()));
    716     if (lost > 0) {
    717         body.appendFormat("warning: lost %zu bytes worth of events", lost);
    718         // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
    719         //      log to push it out.  Consider keeping the timestamp/body between calls to readAt().
    720         dumpLine(timestamp, body);
    721     }
    722 #if 0
    723     size_t width = 1;
    724     while (maxSec >= 10) {
    725         ++width;
    726         maxSec /= 10;
    727     }
    728     if (maxSec >= 0) {
    729         timestamp.appendFormat("[%*s]", (int) width + 4, "");
    730     }
    731     bool deferredTimestamp = false;
    732 #endif
    733     for (auto entry = snapshot.begin(); entry != snapshot.end();) {
    734         switch (entry->type) {
    735 #if 0
    736         case EVENT_STRING:
    737             body.appendFormat("%.*s", (int) entry.length(), entry.data());
    738             break;
    739         case EVENT_TIMESTAMP: {
    740             // already checked that length == sizeof(struct timespec);
    741             entry.copyData((const uint8_t*) &ts);
    742             long prevNsec = ts.tv_nsec;
    743             long deltaMin = LONG_MAX;
    744             long deltaMax = -1;
    745             long deltaTotal = 0;
    746             auto aux(entry);
    747             for (;;) {
    748                 ++aux;
    749                 if (end - aux >= 0 || aux.type() != EVENT_TIMESTAMP) {
    750                     break;
    751                 }
    752                 struct timespec tsNext;
    753                 aux.copyData((const uint8_t*) &tsNext);
    754                 if (tsNext.tv_sec != ts.tv_sec) {
    755                     break;
    756                 }
    757                 long delta = tsNext.tv_nsec - prevNsec;
    758                 if (delta < 0) {
    759                     break;
    760                 }
    761                 if (delta < deltaMin) {
    762                     deltaMin = delta;
    763                 }
    764                 if (delta > deltaMax) {
    765                     deltaMax = delta;
    766                 }
    767                 deltaTotal += delta;
    768                 prevNsec = tsNext.tv_nsec;
    769             }
    770             size_t n = (aux - entry) / (sizeof(struct timespec) + 3 /*Entry::kOverhead?*/);
    771             if (deferredTimestamp) {
    772                 dumpLine(timestamp, body);
    773                 deferredTimestamp = false;
    774             }
    775             timestamp.clear();
    776             if (n >= kSquashTimestamp) {
    777                 timestamp.appendFormat("[%d.%03d to .%.03d by .%.03d to .%.03d]",
    778                         (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
    779                         (int) ((ts.tv_nsec + deltaTotal) / 1000000),
    780                         (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
    781                 entry = aux;
    782                 // advance = 0;
    783                 break;
    784             }
    785             timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
    786                     (int) (ts.tv_nsec / 1000000));
    787             deferredTimestamp = true;
    788             }
    789             break;
    790         case EVENT_INTEGER:
    791             appendInt(&body, entry.data());
    792             break;
    793         case EVENT_FLOAT:
    794             appendFloat(&body, entry.data());
    795             break;
    796         case EVENT_PID:
    797             appendPID(&body, entry.data(), entry.length());
    798             break;
    799 #endif
    800         case EVENT_START_FMT:
    801             // right now, this is the only supported case
    802             entry = handleFormat(FormatEntry(entry), &timestamp, &body);
    803             break;
    804         case EVENT_END_FMT:
    805             body.appendFormat("warning: got to end format event");
    806             ++entry;
    807             break;
    808         case EVENT_RESERVED:
    809         default:
    810             body.appendFormat("warning: unexpected event %d", entry->type);
    811             ++entry;
    812             break;
    813         }
    814 
    815         if (!body.isEmpty()) {
    816             dumpLine(timestamp, body);
    817             // deferredTimestamp = false;
    818         }
    819     }
    820     // if (deferredTimestamp) {
    821     //     dumpLine(timestamp, body);
    822     // }
    823 }
    824 
    825 void NBLog::Reader::dump(int fd, size_t indent)
    826 {
    827     // get a snapshot, dump it
    828     std::unique_ptr<Snapshot> snap = getSnapshot();
    829     dump(fd, indent, *snap);
    830 }
    831 
    832 void NBLog::Reader::dumpLine(const String8 &timestamp, String8 &body)
    833 {
    834     if (mFd >= 0) {
    835         dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
    836     } else {
    837         ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
    838     }
    839     body.clear();
    840 }
    841 
    842 bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
    843 {
    844     return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
    845 }
    846 
    847 void NBLog::appendTimestamp(String8 *body, const void *data) {
    848     struct timespec ts;
    849     memcpy(&ts, data, sizeof(struct timespec));
    850     body->appendFormat("[%d.%03d]", (int) ts.tv_sec,
    851                     (int) (ts.tv_nsec / 1000000));
    852 }
    853 
    854 void NBLog::appendInt(String8 *body, const void *data) {
    855     int x = *((int*) data);
    856     body->appendFormat("<%d>", x);
    857 }
    858 
    859 void NBLog::appendFloat(String8 *body, const void *data) {
    860     float f;
    861     memcpy(&f, data, sizeof(float));
    862     body->appendFormat("<%f>", f);
    863 }
    864 
    865 void NBLog::appendPID(String8 *body, const void* data, size_t length) {
    866     pid_t id = *((pid_t*) data);
    867     char * name = &((char*) data)[sizeof(pid_t)];
    868     body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
    869 }
    870 
    871 NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
    872                                                          String8 *timestamp,
    873                                                          String8 *body) {
    874     // log timestamp
    875     struct timespec ts = fmtEntry.timestamp();
    876     timestamp->clear();
    877     timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
    878                     (int) (ts.tv_nsec / 1000000));
    879 
    880     // log author (if present)
    881     handleAuthor(fmtEntry, body);
    882 
    883     // log string
    884     NBLog::FormatEntry::iterator arg = fmtEntry.args();
    885 
    886     const char* fmt = fmtEntry.formatString();
    887     size_t fmt_length = fmtEntry.formatStringLength();
    888 
    889     for (size_t fmt_offset = 0; fmt_offset < fmt_length; ++fmt_offset) {
    890         if (fmt[fmt_offset] != '%') {
    891             body->append(&fmt[fmt_offset], 1); // TODO optimize to write consecutive strings at once
    892             continue;
    893         }
    894         // case "%%""
    895         if (fmt[++fmt_offset] == '%') {
    896             body->append("%");
    897             continue;
    898         }
    899         // case "%\0"
    900         if (fmt_offset == fmt_length) {
    901             continue;
    902         }
    903 
    904         NBLog::Event event = (NBLog::Event) arg->type;
    905         size_t length = arg->length;
    906 
    907         // TODO check length for event type is correct
    908 
    909         if (event == EVENT_END_FMT) {
    910             break;
    911         }
    912 
    913         // TODO: implement more complex formatting such as %.3f
    914         const uint8_t *datum = arg->data; // pointer to the current event args
    915         switch(fmt[fmt_offset])
    916         {
    917         case 's': // string
    918             ALOGW_IF(event != EVENT_STRING,
    919                 "NBLog Reader incompatible event for string specifier: %d", event);
    920             body->append((const char*) datum, length);
    921             break;
    922 
    923         case 't': // timestamp
    924             ALOGW_IF(event != EVENT_TIMESTAMP,
    925                 "NBLog Reader incompatible event for timestamp specifier: %d", event);
    926             appendTimestamp(body, datum);
    927             break;
    928 
    929         case 'd': // integer
    930             ALOGW_IF(event != EVENT_INTEGER,
    931                 "NBLog Reader incompatible event for integer specifier: %d", event);
    932             appendInt(body, datum);
    933             break;
    934 
    935         case 'f': // float
    936             ALOGW_IF(event != EVENT_FLOAT,
    937                 "NBLog Reader incompatible event for float specifier: %d", event);
    938             appendFloat(body, datum);
    939             break;
    940 
    941         case 'p': // pid
    942             ALOGW_IF(event != EVENT_PID,
    943                 "NBLog Reader incompatible event for pid specifier: %d", event);
    944             appendPID(body, datum, length);
    945             break;
    946 
    947         default:
    948             ALOGW("NBLog Reader encountered unknown character %c", fmt[fmt_offset]);
    949         }
    950         ++arg;
    951     }
    952     ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type);
    953     ++arg;
    954     return arg;
    955 }
    956 
    957 // ---------------------------------------------------------------------------
    958 
    959 NBLog::Merger::Merger(const void *shared, size_t size):
    960       mBuffer(NULL),
    961       mShared((Shared *) shared),
    962       mFifo(mShared != NULL ?
    963         new audio_utils_fifo(size, sizeof(uint8_t),
    964             mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
    965       mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL)
    966       {}
    967 
    968 void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
    969     mNamedReaders.push_back(reader);
    970 }
    971 
    972 // items placed in priority queue during merge
    973 // composed by a timestamp and the index of the snapshot where the timestamp came from
    974 struct MergeItem
    975 {
    976     struct timespec ts;
    977     int index;
    978     MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
    979 };
    980 
    981 // operators needed for priority queue in merge
    982 bool operator>(const struct timespec &t1, const struct timespec &t2) {
    983     return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
    984 }
    985 
    986 bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) {
    987     return i1.ts > i2.ts ||
    988         (i1.ts.tv_sec == i2.ts.tv_sec && i1.ts.tv_nsec == i2.ts.tv_nsec && i1.index > i2.index);
    989 }
    990 
    991 // Merge registered readers, sorted by timestamp
    992 void NBLog::Merger::merge() {
    993     int nLogs = mNamedReaders.size();
    994     std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
    995     std::vector<NBLog::FormatEntry::iterator> offsets(nLogs);
    996     for (int i = 0; i < nLogs; ++i) {
    997         snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
    998         offsets[i] = snapshots[i]->begin();
    999     }
   1000     // initialize offsets
   1001     // TODO custom heap implementation could allow to update top, improving performance
   1002     // for bursty buffers
   1003     std::priority_queue<MergeItem, std::vector<MergeItem>, std::greater<MergeItem>> timestamps;
   1004     for (int i = 0; i < nLogs; ++i)
   1005     {
   1006         if (offsets[i] != snapshots[i]->end()) {
   1007             timespec ts = FormatEntry(offsets[i]).timestamp();
   1008             timestamps.emplace(ts, i);
   1009         }
   1010     }
   1011 
   1012     while (!timestamps.empty()) {
   1013         // find minimum timestamp
   1014         int index = timestamps.top().index;
   1015         // copy it to the log, increasing offset
   1016         offsets[index] = FormatEntry(offsets[index]).copyWithAuthor(mFifoWriter, index);
   1017         // update data structures
   1018         timestamps.pop();
   1019         if (offsets[index] != snapshots[index]->end()) {
   1020             timespec ts = FormatEntry(offsets[index]).timestamp();
   1021             timestamps.emplace(ts, index);
   1022         }
   1023     }
   1024 }
   1025 
   1026 const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
   1027     return &mNamedReaders;
   1028 }
   1029 
   1030 NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
   1031     : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
   1032 
   1033 size_t NBLog::MergeReader::handleAuthor(const NBLog::FormatEntry &fmtEntry, String8 *body) {
   1034     int author = fmtEntry.author();
   1035     const char* name = (*mNamedReaders)[author].name();
   1036     body->appendFormat("%s: ", name);
   1037     return NBLog::Entry::kOverhead + sizeof(author);
   1038 }
   1039 
   1040 NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
   1041     : mMerger(merger),
   1042       mTimeoutUs(0) {}
   1043 
   1044 NBLog::MergeThread::~MergeThread() {
   1045     // set exit flag, set timeout to 0 to force threadLoop to exit and wait for the thread to join
   1046     requestExit();
   1047     setTimeoutUs(0);
   1048     join();
   1049 }
   1050 
   1051 bool NBLog::MergeThread::threadLoop() {
   1052     bool doMerge;
   1053     {
   1054         AutoMutex _l(mMutex);
   1055         // If mTimeoutUs is negative, wait on the condition variable until it's positive.
   1056         // If it's positive, wait kThreadSleepPeriodUs and then merge
   1057         nsecs_t waitTime = mTimeoutUs > 0 ? kThreadSleepPeriodUs * 1000 : LLONG_MAX;
   1058         mCond.waitRelative(mMutex, waitTime);
   1059         doMerge = mTimeoutUs > 0;
   1060         mTimeoutUs -= kThreadSleepPeriodUs;
   1061     }
   1062     if (doMerge) {
   1063         mMerger.merge();
   1064     }
   1065     return true;
   1066 }
   1067 
   1068 void NBLog::MergeThread::wakeup() {
   1069     setTimeoutUs(kThreadWakeupPeriodUs);
   1070 }
   1071 
   1072 void NBLog::MergeThread::setTimeoutUs(int time) {
   1073     AutoMutex _l(mMutex);
   1074     mTimeoutUs = time;
   1075     mCond.signal();
   1076 }
   1077 
   1078 }   // namespace android
   1079