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