-
Notifications
You must be signed in to change notification settings - Fork 6k
[profiling] CPU Profiling support for iOS #18087
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Profiling applies to all shells in the process. Ideally, there would only be one profiler in the process at any given time with the shells having to ability to turn this on or off. How about putting this thread by the concurrent message loop threads in DartVM (which is also process global) or just using the one of the threads in the concurrent thread pool?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chinmaygarde I agree that there only needs to be one profiler for all the shells. Moving this to the concurrent thread loop is a more involved change, given that the concurrent thread loops don't have scheduling mechanisms (at fixed delays). Mind if I file an issue for this and address it in a follow up change. I think this will be useful as-is.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. |
||
| } | ||
|
|
||
| } // namespace flutter | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that we can also do
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would make failing early by returning |
||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TRACE_EVENT_INSTANT1seems to be unused.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's nice to have 0, 1 and 2 rather than just 0 and 2. If you feel strongly about this, I can remove it.