Commit d5638ffd authored by Kirill Smelkov's avatar Kirill Smelkov Committed by Han-Wen Nienhuys

fuse: Print debug flags in stable order

This helps to compare fuse logs of e.g. different test runs. For example
below are two INIT messages with the same flags, but formatted, before
hereby patch, differently:

     rx 2: INIT n0 {7.37 Ra 131072 EXPORT_SUPPORT,SPLICE_READ,AUTO_INVAL_DATA,ASYNC_DIO,WRITEBACK_CACHE,BIG_WRITES,NO_OPEN_SUPPORT,PARALLEL_DIROPS,POSIX_ACL,EXPLICIT_INVAL_DATA,NO_OPENDIR_SUPPORT,ASYNC_READ,DONT_MASK,SPLICE_MOVE,IOCTL_DIR,READDIRPLUS,READDIRPLUS_AUTO,HANDLE_KILLPRIV,POSIX_LOCKS,ATOMIC_O_TRUNC,SPLICE_WRITE,FLOCK_LOCKS,ABORT_ERROR,MAX_PAGES,CACHE_SYMLINKS,0x70000000} "\x03\x00\x00\x00\x00\x00\x00\x00"... 48b

     rx 2: INIT n0 {7.37 Ra 131072 EXPORT_SUPPORT,SPLICE_READ,AUTO_INVAL_DATA,ASYNC_DIO,WRITEBACK_CACHE,BIG_WRITES,NO_OPEN_SUPPORT,PARALLEL_DIROPS,POSIX_ACL,EXPLICIT_INVAL_DATA,ASYNC_READ,DONT_MASK,SPLICE_MOVE,IOCTL_DIR,READDIRPLUS,READDIRPLUS_AUTO,HANDLE_KILLPRIV,NO_OPENDIR_SUPPORT,POSIX_LOCKS,ATOMIC_O_TRUNC,SPLICE_WRITE,FLOCK_LOCKS,ABORT_ERROR,MAX_PAGES,CACHE_SYMLINKS,0x70000000} "\x03\x00\x00\x00\x00\x00\x00\x00"... 48b

which leads to corresponding lines to be present in the diff and
requires human attention to verify that the messages are really the
same.

After this patch above INIT always formats as

     rx 2: INIT n0 {7.37 Ra 131072 ASYNC_READ,POSIX_LOCKS,ATOMIC_O_TRUNC,EXPORT_SUPPORT,BIG_WRITES,DONT_MASK,SPLICE_WRITE,SPLICE_MOVE,SPLICE_READ,FLOCK_LOCKS,IOCTL_DIR,AUTO_INVAL_DATA,READDIRPLUS,READDIRPLUS_AUTO,ASYNC_DIO,WRITEBACK_CACHE,NO_OPEN_SUPPORT,PARALLEL_DIROPS,HANDLE_KILLPRIV,POSIX_ACL,ABORT_ERROR,MAX_PAGES,CACHE_SYMLINKS,NO_OPENDIR_SUPPORT,EXPLICIT_INVAL_DATA,0x70000000} "\x03\x00\x00\x00\x00\x00\x00\x00"... 48b

and the diff log does not unnecessarily distract attention to it.

flagString is used for many messages, this fix should benefit all of
them.

Change-Id: Id4bdab9dd90552313ede41a3b9a6c337588e4b3e
parent 9836d614
...@@ -12,14 +12,14 @@ import ( ...@@ -12,14 +12,14 @@ import (
) )
var ( var (
writeFlagNames = map[int64]string{ writeFlagNames = newFlagNames(map[int64]string{
WRITE_CACHE: "CACHE", WRITE_CACHE: "CACHE",
WRITE_LOCKOWNER: "LOCKOWNER", WRITE_LOCKOWNER: "LOCKOWNER",
} })
readFlagNames = map[int64]string{ readFlagNames = newFlagNames(map[int64]string{
READ_LOCKOWNER: "LOCKOWNER", READ_LOCKOWNER: "LOCKOWNER",
} })
initFlagNames = map[int64]string{ initFlagNames = newFlagNames(map[int64]string{
CAP_ASYNC_READ: "ASYNC_READ", CAP_ASYNC_READ: "ASYNC_READ",
CAP_POSIX_LOCKS: "POSIX_LOCKS", CAP_POSIX_LOCKS: "POSIX_LOCKS",
CAP_FILE_OPS: "FILE_OPS", CAP_FILE_OPS: "FILE_OPS",
...@@ -46,11 +46,11 @@ var ( ...@@ -46,11 +46,11 @@ var (
CAP_CACHE_SYMLINKS: "CACHE_SYMLINKS", CAP_CACHE_SYMLINKS: "CACHE_SYMLINKS",
CAP_NO_OPENDIR_SUPPORT: "NO_OPENDIR_SUPPORT", CAP_NO_OPENDIR_SUPPORT: "NO_OPENDIR_SUPPORT",
CAP_EXPLICIT_INVAL_DATA: "EXPLICIT_INVAL_DATA", CAP_EXPLICIT_INVAL_DATA: "EXPLICIT_INVAL_DATA",
} })
releaseFlagNames = map[int64]string{ releaseFlagNames = newFlagNames(map[int64]string{
RELEASE_FLUSH: "FLUSH", RELEASE_FLUSH: "FLUSH",
} })
openFlagNames = map[int64]string{ openFlagNames = newFlagNames(map[int64]string{
int64(os.O_WRONLY): "WRONLY", int64(os.O_WRONLY): "WRONLY",
int64(os.O_RDWR): "RDWR", int64(os.O_RDWR): "RDWR",
int64(os.O_APPEND): "APPEND", int64(os.O_APPEND): "APPEND",
...@@ -64,30 +64,73 @@ var ( ...@@ -64,30 +64,73 @@ var (
int64(syscall.O_CLOEXEC): "CLOEXEC", int64(syscall.O_CLOEXEC): "CLOEXEC",
int64(syscall.O_DIRECTORY): "DIRECTORY", int64(syscall.O_DIRECTORY): "DIRECTORY",
} })
fuseOpenFlagNames = map[int64]string{ fuseOpenFlagNames = newFlagNames(map[int64]string{
FOPEN_DIRECT_IO: "DIRECT", FOPEN_DIRECT_IO: "DIRECT",
FOPEN_KEEP_CACHE: "CACHE", FOPEN_KEEP_CACHE: "CACHE",
FOPEN_NONSEEKABLE: "NONSEEK", FOPEN_NONSEEKABLE: "NONSEEK",
FOPEN_CACHE_DIR: "CACHE_DIR", FOPEN_CACHE_DIR: "CACHE_DIR",
FOPEN_STREAM: "STREAM", FOPEN_STREAM: "STREAM",
} })
accessFlagName = map[int64]string{ accessFlagName = newFlagNames(map[int64]string{
X_OK: "x", X_OK: "x",
W_OK: "w", W_OK: "w",
R_OK: "r", R_OK: "r",
} })
getAttrFlagNames = map[int64]string{ getAttrFlagNames = newFlagNames(map[int64]string{
FUSE_GETATTR_FH: "FH", FUSE_GETATTR_FH: "FH",
} })
) )
func flagString(names map[int64]string, fl int64, def string) string { // flagNames associate flag bits to their names.
type flagNames [64]flagNameEntry
// flagNameEntry describes one flag value.
//
// Usually a flag constitues only one bit, but, for example at least O_SYNC and
// O_TMPFILE are represented by a value with two bits set. To handle such
// situations we map all bits of a flag to the same flagNameEntry.
type flagNameEntry struct {
bits int64
name string
}
// newFlagNames creates flagNames from flag->name map.
func newFlagNames(names map[int64]string) *flagNames {
var v flagNames
for flag, name := range names {
v.set(flag, name)
}
return &v
}
// set associates flag value with name.
func (names *flagNames) set(flag int64, name string) {
entry := flagNameEntry{bits: flag, name: name}
for i := 0; i < 64; i++ {
if flag&(1<<i) != 0 {
if ie := names[i]; ie.bits != 0 {
panic(fmt.Sprintf("%s (%x) overlaps with %s (%x)", name, flag, ie.name, ie.bits))
}
names[i] = entry
}
}
}
func flagString(names *flagNames, fl int64, def string) string {
s := []string{} s := []string{}
for k, v := range names { // emit flags in their numeric order
if fl&k != 0 { for i := range names {
s = append(s, v) entry := &names[i]
fl ^= k if entry.bits == 0 {
continue
}
if fl&entry.bits == entry.bits {
s = append(s, entry.name)
fl ^= entry.bits
if fl == 0 {
break
}
} }
} }
if len(s) == 0 && def != "" { if len(s) == 0 && def != "" {
......
...@@ -9,9 +9,9 @@ import ( ...@@ -9,9 +9,9 @@ import (
) )
func init() { func init() {
initFlagNames[CAP_XTIMES] = "XTIMES" initFlagNames.set(CAP_XTIMES, "XTIMES")
initFlagNames[CAP_VOL_RENAME] = "VOL_RENAME" initFlagNames.set(CAP_VOL_RENAME, "VOL_RENAME")
initFlagNames[CAP_CASE_INSENSITIVE] = "CASE_INSENSITIVE" initFlagNames.set(CAP_CASE_INSENSITIVE, "CASE_INSENSITIVE")
} }
func (a *Attr) string() string { func (a *Attr) string() string {
......
...@@ -10,9 +10,9 @@ import ( ...@@ -10,9 +10,9 @@ import (
) )
func init() { func init() {
openFlagNames[syscall.O_DIRECT] = "DIRECT" openFlagNames.set(syscall.O_DIRECT, "DIRECT")
openFlagNames[syscall.O_LARGEFILE] = "LARGEFILE" openFlagNames.set(syscall.O_LARGEFILE, "LARGEFILE")
openFlagNames[syscall_O_NOATIME] = "NOATIME" openFlagNames.set(syscall_O_NOATIME, "NOATIME")
} }
func (a *Attr) string() string { func (a *Attr) string() string {
......
// Copyright 2023 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"testing"
)
// verify that flagString always formats flags in the same order.
func TestFlagStringOrder(t *testing.T) {
var flags int64 = CAP_ASYNC_READ | CAP_SPLICE_WRITE | CAP_READDIRPLUS | CAP_MAX_PAGES | CAP_EXPLICIT_INVAL_DATA
want := "ASYNC_READ,SPLICE_WRITE,READDIRPLUS,MAX_PAGES,EXPLICIT_INVAL_DATA"
// loop many times to check for sure the order is untied from map iteration order
for i := 0; i < 100; i++ {
have := flagString(initFlagNames, flags, "")
if have != want {
t.Fatalf("flagString:\nhave: %q\nwant: %q", have, want)
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment