blob: 49caeb8097f9dc86dc6a03cefb1d65f08a01e32e [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
20#include <stdarg.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <string.h>
Nicolas Rouletc20cb502017-02-01 12:35:24 -080024#include <sys/prctl.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080025#include <time.h>
26#include <new>
Glenn Kasten535e1612016-12-05 12:19:36 -080027#include <audio_utils/roundup.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080028#include <media/nbaio/NBLog.h>
29#include <utils/Log.h>
Glenn Kasten4e01ef62013-07-11 14:29:59 -070030#include <utils/String8.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080031
Nicolas Roulet40a44982017-02-03 13:39:57 -080032#include <queue>
33
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080034namespace android {
35
36int NBLog::Entry::readAt(size_t offset) const
37{
38 // FIXME This is too slow, despite the name it is used during writing
39 if (offset == 0)
40 return mEvent;
41 else if (offset == 1)
42 return mLength;
43 else if (offset < (size_t) (mLength + 2))
44 return ((char *) mData)[offset - 2];
45 else if (offset == (size_t) (mLength + 2))
46 return mLength;
47 else
48 return 0;
49}
50
51// ---------------------------------------------------------------------------
52
Nicolas Roulet40a44982017-02-03 13:39:57 -080053NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
54 ALOGW_IF(entry[0] != EVENT_START_FMT,
55 "Created format entry with invalid event type %d",
56 entry[0]);
57}
58
59const char *NBLog::FormatEntry::formatString() const {
60 return (const char*) mEntry + 2;
61}
62
63size_t NBLog::FormatEntry::formatStringLength() const {
64 return mEntry[1];
65}
66
67const uint8_t *NBLog::FormatEntry::args() const {
68 const uint8_t *ptr = mEntry + mEntry[1] + NBLog::Entry::kOverhead;
69 if (ptr[0] != EVENT_TIMESTAMP) { // skip author if present
70 ptr += ptr[1] + NBLog::Entry::kOverhead;
71 }
72 return ptr + ptr[1] + NBLog::Entry::kOverhead;
73}
74
75timespec NBLog::FormatEntry::timestamp() const {
76 const uint8_t *ptr = mEntry + mEntry[1] + NBLog::Entry::kOverhead;
77 if (ptr[0] != EVENT_TIMESTAMP) { // skip authors if present
78 ptr += ptr[1] + NBLog::Entry::kOverhead;
79 }
80 // by this point, we should be standing in the timestamp entry
81 return *((struct timespec*) (&ptr[2]));
82}
83
84pid_t NBLog::FormatEntry::author() const {
85 size_t authorOffset = mEntry[1] + NBLog::Entry::kOverhead;
86 // return -1 if the entry has no author
87 if (mEntry[authorOffset] != EVENT_AUTHOR) {
88 return -1;
89 }
90 return *(pid_t*)(mEntry + authorOffset + 2);
91}
92
93size_t NBLog::FormatEntry::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
94 // copy fmt start entry
95 size_t entryOffset = copyEntry(dst, mEntry);
96 // insert author entry
97 size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
98 uint8_t authorEntry[authorEntrySize];
99 authorEntry[0] = EVENT_AUTHOR;
100 authorEntry[1] = authorEntry[authorEntrySize - 1] = sizeof(author);
101 *(int*) (&authorEntry[2]) = author;
102 dst->write(authorEntry, authorEntrySize);
103 // copy rest of entries
104 Event lastEvent = EVENT_TIMESTAMP;
105 while (lastEvent != EVENT_END_FMT) {
106 lastEvent = (Event) mEntry[entryOffset];
107 entryOffset += copyEntry(dst, mEntry + entryOffset);
108 }
109 return entryOffset;
110}
111
112
113size_t NBLog::FormatEntry::copyEntry(std::unique_ptr<audio_utils_fifo_writer> &dst,
114 const uint8_t *src) const {
115 size_t length = src[1] + NBLog::Entry::kOverhead;
116 dst->write(src, length);
117 return length;
118}
119
120// ---------------------------------------------------------------------------
121
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800122#if 0 // FIXME see note in NBLog.h
123NBLog::Timeline::Timeline(size_t size, void *shared)
124 : mSize(roundup(size)), mOwn(shared == NULL),
125 mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared))
126{
127 new (mShared) Shared;
128}
129
130NBLog::Timeline::~Timeline()
131{
132 mShared->~Shared();
133 if (mOwn) {
134 delete[] (char *) mShared;
135 }
136}
137#endif
138
139/*static*/
140size_t NBLog::Timeline::sharedSize(size_t size)
141{
Glenn Kastened99c2b2016-12-12 08:31:24 -0800142 // TODO fifo now supports non-power-of-2 buffer sizes, so could remove the roundup
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800143 return sizeof(Shared) + roundup(size);
144}
145
146// ---------------------------------------------------------------------------
147
148NBLog::Writer::Writer()
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800149 : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false), mPidTag(NULL), mPidTagSize(0)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800150{
151}
152
Glenn Kasten535e1612016-12-05 12:19:36 -0800153NBLog::Writer::Writer(void *shared, size_t size)
154 : mShared((Shared *) shared),
155 mFifo(mShared != NULL ?
156 new audio_utils_fifo(size, sizeof(uint8_t),
157 mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
158 mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL),
159 mEnabled(mFifoWriter != NULL)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800160{
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800161 // caching pid and process name
162 pid_t id = ::getpid();
163 char procName[16];
164 int status = prctl(PR_GET_NAME, procName);
165 if (status) { // error getting process name
166 procName[0] = '\0';
167 }
168 size_t length = strlen(procName);
169 mPidTagSize = length + sizeof(pid_t);
170 mPidTag = new char[mPidTagSize];
171 memcpy(mPidTag, &id, sizeof(pid_t));
172 memcpy(mPidTag + sizeof(pid_t), procName, length);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800173}
174
Glenn Kasten535e1612016-12-05 12:19:36 -0800175NBLog::Writer::Writer(const sp<IMemory>& iMemory, size_t size)
176 : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800177{
Glenn Kasten535e1612016-12-05 12:19:36 -0800178 mIMemory = iMemory;
179}
180
181NBLog::Writer::~Writer()
182{
183 delete mFifoWriter;
184 delete mFifo;
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800185 delete[] mPidTag;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800186}
187
188void NBLog::Writer::log(const char *string)
189{
190 if (!mEnabled) {
191 return;
192 }
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800193 LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800194 size_t length = strlen(string);
Glenn Kasten535e1612016-12-05 12:19:36 -0800195 if (length > Entry::kMaxLength) {
196 length = Entry::kMaxLength;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800197 }
198 log(EVENT_STRING, string, length);
199}
200
201void NBLog::Writer::logf(const char *fmt, ...)
202{
203 if (!mEnabled) {
204 return;
205 }
206 va_list ap;
207 va_start(ap, fmt);
208 Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter
209 va_end(ap);
210}
211
212void NBLog::Writer::logvf(const char *fmt, va_list ap)
213{
214 if (!mEnabled) {
215 return;
216 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800217 char buffer[Entry::kMaxLength + 1 /*NUL*/];
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800218 int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
219 if (length >= (int) sizeof(buffer)) {
220 length = sizeof(buffer) - 1;
221 // NUL termination is not required
222 // buffer[length] = '\0';
223 }
224 if (length >= 0) {
225 log(EVENT_STRING, buffer, length);
226 }
227}
228
229void NBLog::Writer::logTimestamp()
230{
231 if (!mEnabled) {
232 return;
233 }
234 struct timespec ts;
235 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800236 log(EVENT_TIMESTAMP, &ts, sizeof(ts));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800237 }
238}
239
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800240void NBLog::Writer::logTimestamp(const struct timespec &ts)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800241{
242 if (!mEnabled) {
243 return;
244 }
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800245 log(EVENT_TIMESTAMP, &ts, sizeof(ts));
246}
247
248void NBLog::Writer::logInteger(const int x)
249{
250 if (!mEnabled) {
251 return;
252 }
253 log(EVENT_INTEGER, &x, sizeof(x));
254}
255
256void NBLog::Writer::logFloat(const float x)
257{
258 if (!mEnabled) {
259 return;
260 }
261 log(EVENT_FLOAT, &x, sizeof(x));
262}
263
264void NBLog::Writer::logPID()
265{
266 if (!mEnabled) {
267 return;
268 }
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800269 log(EVENT_PID, mPidTag, mPidTagSize);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800270}
271
272void NBLog::Writer::logStart(const char *fmt)
273{
274 if (!mEnabled) {
275 return;
276 }
277 size_t length = strlen(fmt);
278 if (length > Entry::kMaxLength) {
279 length = Entry::kMaxLength;
280 }
281 log(EVENT_START_FMT, fmt, length);
282}
283
284void NBLog::Writer::logEnd()
285{
286 if (!mEnabled) {
287 return;
288 }
289 Entry entry = Entry(EVENT_END_FMT, NULL, 0);
290 log(&entry, true);
291}
292
293void NBLog::Writer::logFormat(const char *fmt, ...)
294{
295 if (!mEnabled) {
296 return;
297 }
298
299 va_list ap;
300 va_start(ap, fmt);
301 Writer::logVFormat(fmt, ap);
302 va_end(ap);
303}
304
305void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
306{
307 if (!mEnabled) {
308 return;
309 }
310 Writer::logStart(fmt);
311 int i;
312 double f;
313 char* s;
314 struct timespec t;
315 Writer::logTimestamp();
316 for (const char *p = fmt; *p != '\0'; p++) {
317 // TODO: implement more complex formatting such as %.3f
318 if (*p != '%') {
319 continue;
320 }
321 switch(*++p) {
322 case 's': // string
323 s = va_arg(argp, char *);
324 Writer::log(s);
325 break;
326
327 case 't': // timestamp
328 t = va_arg(argp, struct timespec);
329 Writer::logTimestamp(t);
330 break;
331
332 case 'd': // integer
333 i = va_arg(argp, int);
334 Writer::logInteger(i);
335 break;
336
337 case 'f': // float
338 f = va_arg(argp, double); // float arguments are promoted to double in vararg lists
339 Writer::logFloat((float)f);
340 break;
341
342 case 'p': // pid
343 Writer::logPID();
344 break;
345
346 // the "%\0" case finishes parsing
347 case '\0':
348 --p;
349 break;
350
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800351 case '%':
352 break;
353
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800354 default:
355 ALOGW("NBLog Writer parsed invalid format specifier: %c", *p);
356 break;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800357 }
358 }
359 Writer::logEnd();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800360}
361
362void NBLog::Writer::log(Event event, const void *data, size_t length)
363{
364 if (!mEnabled) {
365 return;
366 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800367 if (data == NULL || length > Entry::kMaxLength) {
368 // TODO Perhaps it makes sense to display truncated data or at least a
369 // message that the data is too long? The current behavior can create
370 // a confusion for a programmer debugging their code.
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800371 return;
372 }
373 switch (event) {
374 case EVENT_STRING:
375 case EVENT_TIMESTAMP:
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800376 case EVENT_INTEGER:
377 case EVENT_FLOAT:
378 case EVENT_PID:
379 case EVENT_START_FMT:
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800380 break;
381 case EVENT_RESERVED:
382 default:
383 return;
384 }
385 Entry entry(event, data, length);
386 log(&entry, true /*trusted*/);
387}
388
389void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
390{
391 if (!mEnabled) {
392 return;
393 }
394 if (!trusted) {
395 log(entry->mEvent, entry->mData, entry->mLength);
396 return;
397 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800398 size_t need = entry->mLength + Entry::kOverhead; // mEvent, mLength, data[length], mLength
399 // need = number of bytes remaining to write
400
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800401 // FIXME optimize this using memcpy for the data part of the Entry.
402 // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
Glenn Kasten535e1612016-12-05 12:19:36 -0800403 uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
404 for (size_t i = 0; i < need; i++) {
405 temp[i] = entry->readAt(i);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800406 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800407 mFifoWriter->write(temp, need);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800408}
409
410bool NBLog::Writer::isEnabled() const
411{
412 return mEnabled;
413}
414
415bool NBLog::Writer::setEnabled(bool enabled)
416{
417 bool old = mEnabled;
418 mEnabled = enabled && mShared != NULL;
419 return old;
420}
421
422// ---------------------------------------------------------------------------
423
424NBLog::LockedWriter::LockedWriter()
425 : Writer()
426{
427}
428
Glenn Kasten535e1612016-12-05 12:19:36 -0800429NBLog::LockedWriter::LockedWriter(void *shared, size_t size)
430 : Writer(shared, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800431{
432}
433
434void NBLog::LockedWriter::log(const char *string)
435{
436 Mutex::Autolock _l(mLock);
437 Writer::log(string);
438}
439
440void NBLog::LockedWriter::logf(const char *fmt, ...)
441{
442 // FIXME should not take the lock until after formatting is done
443 Mutex::Autolock _l(mLock);
444 va_list ap;
445 va_start(ap, fmt);
446 Writer::logvf(fmt, ap);
447 va_end(ap);
448}
449
450void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
451{
452 // FIXME should not take the lock until after formatting is done
453 Mutex::Autolock _l(mLock);
454 Writer::logvf(fmt, ap);
455}
456
457void NBLog::LockedWriter::logTimestamp()
458{
459 // FIXME should not take the lock until after the clock_gettime() syscall
460 Mutex::Autolock _l(mLock);
461 Writer::logTimestamp();
462}
463
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800464void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800465{
466 Mutex::Autolock _l(mLock);
467 Writer::logTimestamp(ts);
468}
469
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800470void NBLog::LockedWriter::logInteger(const int x)
471{
472 Mutex::Autolock _l(mLock);
473 Writer::logInteger(x);
474}
475
476void NBLog::LockedWriter::logFloat(const float x)
477{
478 Mutex::Autolock _l(mLock);
479 Writer::logFloat(x);
480}
481
482void NBLog::LockedWriter::logPID()
483{
484 Mutex::Autolock _l(mLock);
485 Writer::logPID();
486}
487
488void NBLog::LockedWriter::logStart(const char *fmt)
489{
490 Mutex::Autolock _l(mLock);
491 Writer::logStart(fmt);
492}
493
494
495void NBLog::LockedWriter::logEnd()
496{
497 Mutex::Autolock _l(mLock);
498 Writer::logEnd();
499}
500
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800501bool NBLog::LockedWriter::isEnabled() const
502{
503 Mutex::Autolock _l(mLock);
504 return Writer::isEnabled();
505}
506
507bool NBLog::LockedWriter::setEnabled(bool enabled)
508{
509 Mutex::Autolock _l(mLock);
510 return Writer::setEnabled(enabled);
511}
512
513// ---------------------------------------------------------------------------
514
Glenn Kasten535e1612016-12-05 12:19:36 -0800515NBLog::Reader::Reader(const void *shared, size_t size)
516 : mShared((/*const*/ Shared *) shared), /*mIMemory*/
517 mFd(-1), mIndent(0),
518 mFifo(mShared != NULL ?
519 new audio_utils_fifo(size, sizeof(uint8_t),
520 mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
521 mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800522{
523}
524
Glenn Kasten535e1612016-12-05 12:19:36 -0800525NBLog::Reader::Reader(const sp<IMemory>& iMemory, size_t size)
526 : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800527{
Glenn Kasten535e1612016-12-05 12:19:36 -0800528 mIMemory = iMemory;
529}
530
531NBLog::Reader::~Reader()
532{
533 delete mFifoReader;
534 delete mFifo;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800535}
536
Nicolas Roulet40a44982017-02-03 13:39:57 -0800537std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800538{
Glenn Kasten535e1612016-12-05 12:19:36 -0800539 if (mFifoReader == NULL) {
Nicolas Roulet40a44982017-02-03 13:39:57 -0800540 return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot());
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800541 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800542 // make a copy to avoid race condition with writer
Glenn Kasten535e1612016-12-05 12:19:36 -0800543 size_t capacity = mFifo->capacity();
544
Nicolas Roulet40a44982017-02-03 13:39:57 -0800545 std::unique_ptr<Snapshot> snapshot(new Snapshot(capacity));
Glenn Kasten535e1612016-12-05 12:19:36 -0800546
Nicolas Roulet40a44982017-02-03 13:39:57 -0800547 ssize_t actual = mFifoReader->read((void*) snapshot->mData, capacity, NULL /*timeout*/,
548 &(snapshot->mLost));
Glenn Kasten535e1612016-12-05 12:19:36 -0800549 ALOG_ASSERT(actual <= capacity);
Nicolas Roulet40a44982017-02-03 13:39:57 -0800550 snapshot->mAvail = actual > 0 ? (size_t) actual : 0;
551 return snapshot;
552}
553
554void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
555{
556 size_t i = snapshot.available();
557 const uint8_t *snapData = snapshot.data();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800558 Event event;
559 size_t length;
560 struct timespec ts;
561 time_t maxSec = -1;
Glenn Kasten535e1612016-12-05 12:19:36 -0800562 while (i >= Entry::kOverhead) {
Nicolas Roulet40a44982017-02-03 13:39:57 -0800563 length = snapData[i - 1];
564 if (length + Entry::kOverhead > i || snapData[i - length - 2] != length) {
565
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800566 break;
567 }
Nicolas Roulet40a44982017-02-03 13:39:57 -0800568 event = (Event) snapData[i - length - Entry::kOverhead];
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800569 if (event == EVENT_TIMESTAMP) {
570 if (length != sizeof(struct timespec)) {
571 // corrupt
572 break;
573 }
Nicolas Roulet40a44982017-02-03 13:39:57 -0800574 memcpy(&ts, &snapData[i - length - 1], sizeof(struct timespec));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800575 if (ts.tv_sec > maxSec) {
576 maxSec = ts.tv_sec;
577 }
578 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800579 i -= length + Entry::kOverhead;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800580 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700581 mFd = fd;
582 mIndent = indent;
583 String8 timestamp, body;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800584 size_t lost = snapshot.lost() + i;
Glenn Kastenc02c9612013-10-15 09:25:11 -0700585 if (lost > 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700586 body.appendFormat("warning: lost %zu bytes worth of events", lost);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700587 // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
588 // log to push it out. Consider keeping the timestamp/body between calls to readAt().
589 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800590 }
591 size_t width = 1;
592 while (maxSec >= 10) {
593 ++width;
594 maxSec /= 10;
595 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800596 if (maxSec >= 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700597 timestamp.appendFormat("[%*s]", (int) width + 4, "");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800598 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700599 bool deferredTimestamp = false;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800600 while (i < snapshot.available()) {
601 event = (Event) snapData[i];
602 length = snapData[i + 1];
603 const void *data = &snapData[i + 2];
Glenn Kasten535e1612016-12-05 12:19:36 -0800604 size_t advance = length + Entry::kOverhead;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800605 switch (event) {
606 case EVENT_STRING:
Glenn Kasten95d287d2014-04-28 14:11:45 -0700607 body.appendFormat("%.*s", (int) length, (const char *) data);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700608 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800609 case EVENT_TIMESTAMP: {
610 // already checked that length == sizeof(struct timespec);
611 memcpy(&ts, data, sizeof(struct timespec));
612 long prevNsec = ts.tv_nsec;
613 long deltaMin = LONG_MAX;
614 long deltaMax = -1;
615 long deltaTotal = 0;
616 size_t j = i;
617 for (;;) {
Glenn Kasten535e1612016-12-05 12:19:36 -0800618 j += sizeof(struct timespec) + 3 /*Entry::kOverhead?*/;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800619 if (j >= snapshot.available() || (Event) snapData[j] != EVENT_TIMESTAMP) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800620 break;
621 }
622 struct timespec tsNext;
Nicolas Roulet40a44982017-02-03 13:39:57 -0800623 memcpy(&tsNext, &snapData[j + 2], sizeof(struct timespec));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800624 if (tsNext.tv_sec != ts.tv_sec) {
625 break;
626 }
627 long delta = tsNext.tv_nsec - prevNsec;
628 if (delta < 0) {
629 break;
630 }
631 if (delta < deltaMin) {
632 deltaMin = delta;
633 }
634 if (delta > deltaMax) {
635 deltaMax = delta;
636 }
637 deltaTotal += delta;
638 prevNsec = tsNext.tv_nsec;
639 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800640 size_t n = (j - i) / (sizeof(struct timespec) + 3 /*Entry::kOverhead?*/);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700641 if (deferredTimestamp) {
642 dumpLine(timestamp, body);
643 deferredTimestamp = false;
644 }
645 timestamp.clear();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800646 if (n >= kSquashTimestamp) {
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700647 timestamp.appendFormat("[%d.%03d to .%.03d by .%.03d to .%.03d]",
648 (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
649 (int) ((ts.tv_nsec + deltaTotal) / 1000000),
650 (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800651 i = j;
652 advance = 0;
653 break;
654 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700655 timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
656 (int) (ts.tv_nsec / 1000000));
657 deferredTimestamp = true;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800658 } break;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800659 case EVENT_INTEGER:
660 appendInt(&body, data);
661 break;
662 case EVENT_FLOAT:
663 appendFloat(&body, data);
664 break;
665 case EVENT_PID:
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800666 appendPID(&body, data, length);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800667 break;
668 case EVENT_START_FMT:
Nicolas Roulet40a44982017-02-03 13:39:57 -0800669 advance += handleFormat(FormatEntry(snapData + i), &timestamp, &body);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800670 break;
671 case EVENT_END_FMT:
672 body.appendFormat("warning: got to end format event");
673 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800674 case EVENT_RESERVED:
675 default:
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700676 body.appendFormat("warning: unknown event %d", event);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800677 break;
678 }
679 i += advance;
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700680
681 if (!body.isEmpty()) {
682 dumpLine(timestamp, body);
683 deferredTimestamp = false;
684 }
685 }
686 if (deferredTimestamp) {
687 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800688 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800689}
690
Nicolas Roulet40a44982017-02-03 13:39:57 -0800691void NBLog::Reader::dump(int fd, size_t indent)
692{
693 // get a snapshot, dump it
694 std::unique_ptr<Snapshot> snap = getSnapshot();
695 dump(fd, indent, *snap);
696}
697
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800698void NBLog::Reader::dumpLine(const String8 &timestamp, String8 &body)
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700699{
700 if (mFd >= 0) {
Elliott Hughes8b5f6422014-05-22 01:22:06 -0700701 dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700702 } else {
703 ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
704 }
705 body.clear();
706}
707
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800708bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
709{
Glenn Kasten481fb672013-09-30 14:39:28 -0700710 return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800711}
712
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800713void NBLog::appendTimestamp(String8 *body, const void *data) {
714 struct timespec ts;
715 memcpy(&ts, data, sizeof(struct timespec));
716 body->appendFormat("[%d.%03d]", (int) ts.tv_sec,
717 (int) (ts.tv_nsec / 1000000));
718}
719
720void NBLog::appendInt(String8 *body, const void *data) {
721 int x = *((int*) data);
722 body->appendFormat("<%d>", x);
723}
724
725void NBLog::appendFloat(String8 *body, const void *data) {
726 float f;
727 memcpy(&f, data, sizeof(float));
728 body->appendFormat("<%f>", f);
729}
730
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800731void NBLog::appendPID(String8 *body, const void* data, size_t length) {
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800732 pid_t id = *((pid_t*) data);
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800733 char * name = &((char*) data)[sizeof(pid_t)];
734 body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800735}
736
Nicolas Roulet40a44982017-02-03 13:39:57 -0800737int NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, String8 *timestamp, String8 *body) {
738 // log timestamp
739 struct timespec ts = fmtEntry.timestamp();
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800740 timestamp->clear();
741 timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
742 (int) (ts.tv_nsec / 1000000));
Nicolas Roulet40a44982017-02-03 13:39:57 -0800743 size_t fullLength = NBLog::Entry::kOverhead + sizeof(ts);
744
745 // log author (if present)
746 fullLength += handleAuthor(fmtEntry, body);
747
748 // log string
749 const uint8_t *args = fmtEntry.args();
750 size_t args_offset = 0;
751
752 const char* fmt = fmtEntry.formatString();
753 size_t fmt_length = fmtEntry.formatStringLength();
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800754
755 for (size_t fmt_offset = 0; fmt_offset < fmt_length; ++fmt_offset) {
756 if (fmt[fmt_offset] != '%') {
757 body->append(&fmt[fmt_offset], 1); // TODO optimize to write consecutive strings at once
758 continue;
759 }
760 if (fmt[++fmt_offset] == '%') {
761 body->append("%");
762 continue;
763 }
764 if (fmt_offset == fmt_length) {
765 continue;
766 }
767
Nicolas Roulet40a44982017-02-03 13:39:57 -0800768 NBLog::Event event = (NBLog::Event) args[args_offset];
769 size_t length = args[args_offset + 1];
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800770
771 // TODO check length for event type is correct
Nicolas Roulet40a44982017-02-03 13:39:57 -0800772 if(length != args[args_offset + length + 2]) {
Nicolas Roulet4da78202017-02-03 12:53:39 -0800773 ALOGW("NBLog Reader received different lengths %zu and %d for event %d", length,
Nicolas Roulet40a44982017-02-03 13:39:57 -0800774 args[args_offset + length + 2], event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800775 body->append("<invalid entry>");
776 ++fmt_offset;
777 continue;
778 }
779
780 // TODO: implement more complex formatting such as %.3f
Nicolas Roulet40a44982017-02-03 13:39:57 -0800781 void * datum = (void*) &args[args_offset + 2]; // pointer to the current event args
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800782 switch(fmt[fmt_offset])
783 {
784 case 's': // string
Nicolas Roulet4da78202017-02-03 12:53:39 -0800785 ALOGW_IF(event != EVENT_STRING,
786 "NBLog Reader incompatible event for string specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800787 body->append((const char*) datum, length);
788 break;
789
790 case 't': // timestamp
Nicolas Roulet4da78202017-02-03 12:53:39 -0800791 ALOGW_IF(event != EVENT_TIMESTAMP,
792 "NBLog Reader incompatible event for timestamp specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800793 appendTimestamp(body, datum);
794 break;
795
796 case 'd': // integer
Nicolas Roulet4da78202017-02-03 12:53:39 -0800797 ALOGW_IF(event != EVENT_INTEGER,
798 "NBLog Reader incompatible event for integer specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800799 appendInt(body, datum);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800800 break;
801
802 case 'f': // float
Nicolas Roulet4da78202017-02-03 12:53:39 -0800803 ALOGW_IF(event != EVENT_FLOAT,
804 "NBLog Reader incompatible event for float specifier: %d", event);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800805 appendFloat(body, datum);
806 break;
807
808 case 'p': // pid
Nicolas Roulet4da78202017-02-03 12:53:39 -0800809 ALOGW_IF(event != EVENT_PID,
810 "NBLog Reader incompatible event for pid specifier: %d", event);
Nicolas Rouletc20cb502017-02-01 12:35:24 -0800811 appendPID(body, datum, length);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800812 break;
813
814 default:
815 ALOGW("NBLog Reader encountered unknown character %c", fmt[fmt_offset]);
816 }
817
Nicolas Roulet40a44982017-02-03 13:39:57 -0800818 args_offset += length + Entry::kOverhead;
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800819
820 }
Nicolas Roulet40a44982017-02-03 13:39:57 -0800821 fullLength += args_offset + Entry::kOverhead;
822 return fullLength;
823}
824
825// ---------------------------------------------------------------------------
826
827NBLog::Merger::Merger(const void *shared, size_t size):
828 mBuffer(NULL),
829 mShared((Shared *) shared),
830 mFifo(mShared != NULL ?
831 new audio_utils_fifo(size, sizeof(uint8_t),
832 mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
833 mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL)
834 {}
835
836void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
837 mNamedReaders.push_back(reader);
838}
839
840// items placed in priority queue during merge
841// composed by a timestamp and the index of the snapshot where the timestamp came from
842struct MergeItem
843{
844 struct timespec ts;
845 int index;
846 MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
847};
848
849// operators needed for priority queue in merge
850bool operator>(const struct timespec &t1, const struct timespec &t2) {
851 return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
852}
853
854bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) {
855 return i1.ts > i2.ts ||
856 (i1.ts.tv_sec == i2.ts.tv_sec && i1.ts.tv_nsec == i2.ts.tv_nsec && i1.index > i2.index);
857}
858
859// Merge registered readers, sorted by timestamp
860void NBLog::Merger::merge() {
861 int nLogs = mNamedReaders.size();
862 std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
863 for (int i = 0; i < nLogs; ++i) {
864 snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
865 }
866 // initialize offsets
867 std::vector<size_t> offsets(nLogs, 0);
868 // TODO custom heap implementation could allow to update top, improving performance
869 // for bursty buffers
870 std::priority_queue<MergeItem, std::vector<MergeItem>, std::greater<MergeItem>> timestamps;
871 for (int i = 0; i < nLogs; ++i)
872 {
873 if (snapshots[i]->available() > 0) {
874 timespec ts = FormatEntry(snapshots[i]->data()).timestamp();
875 MergeItem item(ts, i);
876 timestamps.push(item);
877 }
878 }
879
880 while (!timestamps.empty()) {
881 // find minimum timestamp
882 int index = timestamps.top().index;
883 // copy it to the log
884 size_t length = FormatEntry(snapshots[index]->data() + offsets[index]).copyTo(
885 mFifoWriter, index);
886 // update data structures
887 offsets[index] += length;
888 ALOGW_IF(offsets[index] > snapshots[index]->available(), "Overflown snapshot capacity");
889 timestamps.pop();
890 if (offsets[index] != snapshots[index]->available()) {
891 timespec ts = FormatEntry(snapshots[index]->data() + offsets[index]).timestamp();
892 MergeItem item(ts, index);
893 timestamps.emplace(item);
894 }
895 }
896}
897
898const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
899 return &mNamedReaders;
900}
901
902NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
903 : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}
904
905size_t NBLog::MergeReader::handleAuthor(const NBLog::FormatEntry &fmtEntry, String8 *body) {
906 int author = fmtEntry.author();
907 const char* name = (*mNamedReaders)[author].name();
908 body->appendFormat("%s: ", name);
909 return NBLog::Entry::kOverhead + sizeof(author);
Nicolas Rouletfe1e1442017-01-30 12:02:03 -0800910}
911
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800912} // namespace android