Chromium Code Reviews| 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 |