64fb044868fd7e405f8aa695c5d74c8acfbf2a9b
[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 static const char *
119 disassemblerSymbolLookupCB(void *DisInfo,
120 uint64_t ReferenceValue,
121 uint64_t *ReferenceType,
122 uint64_t ReferencePC,
123 const char **ReferenceName)
124 {
125 // TODO: Maybe this can be used to guess jumps
126 return NULL;
127 }
128
129
130 /*
131 * Disassemble a function, using the LLVM MC disassembler.
132 *
133 * See also:
134 * - http://blog.llvm.org/2010/01/x86-disassembler.html
135 * - http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html
136 */
137 static size_t
138 disassemble(const void* func, llvm::raw_ostream & Out)
139 {
140 const uint8_t *bytes = (const uint8_t *)func;
141
142 /*
143 * Limit disassembly to this extent
144 */
145 const uint64_t extent = 96 * 1024;
146
147 /*
148 * Initialize all used objects.
149 */
150
151 std::string Triple = llvm::sys::getProcessTriple();
152 LLVMDisasmContextRef D = LLVMCreateDisasm(Triple.c_str(), NULL, 0, NULL, &disassemblerSymbolLookupCB);
153 char outline[1024];
154
155 if (!D) {
156 Out << "error: couldn't create disassembler for triple " << Triple << "\n";
157 return 0;
158 }
159
160 uint64_t pc;
161 pc = 0;
162 while (pc < extent) {
163 size_t Size;
164
165 /*
166 * Print address. We use addresses relative to the start of the function,
167 * so that between runs.
168 */
169
170 Out << llvm::format("%6lu:\t", (unsigned long)pc);
171
172 Size = LLVMDisasmInstruction(D, (uint8_t *)bytes + pc, extent - pc, 0, outline,
173 sizeof outline);
174
175 if (!Size) {
176 Out << "invalid\n";
177 pc += 1;
178 break;
179 }
180
181 /*
182 * Output the bytes in hexidecimal format.
183 */
184
185 if (0) {
186 unsigned i;
187 for (i = 0; i < Size; ++i) {
188 Out << llvm::format("%02x ", bytes[pc + i]);
189 }
190 for (; i < 16; ++i) {
191 Out << " ";
192 }
193 }
194
195 /*
196 * Print the instruction.
197 */
198
199 Out << outline;
200
201 Out << "\n";
202
203 /*
204 * Stop disassembling on return statements, if there is no record of a
205 * jump to a successive address.
206 *
207 * XXX: This currently assumes x86
208 */
209
210 if (Size == 1 && bytes[pc] == 0xc3) {
211 break;
212 }
213
214 /*
215 * Advance.
216 */
217
218 pc += Size;
219
220 if (pc >= extent) {
221 Out << "disassembly larger than " << extent << "bytes, aborting\n";
222 break;
223 }
224 }
225
226 Out << "\n";
227 Out.flush();
228
229 LLVMDisasmDispose(D);
230
231 /*
232 * Print GDB command, useful to verify output.
233 */
234 if (0) {
235 _debug_printf("disassemble %p %p\n", bytes, bytes + pc);
236 }
237
238 return pc;
239 }
240
241
242 extern "C" void
243 lp_disassemble(LLVMValueRef func, const void *code) {
244 raw_debug_ostream Out;
245 Out << LLVMGetValueName(func) << ":\n";
246 disassemble(code, Out);
247 }
248
249
250 /*
251 * Linux perf profiler integration.
252 *
253 * See also:
254 * - http://penberg.blogspot.co.uk/2009/06/jato-has-profiler.html
255 * - https://github.com/penberg/jato/commit/73ad86847329d99d51b386f5aba692580d1f8fdc
256 * - http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=80d496be89ed7dede5abee5c057634e80a31c82d
257 */
258 extern "C" void
259 lp_profile(LLVMValueRef func, const void *code)
260 {
261 #if defined(__linux__) && (defined(DEBUG) || defined(PROFILE))
262 static boolean first_time = TRUE;
263 static FILE *perf_map_file = NULL;
264 static int perf_asm_fd = -1;
265 if (first_time) {
266 /*
267 * We rely on the disassembler for determining a function's size, but
268 * the disassembly is a leaky and slow operation, so avoid running
269 * this except when running inside linux perf, which can be inferred
270 * by the PERF_BUILDID_DIR environment variable.
271 */
272 if (getenv("PERF_BUILDID_DIR")) {
273 pid_t pid = getpid();
274 char filename[256];
275 util_snprintf(filename, sizeof filename, "/tmp/perf-%llu.map", (unsigned long long)pid);
276 perf_map_file = fopen(filename, "wt");
277 util_snprintf(filename, sizeof filename, "/tmp/perf-%llu.map.asm", (unsigned long long)pid);
278 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
279 perf_asm_fd = open(filename, O_WRONLY | O_CREAT, mode);
280 }
281 first_time = FALSE;
282 }
283 if (perf_map_file) {
284 const char *symbol = LLVMGetValueName(func);
285 unsigned long addr = (uintptr_t)code;
286 llvm::raw_fd_ostream Out(perf_asm_fd, false);
287 Out << symbol << ":\n";
288 unsigned long size = disassemble(code, Out);
289 fprintf(perf_map_file, "%lx %lx %s\n", addr, size, symbol);
290 fflush(perf_map_file);
291 }
292 #else
293 (void)func;
294 (void)code;
295 #endif
296 }
297
298