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#include <fstream> 30#include <sstream> 31#include <iomanip> 32 33#include <llvm/Config/llvm-config.h> 34#include <llvm-c/Core.h> 35#include <llvm-c/Disassembler.h> 36#include <llvm/Support/raw_ostream.h> 37#include <llvm/Support/Format.h> 38#include <llvm/Support/Host.h> 39#include <llvm/IR/Module.h> 40 41#include "util/u_math.h" 42#include "util/u_debug.h" 43 44#include "lp_bld_debug.h" 45 46#ifdef __linux__ 47#include <sys/stat.h> 48#include <fcntl.h> 49#endif 50 51 52 53/** 54 * Check alignment. 55 * 56 * It is important that this check is not implemented as a macro or inlined 57 * function, as the compiler assumptions in respect to alignment of global 58 * and stack variables would often make the check a no op, defeating the 59 * whole purpose of the exercise. 60 */ 61extern "C" boolean 62lp_check_alignment(const void *ptr, unsigned alignment) 63{ 64 assert(util_is_power_of_two_or_zero(alignment)); 65 return ((uintptr_t)ptr & (alignment - 1)) == 0; 66} 67 68 69/** 70 * Same as LLVMDumpValue, but through our debugging channels. 71 */ 72extern "C" void 73lp_debug_dump_value(LLVMValueRef value) 74{ 75 char *str = LLVMPrintValueToString(value); 76 if (str) { 77 os_log_message(str); 78 LLVMDisposeMessage(str); 79 } 80} 81 82 83/* 84 * Disassemble a function, using the LLVM MC disassembler. 85 * 86 * See also: 87 * - http://blog.llvm.org/2010/01/x86-disassembler.html 88 * - http://blog.llvm.org/2010/04/intro-to-llvm-mc-project.html 89 */ 90static size_t 91disassemble(const void* func, std::ostream &buffer) 92{ 93 const uint8_t *bytes = (const uint8_t *)func; 94 95 /* 96 * Limit disassembly to this extent 97 */ 98 const uint64_t extent = 96 * 1024; 99 100 /* 101 * Initialize all used objects. 102 */ 103 104 const char *triple = LLVM_HOST_TRIPLE; 105 LLVMDisasmContextRef D = LLVMCreateDisasm(triple, NULL, 0, NULL, NULL); 106 char outline[1024]; 107 108 if (!D) { 109 buffer << "error: could not create disassembler for triple " 110 << triple << '\n'; 111 return 0; 112 } 113 114 uint64_t pc; 115 pc = 0; 116 while (pc < extent) { 117 size_t Size; 118 119 /* 120 * Print address. We use addresses relative to the start of the function, 121 * so that between runs. 122 */ 123 124 buffer << std::setw(6) << (unsigned long)pc << ":\t"; 125 126 Size = LLVMDisasmInstruction(D, (uint8_t *)bytes + pc, extent - pc, 0, outline, 127 sizeof outline); 128 129 if (!Size) { 130 buffer << "invalid\n"; 131 pc += 1; 132 break; 133 } 134 135 /* 136 * Output the bytes in hexidecimal format. 137 */ 138 139 if (0) { 140 unsigned i; 141 for (i = 0; i < Size; ++i) { 142 buffer << std::hex << std::setfill('0') << std::setw(2) 143 << static_cast<int> (bytes[pc + i]); 144 } 145 for (; i < 16; ++i) { 146 buffer << std::dec << " "; 147 } 148 } 149 150 /* 151 * Print the instruction. 152 */ 153 154 buffer << std::setw(Size) << outline << '\n'; 155 156 /* 157 * Stop disassembling on return statements, if there is no record of a 158 * jump to a successive address. 159 * 160 * XXX: This currently assumes x86 161 */ 162 163#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) 164 if (Size == 1 && bytes[pc] == 0xc3) { 165 break; 166 } 167#endif 168 169 /* 170 * Advance. 171 */ 172 173 pc += Size; 174 175 if (pc >= extent) { 176 buffer << "disassembly larger than " << extent << " bytes, aborting\n"; 177 break; 178 } 179 } 180 181 buffer << '\n'; 182 183 LLVMDisasmDispose(D); 184 185 /* 186 * Print GDB command, useful to verify output. 187 */ 188 if (0) { 189 buffer << "disassemble " << static_cast<const void*>(bytes) << ' ' 190 << static_cast<const void*>(bytes + pc) << '\n'; 191 } 192 193 return pc; 194} 195 196 197extern "C" void 198lp_disassemble(LLVMValueRef func, const void *code) 199{ 200 std::ostringstream buffer; 201 std::string s; 202 203 buffer << LLVMGetValueName(func) << ":\n"; 204 disassemble(code, buffer); 205 s = buffer.str(); 206 os_log_message(s.c_str()); 207 os_log_message("\n"); 208} 209 210 211/* 212 * Linux perf profiler integration. 213 * 214 * See also: 215 * - http://penberg.blogspot.co.uk/2009/06/jato-has-profiler.html 216 * - https://github.com/penberg/jato/commit/73ad86847329d99d51b386f5aba692580d1f8fdc 217 * - http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=80d496be89ed7dede5abee5c057634e80a31c82d 218 */ 219extern "C" void 220lp_profile(LLVMValueRef func, const void *code) 221{ 222#if defined(__linux__) && defined(PROFILE) 223 static std::ofstream perf_asm_file; 224 static boolean first_time = TRUE; 225 static FILE *perf_map_file = NULL; 226 if (first_time) { 227 /* 228 * We rely on the disassembler for determining a function's size, but 229 * the disassembly is a leaky and slow operation, so avoid running 230 * this except when running inside linux perf, which can be inferred 231 * by the PERF_BUILDID_DIR environment variable. 232 */ 233 if (getenv("PERF_BUILDID_DIR")) { 234 pid_t pid = getpid(); 235 char filename[256]; 236 snprintf(filename, sizeof filename, "/tmp/perf-%llu.map", (unsigned long long)pid); 237 perf_map_file = fopen(filename, "wt"); 238 snprintf(filename, sizeof filename, "/tmp/perf-%llu.map.asm", (unsigned long long)pid); 239 perf_asm_file.open(filename); 240 } 241 first_time = FALSE; 242 } 243 if (perf_map_file) { 244 const char *symbol = LLVMGetValueName(func); 245 unsigned long addr = (uintptr_t)code; 246 perf_asm_file << symbol << ":\n"; 247 unsigned long size = disassemble(code, perf_asm_file); 248 perf_asm_file.flush(); 249 fprintf(perf_map_file, "%lx %lx %s\n", addr, size, symbol); 250 fflush(perf_map_file); 251 } 252#else 253 (void)func; 254 (void)code; 255#endif 256} 257 258 259