|  | // 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 <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <errno.h> | 
|  | #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_ { | 
|  |  | 
|  | // 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. | 
|  | GOOGLE_GLOG_DLL_DECL 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 (1) { | 
|  | 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_; | 
|  |  | 
|  | int32 kLogSiteUninitialized = 1000; | 
|  |  | 
|  | // 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 = 0; | 
|  | // 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 = NULL; | 
|  | VModuleInfo* tail = NULL; | 
|  | while ((sep = strchr(vmodule, '=')) != NULL) { | 
|  | string pattern(vmodule, sep - vmodule); | 
|  | int module_level; | 
|  | if (sscanf(sep, "=%d", &module_level) == 1) { | 
|  | VModuleInfo* 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 == NULL) 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; | 
|  | int 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 != NULL; 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) { | 
|  | VModuleInfo* info = new VModuleInfo; | 
|  | info->module_pattern = module_pattern; | 
|  | info->vlog_level = log_level; | 
|  | info->next = vmodule_list; | 
|  | vmodule_list = info; | 
|  | } | 
|  | 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__(int32** site_flag, int32* site_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 = site_default; | 
|  |  | 
|  | // Get basename for file | 
|  | const char* base = strrchr(fname, '/'); | 
|  | base = base ? (base+1) : fname; | 
|  | const char* base_end = strchr(base, '.'); | 
|  | size_t base_length = base_end ? 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 != NULL; 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 = site_flag_value; | 
|  |  | 
|  | // 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_ |