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 
initOnce()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 
instance()46 BufLog *BufLogSingleton::instance() {
47     pthread_once(&onceControl, initOnce);
48     return mInstance;
49 }
50 
instanceExists()51 bool BufLogSingleton::instanceExists() {
52     return mInstance != NULL;
53 }
54 
55 // ------------------------------
56 // BufLog
57 // ------------------------------
58 
BufLog()59 BufLog::BufLog() {
60     memset(mStreams, 0, sizeof(mStreams));
61 }
62 
~BufLog()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 
write(int streamid,const char * tag,int format,int channels,int samplingRate,size_t maxBytes,const void * buf,size_t size)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 
reset()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 
BufLogStream(unsigned int id,const char * tag,unsigned int format,unsigned int channels,unsigned int samplingRate,size_t maxBytes=0)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 = 0;
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 
closeStream_l()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 
~BufLogStream()157 BufLogStream::~BufLogStream() {
158     ALOGV("Destroying BufLogStream id:%d tag:%s", mId, mTag);
159     android::Mutex::Autolock autoLock(mLock);
160     closeStream_l();
161 }
162 
write(const void * buf,size_t size)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 
setPause(bool pause)187 bool BufLogStream::setPause(bool pause) {
188     bool old = mPaused;
189     mPaused = pause;
190     return old;
191 }
192 
finalize()193 void BufLogStream::finalize() {
194     android::Mutex::Autolock autoLock(mLock);
195     closeStream_l();
196 }
197