blob: f019df5dd7b940bdafb744cea204a92e2273e2b3 [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>
24#include <time.h>
25#include <new>
Glenn Kasten535e1612016-12-05 12:19:36 -080026#include <audio_utils/roundup.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080027#include <media/nbaio/NBLog.h>
28#include <utils/Log.h>
Glenn Kasten4e01ef62013-07-11 14:29:59 -070029#include <utils/String8.h>
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080030
31namespace android {
32
33int NBLog::Entry::readAt(size_t offset) const
34{
35 // FIXME This is too slow, despite the name it is used during writing
36 if (offset == 0)
37 return mEvent;
38 else if (offset == 1)
39 return mLength;
40 else if (offset < (size_t) (mLength + 2))
41 return ((char *) mData)[offset - 2];
42 else if (offset == (size_t) (mLength + 2))
43 return mLength;
44 else
45 return 0;
46}
47
48// ---------------------------------------------------------------------------
49
50#if 0 // FIXME see note in NBLog.h
51NBLog::Timeline::Timeline(size_t size, void *shared)
52 : mSize(roundup(size)), mOwn(shared == NULL),
53 mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared))
54{
55 new (mShared) Shared;
56}
57
58NBLog::Timeline::~Timeline()
59{
60 mShared->~Shared();
61 if (mOwn) {
62 delete[] (char *) mShared;
63 }
64}
65#endif
66
67/*static*/
68size_t NBLog::Timeline::sharedSize(size_t size)
69{
Glenn Kastened99c2b2016-12-12 08:31:24 -080070 // TODO fifo now supports non-power-of-2 buffer sizes, so could remove the roundup
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080071 return sizeof(Shared) + roundup(size);
72}
73
74// ---------------------------------------------------------------------------
75
76NBLog::Writer::Writer()
Glenn Kasten535e1612016-12-05 12:19:36 -080077 : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080078{
79}
80
Glenn Kasten535e1612016-12-05 12:19:36 -080081NBLog::Writer::Writer(void *shared, size_t size)
82 : mShared((Shared *) shared),
83 mFifo(mShared != NULL ?
84 new audio_utils_fifo(size, sizeof(uint8_t),
85 mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
86 mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL),
87 mEnabled(mFifoWriter != NULL)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080088{
89}
90
Glenn Kasten535e1612016-12-05 12:19:36 -080091NBLog::Writer::Writer(const sp<IMemory>& iMemory, size_t size)
92 : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -080093{
Glenn Kasten535e1612016-12-05 12:19:36 -080094 mIMemory = iMemory;
95}
96
97NBLog::Writer::~Writer()
98{
99 delete mFifoWriter;
100 delete mFifo;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800101}
102
103void NBLog::Writer::log(const char *string)
104{
105 if (!mEnabled) {
106 return;
107 }
108 size_t length = strlen(string);
Glenn Kasten535e1612016-12-05 12:19:36 -0800109 if (length > Entry::kMaxLength) {
110 length = Entry::kMaxLength;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800111 }
112 log(EVENT_STRING, string, length);
113}
114
115void NBLog::Writer::logf(const char *fmt, ...)
116{
117 if (!mEnabled) {
118 return;
119 }
120 va_list ap;
121 va_start(ap, fmt);
122 Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter
123 va_end(ap);
124}
125
126void NBLog::Writer::logvf(const char *fmt, va_list ap)
127{
128 if (!mEnabled) {
129 return;
130 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800131 char buffer[Entry::kMaxLength + 1 /*NUL*/];
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800132 int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
133 if (length >= (int) sizeof(buffer)) {
134 length = sizeof(buffer) - 1;
135 // NUL termination is not required
136 // buffer[length] = '\0';
137 }
138 if (length >= 0) {
139 log(EVENT_STRING, buffer, length);
140 }
141}
142
143void NBLog::Writer::logTimestamp()
144{
145 if (!mEnabled) {
146 return;
147 }
148 struct timespec ts;
149 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
150 log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
151 }
152}
153
154void NBLog::Writer::logTimestamp(const struct timespec& ts)
155{
156 if (!mEnabled) {
157 return;
158 }
159 log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
160}
161
162void NBLog::Writer::log(Event event, const void *data, size_t length)
163{
164 if (!mEnabled) {
165 return;
166 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800167 if (data == NULL || length > Entry::kMaxLength) {
168 // TODO Perhaps it makes sense to display truncated data or at least a
169 // message that the data is too long? The current behavior can create
170 // a confusion for a programmer debugging their code.
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800171 return;
172 }
173 switch (event) {
174 case EVENT_STRING:
175 case EVENT_TIMESTAMP:
176 break;
177 case EVENT_RESERVED:
178 default:
179 return;
180 }
181 Entry entry(event, data, length);
182 log(&entry, true /*trusted*/);
183}
184
185void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
186{
187 if (!mEnabled) {
188 return;
189 }
190 if (!trusted) {
191 log(entry->mEvent, entry->mData, entry->mLength);
192 return;
193 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800194 size_t need = entry->mLength + Entry::kOverhead; // mEvent, mLength, data[length], mLength
195 // need = number of bytes remaining to write
196
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800197 // FIXME optimize this using memcpy for the data part of the Entry.
198 // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
Glenn Kasten535e1612016-12-05 12:19:36 -0800199 uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
200 for (size_t i = 0; i < need; i++) {
201 temp[i] = entry->readAt(i);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800202 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800203 mFifoWriter->write(temp, need);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800204}
205
206bool NBLog::Writer::isEnabled() const
207{
208 return mEnabled;
209}
210
211bool NBLog::Writer::setEnabled(bool enabled)
212{
213 bool old = mEnabled;
214 mEnabled = enabled && mShared != NULL;
215 return old;
216}
217
218// ---------------------------------------------------------------------------
219
220NBLog::LockedWriter::LockedWriter()
221 : Writer()
222{
223}
224
Glenn Kasten535e1612016-12-05 12:19:36 -0800225NBLog::LockedWriter::LockedWriter(void *shared, size_t size)
226 : Writer(shared, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800227{
228}
229
230void NBLog::LockedWriter::log(const char *string)
231{
232 Mutex::Autolock _l(mLock);
233 Writer::log(string);
234}
235
236void NBLog::LockedWriter::logf(const char *fmt, ...)
237{
238 // FIXME should not take the lock until after formatting is done
239 Mutex::Autolock _l(mLock);
240 va_list ap;
241 va_start(ap, fmt);
242 Writer::logvf(fmt, ap);
243 va_end(ap);
244}
245
246void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
247{
248 // FIXME should not take the lock until after formatting is done
249 Mutex::Autolock _l(mLock);
250 Writer::logvf(fmt, ap);
251}
252
253void NBLog::LockedWriter::logTimestamp()
254{
255 // FIXME should not take the lock until after the clock_gettime() syscall
256 Mutex::Autolock _l(mLock);
257 Writer::logTimestamp();
258}
259
260void NBLog::LockedWriter::logTimestamp(const struct timespec& ts)
261{
262 Mutex::Autolock _l(mLock);
263 Writer::logTimestamp(ts);
264}
265
266bool NBLog::LockedWriter::isEnabled() const
267{
268 Mutex::Autolock _l(mLock);
269 return Writer::isEnabled();
270}
271
272bool NBLog::LockedWriter::setEnabled(bool enabled)
273{
274 Mutex::Autolock _l(mLock);
275 return Writer::setEnabled(enabled);
276}
277
278// ---------------------------------------------------------------------------
279
Glenn Kasten535e1612016-12-05 12:19:36 -0800280NBLog::Reader::Reader(const void *shared, size_t size)
281 : mShared((/*const*/ Shared *) shared), /*mIMemory*/
282 mFd(-1), mIndent(0),
283 mFifo(mShared != NULL ?
284 new audio_utils_fifo(size, sizeof(uint8_t),
285 mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
286 mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800287{
288}
289
Glenn Kasten535e1612016-12-05 12:19:36 -0800290NBLog::Reader::Reader(const sp<IMemory>& iMemory, size_t size)
291 : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800292{
Glenn Kasten535e1612016-12-05 12:19:36 -0800293 mIMemory = iMemory;
294}
295
296NBLog::Reader::~Reader()
297{
298 delete mFifoReader;
299 delete mFifo;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800300}
301
302void NBLog::Reader::dump(int fd, size_t indent)
303{
Glenn Kasten535e1612016-12-05 12:19:36 -0800304 if (mFifoReader == NULL) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800305 return;
306 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800307 // make a copy to avoid race condition with writer
Glenn Kasten535e1612016-12-05 12:19:36 -0800308 size_t capacity = mFifo->capacity();
309
310 // TODO Stack-based allocation of large objects may fail.
311 // Currently the log buffers are a page or two, which should be safe.
312 // But if the log buffers ever get a lot larger,
313 // then change this to allocate from heap when necessary.
314 static size_t kReasonableStackObjectSize = 32768;
315 ALOGW_IF(capacity > kReasonableStackObjectSize, "Stack-based allocation of object may fail");
316 uint8_t copy[capacity];
317
318 size_t lost;
319 ssize_t actual = mFifoReader->read(copy, capacity, NULL /*timeout*/, &lost);
320 ALOG_ASSERT(actual <= capacity);
321 size_t avail = actual > 0 ? (size_t) actual : 0;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800322 size_t i = avail;
323 Event event;
324 size_t length;
325 struct timespec ts;
326 time_t maxSec = -1;
Glenn Kasten535e1612016-12-05 12:19:36 -0800327 while (i >= Entry::kOverhead) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800328 length = copy[i - 1];
Glenn Kasten535e1612016-12-05 12:19:36 -0800329 if (length + Entry::kOverhead > i || copy[i - length - 2] != length) {
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800330 break;
331 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800332 event = (Event) copy[i - length - Entry::kOverhead];
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800333 if (event == EVENT_TIMESTAMP) {
334 if (length != sizeof(struct timespec)) {
335 // corrupt
336 break;
337 }
338 memcpy(&ts, &copy[i - length - 1], sizeof(struct timespec));
339 if (ts.tv_sec > maxSec) {
340 maxSec = ts.tv_sec;
341 }
342 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800343 i -= length + Entry::kOverhead;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800344 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700345 mFd = fd;
346 mIndent = indent;
347 String8 timestamp, body;
Glenn Kastenc02c9612013-10-15 09:25:11 -0700348 lost += i;
349 if (lost > 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700350 body.appendFormat("warning: lost %zu bytes worth of events", lost);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700351 // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
352 // log to push it out. Consider keeping the timestamp/body between calls to readAt().
353 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800354 }
355 size_t width = 1;
356 while (maxSec >= 10) {
357 ++width;
358 maxSec /= 10;
359 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800360 if (maxSec >= 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700361 timestamp.appendFormat("[%*s]", (int) width + 4, "");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800362 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700363 bool deferredTimestamp = false;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800364 while (i < avail) {
365 event = (Event) copy[i];
366 length = copy[i + 1];
367 const void *data = &copy[i + 2];
Glenn Kasten535e1612016-12-05 12:19:36 -0800368 size_t advance = length + Entry::kOverhead;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800369 switch (event) {
370 case EVENT_STRING:
Glenn Kasten95d287d2014-04-28 14:11:45 -0700371 body.appendFormat("%.*s", (int) length, (const char *) data);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700372 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800373 case EVENT_TIMESTAMP: {
374 // already checked that length == sizeof(struct timespec);
375 memcpy(&ts, data, sizeof(struct timespec));
376 long prevNsec = ts.tv_nsec;
377 long deltaMin = LONG_MAX;
378 long deltaMax = -1;
379 long deltaTotal = 0;
380 size_t j = i;
381 for (;;) {
Glenn Kasten535e1612016-12-05 12:19:36 -0800382 j += sizeof(struct timespec) + 3 /*Entry::kOverhead?*/;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800383 if (j >= avail || (Event) copy[j] != EVENT_TIMESTAMP) {
384 break;
385 }
386 struct timespec tsNext;
387 memcpy(&tsNext, &copy[j + 2], sizeof(struct timespec));
388 if (tsNext.tv_sec != ts.tv_sec) {
389 break;
390 }
391 long delta = tsNext.tv_nsec - prevNsec;
392 if (delta < 0) {
393 break;
394 }
395 if (delta < deltaMin) {
396 deltaMin = delta;
397 }
398 if (delta > deltaMax) {
399 deltaMax = delta;
400 }
401 deltaTotal += delta;
402 prevNsec = tsNext.tv_nsec;
403 }
Glenn Kasten535e1612016-12-05 12:19:36 -0800404 size_t n = (j - i) / (sizeof(struct timespec) + 3 /*Entry::kOverhead?*/);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700405 if (deferredTimestamp) {
406 dumpLine(timestamp, body);
407 deferredTimestamp = false;
408 }
409 timestamp.clear();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800410 if (n >= kSquashTimestamp) {
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700411 timestamp.appendFormat("[%d.%03d to .%.03d by .%.03d to .%.03d]",
412 (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
413 (int) ((ts.tv_nsec + deltaTotal) / 1000000),
414 (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800415 i = j;
416 advance = 0;
417 break;
418 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700419 timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
420 (int) (ts.tv_nsec / 1000000));
421 deferredTimestamp = true;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800422 } break;
423 case EVENT_RESERVED:
424 default:
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700425 body.appendFormat("warning: unknown event %d", event);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800426 break;
427 }
428 i += advance;
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700429
430 if (!body.isEmpty()) {
431 dumpLine(timestamp, body);
432 deferredTimestamp = false;
433 }
434 }
435 if (deferredTimestamp) {
436 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800437 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800438}
439
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700440void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
441{
442 if (mFd >= 0) {
Elliott Hughes8b5f6422014-05-22 01:22:06 -0700443 dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700444 } else {
445 ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
446 }
447 body.clear();
448}
449
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800450bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
451{
Glenn Kasten481fb672013-09-30 14:39:28 -0700452 return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800453}
454
455} // namespace android