diff options
Diffstat (limited to 'libs/hwui/JankTracker.cpp')
-rw-r--r-- | libs/hwui/JankTracker.cpp | 239 |
1 files changed, 35 insertions, 204 deletions
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 028d9f756fb7..9d1182819444 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -34,14 +34,6 @@ namespace android { namespace uirenderer { -static const char* JANK_TYPE_NAMES[] = { - "Missed Vsync", - "High input latency", - "Slow UI thread", - "Slow bitmap uploads", - "Slow issue draw commands", -}; - struct Comparison { FrameInfoIndex start; FrameInfoIndex end; @@ -68,70 +60,13 @@ static const int64_t IGNORE_EXCEEDING = seconds_to_nanoseconds(10); */ static const int64_t EXEMPT_FRAMES_FLAGS = FrameInfoFlags::SurfaceCanvas; -// The bucketing algorithm controls so to speak -// If a frame is <= to this it goes in bucket 0 -static const uint32_t kBucketMinThreshold = 5; -// If a frame is > this, start counting in increments of 2ms -static const uint32_t kBucket2msIntervals = 32; -// If a frame is > this, start counting in increments of 4ms -static const uint32_t kBucket4msIntervals = 48; - // For testing purposes to try and eliminate test infra overhead we will // consider any unknown delay of frame start as part of the test infrastructure // and filter it out of the frame profile data static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; -// The interval of the slow frame histogram -static const uint32_t kSlowFrameBucketIntervalMs = 50; -// The start point of the slow frame bucket in ms -static const uint32_t kSlowFrameBucketStartMs = 150; - -// This will be called every frame, performance sensitive -// Uses bit twiddling to avoid branching while achieving the packing desired -static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime) { - uint32_t index = static_cast<uint32_t>(ns2ms(frameTime)); - // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result - // of negating 1 (twos compliment, yaay) else mask will be 0 - uint32_t mask = -(index > kBucketMinThreshold); - // If index > threshold, this will essentially perform: - // amountAboveThreshold = index - threshold; - // index = threshold + (amountAboveThreshold / 2) - // However if index is <= this will do nothing. It will underflow, do - // a right shift by 0 (no-op), then overflow back to the original value - index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals)) - + kBucket4msIntervals; - index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals)) - + kBucket2msIntervals; - // If index was < minThreshold at the start of all this it's going to - // be a pretty garbage value right now. However, mask is 0 so we'll end - // up with the desired result of 0. - index = (index - kBucketMinThreshold) & mask; - return index; -} - -// Only called when dumping stats, less performance sensitive -int32_t JankTracker::frameTimeForFrameCountIndex(uint32_t index) { - index = index + kBucketMinThreshold; - if (index > kBucket2msIntervals) { - index += (index - kBucket2msIntervals); - } - if (index > kBucket4msIntervals) { - // This works because it was already doubled by the above if - // 1 is added to shift slightly more towards the middle of the bucket - index += (index - kBucket4msIntervals) + 1; - } - return index; -} - -int32_t JankTracker::frameTimeForSlowFrameCountIndex(uint32_t index) { - return (index * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs; -} - -JankTracker::JankTracker(const DisplayInfo& displayInfo) { - // By default this will use malloc memory. It may be moved later to ashmem - // if there is shared space for it and a request comes in to do that. - mData = new ProfileData; - reset(); +JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) { + mGlobalData = globalData; nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps); #if USE_HWC2 nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms); @@ -151,82 +86,6 @@ JankTracker::JankTracker(const DisplayInfo& displayInfo) { setFrameInterval(frameIntervalNanos); } -JankTracker::~JankTracker() { - freeData(); -} - -void JankTracker::freeData() { - if (mIsMapped) { - munmap(mData, sizeof(ProfileData)); - } else { - delete mData; - } - mIsMapped = false; - mData = nullptr; -} - -void JankTracker::rotateStorage() { - // If we are mapped we want to stop using the ashmem backend and switch to malloc - // We are expecting a switchStorageToAshmem call to follow this, but it's not guaranteed - // If we aren't sitting on top of ashmem then just do a reset() as it's functionally - // equivalent do a free, malloc, reset. - if (mIsMapped) { - freeData(); - mData = new ProfileData; - } - reset(); -} - -void JankTracker::switchStorageToAshmem(int ashmemfd) { - int regionSize = ashmem_get_size_region(ashmemfd); - if (regionSize < 0) { - int err = errno; - ALOGW("Failed to get ashmem region size from fd %d, err %d %s", ashmemfd, err, strerror(err)); - return; - } - if (regionSize < static_cast<int>(sizeof(ProfileData))) { - ALOGW("Ashmem region is too small! Received %d, required %u", - regionSize, static_cast<unsigned int>(sizeof(ProfileData))); - return; - } - ProfileData* newData = reinterpret_cast<ProfileData*>( - mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE, - MAP_SHARED, ashmemfd, 0)); - if (newData == MAP_FAILED) { - int err = errno; - ALOGW("Failed to move profile data to ashmem fd %d, error = %d", - ashmemfd, err); - return; - } - - // The new buffer may have historical data that we want to build on top of - // But let's make sure we don't overflow Just In Case - uint32_t divider = 0; - if (newData->totalFrameCount > (1 << 24)) { - divider = 4; - } - for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) { - newData->jankTypeCounts[i] >>= divider; - newData->jankTypeCounts[i] += mData->jankTypeCounts[i]; - } - for (size_t i = 0; i < mData->frameCounts.size(); i++) { - newData->frameCounts[i] >>= divider; - newData->frameCounts[i] += mData->frameCounts[i]; - } - newData->jankFrameCount >>= divider; - newData->jankFrameCount += mData->jankFrameCount; - newData->totalFrameCount >>= divider; - newData->totalFrameCount += mData->totalFrameCount; - if (newData->statStartTime > mData->statStartTime - || newData->statStartTime == 0) { - newData->statStartTime = mData->statStartTime; - } - - freeData(); - mData = newData; - mIsMapped = true; -} - void JankTracker::setFrameInterval(nsecs_t frameInterval) { mFrameInterval = frameInterval; mThresholds[kMissedVsync] = 1; @@ -250,8 +109,7 @@ void JankTracker::setFrameInterval(nsecs_t frameInterval) { } -void JankTracker::addFrame(const FrameInfo& frame) { - mData->totalFrameCount++; +void JankTracker::finishFrame(const FrameInfo& frame) { // Fast-path for jank-free frames int64_t totalDuration = frame.duration(sFrameStart, FrameInfoIndex::FrameCompleted); if (mDequeueTimeForgiveness @@ -271,11 +129,11 @@ void JankTracker::addFrame(const FrameInfo& frame) { } } LOG_ALWAYS_FATAL_IF(totalDuration <= 0, "Impossible totalDuration %" PRId64, totalDuration); - uint32_t framebucket = frameCountIndexForFrameTime(totalDuration); - LOG_ALWAYS_FATAL_IF(framebucket < 0, "framebucket < 0 (%u)", framebucket); + mData->reportFrame(totalDuration); + (*mGlobalData)->reportFrame(totalDuration); + // Keep the fast path as fast as possible. if (CC_LIKELY(totalDuration < mFrameInterval)) { - mData->frameCounts[framebucket]++; return; } @@ -284,22 +142,14 @@ void JankTracker::addFrame(const FrameInfo& frame) { return; } - if (framebucket <= mData->frameCounts.size()) { - mData->frameCounts[framebucket]++; - } else { - framebucket = (ns2ms(totalDuration) - kSlowFrameBucketStartMs) - / kSlowFrameBucketIntervalMs; - framebucket = std::min(framebucket, - static_cast<uint32_t>(mData->slowFrameCounts.size() - 1)); - mData->slowFrameCounts[framebucket]++; - } - - mData->jankFrameCount++; + mData->reportJank(); + (*mGlobalData)->reportJank(); for (int i = 0; i < NUM_BUCKETS; i++) { int64_t delta = frame.duration(COMPARISONS[i].start, COMPARISONS[i].end); if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) { - mData->jankTypeCounts[i]++; + mData->reportJankType((JankType) i); + (*mGlobalData)->reportJankType((JankType) i); } } } @@ -320,58 +170,39 @@ void JankTracker::dumpData(int fd, const ProfileDataDescription* description, co if (sFrameStart != FrameInfoIndex::IntendedVsync) { dprintf(fd, "\nNote: Data has been filtered!"); } - dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime); - dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount); - dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount, - (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f); - dprintf(fd, "\n50th percentile: %ums", findPercentile(data, 50)); - dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90)); - dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95)); - dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99)); - for (int i = 0; i < NUM_BUCKETS; i++) { - dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]); - } - dprintf(fd, "\nHISTOGRAM:"); - for (size_t i = 0; i < data->frameCounts.size(); i++) { - dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i), - data->frameCounts[i]); - } - for (size_t i = 0; i < data->slowFrameCounts.size(); i++) { - dprintf(fd, " %ums=%u", frameTimeForSlowFrameCountIndex(i), - data->slowFrameCounts[i]); - } + data->dump(fd); dprintf(fd, "\n"); } +void JankTracker::dumpFrames(int fd) { + FILE* file = fdopen(fd, "a"); + fprintf(file, "\n\n---PROFILEDATA---\n"); + for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) { + fprintf(file, "%s", FrameInfoNames[i].c_str()); + fprintf(file, ","); + } + for (size_t i = 0; i < mFrames.size(); i++) { + FrameInfo& frame = mFrames[i]; + if (frame[FrameInfoIndex::SyncStart] == 0) { + continue; + } + fprintf(file, "\n"); + for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) { + fprintf(file, "%" PRId64 ",", frame[i]); + } + } + fprintf(file, "\n---PROFILEDATA---\n\n"); + fflush(file); +} + void JankTracker::reset() { - mData->jankTypeCounts.fill(0); - mData->frameCounts.fill(0); - mData->slowFrameCounts.fill(0); - mData->totalFrameCount = 0; - mData->jankFrameCount = 0; - mData->statStartTime = systemTime(CLOCK_MONOTONIC); + mFrames.clear(); + mData->reset(); + (*mGlobalData)->reset(); sFrameStart = Properties::filterOutTestOverhead ? FrameInfoIndex::HandleInputStart : FrameInfoIndex::IntendedVsync; } -uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) { - int pos = percentile * data->totalFrameCount / 100; - int remaining = data->totalFrameCount - pos; - for (int i = data->slowFrameCounts.size() - 1; i >= 0; i--) { - remaining -= data->slowFrameCounts[i]; - if (remaining <= 0) { - return (i * kSlowFrameBucketIntervalMs) + kSlowFrameBucketStartMs; - } - } - for (int i = data->frameCounts.size() - 1; i >= 0; i--) { - remaining -= data->frameCounts[i]; - if (remaining <= 0) { - return frameTimeForFrameCountIndex(i); - } - } - return 0; -} - } /* namespace uirenderer */ } /* namespace android */ |