1 #ifndef HALIDE_INTROSPECTION_H
2 #define HALIDE_INTROSPECTION_H
3
4 #include <iostream>
5 #include <stdint.h>
6 #include <string>
7
8 /** \file
9 *
10 * Defines methods for introspecting in C++. Relies on DWARF debugging
11 * metadata, so the compilation unit that uses this must be compiled
12 * with -g.
13 */
14
15 namespace Halide {
16 namespace Internal {
17
18 namespace Introspection {
19 /** Get the name of a stack variable from its address. The stack
20 * variable must be in a compilation unit compiled with -g to
21 * work. The expected type helps distinguish between variables at the
22 * same address, e.g a class instance vs its first member. */
23 std::string get_variable_name(const void *, const std::string &expected_type);
24
25 /** Register an untyped heap object. Derive type information from an
26 * introspectable pointer to a pointer to a global object of the same
27 * type. Not thread-safe. */
28 void register_heap_object(const void *obj, size_t size, const void *helper);
29
30 /** Deregister a heap object. Not thread-safe. */
31 void deregister_heap_object(const void *obj, size_t size);
32
33 /** Dump the contents of the stack frame of the calling function. Used
34 * for debugging stack frame sizes inside the compiler. Returns
35 * whether or not it was able to find the relevant debug
36 * information. */
37 bool dump_stack_frame();
38
39 #define HALIDE_DUMP_STACK_FRAME \
40 { \
41 static bool check = Halide::Internal::Introspection::dump_stack_frame(); \
42 (void)check; \
43 }
44
45 /** Return the address of a global with type T *. Call this to
46 * generate something to pass as the last argument to
47 * register_heap_object.
48 */
49 template<typename T>
get_introspection_helper()50 const void *get_introspection_helper() {
51 static T *introspection_helper = nullptr;
52 return &introspection_helper;
53 }
54
55 /** Get the source location in the call stack, skipping over calls in
56 * the Halide namespace. */
57 std::string get_source_location();
58
59 // This gets called automatically by anyone who includes Halide.h by
60 // the code below. It tests if this functionality works for the given
61 // compilation unit, and disables it if not.
62 void test_compilation_unit(bool (*test)(bool (*)(const void *, const std::string &)),
63 bool (*test_a)(const void *, const std::string &),
64 void (*calib)());
65 } // namespace Introspection
66
67 } // namespace Internal
68 } // namespace Halide
69
70 // This code verifies that introspection is working before relying on
71 // it. The definitions must appear in Halide.h, but they should not
72 // appear in libHalide itself. They're defined as static so that clients
73 // can include Halide.h multiple times without link errors.
74 #ifndef COMPILING_HALIDE
75
76 namespace Halide {
77 namespace Internal {
check_introspection(const void * var,const std::string & type,const std::string & correct_name,const std::string & correct_file,int line)78 static bool check_introspection(const void *var, const std::string &type,
79 const std::string &correct_name,
80 const std::string &correct_file, int line) {
81 std::string correct_loc = correct_file + ":" + std::to_string(line);
82 std::string loc = Introspection::get_source_location();
83 std::string name = Introspection::get_variable_name(var, type);
84 return name == correct_name && loc == correct_loc;
85 }
86 } // namespace Internal
87 } // namespace Halide
88
89 namespace HalideIntrospectionCanary {
90
91 // A function that acts as a signpost. By taking it's address and
92 // comparing it to the program counter listed in the debugging info,
93 // we can calibrate for any offset between the debugging info and the
94 // actual memory layout where the code was loaded.
offset_marker()95 static void offset_marker() {
96 std::cerr << "You should not have called this function\n";
97 }
98
99 struct A {
100 int an_int;
101
102 class B {
103 int private_member;
104
105 public:
106 float a_float;
107 A *parent;
BA108 B()
109 : private_member(17) {
110 a_float = private_member * 2.0f;
111 }
112 };
113
114 B a_b;
115
AA116 A() {
117 a_b.parent = this;
118 }
119
120 bool test(const std::string &my_name);
121 };
122
test_a(const void * a_ptr,const std::string & my_name)123 static bool test_a(const void *a_ptr, const std::string &my_name) {
124 const A *a = (const A *)a_ptr;
125 bool success = true;
126 success &= Halide::Internal::check_introspection(&a->an_int, "int", my_name + ".an_int", __FILE__, __LINE__);
127 success &= Halide::Internal::check_introspection(&a->a_b, "HalideIntrospectionCanary::A::B", my_name + ".a_b", __FILE__, __LINE__);
128 success &= Halide::Internal::check_introspection(&a->a_b.parent, "HalideIntrospectionCanary::A \\*", my_name + ".a_b.parent", __FILE__, __LINE__);
129 success &= Halide::Internal::check_introspection(&a->a_b.a_float, "float", my_name + ".a_b.a_float", __FILE__, __LINE__);
130 success &= Halide::Internal::check_introspection(a->a_b.parent, "HalideIntrospectionCanary::A", my_name, __FILE__, __LINE__);
131 return success;
132 }
133
test(bool (* f)(const void *,const std::string &))134 static bool test(bool (*f)(const void *, const std::string &)) {
135 A a1, a2;
136
137 // Call via pointer to prevent inlining.
138 return f(&a1, "a1") && f(&a2, "a2");
139 }
140
141 // Run the tests, and calibrate for the PC offset at static initialization time.
142 namespace {
143 struct TestCompilationUnit {
TestCompilationUnitTestCompilationUnit144 TestCompilationUnit() {
145 Halide::Internal::Introspection::test_compilation_unit(&test, &test_a, &offset_marker);
146 }
147 };
148 } // namespace
149
150 static TestCompilationUnit test_object;
151
152 } // namespace HalideIntrospectionCanary
153
154 #endif
155
156 #endif
157