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