9a85248018c07acd9612264fa99e319807086ba9
[mesa.git] / src / gallium / auxiliary / gallivm / lp_bld_debug.cpp
1 /**************************************************************************
2 *
3 * Copyright 2009-2011 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include <stddef.h>
29
30 #include <llvm-c/Core.h>
31 #include <llvm-c/Disassembler.h>
32 #include <llvm/Support/raw_ostream.h>
33 #include <llvm/Support/Format.h>
34 #include <llvm/Support/Host.h>
35 #include <llvm/IR/Module.h>
36
37 #include "util/u_math.h"
38 #include "util/u_debug.h"
39
40 #include "lp_bld_debug.h"
41
42 #ifdef __linux__
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #endif
46
47
48
49 /**
50 * Check alignment.
51 *
52 * It is important that this check is not implemented as a macro or inlined
53 * function, as the compiler assumptions in respect to alignment of global
54 * and stack variables would often make the check a no op, defeating the
55 * whole purpose of the exercise.
56 */
57 extern "C" boolean
58 lp_check_alignment(const void *ptr, unsigned alignment)
59 {
60 assert(util_is_power_of_two(alignment));
61 return ((uintptr_t)ptr & (alignment - 1)) == 0;
62 }
63
64
65 class raw_debug_ostream :
66 public llvm::raw_ostream
67 {
68 private:
69 uint64_t pos;
70
71 public:
72 raw_debug_ostream() : pos(0) { }
73
74 void write_impl(const char *Ptr, size_t Size);
75
76 uint64_t current_pos() const { return pos; }
77 size_t preferred_buffer_size() const { return 512; }
78 };
79
80
81 void
82 raw_debug_ostream::write_impl(const char *Ptr, size_t Size)
83 {
84 if (Size > 0) {
85 char *lastPtr = (char *)&Ptr[Size];
86 char last = *lastPtr;
87 *lastPtr = 0;
88 _debug_printf("%*s", Size, Ptr);
89 *lastPtr = last;
90 pos += Size;
91 }
92 }
93
94
95 extern "C" const char *
96 lp_get_module_id(LLVMModuleRef module)
97 {
98 return llvm::unwrap(module)->getModuleIdentifier().c_str();
99 }
100
101
102 /**
103 * Same as LLVMDumpValue, but through our debugging channels.
104 */
105 extern "C" void
106 lp_debug_dump_value(LLVMValueRef value)
107 {
108 #if (defined(PIPE_OS_WINDOWS) && !defined(PIPE_CC_MSVC)) || defined(PIPE_OS_EMBEDDED)
109 raw_debug_ostream os;
110 llvm::unwrap(value)->print(os);
111 os.flush();
112 #else
113 LLVMDumpValue(value);
114 #endif
115 }
116
117
118 /*
119 * Disassemble a function, using the LLVM MC disassembler.
120 *
121 * See also:
122 * - http://blog.llvm.org/2010/01/x86-disassembler.html
123 * - http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html
124 */
125 static size_t
126 disassemble(const void* func, llvm::raw_ostream & Out)
127 {
128 const uint8_t *bytes = (const uint8_t *)func;
129
130 /*
131 * Limit disassembly to this extent
132 */
133 const uint64_t extent = 96 * 1024;
134
135 /*
136 * Initialize all used objects.
137 */
138
139 std::string Triple = llvm::sys::getProcessTriple();
140 LLVMDisasmContextRef D = LLVMCreateDisasm(Triple.c_str(), NULL, 0, NULL, NULL);
141 char outline[1024];
142
143 if (!D) {
144 Out << "error: couldn't create disassembler for triple " << Triple << "\n";
145 return 0;
146 }
147
148 uint64_t pc;
149 pc = 0;
150 while (pc < extent) {
151 size_t Size;
152
153 /*
154 * Print address. We use addresses relative to the start of the function,
155 * so that between runs.
156 */
157
158 Out << llvm::format("%6lu:\t", (unsigned long)pc);
159
160 Size = LLVMDisasmInstruction(D, (uint8_t *)bytes + pc, extent - pc, 0, outline,
161 sizeof outline);
162
163 if (!Size) {
164 Out << "invalid\n";
165 pc += 1;
166 break;
167 }
168
169 /*
170 * Output the bytes in hexidecimal format.
171 */
172
173 if (0) {
174 unsigned i;
175 for (i = 0; i < Size; ++i) {
176 Out << llvm::format("%02x ", bytes[pc + i]);
177 }
178 for (; i < 16; ++i) {
179 Out << " ";
180 }
181 }
182
183 /*
184 * Print the instruction.
185 */
186
187 Out << outline;
188
189 Out << "\n";
190
191 /*
192 * Stop disassembling on return statements, if there is no record of a
193 * jump to a successive address.
194 *
195 * XXX: This currently assumes x86
196 */
197
198 if (Size == 1 && bytes[pc] == 0xc3) {
199 break;
200 }
201
202 /*
203 * Advance.
204 */
205
206 pc += Size;
207
208 if (pc >= extent) {
209 Out << "disassembly larger than " << extent << "bytes, aborting\n";
210 break;
211 }
212 }
213
214 Out << "\n";
215 Out.flush();
216
217 LLVMDisasmDispose(D);
218
219 /*
220 * Print GDB command, useful to verify output.
221 */
222 if (0) {
223 _debug_printf("disassemble %p %p\n", bytes, bytes + pc);
224 }
225
226 return pc;
227 }
228
229
230 extern "C" void
231 lp_disassemble(LLVMValueRef func, const void *code) {
232 raw_debug_ostream Out;
233 Out << LLVMGetValueName(func) << ":\n";
234 disassemble(code, Out);
235 }
236
237
238 /*
239 * Linux perf profiler integration.
240 *
241 * See also:
242 * - http://penberg.blogspot.co.uk/2009/06/jato-has-profiler.html
243 * - https://github.com/penberg/jato/commit/73ad86847329d99d51b386f5aba692580d1f8fdc
244 * - http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=80d496be89ed7dede5abee5c057634e80a31c82d
245 */
246 extern "C" void
247 lp_profile(LLVMValueRef func, const void *code)
248 {
249 #if defined(__linux__) && (defined(DEBUG) || defined(PROFILE))
250 static boolean first_time = TRUE;
251 static FILE *perf_map_file = NULL;
252 static int perf_asm_fd = -1;
253 if (first_time) {
254 /*
255 * We rely on the disassembler for determining a function's size, but
256 * the disassembly is a leaky and slow operation, so avoid running
257 * this except when running inside linux perf, which can be inferred
258 * by the PERF_BUILDID_DIR environment variable.
259 */
260 if (getenv("PERF_BUILDID_DIR")) {
261 pid_t pid = getpid();
262 char filename[256];
263 util_snprintf(filename, sizeof filename, "/tmp/perf-%llu.map", (unsigned long long)pid);
264 perf_map_file = fopen(filename, "wt");
265 util_snprintf(filename, sizeof filename, "/tmp/perf-%llu.map.asm", (unsigned long long)pid);
266 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
267 perf_asm_fd = open(filename, O_WRONLY | O_CREAT, mode);
268 }
269 first_time = FALSE;
270 }
271 if (perf_map_file) {
272 const char *symbol = LLVMGetValueName(func);
273 unsigned long addr = (uintptr_t)code;
274 llvm::raw_fd_ostream Out(perf_asm_fd, false);
275 Out << symbol << ":\n";
276 unsigned long size = disassemble(code, Out);
277 fprintf(perf_map_file, "%lx %lx %s\n", addr, size, symbol);
278 fflush(perf_map_file);
279 }
280 #else
281 (void)func;
282 (void)code;
283 #endif
284 }
285
286