github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/fs/debug.go (about) 1 // +build linux darwin 2 3 /* 4 Copyright 2013 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package fs 20 21 import ( 22 "bytes" 23 "fmt" 24 "os" 25 "strconv" 26 27 "camlistore.org/pkg/types" 28 29 "camlistore.org/third_party/code.google.com/p/rsc/fuse" 30 ) 31 32 // If TrackStats is true, statistics are kept on operations. 33 var TrackStats bool 34 35 func init() { 36 TrackStats, _ = strconv.ParseBool(os.Getenv("CAMLI_TRACK_FS_STATS")) 37 } 38 39 var ( 40 mutFileOpen = newStat("mutfile-open") 41 mutFileOpenError = newStat("mutfile-open-error") 42 mutFileOpenRO = newStat("mutfile-open-ro") 43 mutFileOpenRW = newStat("mutfile-open-rw") 44 roFileOpen = newStat("rofile-open") 45 roFileOpenError = newStat("rofile-open-error") 46 ) 47 48 var statByName = map[string]*stat{} 49 50 func newStat(name string) *stat { 51 if statByName[name] != nil { 52 panic("duplicate registraton of " + name) 53 } 54 s := &stat{name: name} 55 statByName[name] = s 56 return s 57 } 58 59 // A stat is a wrapper around an atomic int64, as is a fuse.Node 60 // exporting that data as a decimal. 61 type stat struct { 62 n types.AtomicInt64 63 name string 64 } 65 66 func (s *stat) Incr() { 67 if TrackStats { 68 s.n.Add(1) 69 } 70 } 71 72 func (s *stat) content() []byte { 73 var buf bytes.Buffer 74 fmt.Fprintf(&buf, "%d", s.n.Get()) 75 buf.WriteByte('\n') 76 return buf.Bytes() 77 } 78 79 func (s *stat) Attr() fuse.Attr { 80 return fuse.Attr{ 81 Mode: 0400, 82 Uid: uint32(os.Getuid()), 83 Gid: uint32(os.Getgid()), 84 Size: uint64(len(s.content())), 85 Mtime: serverStart, 86 Ctime: serverStart, 87 Crtime: serverStart, 88 } 89 } 90 91 func (s *stat) Read(req *fuse.ReadRequest, res *fuse.ReadResponse, intr fuse.Intr) fuse.Error { 92 c := s.content() 93 if req.Offset > int64(len(c)) { 94 return nil 95 } 96 c = c[req.Offset:] 97 size := req.Size 98 if size > len(c) { 99 size = len(c) 100 } 101 res.Data = make([]byte, size) 102 copy(res.Data, c) 103 return nil 104 } 105 106 // A statsDir FUSE directory node is returned by root.go, by opening 107 // ".camli_fs_stats" in the root directory. 108 type statsDir struct{} 109 110 func (statsDir) Attr() fuse.Attr { 111 return fuse.Attr{ 112 Mode: os.ModeDir | 0700, 113 Uid: uint32(os.Getuid()), 114 Gid: uint32(os.Getgid()), 115 } 116 } 117 118 func (statsDir) ReadDir(intr fuse.Intr) (ents []fuse.Dirent, err fuse.Error) { 119 for k := range statByName { 120 ents = append(ents, fuse.Dirent{Name: k}) 121 } 122 return 123 } 124 125 func (statsDir) Lookup(req *fuse.LookupRequest, res *fuse.LookupResponse, intr fuse.Intr) (fuse.Node, fuse.Error) { 126 name := req.Name 127 s, ok := statByName[name] 128 if !ok { 129 return nil, fuse.ENOENT 130 } 131 return s, nil 132 }