diff options
-rw-r--r-- | src/jit/p.lua | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/jit/p.lua b/src/jit/p.lua new file mode 100644 index 00000000..a93172da --- /dev/null +++ b/src/jit/p.lua | |||
@@ -0,0 +1,216 @@ | |||
1 | ---------------------------------------------------------------------------- | ||
2 | -- LuaJIT profiler. | ||
3 | -- | ||
4 | -- Copyright (C) 2005-2013 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 | -- G Produce output suitable for graphical tools (e.g. flame graphs). | ||
35 | -- n<number> Show top N samples. Default: 10. | ||
36 | -- i<number> Sampling interval in milliseconds. Default: 10. | ||
37 | -- | ||
38 | ---------------------------------------------------------------------------- | ||
39 | |||
40 | -- Cache some library functions and objects. | ||
41 | local jit = require("jit") | ||
42 | assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") | ||
43 | local profile = require("jit.profile") | ||
44 | local vmdef = require("jit.vmdef") | ||
45 | local pairs, tonumber, floor, min = pairs, tonumber, math.floor, math.min | ||
46 | local sort, format = table.sort, string.format | ||
47 | local stdout = io.stdout | ||
48 | local zone -- Load jit.zone module on demand. | ||
49 | |||
50 | -- Output file handle. | ||
51 | local out | ||
52 | |||
53 | ------------------------------------------------------------------------------ | ||
54 | |||
55 | local prof_ud | ||
56 | local prof_states, prof_split, prof_maxn, prof_raw, prof_fmt, prof_depth | ||
57 | local prof_count1, prof_count2, prof_samples | ||
58 | |||
59 | local map_vmmode = { | ||
60 | N = "Compiled", | ||
61 | I = "Interpreted", | ||
62 | C = "C code", | ||
63 | G = "Garbage Collector", | ||
64 | J = "JIT Compiler", | ||
65 | } | ||
66 | |||
67 | -- Profiler callback. | ||
68 | local function prof_cb(th, samples, vmmode) | ||
69 | prof_samples = prof_samples + samples | ||
70 | local key_stack, key_stack2, key_state | ||
71 | -- Collect keys for sample. | ||
72 | if prof_states then | ||
73 | if prof_states == "v" then | ||
74 | key_state = map_vmmode[vmmode] or vmmode | ||
75 | else | ||
76 | key_state = zone:get() or "(none)" | ||
77 | end | ||
78 | end | ||
79 | if prof_fmt then | ||
80 | key_stack = profile.dumpstack(th, prof_fmt, prof_depth) | ||
81 | key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) | ||
82 | return vmdef.ffnames[tonumber(x)] | ||
83 | end) | ||
84 | if prof_split == 2 then | ||
85 | local k1, k2 = key_stack:match("(.-) [<>] (.*)") | ||
86 | if k2 then key_stack, key_stack2 = k1, k2 end | ||
87 | end | ||
88 | end | ||
89 | -- Order keys. | ||
90 | local k1, k2 | ||
91 | if prof_split == 1 then | ||
92 | if key_state then | ||
93 | k1 = key_state | ||
94 | if key_stack then k2 = key_stack end | ||
95 | end | ||
96 | elseif key_stack then | ||
97 | k1 = key_stack | ||
98 | if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end | ||
99 | end | ||
100 | -- Coalesce samples in one or two levels. | ||
101 | if k1 then | ||
102 | local t1 = prof_count1 | ||
103 | t1[k1] = (t1[k1] or 0) + samples | ||
104 | if k2 then | ||
105 | local t2 = prof_count2 | ||
106 | local t3 = t2[k1] | ||
107 | if not t3 then t3 = {}; t2[k1] = t3 end | ||
108 | t3[k2] = (t3[k2] or 0) + samples | ||
109 | end | ||
110 | end | ||
111 | end | ||
112 | |||
113 | ------------------------------------------------------------------------------ | ||
114 | |||
115 | -- Show top N list. | ||
116 | local function prof_top(count1, count2, samples, indent) | ||
117 | local t, n = {}, 0 | ||
118 | for k, v in pairs(count1) do | ||
119 | n = n + 1 | ||
120 | t[n] = k | ||
121 | end | ||
122 | if not t[1] then return end | ||
123 | sort(t, function(a, b) return count1[a] > count1[b] end) | ||
124 | local raw = prof_raw | ||
125 | for i=1,min(n, prof_maxn) do | ||
126 | local k = t[i] | ||
127 | local v = count1[k] | ||
128 | if not raw then | ||
129 | out:write(format("%s%2d%% %s\n", indent, floor(v*100/samples + 0.5), k)) | ||
130 | elseif raw == "r" then | ||
131 | out:write(format("%s%5d %s\n", indent, v, k)) | ||
132 | else | ||
133 | out:write(format("%s %d\n", k, v)) | ||
134 | end | ||
135 | if count2 then | ||
136 | local r = count2[k] | ||
137 | if r then | ||
138 | prof_top(r, nil, v, prof_depth < 0 and " -> " or " <- ") | ||
139 | end | ||
140 | end | ||
141 | end | ||
142 | end | ||
143 | |||
144 | ------------------------------------------------------------------------------ | ||
145 | |||
146 | -- Finish profiling and dump result. | ||
147 | local function prof_finish() | ||
148 | if prof_ud then | ||
149 | profile.stop() | ||
150 | local samples = prof_samples | ||
151 | if samples == 0 then return end | ||
152 | prof_top(prof_count1, prof_count2, samples, "") | ||
153 | prof_count1 = nil | ||
154 | prof_count2 = nil | ||
155 | prof_ud = nil | ||
156 | end | ||
157 | end | ||
158 | |||
159 | -- Start profiling. | ||
160 | local function prof_start(mode) | ||
161 | local interval = "" | ||
162 | mode = mode:gsub("i%d*", function(s) interval = s; return "" end) | ||
163 | prof_maxn = 10 | ||
164 | mode = mode:gsub("n(%d+)", function(s) prof_maxn = tonumber(s); return "" end) | ||
165 | prof_depth = 1 | ||
166 | mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) | ||
167 | local m = {} | ||
168 | for c in mode:gmatch(".") do m[c] = c end | ||
169 | prof_states = m.z or m.v | ||
170 | if prof_states == "z" then zone = require("jit.zone") end | ||
171 | local scope = m.l or m.f or m.F or (prof_states and "" or "f") | ||
172 | local flags = (m.p or "") | ||
173 | prof_raw = m.r | ||
174 | if m.s then | ||
175 | prof_split = 2 | ||
176 | if prof_depth == -1 or m["-"] then prof_depth = -2 | ||
177 | elseif prof_depth == 1 then prof_depth = 2 end | ||
178 | else | ||
179 | prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 | ||
180 | end | ||
181 | if m.G and scope ~= "" then | ||
182 | prof_fmt = flags..scope.."Z;" | ||
183 | prof_depth = -100 | ||
184 | prof_raw = true | ||
185 | prof_maxn = 2147483647 | ||
186 | elseif scope == "" then | ||
187 | prof_fmt = false | ||
188 | else | ||
189 | prof_fmt = flags..scope..(prof_depth >= 0 and "Z < " or "Z > ") | ||
190 | end | ||
191 | prof_count1 = {} | ||
192 | prof_count2 = {} | ||
193 | prof_samples = 0 | ||
194 | profile.start(scope:lower()..interval, prof_cb) | ||
195 | prof_ud = newproxy(true) | ||
196 | getmetatable(prof_ud).__gc = prof_finish | ||
197 | end | ||
198 | |||
199 | ------------------------------------------------------------------------------ | ||
200 | |||
201 | local function start(mode, outfile) | ||
202 | if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end | ||
203 | if outfile then | ||
204 | out = outfile == "-" and stdout or assert(io.open(outfile, "w")) | ||
205 | else | ||
206 | out = stdout | ||
207 | end | ||
208 | prof_start(mode or "f") | ||
209 | end | ||
210 | |||
211 | -- Public module functions. | ||
212 | return { | ||
213 | start = start, -- For -j command line option. | ||
214 | stop = prof_finish | ||
215 | } | ||
216 | |||