Commit 8578dfc6 authored by vegorov@chromium.org's avatar vegorov@chromium.org

Add GCMole to the repository.

GCMole is a simple static analysis tool that searches for GC-usafe evaluation order dependent callsites.

Review URL: http://codereview.chromium.org/6812002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7540 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent c9daea09
# Copyright 2011 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This is Makefile for clang plugin part of gcmole tool. See README.
LLVM_INCLUDE:=$(LLVM_SRC_ROOT)/include
CLANG_INCLUDE:=$(LLVM_SRC_ROOT)/tools/clang/include
libgcmole.so: gcmole.cc
g++ -I$(LLVM_INCLUDE) -I$(CLANG_INCLUDE) -I. -D_DEBUG -D_GNU_SOURCE \
-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -O3 \
-fomit-frame-pointer -fno-exceptions -fno-rtti -fPIC \
-Woverloaded-virtual -Wcast-qual -fno-strict-aliasing \
-pedantic -Wno-long-long -Wall \
-W -Wno-unused-parameter -Wwrite-strings \
-shared -o libgcmole.so gcmole.cc
clean:
rm libgcmole.so
DESCRIPTION -------------------------------------------------------------------
gcmole is a simple static analysis tool used to find possible evaluation order
dependent GC-unsafe places in the V8 codebase.
For example the following code is GC-unsafe:
Handle<Object> Foo(); // Assume Foo can trigger a GC.
void Bar(Object*, Object*);
Handle<Object> baz;
baz->Qux(*Foo()); // (a)
Bar(*Foo(), *baz); // (b)
Both in cases (a) and (b) compiler is free to evaluate call arguments (that
includes receiver) in any order. That means it can dereference baz before
calling to Foo and save a raw pointer to a heap object in the register or
on the stack.
PREREQUISITES -----------------------------------------------------------------
1) Install Lua 5.1
2) Get LLVM and Clang sources and build them.
Follow the instructions on http://clang.llvm.org/get_started.html.
Make sure to pass --enable-optimized to configure to get Release build
instead of a Debug one.
3) Build gcmole Clang plugin (libgcmole.so)
In the tools/gcmole execute the following command:
LLVM_SRC_ROOT=<path-to-llvm-source-root> make
USING GCMOLE ------------------------------------------------------------------
gcmole consists of driver script written in Lua and Clang plugin that does
C++ AST processing. Plugin (libgcmole.so) is expected to be in the same
folder as driver (gcmole.lua).
To start analysis cd into the root of v8 checkout and execute the following
command:
CLANG_BIN=<path-to-clang-bin-folder> lua tools/gcmole/gcmole.lua [<arch>]
where arch should be one of architectures supported by V8 (arm, ia32, x64).
Analysis will be performed in 2 stages:
- on the first stage driver will parse all files and build a global callgraph
approximation to find all functions that might potentially cause GC, list
of this functions will be written into gcsuspects file.
- on the second stage driver will parse all files again and will locate all
callsites that might be GC-unsafe based on the list of functions causing GC.
Such places are marked with a "Possible problem with evaluation order."
warning. Messages "Failed to resolve v8::internal::Object" are benign and
can be ignored.
If any errors were found driver exits with non-zero status.
This diff is collapsed.
-- Copyright 2011 the V8 project authors. All rights reserved.
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
-- * Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- * Redistributions in binary form must reproduce the above
-- copyright notice, this list of conditions and the following
-- disclaimer in the documentation and/or other materials provided
-- with the distribution.
-- * Neither the name of Google Inc. nor the names of its
-- contributors may be used to endorse or promote products derived
-- from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-- This is main driver for gcmole tool. See README for more details.
-- Usage: CLANG_BIN=clang-bin-dir lua tools/gcmole/gcmole.lua [arm|ia32|x64]
local DIR = arg[0]:match("^(.+)/[^/]+$")
local ARCHS = arg[1] and { arg[1] } or { 'ia32', 'arm', 'x64' }
local io = require "io"
local os = require "os"
function log(...)
io.stderr:write(string.format(...))
io.stderr:write "\n"
end
-------------------------------------------------------------------------------
-- Clang invocation
local CLANG_BIN = os.getenv "CLANG_BIN"
if not CLANG_BIN or CLANG_BIN == "" then
error "CLANG_BIN not set"
end
local function MakeClangCommandLine(plugin, triple, arch_define)
return CLANG_BIN .. "/clang -cc1 -load " .. DIR .. "/libgcmole.so"
.. " -plugin " .. plugin
.. " -triple " .. triple
.. " -D" .. arch_define
.. " -DENABLE_VMSTATE_TRACKING"
.. " -DENABLE_LOGGING_AND_PROFILING"
.. " -DENABLE_DEBUGGER_SUPPORT"
.. " -Isrc"
end
function InvokeClangPluginForEachFile(filenames, cfg, func)
local cmd_line = MakeClangCommandLine(cfg.plugin,
cfg.triple,
cfg.arch_define)
for _, filename in ipairs(filenames) do
log("-- %s", filename)
local action = cmd_line .. " src/" .. filename .. " 2>&1"
local pipe = io.popen(action)
func(filename, pipe:lines())
pipe:close()
end
end
-------------------------------------------------------------------------------
-- SConscript parsing
local function ParseSConscript()
local f = assert(io.open("src/SConscript"), "failed to open SConscript")
local sconscript = f:read('*a')
f:close()
local SOURCES = sconscript:match "SOURCES = {(.-)}";
local sources = {}
for condition, list in
SOURCES:gmatch "'([^']-)': Split%(\"\"\"(.-)\"\"\"%)" do
local files = {}
for file in list:gmatch "[^%s]+" do table.insert(files, file) end
sources[condition] = files
end
for condition, list in SOURCES:gmatch "'([^']-)': %[(.-)%]" do
local files = {}
for file in list:gmatch "'([^']-)'" do table.insert(files, file) end
sources[condition] = files
end
return sources
end
local function EvaluateCondition(cond, props)
if cond == 'all' then return true end
local p, v = cond:match "(%w+):(%w+)"
assert(p and v, "failed to parse condition: " .. cond)
assert(props[p] ~= nil, "undefined configuration property: " .. p)
return props[p] == v
end
local function BuildFileList(sources, props)
local list = {}
for condition, files in pairs(sources) do
if EvaluateCondition(condition, props) then
for i = 1, #files do table.insert(list, files[i]) end
end
end
return list
end
local sources = ParseSConscript()
local function FilesForArch(arch)
return BuildFileList(sources, { os = 'linux',
arch = arch,
mode = 'debug',
simulator = ''})
end
local mtConfig = {}
mtConfig.__index = mtConfig
local function config (t) return setmetatable(t, mtConfig) end
function mtConfig:extend(t)
local e = {}
for k, v in pairs(self) do e[k] = v end
for k, v in pairs(t) do e[k] = v end
return config(e)
end
local ARCHITECTURES = {
ia32 = config { triple = "i586-unknown-linux",
arch_define = "V8_TARGET_ARCH_IA32" },
arm = config { triple = "i586-unknown-linux",
arch_define = "V8_TARGET_ARCH_ARM" },
x64 = config { triple = "x86_64-unknown-linux",
arch_define = "V8_TARGET_ARCH_X64" }
}
-------------------------------------------------------------------------------
-- GCSuspects Generation
local gc = {}
local funcs = {}
local function resolve(name)
local f = funcs[name]
if not f then
f = {}
funcs[name] = f
if name:match "Collect.*Garbage" then gc[name] = true end
end
return f
end
local function parse (filename, lines)
local scope
for funcname in lines do
if funcname:sub(1, 1) ~= '\t' then
resolve(funcname)
scope = funcname
else
local name = funcname:sub(2)
resolve(name)[scope] = true
end
end
end
local function propagate ()
log "** Propagating GC information"
local function mark(callers)
for caller, _ in pairs(callers) do
if not gc[caller] then
gc[caller] = true
mark(funcs[caller])
end
end
end
for funcname, callers in pairs(funcs) do
if gc[funcname] then mark(callers) end
end
end
local function GenerateGCSuspects(arch, files, cfg)
log ("** Building GC Suspects for %s", arch)
InvokeClangPluginForEachFile (files,
cfg:extend { plugin = "dump-callees" },
parse)
propagate()
local out = assert(io.open("gcsuspects", "w"))
for name, _ in pairs(gc) do out:write (name, '\n') end
out:close()
log ("** GCSuspects generated for %s", arch)
end
-------------------------------------------------------------------------------
-- Analysis
local function CheckCorrectnessForArch(arch)
local files = FilesForArch(arch)
local cfg = ARCHITECTURES[arch]
GenerateGCSuspects(arch, files, cfg)
local processed_files = 0
local errors_found = false
local function SearchForErrors(filename, lines)
processed_files = processed_files + 1
for l in lines do
errors_found = errors_found or
l:match "^[^:]+:%d+:%d+:" or
l:match "error" or
l:match "warning"
print(l)
end
end
log("** Searching for evaluation order problems for %s", arch)
InvokeClangPluginForEachFile(files,
cfg:extend { plugin = "find-problems" },
SearchForErrors)
log("** Done processing %d files. %s",
processed_files,
errors_found and "Errors found" or "No errors found")
return errors_found
end
local function SafeCheckCorrectnessForArch(arch)
local status, errors = pcall(CheckCorrectnessForArch, arch)
if not status then
print(string.format("There was an error: %s", errors))
errors = true
end
return errors
end
local errors = false
for _, arch in ipairs(ARCHS) do
if not ARCHITECTURES[arch] then
error ("Unknown arch: " .. arch)
end
errors = SafeCheckCorrectnessForArch(arch, report) or errors
end
os.exit(errors and 1 or 0)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment