blob: c728e3e025ab02464c87b5135f7e673b8944cb91 [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>
26#include <cutils/atomic.h>
27#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()
77 : mSize(0), mShared(NULL), mRear(0), mEnabled(false)
78{
79}
80
81NBLog::Writer::Writer(size_t size, void *shared)
82 : mSize(roundup(size)), mShared((Shared *) shared), mRear(0), mEnabled(mShared != NULL)
83{
84}
85
86NBLog::Writer::Writer(size_t size, const sp<IMemory>& iMemory)
87 : mSize(roundup(size)), mShared(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL),
88 mIMemory(iMemory), mRear(0), mEnabled(mShared != NULL)
89{
90}
91
92void NBLog::Writer::log(const char *string)
93{
94 if (!mEnabled) {
95 return;
96 }
97 size_t length = strlen(string);
98 if (length > 255) {
99 length = 255;
100 }
101 log(EVENT_STRING, string, length);
102}
103
104void NBLog::Writer::logf(const char *fmt, ...)
105{
106 if (!mEnabled) {
107 return;
108 }
109 va_list ap;
110 va_start(ap, fmt);
111 Writer::logvf(fmt, ap); // the Writer:: is needed to avoid virtual dispatch for LockedWriter
112 va_end(ap);
113}
114
115void NBLog::Writer::logvf(const char *fmt, va_list ap)
116{
117 if (!mEnabled) {
118 return;
119 }
120 char buffer[256];
121 int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
122 if (length >= (int) sizeof(buffer)) {
123 length = sizeof(buffer) - 1;
124 // NUL termination is not required
125 // buffer[length] = '\0';
126 }
127 if (length >= 0) {
128 log(EVENT_STRING, buffer, length);
129 }
130}
131
132void NBLog::Writer::logTimestamp()
133{
134 if (!mEnabled) {
135 return;
136 }
137 struct timespec ts;
138 if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
139 log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
140 }
141}
142
143void NBLog::Writer::logTimestamp(const struct timespec& ts)
144{
145 if (!mEnabled) {
146 return;
147 }
148 log(EVENT_TIMESTAMP, &ts, sizeof(struct timespec));
149}
150
151void NBLog::Writer::log(Event event, const void *data, size_t length)
152{
153 if (!mEnabled) {
154 return;
155 }
156 if (data == NULL || length > 255) {
157 return;
158 }
159 switch (event) {
160 case EVENT_STRING:
161 case EVENT_TIMESTAMP:
162 break;
163 case EVENT_RESERVED:
164 default:
165 return;
166 }
167 Entry entry(event, data, length);
168 log(&entry, true /*trusted*/);
169}
170
171void NBLog::Writer::log(const NBLog::Entry *entry, bool trusted)
172{
173 if (!mEnabled) {
174 return;
175 }
176 if (!trusted) {
177 log(entry->mEvent, entry->mData, entry->mLength);
178 return;
179 }
180 size_t rear = mRear & (mSize - 1);
181 size_t written = mSize - rear; // written = number of bytes that have been written so far
182 size_t need = entry->mLength + 3; // mEvent, mLength, data[length], mLength
183 // need = number of bytes remaining to write
184 if (written > need) {
185 written = need;
186 }
187 size_t i;
188 // FIXME optimize this using memcpy for the data part of the Entry.
189 // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
190 for (i = 0; i < written; ++i) {
191 mShared->mBuffer[rear + i] = entry->readAt(i);
192 }
193 if (rear + written == mSize && (need -= written) > 0) {
194 for (i = 0; i < need; ++i) {
195 mShared->mBuffer[i] = entry->readAt(written + i);
196 }
197 written += need;
198 }
199 android_atomic_release_store(mRear += written, &mShared->mRear);
200}
201
202bool NBLog::Writer::isEnabled() const
203{
204 return mEnabled;
205}
206
207bool NBLog::Writer::setEnabled(bool enabled)
208{
209 bool old = mEnabled;
210 mEnabled = enabled && mShared != NULL;
211 return old;
212}
213
214// ---------------------------------------------------------------------------
215
216NBLog::LockedWriter::LockedWriter()
217 : Writer()
218{
219}
220
221NBLog::LockedWriter::LockedWriter(size_t size, void *shared)
222 : Writer(size, shared)
223{
224}
225
226void NBLog::LockedWriter::log(const char *string)
227{
228 Mutex::Autolock _l(mLock);
229 Writer::log(string);
230}
231
232void NBLog::LockedWriter::logf(const char *fmt, ...)
233{
234 // FIXME should not take the lock until after formatting is done
235 Mutex::Autolock _l(mLock);
236 va_list ap;
237 va_start(ap, fmt);
238 Writer::logvf(fmt, ap);
239 va_end(ap);
240}
241
242void NBLog::LockedWriter::logvf(const char *fmt, va_list ap)
243{
244 // FIXME should not take the lock until after formatting is done
245 Mutex::Autolock _l(mLock);
246 Writer::logvf(fmt, ap);
247}
248
249void NBLog::LockedWriter::logTimestamp()
250{
251 // FIXME should not take the lock until after the clock_gettime() syscall
252 Mutex::Autolock _l(mLock);
253 Writer::logTimestamp();
254}
255
256void NBLog::LockedWriter::logTimestamp(const struct timespec& ts)
257{
258 Mutex::Autolock _l(mLock);
259 Writer::logTimestamp(ts);
260}
261
262bool NBLog::LockedWriter::isEnabled() const
263{
264 Mutex::Autolock _l(mLock);
265 return Writer::isEnabled();
266}
267
268bool NBLog::LockedWriter::setEnabled(bool enabled)
269{
270 Mutex::Autolock _l(mLock);
271 return Writer::setEnabled(enabled);
272}
273
274// ---------------------------------------------------------------------------
275
276NBLog::Reader::Reader(size_t size, const void *shared)
277 : mSize(roundup(size)), mShared((const Shared *) shared), mFront(0)
278{
279}
280
281NBLog::Reader::Reader(size_t size, const sp<IMemory>& iMemory)
282 : mSize(roundup(size)), mShared(iMemory != 0 ? (const Shared *) iMemory->pointer() : NULL),
283 mIMemory(iMemory), mFront(0)
284{
285}
286
287void NBLog::Reader::dump(int fd, size_t indent)
288{
289 int32_t rear = android_atomic_acquire_load(&mShared->mRear);
290 size_t avail = rear - mFront;
291 if (avail == 0) {
292 return;
293 }
294 size_t lost = 0;
295 if (avail > mSize) {
296 lost = avail - mSize;
297 mFront += lost;
298 avail = mSize;
299 }
300 size_t remaining = avail; // remaining = number of bytes left to read
301 size_t front = mFront & (mSize - 1);
302 size_t read = mSize - front; // read = number of bytes that have been read so far
303 if (read > remaining) {
304 read = remaining;
305 }
306 // make a copy to avoid race condition with writer
307 uint8_t *copy = new uint8_t[avail];
308 // copy first part of circular buffer up until the wraparound point
309 memcpy(copy, &mShared->mBuffer[front], read);
310 if (front + read == mSize) {
311 if ((remaining -= read) > 0) {
312 // copy second part of circular buffer starting at beginning
313 memcpy(&copy[read], mShared->mBuffer, remaining);
314 read += remaining;
315 // remaining = 0 but not necessary
316 }
317 }
318 mFront += read;
319 size_t i = avail;
320 Event event;
321 size_t length;
322 struct timespec ts;
323 time_t maxSec = -1;
324 while (i >= 3) {
325 length = copy[i - 1];
326 if (length + 3 > i || copy[i - length - 2] != length) {
327 break;
328 }
329 event = (Event) copy[i - length - 3];
330 if (event == EVENT_TIMESTAMP) {
331 if (length != sizeof(struct timespec)) {
332 // corrupt
333 break;
334 }
335 memcpy(&ts, &copy[i - length - 1], sizeof(struct timespec));
336 if (ts.tv_sec > maxSec) {
337 maxSec = ts.tv_sec;
338 }
339 }
340 i -= length + 3;
341 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700342 mFd = fd;
343 mIndent = indent;
344 String8 timestamp, body;
Glenn Kastenc02c9612013-10-15 09:25:11 -0700345 lost += i;
346 if (lost > 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700347 body.appendFormat("warning: lost %zu bytes worth of events", lost);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700348 // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
349 // log to push it out. Consider keeping the timestamp/body between calls to readAt().
350 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800351 }
352 size_t width = 1;
353 while (maxSec >= 10) {
354 ++width;
355 maxSec /= 10;
356 }
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800357 if (maxSec >= 0) {
Glenn Kasten95d287d2014-04-28 14:11:45 -0700358 timestamp.appendFormat("[%*s]", (int) width + 4, "");
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800359 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700360 bool deferredTimestamp = false;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800361 while (i < avail) {
362 event = (Event) copy[i];
363 length = copy[i + 1];
364 const void *data = &copy[i + 2];
365 size_t advance = length + 3;
366 switch (event) {
367 case EVENT_STRING:
Glenn Kasten95d287d2014-04-28 14:11:45 -0700368 body.appendFormat("%.*s", (int) length, (const char *) data);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700369 break;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800370 case EVENT_TIMESTAMP: {
371 // already checked that length == sizeof(struct timespec);
372 memcpy(&ts, data, sizeof(struct timespec));
373 long prevNsec = ts.tv_nsec;
374 long deltaMin = LONG_MAX;
375 long deltaMax = -1;
376 long deltaTotal = 0;
377 size_t j = i;
378 for (;;) {
379 j += sizeof(struct timespec) + 3;
380 if (j >= avail || (Event) copy[j] != EVENT_TIMESTAMP) {
381 break;
382 }
383 struct timespec tsNext;
384 memcpy(&tsNext, &copy[j + 2], sizeof(struct timespec));
385 if (tsNext.tv_sec != ts.tv_sec) {
386 break;
387 }
388 long delta = tsNext.tv_nsec - prevNsec;
389 if (delta < 0) {
390 break;
391 }
392 if (delta < deltaMin) {
393 deltaMin = delta;
394 }
395 if (delta > deltaMax) {
396 deltaMax = delta;
397 }
398 deltaTotal += delta;
399 prevNsec = tsNext.tv_nsec;
400 }
401 size_t n = (j - i) / (sizeof(struct timespec) + 3);
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700402 if (deferredTimestamp) {
403 dumpLine(timestamp, body);
404 deferredTimestamp = false;
405 }
406 timestamp.clear();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800407 if (n >= kSquashTimestamp) {
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700408 timestamp.appendFormat("[%d.%03d to .%.03d by .%.03d to .%.03d]",
409 (int) ts.tv_sec, (int) (ts.tv_nsec / 1000000),
410 (int) ((ts.tv_nsec + deltaTotal) / 1000000),
411 (int) (deltaMin / 1000000), (int) (deltaMax / 1000000));
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800412 i = j;
413 advance = 0;
414 break;
415 }
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700416 timestamp.appendFormat("[%d.%03d]", (int) ts.tv_sec,
417 (int) (ts.tv_nsec / 1000000));
418 deferredTimestamp = true;
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800419 } break;
420 case EVENT_RESERVED:
421 default:
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700422 body.appendFormat("warning: unknown event %d", event);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800423 break;
424 }
425 i += advance;
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700426
427 if (!body.isEmpty()) {
428 dumpLine(timestamp, body);
429 deferredTimestamp = false;
430 }
431 }
432 if (deferredTimestamp) {
433 dumpLine(timestamp, body);
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800434 }
435 // FIXME it would be more efficient to put a char mCopy[256] as a member variable of the dumper
436 delete[] copy;
437}
438
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700439void NBLog::Reader::dumpLine(const String8& timestamp, String8& body)
440{
441 if (mFd >= 0) {
Elliott Hughes8b5f6422014-05-22 01:22:06 -0700442 dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string());
Glenn Kasten4e01ef62013-07-11 14:29:59 -0700443 } else {
444 ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string());
445 }
446 body.clear();
447}
448
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800449bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
450{
Glenn Kasten481fb672013-09-30 14:39:28 -0700451 return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer();
Glenn Kasten11d8dfc2013-01-14 14:53:13 -0800452}
453
454} // namespace android