diff --git a/src/shammodels/ramses/src/Solver.cpp b/src/shammodels/ramses/src/Solver.cpp index 9c43b8f885..cee99ba3cc 100644 --- a/src/shammodels/ramses/src/Solver.cpp +++ b/src/shammodels/ramses/src/Solver.cpp @@ -269,9 +269,155 @@ class PatchDataLayerToVtk : public shamrock::solvergraph::INode { std::string _impl_get_tex() { return "TODO"; } }; +class GraphTracer { + public: + std::string filename; + + std::queue events; + + public: + inline void add_event(std::string event) { events.push(event); } + + inline void maybe_flush() { + + // open the file for writing + std::ofstream file(filename, std::ios::app); + + // lock the file with flock + + // flush the events + while (!events.empty()) { + std::string event = events.front(); + events.pop(); + file << event << std::endl; + } + + // close the file + file.close(); + } +}; + +GraphTracer graph_tracer("graph_trace.txt"); + +void on_node_create(u64 uuid) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_create"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); + graph_tracer.maybe_flush(); +} + +void on_node_destroy(u64 uuid) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_destroy"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); + graph_tracer.maybe_flush(); +} + +void on_edge_create(u64 uuid) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "edge_create"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); +} + +void on_edge_destroy(u64 uuid) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "edge_destroy"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); +} + +void on_node_update(shamrock::solvergraph::INode &node) { + + nlohmann::json edge_list = nlohmann::json::array(); + + node.on_edge_ro_edges([&](shamrock::solvergraph::IEdge &e) { + edge_list.push_back( + nlohmann::json{{"type", "read"}, {"label", e.get_label()}, {"uuid", e.get_uuid()}}); + }); + + node.on_edge_rw_edges([&](shamrock::solvergraph::IEdge &e) { + edge_list.push_back( + nlohmann::json{ + {"type", "read_write"}, {"label", e.get_label()}, {"uuid", e.get_uuid()}}); + }); + + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_update"}, + {"uuid", node.get_uuid()}, + {"type", typeid(node).name()}, + {"label", node.get_label()}, + {"wtime", shambase::details::get_wtime()}, + {"edges", edge_list}} + .dump()); + graph_tracer.maybe_flush(); +} + +void on_node_op(u64 uuid, u64 op_id) { + if (op_id == 0) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_evaluate_begin"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); + } else if (op_id == 1) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_evaluate_end"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); + } else { + shambase::throw_with_loc(shambase::format("Invalid op_id: {}", op_id)); + } + graph_tracer.maybe_flush(); +} + +void on_node_evaluate_end(u64 uuid) { + graph_tracer.add_event( + nlohmann::json{ + {"event", "node_evaluate_end"}, + {"uuid", uuid}, + {"wtime", shambase::details::get_wtime()}, + } + .dump()); + graph_tracer.maybe_flush(); +} + template void shammodels::basegodunov::Solver::init_solver_graph() { + shamrock::solvergraph::LifetimeTracker::on_create + = on_node_create; + shamrock::solvergraph::LifetimeTracker::on_destroy + = on_node_destroy; + shamrock::solvergraph::LifetimeTracker::on_state_update + = on_node_update; + shamrock::solvergraph::LifetimeTracker::on_op = on_node_op; + + shamrock::solvergraph::LifetimeTracker::on_create + = on_edge_create; + shamrock::solvergraph::LifetimeTracker::on_destroy + = on_edge_destroy; + bool enable_mem_free = false; auto get_optional_free_mem = [&](auto &bind_to, auto &add_to) { diff --git a/src/shamrock/include/shamrock/solvergraph/IEdge.hpp b/src/shamrock/include/shamrock/solvergraph/IEdge.hpp index 4ace79c490..efddfbfe95 100644 --- a/src/shamrock/include/shamrock/solvergraph/IEdge.hpp +++ b/src/shamrock/include/shamrock/solvergraph/IEdge.hpp @@ -18,14 +18,23 @@ #include "shambase/WithUUID.hpp" #include "shambase/aliases_int.hpp" +#include "shambase/memory.hpp" #include "shamrock/solvergraph/IFreeable.hpp" +#include "shamrock/solvergraph/LifetimeTracker.hpp" #include namespace shamrock::solvergraph { class INode; - class IEdge : public shambase::WithUUID, public IFreeable { + class IEdge : public IFreeable { + + /// track the UUID of the node to notify changes. + /// Sadly we can not use a WithUUID directly since it is hard to log destruction in the + /// destructor if the object was moved which could lead to double free notifications. + std::shared_ptr> tracker + = std::make_shared>(); + public: inline std::string get_label() const { return _impl_get_dot_label(); } inline std::string get_tex_symbol() const { return _impl_get_tex_symbol(); } @@ -33,6 +42,9 @@ namespace shamrock::solvergraph { virtual std::string _impl_get_dot_label() const = 0; virtual std::string _impl_get_tex_symbol() const = 0; + /// Get the UUID of the node + inline u64 get_uuid() const { return shambase::get_check_ref(tracker).get_uuid(); } + inline virtual ~IEdge() {} }; diff --git a/src/shamrock/include/shamrock/solvergraph/INode.hpp b/src/shamrock/include/shamrock/solvergraph/INode.hpp index 6ca3f0606d..df7db7ed72 100644 --- a/src/shamrock/include/shamrock/solvergraph/INode.hpp +++ b/src/shamrock/include/shamrock/solvergraph/INode.hpp @@ -20,19 +20,25 @@ #include "shambase/memory.hpp" #include "shambase/stacktrace.hpp" #include "shamrock/solvergraph/IEdge.hpp" +#include "shamrock/solvergraph/LifetimeTracker.hpp" #include #include namespace shamrock::solvergraph { /// Inode is node between data edges, takes multiple inputs, multiple outputs - class INode : public std::enable_shared_from_this, - public shambase::WithUUID { + class INode : public std::enable_shared_from_this { /// Read only edges - std::vector> ro_edges; + std::vector> ro_edges = {}; /// Read write edges - std::vector> rw_edges; + std::vector> rw_edges = {}; + + /// track the UUID of the node to notify changes. + /// Sadly we can not use a WithUUID directly since it is hard to log destruction in the + /// destructor if the object was moved which could lead to double free notifications. + std::shared_ptr> tracker + = std::make_shared>(); public: INode() = default; @@ -46,6 +52,9 @@ namespace shamrock::solvergraph { /// Move assignment - automatically delegates to base classes and members INode &operator=(INode &&) noexcept = default; + /// Get the UUID of the node + inline u64 get_uuid() const { return shambase::get_check_ref(tracker).get_uuid(); } + /// Get a shared pointer to this node inline std::shared_ptr getptr_shared() { return shared_from_this(); } /// Get a weak pointer to this node @@ -71,6 +80,7 @@ namespace shamrock::solvergraph { /// Destructor (virtual) & reset the edges virtual ~INode() { + tracker.reset(); __internal_set_ro_edges({}); __internal_set_rw_edges({}); } @@ -106,7 +116,15 @@ namespace shamrock::solvergraph { } /// Evaluate the node - inline void evaluate() { _impl_evaluate_internal(); } + inline void evaluate() { + if (tracker) { + tracker->notify_op(0); + } + _impl_evaluate_internal(); + if (tracker) { + tracker->notify_op(1); + } + } /// Get the dot graph of the node (Currently only an alias to get_dot_graph_partial) inline std::string get_dot_graph() { return get_dot_graph_partial(); }; @@ -124,6 +142,9 @@ namespace shamrock::solvergraph { /// Get the TeX of the node partial inline std::string get_tex_partial() { return _impl_get_tex(); }; + /// Get the label of the node + inline std::string get_label() { return _impl_get_label(); }; + /// print the node info inline virtual std::string print_node_info() const { std::string node_info = shambase::format("Node info :\n"); @@ -176,6 +197,9 @@ namespace shamrock::solvergraph { for (auto e : ro_edges) { // shambase::get_check_ref(e).parent = getptr_weak(); } + if (tracker) { + tracker->notify_update(*this); + } } inline void INode::__internal_set_rw_edges(std::vector> new_rw_edges) { @@ -186,6 +210,9 @@ namespace shamrock::solvergraph { for (auto e : rw_edges) { // shambase::get_check_ref(e).child = getptr_weak(); } + if (tracker) { + tracker->notify_update(*this); + } } template diff --git a/src/shamrock/include/shamrock/solvergraph/LifetimeTracker.hpp b/src/shamrock/include/shamrock/solvergraph/LifetimeTracker.hpp new file mode 100644 index 0000000000..f4ecd2fd9e --- /dev/null +++ b/src/shamrock/include/shamrock/solvergraph/LifetimeTracker.hpp @@ -0,0 +1,63 @@ +// -------------------------------------------------------// +// +// SHAMROCK code for hydrodynamics +// Copyright (c) 2021-2026 Timothée David--Cléris +// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1 +// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information +// +// -------------------------------------------------------// + +#pragma once + +/** + * @file LifetimeTracker.hpp + * @author Timothée David--Cléris (tim.shamrock@proton.me) + * @brief + * + */ + +#include "shambase/WithUUID.hpp" +#include "shambase/aliases_int.hpp" +namespace shamrock::solvergraph { + + template + class LifetimeTracker : public shambase::WithUUID, u64> { + public: + inline static void (*on_create)(u64 uuid) = nullptr; + inline static void (*on_destroy)(u64 uuid) = nullptr; + + inline static void (*on_state_update)(T &node) = nullptr; + inline static void (*on_op)(u64 uuid, u64 op_id) = nullptr; + + LifetimeTracker() : shambase::WithUUID() { + if (on_create != nullptr) { + on_create(this->get_uuid()); + } + }; + + LifetimeTracker(const LifetimeTracker &) = delete; + LifetimeTracker &operator=(const LifetimeTracker &) = delete; + + LifetimeTracker(LifetimeTracker &&) noexcept = default; + LifetimeTracker &operator=(LifetimeTracker &&) noexcept = default; + + inline void notify_update(T &node) { + if (on_state_update != nullptr) { + on_state_update(node); + } + } + + inline void notify_op(u64 op_id) { + if (on_op != nullptr) { + on_op(this->get_uuid(), op_id); + } + } + + ~LifetimeTracker() { + if (on_destroy != nullptr) { + on_destroy(this->get_uuid()); + } + }; + }; + +} // namespace shamrock::solvergraph