OLD | NEW |
---|---|
1 -- Copyright 2011 the V8 project authors. All rights reserved. | 1 -- Copyright 2011 the V8 project authors. All rights reserved. |
2 -- Redistribution and use in source and binary forms, with or without | 2 -- Redistribution and use in source and binary forms, with or without |
3 -- modification, are permitted provided that the following conditions are | 3 -- modification, are permitted provided that the following conditions are |
4 -- met: | 4 -- met: |
5 -- | 5 -- |
6 -- * Redistributions of source code must retain the above copyright | 6 -- * Redistributions of source code must retain the above copyright |
7 -- notice, this list of conditions and the following disclaimer. | 7 -- notice, this list of conditions and the following disclaimer. |
8 -- * Redistributions in binary form must reproduce the above | 8 -- * Redistributions in binary form must reproduce the above |
9 -- copyright notice, this list of conditions and the following | 9 -- copyright notice, this list of conditions and the following |
10 -- disclaimer in the documentation and/or other materials provided | 10 -- disclaimer in the documentation and/or other materials provided |
(...skipping 11 matching lines...) Expand all Loading... | |
22 -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 -- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 -- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 -- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 -- This is main driver for gcmole tool. See README for more details. | 28 -- This is main driver for gcmole tool. See README for more details. |
29 -- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|x64] | 29 -- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|x64] |
30 | 30 |
31 local DIR = arg[0]:match("^(.+)/[^/]+$") | 31 local DIR = arg[0]:match("^(.+)/[^/]+$") |
32 | 32 |
33 local ARCHS = arg[1] and { arg[1] } or { 'ia32', 'arm', 'x64' } | 33 local FLAGS = { |
34 -- Do not build gcsuspects file and reuse previously generated one. | |
35 reuse_gcsuspects = false; | |
36 | |
37 -- Print commands to console before executing them. | |
38 verbose = false; | |
39 | |
40 -- Perform dead variable analysis (generates many false positives). | |
41 -- TODO add some sort of whiteliste to filter out false positives. | |
42 dead_vars = false; | |
43 | |
44 -- When building gcsuspects whitelist certain functions as if they | |
45 -- can be causing GC. Currently used to reduce number of false | |
46 -- positives in dead variables analysis. See TODO for WHITELIST | |
47 -- below. | |
48 whitelist = true; | |
49 } | |
50 local ARGS = {} | |
51 | |
52 for i = 1, #arg do | |
53 local flag = arg[i]:match "^%-%-([%w_-]+)$" | |
54 if flag then | |
55 local no, real_flag = flag:match "^(no)([%w_-]+)$" | |
56 if real_flag then flag = real_flag end | |
57 | |
58 flag = flag:gsub("%-", "_") | |
59 if FLAGS[flag] ~= nil then | |
60 » FLAGS[flag] = (no ~= "no") | |
Mads Ager (chromium)
2011/05/16 06:10:27
Tabs in the file. Here and later.
| |
61 else | |
62 » error("Unknown flag: " .. flag) | |
63 end | |
64 else | |
65 table.insert(ARGS, arg[i]) | |
66 end | |
67 end | |
68 | |
69 local ARCHS = ARGS[1] and { ARGS[1] } or { 'ia32', 'arm', 'x64' } | |
34 | 70 |
35 local io = require "io" | 71 local io = require "io" |
36 local os = require "os" | 72 local os = require "os" |
37 | 73 |
38 function log(...) | 74 function log(...) |
39 io.stderr:write(string.format(...)) | 75 io.stderr:write(string.format(...)) |
40 io.stderr:write "\n" | 76 io.stderr:write "\n" |
41 end | 77 end |
42 | 78 |
43 ------------------------------------------------------------------------------- | 79 ------------------------------------------------------------------------------- |
44 -- Clang invocation | 80 -- Clang invocation |
45 | 81 |
46 local CLANG_BIN = os.getenv "CLANG_BIN" | 82 local CLANG_BIN = os.getenv "CLANG_BIN" |
47 | 83 |
48 if not CLANG_BIN or CLANG_BIN == "" then | 84 if not CLANG_BIN or CLANG_BIN == "" then |
49 error "CLANG_BIN not set" | 85 error "CLANG_BIN not set" |
50 end | 86 end |
51 | 87 |
52 local function MakeClangCommandLine(plugin, triple, arch_define) | 88 local function MakeClangCommandLine(plugin, plugin_args, triple, arch_define) |
53 return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so" | 89 if plugin_args then |
90 for i = 1, #plugin_args do | |
91 » plugin_args[i] = "-plugin-arg-" .. plugin .. " " .. plugin_args[i] | |
92 end | |
93 plugin_args = " " .. table.concat(plugin_args, " ") | |
94 end | |
95 return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so" | |
54 .. " -plugin " .. plugin | 96 .. " -plugin " .. plugin |
55 .. " -triple " .. triple | 97 .. (plugin_args or "") |
98 .. " -triple " .. triple | |
56 .. " -D" .. arch_define | 99 .. " -D" .. arch_define |
57 .. " -DENABLE_VMSTATE_TRACKING" | 100 .. " -DENABLE_VMSTATE_TRACKING" |
58 .. " -DENABLE_LOGGING_AND_PROFILING" | 101 .. " -DENABLE_LOGGING_AND_PROFILING" |
59 .. " -DENABLE_DEBUGGER_SUPPORT" | 102 .. " -DENABLE_DEBUGGER_SUPPORT" |
60 .. " -Isrc" | 103 .. " -Isrc" |
61 end | 104 end |
62 | 105 |
63 function InvokeClangPluginForEachFile(filenames, cfg, func) | 106 function InvokeClangPluginForEachFile(filenames, cfg, func) |
64 local cmd_line = MakeClangCommandLine(cfg.plugin, | 107 local cmd_line = MakeClangCommandLine(cfg.plugin, |
108 cfg.plugin_args, | |
65 cfg.triple, | 109 cfg.triple, |
66 cfg.arch_define) | 110 cfg.arch_define) |
67 | 111 |
68 for _, filename in ipairs(filenames) do | 112 for _, filename in ipairs(filenames) do |
69 log("-- %s", filename) | 113 log("-- %s", filename) |
70 | |
71 local action = cmd_line .. " src/" .. filename .. " 2>&1" | 114 local action = cmd_line .. " src/" .. filename .. " 2>&1" |
72 | 115 if FLAGS.verbose then print('popen ', action) end |
73 local pipe = io.popen(action) | 116 local pipe = io.popen(action) |
74 func(filename, pipe:lines()) | 117 func(filename, pipe:lines()) |
75 pipe:close() | 118 pipe:close() |
76 end | 119 end |
77 end | 120 end |
78 | 121 |
79 ------------------------------------------------------------------------------- | 122 ------------------------------------------------------------------------------- |
80 -- SConscript parsing | 123 -- SConscript parsing |
81 | 124 |
82 local function ParseSConscript() | 125 local function ParseSConscript() |
83 local f = assert(io.open("src/SConscript"), "failed to open SConscript") | 126 local f = assert(io.open("src/SConscript"), "failed to open SConscript") |
84 local sconscript = f:read('*a') | 127 local sconscript = f:read('*a') |
85 f:close() | 128 f:close() |
86 | 129 |
87 local SOURCES = sconscript:match "SOURCES = {(.-)}"; | 130 local SOURCES = sconscript:match "SOURCES = {(.-)}"; |
88 | 131 |
89 local sources = {} | 132 local sources = {} |
90 | 133 |
91 for condition, list in | 134 for condition, list in |
92 SOURCES:gmatch "'([^']-)': Split%(\"\"\"(.-)\"\"\"%)" do | 135 SOURCES:gmatch "'([^']-)': Split%(\"\"\"(.-)\"\"\"%)" do |
93 local files = {} | 136 local files = {} |
94 for file in list:gmatch "[^%s]+" do table.insert(files, file) end | 137 for file in list:gmatch "[^%s]+" do table.insert(files, file) end |
95 sources[condition] = files | 138 sources[condition] = files |
96 end | 139 end |
97 | 140 |
98 for condition, list in SOURCES:gmatch "'([^']-)': %[(.-)%]" do | 141 for condition, list in SOURCES:gmatch "'([^']-)': %[(.-)%]" do |
99 local files = {} | 142 local files = {} |
100 for file in list:gmatch "'([^']-)'" do table.insert(files, file) end | 143 for file in list:gmatch "'([^']-)'" do table.insert(files, file) end |
101 sources[condition] = files | 144 sources[condition] = files |
102 end | 145 end |
103 | 146 |
104 return sources | 147 return sources |
105 end | 148 end |
106 | 149 |
107 local function EvaluateCondition(cond, props) | 150 local function EvaluateCondition(cond, props) |
108 if cond == 'all' then return true end | 151 if cond == 'all' then return true end |
109 | 152 |
110 local p, v = cond:match "(%w+):(%w+)" | 153 local p, v = cond:match "(%w+):(%w+)" |
111 | 154 |
112 assert(p and v, "failed to parse condition: " .. cond) | 155 assert(p and v, "failed to parse condition: " .. cond) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
150 local ARCHITECTURES = { | 193 local ARCHITECTURES = { |
151 ia32 = config { triple = "i586-unknown-linux", | 194 ia32 = config { triple = "i586-unknown-linux", |
152 arch_define = "V8_TARGET_ARCH_IA32" }, | 195 arch_define = "V8_TARGET_ARCH_IA32" }, |
153 arm = config { triple = "i586-unknown-linux", | 196 arm = config { triple = "i586-unknown-linux", |
154 arch_define = "V8_TARGET_ARCH_ARM" }, | 197 arch_define = "V8_TARGET_ARCH_ARM" }, |
155 x64 = config { triple = "x86_64-unknown-linux", | 198 x64 = config { triple = "x86_64-unknown-linux", |
156 arch_define = "V8_TARGET_ARCH_X64" } | 199 arch_define = "V8_TARGET_ARCH_X64" } |
157 } | 200 } |
158 | 201 |
159 ------------------------------------------------------------------------------- | 202 ------------------------------------------------------------------------------- |
160 -- GCSuspects Generation | 203 -- GCSuspects Generation |
161 | 204 |
162 local gc = {} | 205 local gc, gc_caused, funcs |
163 local funcs = {} | 206 |
207 local WHITELIST = { | |
208 -- The following functions call CEntryStub which is always present. | |
209 "MacroAssembler.*CallExternalReference", | |
210 "MacroAssembler.*CallRuntime", | |
211 "CompileCallLoadPropertyWithInterceptor", | |
212 "CallIC.*GenerateMiss", | |
213 | |
214 -- DirectCEntryStub is a special stub used on ARM. | |
215 -- It is pinned and always present. | |
216 "DirectCEntryStub.*GenerateCall", | |
217 | |
218 -- TODO GCMole currently is sensitive enough to understand that certain | |
219 -- functions only cause GC and return Failure simulataneously. | |
220 -- Callsites of such functions are safe as long as they are properly | |
221 -- check return value and propagate the Failure to the caller. | |
222 -- It should be possible to extend GCMole to understand this. | |
223 "Heap.*AllocateFunctionPrototype" | |
224 }; | |
225 | |
226 local function AddCause(name, cause) | |
227 local t = gc_caused[name] | |
228 if not t then | |
229 t = {} | |
230 gc_caused[name] = t | |
231 end | |
232 table.insert(t, cause) | |
233 end | |
164 | 234 |
165 local function resolve(name) | 235 local function resolve(name) |
166 local f = funcs[name] | 236 local f = funcs[name] |
167 | 237 |
168 if not f then | 238 if not f then |
169 f = {} | 239 f = {} |
170 funcs[name] = f | 240 funcs[name] = f |
171 | 241 |
172 if name:match "Collect.*Garbage" then gc[name] = true end | 242 if name:match "Collect.*Garbage" then |
243 » gc[name] = true | |
244 » AddCause(name, "<GC>") | |
245 end | |
246 | |
247 if FLAGS.whitelist then | |
248 » for i = 1, #WHITELIST do | |
249 » if name:match(WHITELIST[i]) then | |
250 » gc[name] = false | |
251 » end | |
252 » end | |
253 end | |
173 end | 254 end |
174 | 255 |
175 return f | 256 return f |
176 end | 257 end |
177 | 258 |
178 local function parse (filename, lines) | 259 local function parse (filename, lines) |
179 local scope | 260 local scope |
180 | 261 |
181 for funcname in lines do | 262 for funcname in lines do |
182 if funcname:sub(1, 1) ~= '\t' then | 263 if funcname:sub(1, 1) ~= '\t' then |
183 resolve(funcname) | 264 resolve(funcname) |
184 scope = funcname | 265 scope = funcname |
185 else | 266 else |
186 local name = funcname:sub(2) | 267 local name = funcname:sub(2) |
187 resolve(name)[scope] = true | 268 resolve(name)[scope] = true |
188 end | 269 end |
189 end | 270 end |
190 end | 271 end |
191 | 272 |
192 local function propagate () | 273 local function propagate () |
193 log "** Propagating GC information" | 274 log "** Propagating GC information" |
194 | 275 |
195 local function mark(callers) | 276 local function mark(from, callers) |
196 for caller, _ in pairs(callers) do | 277 for caller, _ in pairs(callers) do |
197 » if not gc[caller] then | 278 » if gc[caller] == nil then |
198 gc[caller] = true | 279 gc[caller] = true |
199 » mark(funcs[caller]) | 280 » mark(caller, funcs[caller]) |
200 end | 281 end |
282 AddCause(caller, from) | |
201 end | 283 end |
202 end | 284 end |
203 | 285 |
204 for funcname, callers in pairs(funcs) do | 286 for funcname, callers in pairs(funcs) do |
205 if gc[funcname] then mark(callers) end | 287 if gc[funcname] then mark(funcname, callers) end |
206 end | 288 end |
207 end | 289 end |
208 | 290 |
209 local function GenerateGCSuspects(arch, files, cfg) | 291 local function GenerateGCSuspects(arch, files, cfg) |
292 -- Reset the global state. | |
293 gc, gc_caused, funcs = {}, {}, {} | |
294 | |
210 log ("** Building GC Suspects for %s", arch) | 295 log ("** Building GC Suspects for %s", arch) |
211 InvokeClangPluginForEachFile (files, | 296 InvokeClangPluginForEachFile (files, |
212 cfg:extend { plugin = "dump-callees" }, | 297 cfg:extend { plugin = "dump-callees" }, |
213 parse) | 298 parse) |
214 | 299 |
215 propagate() | 300 propagate() |
216 | 301 |
217 local out = assert(io.open("gcsuspects", "w")) | 302 local out = assert(io.open("gcsuspects", "w")) |
218 for name, _ in pairs(gc) do out:write (name, '\n') end | 303 for name, value in pairs(gc) do if value then out:write (name, '\n') end end |
219 out:close() | 304 out:close() |
305 | |
306 local out = assert(io.open("gccauses", "w")) | |
307 out:write "GC = {" | |
308 for name, causes in pairs(gc_caused) do | |
309 out:write("['", name, "'] = {") | |
310 for i = 1, #causes do out:write ("'", causes[i], "';") end | |
311 out:write("};\n") | |
312 end | |
313 out:write "}" | |
314 out:close() | |
315 | |
220 log ("** GCSuspects generated for %s", arch) | 316 log ("** GCSuspects generated for %s", arch) |
221 end | 317 end |
222 | 318 |
223 ------------------------------------------------------------------------------- | 319 -------------------------------------------------------------------------------- |
224 -- Analysis | 320 -- Analysis |
225 | 321 |
226 local function CheckCorrectnessForArch(arch) | 322 local function CheckCorrectnessForArch(arch) |
227 local files = FilesForArch(arch) | 323 local files = FilesForArch(arch) |
228 local cfg = ARCHITECTURES[arch] | 324 local cfg = ARCHITECTURES[arch] |
229 | 325 |
230 GenerateGCSuspects(arch, files, cfg) | 326 if not FLAGS.reuse_gcsuspects then |
327 GenerateGCSuspects(arch, files, cfg) | |
328 end | |
231 | 329 |
232 local processed_files = 0 | 330 local processed_files = 0 |
233 local errors_found = false | 331 local errors_found = false |
234 local function SearchForErrors(filename, lines) | 332 local function SearchForErrors(filename, lines) |
235 processed_files = processed_files + 1 | 333 processed_files = processed_files + 1 |
236 for l in lines do | 334 for l in lines do |
237 errors_found = errors_found or | 335 errors_found = errors_found or |
238 l:match "^[^:]+:%d+:%d+:" or | 336 l:match "^[^:]+:%d+:%d+:" or |
239 l:match "error" or | 337 l:match "error" or |
240 l:match "warning" | 338 l:match "warning" |
241 print(l) | 339 print(l) |
242 end | 340 end |
243 end | 341 end |
244 | 342 |
245 log("** Searching for evaluation order problems for %s", arch) | 343 log("** Searching for evaluation order problems%s for %s", |
344 FLAGS.dead_vars and " and dead variables" or "", | |
345 arch) | |
346 local plugin_args | |
347 if FLAGS.dead_vars then plugin_args = { "--dead-vars" } end | |
246 InvokeClangPluginForEachFile(files, | 348 InvokeClangPluginForEachFile(files, |
247 » » » » cfg:extend { plugin = "find-problems" }, | 349 » » » » cfg:extend { plugin = "find-problems", |
350 » » » » » plugin_args = plugin_args }, | |
248 SearchForErrors) | 351 SearchForErrors) |
249 log("** Done processing %d files. %s", | 352 log("** Done processing %d files. %s", |
250 processed_files, | 353 processed_files, |
251 errors_found and "Errors found" or "No errors found") | 354 errors_found and "Errors found" or "No errors found") |
252 | 355 |
253 return errors_found | 356 return errors_found |
254 end | 357 end |
255 | 358 |
256 local function SafeCheckCorrectnessForArch(arch) | 359 local function SafeCheckCorrectnessForArch(arch) |
257 local status, errors = pcall(CheckCorrectnessForArch, arch) | 360 local status, errors = pcall(CheckCorrectnessForArch, arch) |
258 if not status then | 361 if not status then |
259 print(string.format("There was an error: %s", errors)) | 362 print(string.format("There was an error: %s", errors)) |
260 errors = true | 363 errors = true |
261 end | 364 end |
262 return errors | 365 return errors |
263 end | 366 end |
264 | 367 |
265 local errors = false | 368 local errors = false |
266 | 369 |
267 for _, arch in ipairs(ARCHS) do | 370 for _, arch in ipairs(ARCHS) do |
268 if not ARCHITECTURES[arch] then | 371 if not ARCHITECTURES[arch] then |
269 error ("Unknown arch: " .. arch) | 372 error ("Unknown arch: " .. arch) |
270 end | 373 end |
271 | 374 |
272 errors = SafeCheckCorrectnessForArch(arch, report) or errors | 375 errors = SafeCheckCorrectnessForArch(arch, report) or errors |
273 end | 376 end |
274 | 377 |
275 os.exit(errors and 1 or 0) | 378 os.exit(errors and 1 or 0) |
OLD | NEW |