TypeScript extension's _startMemoryMonitor continuously spawns ps via pidusage, causing tens of thousands of syscalls per second on macOS

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Cursor’s built-in typescript-language-features extension contains a memory monitor (absent from upstream VS Code) that uses the pidusage npm package to periodically check tsserver’s RSS. On macOS, pidusage does this by spawning ps. Each successful ps invocation generates tens of thousands of syscalls as the OS processes the request. This runs in a continuous setTimeout loop for the lifetime of every Cursor window, resulting in a chronic, measurable syscall storm — particularly damaging on machines running endpoint security software like SentinelOne, which must inspect every spawned process.

Steps to Reproduce

  1. Open any TypeScript project in Cursor on macOS
  2. Run sudo fs_usage -f exec | grep ps in a terminal
  3. Observe a steady stream of ps invocations emanating from Cursor Helper (Plugin)

Expected Behavior

Expected Behavior

The memory monitor should not spawn external processes at all. If tsserver’s RSS is needed, it should be obtained via native macOS APIs (proc_pidinfo with PROC_PIDTASKINFO, or sysctl) — these return memory stats directly from the kernel with negligible syscall overhead and no child process creation.

At minimum, the monitor should only activate when the user has explicitly configured typescript.tsserver.maxMemory. In the default configuration, tsserver has no memory cap, so there is nothing to monitor — the feature should be dormant.

Operating System

MacOS

Version Information

Version: 3.5.38
VSCode Version: 1.105.1
Commit: 009bb5a3600dd98fe1c1f25798f767f686e14750
Date: 2026-05-26T21:32:06.537Z
Layout: editor
Build Type: Stable
Release Track: Default
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Darwin arm64 25.4.0

Additional Information

Root Cause

typescriptServiceClient (in the Cursor-modified typescript-language-features bundle) calls _startMemoryMonitor(server) on tsserver start. This method:

  1. Takes the tsserver process PIDs
  2. Enters a setTimeout loop calling pidusage(pids)
  3. pidusage spawns ps -o etime,pid,ppid,pcpu,rss,time -p <pids> on each iteration
  4. Each ps invocation triggers tens of thousands of syscalls at the OS level (primarily close() calls as macOS’s ps iterates file descriptors across the process table)

Only the memory (RSS) field from the result is ever used — pcpu, etime, and time are fetched and discarded. The monitor fires regardless of whether tsserver is anywhere near a memory limit, and regardless of whether the user has configured typescript.tsserver.maxMemory.

This is a Cursor-specific addition — _startMemoryMonitor and the pidusage dependency do not exist in the upstream VS Code 1.105 TypeScript extension.

Impact

  • Tens of thousands of syscalls per second sustained across every Cursor session on macOS
  • On machines running endpoint security software (SentinelOne, CrowdStrike, etc.), each ps spawn triggers a security scan of the child process, compounding the overhead significantly
  • Elevates Cursor Helper (Plugin) CPU% visible in Activity Monitor
  • Wakes security daemons (endpointsecurity, com.apple.xpc.proxy) on every spawn
  • Compounds with multiple tsserver instances across workspace windows
  • Entirely wasted work: the monitor fires constantly even when tsserver memory usage is nominal

Workaround

Patch extension.js in the Cursor app bundle to replace the ps spawn with /bin/false, which exits immediately with code 1 — pidusage handles this gracefully as “no matching pid found” and the monitor silently no-ops. Language features are unaffected; only the memory warning dialog becomes inert.

sudo sed -i '' 's|i("ps",l,|i("/bin/false",[],|g' \
  /Applications/Cursor.app/Contents/Resources/app/extensions/typescript-language-features/dist/extension.js

Requires a Cursor restart. Must be reapplied after Cursor updates.

Suggested Fixes

  1. Gate the monitor — only call _startMemoryMonitor if the user has explicitly set typescript.tsserver.maxMemory. Most users never configure this, so the monitor is pure overhead for the majority of installs.

  2. Use native macOS process info — RSS for a child PID is available via proc_pidinfo(pid, PROC_PIDTASKINFO, ...) without spawning any subprocess. Zero syscall overhead compared to ps.

  3. Increase the poll interval substantially — if ps must be used, polling infrequently (e.g. every 30–60 seconds) rather than in a tight loop would reduce the impact to acceptable levels.

Does this stop you from using Cursor

No - Cursor works, but with this issue

This is a confirmed bug. The _startMemoryMonitor code that spawns ps via pidusage was disabled on Windows last October after similar reports, but the macOS path was left enabled. Your analysis is spot-on.

Your workaround of patching extension.js with /bin/false is a solid interim fix. We’ve flagged this to extend the Windows disable to macOS as well.