blob: de38e7ff95d270f2426e8c1c80d001062f7eaca4 [file] [log] [blame]
Glenn Kasten11d8dfc2013-01-14 14:53:13 -08001/*
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
Nicolas Rouletdcdfaec2017-02-14 10:18:39 -080020#include <climits>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080021#include <stdarg.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
Nicolas Rouletc20cb502017-02-01 12:35:24 -080025#include <sys/prctl.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080026#include <time.h>
27#include <new>
Glenn Kasten535e1612016-12-05 12:19:36 -080028#include <audio_utils/roundup.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080029#include <media/nbaio/NBLog.h>
30#include <utils/Log.h>
Glenn Kasten4e01ef62013-07-11 14:29:59 -070031#include <utils/String8.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080032
Nicolas Roulet40a44982017-02-03 13:39:57 -080033#include <queue>
34
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080035namespace android {
36
37int 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
Nicolas Roulet40a44982017-02-03 13:39:57 -080054NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080055 ALOGW_IF(entry[offsetof(struct entry, type)] != EVENT_START_FMT,
56 "Created format entry with invalid event type %d", entry[offsetof(struct entry, type)]);
Nicolas Roulet40a44982017-02-03 13:39:57 -080057}
58
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080059NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
60
Nicolas Roulet40a44982017-02-03 13:39:57 -080061const char *NBLog::FormatEntry::formatString() const {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080062 return (const char*) mEntry + offsetof(entry, data);
Nicolas Roulet40a44982017-02-03 13:39:57 -080063}
64
65size_t NBLog::FormatEntry::formatStringLength() const {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080066 return mEntry[offsetof(entry, length)];
Nicolas Roulet40a44982017-02-03 13:39:57 -080067}
68
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080069NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
70 auto it = begin();
Nicolas Roulet1ca75122017-03-16 14:19:59 -070071 // skip start fmt
72 ++it;
73 // skip timestamp
74 ++it;
75 // Skip author if present
76 if (it->type == EVENT_AUTHOR) {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080077 ++it;
Nicolas Roulet40a44982017-02-03 13:39:57 -080078 }
Nicolas Roulet1ca75122017-03-16 14:19:59 -070079 return it;
Nicolas Roulet40a44982017-02-03 13:39:57 -080080}
81
82timespec NBLog::FormatEntry::timestamp() const {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080083 auto it = begin();
Nicolas Roulet1ca75122017-03-16 14:19:59 -070084 // skip start fmt
85 ++it;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080086 return it.payload<timespec>();
Nicolas Roulet40a44982017-02-03 13:39:57 -080087}
88
89pid_t NBLog::FormatEntry::author() const {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080090 auto it = begin();
Nicolas Roulet1ca75122017-03-16 14:19:59 -070091 // 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) {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080097 return it.payload<int>();
Nicolas Roulet40a44982017-02-03 13:39:57 -080098 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -080099 return -1;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800100}
101
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800102NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
103 std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
104 auto it = begin();
Nicolas Roulet40a44982017-02-03 13:39:57 -0800105 // copy fmt start entry
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800106 it.copyTo(dst);
Nicolas Roulet1ca75122017-03-16 14:19:59 -0700107 // copy timestamp
108 (++it).copyTo(dst);
Nicolas Roulet40a44982017-02-03 13:39:57 -0800109 // insert author entry
110 size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
111 uint8_t authorEntry[authorEntrySize];
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800112 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;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800117 dst->write(authorEntry, authorEntrySize);
118 // copy rest of entries
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800119 while ((++it)->type != EVENT_END_FMT) {
120 it.copyTo(dst);
Nicolas Roulet40a44982017-02-03 13:39:57 -0800121 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800122 it.copyTo(dst);
123 ++it;
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800124 return it;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800125}
126
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800127void 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}
Nicolas Roulet40a44982017-02-03 13:39:57 -0800131
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800132void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
133 memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
134}
135
136NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
137 return iterator(mEntry);
138}
139
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800140NBLog::FormatEntry::iterator::iterator()
141 : ptr(nullptr) {}
142
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800143NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
144 : ptr(entry) {}
145
146NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
147 : ptr(other.ptr) {}
148
149const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
150 return *(entry*) ptr;
151}
152
153const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
154 return (entry*) ptr;
155}
156
157NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
158 ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
159 return *this;
160}
161
162NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
163 ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
164 return *this;
165}
166
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800167NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
168 iterator aux(*this);
169 return ++aux;
170}
171
172NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
173 iterator aux(*this);
174 return --aux;
175}
176
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800177int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
178 return ptr - other.ptr;
179}
180
181bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
182 return ptr != other.ptr;
183}
184
185bool NBLog::FormatEntry::iterator::hasConsistentLength() const {
186 return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] +
187 NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset];
Nicolas Roulet40a44982017-02-03 13:39:57 -0800188}
189
190// ---------------------------------------------------------------------------
191
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800192#if 0 // FIXME see note in NBLog.h
193NBLog::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
200NBLog::Timeline::~Timeline()
201{
202 mShared->~Shared();
203 if (mOwn) {
204 delete[] (char *) mShared;
205 }
206}
207#endif
208
209/*static*/
210size_t NBLog::Timeline::sharedSize(size_t size)
211{
Glenn Kastened99c2b2016-12-12 08:31:24 -0800212 // TODO fifo now supports non-power-of-2 buffer sizes, so could remove the roundup
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800213 return sizeof(Shared) + roundup(size);
214}
215
216// ---------------------------------------------------------------------------
217
218NBLog::Writer::Writer()
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800219 : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false), mPidTag(NULL), mPidTagSize(0)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800220{
221}
222
Glenn Kasten535e1612016-12-05 12:19:36 -0800223NBLog::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)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800230{
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800231 // 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);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800243}
244
Glenn Kasten535e1612016-12-05 12:19:36 -0800245NBLog::Writer::Writer(const sp<IMemory>& iMemory, size_t size)
246 : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800247{
Glenn Kasten535e1612016-12-05 12:19:36 -0800248 mIMemory = iMemory;
249}
250
251NBLog::Writer::~Writer()
252{
253 delete mFifoWriter;
254 delete mFifo;
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800255 delete[] mPidTag;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800256}
257
258void NBLog::Writer::log(const char *string)
259{
260 if (!mEnabled) {
261 return;
262 }
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800263 LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800264 size_t length = strlen(string);
Glenn Kasten535e1612016-12-05 12:19:36 -0800265 if (length > Entry::kMaxLength) {
266 length = Entry::kMaxLength;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800267 }
268 log(EVENT_STRING, string, length);
269}
270
271void 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
282void NBLog::Writer::logvf(const char *fmt, va_list ap)
283{
284 if (!mEnabled) {
285 return;
286 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800287 char buffer[Entry::kMaxLength + 1 /*NUL*/];
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800288 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
299void NBLog::Writer::logTimestamp()
300{
301 if (!mEnabled) {
302 return;
303 }
304 struct timespec ts;
305 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800306 log(EVENT_TIMESTAMP, &ts, sizeof(ts));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800307 }
308}
309
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800310void NBLog::Writer::logTimestamp(const struct timespec &ts)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800311{
312 if (!mEnabled) {
313 return;
314 }
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800315 log(EVENT_TIMESTAMP, &ts, sizeof(ts));
316}
317
318void NBLog::Writer::logInteger(const int x)
319{
320 if (!mEnabled) {
321 return;
322 }
323 log(EVENT_INTEGER, &x, sizeof(x));
324}
325
326void NBLog::Writer::logFloat(const float x)
327{
328 if (!mEnabled) {
329 return;
330 }
331 log(EVENT_FLOAT, &x, sizeof(x));
332}
333
334void NBLog::Writer::logPID()
335{
336 if (!mEnabled) {
337 return;
338 }
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800339 log(EVENT_PID, mPidTag, mPidTagSize);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800340}
341
342void 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
354void 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
363void 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
375void 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
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800421 case '%':
422 break;
423
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800424 default:
425 ALOGW("NBLog Writer parsed invalid format specifier: %c", *p);
426 break;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800427 }
428 }
429 Writer::logEnd();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800430}
431
432void NBLog::Writer::log(Event event, const void *data, size_t length)
433{
434 if (!mEnabled) {
435 return;
436 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800437 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.
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800441 return;
442 }
443 switch (event) {
444 case EVENT_STRING:
445 case EVENT_TIMESTAMP:
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800446 case EVENT_INTEGER:
447 case EVENT_FLOAT:
448 case EVENT_PID:
449 case EVENT_START_FMT:
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800450 break;
451 case EVENT_RESERVED:
452 default:
453 return;
454 }
455 Entry entry(event, data, length);
456 log(&entry, true /*trusted*/);
457}
458
459void 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 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800468 size_t need = entry->mLength + Entry::kOverhead; // mEvent, mLength, data[length], mLength
469 // need = number of bytes remaining to write
470
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800471 // 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.
Glenn Kasten535e1612016-12-05 12:19:36 -0800473 uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
474 for (size_t i = 0; i < need; i++) {
475 temp[i] = entry->readAt(i);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800476 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800477 mFifoWriter->write(temp, need);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800478}
479
480bool NBLog::Writer::isEnabled() const
481{
482 return mEnabled;
483}
484
485bool NBLog::Writer::setEnabled(bool enabled)
486{
487 bool old = mEnabled;
488 mEnabled = enabled && mShared != NULL;
489 return old;
490}
491
492// ---------------------------------------------------------------------------
493
494NBLog::LockedWriter::LockedWriter()
495 : Writer()
496{
497}
498
Glenn Kasten535e1612016-12-05 12:19:36 -0800499NBLog::LockedWriter::LockedWriter(void *shared, size_t size)
500 : Writer(shared, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800501{
502}
503
504void NBLog::LockedWriter::log(const char *string)
505{
506 Mutex::Autolock _l(mLock);
507 Writer::log(string);
508}
509
510void 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
520void 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
527void 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
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800534void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800535{
536 Mutex::Autolock _l(mLock);
537 Writer::logTimestamp(ts);
538}
539
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800540void NBLog::LockedWriter::logInteger(const int x)
541{
542 Mutex::Autolock _l(mLock);
543 Writer::logInteger(x);
544}
545
546void NBLog::LockedWriter::logFloat(const float x)
547{
548 Mutex::Autolock _l(mLock);
549 Writer::logFloat(x);
550}
551
552void NBLog::LockedWriter::logPID()
553{
554 Mutex::Autolock _l(mLock);
555 Writer::logPID();
556}
557
558void NBLog::LockedWriter::logStart(const char *fmt)
559{
560 Mutex::Autolock _l(mLock);
561 Writer::logStart(fmt);
562}
563
564
565void NBLog::LockedWriter::logEnd()
566{
567 Mutex::Autolock _l(mLock);
568 Writer::logEnd();
569}
570
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800571bool NBLog::LockedWriter::isEnabled() const
572{
573 Mutex::Autolock _l(mLock);
574 return Writer::isEnabled();
575}
576
577bool NBLog::LockedWriter::setEnabled(bool enabled)
578{
579 Mutex::Autolock _l(mLock);
580 return Writer::setEnabled(enabled);
581}
582
583// ---------------------------------------------------------------------------
584
Glenn Kasten535e1612016-12-05 12:19:36 -0800585NBLog::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)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800592{
593}
594
Glenn Kasten535e1612016-12-05 12:19:36 -0800595NBLog::Reader::Reader(const sp<IMemory>& iMemory, size_t size)
596 : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800597{
Glenn Kasten535e1612016-12-05 12:19:36 -0800598 mIMemory = iMemory;
599}
600
601NBLog::Reader::~Reader()
602{
603 delete mFifoReader;
604 delete mFifo;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800605}
606
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800607uint8_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
Nicolas Roulet40a44982017-02-03 13:39:57 -0800624std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800625{
Glenn Kasten535e1612016-12-05 12:19:36 -0800626 if (mFifoReader == NULL) {
Nicolas Roulet40a44982017-02-03 13:39:57 -0800627 return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot());
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800628 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800629 // make a copy to avoid race condition with writer
Glenn Kasten535e1612016-12-05 12:19:36 -0800630 size_t capacity = mFifo->capacity();
631
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800632 // 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 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800641
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800642 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;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800685 return snapshot;
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800686
Nicolas Roulet40a44982017-02-03 13:39:57 -0800687}
688
689void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
690{
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800691#if 0
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800692 struct timespec ts;
693 time_t maxSec = -1;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800694 while (entry - start >= (int) Entry::kOverhead) {
695 if (prevEntry - start < 0 || !prevEntry.hasConsistentLength()) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800696 break;
697 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800698 if (prevEntry->type == EVENT_TIMESTAMP) {
699 if (prevEntry->length != sizeof(struct timespec)) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800700 // corrupt
701 break;
702 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800703 prevEntry.copyData((uint8_t*) &ts);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800704 if (ts.tv_sec > maxSec) {
705 maxSec = ts.tv_sec;
706 }
707 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800708 --entry;
709 --prevEntry;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800710 }
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800711#endif
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700712 mFd = fd;
713 mIndent = indent;
714 String8 timestamp, body;
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800715 size_t lost = snapshot.lost() + (snapshot.begin() - FormatEntry::iterator(snapshot.data()));
Glenn Kastenc02c9612013-10-15 09:25:11 -0700716 if (lost > 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700717 body.appendFormat("warning: lost %zu bytes worth of events", lost);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700718 // 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);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800721 }
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800722#if 0
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800723 size_t width = 1;
724 while (maxSec >= 10) {
725 ++width;
726 maxSec /= 10;
727 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800728 if (maxSec >= 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700729 timestamp.appendFormat("[%*s]", (int) width + 4, "");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800730 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700731 bool deferredTimestamp = false;
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800732#endif
733 for (auto entry = snapshot.begin(); entry != snapshot.end();) {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800734 switch (entry->type) {
735#if 0
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800736 case EVENT_STRING:
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800737 body.appendFormat("%.*s", (int) entry.length(), entry.data());
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700738 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800739 case EVENT_TIMESTAMP: {
740 // already checked that length == sizeof(struct timespec);
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800741 entry.copyData((const uint8_t*) &ts);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800742 long prevNsec = ts.tv_nsec;
743 long deltaMin = LONG_MAX;
744 long deltaMax = -1;
745 long deltaTotal = 0;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800746 auto aux(entry);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800747 for (;;) {
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800748 ++aux;
749 if (end - aux >= 0 || aux.type() != EVENT_TIMESTAMP) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800750 break;
751 }
752 struct timespec tsNext;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800753 aux.copyData((const uint8_t*) &tsNext);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800754 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 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800770 size_t n = (aux - entry) / (sizeof(struct timespec) + 3 /*Entry::kOverhead?*/);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700771 if (deferredTimestamp) {
772 dumpLine(timestamp, body);
773 deferredTimestamp = false;
774 }
775 timestamp.clear();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800776 if (n >= kSquashTimestamp) {
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700777 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));
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800781 entry = aux;
782 // advance = 0;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800783 break;
784 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700785 timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
786 (int) (ts.tv_nsec / 1000000));
787 deferredTimestamp = true;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800788 }
789 break;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800790 case EVENT_INTEGER:
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800791 appendInt(&body, entry.data());
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800792 break;
793 case EVENT_FLOAT:
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800794 appendFloat(&body, entry.data());
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800795 break;
796 case EVENT_PID:
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800797 appendPID(&body, entry.data(), entry.length());
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800798 break;
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800799#endif
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800800 case EVENT_START_FMT:
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800801 // right now, this is the only supported case
802 entry = handleFormat(FormatEntry(entry), &timestamp, &body);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800803 break;
804 case EVENT_END_FMT:
805 body.appendFormat("warning: got to end format event");
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800806 ++entry;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800807 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800808 case EVENT_RESERVED:
809 default:
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800810 body.appendFormat("warning: unexpected event %d", entry->type);
811 ++entry;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800812 break;
813 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700814
815 if (!body.isEmpty()) {
816 dumpLine(timestamp, body);
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800817 // deferredTimestamp = false;
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700818 }
819 }
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800820 // if (deferredTimestamp) {
821 // dumpLine(timestamp, body);
822 // }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800823}
824
Nicolas Roulet40a44982017-02-03 13:39:57 -0800825void 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
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800832void NBLog::Reader::dumpLine(const String8 &timestamp, String8 &body)
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700833{
834 if (mFd >= 0) {
Elliott Hughes8b5f6422014-05-22 01:22:06 -0700835 dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700836 } else {
837 ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
838 }
839 body.clear();
840}
841
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800842bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
843{
Glenn Kasten481fb672013-09-30 14:39:28 -0700844 return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800845}
846
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800847void 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
854void NBLog::appendInt(String8 *body, const void *data) {
855 int x = *((int*) data);
856 body->appendFormat("<%d>", x);
857}
858
859void NBLog::appendFloat(String8 *body, const void *data) {
860 float f;
861 memcpy(&f, data, sizeof(float));
862 body->appendFormat("<%f>", f);
863}
864
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800865void NBLog::appendPID(String8 *body, const void* data, size_t length) {
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800866 pid_t id = *((pid_t*) data);
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800867 char * name = &((char*) data)[sizeof(pid_t)];
868 body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800869}
870
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800871NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
872 String8 *timestamp,
873 String8 *body) {
Nicolas Roulet40a44982017-02-03 13:39:57 -0800874 // log timestamp
875 struct timespec ts = fmtEntry.timestamp();
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800876 timestamp->clear();
877 timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
878 (int) (ts.tv_nsec / 1000000));
Nicolas Roulet40a44982017-02-03 13:39:57 -0800879
880 // log author (if present)
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800881 handleAuthor(fmtEntry, body);
Nicolas Roulet40a44982017-02-03 13:39:57 -0800882
883 // log string
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800884 NBLog::FormatEntry::iterator arg = fmtEntry.args();
Nicolas Roulet40a44982017-02-03 13:39:57 -0800885
886 const char* fmt = fmtEntry.formatString();
887 size_t fmt_length = fmtEntry.formatStringLength();
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800888
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 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800894 // case "%%""
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800895 if (fmt[++fmt_offset] == '%') {
896 body->append("%");
897 continue;
898 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800899 // case "%\0"
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800900 if (fmt_offset == fmt_length) {
901 continue;
902 }
903
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800904 NBLog::Event event = (NBLog::Event) arg->type;
905 size_t length = arg->length;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800906
907 // TODO check length for event type is correct
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800908
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800909 if (event == EVENT_END_FMT) {
910 break;
911 }
912
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800913 // TODO: implement more complex formatting such as %.3f
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800914 const uint8_t *datum = arg->data; // pointer to the current event args
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800915 switch(fmt[fmt_offset])
916 {
917 case 's': // string
Nicolas Roulet4da78202017-02-03 12:53:39 -0800918 ALOGW_IF(event != EVENT_STRING,
919 "NBLog Reader incompatible event for string specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800920 body->append((const char*) datum, length);
921 break;
922
923 case 't': // timestamp
Nicolas Roulet4da78202017-02-03 12:53:39 -0800924 ALOGW_IF(event != EVENT_TIMESTAMP,
925 "NBLog Reader incompatible event for timestamp specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800926 appendTimestamp(body, datum);
927 break;
928
929 case 'd': // integer
Nicolas Roulet4da78202017-02-03 12:53:39 -0800930 ALOGW_IF(event != EVENT_INTEGER,
931 "NBLog Reader incompatible event for integer specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800932 appendInt(body, datum);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800933 break;
934
935 case 'f': // float
Nicolas Roulet4da78202017-02-03 12:53:39 -0800936 ALOGW_IF(event != EVENT_FLOAT,
937 "NBLog Reader incompatible event for float specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800938 appendFloat(body, datum);
939 break;
940
941 case 'p': // pid
Nicolas Roulet4da78202017-02-03 12:53:39 -0800942 ALOGW_IF(event != EVENT_PID,
943 "NBLog Reader incompatible event for pid specifier: %d", event);
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800944 appendPID(body, datum, length);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800945 break;
946
947 default:
948 ALOGW("NBLog Reader encountered unknown character %c", fmt[fmt_offset]);
949 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800950 ++arg;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800951 }
Nicolas Rouletcd5dd012017-02-13 12:09:28 -0800952 ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type);
953 ++arg;
954 return arg;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800955}
956
957// ---------------------------------------------------------------------------
958
959NBLog::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
968void 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
974struct 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
982bool 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
986bool 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
992void NBLog::Merger::merge() {
993 int nLogs = mNamedReaders.size();
994 std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800995 std::vector<NBLog::FormatEntry::iterator> offsets(nLogs);
Nicolas Roulet40a44982017-02-03 13:39:57 -0800996 for (int i = 0; i < nLogs; ++i) {
997 snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -0800998 offsets[i] = snapshots[i]->begin();
Nicolas Roulet40a44982017-02-03 13:39:57 -0800999 }
1000 // initialize offsets
Nicolas Roulet40a44982017-02-03 13:39:57 -08001001 // 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 {
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -08001006 if (offsets[i] != snapshots[i]->end()) {
1007 timespec ts = FormatEntry(offsets[i]).timestamp();
1008 timestamps.emplace(ts, i);
Nicolas Roulet40a44982017-02-03 13:39:57 -08001009 }
1010 }
1011
1012 while (!timestamps.empty()) {
1013 // find minimum timestamp
1014 int index = timestamps.top().index;
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -08001015 // copy it to the log, increasing offset
1016 offsets[index] = FormatEntry(offsets[index]).copyWithAuthor(mFifoWriter, index);
Nicolas Roulet40a44982017-02-03 13:39:57 -08001017 // update data structures
Nicolas Roulet40a44982017-02-03 13:39:57 -08001018 timestamps.pop();
Nicolas Roulet6ea1d7e2017-02-14 16:17:31 -08001019 if (offsets[index] != snapshots[index]->end()) {
1020 timespec ts = FormatEntry(offsets[index]).timestamp();
1021 timestamps.emplace(ts, index);
Nicolas Roulet40a44982017-02-03 13:39:57 -08001022 }
1023 }
1024}
1025
1026const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
1027 return &mNamedReaders;
1028}
1029
1030NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
1031 : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
1032
1033size_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);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -08001038}
1039
Nicolas Rouletdcdfaec2017-02-14 10:18:39 -08001040NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
1041 : mMerger(merger),
1042 mTimeoutUs(0) {}
1043
1044NBLog::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
1051bool 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
1068void NBLog::MergeThread::wakeup() {
1069 setTimeoutUs(kThreadWakeupPeriodUs);
1070}
1071
1072void NBLog::MergeThread::setTimeoutUs(int time) {
1073 AutoMutex _l(mMutex);
1074 mTimeoutUs = time;
1075 mCond.signal();
1076}
1077
Glenn Kasten11d8dfc2013-01-14 14:53:13 -08001078} // namespace android