Home | History | Annotate | Download | only in nblog
      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>
    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);
    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);
    169         explicit FormatEntry(const uint8_t *ptr) : AbstractEntry(ptr) {}
    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:
    202         explicit HistogramEntry(const uint8_t *ptr) : AbstractEntry(ptr) {
    203         }
    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 {
    221         Entry(Event event, const void *data, size_t length)
    222             : mEvent(event), mLength(length), mData(data) { }
    223         /*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 {
    283         Shared() /* mRear initialized via default constructor */ { }
    284         /*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
    353         bool    enable()    { return setEnabled(true); }
    354         bool    disable()   { return setEnabled(false); }
    355 
    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:
    415             Snapshot() : mData(NULL), mLost(0) {}
    416 
    417             Snapshot(size_t bufferSize) : mData(new uint8_t[bufferSize]) {}
    418 
    419             ~Snapshot() { delete[] mData; }
    420 
    421             // copy of the buffer
    422             uint8_t *data() const { return mData; }
    423 
    424             // amount of data lost (given by audio_utils_fifo_reader)
    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
    429             EntryIterator begin() { return mBegin; }
    430 
    431             // iterator to end of readable segment of snapshot
    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
    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:
    490         NamedReader() { mName[0] = '\0'; } // for Vector
    491         NamedReader(const sp<NBLog::Reader>& reader, const char *name) :
    492             mReader(reader)
    493             { strlcpy(mName, name, sizeof(mName)); }
    494         ~NamedReader() { }
    495         const sp<NBLog::Reader>&  reader() const { return mReader; }
    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 
    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
    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