24#if __has_include(<cxxabi.h>)
25#define LIBTCSPC_HAVE_CXA_DEMANGLE
28#warning introspected type names will be mangled (cxxabi.h not available)
36[[nodiscard]]
inline auto maybe_demangle(
char const *mangled) -> std::string {
37#ifdef LIBTCSPC_HAVE_CXA_DEMANGLE
39 auto demangled = std::unique_ptr<char, void (*)(void *)>(
40 abi::__cxa_demangle(mangled,
nullptr,
nullptr, &status), std::free);
41 return status == 0 ? demangled.get() : mangled;
76 template <
typename Processor>
78 : addr(std::uintptr_t(
processor)), typ(typeid(Processor)),
79 nm(std::move(
name)) {}
89 [[nodiscard]]
auto address() const -> std::uintptr_t {
return addr; }
104 return internal::maybe_demangle(typ.name());
112 [[nodiscard]]
auto name() const -> std::
string {
return nm; }
147 template <
typename Processor>
149 : addr(std::uintptr_t(
processor)), typ(typeid(Processor)) {}
158 return lhs.addr < rhs.addr ||
159 (lhs.addr == rhs.addr && lhs.typ < rhs.typ);
165 return lhs.addr > rhs.addr ||
166 (lhs.addr == rhs.addr && lhs.typ > rhs.typ);
172 return not(lhs > rhs);
178 return not(lhs < rhs);
182class processor_graph;
197template <
typename Proc>
199 { p.introspect_node() } -> std::same_as<processor_info>;
200 { p.introspect_graph() } -> std::same_as<processor_graph>;
227 std::vector<node_type> nds;
228 std::vector<std::pair<processor_node_id, processor_node_id>> edgs;
229 std::vector<processor_node_id> entrypts;
250 template <
typename Processor>
253 if (entrypts.size() > 1) {
254 throw std::logic_error(
255 "processor_graph can only push entry point when it has at most one entry point");
259 auto node = node_type{id,
processor->introspect_node()};
260 auto [node_lower, node_upper] = std::equal_range(
261 nds.begin(), nds.end(), node,
262 [](
auto const &l,
auto const &r) { return l.id < r.id; });
263 if (node_lower != node_upper) {
264 throw std::logic_error(
265 "processor_graph cannot push entry point that already exists");
267 nds.insert(node_upper, std::move(node));
269 if (entrypts.empty()) {
270 entrypts.push_back(
id);
272 auto const edge = std::pair{id, entrypts[0]};
273 auto edge_inspt = std::upper_bound(edgs.begin(), edgs.end(), edge);
274 edgs.insert(edge_inspt, edge);
287 std::vector<processor_node_id> ret;
288 ret.reserve(nds.size());
289 std::transform(nds.begin(), nds.end(), std::back_inserter(ret),
290 [](
auto const &n) { return n.id; });
323 return std::find(entrypts.begin(), entrypts.end(),
id) !=
338 auto it = std::find_if(nds.begin(), nds.end(),
339 [
id](
auto const &n) { return n.id == id; });
341 throw std::out_of_range(
"no such node id in processor_graph");
342 return std::size_t(std::distance(nds.begin(), it));
354 auto it = std::find_if(nds.begin(), nds.end(),
355 [
id](
auto const &n) { return n.id == id; });
357 throw std::out_of_range(
"no such node id in processor_graph");
385 c.nds.reserve(a.nds.size() + b.nds.size());
386 std::set_union(a.nds.begin(), a.nds.end(), b.nds.begin(), b.nds.end(),
387 std::back_inserter(c.nds),
388 [](
auto const &l,
auto const &r) { return l.id < r.id; });
390 c.edgs.reserve(a.edgs.size() + b.edgs.size());
391 std::set_union(a.edgs.begin(), a.edgs.end(), b.edgs.begin(), b.edgs.end(),
392 std::back_inserter(c.edgs));
394 c.entrypts.reserve(a.entrypts.size() + b.entrypts.size());
395 std::set_union(a.entrypts.begin(), a.entrypts.end(), b.entrypts.begin(),
396 b.entrypts.end(), std::back_inserter(c.entrypts));
403inline auto format_hex_addr(std::uintptr_t p) -> std::string {
404 std::array<char,
sizeof(
void *) * 2 + 3> buf{};
405 std::fill(buf.begin(), buf.end(),
'0');
408 std::to_chars(buf.data(), buf.data() + buf.size() - 1, p, 16);
410 std::rotate(buf.begin(),
411 std::next(buf.begin(), std::distance(buf.data(), r.ptr) + 1),
429[[nodiscard]]
inline auto
432 dot +=
"digraph G {\n";
433 for (
auto const &node : graph.nodes()) {
436 dot += std::to_string(graph.node_index(node));
437 dot +=
" [shape=box label=\"";
439 dot +=
"\" tooltip=\"";
442 dot += internal::format_hex_addr(info.
address());
445 for (
auto const &edge : graph.edges()) {
447 dot += std::to_string(graph.node_index(edge.first));
449 dot += std::to_string(graph.node_index(edge.second));
Value type representing a directed acyclic graph of processors.
Definition introspect.hpp:220
auto node_info(processor_node_id id) const -> processor_info
Return metadata for the processor represented by the given node.
Definition introspect.hpp:352
auto edges() const -> std::vector< std::pair< processor_node_id, processor_node_id > >
Return all of the edges of this graph.
Definition introspect.hpp:301
auto node_index(processor_node_id id) const -> std::size_t
Return the numerical index of the given node in this graph.
Definition introspect.hpp:337
auto nodes() const -> std::vector< processor_node_id >
Return all of the nodes of this graph.
Definition introspect.hpp:286
auto entry_points() const -> std::vector< processor_node_id >
Return all of the entry points of this graph.
Definition introspect.hpp:311
auto is_entry_point(processor_node_id id) const -> bool
Return whether the given node is an entry point of this graph.
Definition introspect.hpp:322
friend auto merge_processor_graphs(processor_graph const &a, processor_graph const &b) -> processor_graph
Create a new processor graph by merging two existing ones.
Definition introspect.hpp:380
auto push_entry_point(Processor const *processor) -> processor_graph &
Add a processor node to this graph, upstream of the current entry point (if any), making it the new e...
Definition introspect.hpp:252
Value type representing metadata of a processor.
Definition introspect.hpp:59
auto type_name() const -> std::string
Return the C++ type name of the processor.
Definition introspect.hpp:103
auto address() const -> std::uintptr_t
Return the address of the processor.
Definition introspect.hpp:89
processor_info(Processor const *processor, std::string name)
Construct with pointer to processor and name.
Definition introspect.hpp:77
friend auto operator==(processor_info const &lhs, processor_info const &rhs) -> bool=default
Equality comparison operator.
auto name() const -> std::string
Return the simple name of the processor.
Definition introspect.hpp:112
Value type representing processor identity within a graph.
Definition introspect.hpp:130
friend auto operator>(processor_node_id const &lhs, processor_node_id const &rhs) -> bool
Greater-than operator.
Definition introspect.hpp:163
friend auto operator==(processor_node_id const &lhs, processor_node_id const &rhs) -> bool=default
Equality comparison operator.
friend auto operator<(processor_node_id const &lhs, processor_node_id const &rhs) -> bool
Less-than operator.
Definition introspect.hpp:156
friend auto operator>=(processor_node_id const &lhs, processor_node_id const &rhs) -> bool
Greater-than-or-equal-to operator.
Definition introspect.hpp:176
friend auto operator<=(processor_node_id const &lhs, processor_node_id const &rhs) -> bool
Less-than-or-equal-to operator.
Definition introspect.hpp:170
processor_node_id(Processor const *processor)
Construct with a pointer to a processor.
Definition introspect.hpp:148
Concept that is satisfied when a processor supports introspection.
Definition introspect.hpp:198
Concept that is satisfied when a processor handles the given event types and flush.
Definition processor.hpp:119
auto graphviz_from_processor_graph(processor_graph const &graph) -> std::string
Return a Graphviz dot representation of a processor graph.
Definition introspect.hpp:430
auto merge_processor_graphs(processor_graph const &a, processor_graph const &b) -> processor_graph
Create a new processor graph by merging two existing ones.
Definition introspect.hpp:380
libtcspc namespace.
Definition acquire.hpp:30