RFC-0074: Expose Process Start Time | |
---|---|
Status | Accepted |
Areas |
|
Description | Make process start time queryable from userspace. |
Gerrit change | |
Authors | |
Reviewers | |
Date submitted (year-month-day) | 2021-02-19 |
Date reviewed (year-month-day) | 2021-03-11 |
Summary
We refactor ZX_INFO_PROCESS
to expose a process's start time and also to
clean up the flags describing its runtime. We say a process has a valid start
time when a ZX_INFO_PROCESS_FLAG_STARTED
flag is present and define it to be
the monotonic time at which zx_process_start()
was called, when valid.
Motivation
First filed at crbug.com/726484 and then https://fxbug.dev/42105649, Chromium requested the feature in order to give a base time for trace events, extending a platform-agnostic interface already supporting Linux, macOS, and Windows. Further, https://fxbug.dev/42119376 was filed to leverage this same feature in order to include uptime in crash reports.
Design
In a small, straightforward extension of ProcessDispatcher
, we would record
the time in ProcessDispatcher::AddInitializedThread()
for the initial thread
and then pass it along in ProcessDispatcher::GetInfo()
; the former method is
the main "starting" routine of zx_process_start()
, while the latter is that
of zx_get_object_info()
for a process handle with ZX_INFO_PROCESS
.
As for the struct populated by zx_object_get_info()
, currently we have:
typedef struct zx_info_process {
// The process's return code; only valid if |exited| is true.
// If the process was killed, it will be one of the ZX_TASK_RETCODE values.
int64_t return_code;
// True if the process has ever left the initial creation state,
// even if it has exited as well.
bool started;
// If true, the process has exited and |return_code| is valid.
bool exited;
// True if a debugger is attached to the process.
bool debugger_attached;
uint8_t padding1[5];
} zx_info_process_t;
We would evolve it to be:
typedef struct zx_info_process {
// The process's return code; only valid if the
// |ZX_PROCESS_INFO_FLAG_EXITED| flag is set. If the process was killed, it
// will be one of the |ZX_TASK_RETCODE| values.
int64_t return_code;
// The monotonic time at which zx_process_start() was called, only valid
// if the |ZX_INFO_PROCESS_FLAG_STARTED| flag is set.
zx_time_t start_time;
// Bitwise OR of ZX_INFO_PROCESS_FLAG_* values.
uint32_t flags;
uint8_t padding1[4];
} zx_info_process_t;
For which the following flag values would be introduced:
// Whether the process has started. zx_process_info_t::start_time is only
// valid if this flag is set.
#define ZX_INFO_PROCESS_FLAG_STARTED (1u << 0)
// Whether the process has exited.
#define ZX_INFO_PROCESS_FLAG_EXITED (1u << 1)
// Whether a debugger is attached to the process.
#define ZX_INFO_PROCESS_FLAG_DEBUGGER_ATTACHED (1u << 2)
The boolean-to-flag refactor is not strictly necessary, but it would bring
zx_info_process_t
in line with current syscall struct policy, save a byte
on padding, and would not increase the amount of work needed to carry out this
proposal (and also would save the next engineer from a fair amount of hassle).
Implementation
Rename the old struct to
zx_info_process_v1_t
and create an alias to refer to it by its old name (zx_info_process_t
->zx_info_process_v1_t
). Rename the topicZX_INFO_PROCESS
toZX_INFO_PROCESS_V1
and create an alias to the old name (ZX_INFO_PROCESS
->ZX_INFO_PROCESS_V1
).Add the new struct (
zx_info_process_v2_t
) and topic (ZX_INFO_PROCESS_V2
). Change the kernel to track process start time and recognize the new topic and struct.Update all code (in-tree and out-of-tree) to use
ZX_INFO_PROCESS_V2
.Change the
ZX_INFO_PROCESS
andzx_info_process_t
alias to refer to the v2 topic and struct.Wait until the previous change has been fully rolled out.
Update all code (in-tree and out-of-tree) to use
ZX_INFO_PROCESS
again.Remove the v1 topic and struct, as well as the v2 alias.
The Rust and Go versions of these types will be similarly updated.
Performance
The added logic should incur a negligible runtime cost, especially as it would be amortized over the lifetime of an individual process.
Security considerations
If a program already has a handle to a process, it can do and glean much more than the time at which that process started. Moreover, start time is already exposed - without seeming concern - in a number of other operating systems.
Privacy considerations
See Security considerations above.
Testing
Zircon's process-related core tests will be extended to assert, for example,
that time samples taken before and after a zx_process_start()
do indeed
sandwich the recorded start time, and that a not-yet-started process has
ZX_INFO_PROCESS_FLAG_STARTED
unset.
Documentation
ZX_INFO_PROCESS
documentation will be updated accordingly.
Drawbacks, alternatives, and unknowns
Also considered was exposing the "start" time as a more general, task-level
concept (exposed under, say, ZX_INFO_TASK_RUNTIME
). For threads, things
extend indentically: a thread's start time is the point at which
zx_thread_start()
is called. For a job, it would then be most natural to say
that it "starts" when its first child starts. Since jobs can be nested,
however, this would introduce walking task subtrees, which is relatively more
complicated logic than dealing with the other two task types. Ultimately the
extensions were decided against, as they would not meet a current need and
would allow us to sidestep the need for task hierarchy walking.
Prior art and references
Linux: /proc/[pid]/stat
exposes starttime
.
FreeBSD: /proc/[pid]/status
exposes start time in a space-separated list of statistics.
macOS: The proc_pidinfo()
syscalls seems to expose this.