| 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std.zig"); |
| 3 | const math = std.math; |
| 4 | |
| 5 | pub fn doClientRequest(default: usize, request: usize, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { |
| 6 | if (!builtin.valgrind_support) { |
| 7 | return default; |
| 8 | } |
| 9 | |
| 10 | const args = &[_]usize{ request, a1, a2, a3, a4, a5 }; |
| 11 | |
| 12 | return switch (builtin.cpu.arch) { |
| 13 | .arm, .armeb, .thumb, .thumbeb => asm volatile ( |
| 14 | \\ mov r12, r12, ror #3 ; mov r12, r12, ror #13 |
| 15 | \\ mov r12, r12, ror #29 ; mov r12, r12, ror #19 |
| 16 | \\ orr r10, r10, r10 |
| 17 | : [_] "={r3}" (-> usize), |
| 18 | : [_] "{r4}" (args), |
| 19 | [_] "{r3}" (default), |
| 20 | : .{ .memory = true }), |
| 21 | .aarch64, .aarch64_be => asm volatile ( |
| 22 | \\ ror x12, x12, #3 ; ror x12, x12, #13 |
| 23 | \\ ror x12, x12, #51 ; ror x12, x12, #61 |
| 24 | \\ orr x10, x10, x10 |
| 25 | : [_] "={x3}" (-> usize), |
| 26 | : [_] "{x4}" (args), |
| 27 | [_] "{x3}" (default), |
| 28 | : .{ .memory = true }), |
| 29 | .mips, .mipsel => asm volatile ( |
| 30 | \\ srl $0, $0, 13 |
| 31 | \\ srl $0, $0, 29 |
| 32 | \\ srl $0, $0, 3 |
| 33 | \\ srl $0, $0, 19 |
| 34 | \\ or $13, $13, $13 |
| 35 | : [_] "={$11}" (-> usize), |
| 36 | : [_] "{$12}" (args), |
| 37 | [_] "{$11}" (default), |
| 38 | : .{ .memory = true }), |
| 39 | .mips64, .mips64el => asm volatile ( |
| 40 | \\ dsll $0, $0, 3 ; dsll $0, $0, 13 |
| 41 | \\ dsll $0, $0, 29 ; dsll $0, $0, 19 |
| 42 | \\ or $13, $13, $13 |
| 43 | : [_] "={$11}" (-> usize), |
| 44 | : [_] "{$12}" (args), |
| 45 | [_] "{$11}" (default), |
| 46 | : .{ .memory = true }), |
| 47 | .powerpc, .powerpcle => asm volatile ( |
| 48 | \\ rlwinm 0, 0, 3, 0, 31 ; rlwinm 0, 0, 13, 0, 31 |
| 49 | \\ rlwinm 0, 0, 29, 0, 31 ; rlwinm 0, 0, 19, 0, 31 |
| 50 | \\ or 1, 1, 1 |
| 51 | : [_] "={r3}" (-> usize), |
| 52 | : [_] "{r4}" (args), |
| 53 | [_] "{r3}" (default), |
| 54 | : .{ .memory = true }), |
| 55 | .powerpc64, .powerpc64le => asm volatile ( |
| 56 | \\ rotldi 0, 0, 3 ; rotldi 0, 0, 13 |
| 57 | \\ rotldi 0, 0, 61 ; rotldi 0, 0, 51 |
| 58 | \\ or 1, 1, 1 |
| 59 | : [_] "={r3}" (-> usize), |
| 60 | : [_] "{r4}" (args), |
| 61 | [_] "{r3}" (default), |
| 62 | : .{ .memory = true }), |
| 63 | .riscv64 => asm volatile ( |
| 64 | \\ .option push |
| 65 | \\ .option norvc |
| 66 | \\ srli zero, zero, 3 |
| 67 | \\ srli zero, zero, 13 |
| 68 | \\ srli zero, zero, 51 |
| 69 | \\ srli zero, zero, 61 |
| 70 | \\ or a0, a0, a0 |
| 71 | \\ .option pop |
| 72 | : [_] "={a3}" (-> usize), |
| 73 | : [_] "{a4}" (args), |
| 74 | [_] "{a3}" (default), |
| 75 | : .{ .memory = true }), |
| 76 | .s390x => asm volatile ( |
| 77 | \\ lr %%r15, %%r15 |
| 78 | \\ lr %%r1, %%r1 |
| 79 | \\ lr %%r2, %%r2 |
| 80 | \\ lr %%r3, %%r3 |
| 81 | \\ lr %%r2, %%r2 |
| 82 | : [_] "={r3}" (-> usize), |
| 83 | : [_] "{r2}" (args), |
| 84 | [_] "{r3}" (default), |
| 85 | : .{ .memory = true }), |
| 86 | .x86 => asm volatile ( |
| 87 | \\ roll $3, %%edi ; roll $13, %%edi |
| 88 | \\ roll $29, %%edi ; roll $19, %%edi |
| 89 | \\ xchgl %%ebx, %%ebx |
| 90 | : [_] "={edx}" (-> usize), |
| 91 | : [_] "{eax}" (args), |
| 92 | [_] "{edx}" (default), |
| 93 | : .{ .memory = true }), |
| 94 | .x86_64 => asm volatile ( |
| 95 | \\ rolq $3, %%rdi ; rolq $13, %%rdi |
| 96 | \\ rolq $61, %%rdi ; rolq $51, %%rdi |
| 97 | \\ xchgq %%rbx, %%rbx |
| 98 | : [_] "={rdx}" (-> usize), |
| 99 | : [_] "{rax}" (args), |
| 100 | [_] "{rdx}" (default), |
| 101 | : .{ .memory = true }), |
| 102 | else => default, |
| 103 | }; |
| 104 | } |
| 105 | |
| 106 | pub const ClientRequest = enum(u32) { |
| 107 | RunningOnValgrind = 4097, |
| 108 | DiscardTranslations = 4098, |
| 109 | ClientCall0 = 4353, |
| 110 | ClientCall1 = 4354, |
| 111 | ClientCall2 = 4355, |
| 112 | ClientCall3 = 4356, |
| 113 | CountErrors = 4609, |
| 114 | GdbMonitorCommand = 4610, |
| 115 | MalloclikeBlock = 4865, |
| 116 | ResizeinplaceBlock = 4875, |
| 117 | FreelikeBlock = 4866, |
| 118 | CreateMempool = 4867, |
| 119 | DestroyMempool = 4868, |
| 120 | MempoolAlloc = 4869, |
| 121 | MempoolFree = 4870, |
| 122 | MempoolTrim = 4871, |
| 123 | MoveMempool = 4872, |
| 124 | MempoolChange = 4873, |
| 125 | MempoolExists = 4874, |
| 126 | Printf = 5121, |
| 127 | PrintfBacktrace = 5122, |
| 128 | PrintfValistByRef = 5123, |
| 129 | PrintfBacktraceValistByRef = 5124, |
| 130 | StackRegister = 5377, |
| 131 | StackDeregister = 5378, |
| 132 | StackChange = 5379, |
| 133 | LoadPdbDebuginfo = 5633, |
| 134 | MapIpToSrcloc = 5889, |
| 135 | ChangeErrDisablement = 6145, |
| 136 | VexInitForIri = 6401, |
| 137 | InnerThreads = 6402, |
| 138 | }; |
| 139 | pub fn ToolBase(base: [2]u8) u32 { |
| 140 | return (@as(u32, base[0] & 0xff) << 24) | (@as(u32, base[1] & 0xff) << 16); |
| 141 | } |
| 142 | pub fn IsTool(base: [2]u8, code: usize) bool { |
| 143 | return ToolBase(base) == (code & 0xffff0000); |
| 144 | } |
| 145 | |
| 146 | fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize { |
| 147 | return doClientRequest(default, @as(usize, @intCast(@backingInt(request))), a1, a2, a3, a4, a5); |
| 148 | } |
| 149 | |
| 150 | fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void { |
| 151 | _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5); |
| 152 | } |
| 153 | |
| 154 | /// Returns the number of Valgrinds this code is running under. That |
| 155 | /// is, 0 if running natively, 1 if running under Valgrind, 2 if |
| 156 | /// running under Valgrind which is running under another Valgrind, |
| 157 | /// etc. |
| 158 | pub fn runningOnValgrind() usize { |
| 159 | return doClientRequestExpr(0, .RunningOnValgrind, 0, 0, 0, 0, 0); |
| 160 | } |
| 161 | |
| 162 | test "works whether running on valgrind or not" { |
| 163 | _ = runningOnValgrind(); |
| 164 | } |
| 165 | |
| 166 | /// Discard translation of code in the slice qzz. Useful if you are debugging |
| 167 | /// a JITter or some such, since it provides a way to make sure valgrind will |
| 168 | /// retranslate the invalidated area. Returns no value. |
| 169 | pub fn discardTranslations(qzz: []const u8) void { |
| 170 | doClientRequestStmt(.DiscardTranslations, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0); |
| 171 | } |
| 172 | |
| 173 | pub fn innerThreads(qzz: [*]u8) void { |
| 174 | doClientRequestStmt(.InnerThreads, @intFromPtr(qzz), 0, 0, 0, 0); |
| 175 | } |
| 176 | |
| 177 | pub fn nonSimdCall0(func: fn (usize) usize) usize { |
| 178 | return doClientRequestExpr(0, .ClientCall0, @intFromPtr(func), 0, 0, 0, 0); |
| 179 | } |
| 180 | |
| 181 | pub fn nonSimdCall1(func: fn (usize, usize) usize, a1: usize) usize { |
| 182 | return doClientRequestExpr(0, .ClientCall1, @intFromPtr(func), a1, 0, 0, 0); |
| 183 | } |
| 184 | |
| 185 | pub fn nonSimdCall2(func: fn (usize, usize, usize) usize, a1: usize, a2: usize) usize { |
| 186 | return doClientRequestExpr(0, .ClientCall2, @intFromPtr(func), a1, a2, 0, 0); |
| 187 | } |
| 188 | |
| 189 | pub fn nonSimdCall3(func: fn (usize, usize, usize, usize) usize, a1: usize, a2: usize, a3: usize) usize { |
| 190 | return doClientRequestExpr(0, .ClientCall3, @intFromPtr(func), a1, a2, a3, 0); |
| 191 | } |
| 192 | |
| 193 | /// Counts the number of errors that have been recorded by a tool. Nb: |
| 194 | /// the tool must record the errors with VG_(maybe_record_error)() or |
| 195 | /// VG_(unique_error)() for them to be counted. |
| 196 | pub fn countErrors() usize { |
| 197 | return doClientRequestExpr(0, // default return |
| 198 | .CountErrors, 0, 0, 0, 0, 0); |
| 199 | } |
| 200 | |
| 201 | pub fn mallocLikeBlock(mem: []u8, rzB: usize, is_zeroed: bool) void { |
| 202 | doClientRequestStmt(.MalloclikeBlock, @intFromPtr(mem.ptr), mem.len, rzB, @intFromBool(is_zeroed), 0); |
| 203 | } |
| 204 | |
| 205 | pub fn resizeInPlaceBlock(oldmem: []u8, newsize: usize, rzB: usize) void { |
| 206 | doClientRequestStmt(.ResizeinplaceBlock, @intFromPtr(oldmem.ptr), oldmem.len, newsize, rzB, 0); |
| 207 | } |
| 208 | |
| 209 | pub fn freeLikeBlock(addr: [*]u8, rzB: usize) void { |
| 210 | doClientRequestStmt(.FreelikeBlock, @intFromPtr(addr), rzB, 0, 0, 0); |
| 211 | } |
| 212 | |
| 213 | /// Create a memory pool. |
| 214 | pub const MempoolFlags = struct { |
| 215 | pub const AutoFree = 1; |
| 216 | pub const MetaPool = 2; |
| 217 | }; |
| 218 | pub fn createMempool(pool: [*]u8, rzB: usize, is_zeroed: bool, flags: usize) void { |
| 219 | doClientRequestStmt(.CreateMempool, @intFromPtr(pool), rzB, @intFromBool(is_zeroed), flags, 0); |
| 220 | } |
| 221 | |
| 222 | /// Destroy a memory pool. |
| 223 | pub fn destroyMempool(pool: [*]u8) void { |
| 224 | doClientRequestStmt(.DestroyMempool, @intFromPtr(pool), 0, 0, 0, 0); |
| 225 | } |
| 226 | |
| 227 | /// Associate a piece of memory with a memory pool. |
| 228 | pub fn mempoolAlloc(pool: [*]u8, mem: []u8) void { |
| 229 | doClientRequestStmt(.MempoolAlloc, @intFromPtr(pool), @intFromPtr(mem.ptr), mem.len, 0, 0); |
| 230 | } |
| 231 | |
| 232 | /// Disassociate a piece of memory from a memory pool. |
| 233 | pub fn mempoolFree(pool: [*]u8, addr: [*]u8) void { |
| 234 | doClientRequestStmt(.MempoolFree, @intFromPtr(pool), @intFromPtr(addr), 0, 0, 0); |
| 235 | } |
| 236 | |
| 237 | /// Disassociate any pieces outside a particular range. |
| 238 | pub fn mempoolTrim(pool: [*]u8, mem: []u8) void { |
| 239 | doClientRequestStmt(.MempoolTrim, @intFromPtr(pool), @intFromPtr(mem.ptr), mem.len, 0, 0); |
| 240 | } |
| 241 | |
| 242 | /// Resize and/or move a piece associated with a memory pool. |
| 243 | pub fn moveMempool(poolA: [*]u8, poolB: [*]u8) void { |
| 244 | doClientRequestStmt(.MoveMempool, @intFromPtr(poolA), @intFromPtr(poolB), 0, 0, 0); |
| 245 | } |
| 246 | |
| 247 | /// Resize and/or move a piece associated with a memory pool. |
| 248 | pub fn mempoolChange(pool: [*]u8, addrA: [*]u8, mem: []u8) void { |
| 249 | doClientRequestStmt(.MempoolChange, @intFromPtr(pool), @intFromPtr(addrA), @intFromPtr(mem.ptr), mem.len, 0); |
| 250 | } |
| 251 | |
| 252 | /// Return if a mempool exists. |
| 253 | pub fn mempoolExists(pool: [*]u8) bool { |
| 254 | return doClientRequestExpr(0, .MempoolExists, @intFromPtr(pool), 0, 0, 0, 0) != 0; |
| 255 | } |
| 256 | |
| 257 | /// Mark a piece of memory as being a stack. Returns a stack id. |
| 258 | /// start is the lowest addressable stack byte, end is the highest |
| 259 | /// addressable stack byte. |
| 260 | pub fn stackRegister(stack: []u8) usize { |
| 261 | return doClientRequestExpr(0, .StackRegister, @intFromPtr(stack.ptr), @intFromPtr(stack.ptr) + stack.len, 0, 0, 0); |
| 262 | } |
| 263 | |
| 264 | /// Unmark the piece of memory associated with a stack id as being a stack. |
| 265 | pub fn stackDeregister(id: usize) void { |
| 266 | doClientRequestStmt(.StackDeregister, id, 0, 0, 0, 0); |
| 267 | } |
| 268 | |
| 269 | /// Change the start and end address of the stack id. |
| 270 | /// start is the new lowest addressable stack byte, end is the new highest |
| 271 | /// addressable stack byte. |
| 272 | pub fn stackChange(id: usize, newstack: []u8) void { |
| 273 | doClientRequestStmt(.StackChange, id, @intFromPtr(newstack.ptr), @intFromPtr(newstack.ptr) + newstack.len, 0, 0); |
| 274 | } |
| 275 | |
| 276 | // Load PDB debug info for Wine PE image_map. |
| 277 | // pub fn loadPdbDebuginfo(fd, ptr, total_size, delta) void { |
| 278 | // doClientRequestStmt(.LoadPdbDebuginfo, |
| 279 | // fd, ptr, total_size, delta, |
| 280 | // 0); |
| 281 | // } |
| 282 | |
| 283 | /// Map a code address to a source file name and line number. buf64 |
| 284 | /// must point to a 64-byte buffer in the caller's address space. The |
| 285 | /// result will be dumped in there and is guaranteed to be zero |
| 286 | /// terminated. If no info is found, the first byte is set to zero. |
| 287 | pub fn mapIpToSrcloc(addr: *const u8, buf64: [64]u8) usize { |
| 288 | return doClientRequestExpr(0, .MapIpToSrcloc, @intFromPtr(addr), @intFromPtr(&buf64[0]), 0, 0, 0); |
| 289 | } |
| 290 | |
| 291 | /// Disable error reporting for this thread. Behaves in a stack like |
| 292 | /// way, so you can safely call this multiple times provided that |
| 293 | /// enableErrorReporting() is called the same number of times |
| 294 | /// to re-enable reporting. The first call of this macro disables |
| 295 | /// reporting. Subsequent calls have no effect except to increase the |
| 296 | /// number of enableErrorReporting() calls needed to re-enable |
| 297 | /// reporting. Child threads do not inherit this setting from their |
| 298 | /// parents -- they are always created with reporting enabled. |
| 299 | pub fn disableErrorReporting() void { |
| 300 | doClientRequestStmt(.ChangeErrDisablement, 1, 0, 0, 0, 0); |
| 301 | } |
| 302 | |
| 303 | /// Re-enable error reporting. (see disableErrorReporting()) |
| 304 | pub fn enableErrorReporting() void { |
| 305 | doClientRequestStmt(.ChangeErrDisablement, math.maxInt(usize), 0, 0, 0, 0); |
| 306 | } |
| 307 | |
| 308 | /// Execute a monitor command from the client program. |
| 309 | /// If a connection is opened with GDB, the output will be sent |
| 310 | /// according to the output mode set for vgdb. |
| 311 | /// If no connection is opened, output will go to the log output. |
| 312 | /// Returns 1 if command not recognised, 0 otherwise. |
| 313 | pub fn monitorCommand(command: [*]u8) bool { |
| 314 | return doClientRequestExpr(0, .GdbMonitorCommand, @intFromPtr(command), 0, 0, 0, 0) != 0; |
| 315 | } |
| 316 | |
| 317 | pub const memcheck = @import("valgrind/memcheck.zig"); |
| 318 | pub const callgrind = @import("valgrind/callgrind.zig"); |
| 319 | pub const cachegrind = @import("valgrind/cachegrind.zig"); |
| 320 | |
| 321 | test { |
| 322 | _ = memcheck; |
| 323 | _ = callgrind; |
| 324 | _ = cachegrind; |
| 325 | } |