Home | History | Annotate | Download | only in libnblog
      1 /*
      2  * Copyright (C) 2018 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 <stddef.h>
     22 #include <sys/prctl.h>
     23 
     24 #include <audio_utils/fifo.h>
     25 #include <binder/IMemory.h>
     26 #include <media/nblog/Entry.h>
     27 #include <media/nblog/Events.h>
     28 #include <media/nblog/Timeline.h>
     29 #include <media/nblog/Writer.h>
     30 #include <utils/Log.h>
     31 #include <utils/Mutex.h>
     32 
     33 namespace android {
     34 namespace NBLog {
     35 
     36 Writer::Writer(void *shared, size_t size)
     37     : mShared((Shared *) shared),
     38       mFifo(mShared != NULL ?
     39         new audio_utils_fifo(size, sizeof(uint8_t),
     40             mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL),
     41       mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL),
     42       mEnabled(mFifoWriter != NULL)
     43 {
     44     // caching pid and process name
     45     pid_t id = ::getpid();
     46     char procName[16];
     47     int status = prctl(PR_GET_NAME, procName);
     48     if (status) {  // error getting process name
     49         procName[0] = '\0';
     50     }
     51     size_t length = strlen(procName);
     52     mPidTagSize = length + sizeof(pid_t);
     53     mPidTag = new char[mPidTagSize];
     54     memcpy(mPidTag, &id, sizeof(pid_t));
     55     memcpy(mPidTag + sizeof(pid_t), procName, length);
     56 }
     57 
     58 Writer::Writer(const sp<IMemory>& iMemory, size_t size)
     59     : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size)
     60 {
     61     mIMemory = iMemory;
     62 }
     63 
     64 Writer::~Writer()
     65 {
     66     delete mFifoWriter;
     67     delete mFifo;
     68     delete[] mPidTag;
     69 }
     70 
     71 void Writer::log(const char *string)
     72 {
     73     if (!mEnabled) {
     74         return;
     75     }
     76     LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string");
     77     size_t length = strlen(string);
     78     if (length > Entry::kMaxLength) {
     79         length = Entry::kMaxLength;
     80     }
     81     log(EVENT_STRING, string, length);
     82 }
     83 
     84 void Writer::logf(const char *fmt, ...)
     85 {
     86     if (!mEnabled) {
     87         return;
     88     }
     89     va_list ap;
     90     va_start(ap, fmt);
     91     Writer::logvf(fmt, ap);     // the Writer:: is needed to avoid virtual dispatch for LockedWriter
     92     va_end(ap);
     93 }
     94 
     95 void Writer::logTimestamp()
     96 {
     97     if (!mEnabled) {
     98         return;
     99     }
    100     struct timespec ts;
    101     if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
    102         log(EVENT_TIMESTAMP, &ts, sizeof(ts));
    103     }
    104 }
    105 
    106 void Writer::logFormat(const char *fmt, log_hash_t hash, ...)
    107 {
    108     if (!mEnabled) {
    109         return;
    110     }
    111     va_list ap;
    112     va_start(ap, hash);
    113     Writer::logVFormat(fmt, hash, ap);
    114     va_end(ap);
    115 }
    116 
    117 void Writer::logEventHistTs(Event event, log_hash_t hash)
    118 {
    119     if (!mEnabled) {
    120         return;
    121     }
    122     HistTsEntry data;
    123     data.hash = hash;
    124     data.ts = systemTime();
    125     if (data.ts > 0) {
    126         log(event, &data, sizeof(data));
    127     } else {
    128         ALOGE("Failed to get timestamp");
    129     }
    130 }
    131 
    132 bool Writer::isEnabled() const
    133 {
    134     return mEnabled;
    135 }
    136 
    137 bool Writer::setEnabled(bool enabled)
    138 {
    139     bool old = mEnabled;
    140     mEnabled = enabled && mShared != NULL;
    141     return old;
    142 }
    143 
    144 void Writer::log(const Entry &etr, bool trusted)
    145 {
    146     if (!mEnabled) {
    147         return;
    148     }
    149     if (!trusted) {
    150         log(etr.mEvent, etr.mData, etr.mLength);
    151         return;
    152     }
    153     const size_t need = etr.mLength + Entry::kOverhead; // mEvent, mLength, data[mLength], mLength
    154                                                         // need = number of bytes written to FIFO
    155 
    156     // FIXME optimize this using memcpy for the data part of the Entry.
    157     // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy.
    158     // checks size of a single log Entry: type, length, data pointer and ending
    159     uint8_t temp[Entry::kMaxLength + Entry::kOverhead];
    160     // write this data to temp array
    161     for (size_t i = 0; i < need; i++) {
    162         temp[i] = etr.copyEntryDataAt(i);
    163     }
    164     // write to circular buffer
    165     mFifoWriter->write(temp, need);
    166 }
    167 
    168 void Writer::log(Event event, const void *data, size_t length)
    169 {
    170     if (!mEnabled) {
    171         return;
    172     }
    173     if (data == NULL || length > Entry::kMaxLength) {
    174         // TODO Perhaps it makes sense to display truncated data or at least a
    175         //      message that the data is too long?  The current behavior can create
    176         //      a confusion for a programmer debugging their code.
    177         return;
    178     }
    179     // Ignore if invalid event
    180     if (event == EVENT_RESERVED || event >= EVENT_UPPER_BOUND) {
    181         return;
    182     }
    183     Entry etr(event, data, length);
    184     log(etr, true /*trusted*/);
    185 }
    186 
    187 void Writer::logvf(const char *fmt, va_list ap)
    188 {
    189     if (!mEnabled) {
    190         return;
    191     }
    192     char buffer[Entry::kMaxLength + 1 /*NUL*/];
    193     int length = vsnprintf(buffer, sizeof(buffer), fmt, ap);
    194     if (length >= (int) sizeof(buffer)) {
    195         length = sizeof(buffer) - 1;
    196         // NUL termination is not required
    197         // buffer[length] = '\0';
    198     }
    199     if (length >= 0) {
    200         log(EVENT_STRING, buffer, length);
    201     }
    202 }
    203 
    204 void Writer::logStart(const char *fmt)
    205 {
    206     if (!mEnabled) {
    207         return;
    208     }
    209     size_t length = strlen(fmt);
    210     if (length > Entry::kMaxLength) {
    211         length = Entry::kMaxLength;
    212     }
    213     log(EVENT_FMT_START, fmt, length);
    214 }
    215 
    216 void Writer::logTimestampFormat()
    217 {
    218     if (!mEnabled) {
    219         return;
    220     }
    221     const nsecs_t ts = systemTime();
    222     if (ts > 0) {
    223         log(EVENT_FMT_TIMESTAMP, &ts, sizeof(ts));
    224     } else {
    225         ALOGE("Failed to get timestamp");
    226     }
    227 }
    228 
    229 void Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp)
    230 {
    231     if (!mEnabled) {
    232         return;
    233     }
    234     Writer::logStart(fmt);
    235     int i;
    236     double d;
    237     float f;
    238     char* s;
    239     size_t length;
    240     int64_t t;
    241     Writer::logTimestampFormat();
    242     log(EVENT_FMT_HASH, &hash, sizeof(hash));
    243     for (const char *p = fmt; *p != '\0'; p++) {
    244         // TODO: implement more complex formatting such as %.3f
    245         if (*p != '%') {
    246             continue;
    247         }
    248         switch(*++p) {
    249         case 's': // string
    250             s = va_arg(argp, char *);
    251             length = strlen(s);
    252             if (length > Entry::kMaxLength) {
    253                 length = Entry::kMaxLength;
    254             }
    255             log(EVENT_FMT_STRING, s, length);
    256             break;
    257 
    258         case 't': // timestamp
    259             t = va_arg(argp, int64_t);
    260             log(EVENT_FMT_TIMESTAMP, &t, sizeof(t));
    261             break;
    262 
    263         case 'd': // integer
    264             i = va_arg(argp, int);
    265             log(EVENT_FMT_INTEGER, &i, sizeof(i));
    266             break;
    267 
    268         case 'f': // float
    269             d = va_arg(argp, double); // float arguments are promoted to double in vararg lists
    270             f = (float)d;
    271             log(EVENT_FMT_FLOAT, &f, sizeof(f));
    272             break;
    273 
    274         case 'p': // pid
    275             log(EVENT_FMT_PID, mPidTag, mPidTagSize);
    276             break;
    277 
    278         // the "%\0" case finishes parsing
    279         case '\0':
    280             --p;
    281             break;
    282 
    283         case '%':
    284             break;
    285 
    286         default:
    287             ALOGW("NBLog Writer parsed invalid format specifier: %c", *p);
    288             break;
    289         }
    290     }
    291     Entry etr(EVENT_FMT_END, nullptr, 0);
    292     log(etr, true);
    293 }
    294 
    295 // ---------------------------------------------------------------------------
    296 
    297 LockedWriter::LockedWriter(void *shared, size_t size)
    298     : Writer(shared, size)
    299 {
    300 }
    301 
    302 bool LockedWriter::isEnabled() const
    303 {
    304     Mutex::Autolock _l(mLock);
    305     return Writer::isEnabled();
    306 }
    307 
    308 bool LockedWriter::setEnabled(bool enabled)
    309 {
    310     Mutex::Autolock _l(mLock);
    311     return Writer::setEnabled(enabled);
    312 }
    313 
    314 void LockedWriter::log(const Entry &entry, bool trusted) {
    315     Mutex::Autolock _l(mLock);
    316     Writer::log(entry, trusted);
    317 }
    318 
    319 }   // namespace NBLog
    320 }   // namespace android
    321