with one click
process-detection
// How dtwiz discovers, identifies, and correlates running OS processes to project directories across Unix and Windows
// How dtwiz discovers, identifies, and correlates running OS processes to project directories across Unix and Windows
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | process-detection |
| description | How dtwiz discovers, identifies, and correlates running OS processes to project directories across Unix and Windows |
This skill covers how dtwiz discovers, identifies, and correlates running OS processes to project directories. Process detection is the foundation for auto-instrumentation — it answers "what's running and where does it live?".
We want to reliably map instrumented services to proper ports.
pkg/installer/
otel_runtime_scan.go # Shared types + project-process correlation
otel_runtime_scan_unix.go # Unix: ps, lsof
otel_runtime_scan_windows.go # Windows: Get-CimInstance Win32_Process, Get-NetTCPConnection
otel_process.go # ManagedProcess lifecycle (launch, port poll, exit tracking)
otel_process_windows.go # Windows child-adoption (leaf PID discovery, watchPID)
otel_process_other.go # Unix no-op stub for adoptExeclChildren
type DetectedProcess struct {
PID int
Command string // Full command line string
WorkingDirectory string // Resolved cwd of the process
Description string // Optional enriched metadata
}
A process is identified by three axes: PID, command line, and working directory. Any correlation logic uses at least two of these.
ps ax -o pid=,command= — one line per process, space-separated PID + full command.lsof -a -d cwd -p <pid> -Fn — output lines prefixed with n contain the path.lsof -a -i TCP -sTCP:LISTEN -p <pid> -Fn -P — parse n lines, extract port after last :.lsof -p <pid> -Fn — verify files loaded by a process even if not on the command line.Get-CimInstance Win32_Process via PowerShell. Fields: ProcessId, CommandLine, WorkingDirectory. Queried with Where-Object filter on CommandLine.Get-NetTCPConnection -State Listen -OwningProcess <pid> with port exclusion filter.Get-Process -Id <pid> | ForEach-Object { $_.Modules } to inspect loaded DLLs/files.| Concern | Unix | Windows |
|---|---|---|
| Process listing | Single ps call, text parsing | PowerShell CimInstance query |
| Working directory | Separate lsof call per PID | Included in CimInstance result |
| Port detection | lsof with TCP filter | Get-NetTCPConnection |
| Performance | Fast (native commands) | Slower (PowerShell startup overhead per call) |
| Parent-child tree | Not directly available from ps | ParentProcessId field in CimInstance |
| PID of self | os.Getpid() filtered out | Same |
detectProcesses(filterTerm, excludeTerms) is the single entry point on both platforms:
"java", "python", "node", "go").os.Getpid()) is always excluded.Each runtime defines its own filter term and exclude list. The pattern: include the runtime binary name, exclude known noise processes that aren't the user's application.
matchingProcessIDs(dirPath, processes) correlates detected processes to a project directory using two independent signals (case-insensitive):
Either condition matching is sufficient. Multiple processes can match a single project.
When parent-child relationships between PIDs can't be determined directly (Unix ps output doesn't include PPID), the system falls back to command-line content matching:
jps -l lists JVMs by main class).ParentProcessId is available from Get-CimInstance Win32_Process, enabling direct tree walks.Many runtimes use wrappers that fork a child process to run the actual app. The wrapper PID is detected first, but the app process is a separate PID.
Unix approach: Use runtime-specific enumeration tools or command-line matching to find the real app process, then probe it for a listening port.
Windows approach: Walk the process tree from the wrapper PID using ParentProcessId, collect descendants matching the runtime binary, then check for listening ports.
os.execl Child Adoption (Windows-only)Some launchers use os.execl() which on Windows spawns a new process and the launcher exits, breaking PID tracking. On Unix, execl replaces the process in-place (same PID) so this isn't an issue.
Windows solution: Query Win32_Process for processes matching the runtime binary name AND the original entrypoint in CommandLine. Filter to leaf processes (those whose PID doesn't appear as any other match's ParentProcessId). Adopt the leaf PID via windows.OpenProcess(SYNCHRONIZE) + WaitForSingleObject.
The adoptExeclChildren function is a no-op on Unix.
After a process is started or adopted, port detection polls until a listening TCP port appears:
Custom portDetector functions can be set per ManagedProcess to handle wrapper/descendant cases where the port is on a child PID rather than the tracked PID.
stopProcesses(pids) handles graceful shutdown:
SIGINT (graceful) then process.Wait().killAndWaitProcess() calls Kill() and polls until PID is gone (no reliable cross-process signal mechanism on Windows)._unix.go with //go:build !windows, _windows.go with //go:build windows).Get-CimInstance call with Where-Object instead of per-PID queries).os.Getpid() to avoid instrumenting dtwiz itself.Description.os.execl pattern), explicit PID adoption re-establishes tracking.strings.ToLower() to handle filesystem and command casing differences across platforms.