1 /* 2 * Copyright (C) 2016 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 18 #include "BufLog.h" 19 #define LOG_TAG "BufLog" 20 //#define LOG_NDEBUG 0 21 22 #include <errno.h> 23 #include "log/log.h" 24 #include <pthread.h> 25 #include <stdio.h> 26 #include <string.h> 27 #include <audio_utils/string.h> 28 29 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 30 31 // ------------------------------ 32 // BufLogSingleton 33 // ------------------------------ 34 pthread_once_t onceControl = PTHREAD_ONCE_INIT; 35 36 BufLog *BufLogSingleton::mInstance = NULL; 37 38 void BufLogSingleton::initOnce() { 39 mInstance = new BufLog(); 40 ALOGW("=====================================\n" \ 41 "Warning: BUFLOG is defined in some part of your code.\n" \ 42 "This will create large audio dumps in %s.\n" \ 43 "=====================================\n", BUFLOG_BASE_PATH); 44 } 45 46 BufLog *BufLogSingleton::instance() { 47 pthread_once(&onceControl, initOnce); 48 return mInstance; 49 } 50 51 bool BufLogSingleton::instanceExists() { 52 return mInstance != NULL; 53 } 54 55 // ------------------------------ 56 // BufLog 57 // ------------------------------ 58 59 BufLog::BufLog() { 60 memset(mStreams, 0, sizeof(mStreams)); 61 } 62 63 BufLog::~BufLog() { 64 android::Mutex::Autolock autoLock(mLock); 65 66 for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) { 67 BufLogStream *pBLStream = mStreams[id]; 68 if (pBLStream != NULL) { 69 delete pBLStream ; 70 mStreams[id] = NULL; 71 } 72 } 73 } 74 75 size_t BufLog::write(int streamid, const char *tag, int format, int channels, 76 int samplingRate, size_t maxBytes, const void *buf, size_t size) { 77 unsigned int id = streamid % BUFLOG_MAXSTREAMS; 78 android::Mutex::Autolock autoLock(mLock); 79 80 BufLogStream *pBLStream = mStreams[id]; 81 82 if (pBLStream == NULL) { 83 pBLStream = mStreams[id] = new BufLogStream(id, tag, format, channels, 84 samplingRate, maxBytes); 85 ALOG_ASSERT(pBLStream != NULL, "BufLogStream Failed to be created"); 86 } 87 88 return pBLStream->write(buf, size); 89 } 90 91 void BufLog::reset() { 92 android::Mutex::Autolock autoLock(mLock); 93 ALOGV("Resetting all BufLogs"); 94 int count = 0; 95 96 for (unsigned int id = 0; id < BUFLOG_MAXSTREAMS; id++) { 97 BufLogStream *pBLStream = mStreams[id]; 98 if (pBLStream != NULL) { 99 delete pBLStream; 100 mStreams[id] = NULL; 101 count++; 102 } 103 } 104 ALOGV("Reset %d BufLogs", count); 105 } 106 107 // ------------------------------ 108 // BufLogStream 109 // ------------------------------ 110 111 BufLogStream::BufLogStream(unsigned int id, 112 const char *tag, 113 unsigned int format, 114 unsigned int channels, 115 unsigned int samplingRate, 116 size_t maxBytes = 0) : mId(id), mFormat(format), mChannels(channels), 117 mSamplingRate(samplingRate), mMaxBytes(maxBytes) { 118 mByteCount = 0l; 119 mPaused = false; 120 if (tag != NULL) { 121 (void)audio_utils_strlcpy(mTag, tag); 122 } else { 123 mTag[0] = 0; 124 } 125 ALOGV("Creating BufLogStream id:%d tag:%s format:%#x ch:%d sr:%d maxbytes:%zu", mId, mTag, 126 mFormat, mChannels, mSamplingRate, mMaxBytes); 127 128 //open file (s), info about tag, format, etc. 129 //timestamp 130 char timeStr[16]; //size 16: format %Y%m%d%H%M%S 14 chars + string null terminator 131 struct timeval tv; 132 gettimeofday(&tv, NULL); 133 struct tm tm; 134 localtime_r(&tv.tv_sec, &tm); 135 strftime(timeStr, sizeof(timeStr), "%Y%m%d%H%M%S", &tm); 136 char logPath[BUFLOG_MAX_PATH_SIZE]; 137 snprintf(logPath, BUFLOG_MAX_PATH_SIZE, "%s/%s_%d_%s_%d_%d_%d.raw", BUFLOG_BASE_PATH, timeStr, 138 mId, mTag, mFormat, mChannels, mSamplingRate); 139 ALOGV("data output: %s", logPath); 140 141 mFile = fopen(logPath, "wb"); 142 if (mFile != NULL) { 143 ALOGV("Success creating file at: %p", mFile); 144 } else { 145 ALOGE("Error: could not create file BufLogStream %s", strerror(errno)); 146 } 147 } 148 149 void BufLogStream::closeStream_l() { 150 ALOGV("Closing BufLogStream id:%d tag:%s", mId, mTag); 151 if (mFile != NULL) { 152 fclose(mFile); 153 mFile = NULL; 154 } 155 } 156 157 BufLogStream::~BufLogStream() { 158 ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag); 159 android::Mutex::Autolock autoLock(mLock); 160 closeStream_l(); 161 } 162 163 size_t BufLogStream::write(const void *buf, size_t size) { 164 165 size_t bytes = 0; 166 if (!mPaused && mFile != NULL) { 167 if (size > 0 && buf != NULL) { 168 android::Mutex::Autolock autoLock(mLock); 169 if (mMaxBytes > 0) { 170 size = MIN(size, mMaxBytes - mByteCount); 171 } 172 bytes = fwrite(buf, 1, size, mFile); 173 mByteCount += bytes; 174 if (mMaxBytes > 0 && mMaxBytes == mByteCount) { 175 closeStream_l(); 176 } 177 } 178 ALOGV("wrote %zu/%zu bytes to BufLogStream %d tag:%s. Total Bytes: %zu", bytes, size, mId, 179 mTag, mByteCount); 180 } else { 181 ALOGV("Warning: trying to write to %s BufLogStream id:%d tag:%s", 182 mPaused ? "paused" : "closed", mId, mTag); 183 } 184 return bytes; 185 } 186 187 bool BufLogStream::setPause(bool pause) { 188 bool old = mPaused; 189 mPaused = pause; 190 return old; 191 } 192 193 void BufLogStream::finalize() { 194 android::Mutex::Autolock autoLock(mLock); 195 closeStream_l(); 196 } 197