RFC-0074:公开进程开始时间

RFC-0074:公开进程启动时间
状态已接受
区域
  • 内核
说明

使进程启动时间可从用户空间查询。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-02-19
审核日期(年-月-日)2021-03-11

摘要

我们重构了 ZX_INFO_PROCESS,以公开进程的开始时间,并清理描述其运行时的标志。我们认为,如果存在 ZX_INFO_PROCESS_FLAG_STARTED 标志,则进程具有有效的开始时间,并将其定义为调用 zx_process_start() 时的时间(如果有效)。

设计初衷

Chromium 最初在 crbug.com/726484 提交了此请求,后来又在 https://fxbug.dev/42105649 提交了此请求,目的是提供轨迹事件的基准时间,从而扩展已支持 Linux、macOS 和 Windows 的平台无关接口。此外,我们还提交了 https://fxbug.dev/42119376,以利用同一功能在崩溃报告中添加正常运行时间。

设计

ProcessDispatcher 的一项简单的小扩展中,我们会在 ProcessDispatcher::AddInitializedThread() 中记录初始线程的时间,然后在 ProcessDispatcher::GetInfo() 中传递该时间;前一种方法是 zx_process_start() 的主要“启动”例程,而后一种方法是 ZX_INFO_PROCESS 中针对进程句柄的 zx_get_object_info() 例程。

至于由 zx_object_get_info() 填充的结构体,目前我们有:

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;

我们将逐步改进该工具,使其:

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;

为此,我们将引入以下标志值:

// 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)

将布尔值重构为标志并非完全必要,但这会使 zx_info_process_t 符合当前的系统调用结构体政策,在填充方面节省一个字节,并且不会增加执行此提案所需的工作量(同时也会为下一位工程师免去不少麻烦)。

实现

  1. 将旧结构体重命名为 zx_info_process_v1_t,并创建一个别名以便通过旧名称引用它(zx_info_process_t -> zx_info_process_v1_t)。将主题 ZX_INFO_PROCESS 重命名为 ZX_INFO_PROCESS_V1,并创建一个别名以便引用旧名称(ZX_INFO_PROCESS -> ZX_INFO_PROCESS_V1)。

  2. 添加新的结构体 (zx_info_process_v2_t) 和主题 (ZX_INFO_PROCESS_V2)。更改内核以跟踪进程启动时间并识别新主题和结构体。

  3. 更新所有代码(树内和树外代码)以使用 ZX_INFO_PROCESS_V2

  4. ZX_INFO_PROCESSzx_info_process_t 别名更改为引用 v2 主题和结构体。

  5. 等待之前的更改全面实施。

  6. 更新所有代码(树内和树外代码),以便再次使用 ZX_INFO_PROCESS

  7. 移除了 v1 主题和结构体,以及 v2 别名。

这些类型的 Rust 和 Go 版本也会以类似的方式更新。

性能

添加的逻辑应该会产生可忽略的运行时开销,尤其是因为它会在单个进程的生命周期内摊销。

安全注意事项

如果程序已拥有进程的句柄,则它可以执行和获取的操作远远超出该进程启动时所能执行和获取的操作。此外,许多其他操作系统已公开开始时间,且似乎没有任何问题。

隐私注意事项

请参阅上文中的安全注意事项

测试

Zircon 的进程相关核心测试将得到扩展,例如,断言在 zx_process_start() 之前和之后采集的时间样本确实夹在记录的开始时间之间,以及尚未启动的进程已取消设置 ZX_INFO_PROCESS_FLAG_STARTED

文档

ZX_INFO_PROCESS 文档将相应更新。

缺点、替代方案和未知情况

我们还考虑过将“开始”时间作为更通用的任务级概念公开(例如在 ZX_INFO_TASK_RUNTIME 下公开)。对于线程,扩展方式完全相同:线程的开始时间是调用 zx_thread_start() 的时间点。因此,对于作业,最自然的说法是,当其第一个子作业开始时,它也“开始”。不过,由于作业可以嵌套,这会引入遍历任务子树,而与处理其他两种任务类型相比,这种逻辑相对较为复杂。最终,我们决定不扩展该功能,因为它无法满足当前需求,而且会让我们无需执行任务层次结构遍历。

在先技术和参考文档

Linux:/proc/[pid]/stat 会公开 starttime。FreeBSD:/proc/[pid]/status 会在以空格分隔的统计信息列表中公开开始时间。 macOS:proc_pidinfo() 系统调用似乎会公开此信息。