Home | History | Annotate | Download | only in base
      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/strings/string_number_conversions.h"
     10 #include "base/strings/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       DLOG(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     DLOG(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       DLOG(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