mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
1 Commits
roller/nod
...
robo/explo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01130e5c9f |
@@ -148,6 +148,10 @@ filenames = {
|
||||
"shell/browser/mac/in_app_purchase_product.mm",
|
||||
"shell/browser/mac/in_app_purchase.h",
|
||||
"shell/browser/mac/in_app_purchase.mm",
|
||||
"shell/browser/mac/samply_profiler_service.h",
|
||||
"shell/browser/mac/samply_profiler_service.mm",
|
||||
"shell/common/mac/samply_profiler_client.h",
|
||||
"shell/common/mac/samply_profiler_client.mm",
|
||||
"shell/browser/native_window_mac.h",
|
||||
"shell/browser/native_window_mac.mm",
|
||||
"shell/browser/notifications/mac/cocoa_notification.h",
|
||||
|
||||
@@ -144,3 +144,4 @@ fix_linux_tray_id.patch
|
||||
expose_gtk_ui_platform_field.patch
|
||||
fix_os_crypt_async_cookie_encryption.patch
|
||||
patch_osr_control_screen_info.patch
|
||||
feat_support_passing_embedder_machrendezvous_ports_to_child.patch
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Sat, 31 Jan 2026 01:33:22 +0900
|
||||
Subject: feat: support passing embedder machrendezvous ports to child
|
||||
|
||||
Used for samply profiler integration
|
||||
|
||||
diff --git a/base/apple/mach_port_rendezvous_mac.cc b/base/apple/mach_port_rendezvous_mac.cc
|
||||
index a267d6efd2270f3d17e6c2e4ab10f26f77fd7bdf..94778ab1fe3f8f50e3e17f279fb7b0259ca19c9e 100644
|
||||
--- a/base/apple/mach_port_rendezvous_mac.cc
|
||||
+++ b/base/apple/mach_port_rendezvous_mac.cc
|
||||
@@ -121,6 +121,8 @@ void MachPortRendezvousServerMac::RegisterPortsForPid(
|
||||
lock_.AssertAcquired();
|
||||
DCHECK_LT(ports.size(), internal::kMaximumRendezvousPorts);
|
||||
DCHECK(!ports.empty());
|
||||
+ VLOG(1) << "MachPortRendezvousServerMac::RegisterPortsForPid: pid=" << pid
|
||||
+ << ", num_ports=" << ports.size();
|
||||
|
||||
ClientData& client = ClientDataForPid(pid);
|
||||
CHECK(client.ports.empty());
|
||||
@@ -149,9 +151,16 @@ MachPortRendezvousServerMac::ClientData::~ClientData() {
|
||||
MachPortRendezvousServerMac::MachPortRendezvousServerMac() {
|
||||
std::string bootstrap_name =
|
||||
StringPrintf(kBootstrapNameFormat, apple::BaseBundleID(), getpid());
|
||||
+ VLOG(1) << "MachPortRendezvousServerMac: Registering bootstrap service: "
|
||||
+ << bootstrap_name;
|
||||
kern_return_t kr = bootstrap_check_in(
|
||||
bootstrap_port, bootstrap_name.c_str(),
|
||||
apple::ScopedMachReceiveRight::Receiver(server_port_).get());
|
||||
+ if (kr == KERN_SUCCESS) {
|
||||
+ VLOG(1) << "MachPortRendezvousServerMac: bootstrap_check_in succeeded";
|
||||
+ } else {
|
||||
+ LOG(ERROR) << "MachPortRendezvousServerMac: bootstrap_check_in FAILED: " << kr;
|
||||
+ }
|
||||
BOOTSTRAP_CHECK(kr == KERN_SUCCESS, kr)
|
||||
<< "bootstrap_check_in " << bootstrap_name;
|
||||
dispatch_source_ = std::make_unique<apple::DispatchSource>(
|
||||
@@ -159,6 +168,7 @@ MachPortRendezvousServerMac::MachPortRendezvousServerMac() {
|
||||
HandleRequest();
|
||||
});
|
||||
dispatch_source_->Resume();
|
||||
+ VLOG(1) << "MachPortRendezvousServerMac: Server ready, PID=" << getpid();
|
||||
}
|
||||
|
||||
MachPortRendezvousServerMac::~MachPortRendezvousServerMac() = default;
|
||||
@@ -245,6 +255,8 @@ bool MachPortRendezvousClientMac::AcquirePorts() {
|
||||
|
||||
apple::ScopedMachSendRight server_port;
|
||||
std::string bootstrap_name = GetBootstrapName();
|
||||
+ VLOG(1) << "MachPortRendezvousClientMac::AcquirePorts: PID=" << getpid()
|
||||
+ << ", PPID=" << getppid() << ", looking up: " << bootstrap_name;
|
||||
kern_return_t kr = bootstrap_look_up(
|
||||
bootstrap_port, const_cast<char*>(bootstrap_name.c_str()),
|
||||
apple::ScopedMachSendRight::Receiver(server_port).get());
|
||||
@@ -252,6 +264,7 @@ bool MachPortRendezvousClientMac::AcquirePorts() {
|
||||
BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " << bootstrap_name;
|
||||
return false;
|
||||
}
|
||||
+ VLOG(1) << "MachPortRendezvousClientMac::AcquirePorts: lookup succeeded";
|
||||
|
||||
mach_msg_id_t message_id = internal::kMachRendezvousMsgIdRequest;
|
||||
size_t additional_data_size = 0;
|
||||
diff --git a/base/process/launch.h b/base/process/launch.h
|
||||
index 59eac5dc8c61b5c2df58d467149d4f3a9936611d..8b3622a57894d58eb193fdb6e4376578275df93a 100644
|
||||
--- a/base/process/launch.h
|
||||
+++ b/base/process/launch.h
|
||||
@@ -40,6 +40,7 @@
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
+#include "base/apple/scoped_mach_port.h"
|
||||
#include "base/mac/process_requirement.h"
|
||||
#endif
|
||||
|
||||
diff --git a/base/process/launch_mac.cc b/base/process/launch_mac.cc
|
||||
index 8387fd7d2bcf8951b6cc024829c16d970799190c..c4681b8f9d4020039ef7907ffc8e8c1bf2882478 100644
|
||||
--- a/base/process/launch_mac.cc
|
||||
+++ b/base/process/launch_mac.cc
|
||||
@@ -81,6 +81,7 @@ int posix_spawnattr_set_csm_np(const posix_spawnattr_t*, uint32_t)
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/process/environment_internal.h"
|
||||
+#include "base/synchronization/lock.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
@@ -377,13 +378,12 @@ Process LaunchProcess(const std::vector<std::string>& argv,
|
||||
int rv;
|
||||
pid_t pid;
|
||||
{
|
||||
+#if !BUILDFLAG(IS_IOS_TVOS)
|
||||
const bool has_mach_ports_for_rendezvous =
|
||||
-#if BUILDFLAG(IS_IOS_TVOS)
|
||||
- false
|
||||
+ !options.mach_ports_for_rendezvous.empty();
|
||||
#else
|
||||
- !options.mach_ports_for_rendezvous.empty()
|
||||
+ const bool has_mach_ports_for_rendezvous = false;
|
||||
#endif // BUILDFLAG(IS_IOS_TVOS)
|
||||
- ;
|
||||
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
// This code is only used for the iOS simulator to launch tests. We do not
|
||||
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
|
||||
index cf9d9bfe7af12e16520354ebd1f7bc4050c57ec7..e91b1e69eec5a194f24287a61a77290b887503c1 100644
|
||||
--- a/content/browser/child_process_launcher_helper_mac.cc
|
||||
+++ b/content/browser/child_process_launcher_helper_mac.cc
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "content/grit/content_resources.h"
|
||||
#include "content/public/browser/child_process_launcher_utils.h"
|
||||
#include "content/public/browser/content_browser_client.h"
|
||||
+#include "content/public/common/content_client.h"
|
||||
#include "content/public/common/content_paths.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/result_codes.h"
|
||||
@@ -106,6 +107,25 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
|
||||
options->mach_ports_for_rendezvous.insert(std::make_pair(
|
||||
'mojo', base::MachRendezvousPort(endpoint.TakeMachReceiveRight())));
|
||||
|
||||
+ VLOG(1) << "BeforeLaunchOnLauncherThread: PID=" << getpid()
|
||||
+ << ", launching child process";
|
||||
+
|
||||
+ // Add any embedder-provided ports (e.g., for profiling)
|
||||
+ base::MachPortsForRendezvous embedder_ports =
|
||||
+ GetContentClient()->browser()->GetMachPortsForChildRendezvous();
|
||||
+ VLOG(1) << "BeforeLaunchOnLauncherThread: Got " << embedder_ports.size()
|
||||
+ << " embedder ports";
|
||||
+ for (auto& port : embedder_ports) {
|
||||
+ VLOG(1) << "BeforeLaunchOnLauncherThread: Adding embedder port key='"
|
||||
+ << static_cast<char>(port.first >> 24)
|
||||
+ << static_cast<char>((port.first >> 16) & 0xff)
|
||||
+ << static_cast<char>((port.first >> 8) & 0xff)
|
||||
+ << static_cast<char>(port.first & 0xff) << "'";
|
||||
+ options->mach_ports_for_rendezvous.insert(std::move(port));
|
||||
+ }
|
||||
+ VLOG(1) << "BeforeLaunchOnLauncherThread: Total rendezvous ports: "
|
||||
+ << options->mach_ports_for_rendezvous.size();
|
||||
+
|
||||
options->environment = delegate_->GetEnvironment();
|
||||
options->clear_environment = !delegate_->ShouldInheritEnvironment();
|
||||
options->current_directory = delegate_->GetCurrentDirectory();
|
||||
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
|
||||
index b1e25f142beae77768128bb4467a3485c888a7ad..32d4b2a6817ba01d6d41f334400d0c0922ea0f79 100644
|
||||
--- a/content/public/browser/content_browser_client.cc
|
||||
+++ b/content/public/browser/content_browser_client.cc
|
||||
@@ -23,6 +23,10 @@
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/buildflag.h"
|
||||
+
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+#include "base/apple/mach_port_rendezvous.h"
|
||||
+#endif
|
||||
#include "components/language_detection/content/browser/content_language_detection_driver.h"
|
||||
#include "components/language_detection/content/common/language_detection.mojom.h"
|
||||
#include "components/language_detection/core/browser/language_detection_model_provider.h"
|
||||
@@ -1748,6 +1752,11 @@ std::string ContentBrowserClient::GetChildProcessSuffix(int child_flags) {
|
||||
NOTIMPLEMENTED();
|
||||
return std::string();
|
||||
}
|
||||
+
|
||||
+base::MachPortsForRendezvous
|
||||
+ContentBrowserClient::GetMachPortsForChildRendezvous() {
|
||||
+ return {};
|
||||
+}
|
||||
#endif
|
||||
|
||||
bool ContentBrowserClient::AreIsolatedWebAppsEnabled(
|
||||
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
|
||||
index 185e0c86bc02a80121af3c213aa496ca62565c31..24fea64674476c182309697466d650b814a5d439 100644
|
||||
--- a/content/public/browser/content_browser_client.h
|
||||
+++ b/content/public/browser/content_browser_client.h
|
||||
@@ -103,6 +103,10 @@
|
||||
#include "base/posix/global_descriptors.h"
|
||||
#endif
|
||||
|
||||
+#if BUILDFLAG(IS_MAC)
|
||||
+#include "base/apple/mach_port_rendezvous.h"
|
||||
+#endif
|
||||
+
|
||||
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
|
||||
#include "content/public/browser/posix_file_descriptor_info.h"
|
||||
#endif
|
||||
@@ -3032,6 +3036,12 @@ class CONTENT_EXPORT ContentBrowserClient {
|
||||
// bundle should be placed next to the known //content Mac helpers in the
|
||||
// framework bundle.
|
||||
virtual std::string GetChildProcessSuffix(int child_flags);
|
||||
+
|
||||
+ // Returns Mach ports that should be passed to child processes via
|
||||
+ // MachPortRendezvous. This allows the embedder to provide ports for
|
||||
+ // services like profiling. The ports are copied (not moved) so the
|
||||
+ // embedder retains ownership.
|
||||
+ virtual base::MachPortsForRendezvous GetMachPortsForChildRendezvous();
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
// Checks if Isolated Web Apps are enabled, e.g. by feature flag
|
||||
@@ -7,17 +7,20 @@
|
||||
#include "shell/app/electron_library_main.h"
|
||||
|
||||
#include "base/apple/bundle_locations.h"
|
||||
#include "base/apple/foundation_util.h"
|
||||
#include "base/apple/scoped_nsautorelease_pool.h"
|
||||
#include "base/at_exit.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/i18n/icu_util.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "content/public/app/content_main.h"
|
||||
#include "electron/fuses.h"
|
||||
#include "shell/app/electron_main_delegate.h"
|
||||
#include "shell/app/node_main.h"
|
||||
#include "shell/common/electron_command_line.h"
|
||||
#include "shell/common/mac/main_application_bundle.h"
|
||||
#include "shell/common/mac/samply_profiler_client.h"
|
||||
#include "uv.h"
|
||||
|
||||
int ElectronMain(int argc, char* argv[]) {
|
||||
@@ -27,12 +30,15 @@ int ElectronMain(int argc, char* argv[]) {
|
||||
|
||||
electron::ElectronMainDelegate delegate;
|
||||
|
||||
// Ensure that Bundle Id is set before ContentMain.
|
||||
// Ensure that Bundle Id is set before ContentMain AND before any code
|
||||
// that uses MachPortRendezvousClient (like samply profiler connection).
|
||||
// Refs https://chromium-review.googlesource.com/c/chromium/src/+/5581006
|
||||
delegate.OverrideChildProcessPath();
|
||||
delegate.OverrideFrameworkBundlePath();
|
||||
delegate.SetUpBundleOverrides();
|
||||
|
||||
electron::MaybeConnectToSamplyProfiler();
|
||||
|
||||
return content::ContentMain(content::ContentMainParams{&delegate});
|
||||
}
|
||||
|
||||
@@ -52,6 +58,21 @@ int ElectronInitializeICUandStartNode(int argc, char* argv[]) {
|
||||
.Append("Contents")
|
||||
.Append("Frameworks")
|
||||
.Append(ELECTRON_PRODUCT_NAME " Framework.framework"));
|
||||
|
||||
// Set the bundle ID before trying to connect to samply profiler.
|
||||
// This ensures MachPortRendezvousClient uses the correct service name.
|
||||
@autoreleasepool {
|
||||
NSBundle* bundle = electron::MainApplicationBundle();
|
||||
std::string base_bundle_id =
|
||||
base::SysNSStringToUTF8([bundle bundleIdentifier]);
|
||||
NSString* team_id = [bundle objectForInfoDictionaryKey:@"ElectronTeamID"];
|
||||
if (team_id)
|
||||
base_bundle_id = base::SysNSStringToUTF8(team_id) + "." + base_bundle_id;
|
||||
base::apple::SetBaseBundleIDOverride(base_bundle_id);
|
||||
}
|
||||
|
||||
electron::MaybeConnectToSamplyProfiler();
|
||||
|
||||
base::i18n::InitializeICU();
|
||||
return electron::NodeMain();
|
||||
}
|
||||
|
||||
@@ -71,11 +71,17 @@ void ElectronMainDelegate::OverrideChildProcessPath() {
|
||||
void ElectronMainDelegate::SetUpBundleOverrides() {
|
||||
@autoreleasepool {
|
||||
NSBundle* bundle = MainApplicationBundle();
|
||||
VLOG(1) << "SetUpBundleOverrides: bundle path="
|
||||
<< (bundle ? base::SysNSStringToUTF8([bundle bundlePath]) : "nil");
|
||||
std::string base_bundle_id =
|
||||
base::SysNSStringToUTF8([bundle bundleIdentifier]);
|
||||
VLOG(1) << "SetUpBundleOverrides: bundle id from Info.plist="
|
||||
<< (base_bundle_id.empty() ? "(empty)" : base_bundle_id);
|
||||
NSString* team_id = [bundle objectForInfoDictionaryKey:@"ElectronTeamID"];
|
||||
if (team_id)
|
||||
base_bundle_id = base::SysNSStringToUTF8(team_id) + "." + base_bundle_id;
|
||||
VLOG(1) << "SetUpBundleOverrides: Setting BaseBundleID to: "
|
||||
<< base_bundle_id;
|
||||
base::apple::SetBaseBundleIDOverride(base_bundle_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,9 @@
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
#include "net/ssl/client_cert_store_win.h"
|
||||
#elif BUILDFLAG(IS_MAC)
|
||||
#include "base/apple/mach_port_rendezvous.h"
|
||||
#include "net/ssl/client_cert_store_mac.h"
|
||||
#include "shell/browser/mac/samply_profiler_service.h"
|
||||
#elif defined(USE_OPENSSL)
|
||||
#include "net/ssl/client_cert_store.h"
|
||||
#endif
|
||||
@@ -1807,6 +1809,28 @@ device::GeolocationSystemPermissionManager*
|
||||
ElectronBrowserClient::GetGeolocationSystemPermissionManager() {
|
||||
return device::GeolocationSystemPermissionManager::GetInstance();
|
||||
}
|
||||
|
||||
base::MachPortsForRendezvous
|
||||
ElectronBrowserClient::GetMachPortsForChildRendezvous() {
|
||||
VLOG(1) << "GetMachPortsForChildRendezvous() called, PID=" << getpid();
|
||||
base::MachPortsForRendezvous ports;
|
||||
auto* profiler = SamplyProfilerService::GetInstance();
|
||||
VLOG(1) << "GetMachPortsForChildRendezvous: profiler="
|
||||
<< (profiler ? "yes" : "null");
|
||||
if (profiler) {
|
||||
mach_port_t port = profiler->GetProfilerPort();
|
||||
VLOG(1) << "GetMachPortsForChildRendezvous: profiler port=" << port;
|
||||
if (port != MACH_PORT_NULL) {
|
||||
ports.emplace(kMachPortKeyProfiler,
|
||||
base::MachRendezvousPort(port, MACH_MSG_TYPE_COPY_SEND));
|
||||
VLOG(1) << "GetMachPortsForChildRendezvous: Added profiler port to "
|
||||
"rendezvous";
|
||||
}
|
||||
}
|
||||
VLOG(1) << "GetMachPortsForChildRendezvous: returning " << ports.size()
|
||||
<< " ports";
|
||||
return ports;
|
||||
}
|
||||
#endif
|
||||
|
||||
content::HidDelegate* ElectronBrowserClient::GetHidDelegate() {
|
||||
|
||||
@@ -120,6 +120,8 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
device::GeolocationSystemPermissionManager*
|
||||
GetGeolocationSystemPermissionManager() override;
|
||||
|
||||
base::MachPortsForRendezvous GetMachPortsForChildRendezvous() override;
|
||||
#endif
|
||||
|
||||
content::PlatformNotificationService* GetPlatformNotificationService();
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include "components/os_crypt/common/keychain_password_mac.h"
|
||||
#include "shell/browser/mac/samply_profiler_service.h"
|
||||
#include "shell/browser/ui/cocoa/views_delegate_mac.h"
|
||||
#else
|
||||
#include "shell/browser/ui/views/electron_views_delegate.h"
|
||||
@@ -284,6 +285,12 @@ void ElectronBrowserMainParts::PostEarlyInitialization() {
|
||||
}
|
||||
|
||||
int ElectronBrowserMainParts::PreCreateThreads() {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
// Initialize samply profiling very early so child processes can connect.
|
||||
// This must be before any child processes are spawned.
|
||||
SamplyProfilerService::Initialize();
|
||||
#endif
|
||||
|
||||
if (!views::LayoutProvider::Get()) {
|
||||
layout_provider_ = std::make_unique<views::LayoutProvider>();
|
||||
}
|
||||
@@ -552,6 +559,8 @@ void ElectronBrowserMainParts::PostCreateMainMessageLoop() {
|
||||
void ElectronBrowserMainParts::PostMainMessageLoopRun() {
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
FreeAppDelegate();
|
||||
// Shutdown samply profiling and save the profile.
|
||||
SamplyProfilerService::Shutdown();
|
||||
#endif
|
||||
|
||||
// Shutdown the DownloadManager before destroying Node to prevent
|
||||
|
||||
84
shell/browser/mac/samply_profiler_service.h
Normal file
84
shell/browser/mac/samply_profiler_service.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2025 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_MAC_SAMPLY_PROFILER_SERVICE_H_
|
||||
#define ELECTRON_SHELL_BROWSER_MAC_SAMPLY_PROFILER_SERVICE_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/apple/scoped_mach_port.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/process/process.h"
|
||||
#include "shell/common/mac/samply_profiler_types.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Manages the samply profiler connection for the browser process.
|
||||
//
|
||||
// When profiling is enabled, this service:
|
||||
// 1. Creates a Mach receive port for task connections
|
||||
// 2. Launches the bundled samply process with --handshake-fd
|
||||
// 3. Sends the receive right to samply via the handshake protocol
|
||||
// 4. Provides the send right for child processes via MachPortRendezvous
|
||||
// 5. Connects the browser process itself to the profiler
|
||||
class SamplyProfilerService {
|
||||
public:
|
||||
// Returns the singleton instance. Returns nullptr if profiling is not
|
||||
// enabled.
|
||||
static SamplyProfilerService* GetInstance();
|
||||
|
||||
// Initialize profiling. Must be called early in browser startup.
|
||||
// Returns true if profiling was successfully started.
|
||||
static bool Initialize();
|
||||
|
||||
// Shutdown profiling and wait for samply to write the profile.
|
||||
static void Shutdown();
|
||||
|
||||
SamplyProfilerService(const SamplyProfilerService&) = delete;
|
||||
SamplyProfilerService& operator=(const SamplyProfilerService&) = delete;
|
||||
|
||||
bool IsEnabled() const { return is_enabled_; }
|
||||
|
||||
// Returns a send right to the profiler port (for child process rendezvous).
|
||||
mach_port_t GetProfilerPort() const {
|
||||
return send_right_.is_valid() ? send_right_.get() : MACH_PORT_NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
SamplyProfilerService(const base::FilePath& samply_path,
|
||||
const base::FilePath& output_path);
|
||||
~SamplyProfilerService();
|
||||
|
||||
// Perform the handshake with samply to transfer the receive right.
|
||||
bool PerformHandshake(int write_fd);
|
||||
|
||||
// Connect browser process to the profiler.
|
||||
void ConnectSelfToProfiler();
|
||||
|
||||
// The receive right for task connections
|
||||
// (owned temporarily during handshake)
|
||||
base::apple::ScopedMachReceiveRight receive_port_;
|
||||
|
||||
// Send right to the profiler port
|
||||
base::apple::ScopedMachSendRight send_right_;
|
||||
|
||||
// The samply process handle
|
||||
base::Process samply_process_;
|
||||
|
||||
// Path to the samply executable
|
||||
base::FilePath samply_path_;
|
||||
|
||||
// Path to where samply should save the profile
|
||||
base::FilePath output_path_;
|
||||
|
||||
// Whether profiling is active
|
||||
bool is_enabled_ = false;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_MAC_SAMPLY_PROFILER_SERVICE_H_
|
||||
334
shell/browser/mac/samply_profiler_service.mm
Normal file
334
shell/browser/mac/samply_profiler_service.mm
Normal file
@@ -0,0 +1,334 @@
|
||||
// Copyright (c) 2025 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/mac/samply_profiler_service.h"
|
||||
|
||||
#include <servers/bootstrap.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/apple/bundle_locations.h"
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/apple/scoped_mach_port.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/byte_conversions.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/process/launch.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/time.h"
|
||||
#include "shell/common/mac/samply_profiler_client.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
SamplyProfilerService* g_instance = nullptr;
|
||||
|
||||
// Returns the default path to the samply executable in the app bundle
|
||||
base::FilePath GetDefaultSamplyPath() {
|
||||
base::FilePath framework_path = base::apple::FrameworkBundlePath();
|
||||
return framework_path.Append("Helpers").Append("samply");
|
||||
}
|
||||
|
||||
// Returns the default output path for the profile.
|
||||
base::FilePath GetDefaultOutputPath() {
|
||||
base::FilePath temp_dir;
|
||||
if (!base::GetTempDir(&temp_dir)) {
|
||||
temp_dir = base::FilePath("/tmp");
|
||||
}
|
||||
return temp_dir.Append("electron_profile.json.gz");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
SamplyProfilerService* SamplyProfilerService::GetInstance() {
|
||||
return g_instance;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SamplyProfilerService::Initialize() {
|
||||
VLOG(1) << "SamplyProfilerService::Initialize() called, PID=" << getpid();
|
||||
|
||||
const base::CommandLine* command_line =
|
||||
base::CommandLine::ForCurrentProcess();
|
||||
if (!command_line->HasSwitch("enable-samply-profiling")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_instance) {
|
||||
VLOG(1) << "SamplyProfilerService: Already initialized, enabled="
|
||||
<< g_instance->IsEnabled();
|
||||
return g_instance->IsEnabled();
|
||||
}
|
||||
|
||||
base::FilePath samply_path;
|
||||
if (command_line->HasSwitch("samply-path")) {
|
||||
samply_path = command_line->GetSwitchValuePath("samply-path");
|
||||
} else {
|
||||
samply_path = GetDefaultSamplyPath();
|
||||
}
|
||||
|
||||
if (!base::PathExists(samply_path)) {
|
||||
LOG(ERROR) << "samply not found at: " << samply_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
base::FilePath output_path;
|
||||
if (command_line->HasSwitch("samply-output-path")) {
|
||||
output_path = command_line->GetSwitchValuePath("samply-output-path");
|
||||
} else {
|
||||
output_path = GetDefaultOutputPath();
|
||||
}
|
||||
|
||||
VLOG(1) << "SamplyProfilerService: Creating new instance...";
|
||||
g_instance = new SamplyProfilerService(samply_path, output_path);
|
||||
VLOG(1) << "SamplyProfilerService: Created, enabled="
|
||||
<< g_instance->IsEnabled()
|
||||
<< ", port=" << g_instance->GetProfilerPort();
|
||||
return g_instance->IsEnabled();
|
||||
}
|
||||
|
||||
// static
|
||||
void SamplyProfilerService::Shutdown() {
|
||||
if (g_instance) {
|
||||
delete g_instance;
|
||||
g_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SamplyProfilerService::SamplyProfilerService(const base::FilePath& samply_path,
|
||||
const base::FilePath& output_path)
|
||||
: samply_path_(samply_path), output_path_(output_path) {
|
||||
mach_port_t port;
|
||||
kern_return_t kr =
|
||||
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_port_allocate for profiler";
|
||||
return;
|
||||
}
|
||||
receive_port_.reset(port);
|
||||
|
||||
mach_port_t send_right;
|
||||
mach_msg_type_name_t acquired_type;
|
||||
kr = mach_port_extract_right(mach_task_self(), receive_port_.get(),
|
||||
MACH_MSG_TYPE_MAKE_SEND, &send_right,
|
||||
&acquired_type);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_port_extract_right for profiler";
|
||||
return;
|
||||
}
|
||||
send_right_.reset(send_right);
|
||||
|
||||
int pipe_fds[2];
|
||||
if (pipe(pipe_fds) != 0) {
|
||||
PLOG(ERROR) << "pipe for samply handshake";
|
||||
return;
|
||||
}
|
||||
|
||||
int read_fd = pipe_fds[0];
|
||||
int write_fd = pipe_fds[1];
|
||||
|
||||
base::LaunchOptions options;
|
||||
options.fds_to_remap.emplace_back(read_fd, read_fd);
|
||||
|
||||
std::vector<std::string> argv = {
|
||||
samply_path_.value(), "record",
|
||||
"--handshake-fd", base::StringPrintf("%d", read_fd),
|
||||
"--output", output_path_.value()};
|
||||
|
||||
samply_process_ = base::LaunchProcess(argv, options);
|
||||
close(read_fd);
|
||||
|
||||
if (!samply_process_.IsValid()) {
|
||||
LOG(ERROR) << "Failed to launch samply";
|
||||
close(write_fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PerformHandshake(write_fd)) {
|
||||
LOG(ERROR) << "Handshake with samply failed";
|
||||
samply_process_.Terminate(0, false);
|
||||
return;
|
||||
}
|
||||
|
||||
ConnectSelfToProfiler();
|
||||
|
||||
is_enabled_ = true;
|
||||
VLOG(1) << "Samply profiling enabled, PID: " << samply_process_.Pid();
|
||||
}
|
||||
|
||||
SamplyProfilerService::~SamplyProfilerService() {
|
||||
// Release our send right - this triggers MACH_NOTIFY_NO_SENDERS in samply
|
||||
// when all other clients have disconnected, causing it to save and exit
|
||||
send_right_.reset();
|
||||
|
||||
if (samply_process_.IsValid()) {
|
||||
// Send SIGINT to samply to request a graceful shutdown and
|
||||
// save the profile.
|
||||
VLOG(1) << "Sending SIGINT to samply to save profile...";
|
||||
kill(samply_process_.Pid(), SIGINT);
|
||||
|
||||
// Wait briefly for samply to save the profile and exit.
|
||||
int exit_code;
|
||||
if (!samply_process_.WaitForExitWithTimeout(base::Seconds(5), &exit_code)) {
|
||||
LOG(WARNING) << "Samply did not exit in time, sending SIGTERM";
|
||||
kill(samply_process_.Pid(), SIGTERM);
|
||||
samply_process_.WaitForExitWithTimeout(base::Seconds(2), &exit_code);
|
||||
}
|
||||
VLOG(1) << "Samply exited with code: " << exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
bool SamplyProfilerService::PerformHandshake(int write_fd) {
|
||||
// Generate a random token for authentication
|
||||
uint64_t token = base::RandUint64();
|
||||
|
||||
// Generate a unique service name
|
||||
std::string service_name = base::StringPrintf("org.electron.samply.%d.%llu",
|
||||
getpid(), base::RandUint64());
|
||||
|
||||
// Register the receive port with the bootstrap server
|
||||
mach_port_t local_bootstrap_port;
|
||||
kern_return_t kr = task_get_special_port(
|
||||
mach_task_self(), TASK_BOOTSTRAP_PORT, &local_bootstrap_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "task_get_special_port";
|
||||
close(write_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract a send right to register with bootstrap
|
||||
mach_port_t service_port;
|
||||
mach_msg_type_name_t acquired_type;
|
||||
kr = mach_port_extract_right(mach_task_self(), receive_port_.get(),
|
||||
MACH_MSG_TYPE_MAKE_SEND, &service_port,
|
||||
&acquired_type);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_port_extract_right for service";
|
||||
close(write_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register with bootstrap server
|
||||
// bootstrap_register is deprecated but there's no modern replacement for
|
||||
// this functionality. We use it to allow samply to look up our port.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
kr =
|
||||
bootstrap_register(local_bootstrap_port,
|
||||
const_cast<char*>(service_name.c_str()), service_port);
|
||||
#pragma clang diagnostic pop
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "bootstrap_register";
|
||||
close(write_fd);
|
||||
mach_port_deallocate(mach_task_self(), service_port);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write(write_fd, &token, sizeof(token)) != sizeof(token)) {
|
||||
PLOG(ERROR) << "write token";
|
||||
close(write_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t name_len = static_cast<uint32_t>(service_name.size());
|
||||
if (write(write_fd, &name_len, sizeof(name_len)) != sizeof(name_len)) {
|
||||
PLOG(ERROR) << "write name_len";
|
||||
close(write_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (write(write_fd, service_name.c_str(), name_len) !=
|
||||
static_cast<ssize_t>(name_len)) {
|
||||
PLOG(ERROR) << "write service_name";
|
||||
close(write_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
close(write_fd);
|
||||
|
||||
// Wait for samply to send its check-in message
|
||||
CheckInMessageBuffer check_in = {};
|
||||
check_in.header.msgh_size = sizeof(CheckInMessageBuffer);
|
||||
check_in.header.msgh_local_port = receive_port_.get();
|
||||
|
||||
kr = mach_msg(&check_in.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
|
||||
sizeof(CheckInMessageBuffer), receive_port_.get(),
|
||||
10000, // 10 second timeout
|
||||
MACH_PORT_NULL);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_msg receive check-in from samply";
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG(1) << "Received check-in message: size=" << check_in.header.msgh_size
|
||||
<< " remote_port=" << check_in.header.msgh_remote_port
|
||||
<< " id=" << check_in.header.msgh_id;
|
||||
|
||||
// Validate message size before accessing fields.
|
||||
if (check_in.header.msgh_size < kCheckInMessageMinSize) {
|
||||
LOG(ERROR) << "Check-in message too small, size="
|
||||
<< check_in.header.msgh_size
|
||||
<< " expected >= " << kCheckInMessageMinSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the token from the received message at known offset.
|
||||
uint64_t received_token = base::U64FromNativeEndian(
|
||||
base::span(check_in.bytes)
|
||||
.subspan<kCheckInTokenOffset, sizeof(uint64_t)>());
|
||||
|
||||
if (received_token != token) {
|
||||
LOG(ERROR) << "Invalid token in samply check-in message";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send reply containing the receive right
|
||||
// After this, samply owns the receive right and we only keep a send right
|
||||
CheckInReply reply = {};
|
||||
// Use MACH_MSG_TYPE_MOVE_SEND because samply created a regular send right
|
||||
// (via MACH_MSG_TYPE_MAKE_SEND), not a send-once right.
|
||||
reply.header.msgh_bits =
|
||||
MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, 0);
|
||||
reply.header.msgh_size = sizeof(CheckInReply);
|
||||
reply.header.msgh_remote_port = check_in.header.msgh_remote_port;
|
||||
reply.header.msgh_local_port = MACH_PORT_NULL;
|
||||
reply.header.msgh_id = check_in.header.msgh_id + 100;
|
||||
|
||||
reply.body.msgh_descriptor_count = 1;
|
||||
|
||||
reply.port.name = receive_port_.release(); // Transfer ownership to samply
|
||||
reply.port.disposition = MACH_MSG_TYPE_MOVE_RECEIVE;
|
||||
reply.port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
|
||||
kr = mach_msg(&reply.header, MACH_SEND_MSG, sizeof(CheckInReply), 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_msg send check-in reply to samply";
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG(1) << "Handshake with samply completed successfully";
|
||||
return true;
|
||||
}
|
||||
|
||||
void SamplyProfilerService::ConnectSelfToProfiler() {
|
||||
if (!send_right_.is_valid()) {
|
||||
LOG(WARNING) << "No send right available for self-connection";
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectToSamplyProfiler(send_right_.get())) {
|
||||
VLOG(1) << "Browser process connected to samply profiler";
|
||||
} else {
|
||||
LOG(WARNING) << "Failed to connect browser process to samply profiler";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
24
shell/common/mac/samply_profiler_client.h
Normal file
24
shell/common/mac/samply_profiler_client.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2025 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_CLIENT_H_
|
||||
#define ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_CLIENT_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Attempts to connect this process to the samply profiler.
|
||||
// Should be called early in process startup.
|
||||
// Uses MachPortRendezvous to get the profiler port from the parent process.
|
||||
void MaybeConnectToSamplyProfiler();
|
||||
|
||||
// Connects this process to the samply profiler using the given send right.
|
||||
// The send right is borrowed (not consumed).
|
||||
// Returns true if connection was successful.
|
||||
bool ConnectToSamplyProfiler(mach_port_t profiler_port);
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_CLIENT_H_
|
||||
118
shell/common/mac/samply_profiler_client.mm
Normal file
118
shell/common/mac/samply_profiler_client.mm
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2025 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/mac/samply_profiler_client.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/apple/mach_port_rendezvous.h"
|
||||
#include "base/apple/scoped_mach_port.h"
|
||||
#include "base/containers/span.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/byte_conversions.h"
|
||||
#include "shell/common/mac/samply_profiler_types.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
bool ConnectToSamplyProfiler(mach_port_t profiler_port) {
|
||||
if (profiler_port == MACH_PORT_NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
base::apple::ScopedMachReceiveRight reply_port;
|
||||
kern_return_t kr = mach_port_allocate(
|
||||
mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
|
||||
base::apple::ScopedMachReceiveRight::Receiver(reply_port).get());
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(WARNING, kr) << "mach_port_allocate for profiler reply";
|
||||
return false;
|
||||
}
|
||||
|
||||
SamplyMessage msg = {};
|
||||
msg.header.msgh_bits =
|
||||
MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
|
||||
msg.header.msgh_size = sizeof(SamplyMessage);
|
||||
msg.header.msgh_remote_port = profiler_port;
|
||||
msg.header.msgh_local_port = MACH_PORT_NULL;
|
||||
msg.header.msgh_id = 0;
|
||||
|
||||
msg.body.msgh_descriptor_count = 2;
|
||||
|
||||
msg.reply_port.name = reply_port.get();
|
||||
msg.reply_port.disposition = MACH_MSG_TYPE_MAKE_SEND;
|
||||
msg.reply_port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
|
||||
msg.task_port.name = mach_task_self();
|
||||
msg.task_port.disposition = MACH_MSG_TYPE_COPY_SEND;
|
||||
msg.task_port.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
|
||||
msg.magic = kSamplyMagic;
|
||||
msg.pid = static_cast<uint32_t>(getpid());
|
||||
|
||||
kr = mach_msg(&msg.header, MACH_SEND_MSG, sizeof(SamplyMessage), 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(WARNING, kr) << "mach_msg send to samply profiler";
|
||||
return false;
|
||||
}
|
||||
|
||||
SamplyReplyBuffer reply = {};
|
||||
reply.header.msgh_size = sizeof(SamplyReplyBuffer);
|
||||
reply.header.msgh_local_port = reply_port.get();
|
||||
|
||||
kr = mach_msg(&reply.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
|
||||
sizeof(SamplyReplyBuffer), reply_port.get(), 5000,
|
||||
MACH_PORT_NULL);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(WARNING, kr) << "mach_msg receive from samply profiler";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply.header.msgh_size < kSamplyReplyMinSize) {
|
||||
LOG(ERROR) << "Samply profiler: reply too small, size="
|
||||
<< reply.header.msgh_size
|
||||
<< " expected >= " << kSamplyReplyMinSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read magic and status from bytes[] at known offsets.
|
||||
uint32_t magic = base::U32FromNativeEndian(
|
||||
base::span(reply.bytes)
|
||||
.subspan<kSamplyReplyMagicOffset, sizeof(uint32_t)>());
|
||||
uint32_t status = base::U32FromNativeEndian(
|
||||
base::span(reply.bytes)
|
||||
.subspan<kSamplyReplyStatusOffset, sizeof(uint32_t)>());
|
||||
|
||||
if (magic != kSamplyMagic) {
|
||||
LOG(ERROR) << "Samply profiler: invalid reply magic";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
LOG(ERROR) << "Samply profiler: error status " << status;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MaybeConnectToSamplyProfiler() {
|
||||
auto* client = base::MachPortRendezvousClient::GetInstance();
|
||||
if (!client) {
|
||||
// Not available (e.g., no rendezvous server or running standalone)
|
||||
return;
|
||||
}
|
||||
|
||||
auto profiler_port = client->TakeSendRight(kMachPortKeyProfiler);
|
||||
if (!profiler_port.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConnectToSamplyProfiler(profiler_port.get())) {
|
||||
VLOG(1) << "Connected to samply profiler (child process)";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace electron
|
||||
81
shell/common/mac/samply_profiler_types.h
Normal file
81
shell/common/mac/samply_profiler_types.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2025 Microsoft, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_TYPES_H_
|
||||
#define ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_TYPES_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Key for the profiler port in MachPortRendezvous.
|
||||
inline constexpr uint32_t kMachPortKeyProfiler = 0x70726F66;
|
||||
|
||||
// Magic number for samply protocol messages.
|
||||
inline constexpr uint32_t kSamplyMagic = 0x534D504C;
|
||||
|
||||
// Expected minimum size for SamplyReply from Rust.
|
||||
inline constexpr size_t kSamplyReplyMinSize = 36;
|
||||
|
||||
// Expected minimum size for CheckIn message from Rust.
|
||||
inline constexpr size_t kCheckInMessageMinSize = 48;
|
||||
|
||||
// Byte offsets for reading fields from raw message buffers.
|
||||
inline constexpr size_t kSamplyReplyMagicOffset =
|
||||
sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
|
||||
inline constexpr size_t kSamplyReplyStatusOffset =
|
||||
kSamplyReplyMagicOffset + sizeof(uint32_t);
|
||||
inline constexpr size_t kCheckInTokenOffset =
|
||||
sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t) +
|
||||
sizeof(mach_msg_port_descriptor_t);
|
||||
|
||||
// Message structure for connecting a process to samply.
|
||||
// Matches SimpleSamplyMessage in samply's simple_server.rs.
|
||||
struct SamplyMessage {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
mach_msg_port_descriptor_t reply_port;
|
||||
mach_msg_port_descriptor_t task_port;
|
||||
uint32_t magic;
|
||||
uint32_t pid;
|
||||
};
|
||||
|
||||
// Reply message structure from samply.
|
||||
// Uses a union to provide both structured access and raw byte access.
|
||||
union SamplyReplyBuffer {
|
||||
mach_msg_header_t header;
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
uint32_t magic;
|
||||
uint32_t status;
|
||||
} structured;
|
||||
uint8_t bytes[128];
|
||||
};
|
||||
|
||||
// Buffer for receiving check-in message from samply during handshake.
|
||||
// Uses a union to provide both structured access and raw byte access.
|
||||
union CheckInMessageBuffer {
|
||||
mach_msg_header_t header;
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
mach_msg_port_descriptor_t port;
|
||||
uint64_t token;
|
||||
} structured;
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
// Reply message sent to samply containing the receive right.
|
||||
// Matches ChildPortCheckInReply in samply's simple_server.rs.
|
||||
struct CheckInReply {
|
||||
mach_msg_header_t header;
|
||||
mach_msg_body_t body;
|
||||
mach_msg_port_descriptor_t port;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_MAC_SAMPLY_PROFILER_TYPES_H_
|
||||
Reference in New Issue
Block a user