| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/tracing/windows_system_tracing_client_impl_win.h" |
| |
| #include <objbase.h> |
| |
| #include <stdint.h> |
| |
| #include <ios> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/strcat.h" |
| #include "base/task/thread_pool.h" |
| #include "base/win/com_init_util.h" |
| #include "base/win/win_util.h" |
| #include "chrome/installer/util/install_service_work_item.h" |
| #include "mojo/public/cpp/platform/named_platform_channel.h" |
| #include "mojo/public/cpp/system/invitation.h" |
| |
| namespace { |
| |
| enum class LaunchStage { |
| kCoCreateInstance, |
| kCoSetProxyBlanket, |
| kAcceptInvitation, |
| }; |
| |
| // Records the result of one stage while launching the elevated tracing service. |
| void RecordLaunchResult(LaunchStage stage, HRESULT result) { |
| std::string_view stage_name; |
| switch (stage) { |
| case LaunchStage::kCoCreateInstance: |
| stage_name = "CoCreateInstance"; |
| break; |
| case LaunchStage::kCoSetProxyBlanket: |
| stage_name = "CoSetProxyBlanket"; |
| break; |
| case LaunchStage::kAcceptInvitation: |
| stage_name = "AcceptInvitation"; |
| break; |
| } |
| base::UmaHistogramSparse( |
| base::StrCat( |
| {"Tracing.ElevatedTracingService.LaunchResult.", stage_name}), |
| result); |
| } |
| |
| } // namespace |
| |
| // WindowsSystemTracingClientImpl::Host ---------------------------------------- |
| |
| WindowsSystemTracingClientImpl::Host::Host(const CLSID& clsid, const IID& iid) |
| : clsid_(clsid), iid_(iid) {} |
| |
| WindowsSystemTracingClientImpl::Host::~Host() = default; |
| |
| base::expected<WindowsSystemTracingClientImpl::ServiceState, HRESULT> |
| WindowsSystemTracingClientImpl::Host::LaunchService() { |
| base::win::AssertComInitialized(); |
| |
| if (auto session_or_result = CreateSession(); session_or_result.has_value()) { |
| return Invite(std::move(session_or_result).value()); |
| } else { |
| return base::unexpected(session_or_result.error()); |
| } |
| } |
| base::expected<Microsoft::WRL::ComPtr<ISystemTraceSession>, HRESULT> |
| WindowsSystemTracingClientImpl::Host::CreateSession() { |
| Microsoft::WRL::ComPtr<ISystemTraceSession> trace_session; |
| HRESULT hresult = ::CoCreateInstance( |
| clsid_, /*pUnkOuter=*/nullptr, CLSCTX_LOCAL_SERVER, iid_, &trace_session); |
| // Do not record failure to create the instance if the service isn't |
| // registered. This is expected for per-machine beta and stable installs for |
| // which the user has not manually registered the service via |
| // chrome://traces-internals/scenarios. |
| if (hresult != REGDB_E_CLASSNOTREG || |
| installer::InstallServiceWorkItem::IsComServiceInstalled(clsid_)) { |
| RecordLaunchResult(LaunchStage::kCoCreateInstance, hresult); |
| } |
| if (FAILED(hresult)) { |
| return base::unexpected(hresult); |
| } |
| |
| hresult = ::CoSetProxyBlanket(trace_session.Get(), RPC_C_AUTHN_DEFAULT, |
| RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, |
| RPC_C_AUTHN_LEVEL_PKT_PRIVACY, |
| RPC_C_IMP_LEVEL_IMPERSONATE, |
| /*pAuthInfo=*/nullptr, EOAC_DYNAMIC_CLOAKING); |
| RecordLaunchResult(LaunchStage::kCoSetProxyBlanket, hresult); |
| if (FAILED(hresult)) { |
| return base::unexpected(hresult); |
| } |
| return base::ok(std::move(trace_session)); |
| } |
| |
| base::expected<WindowsSystemTracingClientImpl::ServiceState, HRESULT> |
| WindowsSystemTracingClientImpl::Host::Invite( |
| Microsoft::WRL::ComPtr<ISystemTraceSession> trace_session) { |
| mojo::NamedPlatformChannel channel(mojo::NamedPlatformChannel::Options{}); |
| mojo::OutgoingInvitation invitation; |
| invitation.set_extra_flags(MOJO_SEND_INVITATION_FLAG_ELEVATED); |
| mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe(0); |
| |
| DWORD pid = base::kNullProcessId; |
| HRESULT hresult = |
| trace_session->AcceptInvitation(channel.GetServerName().c_str(), &pid); |
| RecordLaunchResult(LaunchStage::kAcceptInvitation, hresult); |
| if (FAILED(hresult)) { |
| return base::unexpected(hresult); |
| } |
| |
| mojo::OutgoingInvitation::Send(std::move(invitation), |
| /*target_process=*/base::kNullProcessHandle, |
| channel.TakeServerEndpoint()); |
| |
| trace_session_ = std::move(trace_session); |
| return base::ok(std::make_pair(pid, std::move(pipe))); |
| } |
| |
| // WindowsSystemTracingClientImpl ---------------------------------------------- |
| |
| WindowsSystemTracingClientImpl::WindowsSystemTracingClientImpl( |
| const CLSID& clsid, |
| const IID& iid) |
| : host_(base::ThreadPool::CreateCOMSTATaskRunner( |
| {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, |
| base::MayBlock()}), |
| clsid, |
| iid) {} |
| |
| WindowsSystemTracingClientImpl::~WindowsSystemTracingClientImpl() = default; |
| |
| void WindowsSystemTracingClientImpl::Start(OnRemoteProcess on_remote_process) { |
| host_.AsyncCall(&Host::LaunchService) |
| .Then(base::BindOnce( |
| &WindowsSystemTracingClientImpl::OnLaunchServiceResult, |
| weak_factory_.GetWeakPtr(), std::move(on_remote_process))); |
| } |
| |
| void WindowsSystemTracingClientImpl::OnLaunchServiceResult( |
| OnRemoteProcess on_remote_process, |
| base::expected<ServiceState, HRESULT> result) { |
| if (!result.has_value()) { |
| return; |
| } |
| std::move(on_remote_process) |
| .Run(result->first, mojo::PendingRemote<tracing::mojom::TracedProcess>( |
| std::move(result->second), /*version=*/0)); |
| } |
| |
| // WindowsSystemTracingClient -------------------------------------------------- |
| |
| // static |
| std::unique_ptr<WindowsSystemTracingClient> WindowsSystemTracingClient::Create( |
| const CLSID& clsid, |
| const IID& iid) { |
| return std::make_unique<WindowsSystemTracingClientImpl>(clsid, iid); |
| } |