Mercurial
comparison third_party/luajit/src/jit/p.lua @ 178:94705b5986b3
[ThirdParty] Added WRK and luajit for load testing.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Thu, 22 Jan 2026 20:10:30 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 177:24fe8ff94056 | 178:94705b5986b3 |
|---|---|
| 1 ---------------------------------------------------------------------------- | |
| 2 -- LuaJIT profiler. | |
| 3 -- | |
| 4 -- Copyright (C) 2005-2023 Mike Pall. All rights reserved. | |
| 5 -- Released under the MIT license. See Copyright Notice in luajit.h | |
| 6 ---------------------------------------------------------------------------- | |
| 7 -- | |
| 8 -- This module is a simple command line interface to the built-in | |
| 9 -- low-overhead profiler of LuaJIT. | |
| 10 -- | |
| 11 -- The lower-level API of the profiler is accessible via the "jit.profile" | |
| 12 -- module or the luaJIT_profile_* C API. | |
| 13 -- | |
| 14 -- Example usage: | |
| 15 -- | |
| 16 -- luajit -jp myapp.lua | |
| 17 -- luajit -jp=s myapp.lua | |
| 18 -- luajit -jp=-s myapp.lua | |
| 19 -- luajit -jp=vl myapp.lua | |
| 20 -- luajit -jp=G,profile.txt myapp.lua | |
| 21 -- | |
| 22 -- The following dump features are available: | |
| 23 -- | |
| 24 -- f Stack dump: function name, otherwise module:line. Default mode. | |
| 25 -- F Stack dump: ditto, but always prepend module. | |
| 26 -- l Stack dump: module:line. | |
| 27 -- <number> stack dump depth (callee < caller). Default: 1. | |
| 28 -- -<number> Inverse stack dump depth (caller > callee). | |
| 29 -- s Split stack dump after first stack level. Implies abs(depth) >= 2. | |
| 30 -- p Show full path for module names. | |
| 31 -- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. | |
| 32 -- z Show zones. Can be combined with stack dumps, e.g. zf or fz. | |
| 33 -- r Show raw sample counts. Default: show percentages. | |
| 34 -- a Annotate excerpts from source code files. | |
| 35 -- A Annotate complete source code files. | |
| 36 -- G Produce raw output suitable for graphical tools (e.g. flame graphs). | |
| 37 -- m<number> Minimum sample percentage to be shown. Default: 3. | |
| 38 -- i<number> Sampling interval in milliseconds. Default: 10. | |
| 39 -- | |
| 40 ---------------------------------------------------------------------------- | |
| 41 | |
| 42 -- Cache some library functions and objects. | |
| 43 local jit = require("jit") | |
| 44 local profile = require("jit.profile") | |
| 45 local vmdef = require("jit.vmdef") | |
| 46 local math = math | |
| 47 local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor | |
| 48 local sort, format = table.sort, string.format | |
| 49 local stdout = io.stdout | |
| 50 local zone -- Load jit.zone module on demand. | |
| 51 | |
| 52 -- Output file handle. | |
| 53 local out | |
| 54 | |
| 55 ------------------------------------------------------------------------------ | |
| 56 | |
| 57 local prof_ud | |
| 58 local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth | |
| 59 local prof_ann, prof_count1, prof_count2, prof_samples | |
| 60 | |
| 61 local map_vmmode = { | |
| 62 N = "Compiled", | |
| 63 I = "Interpreted", | |
| 64 C = "C code", | |
| 65 G = "Garbage Collector", | |
| 66 J = "JIT Compiler", | |
| 67 } | |
| 68 | |
| 69 -- Profiler callback. | |
| 70 local function prof_cb(th, samples, vmmode) | |
| 71 prof_samples = prof_samples + samples | |
| 72 local key_stack, key_stack2, key_state | |
| 73 -- Collect keys for sample. | |
| 74 if prof_states then | |
| 75 if prof_states == "v" then | |
| 76 key_state = map_vmmode[vmmode] or vmmode | |
| 77 else | |
| 78 key_state = zone:get() or "(none)" | |
| 79 end | |
| 80 end | |
| 81 if prof_fmt then | |
| 82 key_stack = profile.dumpstack(th, prof_fmt, prof_depth) | |
| 83 key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) | |
| 84 return vmdef.ffnames[tonumber(x)] | |
| 85 end) | |
| 86 if prof_split == 2 then | |
| 87 local k1, k2 = key_stack:match("(.-) [<>] (.*)") | |
| 88 if k2 then key_stack, key_stack2 = k1, k2 end | |
| 89 elseif prof_split == 3 then | |
| 90 key_stack2 = profile.dumpstack(th, "l", 1) | |
| 91 end | |
| 92 end | |
| 93 -- Order keys. | |
| 94 local k1, k2 | |
| 95 if prof_split == 1 then | |
| 96 if key_state then | |
| 97 k1 = key_state | |
| 98 if key_stack then k2 = key_stack end | |
| 99 end | |
| 100 elseif key_stack then | |
| 101 k1 = key_stack | |
| 102 if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end | |
| 103 end | |
| 104 -- Coalesce samples in one or two levels. | |
| 105 if k1 then | |
| 106 local t1 = prof_count1 | |
| 107 t1[k1] = (t1[k1] or 0) + samples | |
| 108 if k2 then | |
| 109 local t2 = prof_count2 | |
| 110 local t3 = t2[k1] | |
| 111 if not t3 then t3 = {}; t2[k1] = t3 end | |
| 112 t3[k2] = (t3[k2] or 0) + samples | |
| 113 end | |
| 114 end | |
| 115 end | |
| 116 | |
| 117 ------------------------------------------------------------------------------ | |
| 118 | |
| 119 -- Show top N list. | |
| 120 local function prof_top(count1, count2, samples, indent) | |
| 121 local t, n = {}, 0 | |
| 122 for k in pairs(count1) do | |
| 123 n = n + 1 | |
| 124 t[n] = k | |
| 125 end | |
| 126 sort(t, function(a, b) return count1[a] > count1[b] end) | |
| 127 for i=1,n do | |
| 128 local k = t[i] | |
| 129 local v = count1[k] | |
| 130 local pct = floor(v*100/samples + 0.5) | |
| 131 if pct < prof_min then break end | |
| 132 if not prof_raw then | |
| 133 out:write(format("%s%2d%% %s\n", indent, pct, k)) | |
| 134 elseif prof_raw == "r" then | |
| 135 out:write(format("%s%5d %s\n", indent, v, k)) | |
| 136 else | |
| 137 out:write(format("%s %d\n", k, v)) | |
| 138 end | |
| 139 if count2 then | |
| 140 local r = count2[k] | |
| 141 if r then | |
| 142 prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or | |
| 143 (prof_depth < 0 and " -> " or " <- ")) | |
| 144 end | |
| 145 end | |
| 146 end | |
| 147 end | |
| 148 | |
| 149 -- Annotate source code | |
| 150 local function prof_annotate(count1, samples) | |
| 151 local files = {} | |
| 152 local ms = 0 | |
| 153 for k, v in pairs(count1) do | |
| 154 local pct = floor(v*100/samples + 0.5) | |
| 155 ms = math.max(ms, v) | |
| 156 if pct >= prof_min then | |
| 157 local file, line = k:match("^(.*):(%d+)$") | |
| 158 if not file then file = k; line = 0 end | |
| 159 local fl = files[file] | |
| 160 if not fl then fl = {}; files[file] = fl; files[#files+1] = file end | |
| 161 line = tonumber(line) | |
| 162 fl[line] = prof_raw and v or pct | |
| 163 end | |
| 164 end | |
| 165 sort(files) | |
| 166 local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" | |
| 167 if prof_raw then | |
| 168 local n = math.max(5, math.ceil(math.log10(ms))) | |
| 169 fmtv = "%"..n.."d | %s\n" | |
| 170 fmtn = (" "):rep(n).." | %s\n" | |
| 171 end | |
| 172 local ann = prof_ann | |
| 173 for _, file in ipairs(files) do | |
| 174 local f0 = file:byte() | |
| 175 if f0 == 40 or f0 == 91 then | |
| 176 out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) | |
| 177 break | |
| 178 end | |
| 179 local fp, err = io.open(file) | |
| 180 if not fp then | |
| 181 out:write(format("====== ERROR: %s: %s\n", file, err)) | |
| 182 break | |
| 183 end | |
| 184 out:write(format("\n====== %s ======\n", file)) | |
| 185 local fl = files[file] | |
| 186 local n, show = 1, false | |
| 187 if ann ~= 0 then | |
| 188 for i=1,ann do | |
| 189 if fl[i] then show = true; out:write("@@ 1 @@\n"); break end | |
| 190 end | |
| 191 end | |
| 192 for line in fp:lines() do | |
| 193 if line:byte() == 27 then | |
| 194 out:write("[Cannot annotate bytecode file]\n") | |
| 195 break | |
| 196 end | |
| 197 local v = fl[n] | |
| 198 if ann ~= 0 then | |
| 199 local v2 = fl[n+ann] | |
| 200 if show then | |
| 201 if v2 then show = n+ann elseif v then show = n | |
| 202 elseif show+ann < n then show = false end | |
| 203 elseif v2 then | |
| 204 show = n+ann | |
| 205 out:write(format("@@ %d @@\n", n)) | |
| 206 end | |
| 207 if not show then goto next end | |
| 208 end | |
| 209 if v then | |
| 210 out:write(format(fmtv, v, line)) | |
| 211 else | |
| 212 out:write(format(fmtn, line)) | |
| 213 end | |
| 214 ::next:: | |
| 215 n = n + 1 | |
| 216 end | |
| 217 fp:close() | |
| 218 end | |
| 219 end | |
| 220 | |
| 221 ------------------------------------------------------------------------------ | |
| 222 | |
| 223 -- Finish profiling and dump result. | |
| 224 local function prof_finish() | |
| 225 if prof_ud then | |
| 226 profile.stop() | |
| 227 local samples = prof_samples | |
| 228 if samples == 0 then | |
| 229 if prof_raw ~= true then out:write("[No samples collected]\n") end | |
| 230 return | |
| 231 end | |
| 232 if prof_ann then | |
| 233 prof_annotate(prof_count1, samples) | |
| 234 else | |
| 235 prof_top(prof_count1, prof_count2, samples, "") | |
| 236 end | |
| 237 prof_count1 = nil | |
| 238 prof_count2 = nil | |
| 239 prof_ud = nil | |
| 240 if out ~= stdout then out:close() end | |
| 241 end | |
| 242 end | |
| 243 | |
| 244 -- Start profiling. | |
| 245 local function prof_start(mode) | |
| 246 local interval = "" | |
| 247 mode = mode:gsub("i%d*", function(s) interval = s; return "" end) | |
| 248 prof_min = 3 | |
| 249 mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end) | |
| 250 prof_depth = 1 | |
| 251 mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) | |
| 252 local m = {} | |
| 253 for c in mode:gmatch(".") do m[c] = c end | |
| 254 prof_states = m.z or m.v | |
| 255 if prof_states == "z" then zone = require("jit.zone") end | |
| 256 local scope = m.l or m.f or m.F or (prof_states and "" or "f") | |
| 257 local flags = (m.p or "") | |
| 258 prof_raw = m.r | |
| 259 if m.s then | |
| 260 prof_split = 2 | |
| 261 if prof_depth == -1 or m["-"] then prof_depth = -2 | |
| 262 elseif prof_depth == 1 then prof_depth = 2 end | |
| 263 elseif mode:find("[fF].*l") then | |
| 264 scope = "l" | |
| 265 prof_split = 3 | |
| 266 else | |
| 267 prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 | |
| 268 end | |
| 269 prof_ann = m.A and 0 or (m.a and 3) | |
| 270 if prof_ann then | |
| 271 scope = "l" | |
| 272 prof_fmt = "pl" | |
| 273 prof_split = 0 | |
| 274 prof_depth = 1 | |
| 275 elseif m.G and scope ~= "" then | |
| 276 prof_fmt = flags..scope.."Z;" | |
| 277 prof_depth = -100 | |
| 278 prof_raw = true | |
| 279 prof_min = 0 | |
| 280 elseif scope == "" then | |
| 281 prof_fmt = false | |
| 282 else | |
| 283 local sc = prof_split == 3 and m.f or m.F or scope | |
| 284 prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ") | |
| 285 end | |
| 286 prof_count1 = {} | |
| 287 prof_count2 = {} | |
| 288 prof_samples = 0 | |
| 289 profile.start(scope:lower()..interval, prof_cb) | |
| 290 prof_ud = newproxy(true) | |
| 291 getmetatable(prof_ud).__gc = prof_finish | |
| 292 end | |
| 293 | |
| 294 ------------------------------------------------------------------------------ | |
| 295 | |
| 296 local function start(mode, outfile) | |
| 297 if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end | |
| 298 if outfile then | |
| 299 out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | |
| 300 else | |
| 301 out = stdout | |
| 302 end | |
| 303 prof_start(mode or "f") | |
| 304 end | |
| 305 | |
| 306 -- Public module functions. | |
| 307 return { | |
| 308 start = start, -- For -j command line option. | |
| 309 stop = prof_finish | |
| 310 } | |
| 311 |