1 /* 2 * Copyright (C) 2012 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 // <IMPORTANT_WARNING> 18 // Design rules for threadLoop() are given in the comments at section "Fast mixer thread" of 19 // StateQueue.h. In particular, avoid library and system calls except at well-known points. 20 // The design rules are only for threadLoop(), and don't apply to FastMixerDumpState methods. 21 // </IMPORTANT_WARNING> 22 23 #define LOG_TAG "FastMixer" 24 //#define LOG_NDEBUG 0 25 26 #define ATRACE_TAG ATRACE_TAG_AUDIO 27 28 #include "Configuration.h" 29 #include <time.h> 30 #include <utils/Log.h> 31 #include <utils/Trace.h> 32 #include <system/audio.h> 33 #ifdef FAST_MIXER_STATISTICS 34 #include <cpustats/CentralTendencyStatistics.h> 35 #ifdef CPU_FREQUENCY_STATISTICS 36 #include <cpustats/ThreadCpuUsage.h> 37 #endif 38 #endif 39 #include <audio_utils/format.h> 40 #include "AudioMixer.h" 41 #include "FastMixer.h" 42 43 #define FCC_2 2 // fixed channel count assumption 44 45 namespace android { 46 47 /*static*/ const FastMixerState FastMixer::initial; 48 49 FastMixer::FastMixer() : FastThread(), 50 slopNs(0), 51 // fastTrackNames 52 // generations 53 outputSink(NULL), 54 outputSinkGen(0), 55 mixer(NULL), 56 mSinkBuffer(NULL), 57 mSinkBufferSize(0), 58 mSinkChannelCount(FCC_2), 59 mMixerBuffer(NULL), 60 mMixerBufferSize(0), 61 mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), 62 mMixerBufferState(UNDEFINED), 63 format(Format_Invalid), 64 sampleRate(0), 65 fastTracksGen(0), 66 totalNativeFramesWritten(0), 67 // timestamp 68 nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler 69 { 70 // FIXME pass initial as parameter to base class constructor, and make it static local 71 previous = &initial; 72 current = &initial; 73 74 mDummyDumpState = &dummyDumpState; 75 // TODO: Add channel mask to NBAIO_Format. 76 // We assume that the channel mask must be a valid positional channel mask. 77 mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); 78 79 unsigned i; 80 for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { 81 fastTrackNames[i] = -1; 82 generations[i] = 0; 83 } 84 #ifdef FAST_MIXER_STATISTICS 85 oldLoad.tv_sec = 0; 86 oldLoad.tv_nsec = 0; 87 #endif 88 } 89 90 FastMixer::~FastMixer() 91 { 92 } 93 94 FastMixerStateQueue* FastMixer::sq() 95 { 96 return &mSQ; 97 } 98 99 const FastThreadState *FastMixer::poll() 100 { 101 return mSQ.poll(); 102 } 103 104 void FastMixer::setLog(NBLog::Writer *logWriter) 105 { 106 if (mixer != NULL) { 107 mixer->setLog(logWriter); 108 } 109 } 110 111 void FastMixer::onIdle() 112 { 113 preIdle = *(const FastMixerState *)current; 114 current = &preIdle; 115 } 116 117 void FastMixer::onExit() 118 { 119 delete mixer; 120 free(mMixerBuffer); 121 free(mSinkBuffer); 122 } 123 124 bool FastMixer::isSubClassCommand(FastThreadState::Command command) 125 { 126 switch ((FastMixerState::Command) command) { 127 case FastMixerState::MIX: 128 case FastMixerState::WRITE: 129 case FastMixerState::MIX_WRITE: 130 return true; 131 default: 132 return false; 133 } 134 } 135 136 void FastMixer::onStateChange() 137 { 138 const FastMixerState * const current = (const FastMixerState *) this->current; 139 const FastMixerState * const previous = (const FastMixerState *) this->previous; 140 FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; 141 const size_t frameCount = current->mFrameCount; 142 143 // handle state change here, but since we want to diff the state, 144 // we're prepared for previous == &initial the first time through 145 unsigned previousTrackMask; 146 147 // check for change in output HAL configuration 148 NBAIO_Format previousFormat = format; 149 if (current->mOutputSinkGen != outputSinkGen) { 150 outputSink = current->mOutputSink; 151 outputSinkGen = current->mOutputSinkGen; 152 if (outputSink == NULL) { 153 format = Format_Invalid; 154 sampleRate = 0; 155 mSinkChannelCount = 0; 156 mSinkChannelMask = AUDIO_CHANNEL_NONE; 157 } else { 158 format = outputSink->format(); 159 sampleRate = Format_sampleRate(format); 160 mSinkChannelCount = Format_channelCount(format); 161 LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); 162 163 // TODO: Add channel mask to NBAIO_Format 164 // We assume that the channel mask must be a valid positional channel mask. 165 mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); 166 } 167 dumpState->mSampleRate = sampleRate; 168 } 169 170 if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { 171 // FIXME to avoid priority inversion, don't delete here 172 delete mixer; 173 mixer = NULL; 174 free(mMixerBuffer); 175 mMixerBuffer = NULL; 176 free(mSinkBuffer); 177 mSinkBuffer = NULL; 178 if (frameCount > 0 && sampleRate > 0) { 179 // FIXME new may block for unbounded time at internal mutex of the heap 180 // implementation; it would be better to have normal mixer allocate for us 181 // to avoid blocking here and to prevent possible priority inversion 182 mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks); 183 const size_t mixerFrameSize = mSinkChannelCount 184 * audio_bytes_per_sample(mMixerBufferFormat); 185 mMixerBufferSize = mixerFrameSize * frameCount; 186 (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); 187 const size_t sinkFrameSize = mSinkChannelCount 188 * audio_bytes_per_sample(format.mFormat); 189 if (sinkFrameSize > mixerFrameSize) { // need a sink buffer 190 mSinkBufferSize = sinkFrameSize * frameCount; 191 (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); 192 } 193 periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 194 underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 195 overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 196 forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 197 warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 198 } else { 199 periodNs = 0; 200 underrunNs = 0; 201 overrunNs = 0; 202 forceNs = 0; 203 warmupNs = 0; 204 } 205 mMixerBufferState = UNDEFINED; 206 #if !LOG_NDEBUG 207 for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { 208 fastTrackNames[i] = -1; 209 } 210 #endif 211 // we need to reconfigure all active tracks 212 previousTrackMask = 0; 213 fastTracksGen = current->mFastTracksGen - 1; 214 dumpState->mFrameCount = frameCount; 215 } else { 216 previousTrackMask = previous->mTrackMask; 217 } 218 219 // check for change in active track set 220 const unsigned currentTrackMask = current->mTrackMask; 221 dumpState->mTrackMask = currentTrackMask; 222 if (current->mFastTracksGen != fastTracksGen) { 223 ALOG_ASSERT(mMixerBuffer != NULL); 224 int name; 225 226 // process removed tracks first to avoid running out of track names 227 unsigned removedTracks = previousTrackMask & ~currentTrackMask; 228 while (removedTracks != 0) { 229 int i = __builtin_ctz(removedTracks); 230 removedTracks &= ~(1 << i); 231 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 232 ALOG_ASSERT(fastTrack->mBufferProvider == NULL); 233 if (mixer != NULL) { 234 name = fastTrackNames[i]; 235 ALOG_ASSERT(name >= 0); 236 mixer->deleteTrackName(name); 237 } 238 #if !LOG_NDEBUG 239 fastTrackNames[i] = -1; 240 #endif 241 // don't reset track dump state, since other side is ignoring it 242 generations[i] = fastTrack->mGeneration; 243 } 244 245 // now process added tracks 246 unsigned addedTracks = currentTrackMask & ~previousTrackMask; 247 while (addedTracks != 0) { 248 int i = __builtin_ctz(addedTracks); 249 addedTracks &= ~(1 << i); 250 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 251 AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; 252 ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); 253 if (mixer != NULL) { 254 name = mixer->getTrackName(fastTrack->mChannelMask, 255 fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); 256 ALOG_ASSERT(name >= 0); 257 fastTrackNames[i] = name; 258 mixer->setBufferProvider(name, bufferProvider); 259 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, 260 (void *)mMixerBuffer); 261 // newly allocated track names default to full scale volume 262 mixer->setParameter( 263 name, 264 AudioMixer::TRACK, 265 AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); 266 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, 267 (void *)(uintptr_t)fastTrack->mFormat); 268 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, 269 (void *)(uintptr_t)fastTrack->mChannelMask); 270 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, 271 (void *)(uintptr_t)mSinkChannelMask); 272 mixer->enable(name); 273 } 274 generations[i] = fastTrack->mGeneration; 275 } 276 277 // finally process (potentially) modified tracks; these use the same slot 278 // but may have a different buffer provider or volume provider 279 unsigned modifiedTracks = currentTrackMask & previousTrackMask; 280 while (modifiedTracks != 0) { 281 int i = __builtin_ctz(modifiedTracks); 282 modifiedTracks &= ~(1 << i); 283 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 284 if (fastTrack->mGeneration != generations[i]) { 285 // this track was actually modified 286 AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; 287 ALOG_ASSERT(bufferProvider != NULL); 288 if (mixer != NULL) { 289 name = fastTrackNames[i]; 290 ALOG_ASSERT(name >= 0); 291 mixer->setBufferProvider(name, bufferProvider); 292 if (fastTrack->mVolumeProvider == NULL) { 293 float f = AudioMixer::UNITY_GAIN_FLOAT; 294 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); 295 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); 296 } 297 mixer->setParameter(name, AudioMixer::RESAMPLE, 298 AudioMixer::REMOVE, NULL); 299 mixer->setParameter( 300 name, 301 AudioMixer::TRACK, 302 AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); 303 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT, 304 (void *)(uintptr_t)fastTrack->mFormat); 305 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, 306 (void *)(uintptr_t)fastTrack->mChannelMask); 307 mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, 308 (void *)(uintptr_t)mSinkChannelMask); 309 // already enabled 310 } 311 generations[i] = fastTrack->mGeneration; 312 } 313 } 314 315 fastTracksGen = current->mFastTracksGen; 316 317 dumpState->mNumTracks = popcount(currentTrackMask); 318 } 319 } 320 321 void FastMixer::onWork() 322 { 323 const FastMixerState * const current = (const FastMixerState *) this->current; 324 FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; 325 const FastMixerState::Command command = this->command; 326 const size_t frameCount = current->mFrameCount; 327 328 if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { 329 ALOG_ASSERT(mMixerBuffer != NULL); 330 // for each track, update volume and check for underrun 331 unsigned currentTrackMask = current->mTrackMask; 332 while (currentTrackMask != 0) { 333 int i = __builtin_ctz(currentTrackMask); 334 currentTrackMask &= ~(1 << i); 335 const FastTrack* fastTrack = ¤t->mFastTracks[i]; 336 337 // Refresh the per-track timestamp 338 if (timestampStatus == NO_ERROR) { 339 uint32_t trackFramesWrittenButNotPresented = 340 nativeFramesWrittenButNotPresented; 341 uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); 342 // Can't provide an AudioTimestamp before first frame presented, 343 // or during the brief 32-bit wraparound window 344 if (trackFramesWritten >= trackFramesWrittenButNotPresented) { 345 AudioTimestamp perTrackTimestamp; 346 perTrackTimestamp.mPosition = 347 trackFramesWritten - trackFramesWrittenButNotPresented; 348 perTrackTimestamp.mTime = timestamp.mTime; 349 fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); 350 } 351 } 352 353 int name = fastTrackNames[i]; 354 ALOG_ASSERT(name >= 0); 355 if (fastTrack->mVolumeProvider != NULL) { 356 gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); 357 float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); 358 float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); 359 360 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); 361 mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); 362 } 363 // FIXME The current implementation of framesReady() for fast tracks 364 // takes a tryLock, which can block 365 // up to 1 ms. If enough active tracks all blocked in sequence, this would result 366 // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO. 367 size_t framesReady = fastTrack->mBufferProvider->framesReady(); 368 if (ATRACE_ENABLED()) { 369 // I wish we had formatted trace names 370 char traceName[16]; 371 strcpy(traceName, "fRdy"); 372 traceName[4] = i + (i < 10 ? '0' : 'A' - 10); 373 traceName[5] = '\0'; 374 ATRACE_INT(traceName, framesReady); 375 } 376 FastTrackDump *ftDump = &dumpState->mTracks[i]; 377 FastTrackUnderruns underruns = ftDump->mUnderruns; 378 if (framesReady < frameCount) { 379 if (framesReady == 0) { 380 underruns.mBitFields.mEmpty++; 381 underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; 382 mixer->disable(name); 383 } else { 384 // allow mixing partial buffer 385 underruns.mBitFields.mPartial++; 386 underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; 387 mixer->enable(name); 388 } 389 } else { 390 underruns.mBitFields.mFull++; 391 underruns.mBitFields.mMostRecent = UNDERRUN_FULL; 392 mixer->enable(name); 393 } 394 ftDump->mUnderruns = underruns; 395 ftDump->mFramesReady = framesReady; 396 } 397 398 int64_t pts; 399 if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { 400 pts = AudioBufferProvider::kInvalidPTS; 401 } 402 403 // process() is CPU-bound 404 mixer->process(pts); 405 mMixerBufferState = MIXED; 406 } else if (mMixerBufferState == MIXED) { 407 mMixerBufferState = UNDEFINED; 408 } 409 //bool didFullWrite = false; // dumpsys could display a count of partial writes 410 if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) { 411 if (mMixerBufferState == UNDEFINED) { 412 memset(mMixerBuffer, 0, mMixerBufferSize); 413 mMixerBufferState = ZEROED; 414 } 415 void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; 416 if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format 417 memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat, 418 frameCount * Format_channelCount(format)); 419 } 420 // if non-NULL, then duplicate write() to this non-blocking sink 421 NBAIO_Sink* teeSink; 422 if ((teeSink = current->mTeeSink) != NULL) { 423 (void) teeSink->write(buffer, frameCount); 424 } 425 // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, 426 // but this code should be modified to handle both non-blocking and blocking sinks 427 dumpState->mWriteSequence++; 428 ATRACE_BEGIN("write"); 429 ssize_t framesWritten = outputSink->write(buffer, frameCount); 430 ATRACE_END(); 431 dumpState->mWriteSequence++; 432 if (framesWritten >= 0) { 433 ALOG_ASSERT((size_t) framesWritten <= frameCount); 434 totalNativeFramesWritten += framesWritten; 435 dumpState->mFramesWritten = totalNativeFramesWritten; 436 //if ((size_t) framesWritten == frameCount) { 437 // didFullWrite = true; 438 //} 439 } else { 440 dumpState->mWriteErrors++; 441 } 442 attemptedWrite = true; 443 // FIXME count # of writes blocked excessively, CPU usage, etc. for dump 444 445 timestampStatus = outputSink->getTimestamp(timestamp); 446 if (timestampStatus == NO_ERROR) { 447 uint32_t totalNativeFramesPresented = timestamp.mPosition; 448 if (totalNativeFramesPresented <= totalNativeFramesWritten) { 449 nativeFramesWrittenButNotPresented = 450 totalNativeFramesWritten - totalNativeFramesPresented; 451 } else { 452 // HAL reported that more frames were presented than were written 453 timestampStatus = INVALID_OPERATION; 454 } 455 } 456 } 457 } 458 459 FastMixerDumpState::FastMixerDumpState( 460 #ifdef FAST_MIXER_STATISTICS 461 uint32_t samplingN 462 #endif 463 ) : FastThreadDumpState(), 464 mWriteSequence(0), mFramesWritten(0), 465 mNumTracks(0), mWriteErrors(0), 466 mSampleRate(0), mFrameCount(0), 467 mTrackMask(0) 468 { 469 #ifdef FAST_MIXER_STATISTICS 470 increaseSamplingN(samplingN); 471 #endif 472 } 473 474 #ifdef FAST_MIXER_STATISTICS 475 void FastMixerDumpState::increaseSamplingN(uint32_t samplingN) 476 { 477 if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) { 478 return; 479 } 480 uint32_t additional = samplingN - mSamplingN; 481 // sample arrays aren't accessed atomically with respect to the bounds, 482 // so clearing reduces chance for dumpsys to read random uninitialized samples 483 memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional); 484 memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional); 485 #ifdef CPU_FREQUENCY_STATISTICS 486 memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional); 487 #endif 488 mSamplingN = samplingN; 489 } 490 #endif 491 492 FastMixerDumpState::~FastMixerDumpState() 493 { 494 } 495 496 // helper function called by qsort() 497 static int compare_uint32_t(const void *pa, const void *pb) 498 { 499 uint32_t a = *(const uint32_t *)pa; 500 uint32_t b = *(const uint32_t *)pb; 501 if (a < b) { 502 return -1; 503 } else if (a > b) { 504 return 1; 505 } else { 506 return 0; 507 } 508 } 509 510 void FastMixerDumpState::dump(int fd) const 511 { 512 if (mCommand == FastMixerState::INITIAL) { 513 dprintf(fd, " FastMixer not initialized\n"); 514 return; 515 } 516 #define COMMAND_MAX 32 517 char string[COMMAND_MAX]; 518 switch (mCommand) { 519 case FastMixerState::INITIAL: 520 strcpy(string, "INITIAL"); 521 break; 522 case FastMixerState::HOT_IDLE: 523 strcpy(string, "HOT_IDLE"); 524 break; 525 case FastMixerState::COLD_IDLE: 526 strcpy(string, "COLD_IDLE"); 527 break; 528 case FastMixerState::EXIT: 529 strcpy(string, "EXIT"); 530 break; 531 case FastMixerState::MIX: 532 strcpy(string, "MIX"); 533 break; 534 case FastMixerState::WRITE: 535 strcpy(string, "WRITE"); 536 break; 537 case FastMixerState::MIX_WRITE: 538 strcpy(string, "MIX_WRITE"); 539 break; 540 default: 541 snprintf(string, COMMAND_MAX, "%d", mCommand); 542 break; 543 } 544 double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + 545 (mMeasuredWarmupTs.tv_nsec / 1000000.0); 546 double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; 547 dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" 548 " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" 549 " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" 550 " mixPeriod=%.2f ms\n", 551 string, mWriteSequence, mFramesWritten, 552 mNumTracks, mWriteErrors, mUnderruns, mOverruns, 553 mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, 554 mixPeriodSec * 1e3); 555 #ifdef FAST_MIXER_STATISTICS 556 // find the interval of valid samples 557 uint32_t bounds = mBounds; 558 uint32_t newestOpen = bounds & 0xFFFF; 559 uint32_t oldestClosed = bounds >> 16; 560 uint32_t n = (newestOpen - oldestClosed) & 0xFFFF; 561 if (n > mSamplingN) { 562 ALOGE("too many samples %u", n); 563 n = mSamplingN; 564 } 565 // statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency, 566 // and adjusted CPU load in MHz normalized for CPU clock frequency 567 CentralTendencyStatistics wall, loadNs; 568 #ifdef CPU_FREQUENCY_STATISTICS 569 CentralTendencyStatistics kHz, loadMHz; 570 uint32_t previousCpukHz = 0; 571 #endif 572 // Assuming a normal distribution for cycle times, three standard deviations on either side of 573 // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the 574 // sample set, we get 99.8% combined, or close to three standard deviations. 575 static const uint32_t kTailDenominator = 1000; 576 uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; 577 // loop over all the samples 578 for (uint32_t j = 0; j < n; ++j) { 579 size_t i = oldestClosed++ & (mSamplingN - 1); 580 uint32_t wallNs = mMonotonicNs[i]; 581 if (tail != NULL) { 582 tail[j] = wallNs; 583 } 584 wall.sample(wallNs); 585 uint32_t sampleLoadNs = mLoadNs[i]; 586 loadNs.sample(sampleLoadNs); 587 #ifdef CPU_FREQUENCY_STATISTICS 588 uint32_t sampleCpukHz = mCpukHz[i]; 589 // skip bad kHz samples 590 if ((sampleCpukHz & ~0xF) != 0) { 591 kHz.sample(sampleCpukHz >> 4); 592 if (sampleCpukHz == previousCpukHz) { 593 double megacycles = (double) sampleLoadNs * (double) (sampleCpukHz >> 4) * 1e-12; 594 double adjMHz = megacycles / mixPeriodSec; // _not_ wallNs * 1e9 595 loadMHz.sample(adjMHz); 596 } 597 } 598 previousCpukHz = sampleCpukHz; 599 #endif 600 } 601 if (n) { 602 dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", 603 wall.n() * mixPeriodSec); 604 dprintf(fd, " wall clock time in ms per mix cycle:\n" 605 " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", 606 wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, 607 wall.stddev()*1e-6); 608 dprintf(fd, " raw CPU load in us per mix cycle:\n" 609 " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", 610 loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, 611 loadNs.stddev()*1e-3); 612 } else { 613 dprintf(fd, " No FastMixer statistics available currently\n"); 614 } 615 #ifdef CPU_FREQUENCY_STATISTICS 616 dprintf(fd, " CPU clock frequency in MHz:\n" 617 " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", 618 kHz.mean()*1e-3, kHz.minimum()*1e-3, kHz.maximum()*1e-3, kHz.stddev()*1e-3); 619 dprintf(fd, " adjusted CPU load in MHz (i.e. normalized for CPU clock frequency):\n" 620 " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", 621 loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); 622 #endif 623 if (tail != NULL) { 624 qsort(tail, n, sizeof(uint32_t), compare_uint32_t); 625 // assume same number of tail samples on each side, left and right 626 uint32_t count = n / kTailDenominator; 627 CentralTendencyStatistics left, right; 628 for (uint32_t i = 0; i < count; ++i) { 629 left.sample(tail[i]); 630 right.sample(tail[n - (i + 1)]); 631 } 632 dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" 633 " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" 634 " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", 635 left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, 636 right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, 637 right.stddev()*1e-6); 638 delete[] tail; 639 } 640 #endif 641 // The active track mask and track states are updated non-atomically. 642 // So if we relied on isActive to decide whether to display, 643 // then we might display an obsolete track or omit an active track. 644 // Instead we always display all tracks, with an indication 645 // of whether we think the track is active. 646 uint32_t trackMask = mTrackMask; 647 dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", 648 FastMixerState::kMaxFastTracks, trackMask); 649 dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); 650 for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { 651 bool isActive = trackMask & 1; 652 const FastTrackDump *ftDump = &mTracks[i]; 653 const FastTrackUnderruns& underruns = ftDump->mUnderruns; 654 const char *mostRecent; 655 switch (underruns.mBitFields.mMostRecent) { 656 case UNDERRUN_FULL: 657 mostRecent = "full"; 658 break; 659 case UNDERRUN_PARTIAL: 660 mostRecent = "partial"; 661 break; 662 case UNDERRUN_EMPTY: 663 mostRecent = "empty"; 664 break; 665 default: 666 mostRecent = "?"; 667 break; 668 } 669 dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", 670 (underruns.mBitFields.mFull) & UNDERRUN_MASK, 671 (underruns.mBitFields.mPartial) & UNDERRUN_MASK, 672 (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, 673 mostRecent, ftDump->mFramesReady); 674 } 675 } 676 677 } // namespace android 678