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/726484https://fxbug.dev/42105649,请求提供该功能,以便为跟踪事件提供基准时间,从而扩展支持 Linux、macOS 和 Windows 且不依赖于平台的接口。此外,提交 https://fxbug.dev/42119376 以利用这一功能,以便将正常运行时间纳入崩溃报告。

设计

ProcessDispatcher 的一个小型简单扩展中,我们会在 ProcessDispatcher::AddInitializedThread() 中记录初始线程的时间,然后在 ProcessDispatcher::GetInfo() 中传递它;前一种方法是 zx_process_start() 的主“启动”例程,而后一种方法是 zx_get_object_info()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() 系统调用似乎会公开此时间。