Commit 600e6b77 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3804] Created Stopwatch class handling code execution time.

parent 18590933
......@@ -22,6 +22,8 @@ libkea_util_la_SOURCES += pid_file.h pid_file.cc
libkea_util_la_SOURCES += process_spawn.h process_spawn.cc
libkea_util_la_SOURCES += range_utilities.h
libkea_util_la_SOURCES += signal_set.cc signal_set.h
libkea_util_la_SOURCES += stopwatch.cc stopwatch.h
libkea_util_la_SOURCES += stopwatch_impl.cc stopwatch_impl.h
libkea_util_la_SOURCES += encode/base16_from_binary.h
libkea_util_la_SOURCES += encode/base32hex.h encode/base64.h
libkea_util_la_SOURCES += encode/base32hex_from_binary.h
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/stopwatch.h>
#include <util/stopwatch_impl.h>
namespace isc {
namespace util {
using namespace boost::posix_time;
Stopwatch::Stopwatch(const bool autostart)
: impl_(new StopwatchImpl()) {
// If the autostart has been specified, invoke start.
if (autostart) {
start();
}
}
void
Stopwatch::start() {
impl_->start();
}
void
Stopwatch::stop() {
impl_->stop();
}
void
Stopwatch::reset() {
impl_->reset();
}
boost::posix_time::time_duration
Stopwatch::getLastDuration() const {
return (impl_->getLastDuration());
}
boost::posix_time::time_duration
Stopwatch::getTotalDuration() const {
return (impl_->getTotalDuration());
}
long
Stopwatch::getMilliseconds() const {
return (getLastDuration().total_milliseconds());
}
long
Stopwatch::getTotalMilliseconds() const {
return (getTotalDuration().total_milliseconds());
}
long
Stopwatch::getMicroseconds() const {
return (getLastDuration().total_microseconds());
}
long
Stopwatch::getTotalMicroseconds() const {
return (getTotalDuration().total_microseconds());
}
} // end of isc::util
} // end of isc
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef STOPWATCH_H
#define STOPWATCH_H
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>
namespace isc {
namespace util {
/// @brief Forward declaration to the @c Stopwatch implementation.
class StopwatchImpl;
/// @brief Utility class to measure code execution times.
///
/// The API of this class is based on the use cases of a stopwatch. It is
/// used to measure time spent executing portions of the code. The typical
/// use case for the @c Stopwatch is to measure the time spent invoking
/// callouts in hooks library. This provides means for diagnosing the
/// server's performance degradations when hooks libraries are in use.
///
/// This class exposes functions like @c start, @c stop and @c reset which
/// behave in the same way as a stopwatch used to measure time for sport
/// activities.
///
/// It is possible to measure the cumulative execution time by invoking
/// @c start and @c stop consecutively. The total measured time will be
/// a sum of durations between the invocations of respective starts and
/// stops.
class Stopwatch {
public:
/// @brief Constructor.
///
/// @param autostart Indicates if the stopwatch should be initialized to
/// the "started" state. In this state the stopwatch is measuring the time
/// since it has been started (object has been constructed in this case.
/// If the parameter is set to false (default value), the
/// @c Stopwatch::start must be called to start time measurement.
Stopwatch(const bool autostart = true);
/// @brief Starts the stopwatch.
///
/// Sets the stopwatch to the "started" state. In this state the stopwatch
/// is measuring the duration since @c Stopwatch::start has been invoked.
///
//// This method is no-op if the stopwatch is already in the "stopped"
/// state.
void start();
/// @brief Stops the stopwatch.
///
/// Sets the stopwatch to the "stopped" state. The stopwatch stops the time
/// measurement and records the duration between the last stopwatch start
/// and the stop invocation. It also updates the total measured duration,
/// i.e. the sum of durations between all start/stop invocations. Both
/// values can be retrieved using @c Stopwatch::getLastDuration and
/// @c Stopwatch::getTotalDuration respectively, or their variants.
///
/// This method is no-op if the stopwatch is already in the "stopped" state.
void stop();
/// @brief Resets the stopwatch.
///
/// It resets the stopwatch to the initial state. In this state, the last
/// measured duration and the total duration is set to 0. The stopwatch
/// is set to the "stopped" state.
void reset();
/// @brief Retrieves last measured duration.
///
/// If the stopwatch is in the "stopped" state this method retrieves the
/// duration between the last start and stop. If the stopwatch is in the
/// "started" state, the retrieved duration is the duration between the
/// last start of the stopwatch and the current time.
boost::posix_time::time_duration getLastDuration() const;
/// @brief Retrieves total measured duration.
///
/// If the stopwatch is in the "stopped" state this method retrieves the
/// total duration between all starts and stops invoked during the
/// lifetime of the object or since the last reset. If the stopwatch is
/// in the "started" state, the returned is the sum of all durations
/// between respective starts and stops, and the duration since the
/// stopwatch has been last started and the current time.
boost::posix_time::time_duration getTotalDuration() const;
/// @brief Retrieves the last measured duration in milliseconds.
long getMilliseconds() const;
/// @brief Retrieves the total measured duration in milliseconds.
long getTotalMilliseconds() const;
/// @brief Retrieves the last measured duration in microseconds.
long getMicroseconds() const;
/// @brief Retrieves the total measured duration in microseconds.
long getTotalMicroseconds() const;
private:
/// @brief Pointer to the @c StopwatchImpl.
boost::scoped_ptr<StopwatchImpl> impl_;
};
}
}
#endif // STOPWATCH_H
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/stopwatch_impl.h>
namespace isc {
namespace util {
using namespace boost::posix_time;
StopwatchImpl::StopwatchImpl()
: started_(false),
last_start_(getCurrentTime()),
last_stop_(last_start_),
cumulative_time_(microseconds(0)) {
}
StopwatchImpl::~StopwatchImpl() {
}
void
StopwatchImpl::start() {
// If stopwatch is "stopped", start it.
if (!started_) {
last_start_ = getCurrentTime();
started_ = true;
}
}
void
StopwatchImpl::stop() {
// Is stopwatch is "started", stop it.
if (started_) {
last_stop_ = getCurrentTime();
// Update the total time with the last measured duration.
cumulative_time_ += last_stop_ - last_start_;
started_ = false;
}
}
void
StopwatchImpl::reset() {
// Set last start and stop values to the current time. This is the
// same as in the constructor. As a result the last duration will
// be 0.
last_start_ = getCurrentTime();
last_stop_ = last_start_;
// Set the total duration to 0.
cumulative_time_ = microseconds(0);
started_ = false;
}
time_duration
StopwatchImpl::getLastDuration() const {
// If the stopwatch is started, the time measured is between the
// start time and the current time. Otherwise, it is between the
// start time and last stop.
ptime end_time = started_ ? getCurrentTime() : last_stop_;
return (end_time - last_start_);
}
time_duration
StopwatchImpl::getTotalDuration() const {
// Get the total time recorded so far.
time_duration total_duration = cumulative_time_;
if (started_) {
// If the stopwatch is started, add the duration between the
// start time and current time.
total_duration += (getCurrentTime() - last_start_);
}
return (total_duration);
}
ptime
StopwatchImpl::getCurrentTime() const {
return (microsec_clock::universal_time());
}
} // end of isc::util
} // end of isc
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef STOPWATCH_IMPL_H
#define STOPWATCH_IMPL_H
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>
namespace isc {
namespace util {
/// @brief @c Stopwatch class implementation.
///
/// The @c Stopwatch class uses the plimpl idiom to make it easier to unit
/// test behavior of the @c Stopwatch class without a need to rely on the system
/// clock. The @c StopwatchImpl API allows for overriding the @c getCurrentTime
/// method to return the arbitrary time value as current time to various
/// methods. By setting the current time to arbitrary values the test can expect
/// arbitrary values being returned by the class methods.
///
/// Also, by using the pimpl idiom the @c Stopwatch class hides its implementation
/// details and leaves the header with only the pointer to the @c StopwatchImpl
/// class.
class StopwatchImpl {
public:
/// @brief Constructor.
///
/// Initializes the internally used timestamps. It also sets the state of
/// the stopwatch to "stopped".
StopwatchImpl();
/// @brief Virtual destructor.
///
/// This destructor is virtual because the @c StopwatchImpl::getCurrentTime
/// is virtual.
virtual ~StopwatchImpl();
/// @brief Starts the stopwatch.
///
/// Sets the stopwatch to the "started" state. It also records the time when
/// the stopwatch is started. This method is no-op if the stopwatch is
/// already in the "started" state.
///
/// Also see the @c Stopwatch::start for details.
void start();
/// @brief Stop the stopwatch.
///
/// Sets the stopwatch to the "stopped" state. The stop time is recorded and
/// the cumulative time is updated to include the duration between the most
/// recent start and stop. This method is no-op if the stopwatch is already
/// in the "stopped" state.
///
/// Also see the @c Stopwatch::stop for details.
void stop();
/// @brief Reset the stopwatch.
///
/// Also see the @c Stopwatch::reset for details.
void reset();
/// @brief Retrieves the measured duration.
///
/// Also see the @c Stopwatch::getLastDuration for details.
boost::posix_time::time_duration getLastDuration() const;
/// @brief Retrieves the total measured duration.
///
/// Also see the @c Stopwatch::getTotalDuration for details.
boost::posix_time::time_duration getTotalDuration() const;
protected:
/// @brief Returns the current time.
///
/// This method is used internally by the @c StopwatchImpl class and
/// its derivations. This class simply returns the value of
/// @c boost::posix_time::micrisec_clock::univeral_time(), which is
/// a current timestamp. The derivations may replace it with the
/// custom implementations. The typical use case is for the unit tests
/// to customize the behavior of this function to return well known
/// (deterministic) values. As a result, it is possible to influence
/// the "measured" values returned by accessors of this class, which
/// can be compared against some exact values.
virtual boost::posix_time::ptime getCurrentTime() const;
private:
/// @brief Holds the state of the stopwatch.
bool started_;
/// @brief Holds the timestamp when the stopwatch has been last started.
boost::posix_time::ptime last_start_;
/// @brief Holds the timestamp when the stopwatch has been last stopped.
boost::posix_time::ptime last_stop_;
/// @brief Holds the total measured time since the stopwatch has been
/// first started after creation or reset.
boost::posix_time::time_duration cumulative_time_;
};
}
}
#endif // STOPWATCH_H
......@@ -49,6 +49,7 @@ run_unittests_SOURCES += strutil_unittest.cc
run_unittests_SOURCES += time_utilities_unittest.cc
run_unittests_SOURCES += range_utilities_unittest.cc
run_unittests_SOURCES += signal_set_unittest.cc
run_unittests_SOURCES += stopwatch_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
......
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/stopwatch.h>
#include <util/stopwatch_impl.h>
#include <gtest/gtest.h>
#include <unistd.h>
namespace {
using namespace isc;
using namespace isc::util;
using namespace boost::posix_time;
/// @brief @c StopwatchImpl mock object.
///
/// This class derives from the @c StopwatchImpl to override the
/// @c StopwatchImpl::getCurrentTime. This method is internally called by
/// the @c StopwatchImpl to determine the current time. By providing the
/// implementation of this method which returns the fixed (well known)
/// timestamp value we can obtain the deterministic values from the accessors
/// of this class.
///
/// This class also includes some convenience methods to return the time
/// durations in milliseconds.
class StopwatchMock : public StopwatchImpl {
public:
/// @brief Constructor.
///
/// @param ref_time Reference time, i.e. the arbitrary time value from
/// which time is measured. The @c current_time_ value returned by the
/// @c StopwatchMock::getCurrentTime is initialized to this value.
/// Subsequent calls to the @c StopwatchMock::ffwd move the value of
/// the @c current_time_ forward.
StopwatchMock(const ptime& ref_time);
/// @brief Fast forward time.
///
/// Moves the value of the @c current_time_ forward by the specified
/// number of milliseconds. As a result the timestamp returned by the
/// @c StopwatchMock::getCurrentTime moves by this value. This simulates
/// the time progress.
///
/// @param ms Specifies the number of milliseconds to move current time.
void ffwd(const uint32_t ms);
/// @brief Returns the last duration in milliseconds.
uint32_t getLastDurationInMs() const;
/// @brief Returns the total duration in milliseconds.
uint32_t getTotalDurationInMs() const;
protected:
/// @brief Returs the current time.
///
/// This method returns the fixed @c current_time_ timestamp.
virtual ptime getCurrentTime() const;
private:
/// @brief Holds the current time to be returned by the
/// @c StopwatchMock::getCurrentTime.
ptime current_time_;
};
StopwatchMock::StopwatchMock(const ptime& ref_time)
: StopwatchImpl(), current_time_(ref_time) {
}
void
StopwatchMock::ffwd(const uint32_t ms) {
current_time_ += milliseconds(ms);
}
uint32_t
StopwatchMock::getLastDurationInMs() const {
return (getLastDuration().total_milliseconds());
}
uint32_t
StopwatchMock::getTotalDurationInMs() const {
return (getTotalDuration().total_milliseconds());
}
ptime
StopwatchMock::getCurrentTime() const {
return (current_time_);
}
/// @brief Test fixture class for testing @c StopwatchImpl.
class StopwatchTest : public ::testing::Test {
protected:
/// @brief Set up the test.
///
/// Initializes the reference time to be used to create the instances
/// of the @c StopwatchMock objects.
virtual void SetUp();
/// @brief Holds the reference time to be used to create the instances
/// of the @c StopwatchMock objects.
ptime ref_time_;
};
void
StopwatchTest::SetUp() {
ref_time_ = microsec_clock::universal_time();
}
/// This test checks the behavior of the stopwatch when it is started
/// and stopped multiple times. It uses the StopwatchMock object to
/// control the "time flow" by setting the current time to arbitrary
/// values using the StopwatchMock::ffwd. In addition, this test
/// checks that the stopwatch can be reset.
TEST_F(StopwatchTest, multipleMeasurements) {
StopwatchMock stopwatch(ref_time_);
// The stopwatch shouldn't automatically start. The initial
// durations should be set to 0.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
stopwatch.start();
// Even though the stopwatch is started, the time is still set to
// the initial value. The durations should not be affected.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(0, stopwatch.getTotalDurationInMs());
// Move the time by 10 ms.
stopwatch.ffwd(10);
// It should be possible to retrieve the durations even when the
// stopwatch is running.
EXPECT_EQ(10, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Now stop it and make sure that the same values are returned.
stopwatch.stop();
EXPECT_EQ(10, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Start it again, but don't move the time forward yet.
stopwatch.start();
// The new duration should be 0, but the total should be equal to
// the previously measured duration.
EXPECT_EQ(0, stopwatch.getLastDurationInMs());
EXPECT_EQ(10, stopwatch.getTotalDurationInMs());
// Move time by 5 ms.
stopwatch.ffwd(5);
// New measured duration should be 5 ms. The total should be 15 ms.
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Stop it again and make sure the values returned are the same.
stopwatch.stop();
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Move the time forward while the stopwatch is stopped.
stopwatch.ffwd(8);
// The measured values should not be affected.
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Stop should be no-op in this case.
stopwatch.stop();
EXPECT_EQ(5, stopwatch.getLastDurationInMs());
EXPECT_EQ(15, stopwatch.getTotalDurationInMs());
// Start the stopwatch again.
stopwatch.start();
// Move time by 3 ms.