Bug 906990 - Part 5: Allow logging related to a given candidate pair to be fetched. r=ekr

This commit is contained in:
Byron Campen [:bwc] 2013-10-29 10:45:09 -07:00
Родитель 9c73373ad4
Коммит 9259ed0435
8 изменённых файлов: 570 добавлений и 1 удалений

Просмотреть файл

@ -12,6 +12,7 @@ EXPORTS.mtransport += [
'../nricectx.h',
'../nricemediastream.h',
'../nriceresolverfake.h',
'../rlogringbuffer.h',
'../runnable_utils.h',
'../runnable_utils_generated.h',
'../sigslot.h',

Просмотреть файл

@ -13,6 +13,7 @@ mtransport_lcppsrcs = [
'nriceresolver.cpp',
'nriceresolverfake.cpp',
'nrinterfaceprioritizer.cpp',
'rlogringbuffer.cpp',
'simpletokenbucket.cpp',
'transportflow.cpp',
'transportlayer.cpp',

Просмотреть файл

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Original author: bcampen@mozilla.com */
#include <cstdarg>
#include "rlogringbuffer.h"
#include <deque>
#include <string>
#include "mozilla/Move.h" // Pinch hitting for <utility> and std::move
#include <vector>
extern "C" {
#include <csi_platform.h>
#include "r_log.h"
}
/* Matches r_dest_vlog type defined in r_log.h */
static int ringbuffer_vlog(int facility,
int level,
const char *format,
va_list ap) {
MOZ_ASSERT(mozilla::RLogRingBuffer::GetInstance());
// I could be evil and printf right into a std::string, but unless this
// shows up in profiling, it is not worth doing.
char temp[4096];
vsnprintf(temp, sizeof(temp), format, ap);
mozilla::RLogRingBuffer::GetInstance()->Log(std::string(temp));
return 0;
}
namespace mozilla {
RLogRingBuffer* RLogRingBuffer::instance;
RLogRingBuffer::RLogRingBuffer()
: log_limit_(4096),
mutex_("RLogRingBuffer::mutex_") {
}
RLogRingBuffer::~RLogRingBuffer() {
}
void RLogRingBuffer::SetLogLimit(uint32_t new_limit) {
mozilla::MutexAutoLock lock(mutex_);
log_limit_ = new_limit;
RemoveOld();
}
void RLogRingBuffer::Log(std::string&& log) {
mozilla::MutexAutoLock lock(mutex_);
log_messages_.push_front(Move(log));
RemoveOld();
}
inline void RLogRingBuffer::RemoveOld() {
if (log_messages_.size() > log_limit_) {
log_messages_.resize(log_limit_);
}
}
RLogRingBuffer* RLogRingBuffer::CreateInstance() {
if (!instance) {
instance = new RLogRingBuffer;
r_log_set_extra_destination(LOG_DEBUG, &ringbuffer_vlog);
}
return instance;
}
RLogRingBuffer* RLogRingBuffer::GetInstance() {
return instance;
}
void RLogRingBuffer::DestroyInstance() {
// First param is ignored when passing null
r_log_set_extra_destination(LOG_DEBUG, nullptr);
delete instance;
instance = nullptr;
}
void RLogRingBuffer::Filter(const std::string& substring,
uint32_t limit,
std::deque<std::string>* matching_logs) {
std::vector<std::string> substrings;
substrings.push_back(substring);
FilterAny(substrings, limit, matching_logs);
}
inline bool AnySubstringMatches(const std::vector<std::string>& substrings,
const std::string& string) {
for (auto sub = substrings.begin(); sub != substrings.end(); ++sub) {
if (string.find(*sub) != std::string::npos) {
return true;
}
}
return false;
}
void RLogRingBuffer::FilterAny(const std::vector<std::string>& substrings,
uint32_t limit,
std::deque<std::string>* matching_logs) {
mozilla::MutexAutoLock lock(mutex_);
if (limit == 0) {
// At a max, all of the log messages.
limit = log_limit_;
}
for (auto log = log_messages_.begin();
log != log_messages_.end() && matching_logs->size() < limit;
++log) {
if (AnySubstringMatches(substrings, *log)) {
matching_logs->push_front(*log);
}
}
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// Some of this code is cut-and-pasted from nICEr. Copyright is:
/*
Copyright (c) 2007, Adobe Systems, Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems, Network Resonance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Original author: bcampen@mozilla.com */
/*
This file defines an r_dest_vlog that can be used to accumulate log messages
for later inspection/filtering. The intent is to use this for interactive
debug purposes on an about:webrtc page or similar.
*/
#ifndef rlogringbuffer_h__
#define rlogringbuffer_h__
#include <deque>
#include <string>
#include <vector>
#include "mozilla/Mutex.h"
#include "m_cpp_utils.h"
namespace mozilla {
class RLogRingBuffer {
public:
/*
NB: These are not threadsafe, nor are they safe to call during static
init/deinit.
*/
static RLogRingBuffer* CreateInstance();
static RLogRingBuffer* GetInstance();
static void DestroyInstance();
/*
Retrieves log statements that match a given substring, subject to a
limit. |matching_logs| will be filled in chronological order (front()
is oldest, back() is newest). |limit| == 0 will be interpreted as no
limit.
*/
void Filter(const std::string& substring,
uint32_t limit,
std::deque<std::string>* matching_logs);
void FilterAny(const std::vector<std::string>& substrings,
uint32_t limit,
std::deque<std::string>* matching_logs);
inline void GetAny(uint32_t limit,
std::deque<std::string>* matching_logs) {
Filter("", limit, matching_logs);
}
void SetLogLimit(uint32_t new_limit);
void Log(std::string&& log);
private:
RLogRingBuffer();
~RLogRingBuffer();
void RemoveOld();
static RLogRingBuffer* instance;
/*
* Might be worthwhile making this a circular buffer, but I think it is
* preferable to take up as little space as possible if no logging is
* happening/the ringbuffer is not being used.
*/
std::deque<std::string> log_messages_;
/* Max size of log buffer (should we use time-depth instead/also?) */
uint32_t log_limit_;
mozilla::Mutex mutex_;
DISALLOW_COPY_ASSIGN(RLogRingBuffer);
}; // class RLogRingBuffer
} // namespace mozilla
#endif // rlogringbuffer_h__

Просмотреть файл

@ -7,6 +7,7 @@
// Original author: ekr@rtfm.com
#include <algorithm>
#include <deque>
#include <iostream>
#include <limits>
#include <map>
@ -30,6 +31,7 @@
#include "nriceresolver.h"
#include "nrinterfaceprioritizer.h"
#include "mtransport_test_utils.h"
#include "rlogringbuffer.h"
#include "runnable_utils.h"
#include "stunserver.h"
// TODO(bcampen@mozilla.com): Big fat hack since the build system doesn't give
@ -1222,6 +1224,39 @@ TEST_F(IceConnectTest, TestPollCandPairsDuringConnect) {
ASSERT_TRUE(ContainsSucceededPair(pairs2));
}
TEST_F(IceConnectTest, TestRLogRingBuffer) {
RLogRingBuffer::CreateInstance();
AddStream("first", 1);
ASSERT_TRUE(Gather(true));
p1_->Connect(p2_, TRICKLE_NONE, false);
p2_->Connect(p1_, TRICKLE_NONE, false);
std::vector<NrIceCandidatePair> pairs1;
std::vector<NrIceCandidatePair> pairs2;
p1_->StartChecks();
p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
p2_->StartChecks();
p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
WaitForComplete();
p1_->UpdateAndValidateCandidatePairs(0, &pairs1);
p2_->UpdateAndValidateCandidatePairs(0, &pairs2);
ASSERT_TRUE(ContainsSucceededPair(pairs1));
ASSERT_TRUE(ContainsSucceededPair(pairs2));
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("CAND-PAIR", 0, &logs);
std::cerr << "Dumping CAND-PAIR logging:" << std::endl;
for (auto i = logs.rbegin(); i != logs.rend(); ++i) {
std::cerr << *i << std::endl;
}
RLogRingBuffer::DestroyInstance();
}
TEST_F(PrioritizerTest, TestPrioritizer) {
SetPriorizer(::mozilla::CreateInterfacePrioritizer());

Просмотреть файл

@ -10,6 +10,7 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
CPP_UNIT_TESTS += [
'ice_unittest.cpp',
'nrappkit_unittest.cpp',
'rlogringbuffer_unittest.cpp',
'runnable_utils_unittest.cpp',
'simpletokenbucket_unittest.cpp',
'sockettransportservice_unittest.cpp',

Просмотреть файл

@ -0,0 +1,269 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Original author: bcampen@mozilla.com */
#include "rlogringbuffer.h"
extern "C" {
#include "registry.h"
#include "r_log.h"
}
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
#include <deque>
#include <string>
#include <vector>
using mozilla::RLogRingBuffer;
int NR_LOG_TEST = 0;
class RLogRingBufferTest : public ::testing::Test {
public:
RLogRingBufferTest() {
Init();
}
~RLogRingBufferTest() {
Free();
}
void Init() {
RLogRingBuffer::CreateInstance();
}
void Free() {
RLogRingBuffer::DestroyInstance();
}
void ReInit() {
Free();
Init();
}
};
TEST_F(RLogRingBufferTest, TestGetFree) {
RLogRingBuffer* instance = RLogRingBuffer::GetInstance();
ASSERT_NE(nullptr, instance);
}
TEST_F(RLogRingBufferTest, TestFilterEmpty) {
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(0U, logs.size());
}
TEST_F(RLogRingBufferTest, TestBasicFilter) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("Test", 0, &logs);
ASSERT_EQ(1U, logs.size());
}
TEST_F(RLogRingBufferTest, TestBasicFilterContent) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("Test", 0, &logs);
ASSERT_EQ("Test", logs.back());
}
TEST_F(RLogRingBufferTest, TestFilterAnyFrontMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::vector<std::string> substrings;
substrings.push_back("foo");
substrings.push_back("Test");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->FilterAny(substrings, 0, &logs);
ASSERT_EQ("Test", logs.back());
}
TEST_F(RLogRingBufferTest, TestFilterAnyBackMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::vector<std::string> substrings;
substrings.push_back("Test");
substrings.push_back("foo");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->FilterAny(substrings, 0, &logs);
ASSERT_EQ("Test", logs.back());
}
TEST_F(RLogRingBufferTest, TestFilterAnyBothMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::vector<std::string> substrings;
substrings.push_back("Tes");
substrings.push_back("est");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->FilterAny(substrings, 0, &logs);
ASSERT_EQ("Test", logs.back());
}
TEST_F(RLogRingBufferTest, TestFilterAnyNeitherMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test");
std::vector<std::string> substrings;
substrings.push_back("tes");
substrings.push_back("esT");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->FilterAny(substrings, 0, &logs);
ASSERT_EQ(0U, logs.size());
}
TEST_F(RLogRingBufferTest, TestAllMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(2U, logs.size());
}
TEST_F(RLogRingBufferTest, TestOrder) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ("Test2", logs.back());
ASSERT_EQ("Test1", logs.front());
}
TEST_F(RLogRingBufferTest, TestNoMatch) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("foo", 0, &logs);
ASSERT_EQ(0U, logs.size());
}
TEST_F(RLogRingBufferTest, TestSubstringFilter) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("t1", 0, &logs);
ASSERT_EQ(1U, logs.size());
ASSERT_EQ("Test1", logs.back());
}
TEST_F(RLogRingBufferTest, TestFilterLimit) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->Filter("Test", 2, &logs);
ASSERT_EQ(2U, logs.size());
ASSERT_EQ("Test6", logs.back());
ASSERT_EQ("Test5", logs.front());
}
TEST_F(RLogRingBufferTest, TestFilterAnyLimit) {
r_log(NR_LOG_TEST, LOG_DEBUG, "TestOne");
r_log(NR_LOG_TEST, LOG_DEBUG, "TestTwo");
r_log(NR_LOG_TEST, LOG_DEBUG, "TestThree");
r_log(NR_LOG_TEST, LOG_DEBUG, "TestFour");
r_log(NR_LOG_TEST, LOG_DEBUG, "TestFive");
r_log(NR_LOG_TEST, LOG_DEBUG, "TestSix");
std::vector<std::string> substrings;
// Matches Two, Three, Four, and Six
substrings.push_back("tT");
substrings.push_back("o");
substrings.push_back("r");
substrings.push_back("S");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->FilterAny(substrings, 2, &logs);
ASSERT_EQ(2U, logs.size());
ASSERT_EQ("TestSix", logs.back());
ASSERT_EQ("TestFour", logs.front());
}
TEST_F(RLogRingBufferTest, TestLimit) {
RLogRingBuffer::GetInstance()->SetLogLimit(3);
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(3U, logs.size());
ASSERT_EQ("Test6", logs.back());
ASSERT_EQ("Test4", logs.front());
}
TEST_F(RLogRingBufferTest, TestLimitBulkDiscard) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
RLogRingBuffer::GetInstance()->SetLogLimit(3);
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(3U, logs.size());
ASSERT_EQ("Test6", logs.back());
ASSERT_EQ("Test4", logs.front());
}
TEST_F(RLogRingBufferTest, TestIncreaseLimit) {
RLogRingBuffer::GetInstance()->SetLogLimit(3);
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
RLogRingBuffer::GetInstance()->SetLogLimit(300);
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(3U, logs.size());
ASSERT_EQ("Test6", logs.back());
ASSERT_EQ("Test4", logs.front());
}
TEST_F(RLogRingBufferTest, TestClear) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
RLogRingBuffer::GetInstance()->SetLogLimit(0);
RLogRingBuffer::GetInstance()->SetLogLimit(4096);
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(0U, logs.size());
}
TEST_F(RLogRingBufferTest, TestReInit) {
r_log(NR_LOG_TEST, LOG_DEBUG, "Test1");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test2");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test3");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test4");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test5");
r_log(NR_LOG_TEST, LOG_DEBUG, "Test6");
ReInit();
std::deque<std::string> logs;
RLogRingBuffer::GetInstance()->GetAny(0, &logs);
ASSERT_EQ(0U, logs.size());
}
int main(int argc, char** argv) {
NR_reg_init(NR_REG_MODE_LOCAL);
r_log_init();
/* Would be nice to be able to register/unregister in the fixture */
const char* facility = "rlogringbuffer_test";
r_log_register(const_cast<char*>(facility), &NR_LOG_TEST);
::testing::InitGoogleTest(&argc, argv);
int rv = RUN_ALL_TESTS();
return rv;
}

Просмотреть файл

@ -330,6 +330,15 @@ int r_dump(int facility,int level,char *name,char *data,int len)
return(0);
}
// Some platforms (notably WIN32) do not have this
#ifndef va_copy
#ifdef WIN32
#define va_copy(dest, src) ( (dest) = (src) )
#else // WIN32
#error va_copy undefined, and semantics of assignment on va_list unknown
#endif //WIN32
#endif //va_copy
int r_vlog(int facility,int level,const char *format,va_list ap)
{
char log_fmt_buf[MAX_ERROR_STRING_SIZE];
@ -354,7 +363,12 @@ int r_vlog(int facility,int level,const char *format,va_list ap)
for(i=0; i<LOG_NUM_DESTINATIONS; i++){
if(r_logging_dest(i,facility,level)){
log_destinations[i].dest_vlog(facility,level,fmt_str,ap);
// Some platforms do not react well when you use a va_list more than
// once
va_list copy;
va_copy(copy, ap);
log_destinations[i].dest_vlog(facility,level,fmt_str,copy);
va_end(copy);
}
}
return(0);