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 
readAt(size_t offset) const37 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 
FormatEntry(const uint8_t * entry)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 
FormatEntry(const NBLog::FormatEntry::iterator & it)59 NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
60 
formatString() const61 const char *NBLog::FormatEntry::formatString() const {
62     return (const char*) mEntry + offsetof(entry, data);
63 }
64 
formatStringLength() const65 size_t NBLog::FormatEntry::formatStringLength() const {
66     return mEntry[offsetof(entry, length)];
67 }
68 
args() const69 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 
timestamp() const82 timespec NBLog::FormatEntry::timestamp() const {
83     auto it = begin();
84     // skip start fmt
85     ++it;
86     return it.payload<timespec>();
87 }
88 
author() const89 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 
copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> & dst,int author) const102 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 
copyTo(std::unique_ptr<audio_utils_fifo_writer> & dst) const127 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 
copyData(uint8_t * dst) const132 void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
133     memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
134 }
135 
begin() const136 NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
137     return iterator(mEntry);
138 }
139 
iterator()140 NBLog::FormatEntry::iterator::iterator()
141     : ptr(nullptr) {}
142 
iterator(const uint8_t * entry)143 NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
144     : ptr(entry) {}
145 
iterator(const NBLog::FormatEntry::iterator & other)146 NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
147     : ptr(other.ptr) {}
148 
operator *() const149 const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
150     return *(entry*) ptr;
151 }
152 
operator ->() const153 const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
154     return (entry*) ptr;
155 }
156 
operator ++()157 NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
158     ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
159     return *this;
160 }
161 
operator --()162 NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
163     ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
164     return *this;
165 }
166 
next() const167 NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
168     iterator aux(*this);
169     return ++aux;
170 }
171 
prev() const172 NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
173     iterator aux(*this);
174     return --aux;
175 }
176 
operator -(const NBLog::FormatEntry::iterator & other) const177 int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
178     return ptr - other.ptr;
179 }
180 
operator !=(const iterator & other) const181 bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
182     return ptr != other.ptr;
183 }
184 
hasConsistentLength() const185 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*/
sharedSize(size_t size)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 
Writer()218 NBLog::Writer::Writer()
219     : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false), mPidTag(NULL), mPidTagSize(0)
220 {
221 }
222 
Writer(void * shared,size_t size)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 
Writer(const sp<IMemory> & iMemory,size_t size)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 
~Writer()251 NBLog::Writer::~Writer()
252 {
253     delete mFifoWriter;
254     delete mFifo;
255     delete[] mPidTag;
256 }
257 
log(const char * string)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 
logf(const char * fmt,...)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 
logvf(const char * fmt,va_list ap)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 
logTimestamp()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 
logTimestamp(const struct timespec & ts)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 
logInteger(const int x)318 void NBLog::Writer::logInteger(const int x)
319 {
320     if (!mEnabled) {
321         return;
322     }
323     log(EVENT_INTEGER, &x, sizeof(x));
324 }
325 
logFloat(const float x)326 void NBLog::Writer::logFloat(const float x)
327 {
328     if (!mEnabled) {
329         return;
330     }
331     log(EVENT_FLOAT, &x, sizeof(x));
332 }
333 
logPID()334 void NBLog::Writer::logPID()
335 {
336     if (!mEnabled) {
337         return;
338     }
339     log(EVENT_PID, mPidTag, mPidTagSize);
340 }
341 
logStart(const char * fmt)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 
logEnd()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 
logFormat(const char * fmt,...)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 
logVFormat(const char * fmt,va_list argp)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 
log(Event event,const void * data,size_t length)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 
log(const NBLog::Entry * entry,bool trusted)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 
isEnabled() const480 bool NBLog::Writer::isEnabled() const
481 {
482     return mEnabled;
483 }
484 
setEnabled(bool enabled)485 bool NBLog::Writer::setEnabled(bool enabled)
486 {
487     bool old = mEnabled;
488     mEnabled = enabled && mShared != NULL;
489     return old;
490 }
491 
492 // ---------------------------------------------------------------------------
493 
LockedWriter()494 NBLog::LockedWriter::LockedWriter()
495     : Writer()
496 {
497 }
498 
LockedWriter(void * shared,size_t size)499 NBLog::LockedWriter::LockedWriter(void *shared, size_t size)
500     : Writer(shared, size)
501 {
502 }
503 
log(const char * string)504 void NBLog::LockedWriter::log(const char *string)
505 {
506     Mutex::Autolock _l(mLock);
507     Writer::log(string);
508 }
509 
logf(const char * fmt,...)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 
logvf(const char * fmt,va_list ap)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 
logTimestamp()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 
logTimestamp(const struct timespec & ts)534 void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
535 {
536     Mutex::Autolock _l(mLock);
537     Writer::logTimestamp(ts);
538 }
539 
logInteger(const int x)540 void NBLog::LockedWriter::logInteger(const int x)
541 {
542     Mutex::Autolock _l(mLock);
543     Writer::logInteger(x);
544 }
545 
logFloat(const float x)546 void NBLog::LockedWriter::logFloat(const float x)
547 {
548     Mutex::Autolock _l(mLock);
549     Writer::logFloat(x);
550 }
551 
logPID()552 void NBLog::LockedWriter::logPID()
553 {
554     Mutex::Autolock _l(mLock);
555     Writer::logPID();
556 }
557 
logStart(const char * fmt)558 void NBLog::LockedWriter::logStart(const char *fmt)
559 {
560     Mutex::Autolock _l(mLock);
561     Writer::logStart(fmt);
562 }
563 
564 
logEnd()565 void NBLog::LockedWriter::logEnd()
566 {
567     Mutex::Autolock _l(mLock);
568     Writer::logEnd();
569 }
570 
isEnabled() const571 bool NBLog::LockedWriter::isEnabled() const
572 {
573     Mutex::Autolock _l(mLock);
574     return Writer::isEnabled();
575 }
576 
setEnabled(bool enabled)577 bool NBLog::LockedWriter::setEnabled(bool enabled)
578 {
579     Mutex::Autolock _l(mLock);
580     return Writer::setEnabled(enabled);
581 }
582 
583 // ---------------------------------------------------------------------------
584 
Reader(const void * shared,size_t size)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 
Reader(const sp<IMemory> & iMemory,size_t size)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 
~Reader()601 NBLog::Reader::~Reader()
602 {
603     delete mFifoReader;
604     delete mFifo;
605 }
606 
findLastEntryOfType(uint8_t * front,uint8_t * back,uint8_t type)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 
getSnapshot()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 
dump(int fd,size_t indent,NBLog::Reader::Snapshot & snapshot)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 
dump(int fd,size_t indent)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 
dumpLine(const String8 & timestamp,String8 & body)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 
isIMemory(const sp<IMemory> & iMemory) const842 bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
843 {
844     return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
845 }
846 
appendTimestamp(String8 * body,const void * data)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 
appendInt(String8 * body,const void * data)854 void NBLog::appendInt(String8 *body, const void *data) {
855     int x = *((int*) data);
856     body->appendFormat("<%d>", x);
857 }
858 
appendFloat(String8 * body,const void * data)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 
appendPID(String8 * body,const void * data,size_t length)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 
handleFormat(const FormatEntry & fmtEntry,String8 * timestamp,String8 * body)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 
Merger(const void * shared,size_t size)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 
addReader(const NBLog::NamedReader & reader)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;
MergeItemandroid::MergeItem978     MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
979 };
980 
981 // operators needed for priority queue in merge
operator >(const struct timespec & t1,const struct timespec & t2)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 
operator >(const struct MergeItem & i1,const struct MergeItem & i2)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
merge()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 
getNamedReaders() const1026 const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
1027     return &mNamedReaders;
1028 }
1029 
MergeReader(const void * shared,size_t size,Merger & merger)1030 NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
1031     : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
1032 
handleAuthor(const NBLog::FormatEntry & fmtEntry,String8 * body)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 
MergeThread(NBLog::Merger & merger)1040 NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
1041     : mMerger(merger),
1042       mTimeoutUs(0) {}
1043 
~MergeThread()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 
threadLoop()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 
wakeup()1068 void NBLog::MergeThread::wakeup() {
1069     setTimeoutUs(kThreadWakeupPeriodUs);
1070 }
1071 
setTimeoutUs(int time)1072 void NBLog::MergeThread::setTimeoutUs(int time) {
1073     AutoMutex _l(mMutex);
1074     mTimeoutUs = time;
1075     mCond.signal();
1076 }
1077 
1078 }   // namespace android
1079