Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ func (p *Process) PpidWithContext(_ context.Context) (int32, error) {
}

func (p *Process) NameWithContext(ctx context.Context) (string, error) {
if p.name != "" {
return p.name, nil
}
if p.Pid == 0 {
return "System Idle Process", nil
}
Expand Down Expand Up @@ -587,6 +590,9 @@ func (p *Process) NumFDsWithContext(_ context.Context) (int32, error) {
}

func (p *Process) NumThreadsWithContext(_ context.Context) (int32, error) {
if p.numThreads != 0 {
return p.numThreads, nil
}
ppid, ret, _, err := getFromSnapProcess(p.Pid)
if err != nil {
return 0, err
Expand All @@ -598,6 +604,8 @@ func (p *Process) NumThreadsWithContext(_ context.Context) (int32, error) {
p.setPpid(ppid)
}

p.numThreads = ret

return ret, nil
}

Expand Down Expand Up @@ -900,6 +908,29 @@ func getFromSnapProcess(pid int32) (int32, int32, string, error) { //nolint:unpa
return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid)
}

func buildSnapProcessMap() (map[uint32]windows.ProcessEntry32, error) {
snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
if err != nil {
return nil, err
}
defer windows.CloseHandle(snap)

var pe32 windows.ProcessEntry32
pe32.Size = uint32(unsafe.Sizeof(pe32))
if err = windows.Process32First(snap, &pe32); err != nil {
return nil, err
}

snapMap := make(map[uint32]windows.ProcessEntry32)
for {
snapMap[pe32.ProcessID] = pe32
if err = windows.Process32Next(snap, &pe32); err != nil {
break
}
}
Comment on lines +927 to +935
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

buildSnapProcessMap breaks out of the loop on any Process32Next error and returns the map, which can silently return an incomplete snapshot if Process32Next fails for reasons other than "no more files". Please explicitly treat windows.ERROR_NO_MORE_FILES (or the appropriate sentinel) as the normal termination condition, and return the error for any other failure.

Copilot uses AI. Check for mistakes.
return snapMap, nil
}

func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
out := []*Process{}

Expand All @@ -908,11 +939,23 @@ func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
return out, fmt.Errorf("could not get Processes %w", err)
}

snapMap, err := buildSnapProcessMap()
if err != nil {
return nil, err
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

On error from buildSnapProcessMap, ProcessesWithContext returns a nil slice (return nil, err), while other error paths in this function (and other OS implementations) return the already-initialized out slice. For API consistency and to avoid surprising nil slices for callers that don’t nil-check, consider returning out here as well (and optionally wrapping the error with context like the earlier PidsWithContext failure).

Suggested change
return nil, err
return out, fmt.Errorf("could not build process snapshot %w", err)

Copilot uses AI. Check for mistakes.
}

for _, pid := range pids {
p, err := NewProcessWithContext(ctx, pid)
if err != nil {
continue
}

if entry, ok := snapMap[uint32(pid)]; ok {
p.name = windows.UTF16ToString(entry.ExeFile[:])
p.setPpid(int32(entry.ParentProcessID))
p.numThreads = int32(entry.Threads)
}

out = append(out, p)
}

Expand Down
Loading