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