func parseMessage(m genetlink.Message, typeAggr uint16) (*Stats, error) {
	attrs, err := netlink.UnmarshalAttributes(m.Data)
	if err != nil {
		return nil, err
	}
	for _, a := range attrs {
		if a.Type != typeAggr {
			continue
		}
		nattrs, err := netlink.UnmarshalAttributes(a.Data)
		if err != nil {
			return nil, err
		}
		for _, na := range nattrs {
			if na.Type != unix.TASKSTATS_TYPE_STATS {
				continue
			}
			if wantV8, wantV9, wantV10, got := sizeofTaskstatsV8, sizeofTaskstatsV9, sizeOfTaskstatsV10, len(na.Data); wantV8 != got && wantV9 != got && wantV10 != got {
				return nil, fmt.Errorf("unexpected taskstats structure size, want %d (v8) or %d (v9) or %d (v10), got %d", wantV8, wantV9, wantV10, got)
			}
			return parseStats(*(*unix.Taskstats)(unsafe.Pointer(&na.Data[0])))
		}
	}
	return nil, os.ErrNotExist
}
parseMessage attempts to parse a Stats structure from a generic netlink message.
package taskstats
import (
	"fmt"
	"os"
	"unsafe"
	"github.com/mdlayher/genetlink"
	"github.com/mdlayher/netlink"
	"github.com/mdlayher/netlink/nlenc"
	"golang.org/x/sys/unix"
)
const (
	sizeofTaskstatsV8 = int(unsafe.Offsetof(unix.Taskstats{}.Thrashing_count))
	sizeofTaskstatsV9 = int(unsafe.Offsetof(unix.Taskstats{}.Ac_btime64))
	sizeOfTaskstatsV10 = int(unsafe.Sizeof(unix.Taskstats{}))
	sizeofCGroupStats = int(unsafe.Sizeof(unix.CGroupStats{}))
)
var _ osClient = &client{}
type client struct {
	c      *genetlink.Conn
	family genetlink.Family
}
func newClient() (*client, error) {
	c, err := genetlink.Dial(nil)
	if err != nil {
		return nil, err
	}
	return initClient(c)
}
func initClient(c *genetlink.Conn) (*client, error) {
	f, err := c.GetFamily(unix.TASKSTATS_GENL_NAME)
	if err != nil {
		_ = c.Close()
		return nil, err
	}
	return &client{
		c:      c,
		family: f,
	}, nil
}
func (c *client) Close() error {
	return c.c.Close()
}
func (c *client) PID(pid int) (*Stats, error) {
	return c.getStats(pid, unix.TASKSTATS_CMD_ATTR_PID, unix.TASKSTATS_TYPE_AGGR_PID)
}
func (c *client) TGID(tgid int) (*Stats, error) {
	return c.getStats(tgid, unix.TASKSTATS_CMD_ATTR_TGID, unix.TASKSTATS_TYPE_AGGR_TGID)
}
func (c *client) getStats(id int, cmdAttr, typeAggr uint16) (*Stats, error) {
	attrs := []netlink.Attribute{{
		Type: cmdAttr,
		Data: nlenc.Uint32Bytes(uint32(id)),
	}}
	msg, err := c.execute(unix.TASKSTATS_CMD_GET, attrs)
	if err != nil {
		return nil, err
	}
	return parseMessage(*msg, typeAggr)
}
func (c *client) CGroupStats(path string) (*CGroupStats, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	attrs := []netlink.Attribute{{
		Type: unix.CGROUPSTATS_CMD_ATTR_FD,
		Data: nlenc.Uint32Bytes(uint32(f.Fd())),
	}}
	msg, err := c.execute(unix.CGROUPSTATS_CMD_GET, attrs)
	if err != nil {
		return nil, err
	}
	return parseCGroupMessage(*msg)
}
func (c *client) execute(cmd uint8, attrs []netlink.Attribute) (*genetlink.Message, error) {
	b, err := netlink.MarshalAttributes(attrs)
	if err != nil {
		return nil, err
	}
	msg := genetlink.Message{
		Header: genetlink.Header{
			Command: cmd,
			Version: unix.TASKSTATS_VERSION,
		},
		Data: b,
	}
	msgs, err := c.c.Execute(msg, c.family.ID, netlink.Request)
	if err != nil {
		oerr, ok := err.(*netlink.OpError)
		if !ok {
			return nil, fmt.Errorf("taskstats: netlink operation returned non-netlink error (please file a bug: https://github.com/mdlayher/taskstats): %v", err)
		}
		return nil, oerr.Err
	}
	if l := len(msgs); l != 1 {
		return nil, fmt.Errorf("taskstats: unexpected number of response messages: %d", l)
	}
	return &msgs[0], nil
}
func parseCGroupMessage(m genetlink.Message) (*CGroupStats, error) {
	attrs, err := netlink.UnmarshalAttributes(m.Data)
	if err != nil {
		return nil, err
	}
	for _, a := range attrs {
		if a.Type != unix.CGROUPSTATS_TYPE_CGROUP_STATS {
			continue
		}
		if want, got := sizeofCGroupStats, len(a.Data); want != got {
			return nil, fmt.Errorf("unexpected cgroupstats structure size, want %d, got %d", want, got)
		}
		cs := *(*unix.CGroupStats)(unsafe.Pointer(&a.Data[0]))
		return parseCGroupStats(cs)
	}
	return nil, os.ErrNotExist
}