• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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