Blame sysdeps/freebsd/procmap.c

Packit d37888
/* Copyright (C) 1998 Joshua Sled
Packit d37888
   This file is part of LibGTop 1.0.
Packit d37888
Packit d37888
   Contributed by Joshua Sled <jsled@xcf.berkeley.edu>, July 1998.
Packit d37888
Packit d37888
   LibGTop is free software; you can redistribute it and/or modify it
Packit d37888
   under the terms of the GNU General Public License as published by
Packit d37888
   the Free Software Foundation; either version 2 of the License,
Packit d37888
   or (at your option) any later version.
Packit d37888
Packit d37888
   LibGTop is distributed in the hope that it will be useful, but WITHOUT
Packit d37888
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit d37888
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit d37888
   for more details.
Packit d37888
Packit d37888
   You should have received a copy of the GNU General Public License
Packit d37888
   along with LibGTop; see the file COPYING. If not, write to the
Packit d37888
   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Packit d37888
   Boston, MA 02110-1301, USA.
Packit d37888
*/
Packit d37888
Packit d37888
#include <config.h>
Packit d37888
#include <glibtop.h>
Packit d37888
#include <glibtop/error.h>
Packit d37888
#include <glibtop/procmap.h>
Packit d37888
Packit d37888
#include <glibtop_suid.h>
Packit d37888
Packit d37888
#include <kvm.h>
Packit d37888
#include <sys/param.h>
Packit d37888
#include <sys/proc.h>
Packit d37888
#include <sys/resource.h>
Packit d37888
#include <vm/vm_object.h>
Packit d37888
#include <vm/vm_map.h>
Packit d37888
#include <vm/vm.h>
Packit d37888
Packit d37888
#define _KVM_VNODE
Packit d37888
#include <sys/vnode.h>
Packit d37888
#undef _KVM_VNODE
Packit d37888
Packit d37888
#include <sys/conf.h>
Packit d37888
#if (__FreeBSD_version >= 800038) || (__FreeBSD_kernel_version >= 800038)
Packit d37888
#define _WANT_FILE
Packit d37888
#include <sys/file.h>
Packit d37888
#undef _WANT_FILE
Packit d37888
#else
Packit d37888
#define _KERNEL
Packit d37888
#include <sys/file.h>
Packit d37888
#undef _KERNEL
Packit d37888
#endif
Packit d37888
#define _KERNEL
Packit d37888
#include <sys/mount.h>
Packit d37888
#include <ufs/ufs/quota.h>
Packit d37888
#include <ufs/ufs/inode.h>
Packit d37888
#include <fs/devfs/devfs.h>
Packit d37888
#if (__FreeBSD_version >= 600006) || defined(__FreeBSD_kernel__)
Packit d37888
#include <fs/devfs/devfs_int.h>
Packit d37888
#endif
Packit d37888
#undef _KERNEL
Packit d37888
Packit d37888
Packit d37888
#if (__FreeBSD_version >= 1101001)
Packit d37888
#define _KERNEL
Packit d37888
#include <ufs/ufs/extattr.h>
Packit d37888
#include <ufs/ufs/ufsmount.h>
Packit d37888
#undef _KERNEL
Packit d37888
#endif
Packit d37888
Packit d37888
Packit d37888
#include <sys/ucred.h>
Packit d37888
#include <sys/sysctl.h>
Packit d37888
Packit d37888
static const unsigned long _glibtop_sysdeps_proc_map =
Packit d37888
        (1L << GLIBTOP_PROC_MAP_TOTAL) + (1L << GLIBTOP_PROC_MAP_NUMBER) +
Packit d37888
        (1L << GLIBTOP_PROC_MAP_SIZE);
Packit d37888
Packit d37888
static const unsigned long _glibtop_sysdeps_map_entry =
Packit d37888
        (1L << GLIBTOP_MAP_ENTRY_START) + (1L << GLIBTOP_MAP_ENTRY_END) +
Packit d37888
        (1L << GLIBTOP_MAP_ENTRY_OFFSET) + (1L << GLIBTOP_MAP_ENTRY_PERM) +
Packit d37888
        (1L << GLIBTOP_MAP_ENTRY_INODE) + (1L << GLIBTOP_MAP_ENTRY_DEVICE);
Packit d37888
Packit d37888
#if (__FreeBSD_version >= 600006) || defined(__FreeBSD_kernel__)
Packit d37888
void _glibtop_sysdeps_freebsd_dev_inode (glibtop *server, struct vnode *vnode, struct vnode *vn, guint64 *inum, guint64 *dev);
Packit d37888
Packit d37888
void
Packit d37888
_glibtop_sysdeps_freebsd_dev_inode (glibtop *server, struct vnode *vnode,
Packit d37888
                                    struct vnode *vn, guint64 *inum,
Packit d37888
                                    guint64 *dev)
Packit d37888
{
Packit d37888
        char *tagptr;
Packit d37888
        char tagstr[12];
Packit d37888
        enum FS_TYPE { UNKNOWN, IS_UFS, IS_ZFS };
Packit d37888
        int fs_type = UNKNOWN;
Packit d37888
        struct inode inode;
Packit d37888
        struct cdev_priv priv;
Packit d37888
#if __FreeBSD_version < 800039
Packit d37888
        struct cdev si;
Packit d37888
#endif
Packit d37888
Packit d37888
        *inum = 0;
Packit d37888
        *dev = 0;
Packit d37888
Packit d37888
        if (kvm_read (server->machine->kd, (gulong) &vnode->v_tag,
Packit d37888
 	             (char *) &tagptr, sizeof (tagptr)) != sizeof (tagptr) ||
Packit d37888
            kvm_read (server->machine->kd, (gulong) tagptr,
Packit d37888
		     (char *) tagstr, sizeof (tagstr)) != sizeof (tagstr))
Packit d37888
        {
Packit d37888
                glibtop_warn_io_r (server, "kvm_read (tagptr)");
Packit d37888
                return;
Packit d37888
        }
Packit d37888
Packit d37888
        tagstr[sizeof(tagstr) - 1] = '\0';
Packit d37888
Packit d37888
        if (!strcmp(tagstr, "ufs")) {
Packit d37888
                fs_type = IS_UFS;
Packit d37888
        } else if (!strcmp(tagstr, "zfs")) {
Packit d37888
                fs_type = IS_ZFS;
Packit d37888
        } else {
Packit d37888
                glibtop_warn_io_r (server, "ignoring fstype %s", tagstr);
Packit d37888
                return;
Packit d37888
        }
Packit d37888
Packit d37888
        if (kvm_read (server->machine->kd, (gulong) VTOI(vn), (char *) &inode,
Packit d37888
 	              sizeof (inode)) != sizeof (inode))
Packit d37888
        {
Packit d37888
                glibtop_warn_io_r (server, "kvm_read (inode)");
Packit d37888
                return;
Packit d37888
        }
Packit d37888
Packit d37888
Packit d37888
        if (fs_type == IS_ZFS) {
Packit d37888
		/* FIXME: I have no idea about what is the actual layout of what we've read
Packit d37888
		   but the inode number is definitely at offset 16, 8 bytes of amd64.
Packit d37888
		   *inum = *(guint64*) ((unsigned char*)&inode + 16);
Packit d37888
		   So this is really hugly, but I don't have anything better for now.
Packit d37888
Packit d37888
                   Actually, this looks like a znode_t as described in kernel's zfs_znode.h.
Packit d37888
                   I don't have that header file, so let's just mimic that.
Packit d37888
                */
Packit d37888
Packit d37888
                struct my_zfsvfs {
Packit d37888
                        /* vfs_t */ void *z_vfs;
Packit d37888
                        /* zfsvfs_t */ void *z_parent;
Packit d37888
                        /* objset_t */ void *z_os;
Packit d37888
                        uint64_t z_root;
Packit d37888
                        /* ... */
Packit d37888
                };
Packit d37888
Packit d37888
                typedef struct my_znode {
Packit d37888
                        struct my_zfsvfs *z_zfsvfs;
Packit d37888
                        /* vnode_t */ void *z_vnode;
Packit d37888
                        uint64_t z_id;
Packit d37888
                        /* ... */
Packit d37888
                } my_znode_t;
Packit d37888
Packit d37888
                G_STATIC_ASSERT(sizeof(my_znode_t) <= sizeof(struct inode));
Packit d37888
Packit d37888
                my_znode_t* znode = (my_znode_t*)&inode;
Packit d37888
                *inum = znode->z_id;
Packit d37888
Packit d37888
                struct my_zfsvfs zvfs;
Packit d37888
Packit d37888
                if (kvm_read(server->machine->kd,
Packit d37888
                             (unsigned long)(znode->z_zfsvfs),
Packit d37888
                             &zvfs, sizeof zvfs) != sizeof zvfs) {
Packit d37888
                        glibtop_warn_io_r(server, "kvm_read (z_zfsvfs)");
Packit d37888
                        return;
Packit d37888
                }
Packit d37888
Packit d37888
                *dev = zvfs.z_root;
Packit d37888
        }
Packit d37888
        else if (fs_type == IS_UFS) {
Packit d37888
		/* Set inum as soon as possible, so that if the next kvm_reads fail
Packit d37888
		   we still have something */
Packit d37888
                *inum = inode.i_number;
Packit d37888
Packit d37888
Packit d37888
#if (__FreeBSD_version >= 1101001)
Packit d37888
/*
Packit d37888
  The ufs struct inode changed between 11.0 and 11.1.
Packit d37888
Packit d37888
  commit 20f1e8ac63b58708989267ea34a6aefa90b46577
Packit d37888
  Author: kib <kib@FreeBSD.org>
Packit d37888
  Date:   Sat Sep 17 16:47:34 2016 +0000
Packit d37888
Packit d37888
  Reduce size of ufs inode.
Packit d37888
  [...]
Packit d37888
*/
Packit d37888
		struct ufsmount um;
Packit d37888
Packit d37888
		if (kvm_read(server->machine->kd, (gulong)inode.i_ump, &um, sizeof um) != sizeof um) {
Packit d37888
			glibtop_warn_io_r (server, "kvm_read (ufsmount)");
Packit d37888
			return;
Packit d37888
		}
Packit d37888
Packit d37888
		if (kvm_read(server->machine->kd, (gulong)cdev2priv(um.um_dev), &priv, sizeof priv) != sizeof priv) {
Packit d37888
			glibtop_warn_io_r (server, "kvm_read (priv)");
Packit d37888
			return;
Packit d37888
		}
Packit d37888
Packit d37888
		*dev = priv.cdp_inode;
Packit d37888
Packit d37888
#else /* older versions */
Packit d37888
#if (__FreeBSD_version >= 800039) || (__FreeBSD_kernel_version >= 800039)
Packit d37888
        if (kvm_read (server->machine->kd, (gulong) cdev2priv(inode.i_dev), (char *) &priv,
Packit d37888
		      sizeof (priv))
Packit d37888
#else
Packit d37888
        if (kvm_read (server->machine->kd, (gulong) inode.i_dev, (char *) &si,
Packit d37888
	              sizeof (si)) != sizeof (si) ||
Packit d37888
            kvm_read (server->machine->kd, (gulong) si.si_priv, (char *) &priv,
Packit d37888
		      sizeof (priv))
Packit d37888
#endif
Packit d37888
	    != sizeof (priv))
Packit d37888
        {
Packit d37888
                glibtop_warn_io_r (server, "kvm_read (priv)");
Packit d37888
                return;
Packit d37888
        }
Packit d37888
Packit d37888
        *dev = (guint64) priv.cdp_inode;
Packit d37888
#endif /* older versions */
Packit d37888
Packit d37888
	    } /* end-if IS_UFS */
Packit d37888
}
Packit d37888
#endif
Packit d37888
Packit d37888
/* Init function. */
Packit d37888
Packit d37888
void
Packit d37888
_glibtop_init_proc_map_p (glibtop *server)
Packit d37888
{
Packit d37888
        server->sysdeps.proc_map = _glibtop_sysdeps_proc_map;
Packit d37888
}
Packit d37888
Packit d37888
/* Provides detailed information about a process. */
Packit d37888
Packit d37888
glibtop_map_entry *
Packit d37888
glibtop_get_proc_map_p (glibtop *server, glibtop_proc_map *buf,
Packit d37888
                        pid_t pid)
Packit d37888
{
Packit d37888
        struct kinfo_proc *pinfo;
Packit d37888
        struct vm_map_entry entry, *first;
Packit d37888
        struct vmspace vmspace;
Packit d37888
        struct vm_object object;
Packit d37888
        GArray *maps;
Packit d37888
        struct vnode vnode;
Packit d37888
        int count;
Packit d37888
        int update = 0;
Packit d37888
Packit d37888
        memset (buf, 0, sizeof (glibtop_proc_map));
Packit d37888
Packit d37888
        /* It does not work for the swapper task. */
Packit d37888
        if (pid == 0) return NULL;
Packit d37888
Packit d37888
        /*return (glibtop_map_entry*) g_array_free(maps, TRUE);*/
Packit d37888
Packit d37888
        glibtop_suid_enter (server);
Packit d37888
Packit d37888
        /* Get the process data */
Packit d37888
        pinfo = kvm_getprocs (server->machine->kd, KERN_PROC_PID, pid, &count);
Packit d37888
        if ((pinfo == NULL) || (count < 1)) {
Packit d37888
                glibtop_warn_io_r (server, "kvm_getprocs (%d)", pid);
Packit d37888
		glibtop_suid_leave (server);
Packit d37888
                return NULL;
Packit d37888
        }
Packit d37888
Packit d37888
        /* Now we get the memory maps. */
Packit d37888
Packit d37888
        if (kvm_read (server->machine->kd,
Packit d37888
                        (gulong) pinfo [0].ki_vmspace,
Packit d37888
                        (char *) &vmspace, sizeof (vmspace)) != sizeof (vmspace)) {
Packit d37888
                glibtop_warn_io_r (server, "kvm_read (vmspace)");
Packit d37888
		glibtop_suid_leave (server);
Packit d37888
                return NULL;
Packit d37888
        }
Packit d37888
Packit d37888
        first = vmspace.vm_map.header.next;
Packit d37888
Packit d37888
        if (kvm_read (server->machine->kd,
Packit d37888
                        (gulong) vmspace.vm_map.header.next,
Packit d37888
                        (char *) &entry, sizeof (entry)) != sizeof (entry)) {
Packit d37888
                glibtop_warn_io_r (server, "kvm_read (entry)");
Packit d37888
		glibtop_suid_leave (server);
Packit d37888
                return NULL;
Packit d37888
        }
Packit d37888
Packit d37888
        /* Walk through the `vm_map_entry' list ... */
Packit d37888
Packit d37888
        /* I tested this a few times with `mmap'; as soon as you write
Packit d37888
         * to the mmap'ed area, the object type changes from OBJT_VNODE
Packit d37888
         * to OBJT_DEFAULT so if seems this really works. */
Packit d37888
Packit d37888
        maps = g_array_sized_new(FALSE, FALSE, sizeof(glibtop_map_entry),
Packit d37888
                                 vmspace.vm_map.nentries);
Packit d37888
Packit d37888
        do {
Packit d37888
                glibtop_map_entry *mentry;
Packit d37888
                guint64 inum, dev;
Packit d37888
                guint len;
Packit d37888
Packit d37888
                if (update) {
Packit d37888
                        if (kvm_read (server->machine->kd,
Packit d37888
                                        (gulong) entry.next,
Packit d37888
                                        (char *) &entry, sizeof (entry)) != sizeof (entry)) {
Packit d37888
                                glibtop_warn_io_r (server, "kvm_read (entry)");
Packit d37888
                                continue;
Packit d37888
                        }
Packit d37888
                } else {
Packit d37888
                        update = 1;
Packit d37888
                }
Packit d37888
Packit d37888
                if (entry.eflags & (MAP_ENTRY_IS_SUB_MAP))
Packit d37888
                        continue;
Packit d37888
Packit d37888
                if (!entry.object.vm_object)
Packit d37888
                        continue;
Packit d37888
Packit d37888
                /* We're only interested in `vm_object's */
Packit d37888
Packit d37888
                if (kvm_read (server->machine->kd,
Packit d37888
                                (gulong) entry.object.vm_object,
Packit d37888
                                (char *) &object, sizeof (object)) != sizeof (object)) {
Packit d37888
                        glibtop_warn_io_r (server, "kvm_read (object)");
Packit d37888
                        continue;
Packit d37888
                }
Packit d37888
Packit d37888
                /* If the object is of type vnode, add its size */
Packit d37888
Packit d37888
                if (object.type != OBJT_VNODE)
Packit d37888
                        continue;
Packit d37888
Packit d37888
                if (!object.handle)
Packit d37888
                        continue;
Packit d37888
Packit d37888
                if (kvm_read (server->machine->kd,
Packit d37888
                                (gulong) object.handle,
Packit d37888
                                (char *) &vnode, sizeof (vnode)) != sizeof (vnode)) {
Packit d37888
                        glibtop_warn_io_r (server, "kvm_read (vnode)");
Packit d37888
                        continue;
Packit d37888
                }
Packit d37888
Packit d37888
                switch (vnode.v_type) {
Packit d37888
                case VNON:
Packit d37888
                case VBAD:
Packit d37888
                        continue;
Packit d37888
                default:
Packit d37888
#if (__FreeBSD_version < 600006) && !defined(__FreeBSD_kernel__)
Packit d37888
                        inum = vnode.v_cachedid;
Packit d37888
                        dev = vnode.v_cachedfs;
Packit d37888
Packit d37888
#else
Packit d37888
                        _glibtop_sysdeps_freebsd_dev_inode (server,
Packit d37888
					(struct vnode *) object.handle,
Packit d37888
					&vnode, &inum, &dev;;
Packit d37888
#endif
Packit d37888
                        break;
Packit d37888
                }
Packit d37888
Packit d37888
                len = maps->len;
Packit d37888
                g_array_set_size(maps, len + 1);
Packit d37888
                mentry = &g_array_index(maps, glibtop_map_entry, len);
Packit d37888
Packit d37888
                memset (mentry, 0, sizeof (glibtop_map_entry));
Packit d37888
Packit d37888
                mentry->flags  = _glibtop_sysdeps_map_entry;
Packit d37888
                mentry->start  = (guint64) entry.start;
Packit d37888
                mentry->end    = (guint64) entry.end;
Packit d37888
                mentry->offset = (guint64) entry.offset;
Packit d37888
                mentry->device = (guint64) dev;
Packit d37888
                mentry->inode  = (guint64) inum;
Packit d37888
Packit d37888
                mentry->perm   = (guint64) 0;
Packit d37888
Packit d37888
                if (entry.protection & VM_PROT_READ)
Packit d37888
                        mentry->perm |= GLIBTOP_MAP_PERM_READ;
Packit d37888
                if (entry.protection & VM_PROT_WRITE)
Packit d37888
                        mentry->perm |= GLIBTOP_MAP_PERM_WRITE;
Packit d37888
                if (entry.protection & VM_PROT_EXECUTE)
Packit d37888
                        mentry->perm |= GLIBTOP_MAP_PERM_EXECUTE;
Packit d37888
Packit d37888
        } while (entry.next != first);
Packit d37888
Packit d37888
        glibtop_suid_leave (server);
Packit d37888
Packit d37888
        buf->flags = _glibtop_sysdeps_proc_map;
Packit d37888
Packit d37888
        buf->number = (guint64) maps->len;
Packit d37888
        buf->size   = (guint64) sizeof (glibtop_map_entry);
Packit d37888
        buf->total  = (guint64) (buf->number * buf->size);
Packit d37888
Packit d37888
        return (glibtop_map_entry*) g_array_free(maps, FALSE);
Packit d37888
}