Commit b71bd81c authored by Dominik Charousset's avatar Dominik Charousset

Add customization point for injecting tracing data

parent b05a4b7b
......@@ -149,6 +149,8 @@ set(LIBCAF_CORE_SRCS
src/term.cpp
src/thread_hook.cpp
src/timestamp.cpp
src/tracing_data.cpp
src/tracing_data_factory.cpp
src/type_erased_tuple.cpp
src/type_erased_value.cpp
src/uniform_type_info_map.cpp
......
......@@ -588,6 +588,10 @@ public:
profiler_->before_sending_scheduled(self, timeout, element);
}
tracing_data_factory* tracing_context() const noexcept {
return tracing_context_;
}
/// @endcond
private:
......@@ -673,6 +677,9 @@ private:
/// Stores custom, system-wide key-value pairs.
runtime_settings_map settings_;
/// Stores the system-wide factory for deserializing tracing data.
tracing_data_factory* tracing_context_;
};
} // namespace caf
......@@ -306,6 +306,11 @@ public:
/// @note Has no effect unless building CAF with CAF_ENABLE_ACTOR_PROFILER.
actor_profiler* profiler = nullptr;
/// Enables CAF to deserialize application-specific tracing information.
/// @experimental
/// @note Has no effect unless building CAF with CAF_ENABLE_ACTOR_PROFILER.
tracing_data_factory* tracing_context = nullptr;
// -- run-time type information ----------------------------------------------
portable_name_map type_names_by_rtti;
......
......@@ -135,6 +135,8 @@ class scoped_actor;
class serializer;
class stream_manager;
class string_view;
class tracing_data;
class tracing_data_factory;
class type_erased_tuple;
class type_erased_value;
class uniform_type_info_map;
......@@ -301,7 +303,8 @@ using stream_manager_ptr = intrusive_ptr<stream_manager>;
// -- unique pointer aliases ---------------------------------------------------
using type_erased_value_ptr = std::unique_ptr<type_erased_value>;
using mailbox_element_ptr = std::unique_ptr<mailbox_element, detail::disposer>;
using tracing_data_ptr = std::unique_ptr<tracing_data>;
using type_erased_value_ptr = std::unique_ptr<type_erased_value>;
} // namespace caf
......@@ -19,25 +19,25 @@
#pragma once
#include <cstddef>
#include <memory>
#include "caf/actor_control_block.hpp"
#include "caf/config.hpp"
#include "caf/detail/disposer.hpp"
#include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_erased_tuple_view.hpp"
#include "caf/extend.hpp"
#include "caf/intrusive/singly_linked.hpp"
#include "caf/make_message.hpp"
#include "caf/memory_managed.hpp"
#include "caf/message.hpp"
#include "caf/message_id.hpp"
#include "caf/ref_counted.hpp"
#include "caf/make_message.hpp"
#include "caf/message_view.hpp"
#include "caf/memory_managed.hpp"
#include "caf/type_erased_tuple.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/intrusive/singly_linked.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/detail/disposer.hpp"
#include "caf/detail/tuple_vals.hpp"
#include "caf/detail/type_erased_tuple_view.hpp"
#include "caf/meta/type_name.hpp"
#include "caf/ref_counted.hpp"
#include "caf/tracing_data.hpp"
#include "caf/type_erased_tuple.hpp"
namespace caf {
......@@ -57,6 +57,13 @@ public:
/// if this is empty then the original sender receives the response.
forwarding_stack stages;
#ifdef CAF_ENABLE_ACTOR_PROFILER
/// Optional tracing information. This field is unused by default, but an
/// @ref actor_profiler can make use of it to inject application-specific
/// instrumentation.
tracing_data_ptr tracing_id;
#endif // CAF_ENABLE_ACTOR_PROFILER
mailbox_element();
mailbox_element(strong_actor_ptr&& x, message_id y,
......@@ -101,7 +108,11 @@ struct mailbox_category_corrector<upstream_msg> {
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, mailbox_element& x) {
return f(meta::type_name("mailbox_element"), x.sender, x.mid,
meta::omittable_if_empty(), x.stages, x.content());
meta::omittable_if_empty(), x.stages,
#ifdef CAF_ENABLE_ACTOR_PROFILER
x.tracing_id,
#endif // CAF_ENABLE_ACTOR_PROFILER
x.content());
}
/// Encapsulates arbitrary data in a message element.
......
......@@ -126,6 +126,8 @@ enum class sec : uint8_t {
unavailable_or_would_block,
/// Resolving a path on a remote node failed.
remote_lookup_failed,
/// Serialization failed because actor_system::tracing_context is null.
no_tracing_context,
};
/// @relates sec
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <memory>
#include "caf/fwd.hpp"
namespace caf {
/// Marker interface for application-specific tracing data. This interface
/// enables users to inject application-specific instrumentation into CAF's
/// messaging layer. CAF provides no default implementation for this
/// customization point.
class tracing_data {
public:
virtual ~tracing_data();
/// Writes the content of this object to `sink`.
virtual error serialize(serializer& sink) const = 0;
};
/// @relates tracing_data
using tracing_data_ptr = std::unique_ptr<tracing_data>;
/// @relates tracing_data
error inspect(serializer& sink, const tracing_data_ptr& x);
/// @relates tracing_data
error inspect(deserializer& source, tracing_data_ptr& x);
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/fwd.hpp"
namespace caf {
/// Creates instances of @ref tracing_data.
class tracing_data_factory {
public:
virtual ~tracing_data_factory();
/// Deserializes tracing data from `source` and either overrides the content
/// of `dst` or allocates a new object if `dst` is null.
/// @returns the result of `source`.
virtual error deserialize(deserializer& source,
std::unique_ptr<tracing_data>& dst) const = 0;
};
} // namespace caf
......@@ -226,7 +226,8 @@ actor_system::actor_system(actor_system_config& cfg)
await_actors_before_shutdown_(true),
detached_(0),
cfg_(cfg),
logger_dtor_done_(false) {
logger_dtor_done_(false),
tracing_context_(cfg.tracing_context) {
CAF_SET_LOGGER_SYS(this);
for (auto& hook : cfg.thread_hooks_)
hook->init(*this);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/tracing_data.hpp"
#include <cstdint>
#include "caf/actor_system.hpp"
#include "caf/deserializer.hpp"
#include "caf/error.hpp"
#include "caf/logger.hpp"
#include "caf/sec.hpp"
#include "caf/serializer.hpp"
#include "caf/tracing_data_factory.hpp"
namespace caf {
tracing_data::~tracing_data() {
// nop
}
error inspect(serializer& sink, const tracing_data_ptr& x) {
if (x == nullptr) {
uint8_t dummy = 0;
return sink(dummy);
}
uint8_t dummy = 1;
if (auto err = sink(dummy))
return err;
return x->serialize(sink);
}
error inspect(deserializer& source, tracing_data_ptr& x) {
uint8_t dummy = 0;
if (auto err = source(dummy))
return err;
if (dummy == 0) {
x.reset();
return none;
}
auto ctx = source.context();
if (ctx == nullptr)
return sec::no_context;
auto tc = ctx->system().tracing_context();
if (tc == nullptr)
return sec::no_tracing_context;
return tc->deserialize(source, x);
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/tracing_data_factory.hpp"
namespace caf {
tracing_data_factory::~tracing_data_factory() {
// nop
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE tracing_data
#include "caf/tracing_data.hpp"
#include "caf/test/dsl.hpp"
#include <vector>
#include "caf/actor_profiler.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/config.hpp"
#include "caf/serializer_impl.hpp"
#include "caf/tracing_data_factory.hpp"
#ifdef CAF_ENABLE_ACTOR_PROFILER
using std::string;
using namespace caf;
namespace {
class dummy_tracing_data : public tracing_data {
public:
string value;
dummy_tracing_data(string value) : value(std::move(value)) {
// nop
}
error serialize(serializer& sink) const override {
return sink(value);
}
};
class dummy_tracing_data_factory : public tracing_data_factory {
public:
error deserialize(deserializer& source,
std::unique_ptr<tracing_data>& dst) const override {
string value;
if (auto err = source(value))
return err;
dst.reset(new dummy_tracing_data(std::move(value)));
return none;
}
};
class dummy_profiler : public actor_profiler {
public:
void add_actor(const local_actor&, const local_actor*) override {
// nop
}
void remove_actor(const local_actor&) override {
// nop
}
void before_processing(const local_actor&, const mailbox_element&) override {
// nop
}
void after_processing(const local_actor&, invoke_message_result) override {
// nop
}
void before_sending(const local_actor& self,
mailbox_element& element) override {
element.tracing_id.reset(new dummy_tracing_data(self.name()));
}
void before_sending_scheduled(const local_actor& self,
actor_clock::time_point,
mailbox_element& element) override {
element.tracing_id.reset(new dummy_tracing_data(self.name()));
}
};
actor_system_config& init(actor_system_config& cfg, actor_profiler& profiler,
tracing_data_factory& factory) {
test_coordinator_fixture<>::init_config(cfg);
cfg.profiler = &profiler;
cfg.tracing_context = &factory;
return cfg;
}
struct fixture {
using scheduler_type = caf::scheduler::test_coordinator;
fixture()
: sys(init(cfg, profiler, factory)),
sched(dynamic_cast<scheduler_type&>(sys.scheduler())) {
run();
}
void run() {
sched.run();
}
dummy_profiler profiler;
dummy_tracing_data_factory factory;
actor_system_config cfg;
actor_system sys;
scheduler_type& sched;
};
const std::string& tracing_id(local_actor* self) {
auto element = self->current_mailbox_element();
if (element == nullptr)
CAF_FAIL("current_mailbox_element == null");
auto tid = element->tracing_id.get();
if (tid == nullptr)
CAF_FAIL("tracing_id == null");
auto dummy_tid = dynamic_cast<dummy_tracing_data*>(tid);
if (dummy_tid == nullptr)
CAF_FAIL("dummy_tracing_id == null");
return dummy_tid->value;
}
# define NAMED_ACTOR_STATE(type) \
struct type##_state { \
const char* name = #type; \
}
NAMED_ACTOR_STATE(alice);
NAMED_ACTOR_STATE(bob);
NAMED_ACTOR_STATE(carl);
} // namespace
CAF_TEST_FIXTURE_SCOPE(actor_profiler_tests, fixture)
CAF_TEST(profilers inject tracing data into asynchronous messages) {
CAF_MESSAGE("spawn a foo and a bar");
auto carl_fun = [](stateful_actor<carl_state>* self) -> behavior {
return {
[=](const string& str) {
CAF_CHECK_EQUAL(str, "hello carl");
CAF_CHECK_EQUAL(tracing_id(self), "bob");
},
};
};
auto bob_fun = [](stateful_actor<bob_state>* self, actor carl) -> behavior {
return {
[=](const string& str) {
CAF_CHECK_EQUAL(str, "hello bob");
CAF_CHECK_EQUAL(tracing_id(self), "alice");
self->send(carl, "hello carl");
},
};
};
auto alice_fun = [](stateful_actor<alice_state>* self, actor bob) {
self->send(bob, "hello bob");
};
sys.spawn(alice_fun, sys.spawn(bob_fun, sys.spawn(carl_fun)));
run();
}
CAF_TEST(tracing data is serializable) {
std::vector<byte> buf;
serializer_impl<std::vector<byte>> sink{sys, buf};
tracing_data_ptr data;
tracing_data_ptr copy;
data.reset(new dummy_tracing_data("iTrace"));
CAF_CHECK_EQUAL(inspect(sink, data), none);
binary_deserializer source{sys, buf};
CAF_CHECK_EQUAL(inspect(source, copy), none);
CAF_REQUIRE_NOT_EQUAL(copy.get(), nullptr);
CAF_CHECK_EQUAL(dynamic_cast<dummy_tracing_data&>(*copy).value, "iTrace");
}
CAF_TEST_FIXTURE_SCOPE_END()
#endif // CAF_ENABLE_ACTOR_PROFILER
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment