Files
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

248 lines
6.6 KiB
Go

//go:build darwin && cgo && !internal_witr_cgo_disabled
package proc
/*
#cgo CFLAGS: -mmacosx-version-min=11.0
#cgo LDFLAGS: -mmacosx-version-min=11.0
#include <libproc.h>
#include <sys/resource.h>
#include <sys/proc_info.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
static int witr_proc_pid_rusage(int pid, int flavor, struct rusage_info_v4 *usage) {
int rv = proc_pid_rusage(pid, flavor, (rusage_info_t)usage);
if (rv != 0) {
return errno;
}
return 0;
}
static int witr_proc_pidtaskinfo(int pid, struct proc_taskinfo *info) {
int rv = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info, PROC_PIDTASKINFO_SIZE);
if (rv < 0) {
return errno;
}
if (rv < PROC_PIDTASKINFO_SIZE) {
return EIO;
}
return 0;
}
static int witr_proc_pidlistfds(int pid, struct proc_fdinfo *fds, int bufsize, int *bytes_used) {
int rv = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds, bufsize);
if (rv < 0) {
if (bytes_used) {
*bytes_used = 0;
}
return errno;
}
if (bytes_used) {
*bytes_used = rv;
}
return 0;
}
static int witr_proc_pidfdinfo_vnode(int pid, int fd, struct vnode_fdinfowithpath *info) {
int rv = proc_pidfdinfo(pid, fd, PROC_PIDFDVNODEPATHINFO, info, PROC_PIDFDVNODEPATHINFO_SIZE);
if (rv < 0) {
return errno;
}
if (rv < PROC_PIDFDVNODEPATHINFO_SIZE) {
return EIO;
}
return 0;
}
static int witr_proc_pidfdinfo_socket(int pid, int fd, struct socket_fdinfo *info) {
int rv = proc_pidfdinfo(pid, fd, PROC_PIDFDSOCKETINFO, info, PROC_PIDFDSOCKETINFO_SIZE);
if (rv < 0) {
return errno;
}
if (rv < PROC_PIDFDSOCKETINFO_SIZE) {
return EIO;
}
return 0;
}
static void witr_format_socket(const struct socket_fdinfo *info, char *buf, size_t buf_len) {
if (buf_len == 0) {
return;
}
buf[0] = '\0';
if (!info) {
return;
}
const struct in_sockinfo *ini = &info->psi.soi_proto.pri_tcp.tcpsi_ini;
char laddr[INET6_ADDRSTRLEN];
char faddr[INET6_ADDRSTRLEN];
laddr[0] = '\0';
faddr[0] = '\0';
uint16_t lport = ntohs((uint16_t)ini->insi_lport);
uint16_t fport = ntohs((uint16_t)ini->insi_fport);
if (ini->insi_vflag & INI_IPV4) {
inet_ntop(AF_INET, &ini->insi_laddr.ina_46.i46a_addr4, laddr, sizeof(laddr));
inet_ntop(AF_INET, &ini->insi_faddr.ina_46.i46a_addr4, faddr, sizeof(faddr));
} else if (ini->insi_vflag & INI_IPV6) {
inet_ntop(AF_INET6, &ini->insi_laddr.ina_6, laddr, sizeof(laddr));
inet_ntop(AF_INET6, &ini->insi_faddr.ina_6, faddr, sizeof(faddr));
}
if (laddr[0] == '\0') {
strlcpy(laddr, "?", sizeof(laddr));
}
if (faddr[0] == '\0') {
strlcpy(faddr, "?", sizeof(faddr));
}
snprintf(buf, buf_len, "%s:%u -> %s:%u", laddr, lport, faddr, fport);
}
*/
import "C"
import (
"errors"
"fmt"
"unsafe"
"github.com/pranshuparmar/witr/pkg/model"
)
func readDarwinIO(pid int) (model.IOStats, error) {
var stats model.IOStats
var usage C.struct_rusage_info_v4
errno := C.witr_proc_pid_rusage(C.int(pid), C.RUSAGE_INFO_V4, &usage)
if errno != 0 {
switch errno {
case C.ESRCH, C.EPERM:
return stats, nil
default:
return stats, fmt.Errorf("proc_pid_rusage: %d", errno)
}
}
stats.ReadBytes = uint64(usage.ri_diskio_bytesread)
stats.WriteBytes = uint64(usage.ri_diskio_byteswritten)
return stats, nil
}
func readDarwinTaskInfo(pid int) (model.MemoryInfo, int, error) {
var info C.struct_proc_taskinfo
if errno := C.witr_proc_pidtaskinfo(C.int(pid), &info); errno != 0 {
switch errno {
case C.ESRCH, C.EPERM:
return model.MemoryInfo{}, 0, nil
default:
return model.MemoryInfo{}, 0, fmt.Errorf("proc_pidinfo taskinfo: %d", errno)
}
}
mem := model.MemoryInfo{
VMS: uint64(info.pti_virtual_size),
RSS: uint64(info.pti_resident_size),
VMSMB: float64(info.pti_virtual_size) / (1024 * 1024),
RSSMB: float64(info.pti_resident_size) / (1024 * 1024),
}
return mem, int(info.pti_threadnum), nil
}
func readDarwinFDs(pid int) (int, []string, error) {
const bytesPerEntry = int(C.sizeof_struct_proc_fdinfo)
entries := make([]C.struct_proc_fdinfo, 256)
for {
var used C.int
errno := C.witr_proc_pidlistfds(C.int(pid), &entries[0], C.int(len(entries)*bytesPerEntry), &used)
if errno == C.EINVAL && len(entries) < 16384 {
entries = make([]C.struct_proc_fdinfo, len(entries)*2)
continue
}
if errno != 0 {
switch errno {
case C.ESRCH, C.EPERM:
return 0, nil, nil
default:
return 0, nil, fmt.Errorf("proc_pidinfo listfds: %d", errno)
}
}
bytesUsed := int(used)
if bytesUsed%bytesPerEntry != 0 {
return 0, nil, errors.New("listfds returned partial record")
}
count := bytesUsed / bytesPerEntry
return count, formatFDEntries(pid, entries[:count]), nil
}
}
func formatFDEntries(pid int, entries []C.struct_proc_fdinfo) []string {
var out []string
for _, entry := range entries {
if len(out) >= 10 {
break
}
fd := int(entry.proc_fd)
label := fdTypeLabel(entry.proc_fdtype)
switch entry.proc_fdtype {
case C.PROX_FDTYPE_VNODE:
var vnode C.struct_vnode_fdinfowithpath
if errno := C.witr_proc_pidfdinfo_vnode(C.int(pid), C.int(fd), &vnode); errno == 0 {
path := C.GoString(&vnode.pvip.vip_path[0])
if path == "" {
path = "<anonymous>"
}
out = append(out, fmt.Sprintf("%d -> %s", fd, path))
continue
}
case C.PROX_FDTYPE_SOCKET:
var sock C.struct_socket_fdinfo
if errno := C.witr_proc_pidfdinfo_socket(C.int(pid), C.int(fd), &sock); errno == 0 {
buf := make([]byte, 128)
C.witr_format_socket(&sock, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
desc := C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
out = append(out, fmt.Sprintf("%d -> %s", fd, desc))
continue
}
}
out = append(out, fmt.Sprintf("%d (%s)", fd, label))
}
return out
}
func fdTypeLabel(fdType C.uint32_t) string {
switch fdType {
case C.PROX_FDTYPE_VNODE:
return "vnode"
case C.PROX_FDTYPE_SOCKET:
return "socket"
case C.PROX_FDTYPE_PIPE:
return "pipe"
case C.PROX_FDTYPE_KQUEUE:
return "kqueue"
case C.PROX_FDTYPE_FSEVENTS:
return "fsevents"
case C.PROX_FDTYPE_NEXUS:
return "nexus"
case C.PROX_FDTYPE_NETPOLICY:
return "netpolicy"
default:
return fmt.Sprintf("fdtype-%d", fdType)
}
}
func describeSocket(info *C.struct_socket_fdinfo) string {
buf := make([]byte, 128)
C.witr_format_socket(info, (*C.char)(unsafe.Pointer(&buf[0])), C.size_t(len(buf)))
desc := C.GoString((*C.char)(unsafe.Pointer(&buf[0])))
if desc == "" {
return "socket"
}
return desc
}