Use native APIs for macOS process metrics by ehds · Pull Request #3602 · apache/brpc · GitHub
Skip to content

Use native APIs for macOS process metrics - #3602

Open
ehds wants to merge 3 commits into
apache:masterfrom
ehds:hds/native_macos_api
Open

ehds wants to merge 3 commits into
apache:masterfrom
ehds:hds/native_macos_api

Conversation

@ehds

@ehds ehds commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: resolve

Problem Summary:

  1. Collecting process metrics through commands such as ps and lsof may fail due to permission restrictions in MacOS sandboxed applications.
brpc/src/bvar/default_variables.cpp:127 read_proc_status] Fail to sscanf: Undefined error: 0
sh: /bin/ps: Operation not permitted
  1. The previous implementation also leaves several process statistics unavailable, like process_faults_minor_second, process_faults_minor_second, process_faults_major.

What is changed and the side effects?

Changed:

This change uses native macOS APIs to collect these metrics:

  • getpid(), getppid(), getpgrp(), and getsid() for process and session IDs.
  • getpriority() for the process nice value.
  • proc_pidinfo() with PROC_PIDTBSDINFO for process flags, state, and terminal information.
  • proc_pidinfo() with PROC_PIDTASKINFO for priority and thread count.
  • getrusage() for process and waited-child page faults and CPU times, converted to clock ticks using sysconf(_SC_CLK_TCK).
  • task_info() with MACH_TASK_BASIC_INFO for virtual and resident memory sizes.
  • proc_pidinfo() with PROC_PIDLISTFDS for open file descriptor counts.

Side effects:

  • Performance effects:

  • Breaking backward compatibility:


Check List:

@ehds ehds changed the title Hds/native macos api Use native APIs for macOS process metrics Oct 13, 2026
@ehds
ehds force-pushed the hds/native_macos_api branch from 4f77516 to 44986d5 Compare October 13, 2026 11:41
@chenBright
chenBright requested a lite review from Copilot October 13, 2026 13:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

4 open findings
What changed in this PR

This PR replaces macOS process-metrics collection that relied on shelling out to ps/lsof with native macOS APIs to improve reliability under sandbox/permission restrictions and to expose additional metrics.

Changes:

  • Reworked macOS read_proc_status to use getpid/getppid/getpgrp/getsid, getpriority, proc_pidinfo, and getrusage.
  • Reworked macOS read_proc_memory to use task_info(MACH_TASK_BASIC_INFO) for RSS/VSS.
  • Implemented macOS FD counting via proc_pidinfo(PROC_PIDLISTFDS) and adjusted FD scan-limit condition.
File Description
src/​bvar/​default_variables.cpp Replaces macOS process metric collection via shell commands with native APIs (proc_pidinfo/getrusage/task_info) and adds FD counting via PROC_PIDLISTFDS.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +113 to +123
const pid_t pid = getpid();
stat.pid = pid;
stat.ppid = getppid();
stat.pgrp = getpgrp();
const pid_t session = getsid(0);
stat.session = session >= 0 ? session : 0;
errno = 0;
const int nice = getpriority(PRIO_PROCESS, 0);
if (errno == 0)
stat.nice = nice;
struct proc_bsdinfo bsd = {};

@ehds ehds Oct 14, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stat is already value-initialized at the beginning of read_proc_status() via stat = ProcStat().

Comment on lines +289 to 297
mach_task_basic_info_data_t info = {};
mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
const kern_return_t result = task_info(
mach_task_self(), MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info), &count);
if (result != KERN_SUCCESS) {
LOG_ONCE(WARNING) << "Cannot read own task memory, Mach error=" << result;
return false;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m has been zero-initialized at the beginning of m = ProcMemory(), it won’t be partially assigned when return false here.

return std::min(count, limit);
*/
return 0;
return bytes / sizeof(proc_fdinfo);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added validation that the returned byte count is a multiple of sizeof(proc_fdinfo) and does not exceed the buffer size. Unexpected lengths are logged and return -1, allowing the reader to retain the last valid count.
Since the buffer holds exactly limit entries, a validated result cannot exceed limit, so an additional clamp is unnecessary.

Comment thread src/bvar/default_variables.cpp Outdated
mach_task_self(), MACH_TASK_BASIC_INFO,
reinterpret_cast<task_info_t>(&info), &count);
if (result != KERN_SUCCESS) {
LOG_ONCE(WARNING) << "Cannot read own task memory, Mach error=" << result;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@wwbmmm
wwbmmm requested a lite review from Copilot October 14, 2026 06:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +163 to +170
static const long ticks_per_second = sysconf(_SC_CLK_TCK);
const auto to_ticks = [](const timeval &time) -> unsigned long {
if (ticks_per_second <= 0) {
return 0;
}
return static_cast<uint64_t>(time.tv_sec) * ticks_per_second +
static_cast<uint64_t>(time.tv_usec) * ticks_per_second / 1000000;
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ticks_per_second is a local static variable which caches the constant tick rate, avoiding repeated sysconf() calls, so the lambda can access it without capturing it.

Comment thread src/bvar/default_variables.cpp Outdated
Comment on lines +407 to +411

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to a thread_local std::vector to reuse the allocation while keeping each thread’s buffer separate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants