Commit 006c32f4 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

sync with trunk


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac448@4131 e5f2f494-b856-4b98-b285-d166d9295462
parents 2890dbb8 09149599
142. [func] jinmei
b10-auth: updated query benchmark so that it can test in memory
data source. Also fixed a bug that the output buffer isn't
cleared after query processing, resulting in misleading results
or program crash. This is a regression due to change #135.
(Trac #465, svn r4103)
141. [bug] jinmei
b10-auth: Fixed a bug that the authoritative server includes
trailing garbage data in responses. This is a regression due to
change #135. (Trac #462, svn r4081)
140. [func] y-aharen
src/bin/auth: Added a feature to count queries and send counter
values to statistics periodically. To support it, added wrapping
class of asio::deadline_timer to use as interval timer.
The counters can be seen using the "Stats show" command from
bindctl. The result would look like:
... "auth.queries.tcp": 1, "auth.queries.udp": 1 ...
Using the "Auth sendstats" command you can make b10-auth send the
counters to b10-stats immediately.
(Trac #347, svn r4026)
139. [build] jreed
Introduced configure option and make targets for generating
Python code coverage report. This adds new make targets:
report-python-coverage and clean-python-coverage. The C++
code coverage targets were renamed to clean-cpp-coverage
and report-cpp-coverage. (Trac #362, svn r4023)
138. [func]* jinmei
b10-auth: added a configuration interface to support in memory
data sources. For example, the following command to bindctl
......
......@@ -8,20 +8,30 @@ DISTCLEANFILES = config.report
# When running distcheck target, do not install the configurations
DISTCHECK_CONFIGURE_FLAGS = --disable-install-configurations
clean-coverage:
clean-cpp-coverage:
@if [ $(USE_LCOV) = yes ] ; then \
$(LCOV) --directory . --zerocounters; \
rm -rf coverage/; \
else \
echo "Code coverage not enabled at configuration time"; \
exit 1; \
echo "C++ code coverage not enabled at configuration time." ; \
echo "Use: ./configure --with-lcov" ; \
fi
clean-python-coverage:
@if [ $(USE_PYCOVERAGE) = yes ] ; then \
rm -f $(abs_top_srcdir)/.coverage ; \
rm -rf $(abs_top_srcdir)/py-coverage-html ; \
else \
echo "Python code coverage not enabled at configuration time." ; \
echo "Use: ./configure --with-pycoverage" ; \
fi
perform-coverage: check
report-coverage:
$(LCOV) --capture --directory . --output-file all.info
$(LCOV) --remove all.info \
report-cpp-coverage:
@if [ $(USE_LCOV) = yes ] ; then \
$(LCOV) --capture --directory . --output-file all.info ; \
$(LCOV) --remove all.info \
c++/4.4\*/\* \
c++/4.4\*/backward/\* \
c++/4.4\*/bits/\* \
......@@ -36,11 +46,30 @@ report-coverage:
\*_unittests.cc \
\*_unittest.cc \
\*_unittests.h \
--output report.info
$(GENHTML) --legend -o coverage report.info
--output report.info ; \
$(GENHTML) --legend -o $(abs_top_builddir)/coverage-cpp-html report.info ; \
echo "Generated C++ Code Coverage report in HTML at $(abs_top_builddir)/coverage-cpp-html" ; \
else \
echo "C++ code coverage not enabled at configuration time." ; \
echo "Use: ./configure --with-lcov" ; \
fi
report-python-coverage:
@if [ $(USE_PYCOVERAGE) = yes ] ; then \
$(PYCOVERAGE) html -d $(abs_top_builddir)/coverage-python-html --omit=src/bin/bind10/tests/,src/bin/bindctl/tests/,src/bin/cfgmgr/tests/,src/bin/cmdctl/tests/,src/bin/loadzone/tests/,src/bin/msgq/tests/,src/bin/stats/tests/,src/bin/tests/,src/bin/xfrin/tests/,src/bin/xfrout/tests/,src/bin/zonemgr/tests/,src/lib/dns/python/tests/,src/lib/dns/tests/,src/lib/python/isc/cc/tests/,src/lib/python/isc/config/tests/,src/lib/python/isc/datasrc/tests/,src/lib/python/isc/log/tests/,src/lib/python/isc/net/tests/,src/lib/python/isc/notify/tests/,src/lib/python/isc/util/tests/ ; \
echo "Generated Python Code Coverage report in HTML at $(abs_top_builddir)/coverage-python-html" ; \
else \
echo "Python code coverage not enabled at configuration time." ; \
echo "Use: ./configure --with-pycoverage" ; \
fi
# for python and c++ test coverage
coverage: clean-coverage perform-coverage report-coverage
clean-coverage: clean-cpp-coverage clean-python-coverage
report-coverage: report-cpp-coverage report-python-coverage
#### include external sources in the distributed tarball:
EXTRA_DIST = ext/asio/README
EXTRA_DIST += ext/asio/asio/local/stream_protocol.hpp
......
......@@ -15,11 +15,11 @@ five year plan are described here:
This release includes the bind10 master process, b10-msgq message
bus, b10-auth authoritative DNS server (with SQLite3 backend),
b10-cmdctl remote control daemon, b10-cfgmgr configuration manager,
b10-xfrin AXFR inbound service, b10-xfrout outgoing AXFR service,
b10-zonemgr secondary manager, b10-stats statistics collection and
reporting daemon, and a new libdns++ library for C++ with a python
wrapper.
b10-recurse forwarding DNS server, b10-cmdctl remote control daemon,
b10-cfgmgr configuration manager, b10-xfrin AXFR inbound service,
b10-xfrout outgoing AXFR service, b10-zonemgr secondary manager,
b10-stats statistics collection and reporting daemon, and a new
libdns++ library for C++ with a python wrapper.
Documentation is included and also available via the BIND 10
website at http://bind10.isc.org/
......@@ -93,26 +93,55 @@ Then run "make check" to run these tests.
TEST COVERAGE
Code coverage reports may be generated using make. These are
based on running on the unit tests. The resulting reports are placed
in coverage-cpp-html and coverage-python-html directories for C++
and Python, respectively.
The code coverage report for the C++ tests uses LCOV. It is available
from http://ltp.sourceforge.net/. To generate your own HTML report,
from http://ltp.sourceforge.net/. To generate the HTML report,
first configure BIND 10 with:
./configure --with-lcov
The code coverage report for the Python tests uses coverage.py (aka
pycoverage). It is available from http://nedbatchelder.com/code/coverage/.
To generate the HTML report, first configure BIND 10 with:
./configure --with-pycoverage
Doing code coverage tests:
make coverage
Does the following:
Does the clean, perform, and report targets for C++ and Python.
make clean-coverage
Zeroes the lcov code coverage counters and removes the coverage HTML.
Zeroes the code coverage counters and removes the HTML reports
for C++ and Python.
make perform-coverage
Runs the C++ tests (using googletests framework).
Runs the C++ (using the googletests framework) and Python
tests.
make report-coverage
Generates the coverage HTML, excluding some unrelated headers.
The HTML reports are placed in a directory called coverage/.
Generates the coverage reports in HTML for C++ and Python.
make clean-cpp-coverage
Zeroes the code coverage counters and removes the HTML report
for the C++ tests.
make clean-python-coverage
Zeroes the code coverage counters and removes the HTML report
for the Python tests.
make report-cpp-coverage
Generates the coverage report in HTML for C++, excluding
some unrelated headers. The HTML reports are placed in a
directory called coverage-cpp-html/.
make report-python-coverage
Generates the coverage report in HTML for Python. The HTML
reports are placed in a directory called coverage-python-html/.
DEVELOPERS
......
......@@ -287,6 +287,26 @@ AC_TRY_COMPILE([
AC_DEFINE(HAVE_SA_LEN, 1, [Define to 1 if sockaddr has a sa_len member, and corresponding sin_len and sun_len])],
AC_MSG_RESULT(no))
AC_ARG_WITH(pycoverage,
[ --with-pycoverage[=PROGRAM] enable python code coverage using the specified coverage], pycoverage="$withval", pycoverage="no")
if test "$pycoverage" = "no" ; then
# just run the tests normally with python
PYCOVERAGE_RUN="${PYTHON}"
USE_PYCOVERAGE="no"
elif test "$pycoverage" = "yes" ; then
PYCOVERAGE="coverage"
PYCOVERAGE_RUN="${PYCOVERAGE} run --branch --append"
USE_PYCOVERAGE="yes"
else
PYCOVERAGE="$pycoverage"
PYCOVERAGE_RUN="${PYCOVERAGE} run --branch --append"
USE_PYCOVERAGE="yes"
fi
AM_CONDITIONAL(ENABLE_PYTHON_COVERAGE, test x$USE_PYCOVERAGE != xno)
AC_SUBST(PYCOVERAGE)
AC_SUBST(PYCOVERAGE_RUN)
AC_SUBST(USE_PYCOVERAGE)
AC_ARG_WITH(lcov,
[ --with-lcov[=PROGRAM] enable gtest and coverage target using the specified lcov], lcov="$withval", lcov="no")
......@@ -351,7 +371,7 @@ if test "${boost_include_path}" ; then
BOOST_INCLUDES="-I${boost_include_path}"
CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
fi
AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp],,
AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp],,
AC_MSG_ERROR([Missing required header files.]))
CPPFLAGS="$CPPFLAGS_SAVES"
AC_SUBST(BOOST_INCLUDES)
......@@ -731,7 +751,8 @@ Features:
Developer:
Google Tests: $gtest_path
Code Coverage: $USE_LCOV
C++ Code Coverage: $USE_LCOV
Python Code Coverage: $USE_PYCOVERAGE
Generate Manuals: $enable_man
END
......
......@@ -42,9 +42,8 @@
<note>
<para>
BIND 10, at this time, does not provide a recursive
DNS server. It does provide a EDNS0- and DNSSEC-capable
authoritative DNS server.
BIND 10 provides a EDNS0- and DNSSEC-capable
authoritative DNS server and a forwarding DNS server.
</para>
</note>
......
......@@ -41,6 +41,7 @@ b10_auth_SOURCES += auth_srv.cc auth_srv.h
b10_auth_SOURCES += change_user.cc change_user.h
b10_auth_SOURCES += config.cc config.h
b10_auth_SOURCES += common.h
b10_auth_SOURCES += statistics.cc statistics.h
b10_auth_SOURCES += main.cc
b10_auth_LDADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
b10_auth_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
......
......@@ -59,6 +59,11 @@
"command_name": "shutdown",
"command_description": "Shut down authoritative DNS server",
"command_args": []
},
{
"command_name": "sendstats",
"command_description": "Send data to a statistics module at once",
"command_args": []
}
]
}
......
......@@ -55,6 +55,7 @@
#include <auth/config.h>
#include <auth/auth_srv.h>
#include <auth/query.h>
#include <auth/statistics.h>
using namespace std;
......@@ -100,6 +101,9 @@ public:
/// Hot spot cache
isc::datasrc::HotCache cache_;
/// Query counters for statistics
AuthCounters counters_;
private:
std::string db_file_;
......@@ -111,6 +115,9 @@ private:
bool xfrout_connected_;
AbstractXfroutClient& xfrout_client_;
/// Increment query counter
void incCounter(const int protocol);
};
AuthSrvImpl::AuthSrvImpl(const bool use_cache,
......@@ -118,6 +125,7 @@ AuthSrvImpl::AuthSrvImpl(const bool use_cache,
config_session_(NULL), verbose_mode_(false),
xfrin_session_(NULL),
memory_datasrc_class_(RRClass::IN()),
counters_(verbose_mode_),
xfrout_connected_(false),
xfrout_client_(xfrout_client)
{
......@@ -154,33 +162,20 @@ private:
AuthSrv* server_;
};
// This is a derived class of \c DNSAnswer, to serve as a
// callback in the asiolink module. It takes a completed
// set of answer data from the DNS lookup and assembles it
// into a wire-format response.
// This is a derived class of \c DNSAnswer, to serve as a callback in the
// asiolink module. We actually shouldn't do anything in this class because
// we build complete response messages in the process methods; otherwise
// the response message will contain trailing garbage. In future, we should
// probably even drop the reliance on DNSAnswer. We don't need the coroutine
// tricks provided in that framework, and its overhead would be significant
// in terms of performance consideration for the authoritative server
// implementation.
class MessageAnswer : public DNSAnswer {
public:
MessageAnswer(AuthSrv* srv) : server_(srv) {}
virtual void operator()(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer) const
{
MessageRenderer renderer(*buffer);
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
ConstEDNSPtr edns(message->getEDNS());
renderer.setLengthLimit(edns ? edns->getUDPSize() :
Message::DEFAULT_MAX_UDPSIZE);
} else {
renderer.setLengthLimit(65535);
}
message->toWire(renderer);
if (server_->getVerbose()) {
cerr << "[b10-auth] sending a response (" << renderer.getLength()
<< " bytes):\n" << message->toText() << endl;
}
}
private:
AuthSrv* server_;
MessageAnswer(AuthSrv*) {}
virtual void operator()(const IOMessage&, MessagePtr,
OutputBufferPtr) const
{}
};
// This is a derived class of \c SimpleCallback, to serve
......@@ -294,6 +289,11 @@ AuthSrv::setConfigSession(ModuleCCSession* config_session) {
impl_->config_session_ = config_session;
}
void
AuthSrv::setStatisticsSession(AbstractSession* statistics_session) {
impl_->counters_.setStatisticsSession(statistics_session);
}
ModuleCCSession*
AuthSrv::getConfigSession() const {
return (impl_->config_session_);
......@@ -429,6 +429,9 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
message->setHeaderFlag(Message::HEADERFLAG_AA);
message->setRcode(Rcode::NOERROR());
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
if (remote_edns) {
EDNSPtr local_edns = EDNSPtr(new EDNS());
local_edns->setDNSSECAwareness(dnssec_ok);
......@@ -476,6 +479,9 @@ bool
AuthSrvImpl::processAxfrQuery(const IOMessage& io_message, MessagePtr message,
OutputBufferPtr buffer)
{
// Increment query counter.
incCounter(io_message.getSocket().getProtocol());
if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
if (verbose_mode_) {
cerr << "[b10-auth] AXFR query over UDP isn't allowed" << endl;
......@@ -601,6 +607,19 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
return (true);
}
void
AuthSrvImpl::incCounter(const int protocol) {
// Increment query counter.
if (protocol == IPPROTO_UDP) {
counters_.inc(AuthCounters::COUNTER_UDP_QUERY);
} else if (protocol == IPPROTO_TCP) {
counters_.inc(AuthCounters::COUNTER_TCP_QUERY);
} else {
// unknown protocol
isc_throw(Unexpected, "Unknown protocol: " << protocol);
}
}
ConstElementPtr
AuthSrvImpl::setDbFile(ConstElementPtr config) {
ConstElementPtr answer = isc::config::createAnswer();
......@@ -670,3 +689,12 @@ AuthSrv::updateConfig(ConstElementPtr new_config) {
return (isc::config::createAnswer(1, error.what()));
}
}
bool AuthSrv::submitStatistics() const {
return (impl_->counters_.submitStatistics());
}
uint64_t
AuthSrv::getCounter(const AuthCounters::CounterType type) const {
return (impl_->counters_.getCounter(type));
}
......@@ -27,6 +27,7 @@
#include <config/ccsession.h>
#include <asiolink/asiolink.h>
#include <auth/statistics.h>
namespace isc {
namespace datasrc {
......@@ -62,6 +63,7 @@ class AuthSrvImpl;
///
/// The design of this class is still in flux. It's quite likely to change
/// in future versions.
///
class AuthSrv {
///
/// \name Constructors, Assignment Operator and Destructor.
......@@ -96,6 +98,8 @@ public:
/// \param message Pointer to the \c Message object
/// \param buffer Pointer to an \c OutputBuffer for the resposne
/// \param server Pointer to the \c DNSServer
///
/// \throw isc::Unexpected Protocol type of \a message is unexpected
void processMessage(const asiolink::IOMessage& io_message,
isc::dns::MessagePtr message,
isc::dns::OutputBufferPtr buffer,
......@@ -281,6 +285,49 @@ public:
void setMemoryDataSrc(const isc::dns::RRClass& rrclass,
MemoryDataSrcPtr memory_datasrc);
/// \brief Set the communication session with Statistics.
///
/// This function never throws an exception as far as
/// AuthCounters::setStatisticsSession() doesn't throw.
///
/// Note: this interface is tentative. We'll revisit the ASIO and
/// session frameworks, at which point the session will probably
/// be passed on construction of the server.
///
/// \param statistics_session A Session object over which statistics
/// information is exchanged with statistics module.
/// The session must be established before setting in the server
/// object.
/// Ownership isn't transferred: the caller is responsible for keeping
/// this object to be valid while the server object is working and for
/// disconnecting the session and destroying the object when the server
/// is shutdown.
void setStatisticsSession(isc::cc::AbstractSession* statistics_session);
/// \brief Submit statistics counters to statistics module.
///
/// This function can throw an exception from
/// AuthCounters::submitStatistics().
///
/// \return true on success, false on failure (e.g. session timeout,
/// session error).
bool submitStatistics() const;
/// \brief Get the value of counter in the AuthCounters.
///
/// This function calls AuthCounters::getCounter() and
/// returns its return value.
///
/// This function never throws an exception as far as
/// AuthCounters::getCounter() doesn't throw.
///
/// Note: Currently this function is for testing purpose only.
///
/// \param type Type of a counter to get the value of
///
/// \return the value of the counter.
uint64_t getCounter(const AuthCounters::CounterType type) const;
private:
AuthSrvImpl* impl_;
asiolink::IOService* io_service_;
......
......@@ -11,6 +11,7 @@ query_bench_SOURCES = query_bench.cc
query_bench_SOURCES += ../query.h ../query.cc
query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
query_bench_SOURCES += ../config.h ../config.cc
query_bench_SOURCES += ../statistics.h ../statistics.cc
query_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
query_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
......
......@@ -33,6 +33,7 @@
#include <xfr/xfrout_client.h>
#include <auth/auth_srv.h>
#include <auth/config.h>
#include <auth/query.h>
#include <asiolink/asiolink.h>
......@@ -53,24 +54,25 @@ XfroutClient xfrout_client("dummy_path"); // path doesn't matter
// Just something to pass as the server to resume
class DummyServer : public DNSServer {
public:
virtual void operator()(asio::error_code, size_t) { }
virtual void resume(const bool) { }
virtual void operator()(asio::error_code, size_t) {}
virtual void resume(const bool) {}
virtual DNSServer* clone() {
return new DummyServer(*this);
return (new DummyServer(*this));
}
};
class QueryBenchMark {
private:
protected:
// Maintain dynamically generated objects via shared pointers because
// QueryBenchMark objects will be copied.
typedef boost::shared_ptr<AuthSrv> AuthSrvPtr;
private:
typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
public:
QueryBenchMark(const int cache_slots, const char* const datasrc_file,
protected:
QueryBenchMark(const bool enable_cache,
const BenchQueries& queries, MessagePtr query_message,
OutputBufferPtr buffer) :
server_(new AuthSrv(cache_slots >= 0 ? true : false, xfrout_client)),
server_(new AuthSrv(enable_cache, xfrout_client)),
queries_(queries),
query_message_(query_message),
buffer_(buffer),
......@@ -78,13 +80,8 @@ public:
dummy_endpoint(IOEndpointPtr(IOEndpoint::create(IPPROTO_UDP,
IOAddress("192.0.2.1"),
5300)))
{
if (cache_slots >= 0) {
server_->setCacheSlots(cache_slots);
}
server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
string(datasrc_file) + "\"}"));
}
{}
public:
unsigned int run() {
BenchQueries::const_iterator query;
const BenchQueries::const_iterator query_end = queries_.end();
......@@ -93,14 +90,16 @@ public:
IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
*dummy_endpoint);
query_message_->clear(Message::PARSE);
buffer_->clear();
server_->processMessage(io_message, query_message_, buffer_,
&server);
&server);
}
return (queries_.size());
}
private:
protected:
AuthSrvPtr server_;
private:
const BenchQueries& queries_;
MessagePtr query_message_;
OutputBufferPtr buffer_;
......@@ -108,26 +107,92 @@ private:
IOEndpointPtr dummy_endpoint;
};
class Sqlite3QueryBenchMark : public QueryBenchMark {
public:
Sqlite3QueryBenchMark(const int cache_slots,
const char* const datasrc_file,
const BenchQueries& queries,
MessagePtr query_message,
OutputBufferPtr buffer) :
QueryBenchMark(cache_slots >= 0 ? true : false, queries,
query_message, buffer)
{
if (cache_slots >= 0) {
server_->setCacheSlots(cache_slots);
}
server_->updateConfig(Element::fromJSON("{\"database_file\": \"" +
string(datasrc_file) + "\"}"));
}
};
class MemoryQueryBenchMark : public QueryBenchMark {
public:
MemoryQueryBenchMark(const char* const zone_file,
const char* const zone_origin,
const BenchQueries& queries,
MessagePtr query_message,
OutputBufferPtr buffer) :
QueryBenchMark(false, queries, query_message, buffer)
{
configureAuthServer(*server_,
Element::fromJSON(
"{\"datasources\": "
" [{\"type\": \"memory\","
" \"zones\": [{\"origin\": \"" +
string(zone_origin) + "\","
" \"file\": \"" +
string(zone_file) + "\"}]}]}"));
}
};
void
printQPSResult(unsigned int iteration, double duration,
double iteration_per_second)
{
cout.precision(6);
cout << "Processed " << iteration << " queries in "
<< fixed << duration << "s";
cout.precision(2);
cout << " (" << fixed << iteration_per_second << "qps)" << endl;
}
}
namespace isc {
namespace bench {
template<>
void
BenchMark<QueryBenchMark>::printResult() const {
cout.precision(6);
cout << "Processed " << getIteration() << " queries in "
<< fixed << getDuration() << "s";
cout.precision(2);
cout << " (" << fixed << getIterationPerSecond() << "qps)" << endl;
BenchMark<Sqlite3QueryBenchMark>::printResult() const {
printQPSResult(getIteration(), getDuration(), getIterationPerSecond());
}