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), ×tamp, &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 ×tamp, 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