Halide
Introspection.h
Go to the documentation of this file.
1 #ifndef HALIDE_INTROSPECTION_H
2 #define HALIDE_INTROSPECTION_H
3 
4 #include <cstdint>
5 #include <iostream>
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>
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 {
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 
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.
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 = 17;
104 
105  public:
106  float a_float;
108  B() {
109  a_float = private_member * 2.0f;
110  }
111  };
112 
114 
115  A() {
116  a_b.parent = this;
117  }
118 
119  bool test(const std::string &my_name);
120 };
121 
122 static bool test_a(const void *a_ptr, const std::string &my_name) {
123  const A *a = (const A *)a_ptr;
124  bool success = true;
125  success &= Halide::Internal::check_introspection(&a->an_int, "int", my_name + ".an_int", __FILE__, __LINE__);
126  success &= Halide::Internal::check_introspection(&a->a_b, "HalideIntrospectionCanary::A::B", my_name + ".a_b", __FILE__, __LINE__);
127  success &= Halide::Internal::check_introspection(&a->a_b.parent, "HalideIntrospectionCanary::A \\*", my_name + ".a_b.parent", __FILE__, __LINE__);
128  success &= Halide::Internal::check_introspection(&a->a_b.a_float, "float", my_name + ".a_b.a_float", __FILE__, __LINE__);
129  success &= Halide::Internal::check_introspection(a->a_b.parent, "HalideIntrospectionCanary::A", my_name, __FILE__, __LINE__);
130  return success;
131 }
132 
133 static bool test(bool (*f)(const void *, const std::string &)) {
134  A a1, a2;
135 
136  // Call via pointer to prevent inlining.
137  return f(&a1, "a1") && f(&a2, "a2");
138 }
139 
140 // Run the tests, and calibrate for the PC offset at static initialization time.
141 namespace {
142 struct TestCompilationUnit {
143  TestCompilationUnit() {
144  Halide::Internal::Introspection::test_compilation_unit(&test, &test_a, &offset_marker);
145  }
146 };
147 } // namespace
148 
149 static TestCompilationUnit test_object;
150 
151 } // namespace HalideIntrospectionCanary
152 
153 #endif
154 
155 #endif
Halide::Internal::Introspection::get_introspection_helper
const void * get_introspection_helper()
Return the address of a global with type T *.
Definition: Introspection.h:50
Halide::Internal::Introspection::test_compilation_unit
void test_compilation_unit(bool(*test)(bool(*)(const void *, const std::string &)), bool(*test_a)(const void *, const std::string &), void(*calib)())
HalideIntrospectionCanary::A::a_b
B a_b
Definition: Introspection.h:113
Halide
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Definition: AbstractGenerator.h:19
Halide::LinkageType::Internal
@ Internal
Not visible externally, similar to 'static' linkage in C.
HalideIntrospectionCanary::A::B::B
B()
Definition: Introspection.h:108
Halide::Internal::Introspection::deregister_heap_object
void deregister_heap_object(const void *obj, size_t size)
Deregister a heap object.
HalideIntrospectionCanary::A::B::a_float
float a_float
Definition: Introspection.h:106
Halide::Internal::Introspection::get_source_location
std::string get_source_location()
Get the source location in the call stack, skipping over calls in the Halide namespace.
HalideIntrospectionCanary::A::B
Definition: Introspection.h:102
HalideIntrospectionCanary::A::test
bool test(const std::string &my_name)
Halide::Internal::Introspection::register_heap_object
void register_heap_object(const void *obj, size_t size, const void *helper)
Register an untyped heap object.
HalideIntrospectionCanary
Definition: Introspection.h:89
HalideIntrospectionCanary::A::A
A()
Definition: Introspection.h:115
Halide::Internal::Introspection::dump_stack_frame
bool dump_stack_frame()
Dump the contents of the stack frame of the calling function.
HalideIntrospectionCanary::A::B::parent
A * parent
Definition: Introspection.h:107
HalideIntrospectionCanary::A
Definition: Introspection.h:99
Halide::Internal::Introspection::get_variable_name
std::string get_variable_name(const void *, const std::string &expected_type)
Get the name of a stack variable from its address.
HalideIntrospectionCanary::A::an_int
int an_int
Definition: Introspection.h:100