• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2010 The Chromium Authors
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 <stddef.h>
8 
9 #include <ostream>
10 #include <string_view>
11 #include <utility>
12 
13 #include "base/check_op.h"
14 #include "base/logging.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 
19 namespace logging {
20 
21 const int VlogInfo::kDefaultVlogLevel = 0;
22 
VmodulePattern(const std::string & pattern)23 VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern)
24     : pattern(pattern),
25       vlog_level(VlogInfo::kDefaultVlogLevel),
26       match_target(MATCH_MODULE) {
27   // If the pattern contains a {forward,back} slash, we assume that
28   // it's meant to be tested against the entire __FILE__ string.
29   std::string::size_type first_slash = pattern.find_first_of("\\/");
30   if (first_slash != std::string::npos)
31     match_target = MATCH_FILE;
32 }
33 
VmodulePattern()34 VlogInfo::VmodulePattern::VmodulePattern()
35     : vlog_level(VlogInfo::kDefaultVlogLevel), match_target(MATCH_MODULE) {}
36 
37 // static
ParseVmoduleLevels(const std::string & vmodule_switch)38 std::vector<VlogInfo::VmodulePattern> VlogInfo::ParseVmoduleLevels(
39     const std::string& vmodule_switch) {
40   std::vector<VmodulePattern> vmodule_levels;
41   base::StringPairs kv_pairs;
42   if (!base::SplitStringIntoKeyValuePairs(vmodule_switch, '=', ',',
43                                           &kv_pairs)) {
44     DLOG(WARNING) << "Could not fully parse vmodule switch \"" << vmodule_switch
45                   << "\"";
46   }
47   for (const auto& pair : kv_pairs) {
48     VmodulePattern pattern(pair.first);
49     if (!base::StringToInt(pair.second, &pattern.vlog_level)) {
50       DLOG(WARNING) << "Parsed vlog level for \"" << pair.first << "="
51                     << pair.second << "\" as " << pattern.vlog_level;
52     }
53     vmodule_levels.push_back(pattern);
54   }
55   return vmodule_levels;
56 }
57 
VlogInfo(const std::string & v_switch,const std::string & vmodule_switch,int * min_log_level)58 VlogInfo::VlogInfo(const std::string& v_switch,
59                    const std::string& vmodule_switch,
60                    int* min_log_level)
61     : vmodule_levels_(ParseVmoduleLevels(vmodule_switch)),
62       min_log_level_(min_log_level) {
63   DCHECK_NE(min_log_level, nullptr);
64 
65   int vlog_level = 0;
66   if (!v_switch.empty()) {
67     if (base::StringToInt(v_switch, &vlog_level)) {
68       SetMaxVlogLevel(vlog_level);
69     } else {
70       DLOG(WARNING) << "Could not parse v switch \"" << v_switch << "\"";
71     }
72   }
73 }
74 
75 VlogInfo::~VlogInfo() = default;
76 
77 namespace {
78 
79 // Given a path, returns the basename with the extension chopped off
80 // (and any -inl suffix).  We avoid using FilePath to minimize the
81 // number of dependencies the logging system has.
GetModule(std::string_view file)82 std::string_view GetModule(std::string_view file) {
83   std::string_view module(file);
84   size_t last_slash_pos = module.find_last_of("\\/");
85   if (last_slash_pos != std::string_view::npos) {
86     module.remove_prefix(last_slash_pos + 1);
87   }
88   size_t extension_start = module.rfind('.');
89   module = module.substr(0, extension_start);
90   static const char kInlSuffix[] = "-inl";
91   static const int kInlSuffixLen = std::size(kInlSuffix) - 1;
92   if (base::EndsWith(module, kInlSuffix))
93     module.remove_suffix(kInlSuffixLen);
94   return module;
95 }
96 
97 }  // namespace
98 
GetVlogLevel(std::string_view file) const99 int VlogInfo::GetVlogLevel(std::string_view file) const {
100   if (!vmodule_levels_.empty()) {
101     std::string_view module(GetModule(file));
102     for (const auto& it : vmodule_levels_) {
103       std::string_view target(
104           (it.match_target == VmodulePattern::MATCH_FILE) ? file : module);
105       if (MatchVlogPattern(target, it.pattern))
106         return it.vlog_level;
107     }
108   }
109   return GetMaxVlogLevel();
110 }
111 
SetMaxVlogLevel(int level)112 void VlogInfo::SetMaxVlogLevel(int level) {
113   // Log severity is the negative verbosity.
114   *min_log_level_ = -level;
115 }
116 
GetMaxVlogLevel() const117 int VlogInfo::GetMaxVlogLevel() const {
118   return -*min_log_level_;
119 }
120 
VlogInfo(std::vector<VmodulePattern> vmodule_levels,int * min_log_level)121 VlogInfo::VlogInfo(std::vector<VmodulePattern> vmodule_levels,
122                    int* min_log_level)
123     : vmodule_levels_(std::move(vmodule_levels)),
124       min_log_level_(min_log_level) {}
125 
WithSwitches(const std::string & vmodule_switch) const126 VlogInfo* VlogInfo::WithSwitches(const std::string& vmodule_switch) const {
127   std::vector<VmodulePattern> vmodule_levels = vmodule_levels_;
128   std::vector<VmodulePattern> additional_vmodule_levels =
129       ParseVmoduleLevels(vmodule_switch);
130   vmodule_levels.insert(vmodule_levels.end(), additional_vmodule_levels.begin(),
131                         additional_vmodule_levels.end());
132   return new VlogInfo(std::move(vmodule_levels), min_log_level_);
133 }
134 
MatchVlogPattern(std::string_view string,std::string_view vlog_pattern)135 bool MatchVlogPattern(std::string_view string, std::string_view vlog_pattern) {
136   // The code implements the glob matching using a greedy approach described in
137   // https://research.swtch.com/glob.
138   size_t s = 0, nexts = 0;
139   size_t p = 0, nextp = 0;
140   size_t slen = string.size(), plen = vlog_pattern.size();
141   while (s < slen || p < plen) {
142     if (p < plen) {
143       switch (vlog_pattern[p]) {
144         // A slash (forward or back) must match a slash (forward or back).
145         case '/':
146         case '\\':
147           if (s < slen && (string[s] == '/' || string[s] == '\\')) {
148             p++, s++;
149             continue;
150           }
151           break;
152         // A '?' matches anything.
153         case '?':
154           if (s < slen) {
155             p++, s++;
156             continue;
157           }
158           break;
159         case '*':
160           nextp = p;
161           nexts = s + 1;
162           p++;
163           continue;
164         // Anything else must match literally.
165         default:
166           if (s < slen && string[s] == vlog_pattern[p]) {
167             p++, s++;
168             continue;
169           }
170           break;
171       }
172     }
173     // Mismatch - maybe restart.
174     if (0 < nexts && nexts <= slen) {
175       p = nextp;
176       s = nexts;
177       continue;
178     }
179     return false;
180   }
181   return true;
182 }
183 
184 }  // namespace logging
185