1 /* 2 Copyright 2013 Google Inc. All Rights Reserved. 3 Copyright 2022 The Kubernetes Authors. 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 */ 17 18 package verbosity 19 20 import ( 21 "bytes" 22 "errors" 23 "flag" 24 "fmt" 25 "path/filepath" 26 "runtime" 27 "strconv" 28 "strings" 29 "sync" 30 "sync/atomic" 31 ) 32 33 // New returns a struct that implements -v and -vmodule support. Changing and 34 // checking these settings is thread-safe, with all concurrency issues handled 35 // internally. 36 func New() *VState { 37 vs := new(VState) 38 39 // The two fields must have a pointer to the overal struct for their 40 // implementation of Set. 41 vs.vmodule.vs = vs 42 vs.verbosity.vs = vs 43 44 return vs 45 } 46 47 // Value is an extension that makes it possible to use the values in pflag. 48 type Value interface { 49 flag.Value 50 Type() string 51 } 52 53 func (vs *VState) V() Value { 54 return &vs.verbosity 55 } 56 57 func (vs *VState) VModule() Value { 58 return &vs.vmodule 59 } 60 61 // VState contains settings and state. Some of its fields can be accessed 62 // through atomic read/writes, in other cases a mutex must be held. 63 type VState struct { 64 mu sync.Mutex 65 66 // These flags are modified only under lock, although verbosity may be fetched 67 // safely using atomic.LoadInt32. 68 vmodule moduleSpec // The state of the -vmodule flag. 69 verbosity levelSpec // V logging level, the value of the -v flag/ 70 71 // pcs is used in V to avoid an allocation when computing the caller's PC. 72 pcs [1]uintptr 73 // vmap is a cache of the V Level for each V() call site, identified by PC. 74 // It is wiped whenever the vmodule flag changes state. 75 vmap map[uintptr]Level 76 // filterLength stores the length of the vmodule filter chain. If greater 77 // than zero, it means vmodule is enabled. It may be read safely 78 // using sync.LoadInt32, but is only modified under mu. 79 filterLength int32 80 } 81 82 // Level must be an int32 to support atomic read/writes. 83 type Level int32 84 85 type levelSpec struct { 86 vs *VState 87 l Level 88 } 89 90 // get returns the value of the level. 91 func (l *levelSpec) get() Level { 92 return Level(atomic.LoadInt32((*int32)(&l.l))) 93 } 94 95 // set sets the value of the level. 96 func (l *levelSpec) set(val Level) { 97 atomic.StoreInt32((*int32)(&l.l), int32(val)) 98 } 99 100 // String is part of the flag.Value interface. 101 func (l *levelSpec) String() string { 102 return strconv.FormatInt(int64(l.l), 10) 103 } 104 105 // Get is part of the flag.Getter interface. It returns the 106 // verbosity level as int32. 107 func (l *levelSpec) Get() interface{} { 108 return int32(l.l) 109 } 110 111 // Type is part of pflag.Value. 112 func (l *levelSpec) Type() string { 113 return "Level" 114 } 115 116 // Set is part of the flag.Value interface. 117 func (l *levelSpec) Set(value string) error { 118 v, err := strconv.ParseInt(value, 10, 32) 119 if err != nil { 120 return err 121 } 122 l.vs.mu.Lock() 123 defer l.vs.mu.Unlock() 124 l.vs.set(Level(v), l.vs.vmodule.filter, false) 125 return nil 126 } 127 128 // moduleSpec represents the setting of the -vmodule flag. 129 type moduleSpec struct { 130 vs *VState 131 filter []modulePat 132 } 133 134 // modulePat contains a filter for the -vmodule flag. 135 // It holds a verbosity level and a file pattern to match. 136 type modulePat struct { 137 pattern string 138 literal bool // The pattern is a literal string 139 level Level 140 } 141 142 // match reports whether the file matches the pattern. It uses a string 143 // comparison if the pattern contains no metacharacters. 144 func (m *modulePat) match(file string) bool { 145 if m.literal { 146 return file == m.pattern 147 } 148 match, _ := filepath.Match(m.pattern, file) 149 return match 150 } 151 152 func (m *moduleSpec) String() string { 153 // Lock because the type is not atomic. TODO: clean this up. 154 // Empty instances don't have and don't need a lock (can 155 // happen when flag uses introspection). 156 if m.vs != nil { 157 m.vs.mu.Lock() 158 defer m.vs.mu.Unlock() 159 } 160 var b bytes.Buffer 161 for i, f := range m.filter { 162 if i > 0 { 163 b.WriteRune(',') 164 } 165 fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) 166 } 167 return b.String() 168 } 169 170 // Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the 171 // struct is not exported. 172 func (m *moduleSpec) Get() interface{} { 173 return nil 174 } 175 176 // Type is part of pflag.Value 177 func (m *moduleSpec) Type() string { 178 return "pattern=N,..." 179 } 180 181 var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") 182 183 // Set will sets module value 184 // Syntax: -vmodule=recordio=2,file=1,gfs*=3 185 func (m *moduleSpec) Set(value string) error { 186 var filter []modulePat 187 for _, pat := range strings.Split(value, ",") { 188 if len(pat) == 0 { 189 // Empty strings such as from a trailing comma can be ignored. 190 continue 191 } 192 patLev := strings.Split(pat, "=") 193 if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { 194 return errVmoduleSyntax 195 } 196 pattern := patLev[0] 197 v, err := strconv.ParseInt(patLev[1], 10, 32) 198 if err != nil { 199 return errors.New("syntax error: expect comma-separated list of filename=N") 200 } 201 if v < 0 { 202 return errors.New("negative value for vmodule level") 203 } 204 if v == 0 { 205 continue // Ignore. It's harmless but no point in paying the overhead. 206 } 207 // TODO: check syntax of filter? 208 filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) 209 } 210 m.vs.mu.Lock() 211 defer m.vs.mu.Unlock() 212 m.vs.set(m.vs.verbosity.l, filter, true) 213 return nil 214 } 215 216 // isLiteral reports whether the pattern is a literal string, that is, has no metacharacters 217 // that require filepath.Match to be called to match the pattern. 218 func isLiteral(pattern string) bool { 219 return !strings.ContainsAny(pattern, `\*?[]`) 220 } 221 222 // set sets a consistent state for V logging. 223 // The mutex must be held. 224 func (vs *VState) set(l Level, filter []modulePat, setFilter bool) { 225 // Turn verbosity off so V will not fire while we are in transition. 226 vs.verbosity.set(0) 227 // Ditto for filter length. 228 atomic.StoreInt32(&vs.filterLength, 0) 229 230 // Set the new filters and wipe the pc->Level map if the filter has changed. 231 if setFilter { 232 vs.vmodule.filter = filter 233 vs.vmap = make(map[uintptr]Level) 234 } 235 236 // Things are consistent now, so enable filtering and verbosity. 237 // They are enabled in order opposite to that in V. 238 atomic.StoreInt32(&vs.filterLength, int32(len(filter))) 239 vs.verbosity.set(l) 240 } 241 242 // Enabled checks whether logging is enabled at the given level. This must be 243 // called with depth=0 when the caller of enabled will do the logging and 244 // higher values when more stack levels need to be skipped. 245 // 246 // The mutex will be locked only if needed. 247 func (vs *VState) Enabled(level Level, depth int) bool { 248 // This function tries hard to be cheap unless there's work to do. 249 // The fast path is two atomic loads and compares. 250 251 // Here is a cheap but safe test to see if V logging is enabled globally. 252 if vs.verbosity.get() >= level { 253 return true 254 } 255 256 // It's off globally but vmodule may still be set. 257 // Here is another cheap but safe test to see if vmodule is enabled. 258 if atomic.LoadInt32(&vs.filterLength) > 0 { 259 // Now we need a proper lock to use the logging structure. The pcs field 260 // is shared so we must lock before accessing it. This is fairly expensive, 261 // but if V logging is enabled we're slow anyway. 262 vs.mu.Lock() 263 defer vs.mu.Unlock() 264 if runtime.Callers(depth+2, vs.pcs[:]) == 0 { 265 return false 266 } 267 // runtime.Callers returns "return PCs", but we want 268 // to look up the symbolic information for the call, 269 // so subtract 1 from the PC. runtime.CallersFrames 270 // would be cleaner, but allocates. 271 pc := vs.pcs[0] - 1 272 v, ok := vs.vmap[pc] 273 if !ok { 274 v = vs.setV(pc) 275 } 276 return v >= level 277 } 278 return false 279 } 280 281 // setV computes and remembers the V level for a given PC 282 // when vmodule is enabled. 283 // File pattern matching takes the basename of the file, stripped 284 // of its .go suffix, and uses filepath.Match, which is a little more 285 // general than the *? matching used in C++. 286 // Mutex is held. 287 func (vs *VState) setV(pc uintptr) Level { 288 fn := runtime.FuncForPC(pc) 289 file, _ := fn.FileLine(pc) 290 // The file is something like /a/b/c/d.go. We want just the d. 291 file = strings.TrimSuffix(file, ".go") 292 if slash := strings.LastIndex(file, "/"); slash >= 0 { 293 file = file[slash+1:] 294 } 295 for _, filter := range vs.vmodule.filter { 296 if filter.match(file) { 297 vs.vmap[pc] = filter.level 298 return filter.level 299 } 300 } 301 vs.vmap[pc] = 0 302 return 0 303 } 304