Commit b6516b0e authored by Mustafa Camurcu's avatar Mustafa Camurcu Committed by Commit Bot

arc: Add jankiness metrics to UMA

Uses GFX metrics to trace and report frame jankiness to UMA.
Traces are done in 5 minute intervals.

BUG=b:169211158
TEST=Manually tested on PlayStore and other apps and looked at results.

Change-Id: I87a381193bc9d0dbd00d81386cc4c0396c9afdff
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2430505
Commit-Queue: Ilya Sherman <isherman@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Reviewed-by: default avatarYury Khmel <khmel@chromium.org>
Auto-Submit: Mustafa Camurcu <camurcu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813015}
parent 52f53335
...@@ -5,15 +5,19 @@ ...@@ -5,15 +5,19 @@
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_custom_session.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_custom_session.h"
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_session.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_session.h"
#include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_uma_session.h" #include "chrome/browser/chromeos/arc/tracing/arc_app_performance_tracing_uma_session.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs_factory.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h" #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_features.h" #include "components/arc/arc_features.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h" #include "components/arc/arc_util.h"
#include "components/arc/session/arc_bridge_service.h"
#include "components/exo/wm_helper.h" #include "components/exo/wm_helper.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
...@@ -21,6 +25,13 @@ namespace arc { ...@@ -21,6 +25,13 @@ namespace arc {
namespace { namespace {
// Tracing delay for jankinees.
constexpr base::TimeDelta kJankinessTracingTime =
base::TimeDelta::FromMinutes(5);
// Minimum number of frames for a jankiness tracing result to be valid.
constexpr int kMinTotalFramesJankiness = 1000;
// Singleton factory for ArcAppPerformanceTracing. // Singleton factory for ArcAppPerformanceTracing.
class ArcAppPerformanceTracingFactory class ArcAppPerformanceTracingFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase< : public internal::ArcBrowserContextKeyedServiceFactoryBase<
...@@ -124,6 +135,8 @@ void ArcAppPerformanceTracing::SetCustomSessionReadyCallbackForTesting( ...@@ -124,6 +135,8 @@ void ArcAppPerformanceTracing::SetCustomSessionReadyCallbackForTesting(
} }
void ArcAppPerformanceTracing::Shutdown() { void ArcAppPerformanceTracing::Shutdown() {
CancelJankinessTracing();
MaybeStopTracing(); MaybeStopTracing();
// |session_|. Make sure that |arc_active_window_| is detached. // |session_|. Make sure that |arc_active_window_| is detached.
...@@ -160,6 +173,9 @@ void ArcAppPerformanceTracing::OnWindowActivated(ActivationReason reason, ...@@ -160,6 +173,9 @@ void ArcAppPerformanceTracing::OnWindowActivated(ActivationReason reason,
// Discard any active tracing if any. // Discard any active tracing if any.
MaybeStopTracing(); MaybeStopTracing();
// Stop and report previous active window's jankiness tracing so far.
FinalizeJankinessTracing(true /* stopped_early */);
// Detach previous active window if it is set. // Detach previous active window if it is set.
DetachActiveWindow(); DetachActiveWindow();
...@@ -170,6 +186,8 @@ void ArcAppPerformanceTracing::OnWindowActivated(ActivationReason reason, ...@@ -170,6 +186,8 @@ void ArcAppPerformanceTracing::OnWindowActivated(ActivationReason reason,
// Observe active ARC++ window. // Observe active ARC++ window.
AttachActiveWindow(gained_active); AttachActiveWindow(gained_active);
StartJankinessTracing();
MaybeStartTracing(); MaybeStartTracing();
} }
...@@ -177,6 +195,8 @@ void ArcAppPerformanceTracing::OnWindowDestroying(aura::Window* window) { ...@@ -177,6 +195,8 @@ void ArcAppPerformanceTracing::OnWindowDestroying(aura::Window* window) {
// ARC++ window will be destroyed. // ARC++ window will be destroyed.
DCHECK_EQ(arc_active_window_, window); DCHECK_EQ(arc_active_window_, window);
CancelJankinessTracing();
MaybeStopTracing(); MaybeStopTracing();
DetachActiveWindow(); DetachActiveWindow();
...@@ -187,7 +207,7 @@ void ArcAppPerformanceTracing::OnTaskCreated(int32_t task_id, ...@@ -187,7 +207,7 @@ void ArcAppPerformanceTracing::OnTaskCreated(int32_t task_id,
const std::string& activity, const std::string& activity,
const std::string& intent) { const std::string& intent) {
const std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity); const std::string app_id = ArcAppListPrefs::GetAppId(package_name, activity);
task_id_to_app_id_[task_id] = app_id; task_id_to_app_id_[task_id] = std::make_pair(app_id, package_name);
MaybeStartTracing(); MaybeStartTracing();
} }
...@@ -195,6 +215,109 @@ void ArcAppPerformanceTracing::OnTaskDestroyed(int32_t task_id) { ...@@ -195,6 +215,109 @@ void ArcAppPerformanceTracing::OnTaskDestroyed(int32_t task_id) {
task_id_to_app_id_.erase(task_id); task_id_to_app_id_.erase(task_id);
} }
void ArcAppPerformanceTracing::StartJankinessTracing() {
DCHECK(!jankiness_timer_.IsRunning());
jankiness_timer_.Start(
FROM_HERE, kJankinessTracingTime,
base::BindOnce(&ArcAppPerformanceTracing::FinalizeJankinessTracing,
base::Unretained(this), false /* stopped_early */));
}
void ArcAppPerformanceTracing::CancelJankinessTracing() {
jankiness_timer_.Stop();
}
void ArcAppPerformanceTracing::FinalizeJankinessTracing(bool stopped_early) {
// Never started. Nothing to do.
if (!jankiness_timer_.IsRunning() && stopped_early)
return;
jankiness_timer_.Stop();
// Check if we have all conditions met, ARC++ window is active and information
// is available for associated task.
if (!arc_active_window_)
return;
const int32_t task_id = arc::GetWindowTaskId(arc_active_window_);
DCHECK_GT(task_id, 0);
const auto it = task_id_to_app_id_.find(task_id);
if (it == task_id_to_app_id_.end())
// It is normal that information might not be available at this time.
return;
// Test instances might not have Service Manager running.
auto* arc_service_manager = ArcServiceManager::Get();
if (!arc_service_manager)
return;
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_service_manager->arc_bridge_service()->metrics(), GetGfxMetrics);
if (!instance)
return;
const std::string package_name = it->second.second;
auto callback = base::BindOnce(&ArcAppPerformanceTracing::OnGfxMetrics,
base::Unretained(this), package_name);
instance->GetGfxMetrics(package_name, std::move(callback));
// Finalized normally, safe to restart.
if (!stopped_early)
StartJankinessTracing();
}
void ArcAppPerformanceTracing::OnGfxMetrics(const std::string& package_name,
mojom::GfxMetricsPtr metrics) {
if (!metrics) {
LOG(ERROR) << "Failed to resolve GFX metrics";
return;
}
uint64_t framesTotal = metrics->framesTotal;
uint64_t framesJanky = metrics->framesJanky;
const uint32_t frameTime95 = metrics->frameTimePercentile95; // in ms.
const auto it = package_name_to_gfx_metrics_.find(package_name);
const bool first_time = it == package_name_to_gfx_metrics_.end();
// Cached data exists and not outdated. Calculate delta.
if (!first_time && it->second.framesTotal <= framesTotal) {
framesTotal -= it->second.framesTotal;
framesJanky -= it->second.framesJanky;
}
// Update cache.
package_name_to_gfx_metrics_[package_name] = *metrics;
// Not enough data.
if (framesTotal < kMinTotalFramesJankiness) {
VLOG(1) << "Not enough GFX metrics data collected to report.";
return;
}
// We can only calculate real numbers for initial data. Only report if first
// time.
if (first_time) {
const base::TimeDelta frameTime =
base::TimeDelta::FromMilliseconds(frameTime95);
base::UmaHistogramTimes("Arc.Runtime.Performance.Generic.FrameTime",
frameTime);
VLOG(1) << "Total Frames: " << framesTotal << " | "
<< "Janky Frames: " << framesJanky << " | "
<< "95 Percentile Frame Time: " << frameTime.InMilliseconds()
<< "ms";
} else {
VLOG(1) << "Total Frames: " << framesTotal << " | "
<< "Janky Frames: " << framesJanky;
}
const int jankiness = (framesJanky * 100) / framesTotal;
base::UmaHistogramPercentage("Arc.Runtime.Performance.Generic.Jankiness",
jankiness);
}
bool ArcAppPerformanceTracing::WasReported(const std::string& category) const { bool ArcAppPerformanceTracing::WasReported(const std::string& category) const {
DCHECK(!category.empty()); DCHECK(!category.empty());
return reported_categories_.count(category); return reported_categories_.count(category);
...@@ -226,8 +349,8 @@ void ArcAppPerformanceTracing::MaybeStartTracing() { ...@@ -226,8 +349,8 @@ void ArcAppPerformanceTracing::MaybeStartTracing() {
return; return;
} }
const std::string& category = const std::string& category = AppToCategoryMapper::GetInstance().GetCategory(
AppToCategoryMapper::GetInstance().GetCategory(it->second /* app_id */); it->second.first /* app_id */);
if (category.empty()) { if (category.empty()) {
// App is not recognized as app for tracing, ignore it. // App is not recognized as app for tracing, ignore it.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "components/arc/mojom/metrics.mojom.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_change_observer.h"
...@@ -33,7 +34,7 @@ class ArcAppPerformanceTracingSession; ...@@ -33,7 +34,7 @@ class ArcAppPerformanceTracingSession;
class ArcBridgeService; class ArcBridgeService;
// Service that monitors ARC++ apps, measures and reports performance metrics // Service that monitors ARC++ apps, measures and reports performance metrics
// for the set of predefined apps. // for the set of predefined apps. Also report GFX metrics jankiness results.
class ArcAppPerformanceTracing : public KeyedService, class ArcAppPerformanceTracing : public KeyedService,
public wm::ActivationChangeObserver, public wm::ActivationChangeObserver,
public aura::WindowObserver, public aura::WindowObserver,
...@@ -121,13 +122,35 @@ class ArcAppPerformanceTracing : public KeyedService, ...@@ -121,13 +122,35 @@ class ArcAppPerformanceTracing : public KeyedService,
// |arc_active_window_|. // |arc_active_window_|.
void DetachActiveWindow(); void DetachActiveWindow();
// Starts timer for jankiness tracing. Called by OnWindowActivation() and
// FinalizeJankinessTracing().
void StartJankinessTracing();
// Cancels jankiness tracing without reporting partial results.
void CancelJankinessTracing();
// Retrieves and reports jankiness metrics and restarts timer. May be called
// early by OnWindowActivation() and OnWindowDestroying().
// In this case, |stopped_early| is set to true.
void FinalizeJankinessTracing(bool stopped_early);
// Callback for jankiness results. Reports results to UMA.
// Note: Results are cumulative. Uses task_id_to_gfx_metrics_
// values for delta calculations.
void OnGfxMetrics(const std::string& package_name,
mojom::GfxMetricsPtr metrics_ptr);
// Unowned pointers. // Unowned pointers.
content::BrowserContext* const context_; content::BrowserContext* const context_;
// Currently active ARC++ app window. // Currently active ARC++ app window.
aura::Window* arc_active_window_ = nullptr; aura::Window* arc_active_window_ = nullptr;
// Maps active tasks to app id. // Maps active tasks to app id and package name.
std::map<int, std::string> task_id_to_app_id_; std::map<int, std::pair<std::string, std::string>> task_id_to_app_id_;
// Maps tasks to most recent GFX jankiness results. Used for delta
// calculation.
std::map<std::string, mojom::GfxMetrics> package_name_to_gfx_metrics_;
// Set of already reported ARC++ apps for the current session. Used to prevent // Set of already reported ARC++ apps for the current session. Used to prevent
// capturing too frequently. // capturing too frequently.
...@@ -139,6 +162,9 @@ class ArcAppPerformanceTracing : public KeyedService, ...@@ -139,6 +162,9 @@ class ArcAppPerformanceTracing : public KeyedService,
// Callback to call when custom session is ready for testing. // Callback to call when custom session is ready for testing.
CustomSessionReadyCallback custom_session_ready_callback_; CustomSessionReadyCallback custom_session_ready_callback_;
// Timer for jankiness tracing.
base::OneShotTimer jankiness_timer_;
DISALLOW_COPY_AND_ASSIGN(ArcAppPerformanceTracing); DISALLOW_COPY_AND_ASSIGN(ArcAppPerformanceTracing);
}; };
......
...@@ -874,7 +874,7 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -874,7 +874,7 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" -->
<owner>khmel@google.com</owner> <owner>khmel@google.com</owner>
<owner>skuhne@google.com</owner> <owner>camurcu@google.com</owner>
<summary>Standard deviation for commit time delta from ideal time.</summary> <summary>Standard deviation for commit time delta from ideal time.</summary>
</histogram> </histogram>
...@@ -883,16 +883,36 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -883,16 +883,36 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" -->
<owner>khmel@google.com</owner> <owner>khmel@google.com</owner>
<owner>skuhne@google.com</owner> <owner>camurcu@google.com</owner>
<summary>Render frames per second.</summary> <summary>Render frames per second.</summary>
</histogram> </histogram>
<histogram name="Arc.Runtime.Performance.Generic.FrameTime" units="ms"
expires_after="2021-09-28">
<owner>camurcu@google.com</owner>
<owner>khmel@google.com</owner>
<summary>
95 percent of the frames in the first 5 minutes after app launch took
shorter time (in ms) than this value.
</summary>
</histogram>
<histogram name="Arc.Runtime.Performance.Generic.Jankiness" units="%"
expires_after="2021-09-28">
<owner>camurcu@google.com</owner>
<owner>khmel@google.com</owner>
<summary>
Percentage ratio of janky frames to total frames recorded in a 5 minute
interval.
</summary>
</histogram>
<histogram name="Arc.Runtime.Performance.RenderQuality" units="%" <histogram name="Arc.Runtime.Performance.RenderQuality" units="%"
expires_after="2021-03-11"> expires_after="2021-03-11">
<!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" --> <!-- Name completed by histogram_suffixes name="ArcPerformanceAppCategories" -->
<owner>khmel@google.com</owner> <owner>khmel@google.com</owner>
<owner>skuhne@google.com</owner> <owner>camurcu@google.com</owner>
<summary>Render quality with maximum 100%.</summary> <summary>Render quality with maximum 100%.</summary>
</histogram> </histogram>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment