1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6  * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 /* Original author: bcampen@mozilla.com */
9 
10 #include "rlogconnector.h"
11 
12 #include <cstdarg>
13 #include <deque>
14 #include <string>
15 #include <utility>  // Pinch hitting for <utility> and std::move
16 #include <vector>
17 
18 #include "logging.h"
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Mutex.h"
21 #include "mozilla/Sprintf.h"
22 
23 extern "C" {
24 #include <csi_platform.h>
25 #include "r_log.h"
26 #include "registry.h"
27 }
28 
29 /* Matches r_dest_vlog type defined in r_log.h */
ringbuffer_vlog(int facility,int level,const char * format,va_list ap)30 static int ringbuffer_vlog(int facility, int level, const char* format,
31                            va_list ap) {
32   if (mozilla::RLogConnector::GetInstance()->ShouldLog(level)) {
33     // I could be evil and printf right into a std::string, but unless this
34     // shows up in profiling, it is not worth doing.
35     char temp[4096];
36     VsprintfLiteral(temp, format, ap);
37 
38     mozilla::RLogConnector::GetInstance()->Log(level, std::string(temp));
39   }
40   return 0;
41 }
42 
rLogLvlToMozLogLvl(int level)43 static mozilla::LogLevel rLogLvlToMozLogLvl(int level) {
44   switch (level) {
45     case LOG_EMERG:
46     case LOG_ALERT:
47     case LOG_CRIT:
48     case LOG_ERR:
49       return mozilla::LogLevel::Error;
50     case LOG_WARNING:
51       return mozilla::LogLevel::Warning;
52     case LOG_NOTICE:
53       return mozilla::LogLevel::Info;
54     case LOG_INFO:
55       return mozilla::LogLevel::Debug;
56     case LOG_DEBUG:
57     default:
58       return mozilla::LogLevel::Verbose;
59   }
60 }
61 
62 MOZ_MTLOG_MODULE("nicer");
63 
64 namespace mozilla {
65 
66 RLogConnector* RLogConnector::instance;
67 
RLogConnector()68 RLogConnector::RLogConnector()
69     : log_limit_(4096), mutex_("RLogConnector::mutex_"), disableCount_(0) {}
70 
71 RLogConnector::~RLogConnector() = default;
72 
SetLogLimit(uint32_t new_limit)73 void RLogConnector::SetLogLimit(uint32_t new_limit) {
74   OffTheBooksMutexAutoLock lock(mutex_);
75   log_limit_ = new_limit;
76   RemoveOld();
77 }
78 
ShouldLog(int level) const79 bool RLogConnector::ShouldLog(int level) const {
80   return level <= LOG_INFO ||
81          MOZ_LOG_TEST(getLogModule(), rLogLvlToMozLogLvl(level));
82 }
83 
Log(int level,std::string && log)84 void RLogConnector::Log(int level, std::string&& log) {
85   MOZ_MTLOG(rLogLvlToMozLogLvl(level), log);
86   OffTheBooksMutexAutoLock lock(mutex_);
87   if (disableCount_ == 0) {
88     AddMsg(std::move(log));
89   }
90 }
91 
AddMsg(std::string && msg)92 void RLogConnector::AddMsg(std::string&& msg) {
93   log_messages_.push_front(std::move(msg));
94   RemoveOld();
95 }
96 
RemoveOld()97 inline void RLogConnector::RemoveOld() {
98   if (log_messages_.size() > log_limit_) {
99     log_messages_.resize(log_limit_);
100   }
101 }
102 
CreateInstance()103 RLogConnector* RLogConnector::CreateInstance() {
104   if (!instance) {
105     instance = new RLogConnector;
106     NR_reg_init(NR_REG_MODE_LOCAL);
107     r_log_set_extra_destination(LOG_DEBUG, &ringbuffer_vlog);
108   }
109   return instance;
110 }
111 
GetInstance()112 RLogConnector* RLogConnector::GetInstance() { return instance; }
113 
DestroyInstance()114 void RLogConnector::DestroyInstance() {
115   // First param is ignored when passing null
116   r_log_set_extra_destination(LOG_DEBUG, nullptr);
117   delete instance;
118   instance = nullptr;
119 }
120 
121 // As long as at least one PeerConnection exists in a Private Window rlog
122 // messages will not be saved in the RLogConnector. This is necessary because
123 // the log_messages buffer is shared across all instances of
124 // PeerConnectionImpls. There is no way with the current structure of r_log to
125 // run separate logs.
126 
EnterPrivateMode()127 void RLogConnector::EnterPrivateMode() {
128   OffTheBooksMutexAutoLock lock(mutex_);
129   ++disableCount_;
130   MOZ_ASSERT(disableCount_ != 0);
131 
132   if (disableCount_ == 1) {
133     AddMsg("LOGGING SUSPENDED: a connection is active in a Private Window ***");
134   }
135 }
136 
ExitPrivateMode()137 void RLogConnector::ExitPrivateMode() {
138   OffTheBooksMutexAutoLock lock(mutex_);
139   MOZ_ASSERT(disableCount_ != 0);
140 
141   if (--disableCount_ == 0) {
142     AddMsg(
143         "LOGGING RESUMED: no connections are active in a Private Window ***");
144   }
145 }
146 
Clear()147 void RLogConnector::Clear() {
148   OffTheBooksMutexAutoLock lock(mutex_);
149   log_messages_.clear();
150 }
151 
Filter(const std::string & substring,uint32_t limit,std::deque<std::string> * matching_logs)152 void RLogConnector::Filter(const std::string& substring, uint32_t limit,
153                            std::deque<std::string>* matching_logs) {
154   std::vector<std::string> substrings;
155   substrings.push_back(substring);
156   FilterAny(substrings, limit, matching_logs);
157 }
158 
AnySubstringMatches(const std::vector<std::string> & substrings,const std::string & string)159 inline bool AnySubstringMatches(const std::vector<std::string>& substrings,
160                                 const std::string& string) {
161   for (auto sub = substrings.begin(); sub != substrings.end(); ++sub) {
162     if (string.find(*sub) != std::string::npos) {
163       return true;
164     }
165   }
166   return false;
167 }
168 
FilterAny(const std::vector<std::string> & substrings,uint32_t limit,std::deque<std::string> * matching_logs)169 void RLogConnector::FilterAny(const std::vector<std::string>& substrings,
170                               uint32_t limit,
171                               std::deque<std::string>* matching_logs) {
172   OffTheBooksMutexAutoLock lock(mutex_);
173   if (limit == 0) {
174     // At a max, all of the log messages.
175     limit = log_limit_;
176   }
177 
178   for (auto log = log_messages_.begin();
179        log != log_messages_.end() && matching_logs->size() < limit; ++log) {
180     if (AnySubstringMatches(substrings, *log)) {
181       matching_logs->push_front(*log);
182     }
183   }
184 }
185 
186 }  // namespace mozilla
187