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