mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[profiling] CPU Profiling support for iOS (flutter/engine#18087)
See flutter.dev/go/engine-cpu-profiling for details
This commit is contained in:
parent
231942e6f6
commit
b1b1cea351
@ -905,6 +905,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_messa
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm
|
||||
FILE: ../../../flutter/shell/platform/darwin/ios/framework/module.modulemap
|
||||
@ -1223,6 +1225,8 @@ FILE: ../../../flutter/shell/platform/windows/win32_window.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window.h
|
||||
FILE: ../../../flutter/shell/platform/windows/win32_window_unittests.cc
|
||||
FILE: ../../../flutter/shell/platform/windows/window_state.h
|
||||
FILE: ../../../flutter/shell/profiling/sampling_profiler.cc
|
||||
FILE: ../../../flutter/shell/profiling/sampling_profiler.h
|
||||
FILE: ../../../flutter/shell/version/version.cc
|
||||
FILE: ../../../flutter/shell/version/version.h
|
||||
FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart
|
||||
|
||||
@ -219,6 +219,40 @@ void TraceEventInstant0(TraceArg category_group, TraceArg name) {
|
||||
);
|
||||
}
|
||||
|
||||
void TraceEventInstant1(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val) {
|
||||
const char* arg_names[] = {arg1_name};
|
||||
const char* arg_values[] = {arg1_val};
|
||||
FlutterTimelineEvent(name, // label
|
||||
Dart_TimelineGetMicros(), // timestamp0
|
||||
0, // timestamp1_or_async_id
|
||||
Dart_Timeline_Event_Instant, // event type
|
||||
1, // argument_count
|
||||
arg_names, // argument_names
|
||||
arg_values // argument_values
|
||||
);
|
||||
}
|
||||
|
||||
void TraceEventInstant2(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val,
|
||||
TraceArg arg2_name,
|
||||
TraceArg arg2_val) {
|
||||
const char* arg_names[] = {arg1_name, arg2_name};
|
||||
const char* arg_values[] = {arg1_val, arg2_val};
|
||||
FlutterTimelineEvent(name, // label
|
||||
Dart_TimelineGetMicros(), // timestamp0
|
||||
0, // timestamp1_or_async_id
|
||||
Dart_Timeline_Event_Instant, // event type
|
||||
2, // argument_count
|
||||
arg_names, // argument_names
|
||||
arg_values // argument_values
|
||||
);
|
||||
}
|
||||
|
||||
void TraceEventFlowBegin0(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceIDArg id) {
|
||||
@ -322,6 +356,18 @@ void TraceEventAsyncEnd1(TraceArg category_group,
|
||||
|
||||
void TraceEventInstant0(TraceArg category_group, TraceArg name) {}
|
||||
|
||||
void TraceEventInstant1(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val) {}
|
||||
|
||||
void TraceEventInstant2(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val,
|
||||
TraceArg arg2_name,
|
||||
TraceArg arg2_val) {}
|
||||
|
||||
void TraceEventFlowBegin0(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceIDArg id) {}
|
||||
|
||||
@ -28,6 +28,10 @@
|
||||
#define TRACE_EVENT_ASYNC_BEGIN1(a, b, c, d, e) TRACE_ASYNC_BEGIN(a, b, c, d, e)
|
||||
#define TRACE_EVENT_ASYNC_END1(a, b, c, d, e) TRACE_ASYNC_END(a, b, c, d, e)
|
||||
#define TRACE_EVENT_INSTANT0(a, b) TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD)
|
||||
#define TRACE_EVENT_INSTANT1(a, b, k1, v1) \
|
||||
TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD, k1, v1)
|
||||
#define TRACE_EVENT_INSTANT2(a, b, k1, v1, k2, v2) \
|
||||
TRACE_INSTANT(a, b, TRACE_SCOPE_THREAD, k1, v1, k2, v2)
|
||||
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -94,6 +98,14 @@
|
||||
#define TRACE_EVENT_INSTANT0(category_group, name) \
|
||||
::fml::tracing::TraceEventInstant0(category_group, name);
|
||||
|
||||
#define TRACE_EVENT_INSTANT1(category_group, name, arg1_name, arg1_val) \
|
||||
::fml::tracing::TraceEventInstant1(category_group, name, arg1_name, arg1_val);
|
||||
|
||||
#define TRACE_EVENT_INSTANT2(category_group, name, arg1_name, arg1_val, \
|
||||
arg2_name, arg2_val) \
|
||||
::fml::tracing::TraceEventInstant2(category_group, name, arg1_name, \
|
||||
arg1_val, arg2_name, arg2_val);
|
||||
|
||||
#define TRACE_FLOW_BEGIN(category, name, id) \
|
||||
::fml::tracing::TraceEventFlowBegin0(category, name, id);
|
||||
|
||||
@ -272,6 +284,18 @@ void TraceEventAsyncEnd1(TraceArg category_group,
|
||||
|
||||
void TraceEventInstant0(TraceArg category_group, TraceArg name);
|
||||
|
||||
void TraceEventInstant1(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val);
|
||||
|
||||
void TraceEventInstant2(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceArg arg1_name,
|
||||
TraceArg arg1_val,
|
||||
TraceArg arg2_name,
|
||||
TraceArg arg2_val);
|
||||
|
||||
void TraceEventFlowBegin0(TraceArg category_group,
|
||||
TraceArg name,
|
||||
TraceIDArg id);
|
||||
|
||||
@ -126,6 +126,7 @@ source_set("common") {
|
||||
"//flutter/fml",
|
||||
"//flutter/lib/ui",
|
||||
"//flutter/runtime",
|
||||
"//flutter/shell/profiling",
|
||||
"//third_party/dart/runtime:dart_api",
|
||||
"//third_party/skia",
|
||||
]
|
||||
|
||||
@ -26,6 +26,10 @@ ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
|
||||
if (mask & ThreadHost::Type::IO) {
|
||||
io_thread = std::make_unique<fml::Thread>(name_prefix + ".io");
|
||||
}
|
||||
|
||||
if (mask & ThreadHost::Type::Profiler) {
|
||||
profiler_thread = std::make_unique<fml::Thread>(name_prefix + ".profiler");
|
||||
}
|
||||
}
|
||||
|
||||
ThreadHost::~ThreadHost() = default;
|
||||
@ -35,6 +39,7 @@ void ThreadHost::Reset() {
|
||||
ui_thread.reset();
|
||||
raster_thread.reset();
|
||||
io_thread.reset();
|
||||
profiler_thread.reset();
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -19,12 +19,14 @@ struct ThreadHost {
|
||||
UI = 1 << 1,
|
||||
GPU = 1 << 2,
|
||||
IO = 1 << 3,
|
||||
Profiler = 1 << 4,
|
||||
};
|
||||
|
||||
std::unique_ptr<fml::Thread> platform_thread;
|
||||
std::unique_ptr<fml::Thread> ui_thread;
|
||||
std::unique_ptr<fml::Thread> raster_thread;
|
||||
std::unique_ptr<fml::Thread> io_thread;
|
||||
std::unique_ptr<fml::Thread> profiler_thread;
|
||||
|
||||
ThreadHost();
|
||||
|
||||
|
||||
@ -79,6 +79,8 @@ source_set("flutter_framework_source") {
|
||||
"framework/Source/platform_message_response_darwin.mm",
|
||||
"framework/Source/platform_message_router.h",
|
||||
"framework/Source/platform_message_router.mm",
|
||||
"framework/Source/profiler_metrics_ios.h",
|
||||
"framework/Source/profiler_metrics_ios.mm",
|
||||
"framework/Source/vsync_waiter_ios.h",
|
||||
"framework/Source/vsync_waiter_ios.mm",
|
||||
"ios_context.h",
|
||||
@ -131,6 +133,7 @@ source_set("flutter_framework_source") {
|
||||
"//flutter/shell/common",
|
||||
"//flutter/shell/platform/darwin/common",
|
||||
"//flutter/shell/platform/darwin/common:framework_shared",
|
||||
"//flutter/shell/profiling:profiling",
|
||||
"//third_party/skia",
|
||||
]
|
||||
|
||||
|
||||
@ -24,11 +24,14 @@
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
|
||||
#import "flutter/shell/platform/darwin/ios/ios_surface.h"
|
||||
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
|
||||
#include "flutter/shell/platform/darwin/ios/rendering_api_selection.h"
|
||||
#include "flutter/shell/profiling/sampling_profiler.h"
|
||||
|
||||
NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
static constexpr int kNumProfilerSamplesPerSec = 5;
|
||||
|
||||
@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
|
||||
@property(nonatomic, assign) FlutterEngine* flutterEngine;
|
||||
@ -56,6 +59,8 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher;
|
||||
|
||||
std::unique_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
|
||||
std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
|
||||
std::unique_ptr<flutter::SamplingProfiler> _profiler;
|
||||
|
||||
// Channels
|
||||
fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
|
||||
@ -262,6 +267,7 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
[self resetChannels];
|
||||
self.isolateId = nil;
|
||||
_shell.reset();
|
||||
_profiler.reset();
|
||||
_threadHost.Reset();
|
||||
_platformViewsController.reset();
|
||||
}
|
||||
@ -319,6 +325,14 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
_settingsChannel.reset();
|
||||
}
|
||||
|
||||
- (void)startProfiler {
|
||||
_profiler_metrics = std::make_unique<flutter::ProfilerMetricsIOS>();
|
||||
_profiler = std::make_unique<flutter::SamplingProfiler>(
|
||||
_threadHost.profiler_thread->GetTaskRunner(),
|
||||
[self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
|
||||
_profiler->Start();
|
||||
}
|
||||
|
||||
// If you add a channel, be sure to also update `resetChannels`.
|
||||
// Channels get a reference to the engine, and therefore need manual
|
||||
// cleanup for proper collection.
|
||||
@ -438,9 +452,18 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
// initialized.
|
||||
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
||||
|
||||
uint32_t threadHostType = flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::GPU |
|
||||
flutter::ThreadHost::Type::IO;
|
||||
bool profilerEnabled = false;
|
||||
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) || \
|
||||
(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_PROFILE)
|
||||
profilerEnabled = true;
|
||||
#endif
|
||||
if (profilerEnabled) {
|
||||
threadHostType = threadHostType | flutter::ThreadHost::Type::Profiler;
|
||||
}
|
||||
_threadHost = {threadLabel.UTF8String, // label
|
||||
flutter::ThreadHost::Type::UI | flutter::ThreadHost::Type::GPU |
|
||||
flutter::ThreadHost::Type::IO};
|
||||
threadHostType};
|
||||
|
||||
// Lambda captures by pointers to ObjC objects are fine here because the
|
||||
// create call is
|
||||
@ -456,6 +479,10 @@ NSString* const FlutterDefaultDartEntrypoint = nil;
|
||||
return std::make_unique<flutter::Rasterizer>(shell, shell.GetTaskRunners());
|
||||
};
|
||||
|
||||
if (profilerEnabled) {
|
||||
[self startProfiler];
|
||||
}
|
||||
|
||||
if (flutter::IsIosEmbeddedViewsPreviewEnabled()) {
|
||||
// Embedded views requires the gpu and the platform views to be the same.
|
||||
// The plan is to eventually dynamically merge the threads when there's a
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PROFILER_METRICS_IOS_H_
|
||||
#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PROFILER_METRICS_IOS_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/shell/profiling/sampling_profiler.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
/**
|
||||
* @brief Utility class that gathers profiling metrics used by
|
||||
* `flutter::SamplingProfiler`.
|
||||
*
|
||||
* @see flutter::SamplingProfiler
|
||||
*/
|
||||
class ProfilerMetricsIOS {
|
||||
public:
|
||||
ProfilerMetricsIOS() = default;
|
||||
|
||||
ProfileSample GenerateSample();
|
||||
|
||||
private:
|
||||
std::optional<CpuUsageInfo> CpuUsage();
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ProfilerMetricsIOS);
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PROFILER_METRICS_IOS_H_
|
||||
@ -0,0 +1,79 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// RAII holder for `thread_array_t` this is so any early returns in
|
||||
// `ProfilerMetricsIOS::CpuUsage` don't leak them.
|
||||
class MachThreads {
|
||||
public:
|
||||
thread_array_t threads = NULL;
|
||||
mach_msg_type_number_t thread_count = 0;
|
||||
|
||||
MachThreads() = default;
|
||||
|
||||
~MachThreads() {
|
||||
kern_return_t kernel_return_code = vm_deallocate(
|
||||
mach_task_self(), reinterpret_cast<vm_offset_t>(threads), thread_count * sizeof(thread_t));
|
||||
FML_CHECK(kernel_return_code == KERN_SUCCESS) << "Failed to deallocate thread infos.";
|
||||
}
|
||||
|
||||
private:
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(MachThreads);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace flutter {
|
||||
|
||||
ProfileSample ProfilerMetricsIOS::GenerateSample() {
|
||||
return {.cpu_usage = CpuUsage()};
|
||||
}
|
||||
|
||||
std::optional<CpuUsageInfo> ProfilerMetricsIOS::CpuUsage() {
|
||||
kern_return_t kernel_return_code;
|
||||
MachThreads mach_threads = MachThreads();
|
||||
|
||||
// Get threads in the task
|
||||
kernel_return_code =
|
||||
task_threads(mach_task_self(), &mach_threads.threads, &mach_threads.thread_count);
|
||||
if (kernel_return_code != KERN_SUCCESS) {
|
||||
FML_LOG(ERROR) << "Error retrieving task information: "
|
||||
<< mach_error_string(kernel_return_code);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
double total_cpu_usage = 0.0;
|
||||
|
||||
// Add the CPU usage for each thread. It should be noted that there may be some CPU usage missing
|
||||
// from this calculation. If a thread ends between calls to this routine, then its info will be
|
||||
// lost. We could solve this by installing a callback using pthread_key_create. The callback would
|
||||
// report the thread is ending and allow the code to get the CPU usage. But we need to call
|
||||
// pthread_setspecific in each thread to set the key's value to a non-null value for the callback
|
||||
// to work. If we really need this information and if we have a good mechanism for calling
|
||||
// pthread_setspecific in every thread, then we can include that value in the CPU usage.
|
||||
for (mach_msg_type_number_t i = 0; i < mach_threads.thread_count; i++) {
|
||||
thread_basic_info_data_t basic_thread_info;
|
||||
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
|
||||
kernel_return_code =
|
||||
thread_info(mach_threads.threads[i], THREAD_BASIC_INFO,
|
||||
reinterpret_cast<thread_info_t>(&basic_thread_info), &thread_info_count);
|
||||
if (kernel_return_code != KERN_SUCCESS) {
|
||||
FML_LOG(ERROR) << "Error retrieving thread information: "
|
||||
<< mach_error_string(kernel_return_code);
|
||||
return std::nullopt;
|
||||
}
|
||||
const double current_thread_cpu_usage =
|
||||
basic_thread_info.cpu_usage / static_cast<float>(TH_USAGE_SCALE);
|
||||
total_cpu_usage += current_thread_cpu_usage;
|
||||
}
|
||||
|
||||
flutter::CpuUsageInfo cpu_usage_info = {.num_threads = mach_threads.thread_count,
|
||||
.total_cpu_usage = total_cpu_usage * 100.0};
|
||||
return cpu_usage_info;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
19
engine/src/flutter/shell/profiling/BUILD.gn
Normal file
19
engine/src/flutter/shell/profiling/BUILD.gn
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("//flutter/shell/config.gni")
|
||||
|
||||
_profiler_deps = [
|
||||
"//flutter/common",
|
||||
"//flutter/fml",
|
||||
]
|
||||
|
||||
source_set("profiling") {
|
||||
sources = [
|
||||
"sampling_profiler.cc",
|
||||
"sampling_profiler.h",
|
||||
]
|
||||
|
||||
deps = _profiler_deps
|
||||
}
|
||||
49
engine/src/flutter/shell/profiling/sampling_profiler.cc
Normal file
49
engine/src/flutter/shell/profiling/sampling_profiler.cc
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/profiling/sampling_profiler.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
SamplingProfiler::SamplingProfiler(
|
||||
fml::RefPtr<fml::TaskRunner> profiler_task_runner,
|
||||
Sampler sampler,
|
||||
int num_samples_per_sec)
|
||||
: profiler_task_runner_(profiler_task_runner),
|
||||
sampler_(std::move(sampler)),
|
||||
num_samples_per_sec_(num_samples_per_sec) {}
|
||||
|
||||
void SamplingProfiler::Start() const {
|
||||
if (!profiler_task_runner_) {
|
||||
return;
|
||||
}
|
||||
FML_CHECK(num_samples_per_sec_ > 0)
|
||||
<< "number of samples must be a positive integer, got: "
|
||||
<< num_samples_per_sec_;
|
||||
double delay_between_samples = 1.0 / num_samples_per_sec_;
|
||||
auto task_delay = fml::TimeDelta::FromSecondsF(delay_between_samples);
|
||||
SampleRepeatedly(task_delay);
|
||||
}
|
||||
|
||||
void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const {
|
||||
profiler_task_runner_->PostDelayedTask(
|
||||
[profiler = this, task_delay = task_delay, sampler = sampler_]() {
|
||||
const ProfileSample usage = sampler();
|
||||
if (usage.cpu_usage) {
|
||||
const auto& cpu_usage = usage.cpu_usage;
|
||||
// TODO(kaushikiska): consider buffering these every n seconds to
|
||||
// avoid spamming the trace buffer.
|
||||
std::string total_cpu_usage =
|
||||
std::to_string(cpu_usage->total_cpu_usage);
|
||||
std::string num_threads = std::to_string(cpu_usage->num_threads);
|
||||
TRACE_EVENT_INSTANT2("flutter::profiling", "CpuUsage",
|
||||
"total_cpu_usage", total_cpu_usage.c_str(),
|
||||
"num_threads", num_threads.c_str());
|
||||
}
|
||||
profiler->SampleRepeatedly(task_delay);
|
||||
},
|
||||
task_delay);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
92
engine/src/flutter/shell/profiling/sampling_profiler.h
Normal file
92
engine/src/flutter/shell/profiling/sampling_profiler.h
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_SHELL_PROFILING_SAMPLING_PROFILER_H_
|
||||
#define FLUTTER_SHELL_PROFILING_SAMPLING_PROFILER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/fml/task_runner.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
/**
|
||||
* @brief CPU usage stats. `num_threads` is the number of threads owned by the
|
||||
* process. It is to be noted that this is not per shell, there can be multiple
|
||||
* shells within the process. `total_cpu_usage` is the percentage (between [0,
|
||||
* 100]) cpu usage of the application. This is across all the cores, for example
|
||||
* an application using 100% of all the core will report `total_cpu_usage` as
|
||||
* `100`, if it has 100% across 2 cores and 0% across the other cores, embedder
|
||||
* must report `total_cpu_usage` as `50`.
|
||||
*/
|
||||
struct CpuUsageInfo {
|
||||
uint32_t num_threads;
|
||||
double total_cpu_usage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Container for the metrics we collect during each run of `Sampler`.
|
||||
* This currently holds `CpuUsageInfo` but the intent is to expand it to other
|
||||
* metrics.
|
||||
*
|
||||
* @see flutter::Sampler
|
||||
*/
|
||||
struct ProfileSample {
|
||||
std::optional<CpuUsageInfo> cpu_usage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sampler is run during `SamplingProfiler::SampleRepeatedly`. Each
|
||||
* platform should implement its version of a `Sampler` if they decide to
|
||||
* participate in gathering profiling metrics.
|
||||
*
|
||||
* @see flutter::SamplingProfiler::SampleRepeatedly
|
||||
*/
|
||||
using Sampler = std::function<ProfileSample(void)>;
|
||||
|
||||
/**
|
||||
* @brief a Sampling Profiler that runs peridically and calls the `Sampler`
|
||||
* which servers as a value function to gather various profiling metrics as
|
||||
* represented by `ProfileSample`. These profiling metrics are then posted to
|
||||
* the observatory timeline.
|
||||
*
|
||||
*/
|
||||
class SamplingProfiler {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Sampling Profiler object
|
||||
*
|
||||
* @param profiler_task_runner the task runner to service sampling requests.
|
||||
* @param sampler the value function to collect the profiling metrics.
|
||||
* @param num_samples_per_sec number of times you wish to run the sampler per
|
||||
* second.
|
||||
*
|
||||
* @see fml::TaskRunner
|
||||
*/
|
||||
SamplingProfiler(fml::RefPtr<fml::TaskRunner> profiler_task_runner,
|
||||
Sampler sampler,
|
||||
int num_samples_per_sec);
|
||||
|
||||
/**
|
||||
* @brief Starts the SamplingProfiler by triggering `SampleRepeatedly`.
|
||||
*
|
||||
*/
|
||||
void Start() const;
|
||||
|
||||
private:
|
||||
const fml::RefPtr<fml::TaskRunner> profiler_task_runner_;
|
||||
const Sampler sampler_;
|
||||
const uint32_t num_samples_per_sec_;
|
||||
|
||||
void SampleRepeatedly(fml::TimeDelta task_delay) const;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(SamplingProfiler);
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_SHELL_PROFILING_SAMPLING_PROFILER_H_
|
||||
Loading…
x
Reference in New Issue
Block a user