游雁
2024-02-19 94de39dde2e616a01683c518023d0fab72b4e103
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright (c) 1999, 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: Ray Sidney and many others
//
// Broken out from logging.cc by Soren Lassen
// logging_unittest.cc covers the functionality herein
 
#include "utilities.h"
 
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <cstdio>
#include <string>
#include "base/commandlineflags.h"
#include <glog/logging.h>
#include <glog/raw_logging.h>
#include "base/googleinit.h"
 
// glog doesn't have annotation
#define ANNOTATE_BENIGN_RACE(address, description)
 
using std::string;
 
GLOG_DEFINE_int32(v, 0, "Show all VLOG(m) messages for m <= this."
" Overridable by --vmodule.");
 
GLOG_DEFINE_string(vmodule, "", "per-module verbose level."
" Argument is a comma-separated list of <module name>=<log level>."
" <module name> is a glob pattern, matched against the filename base"
" (that is, name ignoring .cc/.h./-inl.h)."
" <log level> overrides any value given by --v.");
 
_START_GOOGLE_NAMESPACE_
 
namespace glog_internal_namespace_ {
 
// Used by logging_unittests.cc so can't make it static here.
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
                              const char* str, size_t str_len);
 
// Implementation of fnmatch that does not need 0-termination
// of arguments and does not allocate any memory,
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
// It's not a static function for the unittest.
GLOG_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
                              const char* str, size_t str_len) {
  size_t p = 0;
  size_t s = 0;
  while (true) {
    if (p == patt_len  &&  s == str_len) return true;
    if (p == patt_len) return false;
    if (s == str_len) return p+1 == patt_len  &&  pattern[p] == '*';
    if (pattern[p] == str[s]  ||  pattern[p] == '?') {
      p += 1;
      s += 1;
      continue;
    }
    if (pattern[p] == '*') {
      if (p+1 == patt_len) return true;
      do {
        if (SafeFNMatch_(pattern+(p+1), patt_len-(p+1), str+s, str_len-s)) {
          return true;
        }
        s += 1;
      } while (s != str_len);
      return false;
    }
    return false;
  }
}
 
}  // namespace glog_internal_namespace_
 
using glog_internal_namespace_::SafeFNMatch_;
 
// List of per-module log levels from FLAGS_vmodule.
// Once created each element is never deleted/modified
// except for the vlog_level: other threads will read VModuleInfo blobs
// w/o locks and we'll store pointers to vlog_level at VLOG locations
// that will never go away.
// We can't use an STL struct here as we wouldn't know
// when it's safe to delete/update it: other threads need to use it w/o locks.
struct VModuleInfo {
  string module_pattern;
  mutable int32 vlog_level;  // Conceptually this is an AtomicWord, but it's
                             // too much work to use AtomicWord type here
                             // w/o much actual benefit.
  const VModuleInfo* next;
};
 
// This protects the following global variables.
static Mutex vmodule_lock;
// Pointer to head of the VModuleInfo list.
// It's a map from module pattern to logging level for those module(s).
static VModuleInfo* vmodule_list = nullptr;
static SiteFlag* cached_site_list = nullptr;
 
// Boolean initialization flag.
static bool inited_vmodule = false;
 
// L >= vmodule_lock.
static void VLOG2Initializer() {
  vmodule_lock.AssertHeld();
  // Can now parse --vmodule flag and initialize mapping of module-specific
  // logging levels.
  inited_vmodule = false;
  const char* vmodule = FLAGS_vmodule.c_str();
  const char* sep;
  VModuleInfo* head = nullptr;
  VModuleInfo* tail = nullptr;
  while ((sep = strchr(vmodule, '=')) != nullptr) {
    string pattern(vmodule, static_cast<size_t>(sep - vmodule));
    int module_level;
    if (sscanf(sep, "=%d", &module_level) == 1) {
      auto* info = new VModuleInfo;
      info->module_pattern = pattern;
      info->vlog_level = module_level;
      if (head) {
        tail->next = info;
      } else {
        head = info;
      }
      tail = info;
    }
    // Skip past this entry
    vmodule = strchr(sep, ',');
    if (vmodule == nullptr) break;
    vmodule++;  // Skip past ","
  }
  if (head) {  // Put them into the list at the head:
    tail->next = vmodule_list;
    vmodule_list = head;
  }
  inited_vmodule = true;
}
 
// This can be called very early, so we use SpinLock and RAW_VLOG here.
int SetVLOGLevel(const char* module_pattern, int log_level) {
  int result = FLAGS_v;
  size_t const pattern_len = strlen(module_pattern);
  bool found = false;
  {
    MutexLock l(&vmodule_lock);  // protect whole read-modify-write
    for (const VModuleInfo* info = vmodule_list; info != nullptr;
         info = info->next) {
      if (info->module_pattern == module_pattern) {
        if (!found) {
          result = info->vlog_level;
          found = true;
        }
        info->vlog_level = log_level;
      } else if (!found  &&
                 SafeFNMatch_(info->module_pattern.c_str(),
                              info->module_pattern.size(),
                              module_pattern, pattern_len)) {
        result = info->vlog_level;
        found = true;
      }
    }
    if (!found) {
      auto* info = new VModuleInfo;
      info->module_pattern = module_pattern;
      info->vlog_level = log_level;
      info->next = vmodule_list;
      vmodule_list = info;
 
      SiteFlag** item_ptr = &cached_site_list;
      SiteFlag* item = cached_site_list;
 
      // We traverse the list fully because the pattern can match several items
      // from the list.
      while (item) {
        if (SafeFNMatch_(module_pattern, pattern_len, item->base_name,
                         item->base_len)) {
          // Redirect the cached value to its module override.
          item->level = &info->vlog_level;
          *item_ptr = item->next;  // Remove the item from the list.
        } else {
          item_ptr = &item->next;
        }
        item = *item_ptr;
      }
    }
  }
  RAW_VLOG(1, "Set VLOG level for \"%s\" to %d", module_pattern, log_level);
  return result;
}
 
// NOTE: Individual VLOG statements cache the integer log level pointers.
// NOTE: This function must not allocate memory or require any locks.
bool InitVLOG3__(SiteFlag* site_flag, int32* level_default,
                 const char* fname, int32 verbose_level) {
  MutexLock l(&vmodule_lock);
  bool read_vmodule_flag = inited_vmodule;
  if (!read_vmodule_flag) {
    VLOG2Initializer();
  }
 
  // protect the errno global in case someone writes:
  // VLOG(..) << "The last error was " << strerror(errno)
  int old_errno = errno;
 
  // site_default normally points to FLAGS_v
  int32* site_flag_value = level_default;
 
  // Get basename for file
  const char* base = strrchr(fname, '/');
 
#ifdef _WIN32
  if (!base) {
    base = strrchr(fname, '\\');
  }
#endif
 
  base = base ? (base+1) : fname;
  const char* base_end = strchr(base, '.');
  size_t base_length =
      base_end ? static_cast<size_t>(base_end - base) : strlen(base);
 
  // Trim out trailing "-inl" if any
  if (base_length >= 4 && (memcmp(base+base_length-4, "-inl", 4) == 0)) {
    base_length -= 4;
  }
 
  // TODO: Trim out _unittest suffix?  Perhaps it is better to have
  // the extra control and just leave it there.
 
  // find target in vector of modules, replace site_flag_value with
  // a module-specific verbose level, if any.
  for (const VModuleInfo* info = vmodule_list; info != nullptr;
       info = info->next) {
    if (SafeFNMatch_(info->module_pattern.c_str(), info->module_pattern.size(),
                     base, base_length)) {
      site_flag_value = &info->vlog_level;
        // value at info->vlog_level is now what controls
        // the VLOG at the caller site forever
      break;
    }
  }
 
  // Cache the vlog value pointer if --vmodule flag has been parsed.
  ANNOTATE_BENIGN_RACE(site_flag,
                       "*site_flag may be written by several threads,"
                       " but the value will be the same");
  if (read_vmodule_flag) {
    site_flag->level = site_flag_value;
    // If VLOG flag has been cached to the default site pointer,
    // we want to add to the cached list in order to invalidate in case
    // SetVModule is called afterwards with new modules.
    // The performance penalty here is neglible, because InitVLOG3__ is called
    // once per site.
    if (site_flag_value == level_default && !site_flag->base_name) {
      site_flag->base_name = base;
      site_flag->base_len = base_length;
      site_flag->next = cached_site_list;
      cached_site_list = site_flag;
    }
  }
 
  // restore the errno in case something recoverable went wrong during
  // the initialization of the VLOG mechanism (see above note "protect the..")
  errno = old_errno;
  return *site_flag_value >= verbose_level;
}
 
_END_GOOGLE_NAMESPACE_