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

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

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

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

摘要

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

设计初衷

此功能最初在 crbug.com/726484https://fxbug.dev/42105649 中提出,Chromium 请求此功能是为了为跟踪事件提供基本时间,从而扩展已支持 Linux、macOS 和 Windows 的平台无关接口。此外,我们还提交了 https://fxbug.dev/42119376,以利用此功能在崩溃报告中包含正常运行时间。

设计

ProcessDispatcher 的一个简单直接的扩展中,我们会在 ProcessDispatcher::AddInitializedThread() 中记录初始线程的时间,然后将其传递到 ProcessDispatcher::GetInfo() 中;前者是 zx_process_start() 的主要“启动”例程,而后者是 zx_get_object_info() 的例程,用于处理具有 ZX_INFO_PROCESS 的进程句柄。

至于由 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() 系统调用似乎公开了此信息。