#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
-// Taken from http://www.boost.org/doc/libs/1_55_0/libs/circular_buffer/example/circular_buffer_bound_example.cpp
+/**
+ Blocking, fixed-capacity, thread-safe FIFO queue useful for communicating between threads.
+ This code was taken from the Boost docs: http://www.boost.org/doc/libs/1_55_0/libs/circular_buffer/example/circular_buffer_bound_example.cpp
+ */
template <class T>
class BoundedBuffer {
public:
explicit BoundedBuffer(size_type capacity) : m_unread(0), m_container(capacity) {
}
+ /**
+ Inserts an element into the queue.
+ Blocks if the queue is full.
+ */
void push_front(typename boost::call_traits<value_type>::param_type item) {
// `param_type` represents the "best" way to pass a parameter of type `value_type` to a method.
boost::mutex::scoped_lock lock(m_mutex);
m_not_empty.notify_one();
}
+ /**
+ Removes an element from the queue.
+ Blocks if the queue is empty.
+ */
void pop_back(value_type* pItem) {
boost::mutex::scoped_lock lock(m_mutex);
m_not_empty.wait(lock, boost::bind(&BoundedBuffer<value_type>::is_not_empty, this));
namespace rbd_replay {
+/**
+ Helper for deserializing data in an architecture-indepdendent way.
+ Everything is read big-endian.
+ @see Ser
+*/
class Deser {
public:
Deser(std::istream &in);
namespace rbd_replay {
+/**
+ Maps image names.
+ */
class ImageNameMap {
public:
typedef std::pair<rbd_loc, rbd_loc> Mapping;
+ /**
+ Parses a mapping.
+ If parsing fails, the contents of \c mapping are undefined.
+ @param[in] mapping_string string representation of the mapping
+ @param[out] mapping stores the parsed mapping
+ @retval true parsing was successful
+ */
bool parse_mapping(std::string mapping_string, Mapping *mapping) const;
void add_mapping(const Mapping& mapping);
+ /**
+ Maps an image name.
+ If no mapping matches the name, it is returned unmodified.
+ */
rbd_loc map(const rbd_loc& name) const;
private:
using namespace rbd_replay;
extern "C"
-void pending_io_callback(librbd::completion_t cb, void *arg) {
+void rbd_replay_pending_io_callback(librbd::completion_t cb, void *arg) {
PendingIO *io = static_cast<PendingIO*>(arg);
io->completed(cb);
}
PendingIO::PendingIO(action_id_t id,
ActionCtx &worker)
: m_id(id),
- m_completion(new librbd::RBD::AioCompletion(this, pending_io_callback)),
+ m_completion(new librbd::RBD::AioCompletion(this, rbd_replay_pending_io_callback)),
m_worker(worker) {
}
#include <boost/enable_shared_from_this.hpp>
#include "actions.hpp"
+/// Do not call outside of rbd_replay::PendingIO.
+extern "C"
+void rbd_replay_pending_io_callback(librbd::completion_t cb, void *arg);
+
namespace rbd_replay {
+/**
+ A PendingIO is an I/O operation that has been started but not completed.
+*/
class PendingIO : public boost::enable_shared_from_this<PendingIO> {
public:
typedef boost::shared_ptr<PendingIO> ptr;
~PendingIO();
- void completed(librbd::completion_t cb);
-
action_id_t id() const {
return m_id;
}
}
private:
+ void completed(librbd::completion_t cb);
+
+ friend void ::rbd_replay_pending_io_callback(librbd::completion_t cb, void *arg);
+
const action_id_t m_id;
ceph::bufferlist m_bl;
librbd::RBD::AioCompletion *m_completion;
class Replayer;
+/**
+ Performs Actions within a single thread.
+ */
class Worker : public ActionCtx {
public:
explicit Worker(Replayer &replayer);
void start();
- // Should only be called by StopThreadAction
+ /// Should only be called by StopThreadAction
void stop();
void join();
private:
struct action_tracker_d {
- // Maps an action ID to the time the action completed
+ /// Maps an action ID to the time the action completed
std::map<action_id_t, boost::system_time> actions;
boost::shared_mutex mutex;
boost::condition condition;
action_tracker_d &tracker_for(action_id_t id);
- // Disallow assignment and copying
+ /// Disallow copying
Replayer(const Replayer& rhs);
+ /// Disallow assignment
const Replayer& operator=(const Replayer& rhs);
librbd::RBD* m_rbd;
std::map<imagectx_id_t, librbd::Image*> m_images;
boost::shared_mutex m_images_mutex;
- // Actions are hashed across the trackers by ID.
- // Number of trackers should probably be larger than the number of cores and prime.
- // Should definitely be odd.
+ /// Actions are hashed across the trackers by ID.
+ /// Number of trackers should probably be larger than the number of cores and prime.
+ /// Should definitely be odd.
const int m_num_action_trackers;
action_tracker_d* m_action_trackers;
};
namespace rbd_replay {
+/**
+ Helper for serializing data in an architecture-indepdendent way.
+ Everything is written big-endian.
+ @see Deser
+*/
class Ser {
public:
Ser(std::ostream &out);
#include "Deser.hpp"
#include "rbd_loc.hpp"
+// Stupid Doxygen requires this or else the typedef docs don't appear anywhere.
+/// @file rbd_replay/actions.hpp
+
namespace rbd_replay {
typedef uint64_t imagectx_id_t;
typedef uint64_t thread_id_t;
-// Even IDs are normal actions, odd IDs are completions
+/// Even IDs are normal actions, odd IDs are completions.
typedef uint32_t action_id_t;
+/**
+ Dependencies link actions to earlier actions or completions.
+ If an action has a dependency \c d then it waits until \c d.time_delta nanoseconds after the action or completion with ID \c d.id has fired.
+*/
struct dependency_d {
+ /// ID of the action or completion to wait for.
action_id_t id;
+ /// Nanoseconds of delay to wait until after the action or completion fires.
uint64_t time_delta;
+ /**
+ @param id ID of the action or completion to wait for.
+ @param time_delta Nanoseconds of delay to wait after the action or completion fires.
+ */
dependency_d(action_id_t id,
uint64_t time_delta)
: id(id),
class PendingIO;
+/**
+ %Context through which an Action interacts with its environment.
+ */
class ActionCtx {
public:
virtual ~ActionCtx() {
}
+ /**
+ Returns the image with the given ID.
+ The image must have been previously tracked with put_image(imagectx_id_t,librbd::Image*).
+ */
virtual librbd::Image* get_image(imagectx_id_t imagectx_id) = 0;
+ /**
+ Tracks an image.
+ put_image(imagectx_id_t,librbd::Image*) must not have been called previously with the same ID,
+ and the image must not be NULL.
+ */
virtual void put_image(imagectx_id_t imagectx_id, librbd::Image* image) = 0;
+ /**
+ Stops tracking an Image and release it.
+ This deletes the C++ object, not the image itself.
+ The image must have been previously tracked with put_image(imagectx_id_t,librbd::Image*).
+ */
virtual void erase_image(imagectx_id_t imagectx_id) = 0;
virtual librbd::RBD* rbd() = 0;
virtual void stop() = 0;
+ /**
+ Maps an image name from the name in the original trace to the name that should be used when replaying.
+ @param image_name name of the image in the original trace
+ @param snap_name name of the snap in the orginal trace
+ @return image name to replay against
+ */
virtual rbd_loc map_image_name(std::string image_name, std::string snap_name) const = 0;
};
+/**
+ Performs an %IO or a maintenance action such as starting or stopping a thread.
+ Actions are read from a replay file and scheduled by Replayer.
+ Corresponds to the IO class, except that Actions are executed by rbd-replay,
+ and IOs are used by rbd-replay-prep for processing the raw trace.
+ */
class Action {
public:
typedef boost::shared_ptr<Action> ptr;
virtual void perform(ActionCtx &ctx) = 0;
+ /// Returns the ID of the completion corresponding to this action.
action_id_t pending_io_id() {
return m_id + 1;
}
return m_predecessors;
}
+ /// Reads and constructs an action from the replay file.
static ptr read_from(Deser &d);
protected:
const std::vector<dependency_d> m_predecessors;
};
+/// Writes human-readable debug information about the action to the stream.
+/// @related Action
std::ostream& operator<<(std::ostream& o, const Action& a);
+/**
+ Placeholder for partially-constructed actions.
+ Does nothing, and does not appear in the replay file.
+ */
class DummyAction : public Action {
public:
DummyAction(action_id_t id,
std::ostream& dump(std::ostream& o) const;
};
+
class StopThreadAction : public Action {
public:
explicit StopThreadAction(Action &src);
typedef std::map<action_id_t, boost::shared_ptr<IO> > io_map_t;
+/**
+ Calculates reachability of IOs in the dependency graph.
+ All IOs in \c deps which are not transitive dependencies of anything in \c base
+ is added to \c unreachable.
+ In other words, for every IO \c x in \c deps: if nothing in \c base depends on \c x,
+ and nothing in \c base has dependencies that depend on \c x, etc.,
+ then \c x is added to \c unreachable.
+ Note that \c unreachable is \em not cleared, so the same set can be used across multiple
+ calls to collect dependencies.
+ @param[in] deps IOs to search for
+ @param[in] base root set of IOs to search from
+ @param[out] unreachable collects unreachable IOs
+ @related IO
+*/
void batch_unreachable_from(const io_set_t& deps, const io_set_t& base, io_set_t* unreachable);
+
+/**
+ Used by rbd-replay-prep for processing the raw trace.
+ Corresponds to the Action class, except that Actions are executed by rbd-replay,
+ and IOs are used by rbd-replay-prep for processing the raw trace.
+ */
class IO : public boost::enable_shared_from_this<IO> {
public:
typedef boost::shared_ptr<IO> ptr;
typedef boost::weak_ptr<IO> weak_ptr;
+ /**
+ @param ionum ID of this %IO
+ @param start_time time the %IO started, in nanoseconds
+ @param thread_id ID of the thread that issued the %IO
+ @param prev previously issued %IO on the same thread. NULL for the first %IO on a thread.
+ */
IO(action_id_t ionum,
uint64_t start_time,
thread_id_t thread_id,
void add_dependencies(const io_set_t& deps);
+ /**
+ Returns the completion's number of successors, or 0 if the %IO does not have a completion.
+ */
uint64_t num_completion_successors() const {
ptr c(m_completion.lock());
return c ? c->m_num_successors : 0;
}
- void write_to(Ser& out, io_type iotype) const;
-
virtual void write_to(Ser& out) const = 0;
virtual bool is_completion() const {
return m_num_successors;
}
- void write_debug_base(std::ostream& out, std::string iotype) const;
-
virtual void write_debug(std::ostream& out) const = 0;
- // The result must be stored somewhere, or else m_completion will expire
+ /**
+ Creates the completion for this IO.
+ This may only be called once per IO, and may not be called on completion IOs.
+ The completion must be stored, or else m_completion will expire.
+ */
ptr create_completion(uint64_t start_time, thread_id_t thread_id);
+protected:
+ void write_to(Ser& out, io_type iotype) const;
+
+ void write_debug_base(std::ostream& out, std::string iotype) const;
+
private:
action_id_t m_ionum;
uint64_t m_start_time;
ptr m_prev;
};
+/// Used for dumping debug info.
+/// @related IO
std::ostream& operator<<(std::ostream& out, IO::ptr io);
}
};
+/// @related IO
bool compare_io_ptrs_by_start_time(IO::ptr p1, IO::ptr p2);
}
namespace rbd_replay {
+/**
+ Stores a pool, image name, and snap name triple.
+ rbd_locs can be converted to/from strings with the format pool/image\@snap.
+ The slash and at signs can be omitted if the pool and snap are empty, respectively.
+ Backslashes can be used to escape slashes and at signs in names.
+ Examples:
+
+ |Pool | Image | Snap | String |
+ |------|-------|------|--------------------|
+ |rbd | vm | 1 | rbd/vm\@1 |
+ |rbd | vm | | rbd/vm |
+ | | vm | 1 | vm\@1 |
+ | | vm | | vm |
+ |rbd | | 1 | rbd/\@1 |
+ |rbd\@x| vm/y | 1 | rbd\\\@x/vm\\/y\@1 |
+
+ (The empty string should obviously be avoided as the image name.)
+
+ Note that the non-canonical forms /vm\@1 and rbd/vm\@ can also be parsed,
+ although they will be formatted as vm\@1 and rbd/vm.
+ */
struct rbd_loc {
+ /**
+ Constructs an rbd_loc with the empty string for the pool, image, and snap.
+ */
rbd_loc();
+ /**
+ Constructs an rbd_loc with the given pool, image, and snap.
+ */
rbd_loc(std::string pool, std::string image, std::string snap);
+ /**
+ Parses an rbd_loc from the given string.
+ If parsing fails, the contents are unmodified.
+ @retval true if parsing succeeded
+ */
bool parse(std::string name_string);
+ /**
+ Returns the string representation of the locator.
+ */
std::string str() const;
+ /**
+ Compares the locators lexicographically by pool, then image, then snap.
+ */
int compare(const rbd_loc& rhs) const;
+ /**
+ Returns true if the locators have identical pool, image, and snap.
+ */
bool operator==(const rbd_loc& rhs) const;
+ /**
+ Compares the locators lexicographically by pool, then image, then snap.
+ */
bool operator<(const rbd_loc& rhs) const;
std::string pool;