1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/vlog.h" 6 7 #include <cstddef> 8 #include <ostream> 9 #include <utility> 10 11 #include "base/basictypes.h" 12 #include "base/logging.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_split.h" 15 16 namespace logging { 17 18 const int VlogInfo::kDefaultVlogLevel = 0; 19 20 struct VlogInfo::VmodulePattern { 21 enum MatchTarget { MATCH_MODULE, MATCH_FILE }; 22 23 explicit VmodulePattern(const std::string& pattern); 24 25 VmodulePattern(); 26 27 std::string pattern; 28 int vlog_level; 29 MatchTarget match_target; 30 }; 31 32 VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern) 33 : pattern(pattern), 34 vlog_level(VlogInfo::kDefaultVlogLevel), 35 match_target(MATCH_MODULE) { 36 // If the pattern contains a {forward,back} slash, we assume that 37 // it's meant to be tested against the entire __FILE__ string. 38 std::string::size_type first_slash = pattern.find_first_of("\\/"); 39 if (first_slash != std::string::npos) 40 match_target = MATCH_FILE; 41 } 42 43 VlogInfo::VmodulePattern::VmodulePattern() 44 : vlog_level(VlogInfo::kDefaultVlogLevel), 45 match_target(MATCH_MODULE) {} 46 47 VlogInfo::VlogInfo(const std::string& v_switch, 48 const std::string& vmodule_switch, 49 int* min_log_level) 50 : min_log_level_(min_log_level) { 51 DCHECK(min_log_level != NULL); 52 53 typedef std::pair<std::string, std::string> KVPair; 54 int vlog_level = 0; 55 if (!v_switch.empty()) { 56 if (base::StringToInt(v_switch, &vlog_level)) { 57 SetMaxVlogLevel(vlog_level); 58 } else { 59 DLOG(WARNING) << "Could not parse v switch \"" << v_switch << "\""; 60 } 61 } 62 63 std::vector<KVPair> kv_pairs; 64 if (!base::SplitStringIntoKeyValuePairs( 65 vmodule_switch, '=', ',', &kv_pairs)) { 66 DLOG(WARNING) << "Could not fully parse vmodule switch \"" 67 << vmodule_switch << "\""; 68 } 69 for (std::vector<KVPair>::const_iterator it = kv_pairs.begin(); 70 it != kv_pairs.end(); ++it) { 71 VmodulePattern pattern(it->first); 72 if (!base::StringToInt(it->second, &pattern.vlog_level)) { 73 DLOG(WARNING) << "Parsed vlog level for \"" 74 << it->first << "=" << it->second 75 << "\" as " << pattern.vlog_level; 76 } 77 vmodule_levels_.push_back(pattern); 78 } 79 } 80 81 VlogInfo::~VlogInfo() {} 82 83 namespace { 84 85 // Given a path, returns the basename with the extension chopped off 86 // (and any -inl suffix). We avoid using FilePath to minimize the 87 // number of dependencies the logging system has. 88 base::StringPiece GetModule(const base::StringPiece& file) { 89 base::StringPiece module(file); 90 base::StringPiece::size_type last_slash_pos = 91 module.find_last_of("\\/"); 92 if (last_slash_pos != base::StringPiece::npos) 93 module.remove_prefix(last_slash_pos + 1); 94 base::StringPiece::size_type extension_start = module.rfind('.'); 95 module = module.substr(0, extension_start); 96 static const char kInlSuffix[] = "-inl"; 97 static const int kInlSuffixLen = arraysize(kInlSuffix) - 1; 98 if (module.ends_with(kInlSuffix)) 99 module.remove_suffix(kInlSuffixLen); 100 return module; 101 } 102 103 } // namespace 104 105 int VlogInfo::GetVlogLevel(const base::StringPiece& file) const { 106 if (!vmodule_levels_.empty()) { 107 base::StringPiece module(GetModule(file)); 108 for (std::vector<VmodulePattern>::const_iterator it = 109 vmodule_levels_.begin(); it != vmodule_levels_.end(); ++it) { 110 base::StringPiece target( 111 (it->match_target == VmodulePattern::MATCH_FILE) ? file : module); 112 if (MatchVlogPattern(target, it->pattern)) 113 return it->vlog_level; 114 } 115 } 116 return GetMaxVlogLevel(); 117 } 118 119 void VlogInfo::SetMaxVlogLevel(int level) { 120 // Log severity is the negative verbosity. 121 *min_log_level_ = -level; 122 } 123 124 int VlogInfo::GetMaxVlogLevel() const { 125 return -*min_log_level_; 126 } 127 128 bool MatchVlogPattern(const base::StringPiece& string, 129 const base::StringPiece& vlog_pattern) { 130 base::StringPiece p(vlog_pattern); 131 base::StringPiece s(string); 132 // Consume characters until the next star. 133 while (!p.empty() && !s.empty() && (p[0] != '*')) { 134 switch (p[0]) { 135 // A slash (forward or back) must match a slash (forward or back). 136 case '/': 137 case '\\': 138 if ((s[0] != '/') && (s[0] != '\\')) 139 return false; 140 break; 141 142 // A '?' matches anything. 143 case '?': 144 break; 145 146 // Anything else must match literally. 147 default: 148 if (p[0] != s[0]) 149 return false; 150 break; 151 } 152 p.remove_prefix(1), s.remove_prefix(1); 153 } 154 155 // An empty pattern here matches only an empty string. 156 if (p.empty()) 157 return s.empty(); 158 159 // Coalesce runs of consecutive stars. There should be at least 160 // one. 161 while (!p.empty() && (p[0] == '*')) 162 p.remove_prefix(1); 163 164 // Since we moved past the stars, an empty pattern here matches 165 // anything. 166 if (p.empty()) 167 return true; 168 169 // Since we moved past the stars and p is non-empty, if some 170 // non-empty substring of s matches p, then we ourselves match. 171 while (!s.empty()) { 172 if (MatchVlogPattern(s, p)) 173 return true; 174 s.remove_prefix(1); 175 } 176 177 // Otherwise, we couldn't find a match. 178 return false; 179 } 180 181 } // namespace 182