Encode stat to bytes manually, instead of calling CopyObjectOut.

CopyObjectOut grows its destination byte slice incrementally, causing
many small slice allocations on the heap. This leads to increased GC and
noticeably slower stat calls.

PiperOrigin-RevId: 233140904
Change-Id: Ieb90295dd8dd45b3e56506fef9d7f86c92e97d97
This commit is contained in:
Nicolas Lacasse 2019-02-08 15:47:25 -08:00 committed by Shentubot
parent 967326131a
commit e884168e1e
2 changed files with 50 additions and 15 deletions

View File

@ -19,6 +19,7 @@ import (
"strings" "strings"
"gvisor.googlesource.com/gvisor/pkg/abi" "gvisor.googlesource.com/gvisor/pkg/abi"
"gvisor.googlesource.com/gvisor/pkg/binary"
) )
// Constants for open(2). // Constants for open(2).
@ -177,6 +178,9 @@ type Stat struct {
X_unused [3]int64 X_unused [3]int64
} }
// SizeOfStat is the size of a Stat struct.
var SizeOfStat = binary.Size(Stat{})
// FileMode represents a mode_t. // FileMode represents a mode_t.
type FileMode uint type FileMode uint

View File

@ -16,6 +16,7 @@ package linux
import ( import (
"gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/binary"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
@ -124,21 +125,51 @@ func stat(t *kernel.Task, d *fs.Dirent, dirPath bool, statAddr usermem.Addr) err
mode |= linux.ModeSocket mode |= linux.ModeSocket
} }
_, err = t.CopyOut(statAddr, linux.Stat{ // We encode the stat struct to bytes manually, as stat() is a very
Dev: uint64(d.Inode.StableAttr.DeviceID), // common syscall for many applications, and t.CopyObjectOut has
Rdev: uint64(linux.MakeDeviceID(d.Inode.StableAttr.DeviceFileMajor, d.Inode.StableAttr.DeviceFileMinor)), // noticeable performance impact due to its many slice allocations and
Ino: uint64(d.Inode.StableAttr.InodeID), // use of reflection.
Nlink: uattr.Links, b := make([]byte, 0, linux.SizeOfStat)
Mode: mode | uint32(uattr.Perms.LinuxMode()),
UID: uint32(uattr.Owner.UID.In(t.UserNamespace()).OrOverflow()), // Dev (uint64)
GID: uint32(uattr.Owner.GID.In(t.UserNamespace()).OrOverflow()), b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.DeviceID))
Size: uattr.Size, // Ino (uint64)
Blksize: d.Inode.StableAttr.BlockSize, b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.InodeID))
Blocks: uattr.Usage / 512, // Nlink (uint64)
ATime: uattr.AccessTime.Timespec(), b = binary.AppendUint64(b, usermem.ByteOrder, uattr.Links)
MTime: uattr.ModificationTime.Timespec(), // Mode (uint32)
CTime: uattr.StatusChangeTime.Timespec(), b = binary.AppendUint32(b, usermem.ByteOrder, mode|uint32(uattr.Perms.LinuxMode()))
}) // UID (uint32)
b = binary.AppendUint32(b, usermem.ByteOrder, uint32(uattr.Owner.UID.In(t.UserNamespace()).OrOverflow()))
// GID (uint32)
b = binary.AppendUint32(b, usermem.ByteOrder, uint32(uattr.Owner.GID.In(t.UserNamespace()).OrOverflow()))
// Padding (uint32)
b = binary.AppendUint32(b, usermem.ByteOrder, 0)
// Rdev (uint64)
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(linux.MakeDeviceID(d.Inode.StableAttr.DeviceFileMajor, d.Inode.StableAttr.DeviceFileMinor)))
// Size (uint64)
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(uattr.Size))
// Blksize (uint64)
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(d.Inode.StableAttr.BlockSize))
// Blocks (uint64)
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(uattr.Usage/512))
// ATime
atime := uattr.AccessTime.Timespec()
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(atime.Sec))
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(atime.Nsec))
// MTime
mtime := uattr.ModificationTime.Timespec()
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(mtime.Sec))
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(mtime.Nsec))
// CTime
ctime := uattr.StatusChangeTime.Timespec()
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(ctime.Sec))
b = binary.AppendUint64(b, usermem.ByteOrder, uint64(ctime.Nsec))
_, err = t.CopyOutBytes(statAddr, b)
return err return err
} }