blob: 5d979c6722a96245f50757e49e91ca787858f53c [file]
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/rpc/session_lifecycle.h"
#include <algorithm>
#include <cstdint>
#include <utility>
#include "perfetto/base/build_config.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/time.h"
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <unistd.h>
#else
#include <tlhelp32.h>
#endif
namespace perfetto::trace_processor {
namespace {
int64_t NowMs() {
return base::GetWallTimeMs().count();
}
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
DWORD GetParentProcessId() {
DWORD pid = GetCurrentProcessId();
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE)
return 0;
PROCESSENTRY32 entry{};
entry.dwSize = sizeof(entry);
DWORD parent = 0;
if (Process32First(snap, &entry)) {
do {
if (entry.th32ProcessID == pid) {
parent = entry.th32ParentProcessID;
break;
}
} while (Process32Next(snap, &entry));
}
CloseHandle(snap);
return parent;
}
#endif
} // namespace
ProcessOwnerMonitor::ProcessOwnerMonitor() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
DWORD parent_pid = GetParentProcessId();
if (parent_pid != 0) {
parent_handle_ = OpenProcess(SYNCHRONIZE, FALSE, parent_pid);
had_owner_at_start_ = parent_handle_ != nullptr;
}
#else
owner_pid_ = getppid();
// A parent pid of 1 means we are already a child of init, i.e. there is no
// controlling owner to reap us.
had_owner_at_start_ = owner_pid_ > 1;
#endif
}
ProcessOwnerMonitor::~ProcessOwnerMonitor() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
if (parent_handle_)
CloseHandle(parent_handle_);
#endif
}
bool ProcessOwnerMonitor::IsOwnerAlive() {
if (!had_owner_at_start_)
return false;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return WaitForSingleObject(parent_handle_, 0) == WAIT_TIMEOUT;
#else
// If the parent exited we are reparented (to init, pid 1, or a subreaper), so
// getppid() no longer matches the owner captured at construction.
return static_cast<base::PlatformProcessId>(getppid()) == owner_pid_;
#endif
}
IdleReaper::IdleReaper(base::TaskRunner* task_runner,
uint32_t idle_timeout_ms,
IdleStart idle_start,
std::function<void()> on_reap)
: task_runner_(task_runner),
idle_timeout_ms_(idle_timeout_ms),
tick_interval_ms_(std::clamp(idle_timeout_ms, 50u, 5000u)),
on_reap_(std::move(on_reap)) {
switch (idle_start) {
case IdleStart::kLastQuery:
effective_start_ = IdleStart::kLastQuery;
break;
case IdleStart::kOrphaned:
effective_start_ = IdleStart::kOrphaned;
break;
case IdleStart::kAuto:
// Owner-aware: a foreground server (live owner) is reaped by its owner,
// so only arm once orphaned. A detached server has no owner, so arm now.
effective_start_ = owner_monitor_.had_owner_at_start()
? IdleStart::kOrphaned
: IdleStart::kLastQuery;
break;
}
}
void IdleReaper::Start() {
if (idle_timeout_ms_ == 0)
return;
last_activity_ms_ = NowMs();
task_runner_->PostDelayedTask([this] { Tick(); }, tick_interval_ms_);
}
void IdleReaper::OnActivity() {
last_activity_ms_ = NowMs();
}
void IdleReaper::Tick() {
if (reaped_)
return;
// The idle clock is armed unless we're waiting for the owner to exit first.
bool armed = effective_start_ == IdleStart::kLastQuery ||
!owner_monitor_.IsOwnerAlive();
if (armed && !query_in_flight_ &&
NowMs() - last_activity_ms_ >= idle_timeout_ms_) {
reaped_ = true;
on_reap_();
return;
}
task_runner_->PostDelayedTask([this] { Tick(); }, tick_interval_ms_);
}
} // namespace perfetto::trace_processor