1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef LOG_TAG
16 #define LOG_TAG "AudioDumpPCM"
17 #endif
18
19 #include "audio_dump_pcm.h"
20 #include "audio_dump_pcm_private.h"
21
22 #include <utility>
23
24 #include "media_monitor_manager.h"
25 #include "callback_handler.h"
26 #include "audio_service_log.h"
27 #include "audio_errors.h"
28 #include "audio_utils.h"
29 #include "audio_schedule.h"
30
31 namespace OHOS {
32 namespace AudioStandard {
33
34 constexpr uint64_t MAX_LIMIT_BUFFER_SIZE = 100 * 1024 * 1024; // 100M
35 constexpr size_t EACH_CHUNK_SIZE = 1024 * 1024; // 1M
36 constexpr int64_t MEMBLOCK_RELEASE_TIME = 5 * 60 * 1000000; // release pcm 5min ago
37 constexpr int64_t MEMBLOCK_CHECK_TIME_MS = 30 * 1000; // check cached data's time every 30s
38 constexpr int64_t MEMORY_PRINT_TIME_MS = 60 * 1000; // print memory info every 60s
39 constexpr int64_t MEMORY_PRINT_MININUM_SIZE = 5 * 1024 * 1024; // print memory info only when used excceds 5M
40 constexpr uint16_t FILENAME_ID_MAX_INDEX = 65530; // FileNameId 0 - 65530
41 constexpr size_t FILENAME_AND_ID_SIZE = 128; // estimate each entry size in map
42 constexpr size_t NAME_MAP_NUM = 2; // FileNameIdMap and idFileNameMap
43 constexpr int32_t MAX_RECYCLE_TIMES = 1000;
44
MemChunk()45 MemChunk::MemChunk() : totalBufferSize_(EACH_CHUNK_SIZE), pointerOffset_(0), curFileNameId_(0)
46 {
47 Trace trace("MemChunk::MemChunk");
48 firstMemBlockTime_ = ClockTime::GetRealNano();
49 lastMemBlockTime_ = firstMemBlockTime_;
50 bufferPointer_ = new (std::nothrow) uint8_t[EACH_CHUNK_SIZE];
51 if (bufferPointer_ == nullptr) {
52 AUDIO_ERR_LOG("failed to get memory!");
53 }
54 memBlockDeque_ = std::make_shared<std::deque<MemBlock>>();
55 }
56
~MemChunk()57 MemChunk::~MemChunk()
58 {
59 Trace trace("MemChunk::~MemChunk");
60 if (bufferPointer_ != nullptr) {
61 delete bufferPointer_;
62 }
63 bufferPointer_ = nullptr;
64 }
65
GetMemBlock(size_t dataLength,std::string & dumpFileName,MemBlock & curMemBlock)66 int32_t MemChunk::GetMemBlock(size_t dataLength, std::string &dumpFileName, MemBlock &curMemBlock)
67 {
68 CHECK_AND_RETURN_RET(totalBufferSize_ - pointerOffset_ >= dataLength, ERROR);
69 Trace trace("MemChunk::GetMemBlock");
70
71 if (fileNameIdMap_.find(dumpFileName) != fileNameIdMap_.end()) {
72 curMemBlock.dumpFileNameId_ = fileNameIdMap_[dumpFileName];
73 } else {
74 curMemBlock.dumpFileNameId_ = (curFileNameId_ + 1 >= FILENAME_ID_MAX_INDEX) ? 0 : ++curFileNameId_;
75 fileNameIdMap_[dumpFileName] = curFileNameId_;
76 idFileNameMap_[curFileNameId_] = dumpFileName;
77 }
78
79 curMemBlock.dataPointer_ = bufferPointer_ + pointerOffset_;
80 curMemBlock.dataLength_ = dataLength;
81 pointerOffset_ += dataLength;
82 lastMemBlockTime_ = ClockTime::GetRealNano();
83 memBlockDeque_->push_back(curMemBlock);
84 return SUCCESS;
85 }
86
GetMemBlockDeque()87 std::shared_ptr<std::deque<MemBlock>> MemChunk::GetMemBlockDeque()
88 {
89 return memBlockDeque_;
90 }
91
GetMemChunkDuration(int64_t & startTime,int64_t & endTime)92 void MemChunk::GetMemChunkDuration(int64_t &startTime, int64_t &endTime)
93 {
94 startTime = firstMemBlockTime_;
95 endTime = lastMemBlockTime_;
96 }
97
GetCurUsedMemory(size_t & dataLength,size_t & bufferLength,size_t & structLength)98 int32_t MemChunk::GetCurUsedMemory(size_t &dataLength, size_t &bufferLength, size_t &structLength)
99 {
100 dataLength = pointerOffset_;
101 bufferLength = totalBufferSize_;
102 structLength = sizeof(MemBlock) * memBlockDeque_->size() + sizeof(MemChunk) +
103 FILENAME_AND_ID_SIZE * idFileNameMap_.size() * NAME_MAP_NUM; // roughly estimate the size of the map structure
104 return SUCCESS;
105 }
106
Reset()107 void MemChunk::Reset()
108 {
109 Trace trace("MemChunk::Reset");
110 pointerOffset_ = 0;
111 curFileNameId_ = 0;
112 firstMemBlockTime_ = ClockTime::GetRealNano();
113 lastMemBlockTime_ = firstMemBlockTime_;
114 idFileNameMap_ = {};
115 fileNameIdMap_ = {};
116 memBlockDeque_->clear();
117 }
118
GetInstance()119 AudioCacheMgr& AudioCacheMgr::GetInstance()
120 {
121 static AudioCacheMgrInner mgr;
122 return mgr;
123 }
124
AudioCacheMgrInner()125 AudioCacheMgrInner::AudioCacheMgrInner()
126 {
127 Trace trace("AudioCacheMgrInner::AudioCacheMgrInner");
128 totalMemChunkNums_ = MAX_LIMIT_BUFFER_SIZE / EACH_CHUNK_SIZE;
129 }
130
~AudioCacheMgrInner()131 AudioCacheMgrInner::~AudioCacheMgrInner()
132 {
133 std::lock_guard<std::mutex> runnerlock(runnerMutex_);
134 if (callbackHandler_ != nullptr) {
135 AUDIO_INFO_LOG("runner move");
136 callbackHandler_->ReleaseEventRunner();
137 callbackHandler_ = nullptr;
138 }
139 }
140
Init()141 bool AudioCacheMgrInner::Init()
142 {
143 if (isInited_ == false) {
144 InitCallbackHandler();
145 isInited_ = true;
146 return true;
147 }
148 AUDIO_WARNING_LOG("AudioCacheMgr is Inited!");
149 return true;
150 }
151
DeInit()152 bool AudioCacheMgrInner::DeInit()
153 {
154 Trace trace("AudioCacheMgrInner::DeInit");
155 std::unique_lock<std::mutex> lock(runnerMutex_);
156 if (callbackHandler_ != nullptr) {
157 callbackHandler_->ReleaseEventRunner();
158 callbackHandler_ = nullptr;
159 handler_ = nullptr;
160
161 // clear all cached pcm
162 memChunkDeque_ = {};
163 AUDIO_INFO_LOG("deinit handler success");
164 }
165 lock.unlock();
166 isInited_ = false;
167 AUDIO_WARNING_LOG("AudioCacheMgr is DeInited!");
168 return true;
169 }
170
CacheData(std::string & dumpFileName,void * srcDataPointer,size_t dataLength)171 void AudioCacheMgrInner::CacheData(std::string &dumpFileName, void* srcDataPointer, size_t dataLength)
172 {
173 if (!isInited_.load()) {
174 Trace trace("AudioCacheMgrInner::CacheData::NotInited");
175 return;
176 }
177 Trace trace("AudioCacheMgrInner::CacheData");
178 if (isDumpingData_.load()) {
179 Trace trace("AudioCacheMgrInner::CacheData::ReturnWhenDumpingData");
180 return;
181 }
182 if (!g_Mutex.try_lock()) {
183 // condition 1: cacheData thread and dumpData thread in Concurrency
184 // if dumpData thread gets mutex, discard cacheData and return directly
185 // if cacheData threads gets mutex, dumpData thread waits for cacheData finish,
186 // cause cacheData excutes very fast.
187 if (isDumpingData_.load()) {
188 Trace trace("AudioCacheMgrInner::CacheData::TryLockFailedWhenDumpingData");
189 return;
190 }
191 // condition 2: two cacheData threads in Concurrency, one gets mutex
192 // the other thread waits for mutex.
193 g_Mutex.lock();
194 }
195
196 MemBlock curMemBlock {nullptr, 0, 0};
197 int ret = GetAvailableMemBlock(dataLength, dumpFileName, curMemBlock);
198 if (ret != SUCCESS) {
199 AUDIO_ERR_LOG("GetAvailableMemBlock failed. Unable to cacheData!");
200 g_Mutex.unlock();
201 return;
202 }
203 g_Mutex.unlock();
204
205 ret = memcpy_s(curMemBlock.dataPointer_, dataLength, srcDataPointer, dataLength);
206 CHECK_AND_RETURN_LOG(ret == SUCCESS, "memcpy_s failed. Unable to cacheData!");
207 }
208
GetAvailableMemBlock(size_t dataLength,std::string & dumpFileName,MemBlock & curMemBlock)209 int32_t AudioCacheMgrInner::GetAvailableMemBlock(size_t dataLength, std::string &dumpFileName, MemBlock &curMemBlock)
210 {
211 if (!memChunkDeque_.empty()) {
212 Trace trace1("AudioCacheMgrInner::GetAvailableMemBlock::hasNotFillMemChunk");
213 std::shared_ptr<MemChunk> lastMemChunk = memChunkDeque_.back();
214 if (lastMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
215 return SUCCESS;
216 }
217 }
218
219 if (memChunkDeque_.size() < totalMemChunkNums_) {
220 Trace trace2("AudioCacheMgrInner::getAvailMemBlock::GetMemBlock");
221 std::shared_ptr<MemChunk> newMemChunk = std::make_shared<MemChunk>();
222 Trace::Count("UsedMemChunk", memChunkDeque_.size());
223 memChunkDeque_.push_back(newMemChunk);
224 if (newMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
225 return SUCCESS;
226 }
227 }
228
229 if (!memChunkDeque_.empty()) {
230 Trace trace3("AudioCacheMgrInner::GetAvailableMemBlock::RecycleOneMemChunk");
231 std::shared_ptr<MemChunk> recycleMemChunk = memChunkDeque_.front();
232 memChunkDeque_.pop_front();
233 recycleMemChunk->Reset();
234 memChunkDeque_.push_back(recycleMemChunk);
235 if (recycleMemChunk->GetMemBlock(dataLength, dumpFileName, curMemBlock) == SUCCESS) {
236 return SUCCESS;
237 }
238 }
239
240 AUDIO_ERR_LOG("failed to get available memBlock");
241 return ERROR;
242 }
243
DumpAllMemBlock()244 int32_t AudioCacheMgrInner::DumpAllMemBlock()
245 {
246 if (!isInited_.load()) {
247 Trace trace("AudioCacheMgrInner::DumpAllMemBlock::NotInited");
248 AUDIO_WARNING_LOG("not inited!");
249 return ERR_ILLEGAL_STATE;
250 }
251
252 Trace trace("AudioCacheMgrInner::DumpAllMemBlock");
253 bool targetStatus = false;
254 if (!isDumpingData_.compare_exchange_strong(targetStatus, true)) {
255 AUDIO_WARNING_LOG("Already in dumping data!");
256 }
257
258 std::lock_guard<std::mutex> processsLock(g_Mutex);
259
260 std::vector<std::pair<std::string, std::string>> paramStart;
261 paramStart.push_back({"BETA", "true"});
262 Media::MediaMonitor::MediaMonitorManager::GetInstance().SetMediaParameters(paramStart);
263
264 while (!memChunkDeque_.empty()) {
265 std::shared_ptr<MemChunk> curMemChunk = memChunkDeque_.front();
266 memChunkDeque_.pop_front();
267
268 std::shared_ptr<std::deque<MemBlock>> curMemBlockDeque = curMemChunk->GetMemBlockDeque();
269 for (auto it = curMemBlockDeque->begin(); it != curMemBlockDeque->end(); ++it) {
270 Media::MediaMonitor::MediaMonitorManager::GetInstance().WriteAudioBuffer("pcm_dump_" +
271 curMemChunk->idFileNameMap_[it->dumpFileNameId_], it->dataPointer_, it->dataLength_);
272 }
273 Trace::Count("UsedMemChunk", memChunkDeque_.size());
274 }
275
276 std::vector<std::pair<std::string, std::string>> paramEnd;
277 paramEnd.push_back({"BETA", "false"});
278 Media::MediaMonitor::MediaMonitorManager::GetInstance().SetMediaParameters(paramEnd);
279
280 isDumpingData_.store(false);
281 return SUCCESS;
282 }
283
GetCachedDuration(int64_t & startTime,int64_t & endTime)284 void AudioCacheMgrInner::GetCachedDuration(int64_t &startTime, int64_t &endTime)
285 {
286 Trace trace("AudioCacheMgrInner::GetCacheDuration");
287 if (!isInited_.load()) {
288 Trace trace("AudioCacheMgrInner::GetCachedDuration::NotInited");
289 AUDIO_WARNING_LOG("not inited!");
290 return;
291 }
292 std::lock_guard<std::mutex> processLock(g_Mutex);
293 // init but no data in memchunk
294 if (memChunkDeque_.size() == 0) {
295 startTime = ClockTime::GetRealNano();
296 endTime = startTime;
297 AUDIO_WARNING_LOG("GetCachedDuration while memChunkDeque_ is empty!");
298 return;
299 }
300
301 int64_t temp;
302 if (memChunkDeque_.front() != nullptr) {
303 memChunkDeque_.front()->GetMemChunkDuration(startTime, temp);
304 }
305 if (memChunkDeque_.back() != nullptr) {
306 memChunkDeque_.back()->GetMemChunkDuration(temp, endTime);
307 }
308 AUDIO_INFO_LOG("startTime:%{public}s, endTime:%{public}s.",
309 ClockTime::NanoTimeToString(startTime).c_str(), ClockTime::NanoTimeToString(endTime).c_str());
310 }
311
PrintCurMemoryCondition()312 void AudioCacheMgrInner::PrintCurMemoryCondition()
313 {
314 Trace trace("AudioCacheMgrInner::PrintCurMemoryCondition");
315 SafeSendCallBackEvent(PRINT_MEMORY_CONDITION, 0, MEMORY_PRINT_TIME_MS);
316
317 size_t dataLength = 0;
318 size_t bufferLength = 0;
319 size_t structLength = 0;
320 GetCurMemoryCondition(dataLength, bufferLength, structLength);
321 if (bufferLength >= MEMORY_PRINT_MININUM_SIZE) {
322 AUDIO_INFO_LOG("dataLength: %{public}zu KB, bufferLength: %{public}zu KB, structLength: %{public}zu KB",
323 dataLength / BYTE_TO_KB_SIZE, bufferLength / BYTE_TO_KB_SIZE, structLength / BYTE_TO_KB_SIZE);
324 }
325 }
326
GetCurMemoryCondition(size_t & dataLength,size_t & bufferLength,size_t & structLength)327 void AudioCacheMgrInner::GetCurMemoryCondition(size_t &dataLength, size_t &bufferLength, size_t &structLength)
328 {
329 Trace trace("AudioCacheMgrInner::GetCurMemoryCondition");
330 std::lock_guard<std::mutex> processLock(g_Mutex);
331
332 size_t curDataLength = 0;
333 size_t curBufferLength = 0;
334 size_t curStructLength = 0;
335 if (memChunkDeque_.empty()) {
336 return;
337 }
338
339 for (auto it = memChunkDeque_.begin(); it != memChunkDeque_.end(); ++it) {
340 (*it)->GetCurUsedMemory(curDataLength, curBufferLength, curStructLength);
341 dataLength += curDataLength;
342 bufferLength += curBufferLength;
343 structLength += curStructLength;
344 }
345 }
346
ReleaseOverTimeMemBlock()347 void AudioCacheMgrInner::ReleaseOverTimeMemBlock()
348 {
349 Trace trace("AudioCacheMgrInner::ReleaseOverTimeMemBlock");
350 SafeSendCallBackEvent(RELEASE_OVERTIME_MEMBLOCK, 0, MEMBLOCK_CHECK_TIME_MS);
351
352 int32_t recycleNums = 0;
353 int64_t curTime = ClockTime::GetRealNano();
354 int64_t startTime;
355 int64_t endTime;
356
357 while (recycleNums < MAX_RECYCLE_TIMES) {
358 Trace trace1("AudioCacheMgrInner::ReleaseOneMemChunk");
359 std::unique_lock<std::mutex> processLock(g_Mutex);
360 if (isDumpingData_.load()) {
361 AUDIO_INFO_LOG("now dumping memblock, no need ReleaseOverTimeMemBlock");
362 return;
363 }
364 if (memChunkDeque_.empty()) {
365 break;
366 }
367 std::shared_ptr<MemChunk> releaseChunk = memChunkDeque_.front();
368 releaseChunk->GetMemChunkDuration(startTime, endTime);
369 if (curTime - endTime < MEMBLOCK_RELEASE_TIME * AUDIO_MS_PER_SECOND) {
370 break;
371 }
372 memChunkDeque_.pop_front();
373 Trace::Count("UsedMemChunk", memChunkDeque_.size());
374 // ~memchunk needs 500ns but is no need to keep lock; in this way we delay the destruct time until out the loop
375 processLock.unlock();
376 ++recycleNums;
377 }
378 if (recycleNums != 0) {
379 AUDIO_INFO_LOG("CheckMemBlock Recycle %{public}d memBlocks", recycleNums);
380 }
381 }
382
GetDumpParameter(const std::vector<std::string> & subKeys,std::vector<std::pair<std::string,std::string>> & result)383 bool AudioCacheMgrInner::GetDumpParameter(const std::vector<std::string> &subKeys,
384 std::vector<std::pair<std::string, std::string>> &result)
385 {
386 // vector size check had done before call this function
387 // audioCacheState 0:close, 1:open, 2:init
388 if (subKeys[0] == GET_STATUS_KEY) {
389 int32_t audioCacheState = 0;
390 GetSysPara("persist.multimedia.audio.audioCacheState", audioCacheState);
391 CHECK_AND_RETURN_RET_LOG(audioCacheState >= 0 &&
392 audioCacheState < static_cast<int32_t>(AUDIO_CACHE_STATE.size()),
393 false, "get invalid audioCacheState %{public}d", audioCacheState);
394 result.push_back({"STATUS", AUDIO_CACHE_STATE[audioCacheState]});
395 } else if (subKeys[0] == GET_TIME_KEY) {
396 int64_t startTime = 0;
397 int64_t endTime = 0;
398 GetCachedDuration(startTime, endTime);
399 result.push_back({ClockTime::NanoTimeToString(startTime), ClockTime::NanoTimeToString(endTime)});
400 } else if (subKeys[0] == GET_MEMORY_KEY) {
401 size_t dataLength = 0;
402 size_t bufferLength = 0;
403 size_t structLength = 0;
404 GetCurMemoryCondition(dataLength, bufferLength, structLength);
405 result.push_back({std::to_string(dataLength), std::to_string(bufferLength + structLength)});
406 } else {
407 AUDIO_ERR_LOG("invalid param %{public}s", subKeys[0].c_str());
408 return false;
409 }
410 return true;
411 }
412
SetDumpParameter(const std::vector<std::pair<std::string,std::string>> & params)413 bool AudioCacheMgrInner::SetDumpParameter(const std::vector<std::pair<std::string, std::string>> ¶ms)
414 {
415 // vector size check had done before call this function
416 // audioCacheState 0:close, 1:open, 2:init
417 int32_t audioCacheState = 0;
418 GetSysPara("persist.multimedia.audio.audioCacheState", audioCacheState);
419 if (params[0].first == SET_OPEN_KEY) {
420 Init();
421 SetSysPara("persist.multimedia.audio.audioCacheState", 1);
422 } else if (params[0].first == SET_CLOSE_KEY) {
423 DeInit();
424 SetSysPara("persist.multimedia.audio.audioCacheState", 0);
425 } else if (params[0].first == SET_UPLOAD_KEY) {
426 // only when user argees to cachedata, audioCacheState will change to 1(open),.
427 CHECK_AND_RETURN_RET_LOG(audioCacheState == 1, false,
428 "cannot upload, curAudioCacheState is %{public}d, not code 1! ", audioCacheState);
429 CHECK_AND_RETURN_RET_LOG(DumpAllMemBlock() == SUCCESS, false,
430 "upload allMemBlock failed!");
431 } else {
432 AUDIO_ERR_LOG("invalid param %{public}s", params[0].first.c_str());
433 return false;
434 }
435 return true;
436 }
437
InitCallbackHandler()438 void AudioCacheMgrInner::InitCallbackHandler()
439 {
440 Trace trace("AudioCacheMgrInner::InitCallbackHandler");
441 std::unique_lock<std::mutex> lock(runnerMutex_);
442 if (callbackHandler_ == nullptr) {
443 handler_ = std::make_shared<AudioCacheHandler>(this);
444 callbackHandler_ = CallbackHandler::GetInstance(handler_, "OS_AUDIODumpCB");
445 AUDIO_INFO_LOG("init handler success");
446 }
447 lock.unlock();
448 SafeSendCallBackEvent(RELEASE_OVERTIME_MEMBLOCK, 0, MEMBLOCK_CHECK_TIME_MS);
449 SafeSendCallBackEvent(PRINT_MEMORY_CONDITION, 0, MEMORY_PRINT_TIME_MS);
450 SafeSendCallBackEvent(RAISE_PRIORITY, 0, 0);
451 }
452
SafeSendCallBackEvent(uint32_t eventCode,int64_t data,int64_t delayTime)453 void AudioCacheMgrInner::SafeSendCallBackEvent(uint32_t eventCode, int64_t data, int64_t delayTime)
454 {
455 Trace trace("AudioCacheMgrInner::SafeSendCallBackEvent");
456 std::lock_guard<std::mutex> lock(runnerMutex_);
457 CHECK_AND_RETURN_LOG(callbackHandler_ != nullptr, "Runner is Release");
458
459 callbackHandler_->SendCallbackEvent(eventCode, data, delayTime);
460 }
461
OnHandle(uint32_t code,int64_t data)462 void AudioCacheMgrInner::OnHandle(uint32_t code, int64_t data)
463 {
464 switch (code) {
465 case RELEASE_OVERTIME_MEMBLOCK:
466 ReleaseOverTimeMemBlock();
467 break;
468 case PRINT_MEMORY_CONDITION:
469 PrintCurMemoryCondition();
470 break;
471 case RAISE_PRIORITY:
472 ScheduleThreadInServer(getpid(), gettid());
473 break;
474 default:
475 break;
476 }
477 }
478
AudioCacheHandler(IHandler * handler)479 AudioCacheHandler::AudioCacheHandler(IHandler* handler) : handler_(handler) {}
480
OnHandle(uint32_t code,int64_t data)481 void AudioCacheHandler::OnHandle(uint32_t code, int64_t data)
482 {
483 CHECK_AND_RETURN_LOG(handler_ != nullptr, "handler is nullptr");
484 handler_->OnHandle(code, data);
485 }
486
487 } // namespace AudioStandard
488 } // namespace OHOS
489