1 /*
2 * Copyright (C) 2017 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 // Non-blocking event logger intended for safe communication between processes via shared memory
18
19 #ifndef ANDROID_MEDIA_NBLOG_H
20 #define ANDROID_MEDIA_NBLOG_H
21
22 #include <deque>
23 #include <map>
24 #include <set>
25 #include <vector>
26
27 #include <audio_utils/fifo.h>
28 #include <binder/IMemory.h>
29 #include <media/nblog/PerformanceAnalysis.h>
30 #include <media/nblog/ReportPerformance.h>
31 #include <utils/Mutex.h>
32 #include <utils/threads.h>
33
34 namespace android {
35
36 class String8;
37
38 class NBLog {
39
40 public:
41
42 using log_hash_t = ReportPerformance::log_hash_t;
43
44 // FIXME Everything needed for client (writer API and registration) should be isolated
45 // from the rest of the implementation.
46 class Writer;
47 class Reader;
48
49 enum Event : uint8_t {
50 EVENT_RESERVED,
51 EVENT_STRING, // ASCII string, not NUL-terminated
52 // TODO: make timestamp optional
53 EVENT_TIMESTAMP, // clock_gettime(CLOCK_MONOTONIC)
54 EVENT_INTEGER, // integer value entry
55 EVENT_FLOAT, // floating point value entry
56 EVENT_PID, // process ID and process name
57 EVENT_AUTHOR, // author index (present in merged logs) tracks entry's
58 // original log
59 EVENT_START_FMT, // logFormat start event: entry includes format string,
60 // following entries contain format arguments
61 EVENT_HASH, // unique HASH of log origin, originates from hash of file name
62 // and line number
63 EVENT_HISTOGRAM_ENTRY_TS, // single datum for timestamp histogram
64 EVENT_AUDIO_STATE, // audio on/off event: logged on FastMixer::onStateChange call
65 EVENT_END_FMT, // end of logFormat argument list
66
67 EVENT_UPPER_BOUND, // to check for invalid events
68 };
69
70 private:
71
72 // ---------------------------------------------------------------------------
73 // API for handling format entry operations
74
75 // a formatted entry has the following structure:
76 // * START_FMT entry, containing the format string
77 // * TIMESTAMP entry
78 // * HASH entry
79 // * author entry of the thread that generated it (optional, present in merged log)
80 // * format arg1
81 // * format arg2
82 // * ...
83 // * END_FMT entry
84
85 // entry representation in memory
86 struct entry {
87 const uint8_t type;
88 const uint8_t length;
89 const uint8_t data[0];
90 };
91
92 // entry tail representation (after data)
93 struct ending {
94 uint8_t length;
95 uint8_t next[0];
96 };
97
98 // entry iterator
99 class EntryIterator {
100 public:
101 EntryIterator();
102 explicit EntryIterator(const uint8_t *entry);
103 EntryIterator(const EntryIterator &other);
104
105 // dereference underlying entry
106 const entry& operator*() const;
107 const entry* operator->() const;
108 // advance to next entry
109 EntryIterator& operator++(); // ++i
110 // back to previous entry
111 EntryIterator& operator--(); // --i
112 EntryIterator next() const;
113 EntryIterator prev() const;
114 bool operator!=(const EntryIterator &other) const;
115 int operator-(const EntryIterator &other) const;
116
117 bool hasConsistentLength() const;
118 void copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const;
119 void copyData(uint8_t *dst) const;
120
121 template<typename T>
payload()122 inline const T& payload() {
123 return *reinterpret_cast<const T *>(ptr + offsetof(entry, data));
124 }
125
126 inline operator const uint8_t*() const {
127 return ptr;
128 }
129
130 private:
131 const uint8_t *ptr;
132 };
133
134 class AbstractEntry {
135 public:
136
137 // Entry starting in the given pointer
138 explicit AbstractEntry(const uint8_t *entry);
~AbstractEntry()139 virtual ~AbstractEntry() {}
140
141 // build concrete entry of appropriate class from pointer
142 static std::unique_ptr<AbstractEntry> buildEntry(const uint8_t *ptr);
143
144 // get format entry timestamp
145 virtual int64_t timestamp() const = 0;
146
147 // get format entry's unique id
148 virtual log_hash_t hash() const = 0;
149
150 // entry's author index (-1 if none present)
151 // a Merger has a vector of Readers, author simply points to the index of the
152 // Reader that originated the entry
153 // TODO consider changing to uint32_t
154 virtual int author() const = 0;
155
156 // copy entry, adding author before timestamp, returns iterator to end of entry
157 virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
158 int author) const = 0;
159
160 protected:
161 // copies ordinary entry from src to dst, and returns length of entry
162 // size_t copyEntry(audio_utils_fifo_writer *dst, const iterator &it);
163 const uint8_t *mEntry;
164 };
165
166 class FormatEntry : public AbstractEntry {
167 public:
168 // explicit FormatEntry(const EntryIterator &it);
FormatEntry(const uint8_t * ptr)169 explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
~FormatEntry()170 virtual ~FormatEntry() {}
171
172 EntryIterator begin() const;
173
174 // Entry's format string
175 const char* formatString() const;
176
177 // Enrty's format string length
178 size_t formatStringLength() const;
179
180 // Format arguments (excluding format string, timestamp and author)
181 EntryIterator args() const;
182
183 // get format entry timestamp
184 virtual int64_t timestamp() const override;
185
186 // get format entry's unique id
187 virtual log_hash_t hash() const override;
188
189 // entry's author index (-1 if none present)
190 // a Merger has a vector of Readers, author simply points to the index of the
191 // Reader that originated the entry
192 virtual int author() const override;
193
194 // copy entry, adding author before timestamp, returns size of original entry
195 virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
196 int author) const override;
197
198 };
199
200 class HistogramEntry : public AbstractEntry {
201 public:
HistogramEntry(const uint8_t * ptr)202 explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
203 }
~HistogramEntry()204 virtual ~HistogramEntry() {}
205
206 virtual int64_t timestamp() const override;
207
208 virtual log_hash_t hash() const override;
209
210 virtual int author() const override;
211
212 virtual EntryIterator copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> &dst,
213 int author) const override;
214
215 };
216
217 // ---------------------------------------------------------------------------
218
219 // representation of a single log entry in private memory
220 struct Entry {
EntryEntry221 Entry(Event event, const void *data, size_t length)
222 : mEvent(event), mLength(length), mData(data) { }
~EntryEntry223 /*virtual*/ ~Entry() { }
224
225 // used during writing to format Entry information as follows:
226 // [type][length][data ... ][length]
227 int copyEntryDataAt(size_t offset) const;
228
229 private:
230 friend class Writer;
231 Event mEvent; // event type
232 uint8_t mLength; // length of additional data, 0 <= mLength <= kMaxLength
233 const void *mData; // event type-specific data
234 static const size_t kMaxLength = 255;
235 public:
236 // mEvent, mLength, mData[...], duplicate mLength
237 static const size_t kOverhead = sizeof(entry) + sizeof(ending);
238 // endind length of previous entry
239 static const ssize_t kPreviousLengthOffset = - sizeof(ending) +
240 offsetof(ending, length);
241 };
242
243 struct HistTsEntry {
244 log_hash_t hash;
245 int64_t ts;
246 }; //TODO __attribute__((packed));
247
248 struct HistTsEntryWithAuthor {
249 log_hash_t hash;
250 int64_t ts;
251 int author;
252 }; //TODO __attribute__((packed));
253
254 struct HistIntEntry {
255 log_hash_t hash;
256 int value;
257 }; //TODO __attribute__((packed));
258
259 // representation of a single log entry in shared memory
260 // byte[0] mEvent
261 // byte[1] mLength
262 // byte[2] mData[0]
263 // ...
264 // byte[2+i] mData[i]
265 // ...
266 // byte[2+mLength-1] mData[mLength-1]
267 // byte[2+mLength] duplicate copy of mLength to permit reverse scan
268 // byte[3+mLength] start of next log entry
269
270 static void appendInt(String8 *body, const void *data);
271 static void appendFloat(String8 *body, const void *data);
272 static void appendPID(String8 *body, const void *data, size_t length);
273 static void appendTimestamp(String8 *body, const void *data);
274 static size_t fmtEntryLength(const uint8_t *data);
275 static String8 bufferDump(const uint8_t *buffer, size_t size);
276 static String8 bufferDump(const EntryIterator &it);
277 public:
278
279 // Located in shared memory, must be POD.
280 // Exactly one process must explicitly call the constructor or use placement new.
281 // Since this is a POD, the destructor is empty and unnecessary to call it explicitly.
282 struct Shared {
SharedShared283 Shared() /* mRear initialized via default constructor */ { }
~SharedShared284 /*virtual*/ ~Shared() { }
285
286 audio_utils_fifo_index mRear; // index one byte past the end of most recent Entry
287 char mBuffer[0]; // circular buffer for entries
288 };
289
290 public:
291
292 // ---------------------------------------------------------------------------
293
294 // FIXME Timeline was intended to wrap Writer and Reader, but isn't actually used yet.
295 // For now it is just a namespace for sharedSize().
296 class Timeline : public RefBase {
297 public:
298 #if 0
299 Timeline(size_t size, void *shared = NULL);
300 virtual ~Timeline();
301 #endif
302
303 // Input parameter 'size' is the desired size of the timeline in byte units.
304 // Returns the size rounded up to a power-of-2, plus the constant size overhead for indices.
305 static size_t sharedSize(size_t size);
306
307 #if 0
308 private:
309 friend class Writer;
310 friend class Reader;
311
312 const size_t mSize; // circular buffer size in bytes, must be a power of 2
313 bool mOwn; // whether I own the memory at mShared
314 Shared* const mShared; // pointer to shared memory
315 #endif
316 };
317
318 // ---------------------------------------------------------------------------
319
320 // Writer is thread-safe with respect to Reader, but not with respect to multiple threads
321 // calling Writer methods. If you need multi-thread safety for writing, use LockedWriter.
322 class Writer : public RefBase {
323 public:
324 Writer(); // dummy nop implementation without shared memory
325
326 // Input parameter 'size' is the desired size of the timeline in byte units.
327 // The size of the shared memory must be at least Timeline::sharedSize(size).
328 Writer(void *shared, size_t size);
329 Writer(const sp<IMemory>& iMemory, size_t size);
330
331 virtual ~Writer();
332
333 // FIXME needs comments, and some should be private
334 virtual void log(const char *string);
335 virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
336 virtual void logvf(const char *fmt, va_list ap);
337 virtual void logTimestamp();
338 virtual void logTimestamp(const int64_t ts);
339 virtual void logInteger(const int x);
340 virtual void logFloat(const float x);
341 virtual void logPID();
342 virtual void logFormat(const char *fmt, log_hash_t hash, ...);
343 virtual void logVFormat(const char *fmt, log_hash_t hash, va_list ap);
344 virtual void logStart(const char *fmt);
345 virtual void logEnd();
346 virtual void logHash(log_hash_t hash);
347 virtual void logEventHistTs(Event event, log_hash_t hash);
348
349 virtual bool isEnabled() const;
350
351 // return value for all of these is the previous isEnabled()
352 virtual bool setEnabled(bool enabled); // but won't enable if no shared memory
enable()353 bool enable() { return setEnabled(true); }
disable()354 bool disable() { return setEnabled(false); }
355
getIMemory()356 sp<IMemory> getIMemory() const { return mIMemory; }
357
358 private:
359 // 0 <= length <= kMaxLength
360 // writes a single Entry to the FIFO
361 void log(Event event, const void *data, size_t length);
362 // checks validity of an event before calling log above this one
363 void log(const Entry *entry, bool trusted = false);
364
365 Shared* const mShared; // raw pointer to shared memory
366 sp<IMemory> mIMemory; // ref-counted version, initialized in constructor
367 // and then const
368 audio_utils_fifo * const mFifo; // FIFO itself, non-NULL
369 // unless constructor fails
370 audio_utils_fifo_writer * const mFifoWriter; // used to write to FIFO, non-NULL
371 // unless dummy constructor used
372 bool mEnabled; // whether to actually log
373
374 // cached pid and process name to use in %p format specifier
375 // total tag length is mPidTagSize and process name is not zero terminated
376 char *mPidTag;
377 size_t mPidTagSize;
378 };
379
380 // ---------------------------------------------------------------------------
381
382 // Similar to Writer, but safe for multiple threads to call concurrently
383 class LockedWriter : public Writer {
384 public:
385 LockedWriter();
386 LockedWriter(void *shared, size_t size);
387
388 virtual void log(const char *string);
389 virtual void logf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
390 virtual void logvf(const char *fmt, va_list ap);
391 virtual void logTimestamp();
392 virtual void logTimestamp(const int64_t ts);
393 virtual void logInteger(const int x);
394 virtual void logFloat(const float x);
395 virtual void logPID();
396 virtual void logStart(const char *fmt);
397 virtual void logEnd();
398 virtual void logHash(log_hash_t hash);
399
400 virtual bool isEnabled() const;
401 virtual bool setEnabled(bool enabled);
402
403 private:
404 mutable Mutex mLock;
405 };
406
407 // ---------------------------------------------------------------------------
408
409 class Reader : public RefBase {
410 public:
411 // A snapshot of a readers buffer
412 // This is raw data. No analysis has been done on it
413 class Snapshot {
414 public:
Snapshot()415 Snapshot() : mData(NULL), mLost(0) {}
416
Snapshot(size_t bufferSize)417 Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
418
~Snapshot()419 ~Snapshot() { delete[] mData; }
420
421 // copy of the buffer
data()422 uint8_t *data() const { return mData; }
423
424 // amount of data lost (given by audio_utils_fifo_reader)
lost()425 size_t lost() const { return mLost; }
426
427 // iterator to beginning of readable segment of snapshot
428 // data between begin and end has valid entries
begin()429 EntryIterator begin() { return mBegin; }
430
431 // iterator to end of readable segment of snapshot
end()432 EntryIterator end() { return mEnd; }
433
434 private:
435 friend class MergeReader;
436 friend class Reader;
437 uint8_t *mData;
438 size_t mLost;
439 EntryIterator mBegin;
440 EntryIterator mEnd;
441 };
442
443 // Input parameter 'size' is the desired size of the timeline in byte units.
444 // The size of the shared memory must be at least Timeline::sharedSize(size).
445 Reader(const void *shared, size_t size);
446 Reader(const sp<IMemory>& iMemory, size_t size);
447
448 virtual ~Reader();
449
450 // get snapshot of readers fifo buffer, effectively consuming the buffer
451 std::unique_ptr<Snapshot> getSnapshot();
452
453 bool isIMemory(const sp<IMemory>& iMemory) const;
454
455 protected:
456 // print a summary of the performance to the console
457 void dumpLine(const String8& timestamp, String8& body);
458 EntryIterator handleFormat(const FormatEntry &fmtEntry,
459 String8 *timestamp,
460 String8 *body);
461 int mFd; // file descriptor
462 int mIndent; // indentation level
463 int mLost; // bytes of data lost before buffer was read
464
465 private:
466 static const std::set<Event> startingTypes;
467 static const std::set<Event> endingTypes;
468
469 // declared as const because audio_utils_fifo() constructor
470 sp<IMemory> mIMemory; // ref-counted version, assigned only in constructor
471
472 /*const*/ Shared* const mShared; // raw pointer to shared memory, actually const but not
473 audio_utils_fifo * const mFifo; // FIFO itself,
474 // non-NULL unless constructor fails
475 audio_utils_fifo_reader * const mFifoReader; // used to read from FIFO,
476 // non-NULL unless constructor fails
477
478 // Searches for the last entry of type <type> in the range [front, back)
479 // back has to be entry-aligned. Returns nullptr if none enconuntered.
480 static const uint8_t *findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
481 const std::set<Event> &types);
482
483 // dummy method for handling absent author entry
handleAuthor(const AbstractEntry &,String8 *)484 virtual void handleAuthor(const AbstractEntry& /*fmtEntry*/, String8* /*body*/) {}
485 };
486
487 // Wrapper for a reader with a name. Contains a pointer to the reader and a pointer to the name
488 class NamedReader {
489 public:
NamedReader()490 NamedReader() { mName[0] = '\0'; } // for Vector
NamedReader(const sp<NBLog::Reader> & reader,const char * name)491 NamedReader(const sp<NBLog::Reader>& reader, const char *name) :
492 mReader(reader)
493 { strlcpy(mName, name, sizeof(mName)); }
~NamedReader()494 ~NamedReader() { }
reader()495 const sp<NBLog::Reader>& reader() const { return mReader; }
name()496 const char* name() const { return mName; }
497
498 private:
499 sp<NBLog::Reader> mReader;
500 static const size_t kMaxName = 32;
501 char mName[kMaxName];
502 };
503
504 // ---------------------------------------------------------------------------
505
506 // This class is used to read data from each thread's individual FIFO in shared memory
507 // and write it to a single FIFO in local memory.
508 class Merger : public RefBase {
509 public:
510 Merger(const void *shared, size_t size);
511
~Merger()512 virtual ~Merger() {}
513
514 void addReader(const NamedReader &reader);
515 // TODO add removeReader
516 void merge();
517
518 // FIXME This is returning a reference to a shared variable that needs a lock
519 const std::vector<NamedReader>& getNamedReaders() const;
520
521 private:
522 // vector of the readers the merger is supposed to merge from.
523 // every reader reads from a writer's buffer
524 // FIXME Needs to be protected by a lock
525 std::vector<NamedReader> mNamedReaders;
526
527 Shared * const mShared; // raw pointer to shared memory
528 std::unique_ptr<audio_utils_fifo> mFifo; // FIFO itself
529 std::unique_ptr<audio_utils_fifo_writer> mFifoWriter; // used to write to FIFO
530 };
531
532 // This class has a pointer to the FIFO in local memory which stores the merged
533 // data collected by NBLog::Merger from all NamedReaders. It is used to process
534 // this data and write the result to PerformanceAnalysis.
535 class MergeReader : public Reader {
536 public:
537 MergeReader(const void *shared, size_t size, Merger &merger);
538
539 void dump(int fd, int indent = 0);
540 // process a particular snapshot of the reader
541 void getAndProcessSnapshot(Snapshot & snap);
542 // call getSnapshot of the content of the reader's buffer and process the data
543 void getAndProcessSnapshot();
544
545 private:
546 // FIXME Needs to be protected by a lock,
547 // because even though our use of it is read-only there may be asynchronous updates
548 const std::vector<NamedReader>& mNamedReaders;
549
550 // analyzes, compresses and stores the merged data
551 // contains a separate instance for every author (thread), and for every source file
552 // location within each author
553 ReportPerformance::PerformanceAnalysisMap mThreadPerformanceAnalysis;
554
555 // handle author entry by looking up the author's name and appending it to the body
556 // returns number of bytes read from fmtEntry
557 void handleAuthor(const AbstractEntry &fmtEntry, String8 *body);
558 };
559
560 // MergeThread is a thread that contains a Merger. It works as a retriggerable one-shot:
561 // when triggered, it awakes for a lapse of time, during which it periodically merges; if
562 // retriggered, the timeout is reset.
563 // The thread is triggered on AudioFlinger binder activity.
564 class MergeThread : public Thread {
565 public:
566 MergeThread(Merger &merger, MergeReader &mergeReader);
567 virtual ~MergeThread() override;
568
569 // Reset timeout and activate thread to merge periodically if it's idle
570 void wakeup();
571
572 // Set timeout period until the merging thread goes idle again
573 void setTimeoutUs(int time);
574
575 private:
576 virtual bool threadLoop() override;
577
578 // the merger who actually does the work of merging the logs
579 Merger& mMerger;
580
581 // the mergereader used to process data merged by mMerger
582 MergeReader& mMergeReader;
583
584 // mutex for the condition variable
585 Mutex mMutex;
586
587 // condition variable to activate merging on timeout >= 0
588 Condition mCond;
589
590 // time left until the thread blocks again (in microseconds)
591 int mTimeoutUs;
592
593 // merging period when the thread is awake
594 static const int kThreadSleepPeriodUs = 1000000 /*1s*/;
595
596 // initial timeout value when triggered
597 static const int kThreadWakeupPeriodUs = 3000000 /*3s*/;
598 };
599
600 }; // class NBLog
601
602 // TODO put somewhere else
get_monotonic_ns()603 static inline int64_t get_monotonic_ns() {
604 timespec ts;
605 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
606 return (uint64_t) ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
607 }
608 return 0; // should not happen.
609 }
610
611 } // namespace android
612
613 #endif // ANDROID_MEDIA_NBLOG_H
614