diff options
| -rw-r--r-- | src/jit/p.lua | 95 |
1 files changed, 88 insertions, 7 deletions
diff --git a/src/jit/p.lua b/src/jit/p.lua index 90b50bca..f3bec9f0 100644 --- a/src/jit/p.lua +++ b/src/jit/p.lua | |||
| @@ -31,6 +31,8 @@ | |||
| 31 | -- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. | 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. | 32 | -- z Show zones. Can be combined with stack dumps, e.g. zf or fz. |
| 33 | -- r Show raw sample counts. Default: show percentages. | 33 | -- r Show raw sample counts. Default: show percentages. |
| 34 | -- a Annotate excerpts from source code files. | ||
| 35 | -- A Annotate complete source code files. | ||
| 34 | -- G Produce output suitable for graphical tools (e.g. flame graphs). | 36 | -- G Produce output suitable for graphical tools (e.g. flame graphs). |
| 35 | -- m<number> Minimum sample percentage to be shown. Default: 3. | 37 | -- m<number> Minimum sample percentage to be shown. Default: 3. |
| 36 | -- i<number> Sampling interval in milliseconds. Default: 10. | 38 | -- i<number> Sampling interval in milliseconds. Default: 10. |
| @@ -42,7 +44,8 @@ local jit = require("jit") | |||
| 42 | assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") | 44 | assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
| 43 | local profile = require("jit.profile") | 45 | local profile = require("jit.profile") |
| 44 | local vmdef = require("jit.vmdef") | 46 | local vmdef = require("jit.vmdef") |
| 45 | local pairs, tonumber, floor, min = pairs, tonumber, math.floor, math.min | 47 | local math = math |
| 48 | local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor | ||
| 46 | local sort, format = table.sort, string.format | 49 | local sort, format = table.sort, string.format |
| 47 | local stdout = io.stdout | 50 | local stdout = io.stdout |
| 48 | local zone -- Load jit.zone module on demand. | 51 | local zone -- Load jit.zone module on demand. |
| @@ -54,7 +57,7 @@ local out | |||
| 54 | 57 | ||
| 55 | local prof_ud | 58 | local prof_ud |
| 56 | local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth | 59 | local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth |
| 57 | local prof_count1, prof_count2, prof_samples | 60 | local prof_ann, prof_count1, prof_count2, prof_samples |
| 58 | 61 | ||
| 59 | local map_vmmode = { | 62 | local map_vmmode = { |
| 60 | N = "Compiled", | 63 | N = "Compiled", |
| @@ -120,15 +123,14 @@ local function prof_top(count1, count2, samples, indent) | |||
| 120 | t[n] = k | 123 | t[n] = k |
| 121 | end | 124 | end |
| 122 | sort(t, function(a, b) return count1[a] > count1[b] end) | 125 | sort(t, function(a, b) return count1[a] > count1[b] end) |
| 123 | local raw = prof_raw | ||
| 124 | for i=1,n do | 126 | for i=1,n do |
| 125 | local k = t[i] | 127 | local k = t[i] |
| 126 | local v = count1[k] | 128 | local v = count1[k] |
| 127 | local pct = floor(v*100/samples + 0.5) | 129 | local pct = floor(v*100/samples + 0.5) |
| 128 | if pct < prof_min then break end | 130 | if pct < prof_min then break end |
| 129 | if not raw then | 131 | if not prof_raw then |
| 130 | out:write(format("%s%2d%% %s\n", indent, pct, k)) | 132 | out:write(format("%s%2d%% %s\n", indent, pct, k)) |
| 131 | elseif raw == "r" then | 133 | elseif prof_raw == "r" then |
| 132 | out:write(format("%s%5d %s\n", indent, v, k)) | 134 | out:write(format("%s%5d %s\n", indent, v, k)) |
| 133 | else | 135 | else |
| 134 | out:write(format("%s %d\n", k, v)) | 136 | out:write(format("%s %d\n", k, v)) |
| @@ -142,6 +144,75 @@ local function prof_top(count1, count2, samples, indent) | |||
| 142 | end | 144 | end |
| 143 | end | 145 | end |
| 144 | 146 | ||
| 147 | -- Annotate source code | ||
| 148 | local function prof_annotate(count1, samples) | ||
| 149 | local files = {} | ||
| 150 | local ms = 0 | ||
| 151 | for k, v in pairs(count1) do | ||
| 152 | local pct = floor(v*100/samples + 0.5) | ||
| 153 | ms = math.max(ms, v) | ||
| 154 | if pct >= prof_min then | ||
| 155 | local file, line = k:match("^(.*):(%d+)$") | ||
| 156 | local fl = files[file] | ||
| 157 | if not fl then fl = {}; files[file] = fl; files[#files+1] = file end | ||
| 158 | line = tonumber(line) | ||
| 159 | fl[line] = prof_raw and v or pct | ||
| 160 | end | ||
| 161 | end | ||
| 162 | sort(files) | ||
| 163 | local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" | ||
| 164 | if prof_raw then | ||
| 165 | local n = math.max(5, math.ceil(math.log10(ms))) | ||
| 166 | fmtv = "%"..n.."d | %s\n" | ||
| 167 | fmtn = (" "):rep(n).." | %s\n" | ||
| 168 | end | ||
| 169 | local ann = prof_ann | ||
| 170 | for _, file in ipairs(files) do | ||
| 171 | local f0 = file:byte() | ||
| 172 | if f0 == 40 or f0 == 91 then | ||
| 173 | out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) | ||
| 174 | break | ||
| 175 | end | ||
| 176 | local fp, err = io.open(file) | ||
| 177 | if not fp then | ||
| 178 | out:write(format("====== ERROR: %s: %s\n", file, err)) | ||
| 179 | break | ||
| 180 | end | ||
| 181 | out:write(format("\n====== %s ======\n", file)) | ||
| 182 | local fl = files[file] | ||
| 183 | local n, show = 1, false | ||
| 184 | if ann ~= 0 then | ||
| 185 | for i=1,ann do | ||
| 186 | if fl[i] then show = true; out:write("@@ 1 @@\n"); break end | ||
| 187 | end | ||
| 188 | end | ||
| 189 | for line in fp:lines() do | ||
| 190 | if line:byte() == 27 then | ||
| 191 | out:write("[Cannot annotate bytecode file]\n") | ||
| 192 | break | ||
| 193 | end | ||
| 194 | local v = fl[n] | ||
| 195 | if ann ~= 0 then | ||
| 196 | if show then | ||
| 197 | if v then show = n elseif show+ann < n then show = false end | ||
| 198 | elseif fl[n+ann] then | ||
| 199 | show = n+ann | ||
| 200 | out:write(format("@@ %d @@\n", n)) | ||
| 201 | end | ||
| 202 | if not show then goto next end | ||
| 203 | end | ||
| 204 | if v then | ||
| 205 | out:write(format(fmtv, v, line)) | ||
| 206 | else | ||
| 207 | out:write(format(fmtn, line)) | ||
| 208 | end | ||
| 209 | ::next:: | ||
| 210 | n = n + 1 | ||
| 211 | end | ||
| 212 | fp:close() | ||
| 213 | end | ||
| 214 | end | ||
| 215 | |||
| 145 | ------------------------------------------------------------------------------ | 216 | ------------------------------------------------------------------------------ |
| 146 | 217 | ||
| 147 | -- Finish profiling and dump result. | 218 | -- Finish profiling and dump result. |
| @@ -153,7 +224,11 @@ local function prof_finish() | |||
| 153 | if prof_raw ~= true then out:write("[no samples collected]\n") end | 224 | if prof_raw ~= true then out:write("[no samples collected]\n") end |
| 154 | return | 225 | return |
| 155 | end | 226 | end |
| 156 | prof_top(prof_count1, prof_count2, samples, "") | 227 | if prof_ann then |
| 228 | prof_annotate(prof_count1, samples) | ||
| 229 | else | ||
| 230 | prof_top(prof_count1, prof_count2, samples, "") | ||
| 231 | end | ||
| 157 | prof_count1 = nil | 232 | prof_count1 = nil |
| 158 | prof_count2 = nil | 233 | prof_count2 = nil |
| 159 | prof_ud = nil | 234 | prof_ud = nil |
| @@ -182,7 +257,13 @@ local function prof_start(mode) | |||
| 182 | else | 257 | else |
| 183 | prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 | 258 | prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 |
| 184 | end | 259 | end |
| 185 | if m.G and scope ~= "" then | 260 | prof_ann = m.A and 0 or (m.a and 3) |
| 261 | if prof_ann then | ||
| 262 | scope = "l" | ||
| 263 | prof_fmt = "pl" | ||
| 264 | prof_split = 0 | ||
| 265 | prof_depth = 1 | ||
| 266 | elseif m.G and scope ~= "" then | ||
| 186 | prof_fmt = flags..scope.."Z;" | 267 | prof_fmt = flags..scope.."Z;" |
| 187 | prof_depth = -100 | 268 | prof_depth = -100 |
| 188 | prof_raw = true | 269 | prof_raw = true |
