Merge V8 at 3.9.24.13
Bug: 5688872
Change-Id: Id0aa8d23375030494d3189c31774059c0f5398fc
diff --git a/tools/bash-completion.sh b/tools/bash-completion.sh
new file mode 100644
index 0000000..9f65c67
--- /dev/null
+++ b/tools/bash-completion.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# Copyright 2012 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.
+
+# Inspired by and based on:
+# http://src.chromium.org/viewvc/chrome/trunk/src/tools/bash-completion
+
+# Flag completion rule for bash.
+# To load in your shell, "source path/to/this/file".
+
+v8_source=$(readlink -f $(dirname $BASH_SOURCE)/..)
+
+_v8_flag() {
+ local cur defines targets
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ defines=$(cat src/flag-definitions.h \
+ | grep "^DEFINE" \
+ | grep -v "DEFINE_implication" \
+ | sed -e 's/_/-/g')
+ targets=$(echo "$defines" \
+ | sed -ne 's/^DEFINE-[^(]*(\([^,]*\).*/--\1/p'; \
+ echo "$defines" \
+ | sed -ne 's/^DEFINE-bool(\([^,]*\).*/--no\1/p'; \
+ cat src/d8.cc \
+ | grep "strcmp(argv\[i\]" \
+ | sed -ne 's/^[^"]*"--\([^"]*\)".*/--\1/p')
+ COMPREPLY=($(compgen -W "$targets" -- "$cur"))
+ return 0
+}
+
+complete -F _v8_flag -f d8
diff --git a/tools/check-static-initializers.sh b/tools/check-static-initializers.sh
new file mode 100644
index 0000000..e6da828
--- /dev/null
+++ b/tools/check-static-initializers.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+# Copyright 2012 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.
+
+# Checks that the number of compilation units having at least one static
+# initializer in d8 matches the one defined below.
+# Note that the project must be built with SCons before running this script.
+
+# Allow:
+# - _GLOBAL__I__ZN2v810LineEditor6first_E
+# - _GLOBAL__I__ZN2v88internal32AtomicOps_Internalx86CPUFeaturesE
+# - _GLOBAL__I__ZN2v88internal8ThreadId18highest_thread_id_E
+expected_static_init_count=3
+
+v8_root=$(readlink -f $(dirname $BASH_SOURCE)/../)
+d8="${v8_root}/d8"
+
+if [ ! -f "$d8" ]; then
+ echo "Please build the project with SCons."
+ exit 1
+fi
+
+static_inits=$(nm "$d8" | grep _GLOBAL__I | awk '{ print $NF; }')
+
+static_init_count=$(echo "$static_inits" | wc -l)
+
+if [ $static_init_count -gt $expected_static_init_count ]; then
+ echo "Too many static initializers."
+ echo "$static_inits"
+ exit 1
+fi
diff --git a/tools/common-includes.sh b/tools/common-includes.sh
new file mode 100644
index 0000000..8f0e78b
--- /dev/null
+++ b/tools/common-includes.sh
@@ -0,0 +1,197 @@
+# Copyright 2012 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 file contains common function definitions for various other shell
+# scripts in this directory. It is not meant to be executed by itself.
+
+# Important: before including this file, the following variables must be set:
+# - BRANCHNAME
+# - PERSISTFILE_BASENAME
+
+TEMP_BRANCH=$BRANCHNAME-temporary-branch-created-by-script
+VERSION_FILE="src/version.cc"
+CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry"
+PATCH_FILE="$PERSISTFILE_BASENAME-patch"
+PATCH_OUTPUT_FILE="$PERSISTFILE_BASENAME-patch-output"
+COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg"
+TOUCHED_FILES_FILE="$PERSISTFILE_BASENAME-touched-files"
+TRUNK_REVISION_FILE="$PERSISTFILE_BASENAME-trunkrevision"
+START_STEP=0
+CURRENT_STEP=0
+
+die() {
+ [[ -n "$1" ]] && echo "Error: $1"
+ echo "Exiting."
+ exit 1
+}
+
+confirm() {
+ echo -n "$1 [Y/n] "
+ read ANSWER
+ if [[ -z "$ANSWER" || "$ANSWER" == "Y" || "$ANSWER" == "y" ]] ; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+delete_branch() {
+ local MATCH=$(git branch | grep "$1" | awk '{print $NF}' | grep -x $1)
+ if [ "$MATCH" == "$1" ] ; then
+ confirm "Branch $1 exists, do you want to delete it?"
+ if [ $? -eq 0 ] ; then
+ git branch -D $1 || die "Deleting branch '$1' failed."
+ echo "Branch $1 deleted."
+ else
+ die "Can't continue. Please delete branch $1 and try again."
+ fi
+ fi
+}
+
+# Persist and restore variables to support canceling/resuming execution
+# of this script.
+persist() {
+ local VARNAME=$1
+ local FILE="$PERSISTFILE_BASENAME-$VARNAME"
+ echo "${!VARNAME}" > $FILE
+}
+
+restore() {
+ local VARNAME=$1
+ local FILE="$PERSISTFILE_BASENAME-$VARNAME"
+ local VALUE="$(cat $FILE)"
+ eval "$VARNAME=\"$VALUE\""
+}
+
+restore_if_unset() {
+ local VARNAME=$1
+ [[ -z "${!VARNAME}" ]] && restore "$VARNAME"
+ [[ -z "${!VARNAME}" ]] && die "Variable '$VARNAME' could not be restored."
+}
+
+initial_environment_checks() {
+ # Cancel if this is not a git checkout.
+ [[ -d .git ]] \
+ || die "This is not a git checkout, this script won't work for you."
+
+ # Cancel if EDITOR is unset or not executable.
+ [[ -n "$EDITOR" && -x "$(which $EDITOR)" ]] \
+ || die "Please set your EDITOR environment variable, you'll need it."
+}
+
+common_prepare() {
+ # Check for a clean workdir.
+ [[ -z "$(git status -s -uno)" ]] \
+ || die "Workspace is not clean. Please commit or undo your changes."
+
+ # Persist current branch.
+ CURRENT_BRANCH=$(git status -s -b -uno | grep "^##" | awk '{print $2}')
+ persist "CURRENT_BRANCH"
+
+ # Fetch unfetched revisions.
+ git svn fetch || die "'git svn fetch' failed."
+
+ # Get ahold of a safe temporary branch and check it out.
+ if [ "$CURRENT_BRANCH" != "$TEMP_BRANCH" ] ; then
+ delete_branch $TEMP_BRANCH
+ git checkout -b $TEMP_BRANCH
+ fi
+
+ # Delete the branch that will be created later if it exists already.
+ delete_branch $BRANCHNAME
+}
+
+common_cleanup() {
+ restore_if_unset "CURRENT_BRANCH"
+ git checkout -f $CURRENT_BRANCH
+ [[ "$TEMP_BRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TEMP_BRANCH
+ [[ "$BRANCHNAME" != "$CURRENT_BRANCH" ]] && git branch -D $BRANCHNAME
+ # Clean up all temporary files.
+ rm -f "$PERSISTFILE_BASENAME"*
+}
+
+# These two functions take a prefix for the variable names as first argument.
+read_and_persist_version() {
+ for v in MAJOR_VERSION MINOR_VERSION BUILD_NUMBER PATCH_LEVEL; do
+ VARNAME="$1${v%%_*}"
+ VALUE=$(grep "#define $v" "$VERSION_FILE" | awk '{print $NF}')
+ eval "$VARNAME=\"$VALUE\""
+ persist "$VARNAME"
+ done
+}
+restore_version_if_unset() {
+ for v in MAJOR MINOR BUILD PATCH; do
+ restore_if_unset "$1$v"
+ done
+}
+
+upload_step() {
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Upload for code review."
+ echo -n "Please enter the email address of a V8 reviewer for your patch: "
+ read REVIEWER
+ git cl upload -r "$REVIEWER" --send-mail \
+ || die "'git cl upload' failed, please try again."
+ fi
+}
+
+wait_for_lgtm() {
+ echo "Please wait for an LGTM, then type \"LGTM<Return>\" to commit your \
+change. (If you need to iterate on the patch or double check that it's \
+sane, do so in another shell, but remember to not change the headline of \
+the uploaded CL."
+ unset ANSWER
+ while [ "$ANSWER" != "LGTM" ] ; do
+ [[ -n "$ANSWER" ]] && echo "That was not 'LGTM'."
+ echo -n "> "
+ read ANSWER
+ done
+}
+
+# Takes a file containing the patch to apply as first argument.
+apply_patch() {
+ patch -p1 < "$1" > "$PATCH_OUTPUT_FILE" || \
+ { cat "$PATCH_OUTPUT_FILE" && die "Applying the patch failed."; }
+ tee < "$PATCH_OUTPUT_FILE" >(awk '{print $NF}' >> "$TOUCHED_FILES_FILE")
+ rm "$PATCH_OUTPUT_FILE"
+}
+
+stage_files() {
+ # Stage added and modified files.
+ TOUCHED_FILES=$(cat "$TOUCHED_FILES_FILE")
+ for FILE in $TOUCHED_FILES ; do
+ git add "$FILE"
+ done
+ # Stage deleted files.
+ DELETED_FILES=$(git status -s -uno --porcelain | grep "^ D" \
+ | awk '{print $NF}')
+ for FILE in $DELETED_FILES ; do
+ git rm "$FILE"
+ done
+ rm -f "$TOUCHED_FILES_FILE"
+}
diff --git a/tools/disasm.py b/tools/disasm.py
index c326382..681b425 100644
--- a/tools/disasm.py
+++ b/tools/disasm.py
@@ -48,7 +48,8 @@
_ARCH_MAP = {
"ia32": "-m i386",
"x64": "-m i386 -M x86-64",
- "arm": "-m arm" # Not supported by our objdump build.
+ "arm": "-m arm", # Not supported by our objdump build.
+ "mips": "-m mips" # Not supported by our objdump build.
}
diff --git a/tools/gc-nvp-trace-processor.py b/tools/gc-nvp-trace-processor.py
index 511ab2b..fe5a7f3 100755
--- a/tools/gc-nvp-trace-processor.py
+++ b/tools/gc-nvp-trace-processor.py
@@ -219,13 +219,17 @@
if r['gc'] == 's':
# there is no 'other' scope for scavenging collections.
return 0
- return r['pause'] - r['mark'] - r['sweep'] - r['compact'] - r['external']
+ return r['pause'] - r['mark'] - r['sweep'] - r['external']
def scavenge_scope(r):
if r['gc'] == 's':
return r['pause'] - r['external']
return 0
+
+def real_mutator(r):
+ return r['mutator'] - r['stepstook']
+
plots = [
[
Set('style fill solid 0.5 noborder'),
@@ -234,9 +238,24 @@
Plot(Item('Scavenge', scavenge_scope, lc = 'green'),
Item('Marking', 'mark', lc = 'purple'),
Item('Sweep', 'sweep', lc = 'blue'),
- Item('Compaction', 'compact', lc = 'red'),
Item('External', 'external', lc = '#489D43'),
- Item('Other', other_scope, lc = 'grey'))
+ Item('Other', other_scope, lc = 'grey'),
+ Item('IGC Steps', 'stepstook', lc = '#FF6347'))
+ ],
+ [
+ Set('style fill solid 0.5 noborder'),
+ Set('style histogram rowstacked'),
+ Set('style data histograms'),
+ Plot(Item('Scavenge', scavenge_scope, lc = 'green'),
+ Item('Marking', 'mark', lc = 'purple'),
+ Item('Sweep', 'sweep', lc = 'blue'),
+ Item('External', 'external', lc = '#489D43'),
+ Item('Other', other_scope, lc = '#ADD8E6'),
+ Item('External', 'external', lc = '#D3D3D3'))
+ ],
+
+ [
+ Plot(Item('Mutator', real_mutator, lc = 'black', style = 'lines'))
],
[
Set('style histogram rowstacked'),
@@ -275,7 +294,7 @@
return reduce(lambda t,r: f(t, r[field]), trace, init)
def calc_total(trace, field):
- return freduce(lambda t,v: t + v, field, trace, 0)
+ return freduce(lambda t,v: t + long(v), field, trace, long(0))
def calc_max(trace, field):
return freduce(lambda t,r: max(t, r), field, trace, 0)
@@ -288,8 +307,9 @@
trace = parse_gc_trace(filename)
marksweeps = filter(lambda r: r['gc'] == 'ms', trace)
- markcompacts = filter(lambda r: r['gc'] == 'mc', trace)
scavenges = filter(lambda r: r['gc'] == 's', trace)
+ globalgcs = filter(lambda r: r['gc'] != 's', trace)
+
charts = plot_all(plots, trace, filename)
@@ -302,7 +322,7 @@
else:
avg = 0
if n > 1:
- dev = math.sqrt(freduce(lambda t,r: (r - avg) ** 2, field, trace, 0) /
+ dev = math.sqrt(freduce(lambda t,r: t + (r - avg) ** 2, field, trace, 0) /
(n - 1))
else:
dev = 0
@@ -311,6 +331,31 @@
'<td>%d</td><td>%d [dev %f]</td></tr>' %
(prefix, n, total, max, avg, dev))
+ def HumanReadable(size):
+ suffixes = ['bytes', 'kB', 'MB', 'GB']
+ power = 1
+ for i in range(len(suffixes)):
+ if size < power*1024:
+ return "%.1f" % (float(size) / power) + " " + suffixes[i]
+ power *= 1024
+
+ def throughput(name, trace):
+ total_live_after = calc_total(trace, 'total_size_after')
+ total_live_before = calc_total(trace, 'total_size_before')
+ total_gc = calc_total(trace, 'pause')
+ if total_gc == 0:
+ return
+ out.write('GC %s Throughput (after): %s / %s ms = %s/ms<br/>' %
+ (name,
+ HumanReadable(total_live_after),
+ total_gc,
+ HumanReadable(total_live_after / total_gc)))
+ out.write('GC %s Throughput (before): %s / %s ms = %s/ms<br/>' %
+ (name,
+ HumanReadable(total_live_before),
+ total_gc,
+ HumanReadable(total_live_before / total_gc)))
+
with open(filename + '.html', 'w') as out:
out.write('<html><body>')
@@ -320,15 +365,17 @@
stats(out, 'Total in GC', trace, 'pause')
stats(out, 'Scavenge', scavenges, 'pause')
stats(out, 'MarkSweep', marksweeps, 'pause')
- stats(out, 'MarkCompact', markcompacts, 'pause')
stats(out, 'Mark', filter(lambda r: r['mark'] != 0, trace), 'mark')
stats(out, 'Sweep', filter(lambda r: r['sweep'] != 0, trace), 'sweep')
- stats(out, 'Compact', filter(lambda r: r['compact'] != 0, trace), 'compact')
stats(out,
'External',
filter(lambda r: r['external'] != 0, trace),
'external')
out.write('</table>')
+ throughput('TOTAL', trace)
+ throughput('MS', marksweeps)
+ throughput('OLDSPACE', globalgcs)
+ out.write('<br/>')
for chart in charts:
out.write('<img src="%s">' % chart)
out.write('</body></html>')
diff --git a/tools/gcmole/gccause.lua b/tools/gcmole/gccause.lua
index a6fe542..b989176 100644
--- a/tools/gcmole/gccause.lua
+++ b/tools/gcmole/gccause.lua
@@ -48,6 +48,8 @@
T[f] = true
TrackCause(f, (lvl or 0) + 1)
end
+
+ if f == '<GC>' then break end
end
end
end
diff --git a/tools/gcmole/gcmole.cc b/tools/gcmole/gcmole.cc
index 71ba24a..38ee6e0 100644
--- a/tools/gcmole/gcmole.cc
+++ b/tools/gcmole/gcmole.cc
@@ -69,6 +69,21 @@
}
+static std::string EXTERNAL("EXTERNAL");
+static std::string STATE_TAG("enum v8::internal::StateTag");
+
+static bool IsExternalVMState(const clang::ValueDecl* var) {
+ const clang::EnumConstantDecl* enum_constant =
+ dyn_cast<clang::EnumConstantDecl>(var);
+ if (enum_constant != NULL && enum_constant->getNameAsString() == EXTERNAL) {
+ clang::QualType type = enum_constant->getType();
+ return (type.getAsString() == STATE_TAG);
+ }
+
+ return false;
+}
+
+
struct Resolver {
explicit Resolver(clang::ASTContext& ctx)
: ctx_(ctx), decl_ctx_(ctx.getTranslationUnitDecl()) {
@@ -121,6 +136,13 @@
return true;
}
+ virtual bool VisitDeclRefExpr(clang::DeclRefExpr* expr) {
+ // If function mentions EXTERNAL VMState add artificial garbage collection
+ // mark.
+ if (IsExternalVMState(expr->getDecl())) AddCallee("CollectGarbage");
+ return true;
+ }
+
void AnalyzeFunction(const clang::FunctionDecl* f) {
MangledName name;
if (InV8Namespace(f) && GetMangledName(ctx_, f, &name)) {
@@ -278,6 +300,10 @@
return reinterpret_cast<Environment*>(effect_ & ~kAllEffects);
}
+ static ExprEffect GC() {
+ return ExprEffect(kCausesGC, NULL);
+ }
+
private:
ExprEffect(int effect, Environment* env)
: effect_((effect & kAllEffects) |
@@ -790,6 +816,9 @@
ExprEffect Use(const clang::Expr* parent,
const clang::ValueDecl* var,
const Environment& env) {
+ if (IsExternalVMState(var)) {
+ return ExprEffect::GC();
+ }
return Use(parent, var->getType(), var->getNameAsString(), env);
}
diff --git a/tools/gcmole/gcmole.lua b/tools/gcmole/gcmole.lua
index f8d3b62..09db547 100644
--- a/tools/gcmole/gcmole.lua
+++ b/tools/gcmole/gcmole.lua
@@ -106,7 +106,6 @@
cfg.plugin_args,
cfg.triple,
cfg.arch_define)
-
for _, filename in ipairs(filenames) do
log("-- %s", filename)
local action = cmd_line .. " src/" .. filename .. " 2>&1"
@@ -218,7 +217,13 @@
-- Callsites of such functions are safe as long as they are properly
-- check return value and propagate the Failure to the caller.
-- It should be possible to extend GCMole to understand this.
- "Heap.*AllocateFunctionPrototype"
+ "Heap.*AllocateFunctionPrototype",
+
+ -- Ignore all StateTag methods.
+ "StateTag",
+
+ -- Ignore printing of elements transition.
+ "PrintElementsTransition"
};
local function AddCause(name, cause)
diff --git a/tools/gen-postmortem-metadata.py b/tools/gen-postmortem-metadata.py
new file mode 100644
index 0000000..b9b1625
--- /dev/null
+++ b/tools/gen-postmortem-metadata.py
@@ -0,0 +1,481 @@
+#!/usr/bin/env python
+
+#
+# Copyright 2012 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.
+#
+
+#
+# Emits a C++ file to be compiled and linked into libv8 to support postmortem
+# debugging tools. Most importantly, this tool emits constants describing V8
+# internals:
+#
+# v8dbg_type_CLASS__TYPE = VALUE Describes class type values
+# v8dbg_class_CLASS__FIELD__TYPE = OFFSET Describes class fields
+# v8dbg_parent_CLASS__PARENT Describes class hierarchy
+# v8dbg_frametype_NAME = VALUE Describes stack frame values
+# v8dbg_off_fp_NAME = OFFSET Frame pointer offsets
+# v8dbg_prop_NAME = OFFSET Object property offsets
+# v8dbg_NAME = VALUE Miscellaneous values
+#
+# These constants are declared as global integers so that they'll be present in
+# the generated libv8 binary.
+#
+
+import re
+import sys
+
+#
+# Miscellaneous constants, tags, and masks used for object identification.
+#
+consts_misc = [
+ { 'name': 'FirstNonstringType', 'value': 'FIRST_NONSTRING_TYPE' },
+
+ { 'name': 'IsNotStringMask', 'value': 'kIsNotStringMask' },
+ { 'name': 'StringTag', 'value': 'kStringTag' },
+ { 'name': 'NotStringTag', 'value': 'kNotStringTag' },
+
+ { 'name': 'StringEncodingMask', 'value': 'kStringEncodingMask' },
+ { 'name': 'TwoByteStringTag', 'value': 'kTwoByteStringTag' },
+ { 'name': 'AsciiStringTag', 'value': 'kAsciiStringTag' },
+
+ { 'name': 'StringRepresentationMask',
+ 'value': 'kStringRepresentationMask' },
+ { 'name': 'SeqStringTag', 'value': 'kSeqStringTag' },
+ { 'name': 'ConsStringTag', 'value': 'kConsStringTag' },
+ { 'name': 'ExternalStringTag', 'value': 'kExternalStringTag' },
+
+ { 'name': 'FailureTag', 'value': 'kFailureTag' },
+ { 'name': 'FailureTagMask', 'value': 'kFailureTagMask' },
+ { 'name': 'HeapObjectTag', 'value': 'kHeapObjectTag' },
+ { 'name': 'HeapObjectTagMask', 'value': 'kHeapObjectTagMask' },
+ { 'name': 'SmiTag', 'value': 'kSmiTag' },
+ { 'name': 'SmiTagMask', 'value': 'kSmiTagMask' },
+ { 'name': 'SmiValueShift', 'value': 'kSmiTagSize' },
+ { 'name': 'PointerSizeLog2', 'value': 'kPointerSizeLog2' },
+
+ { 'name': 'prop_idx_content',
+ 'value': 'DescriptorArray::kContentArrayIndex' },
+ { 'name': 'prop_idx_first',
+ 'value': 'DescriptorArray::kFirstIndex' },
+ { 'name': 'prop_type_field',
+ 'value': 'FIELD' },
+ { 'name': 'prop_type_first_phantom',
+ 'value': 'MAP_TRANSITION' },
+ { 'name': 'prop_type_mask',
+ 'value': 'PropertyDetails::TypeField::kMask' },
+
+ { 'name': 'off_fp_context',
+ 'value': 'StandardFrameConstants::kContextOffset' },
+ { 'name': 'off_fp_marker',
+ 'value': 'StandardFrameConstants::kMarkerOffset' },
+ { 'name': 'off_fp_function',
+ 'value': 'JavaScriptFrameConstants::kFunctionOffset' },
+ { 'name': 'off_fp_args',
+ 'value': 'JavaScriptFrameConstants::kLastParameterOffset' },
+];
+
+#
+# The following useful fields are missing accessors, so we define fake ones.
+#
+extras_accessors = [
+ 'HeapObject, map, Map, kMapOffset',
+ 'JSObject, elements, Object, kElementsOffset',
+ 'FixedArray, data, uintptr_t, kHeaderSize',
+ 'Map, instance_attributes, int, kInstanceAttributesOffset',
+ 'Map, instance_descriptors, int, kInstanceDescriptorsOrBitField3Offset',
+ 'Map, inobject_properties, int, kInObjectPropertiesOffset',
+ 'Map, instance_size, int, kInstanceSizeOffset',
+ 'HeapNumber, value, double, kValueOffset',
+ 'ConsString, first, String, kFirstOffset',
+ 'ConsString, second, String, kSecondOffset',
+ 'ExternalString, resource, Object, kResourceOffset',
+ 'SeqAsciiString, chars, char, kHeaderSize',
+ 'SharedFunctionInfo, code, Code, kCodeOffset',
+ 'Code, instruction_start, uintptr_t, kHeaderSize',
+ 'Code, instruction_size, int, kInstructionSizeOffset',
+];
+
+#
+# The following is a whitelist of classes we expect to find when scanning the
+# source code. This list is not exhaustive, but it's still useful to identify
+# when this script gets out of sync with the source. See load_objects().
+#
+expected_classes = [
+ 'ConsString', 'FixedArray', 'HeapNumber', 'JSArray', 'JSFunction',
+ 'JSObject', 'JSRegExp', 'JSValue', 'Map', 'Oddball', 'Script',
+ 'SeqAsciiString', 'SharedFunctionInfo'
+];
+
+
+#
+# The following structures store high-level representations of the structures
+# for which we're going to emit descriptive constants.
+#
+types = {}; # set of all type names
+typeclasses = {}; # maps type names to corresponding class names
+klasses = {}; # known classes, including parents
+fields = []; # field declarations
+
+header = '''
+/*
+ * This file is generated by %s. Do not edit directly.
+ */
+
+#include "v8.h"
+#include "frames.h"
+#include "frames-inl.h" /* for architecture-specific frame constants */
+
+using namespace v8::internal;
+
+extern "C" {
+
+/* stack frame constants */
+#define FRAME_CONST(value, klass) \
+ int v8dbg_frametype_##klass = StackFrame::value;
+
+STACK_FRAME_TYPE_LIST(FRAME_CONST)
+
+#undef FRAME_CONST
+
+''' % sys.argv[0];
+
+footer = '''
+}
+'''
+
+#
+# Loads class hierarchy and type information from "objects.h".
+#
+def load_objects():
+ objfilename = sys.argv[2];
+ objfile = open(objfilename, 'r');
+ in_insttype = False;
+
+ typestr = '';
+
+ #
+ # Construct a dictionary for the classes we're sure should be present.
+ #
+ checktypes = {};
+ for klass in expected_classes:
+ checktypes[klass] = True;
+
+ #
+ # Iterate objects.h line-by-line to collect type and class information.
+ # For types, we accumulate a string representing the entire InstanceType
+ # enum definition and parse it later because it's easier to do so
+ # without the embedded newlines.
+ #
+ for line in objfile:
+ if (line.startswith('enum InstanceType {')):
+ in_insttype = True;
+ continue;
+
+ if (in_insttype and line.startswith('};')):
+ in_insttype = False;
+ continue;
+
+ line = re.sub('//.*', '', line.rstrip().lstrip());
+
+ if (in_insttype):
+ typestr += line;
+ continue;
+
+ match = re.match('class (\w[^\s:]*)(: public (\w[^\s{]*))?\s*{',
+ line);
+
+ if (match):
+ klass = match.group(1);
+ pklass = match.group(3);
+ klasses[klass] = { 'parent': pklass };
+
+ #
+ # Process the instance type declaration.
+ #
+ entries = typestr.split(',');
+ for entry in entries:
+ types[re.sub('\s*=.*', '', entry).lstrip()] = True;
+
+ #
+ # Infer class names for each type based on a systematic transformation.
+ # For example, "JS_FUNCTION_TYPE" becomes "JSFunction". We find the
+ # class for each type rather than the other way around because there are
+ # fewer cases where one type maps to more than one class than the other
+ # way around.
+ #
+ for type in types:
+ #
+ # Symbols and Strings are implemented using the same classes.
+ #
+ usetype = re.sub('SYMBOL_', 'STRING_', type);
+
+ #
+ # REGEXP behaves like REG_EXP, as in JS_REGEXP_TYPE => JSRegExp.
+ #
+ usetype = re.sub('_REGEXP_', '_REG_EXP_', usetype);
+
+ #
+ # Remove the "_TYPE" suffix and then convert to camel case,
+ # except that a "JS" prefix remains uppercase (as in
+ # "JS_FUNCTION_TYPE" => "JSFunction").
+ #
+ if (not usetype.endswith('_TYPE')):
+ continue;
+
+ usetype = usetype[0:len(usetype) - len('_TYPE')];
+ parts = usetype.split('_');
+ cctype = '';
+
+ if (parts[0] == 'JS'):
+ cctype = 'JS';
+ start = 1;
+ else:
+ cctype = '';
+ start = 0;
+
+ for ii in range(start, len(parts)):
+ part = parts[ii];
+ cctype += part[0].upper() + part[1:].lower();
+
+ #
+ # Mapping string types is more complicated. Both types and
+ # class names for Strings specify a representation (e.g., Seq,
+ # Cons, External, or Sliced) and an encoding (TwoByte or Ascii),
+ # In the simplest case, both of these are explicit in both
+ # names, as in:
+ #
+ # EXTERNAL_ASCII_STRING_TYPE => ExternalAsciiString
+ #
+ # However, either the representation or encoding can be omitted
+ # from the type name, in which case "Seq" and "TwoByte" are
+ # assumed, as in:
+ #
+ # STRING_TYPE => SeqTwoByteString
+ #
+ # Additionally, sometimes the type name has more information
+ # than the class, as in:
+ #
+ # CONS_ASCII_STRING_TYPE => ConsString
+ #
+ # To figure this out dynamically, we first check for a
+ # representation and encoding and add them if they're not
+ # present. If that doesn't yield a valid class name, then we
+ # strip out the representation.
+ #
+ if (cctype.endswith('String')):
+ if (cctype.find('Cons') == -1 and
+ cctype.find('External') == -1 and
+ cctype.find('Sliced') == -1):
+ if (cctype.find('Ascii') != -1):
+ cctype = re.sub('AsciiString$',
+ 'SeqAsciiString', cctype);
+ else:
+ cctype = re.sub('String$',
+ 'SeqString', cctype);
+
+ if (cctype.find('Ascii') == -1):
+ cctype = re.sub('String$', 'TwoByteString',
+ cctype);
+
+ if (not (cctype in klasses)):
+ cctype = re.sub('Ascii', '', cctype);
+ cctype = re.sub('TwoByte', '', cctype);
+
+ #
+ # Despite all that, some types have no corresponding class.
+ #
+ if (cctype in klasses):
+ typeclasses[type] = cctype;
+ if (cctype in checktypes):
+ del checktypes[cctype];
+
+ if (len(checktypes) > 0):
+ for klass in checktypes:
+ print('error: expected class \"%s\" not found' % klass);
+
+ sys.exit(1);
+
+
+#
+# For a given macro call, pick apart the arguments and return an object
+# describing the corresponding output constant. See load_fields().
+#
+def parse_field(call):
+ # Replace newlines with spaces.
+ for ii in range(0, len(call)):
+ if (call[ii] == '\n'):
+ call[ii] == ' ';
+
+ idx = call.find('(');
+ kind = call[0:idx];
+ rest = call[idx + 1: len(call) - 1];
+ args = re.split('\s*,\s*', rest);
+
+ consts = [];
+
+ if (kind == 'ACCESSORS' or kind == 'ACCESSORS_GCSAFE'):
+ klass = args[0];
+ field = args[1];
+ dtype = args[2];
+ offset = args[3];
+
+ return ({
+ 'name': 'class_%s__%s__%s' % (klass, field, dtype),
+ 'value': '%s::%s' % (klass, offset)
+ });
+
+ assert(kind == 'SMI_ACCESSORS');
+ klass = args[0];
+ field = args[1];
+ offset = args[2];
+
+ return ({
+ 'name': 'class_%s__%s__%s' % (klass, field, 'SMI'),
+ 'value': '%s::%s' % (klass, offset)
+ });
+
+#
+# Load field offset information from objects-inl.h.
+#
+def load_fields():
+ inlfilename = sys.argv[3];
+ inlfile = open(inlfilename, 'r');
+
+ #
+ # Each class's fields and the corresponding offsets are described in the
+ # source by calls to macros like "ACCESSORS" (and friends). All we do
+ # here is extract these macro invocations, taking into account that they
+ # may span multiple lines and may contain nested parentheses. We also
+ # call parse_field() to pick apart the invocation.
+ #
+ prefixes = [ 'ACCESSORS', 'ACCESSORS_GCSAFE', 'SMI_ACCESSORS' ];
+ current = '';
+ opens = 0;
+
+ for line in inlfile:
+ if (opens > 0):
+ # Continuation line
+ for ii in range(0, len(line)):
+ if (line[ii] == '('):
+ opens += 1;
+ elif (line[ii] == ')'):
+ opens -= 1;
+
+ if (opens == 0):
+ break;
+
+ current += line[0:ii + 1];
+ continue;
+
+ for prefix in prefixes:
+ if (not line.startswith(prefix + '(')):
+ continue;
+
+ if (len(current) > 0):
+ fields.append(parse_field(current));
+ current = '';
+
+ for ii in range(len(prefix), len(line)):
+ if (line[ii] == '('):
+ opens += 1;
+ elif (line[ii] == ')'):
+ opens -= 1;
+
+ if (opens == 0):
+ break;
+
+ current += line[0:ii + 1];
+
+ if (len(current) > 0):
+ fields.append(parse_field(current));
+ current = '';
+
+ for body in extras_accessors:
+ fields.append(parse_field('ACCESSORS(%s)' % body));
+
+#
+# Emit a block of constants.
+#
+def emit_set(out, consts):
+ for ii in range(0, len(consts)):
+ out.write('int v8dbg_%s = %s;\n' %
+ (consts[ii]['name'], consts[ii]['value']));
+ out.write('\n');
+
+#
+# Emit the whole output file.
+#
+def emit_config():
+ out = file(sys.argv[1], 'w');
+
+ out.write(header);
+
+ out.write('/* miscellaneous constants */\n');
+ emit_set(out, consts_misc);
+
+ out.write('/* class type information */\n');
+ consts = [];
+ keys = typeclasses.keys();
+ keys.sort();
+ for typename in keys:
+ klass = typeclasses[typename];
+ consts.append({
+ 'name': 'type_%s__%s' % (klass, typename),
+ 'value': typename
+ });
+
+ emit_set(out, consts);
+
+ out.write('/* class hierarchy information */\n');
+ consts = [];
+ keys = klasses.keys();
+ keys.sort();
+ for klassname in keys:
+ pklass = klasses[klassname]['parent'];
+ if (pklass == None):
+ continue;
+
+ consts.append({
+ 'name': 'parent_%s__%s' % (klassname, pklass),
+ 'value': 0
+ });
+
+ emit_set(out, consts);
+
+ out.write('/* field information */\n');
+ emit_set(out, fields);
+
+ out.write(footer);
+
+if (len(sys.argv) < 4):
+ print('usage: %s output.cc objects.h objects-inl.h' % sys.argv[0]);
+ sys.exit(2);
+
+load_objects();
+load_fields();
+emit_config();
diff --git a/tools/grokdump.py b/tools/grokdump.py
index 6bc49c6..9977289 100755
--- a/tools/grokdump.py
+++ b/tools/grokdump.py
@@ -52,6 +52,7 @@
$ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
"""
+
DEBUG=False
@@ -233,6 +234,80 @@
MD_CONTEXT_X86_EXTENDED_REGISTERS))
])
+MD_CONTEXT_AMD64 = 0x00100000
+MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
+MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
+MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
+MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
+MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
+
+MINIDUMP_CONTEXT_AMD64 = Descriptor([
+ ("p1_home", ctypes.c_uint64),
+ ("p2_home", ctypes.c_uint64),
+ ("p3_home", ctypes.c_uint64),
+ ("p4_home", ctypes.c_uint64),
+ ("p5_home", ctypes.c_uint64),
+ ("p6_home", ctypes.c_uint64),
+ ("context_flags", ctypes.c_uint32),
+ ("mx_csr", ctypes.c_uint32),
+ # MD_CONTEXT_AMD64_CONTROL.
+ ("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
+ # MD_CONTEXT_AMD64_SEGMENTS
+ ("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
+ ("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
+ ("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
+ ("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
+ # MD_CONTEXT_AMD64_CONTROL.
+ ("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
+ ("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
+ # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
+ ("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ # MD_CONTEXT_AMD64_INTEGER.
+ ("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ # MD_CONTEXT_AMD64_CONTROL.
+ ("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
+ # MD_CONTEXT_AMD64_INTEGER.
+ ("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ ("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
+ # MD_CONTEXT_AMD64_CONTROL.
+ ("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
+ # MD_CONTEXT_AMD64_FLOATING_POINT
+ ("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
+ MD_CONTEXT_AMD64_FLOATING_POINT)),
+ ("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
+ MD_CONTEXT_AMD64_FLOATING_POINT)),
+ ("vector_control", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_FLOATING_POINT)),
+ # MD_CONTEXT_AMD64_DEBUG_REGISTERS.
+ ("debug_control", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
+ ("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
+ MD_CONTEXT_AMD64_DEBUG_REGISTERS))
+])
+
MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
("start", ctypes.c_uint64),
("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
@@ -269,6 +344,12 @@
("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
])
+MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
+ ("processor_architecture", ctypes.c_uint16)
+])
+
+MD_CPU_ARCHITECTURE_X86 = 0
+MD_CPU_ARCHITECTURE_AMD64 = 9
class MinidumpReader(object):
"""Minidump (.dmp) reader."""
@@ -288,20 +369,34 @@
for _ in xrange(self.header.stream_count):
directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
offset += MINIDUMP_DIRECTORY.size
+ self.arch = None
self.exception = None
self.exception_context = None
self.memory_list = None
self.memory_list64 = None
self.thread_map = {}
+
+ # Find MDRawSystemInfo stream and determine arch.
+ for d in directories:
+ if d.stream_type == MD_SYSTEM_INFO_STREAM:
+ system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
+ self.minidump, d.location.rva)
+ self.arch = system_info.processor_architecture
+ assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86]
+ assert not self.arch is None
+
for d in directories:
DebugPrint(d)
- # TODO(vitalyr): extract system info including CPU features.
if d.stream_type == MD_EXCEPTION_STREAM:
self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
self.minidump, d.location.rva)
DebugPrint(self.exception)
- self.exception_context = MINIDUMP_CONTEXT_X86.Read(
- self.minidump, self.exception.thread_context.rva)
+ if self.arch == MD_CPU_ARCHITECTURE_X86:
+ self.exception_context = MINIDUMP_CONTEXT_X86.Read(
+ self.minidump, self.exception.thread_context.rva)
+ elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
+ self.minidump, self.exception.thread_context.rva)
DebugPrint(self.exception_context)
elif d.stream_type == MD_THREAD_LIST_STREAM:
thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
@@ -335,6 +430,16 @@
location = self.FindLocation(address)
return ctypes.c_uint32.from_buffer(self.minidump, location).value
+ def ReadU64(self, address):
+ location = self.FindLocation(address)
+ return ctypes.c_uint64.from_buffer(self.minidump, location).value
+
+ def ReadUIntPtr(self, address):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return self.ReadU64(address)
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return self.ReadU32(address)
+
def ReadBytes(self, address, size):
location = self.FindLocation(address)
return self.minidump[location:location + size]
@@ -355,10 +460,15 @@
def GetDisasmLines(self, address, size):
location = self.FindLocation(address)
if location is None: return []
+ arch = None
+ if self.arch == MD_CPU_ARCHITECTURE_X86:
+ arch = "ia32"
+ elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ arch = "x64"
return disasm.GetDisasmLines(self.minidump_name,
location,
size,
- "ia32",
+ arch,
False)
@@ -366,13 +476,40 @@
self.minidump.close()
self.minidump_file.close()
+ def ExceptionIP(self):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return self.exception_context.rip
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return self.exception_context.eip
+
+ def ExceptionSP(self):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return self.exception_context.rsp
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return self.exception_context.esp
+
+ def FormatIntPtr(self, value):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return "%016x" % value
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return "%08x" % value
+
+ def PointerSize(self):
+ if self.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return 8
+ elif self.arch == MD_CPU_ARCHITECTURE_X86:
+ return 4
+
+ def Register(self, name):
+ return self.exception_context.__getattribute__(name)
+
# List of V8 instance types. Obtained by adding the code below to any .cc file.
#
-# #define DUMP_TYPE(T) printf("%d: \"%s\",\n", T, #T);
+# #define DUMP_TYPE(T) printf(" %d: \"%s\",\n", T, #T);
# struct P {
# P() {
-# printf("{\n");
+# printf("INSTANCE_TYPES = {\n");
# INSTANCE_TYPE_LIST(DUMP_TYPE)
# printf("}\n");
# }
@@ -386,13 +523,20 @@
66: "EXTERNAL_SYMBOL_TYPE",
74: "EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
70: "EXTERNAL_ASCII_SYMBOL_TYPE",
+ 82: "SHORT_EXTERNAL_SYMBOL_TYPE",
+ 90: "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE",
+ 86: "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE",
0: "STRING_TYPE",
4: "ASCII_STRING_TYPE",
1: "CONS_STRING_TYPE",
5: "CONS_ASCII_STRING_TYPE",
+ 3: "SLICED_STRING_TYPE",
2: "EXTERNAL_STRING_TYPE",
10: "EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
6: "EXTERNAL_ASCII_STRING_TYPE",
+ 18: "SHORT_EXTERNAL_STRING_TYPE",
+ 26: "SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE",
+ 22: "SHORT_EXTERNAL_ASCII_STRING_TYPE",
6: "PRIVATE_EXTERNAL_ASCII_STRING_TYPE",
128: "MAP_TYPE",
129: "CODE_TYPE",
@@ -401,43 +545,46 @@
132: "HEAP_NUMBER_TYPE",
133: "FOREIGN_TYPE",
134: "BYTE_ARRAY_TYPE",
- 135: "EXTERNAL_BYTE_ARRAY_TYPE",
- 136: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
- 137: "EXTERNAL_SHORT_ARRAY_TYPE",
- 138: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
- 139: "EXTERNAL_INT_ARRAY_TYPE",
- 140: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
- 141: "EXTERNAL_FLOAT_ARRAY_TYPE",
- 143: "EXTERNAL_PIXEL_ARRAY_TYPE",
- 145: "FILLER_TYPE",
- 146: "ACCESSOR_INFO_TYPE",
- 147: "ACCESS_CHECK_INFO_TYPE",
- 148: "INTERCEPTOR_INFO_TYPE",
- 149: "CALL_HANDLER_INFO_TYPE",
- 150: "FUNCTION_TEMPLATE_INFO_TYPE",
- 151: "OBJECT_TEMPLATE_INFO_TYPE",
- 152: "SIGNATURE_INFO_TYPE",
- 153: "TYPE_SWITCH_INFO_TYPE",
- 154: "SCRIPT_TYPE",
- 155: "CODE_CACHE_TYPE",
- 156: "POLYMORPHIC_CODE_CACHE_TYPE",
- 159: "FIXED_ARRAY_TYPE",
- 160: "SHARED_FUNCTION_INFO_TYPE",
- 161: "JS_MESSAGE_OBJECT_TYPE",
- 162: "JS_VALUE_TYPE",
- 163: "JS_OBJECT_TYPE",
- 164: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
- 165: "JS_GLOBAL_OBJECT_TYPE",
- 166: "JS_BUILTINS_OBJECT_TYPE",
- 167: "JS_GLOBAL_PROXY_TYPE",
- 168: "JS_ARRAY_TYPE",
- 169: "JS_PROXY_TYPE",
- 170: "JS_WEAK_MAP_TYPE",
- 171: "JS_REGEXP_TYPE",
- 172: "JS_FUNCTION_TYPE",
- 173: "JS_FUNCTION_PROXY_TYPE",
- 157: "DEBUG_INFO_TYPE",
- 158: "BREAK_POINT_INFO_TYPE",
+ 135: "FREE_SPACE_TYPE",
+ 136: "EXTERNAL_BYTE_ARRAY_TYPE",
+ 137: "EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE",
+ 138: "EXTERNAL_SHORT_ARRAY_TYPE",
+ 139: "EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE",
+ 140: "EXTERNAL_INT_ARRAY_TYPE",
+ 141: "EXTERNAL_UNSIGNED_INT_ARRAY_TYPE",
+ 142: "EXTERNAL_FLOAT_ARRAY_TYPE",
+ 144: "EXTERNAL_PIXEL_ARRAY_TYPE",
+ 146: "FILLER_TYPE",
+ 147: "ACCESSOR_INFO_TYPE",
+ 148: "ACCESSOR_PAIR_TYPE",
+ 149: "ACCESS_CHECK_INFO_TYPE",
+ 150: "INTERCEPTOR_INFO_TYPE",
+ 151: "CALL_HANDLER_INFO_TYPE",
+ 152: "FUNCTION_TEMPLATE_INFO_TYPE",
+ 153: "OBJECT_TEMPLATE_INFO_TYPE",
+ 154: "SIGNATURE_INFO_TYPE",
+ 155: "TYPE_SWITCH_INFO_TYPE",
+ 156: "SCRIPT_TYPE",
+ 157: "CODE_CACHE_TYPE",
+ 158: "POLYMORPHIC_CODE_CACHE_TYPE",
+ 161: "FIXED_ARRAY_TYPE",
+ 145: "FIXED_DOUBLE_ARRAY_TYPE",
+ 162: "SHARED_FUNCTION_INFO_TYPE",
+ 163: "JS_MESSAGE_OBJECT_TYPE",
+ 166: "JS_VALUE_TYPE",
+ 167: "JS_OBJECT_TYPE",
+ 168: "JS_CONTEXT_EXTENSION_OBJECT_TYPE",
+ 169: "JS_GLOBAL_OBJECT_TYPE",
+ 170: "JS_BUILTINS_OBJECT_TYPE",
+ 171: "JS_GLOBAL_PROXY_TYPE",
+ 172: "JS_ARRAY_TYPE",
+ 165: "JS_PROXY_TYPE",
+ 175: "JS_WEAK_MAP_TYPE",
+ 176: "JS_REGEXP_TYPE",
+ 177: "JS_FUNCTION_TYPE",
+ 164: "JS_FUNCTION_PROXY_TYPE",
+ 159: "DEBUG_INFO_TYPE",
+ 160: "BREAK_POINT_INFO_TYPE",
}
@@ -501,34 +648,36 @@
p.Print(str(self))
def __str__(self):
- return "HeapObject(%08x, %s)" % (self.address,
- INSTANCE_TYPES[self.map.instance_type])
+ return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
+ INSTANCE_TYPES[self.map.instance_type])
def ObjectField(self, offset):
- field_value = self.heap.reader.ReadU32(self.address + offset)
+ field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
return self.heap.FindObjectOrSmi(field_value)
def SmiField(self, offset):
- field_value = self.heap.reader.ReadU32(self.address + offset)
+ field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
assert (field_value & 1) == 0
return field_value / 2
class Map(HeapObject):
- INSTANCE_TYPE_OFFSET = 8
+ def InstanceTypeOffset(self):
+ return self.heap.PointerSize() + self.heap.IntSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
self.instance_type = \
- heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
+ heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
class String(HeapObject):
- LENGTH_OFFSET = 4
+ def LengthOffset(self):
+ return self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.length = self.SmiField(String.LENGTH_OFFSET)
+ self.length = self.SmiField(self.LengthOffset())
def GetChars(self):
return "?string?"
@@ -541,11 +690,12 @@
class SeqString(String):
- CHARS_OFFSET = 12
+ def CharsOffset(self):
+ return self.heap.PointerSize() * 3
def __init__(self, heap, map, address):
String.__init__(self, heap, map, address)
- self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
+ self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
self.length)
def GetChars(self):
@@ -553,6 +703,7 @@
class ExternalString(String):
+ # TODO(vegorov) fix ExternalString for X64 architecture
RESOURCE_OFFSET = 12
WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
@@ -582,24 +733,28 @@
class ConsString(String):
- LEFT_OFFSET = 12
- RIGHT_OFFSET = 16
+ def LeftOffset(self):
+ return self.heap.PointerSize() * 3
+
+ def RightOffset(self):
+ return self.heap.PointerSize() * 4
def __init__(self, heap, map, address):
String.__init__(self, heap, map, address)
- self.left = self.ObjectField(ConsString.LEFT_OFFSET)
- self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
+ self.left = self.ObjectField(self.LeftOffset())
+ self.right = self.ObjectField(self.RightOffset())
def GetChars(self):
return self.left.GetChars() + self.right.GetChars()
class Oddball(HeapObject):
- TO_STRING_OFFSET = 4
+ def ToStringOffset(self):
+ return self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
+ self.to_string = self.ObjectField(self.ToStringOffset())
def Print(self, p):
p.Print(str(self))
@@ -609,19 +764,23 @@
class FixedArray(HeapObject):
- LENGTH_OFFSET = 4
- ELEMENTS_OFFSET = 8
+ def LengthOffset(self):
+ return self.heap.PointerSize()
+
+ def ElementsOffset(self):
+ return self.heap.PointerSize() * 2
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
+ self.length = self.SmiField(self.LengthOffset())
def Print(self, p):
- p.Print("FixedArray(%08x) {" % self.address)
+ p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("length: %d" % self.length)
+ base_offset = self.ElementsOffset()
for i in xrange(self.length):
- offset = FixedArray.ELEMENTS_OFFSET + 4 * i
+ offset = base_offset + 4 * i
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
p.Dedent()
p.Print("}")
@@ -631,19 +790,22 @@
class JSFunction(HeapObject):
- CODE_ENTRY_OFFSET = 12
- SHARED_OFFSET = 20
+ def CodeEntryOffset(self):
+ return 3 * self.heap.PointerSize()
+
+ def SharedOffset(self):
+ return 5 * self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
code_entry = \
- heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
- self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
- self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
+ heap.reader.ReadU32(self.address + self.CodeEntryOffset())
+ self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
+ self.shared = self.ObjectField(self.SharedOffset())
def Print(self, p):
source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
- p.Print("JSFunction(%08x) {" % self.address)
+ p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("inferred name: %s" % self.shared.inferred_name)
if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
@@ -662,7 +824,8 @@
inferred_name = ""
if self.shared.Is(SharedFunctionInfo):
inferred_name = self.shared.inferred_name
- return "JSFunction(%08x, %s)" % (self.address, inferred_name)
+ return "JSFunction(%s, %s)" % \
+ (self.heap.reader.FormatIntPtr(self.address), inferred_name)
def _GetSource(self):
source = "?source?"
@@ -675,47 +838,75 @@
class SharedFunctionInfo(HeapObject):
- CODE_OFFSET = 2 * 4
- SCRIPT_OFFSET = 7 * 4
- INFERRED_NAME_OFFSET = 9 * 4
- START_POSITION_AND_TYPE_OFFSET = 17 * 4
- END_POSITION_OFFSET = 18 * 4
+ def CodeOffset(self):
+ return 2 * self.heap.PointerSize()
+
+ def ScriptOffset(self):
+ return 7 * self.heap.PointerSize()
+
+ def InferredNameOffset(self):
+ return 9 * self.heap.PointerSize()
+
+ def EndPositionOffset(self):
+ return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
+
+ def StartPositionAndTypeOffset(self):
+ return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
- self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
- self.inferred_name = \
- self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
- start_position_and_type = \
- self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
- self.start_position = start_position_and_type >> 2
- self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
+ self.code = self.ObjectField(self.CodeOffset())
+ self.script = self.ObjectField(self.ScriptOffset())
+ self.inferred_name = self.ObjectField(self.InferredNameOffset())
+ if heap.PointerSize() == 8:
+ start_position_and_type = \
+ heap.reader.ReadU32(self.StartPositionAndTypeOffset())
+ self.start_position = start_position_and_type >> 2
+ pseudo_smi_end_position = \
+ heap.reader.ReadU32(self.EndPositionOffset())
+ self.end_position = pseudo_smi_end_position >> 2
+ else:
+ start_position_and_type = \
+ self.SmiField(self.StartPositionAndTypeOffset())
+ self.start_position = start_position_and_type >> 2
+ self.end_position = \
+ self.SmiField(self.EndPositionOffset())
class Script(HeapObject):
- SOURCE_OFFSET = 4
- NAME_OFFSET = 8
+ def SourceOffset(self):
+ return self.heap.PointerSize()
+
+ def NameOffset(self):
+ return self.SourceOffset() + self.heap.PointerSize()
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.source = self.ObjectField(Script.SOURCE_OFFSET)
- self.name = self.ObjectField(Script.NAME_OFFSET)
+ self.source = self.ObjectField(self.SourceOffset())
+ self.name = self.ObjectField(self.NameOffset())
class Code(HeapObject):
- INSTRUCTION_SIZE_OFFSET = 4
- ENTRY_OFFSET = 32
+ CODE_ALIGNMENT_MASK = (1 << 5) - 1
+
+ def InstructionSizeOffset(self):
+ return self.heap.PointerSize()
+
+ @staticmethod
+ def HeaderSize(heap):
+ return (heap.PointerSize() + heap.IntSize() + \
+ 4 * heap.PointerSize() + 3 * heap.IntSize() + \
+ Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
def __init__(self, heap, map, address):
HeapObject.__init__(self, heap, map, address)
- self.entry = self.address + Code.ENTRY_OFFSET
+ self.entry = self.address + Code.HeaderSize(heap)
self.instruction_size = \
- heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
+ heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
def Print(self, p):
lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
- p.Print("Code(%08x) {" % self.address)
+ p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
p.Indent()
p.Print("instruction_size: %d" % self.instruction_size)
p.PrintLines(self._FormatLine(line) for line in lines)
@@ -735,6 +926,9 @@
"EXTERNAL_SYMBOL_TYPE": ExternalString,
"EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
"EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
+ "SHORT_EXTERNAL_SYMBOL_TYPE": ExternalString,
+ "SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE": ExternalString,
+ "SHORT_EXTERNAL_ASCII_SYMBOL_TYPE": ExternalString,
"STRING_TYPE": SeqString,
"ASCII_STRING_TYPE": SeqString,
"CONS_STRING_TYPE": ConsString,
@@ -764,10 +958,10 @@
def FindObject(self, tagged_address):
if tagged_address in self.objects:
return self.objects[tagged_address]
- if (tagged_address & 1) != 1: return None
+ if (tagged_address & self.ObjectAlignmentMask()) != 1: return None
address = tagged_address - 1
if not self.reader.IsValidAddress(address): return None
- map_tagged_address = self.reader.ReadU32(address)
+ map_tagged_address = self.reader.ReadUIntPtr(address)
if tagged_address == map_tagged_address:
# Meta map?
meta_map = Map(self, None, address)
@@ -776,7 +970,7 @@
meta_map.map = meta_map
object = meta_map
else:
- map = self.FindObject(map_tagged_address)
+ map = self.FindMap(map_tagged_address)
if map is None: return None
instance_type_name = INSTANCE_TYPES.get(map.instance_type)
if instance_type_name is None: return None
@@ -785,9 +979,37 @@
self.objects[tagged_address] = object
return object
+ def FindMap(self, tagged_address):
+ if (tagged_address & self.MapAlignmentMask()) != 1: return None
+ address = tagged_address - 1
+ if not self.reader.IsValidAddress(address): return None
+ object = Map(self, None, address)
+ return object
+
+ def IntSize(self):
+ return 4
+
+ def PointerSize(self):
+ return self.reader.PointerSize()
+
+ def ObjectAlignmentMask(self):
+ return self.PointerSize() - 1
+
+ def MapAlignmentMask(self):
+ if self.reader.arch == MD_CPU_ARCHITECTURE_AMD64:
+ return (1 << 4) - 1
+ elif self.reader.arch == MD_CPU_ARCHITECTURE_X86:
+ return (1 << 5) - 1
+
EIP_PROXIMITY = 64
+CONTEXT_FOR_ARCH = {
+ MD_CPU_ARCHITECTURE_AMD64:
+ ['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'],
+ MD_CPU_ARCHITECTURE_X86:
+ ['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
+}
def AnalyzeMinidump(options, minidump_name):
reader = MinidumpReader(options, minidump_name)
@@ -800,40 +1022,35 @@
print " thread id: %d" % exception_thread.id
print " code: %08X" % reader.exception.exception.code
print " context:"
- print " eax: %08x" % reader.exception_context.eax
- print " ebx: %08x" % reader.exception_context.ebx
- print " ecx: %08x" % reader.exception_context.ecx
- print " edx: %08x" % reader.exception_context.edx
- print " edi: %08x" % reader.exception_context.edi
- print " esi: %08x" % reader.exception_context.esi
- print " ebp: %08x" % reader.exception_context.ebp
- print " esp: %08x" % reader.exception_context.esp
- print " eip: %08x" % reader.exception_context.eip
+ for r in CONTEXT_FOR_ARCH[reader.arch]:
+ print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
# TODO(vitalyr): decode eflags.
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
print
+ stack_top = reader.ExceptionSP()
stack_bottom = exception_thread.stack.start + \
exception_thread.stack.memory.data_size
- stack_map = {reader.exception_context.eip: -1}
- for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
- maybe_address = reader.ReadU32(slot)
+ stack_map = {reader.ExceptionIP(): -1}
+ for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
+ maybe_address = reader.ReadUIntPtr(slot)
if not maybe_address in stack_map:
stack_map[maybe_address] = slot
heap = V8Heap(reader, stack_map)
print "Disassembly around exception.eip:"
- start = reader.exception_context.eip - EIP_PROXIMITY
+ start = reader.ExceptionIP() - EIP_PROXIMITY
lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
for line in lines:
print FormatDisasmLine(start, heap, line)
print
print "Annotated stack (from exception.esp to bottom):"
- for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
- maybe_address = reader.ReadU32(slot)
+ for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
+ maybe_address = reader.ReadUIntPtr(slot)
heap_object = heap.FindObject(maybe_address)
- print "%08x: %08x" % (slot, maybe_address)
+ print "%s: %s" % (reader.FormatIntPtr(slot),
+ reader.FormatIntPtr(maybe_address))
if heap_object:
heap_object.Print(Printer())
print
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 5014417..538b7ef 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -1,4 +1,4 @@
-# Copyright 2011 the V8 project authors. All rights reserved.
+# Copyright 2012 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:
@@ -32,6 +32,7 @@
'targets': [
{
'target_name': 'v8',
+ 'dependencies_traverse': 1,
'conditions': [
['want_separate_host_toolset==1', {
'toolsets': ['host', 'target'],
@@ -39,10 +40,16 @@
'toolsets': ['target'],
}],
['v8_use_snapshot=="true"', {
- 'dependencies': ['v8_snapshot'],
+ # The dependency on v8_base should come from a transitive
+ # dependency however the Android toolchain requires libv8_base.a
+ # to appear before libv8_snapshot.a so it's listed explicitly.
+ 'dependencies': ['v8_base', 'v8_snapshot'],
},
{
- 'dependencies': ['v8_nosnapshot'],
+ # The dependency on v8_base should come from a transitive
+ # dependency however the Android toolchain requires libv8_base.a
+ # to appear before libv8_snapshot.a so it's listed explicitly.
+ 'dependencies': ['v8_base', 'v8_nosnapshot'],
}],
['component=="shared_library"', {
'type': '<(component)',
@@ -72,11 +79,7 @@
},
}],
['soname_version!=""', {
- # Ideally, we'd like to specify the full filename for the
- # library and set it to "libv8.so.<(soname_version)",
- # but currently the best we can do is use 'product_name' and
- # get "libv8-<(soname_version).so".
- 'product_name': 'v8-<(soname_version)',
+ 'product_extension': 'so.<(soname_version)',
}],
],
},
@@ -225,6 +228,9 @@
{
'target_name': 'v8_base',
'type': '<(library)',
+ 'variables': {
+ 'optimize': 'max',
+ },
'include_dirs+': [
'../../src',
],
@@ -240,8 +246,8 @@
'../../src/assembler.cc',
'../../src/assembler.h',
'../../src/ast.cc',
- '../../src/ast-inl.h',
'../../src/ast.h',
+ '../../src/atomicops.h',
'../../src/atomicops_internals_x86_gcc.cc',
'../../src/bignum.cc',
'../../src/bignum.h',
@@ -283,6 +289,8 @@
'../../src/cpu-profiler.h',
'../../src/data-flow.cc',
'../../src/data-flow.h',
+ '../../src/date.cc',
+ '../../src/date.h',
'../../src/dateparser.cc',
'../../src/dateparser.h',
'../../src/dateparser-inl.h',
@@ -326,7 +334,6 @@
'../../src/handles-inl.h',
'../../src/handles.cc',
'../../src/handles.h',
- '../../src/hashmap.cc',
'../../src/hashmap.h',
'../../src/heap-inl.h',
'../../src/heap.cc',
@@ -340,8 +347,12 @@
'../../src/ic-inl.h',
'../../src/ic.cc',
'../../src/ic.h',
+ '../../src/incremental-marking.cc',
+ '../../src/incremental-marking.h',
'../../src/inspector.cc',
'../../src/inspector.h',
+ '../../src/interface.cc',
+ '../../src/interface.h',
'../../src/interpreter-irregexp.cc',
'../../src/interpreter-irregexp.h',
'../../src/json-parser.h',
@@ -349,6 +360,7 @@
'../../src/jsregexp.h',
'../../src/isolate.cc',
'../../src/isolate.h',
+ '../../src/lazy-instance.h'
'../../src/list-inl.h',
'../../src/list.h',
'../../src/lithium.cc',
@@ -379,8 +391,11 @@
'../../src/objects-visiting.h',
'../../src/objects.cc',
'../../src/objects.h',
+ '../../src/once.cc',
+ '../../src/once.h',
'../../src/parser.cc',
'../../src/parser.h',
+ '../../src/platform-posix.h',
'../../src/platform-tls-mac.h',
'../../src/platform-tls-win32.h',
'../../src/platform-tls.h',
@@ -394,6 +409,7 @@
'../../src/prettyprinter.h',
'../../src/property.cc',
'../../src/property.h',
+ '../../src/property-details.h',
'../../src/profile-generator-inl.h',
'../../src/profile-generator.cc',
'../../src/profile-generator.h',
@@ -431,6 +447,9 @@
'../../src/spaces-inl.h',
'../../src/spaces.cc',
'../../src/spaces.h',
+ '../../src/store-buffer-inl.h',
+ '../../src/store-buffer.cc',
+ '../../src/store-buffer.h',
'../../src/string-search.cc',
'../../src/string-search.h',
'../../src/string-stream.cc',
@@ -549,6 +568,40 @@
'../../src/ia32/stub-cache-ia32.cc',
],
}],
+ ['v8_target_arch=="mips"', {
+ 'sources': [
+ '../../src/mips/assembler-mips.cc',
+ '../../src/mips/assembler-mips.h',
+ '../../src/mips/assembler-mips-inl.h',
+ '../../src/mips/builtins-mips.cc',
+ '../../src/mips/codegen-mips.cc',
+ '../../src/mips/codegen-mips.h',
+ '../../src/mips/code-stubs-mips.cc',
+ '../../src/mips/code-stubs-mips.h',
+ '../../src/mips/constants-mips.cc',
+ '../../src/mips/constants-mips.h',
+ '../../src/mips/cpu-mips.cc',
+ '../../src/mips/debug-mips.cc',
+ '../../src/mips/deoptimizer-mips.cc',
+ '../../src/mips/disasm-mips.cc',
+ '../../src/mips/frames-mips.cc',
+ '../../src/mips/frames-mips.h',
+ '../../src/mips/full-codegen-mips.cc',
+ '../../src/mips/ic-mips.cc',
+ '../../src/mips/lithium-codegen-mips.cc',
+ '../../src/mips/lithium-codegen-mips.h',
+ '../../src/mips/lithium-gap-resolver-mips.cc',
+ '../../src/mips/lithium-gap-resolver-mips.h',
+ '../../src/mips/lithium-mips.cc',
+ '../../src/mips/lithium-mips.h',
+ '../../src/mips/macro-assembler-mips.cc',
+ '../../src/mips/macro-assembler-mips.h',
+ '../../src/mips/regexp-macro-assembler-mips.cc',
+ '../../src/mips/regexp-macro-assembler-mips.h',
+ '../../src/mips/simulator-mips.cc',
+ '../../src/mips/stub-cache-mips.cc',
+ ],
+ }],
['v8_target_arch=="x64" or v8_target_arch=="mac" or OS=="mac"', {
'sources': [
'../../src/x64/assembler-x64-inl.h',
@@ -586,7 +639,8 @@
['v8_compress_startup_data=="bz2"', {
'libraries': [
'-lbz2',
- ]}],
+ ]
+ }],
],
},
'sources': [
@@ -596,26 +650,30 @@
}
],
['OS=="android"', {
+ 'defines': [
+ 'CAN_USE_VFP_INSTRUCTIONS',
+ ],
'sources': [
'../../src/platform-posix.cc',
],
'conditions': [
- ['host_os=="mac" and _toolset!="target"', {
- 'sources': [
- '../../src/platform-macos.cc'
- ]
+ ['host_os=="mac"', {
+ 'target_conditions': [
+ ['_toolset=="host"', {
+ 'sources': [
+ '../../src/platform-macos.cc'
+ ]
+ }, {
+ 'sources': [
+ '../../src/platform-linux.cc'
+ ]
+ }],
+ ],
}, {
'sources': [
'../../src/platform-linux.cc'
]
}],
- ['_toolset=="target"', {
- 'link_settings': {
- 'libraries': [
- '-llog',
- ],
- }
- }],
],
},
],
@@ -641,6 +699,28 @@
],
}
],
+ ['OS=="netbsd"', {
+ 'link_settings': {
+ 'libraries': [
+ '-L/usr/pkg/lib -Wl,-R/usr/pkg/lib -lexecinfo',
+ ]},
+ 'sources': [
+ '../../src/platform-openbsd.cc',
+ '../../src/platform-posix.cc'
+ ],
+ }
+ ],
+ ['OS=="solaris"', {
+ 'link_settings': {
+ 'libraries': [
+ '-lsocket -lnsl',
+ ]},
+ 'sources': [
+ '../../src/platform-solaris.cc',
+ '../../src/platform-posix.cc',
+ ],
+ }
+ ],
['OS=="mac"', {
'sources': [
'../../src/platform-macos.cc',
@@ -664,6 +744,11 @@
'V8_SHARED',
],
}],
+ ['v8_postmortem_support=="true"', {
+ 'sources': [
+ '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc',
+ ]
+ }],
],
},
{
@@ -697,7 +782,7 @@
'experimental_library_files': [
'../../src/macros.py',
'../../src/proxy.js',
- '../../src/weakmap.js',
+ '../../src/collection.js',
],
},
'actions': [
@@ -740,9 +825,38 @@
],
},
{
+ 'target_name': 'postmortem-metadata',
+ 'type': 'none',
+ 'variables': {
+ 'heapobject_files': [
+ '../../src/objects.h',
+ '../../src/objects-inl.h',
+ ],
+ },
+ 'actions': [
+ {
+ 'action_name': 'gen-postmortem-metadata',
+ 'inputs': [
+ '../../tools/gen-postmortem-metadata.py',
+ '<@(heapobject_files)',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/debug-support.cc',
+ ],
+ 'action': [
+ 'python',
+ '../../tools/gen-postmortem-metadata.py',
+ '<@(_outputs)',
+ '<@(heapobject_files)'
+ ]
+ }
+ ]
+ },
+ {
'target_name': 'mksnapshot',
'type': 'executable',
'dependencies': [
+ 'v8_base',
'v8_nosnapshot',
],
'include_dirs+': [
@@ -760,8 +874,8 @@
['v8_compress_startup_data=="bz2"', {
'libraries': [
'-lbz2',
- ]}
- ],
+ ]
+ }],
],
},
{
@@ -786,7 +900,8 @@
['v8_compress_startup_data=="bz2"', {
'libraries': [
'-lbz2',
- ]}],
+ ]
+ }],
],
},
{
@@ -800,6 +915,8 @@
'../../include/v8stdint.h',
'../../src/allocation.cc',
'../../src/allocation.h',
+ '../../src/atomicops.h',
+ '../../src/atomicops_internals_x86_gcc.cc',
'../../src/bignum.cc',
'../../src/bignum.h',
'../../src/bignum-dtoa.cc',
@@ -822,10 +939,11 @@
'../../src/fixed-dtoa.cc',
'../../src/fixed-dtoa.h',
'../../src/globals.h',
- '../../src/hashmap.cc',
'../../src/hashmap.h',
'../../src/list-inl.h',
'../../src/list.h',
+ '../../src/once.cc',
+ '../../src/once.h',
'../../src/preparse-data-format.h',
'../../src/preparse-data.cc',
'../../src/preparse-data.h',
@@ -858,7 +976,7 @@
'targets': [
{
'target_name': 'v8',
- 'type': 'settings',
+ 'type': 'none',
'conditions': [
['want_separate_host_toolset==1', {
'toolsets': ['host', 'target'],
diff --git a/tools/js2c.py b/tools/js2c.py
index a2ea8ea..fa559f3 100644
--- a/tools/js2c.py
+++ b/tools/js2c.py
@@ -128,12 +128,13 @@
end = pattern_match.end()
assert lines[end - 1] == '('
last_match = end
- arg_index = 0
+ arg_index = [0] # Wrap state into array, to work around Python "scoping"
mapping = { }
def add_arg(str):
# Remember to expand recursively in the arguments
replacement = ExpandMacros(str.strip(), macros)
- mapping[macro.args[arg_index]] = replacement
+ mapping[macro.args[arg_index[0]]] = replacement
+ arg_index[0] += 1
while end < len(lines) and height > 0:
# We don't count commas at higher nesting levels.
if lines[end] == ',' and height == 1:
diff --git a/tools/jsmin.py b/tools/jsmin.py
index 646bf14..e82f3d0 100644
--- a/tools/jsmin.py
+++ b/tools/jsmin.py
@@ -232,7 +232,9 @@
# A regexp that matches a regexp literal surrounded by /slashes/.
# Don't allow a regexp to have a ) before the first ( since that's a
# syntax error and it's probably just two unrelated slashes.
- slash_quoted_regexp = r"/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
+ # Also don't allow it to come after anything that can only be the
+ # end of a primary expression.
+ slash_quoted_regexp = r"(?<![\w$'\")\]])/(?:(?=\()|(?:[^()/\\]|\\.)+)(?:\([^/\\]|\\.)*/"
# Replace multiple spaces with a single space.
line = re.sub("|".join([double_quoted_string,
single_quoted_string,
diff --git a/tools/linux-tick-processor b/tools/linux-tick-processor
index 0b0a1fb..7070ce6 100755
--- a/tools/linux-tick-processor
+++ b/tools/linux-tick-processor
@@ -1,20 +1,5 @@
#!/bin/sh
-tools_path=`cd $(dirname "$0");pwd`
-if [ ! "$D8_PATH" ]; then
- d8_public=`which d8`
- if [ -x $d8_public ]; then D8_PATH=$(dirname "$d8_public"); fi
-fi
-[ "$D8_PATH" ] || D8_PATH=$tools_path/..
-d8_exec=$D8_PATH/d8
-
-if [ ! -x $d8_exec ]; then
- echo "d8 shell not found in $D8_PATH"
- echo "To build, execute 'scons <flags> d8' from the V8 directory"
- exit 1
-fi
-
-
# find the name of the log file to process, it must not start with a dash.
log_file="v8.log"
for arg in "$@"
@@ -24,6 +9,28 @@
fi
done
+tools_path=`cd $(dirname "$0");pwd`
+if [ ! "$D8_PATH" ]; then
+ d8_public=`which d8`
+ if [ -x $d8_public ]; then D8_PATH=$(dirname "$d8_public"); fi
+fi
+[ "$D8_PATH" ] || D8_PATH=$tools_path/..
+d8_exec=$D8_PATH/d8
+
+if [ ! -x $d8_exec ]; then
+ D8_PATH=`pwd`/out/native
+ d8_exec=$D8_PATH/d8
+fi
+
+if [ ! -x $d8_exec ]; then
+ d8_exec=`grep -m 1 -o '".*/d8"' $log_file | sed 's/"//g'`
+fi
+
+if [ ! -x $d8_exec ]; then
+ echo "d8 shell not found in $D8_PATH"
+ echo "To build, execute 'make native' from the V8 directory"
+ exit 1
+fi
# nm spits out 'no symbols found' messages to stderr.
cat $log_file | $d8_exec $tools_path/splaytree.js $tools_path/codemap.js \
diff --git a/tools/ll_prof.py b/tools/ll_prof.py
index 58cbb95..51ba672 100755
--- a/tools/ll_prof.py
+++ b/tools/ll_prof.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2010 the V8 project authors. All rights reserved.
+# Copyright 2012 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:
@@ -334,6 +334,7 @@
_ARCH_TO_POINTER_TYPE_MAP = {
"ia32": ctypes.c_uint32,
"arm": ctypes.c_uint32,
+ "mips": ctypes.c_uint32,
"x64": ctypes.c_uint64
}
@@ -399,12 +400,16 @@
code = Code(name, start_address, end_address, origin, origin_offset)
conficting_code = self.code_map.Find(start_address)
if conficting_code:
- LogReader._HandleCodeConflict(conficting_code, code)
- # TODO(vitalyr): this warning is too noisy because of our
- # attempts to reconstruct code log from the snapshot.
- # print >>sys.stderr, \
- # "Warning: Skipping duplicate code log entry %s" % code
- continue
+ if not (conficting_code.start_address == code.start_address and
+ conficting_code.end_address == code.end_address):
+ self.code_map.Remove(conficting_code)
+ else:
+ LogReader._HandleCodeConflict(conficting_code, code)
+ # TODO(vitalyr): this warning is too noisy because of our
+ # attempts to reconstruct code log from the snapshot.
+ # print >>sys.stderr, \
+ # "Warning: Skipping duplicate code log entry %s" % code
+ continue
self.code_map.Add(code)
continue
@@ -668,7 +673,9 @@
OBJDUMP_SYMBOL_LINE_RE = re.compile(
r"^([a-f0-9]+)\s(.{7})\s(\S+)\s+([a-f0-9]+)\s+(?:\.hidden\s+)?(.*)$")
OBJDUMP_DYNAMIC_SYMBOLS_START_RE = re.compile(
- r"^DYNAMIC SYMBOL TABLE")
+ r"^DYNAMIC SYMBOL TABLE")
+OBJDUMP_SKIP_RE = re.compile(
+ r"^.*ld\.so\.cache$")
KERNEL_ALLSYMS_FILE = "/proc/kallsyms"
PERF_KERNEL_ALLSYMS_RE = re.compile(
r".*kallsyms.*")
@@ -687,6 +694,8 @@
# is 0.
if mmap_info.tid == 0 and not options.kernel:
return True
+ if OBJDUMP_SKIP_RE.match(mmap_info.filename):
+ return True
if PERF_KERNEL_ALLSYMS_RE.match(mmap_info.filename):
return self._LoadKernelSymbols(code_map)
self.infos.append(mmap_info)
diff --git a/tools/logreader.js b/tools/logreader.js
index 315e721..a8141da 100644
--- a/tools/logreader.js
+++ b/tools/logreader.js
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// 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:
@@ -134,9 +134,8 @@
LogReader.prototype.dispatchLogRow_ = function(fields) {
// Obtain the dispatch.
var command = fields[0];
- if (!(command in this.dispatchTable_)) {
- throw new Error('unknown command: ' + command);
- }
+ if (!(command in this.dispatchTable_)) return;
+
var dispatch = this.dispatchTable_[command];
if (dispatch === null || this.skipDispatch(dispatch)) {
diff --git a/tools/merge-to-branch.sh b/tools/merge-to-branch.sh
new file mode 100644
index 0000000..49bf3e4
--- /dev/null
+++ b/tools/merge-to-branch.sh
@@ -0,0 +1,267 @@
+#!/bin/bash
+# Copyright 2012 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.
+
+########## Global variable definitions
+
+BRANCHNAME=prepare-merge
+PERSISTFILE_BASENAME=/tmp/v8-merge-to-branch-tempfile
+ALREADY_MERGING_SENTINEL_FILE="$PERSISTFILE_BASENAME-already-merging"
+COMMIT_HASHES_FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES"
+TEMPORARY_PATCH_FILE="$PERSISTFILE_BASENAME-temporary-patch"
+
+########## Function definitions
+
+source $(dirname $BASH_SOURCE)/common-includes.sh
+
+usage() {
+cat << EOF
+usage: $0 [OPTIONS]... [BRANCH] [REVISION]...
+
+Performs the necessary steps to merge revisions from bleeding_edge
+to other branches, including trunk.
+
+OPTIONS:
+ -h Show this message
+ -s Specify the step where to start work. Default: 0.
+ -p Specify a patch file to apply as part of the merge
+EOF
+}
+
+persist_patch_commit_hashes() {
+ echo "PATCH_COMMIT_HASHES=( ${PATCH_COMMIT_HASHES[@]} )" > $COMMIT_HASHES_FILE
+}
+
+restore_patch_commit_hashes() {
+ source $COMMIT_HASHES_FILE
+}
+
+restore_patch_commit_hashes_if_unset() {
+ [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && restore_patch_commit_hashes
+ [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && [[ -z "$EXTRA_PATCH" ]] && \
+ die "Variable PATCH_COMMIT_HASHES could not be restored."
+}
+
+########## Option parsing
+
+while getopts ":hs:fp:" OPTION ; do
+ case $OPTION in
+ h) usage
+ exit 0
+ ;;
+ p) EXTRA_PATCH=$OPTARG
+ ;;
+ f) rm -f "$ALREADY_MERGING_SENTINEL_FILE"
+ ;;
+ s) START_STEP=$OPTARG
+ ;;
+ ?) echo "Illegal option: -$OPTARG"
+ usage
+ exit 1
+ ;;
+ esac
+done
+let OPTION_COUNT=$OPTIND-1
+shift $OPTION_COUNT
+
+########## Regular workflow
+
+# If there is a merge in progress, abort.
+[[ -e "$ALREADY_MERGING_SENTINEL_FILE" ]] && [[ $START_STEP -eq 0 ]] \
+ && die "A merge is already in progress"
+touch "$ALREADY_MERGING_SENTINEL_FILE"
+
+initial_environment_checks
+
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ if [ ${#@} -lt 2 ] && [ -z "$EXTRA_PATCH" ] ; then
+ die "Either a patch file or revision numbers must be specified"
+ fi
+ echo ">>> Step $CURRENT_STEP: Preparation"
+ MERGE_TO_BRANCH=$1
+ [[ -n "$MERGE_TO_BRANCH" ]] || die "Please specify a branch to merge to"
+ shift
+ persist "MERGE_TO_BRANCH"
+ common_prepare
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a fresh branch for the patch."
+ restore_if_unset "MERGE_TO_BRANCH"
+ git checkout -b $BRANCHNAME svn/$MERGE_TO_BRANCH \
+ || die "Creating branch $BRANCHNAME failed."
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Find the git \
+revisions associated with the patches."
+ current=0
+ for REVISION in "$@" ; do
+ NEXT_HASH=$(git svn find-rev "r$REVISION" svn/bleeding_edge)
+ [[ -n "$NEXT_HASH" ]] \
+ || die "Cannot determine git hash for r$REVISION"
+ PATCH_COMMIT_HASHES[$current]="$NEXT_HASH"
+ [[ -n "$REVISION_LIST" ]] && REVISION_LIST="$REVISION_LIST,"
+ REVISION_LIST="$REVISION_LIST r$REVISION"
+ let current+=1
+ done
+ if [ -z "$REVISION_LIST" ] ; then
+ NEW_COMMIT_MSG="Applied patch to $MERGE_TO_BRANCH branch."
+ else
+ NEW_COMMIT_MSG="Merged$REVISION_LIST into $MERGE_TO_BRANCH branch."
+ fi;
+
+ echo "$NEW_COMMIT_MSG" > $COMMITMSG_FILE
+ echo "" >> $COMMITMSG_FILE
+ for HASH in ${PATCH_COMMIT_HASHES[@]} ; do
+ PATCH_MERGE_DESCRIPTION=$(git log -1 --format=%s $HASH)
+ echo "$PATCH_MERGE_DESCRIPTION" >> $COMMITMSG_FILE
+ echo "" >> $COMMITMSG_FILE
+ done
+ for HASH in ${PATCH_COMMIT_HASHES[@]} ; do
+ BUG=$(git log -1 $HASH | grep "BUG=" | awk -F '=' '{print $NF}')
+ if [ -n "$BUG" ] ; then
+ [[ -n "$BUG_AGGREGATE" ]] && BUG_AGGREGATE="$BUG_AGGREGATE,"
+ BUG_AGGREGATE="$BUG_AGGREGATE$BUG"
+ fi
+ done
+ if [ -n "$BUG_AGGREGATE" ] ; then
+ echo "BUG=$BUG_AGGREGATE" >> $COMMITMSG_FILE
+ fi
+ persist "NEW_COMMIT_MSG"
+ persist "REVISION_LIST"
+ persist_patch_commit_hashes
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Apply patches for selected revisions."
+ restore_if_unset "MERGE_TO_BRANCH"
+ restore_patch_commit_hashes_if_unset "PATCH_COMMIT_HASHES"
+ rm -f "$TOUCHED_FILES_FILE"
+ for HASH in ${PATCH_COMMIT_HASHES[@]} ; do
+ echo "Applying patch for $HASH to $MERGE_TO_BRANCH..."
+ git log -1 -p $HASH > "$TEMPORARY_PATCH_FILE"
+ apply_patch "$TEMPORARY_PATCH_FILE"
+ done
+ if [ -n "$EXTRA_PATCH" ] ; then
+ apply_patch "$EXTRA_PATCH"
+ fi
+ stage_files
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Prepare $VERSION_FILE."
+ # These version numbers are used again for creating the tag
+ read_and_persist_version
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Increment version number."
+ restore_if_unset "PATCH"
+ NEWPATCH=$(($PATCH + 1))
+ confirm "Automatically increment PATCH_LEVEL? (Saying 'n' will fire up \
+your EDITOR on $VERSION_FILE so you can make arbitrary changes. When \
+you're done, save the file and exit your EDITOR.)"
+ if [ $? -eq 0 ] ; then
+ sed -e "/#define PATCH_LEVEL/s/[0-9]*$/$NEWPATCH/" \
+ -i "$VERSION_FILE"
+ else
+ $EDITOR "$VERSION_FILE"
+ fi
+ read_and_persist_version "NEW"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to local branch."
+ git commit -a -F "$COMMITMSG_FILE" \
+ || die "'git commit -a' failed."
+fi
+
+upload_step
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to the repository."
+ restore_if_unset "MERGE_TO_BRANCH"
+ git checkout $BRANCHNAME \
+ || die "cannot ensure that the current branch is $BRANCHNAME"
+ wait_for_lgtm
+ git cl dcommit || die "failed to commit to $MERGE_TO_BRANCH"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Determine svn commit revision"
+ restore_if_unset "NEW_COMMIT_MSG"
+ restore_if_unset "MERGE_TO_BRANCH"
+ git svn fetch || die "'git svn fetch' failed."
+ COMMIT_HASH=$(git log -1 --format=%H --grep="$NEW_COMMIT_MSG" \
+ svn/$MERGE_TO_BRANCH)
+ [[ -z "$COMMIT_HASH" ]] && die "Unable to map git commit to svn revision"
+ SVN_REVISION=$(git svn find-rev $COMMIT_HASH)
+ echo "subversion revision number is r$SVN_REVISION"
+ persist "SVN_REVISION"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create the tag."
+ restore_if_unset "SVN_REVISION"
+ restore_version_if_unset "NEW"
+ echo "Creating tag svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ if [ "$MERGE_TO_BRANCH" == "trunk" ] ; then
+ TO_URL="$MERGE_TO_BRANCH"
+ else
+ TO_URL="branches/$MERGE_TO_BRANCH"
+ fi
+ svn copy -r $SVN_REVISION \
+ https://v8.googlecode.com/svn/$TO_URL \
+ https://v8.googlecode.com/svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH \
+ -m "Tagging version $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ persist "TO_URL"
+fi
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Cleanup."
+ restore_if_unset "SVN_REVISION"
+ restore_if_unset "TO_URL"
+ restore_if_unset "REVISION_LIST"
+ restore_version_if_unset "NEW"
+ common_cleanup
+ echo "*** SUMMARY ***"
+ echo "version: $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH"
+ echo "branch: $TO_URL"
+ echo "svn revision: $SVN_REVISION"
+ [[ -n "$REVISION_LIST" ]] && echo "patches:$REVISION_LIST"
+fi
diff --git a/tools/presubmit.py b/tools/presubmit.py
index fda7ba9..a5f4c61 100755
--- a/tools/presubmit.py
+++ b/tools/presubmit.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2011 the V8 project authors. All rights reserved.
+# Copyright 2012 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:
@@ -42,6 +42,7 @@
import re
import sys
import subprocess
+import multiprocessing
from subprocess import PIPE
# Disabled LINT rules and reason.
@@ -101,6 +102,33 @@
""".split()
+LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]|^Done processing')
+
+
+def CppLintWorker(command):
+ try:
+ process = subprocess.Popen(command, stderr=subprocess.PIPE)
+ process.wait()
+ out_lines = ""
+ error_count = -1
+ while True:
+ out_line = process.stderr.readline()
+ if out_line == '' and process.poll() != None:
+ break
+ m = LINT_OUTPUT_PATTERN.match(out_line)
+ if m:
+ out_lines += out_line
+ error_count += 1
+ sys.stderr.write(out_lines)
+ return error_count
+ except KeyboardInterrupt:
+ process.kill()
+ except:
+ print('Error running cpplint.py. Please make sure you have depot_tools' +
+ ' in your $PATH. Lint check skipped.')
+ process.kill()
+
+
class FileContentsCache(object):
def __init__(self, sums_file_name):
@@ -206,24 +234,28 @@
return True
filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES])
- command = ['cpplint.py', '--filter', filt] + join(files)
+ command = ['cpplint.py', '--filter', filt]
local_cpplint = join(path, "tools", "cpplint.py")
if exists(local_cpplint):
- command = ['python', local_cpplint, '--filter', filt] + join(files)
+ command = ['python', local_cpplint, '--filter', filt]
- process = subprocess.Popen(command, stderr=subprocess.PIPE)
- LINT_ERROR_PATTERN = re.compile(r'^(.+)[:(]\d+[:)]')
- while True:
- out_line = process.stderr.readline()
- if out_line == '' and process.poll() != None:
- break
- sys.stderr.write(out_line)
- m = LINT_ERROR_PATTERN.match(out_line)
- if m:
- good_files_cache.RemoveFile(m.group(1))
+ commands = join([command + [file] for file in files])
+ count = multiprocessing.cpu_count()
+ pool = multiprocessing.Pool(count)
+ try:
+ results = pool.map_async(CppLintWorker, commands).get(999999)
+ except KeyboardInterrupt:
+ print "\nCaught KeyboardInterrupt, terminating workers."
+ sys.exit(1)
+ for i in range(len(files)):
+ if results[i] > 0:
+ good_files_cache.RemoveFile(files[i])
+
+ total_errors = sum(results)
+ print "Total errors found: %d" % total_errors
good_files_cache.Save()
- return process.returncode == 0
+ return total_errors == 0
COPYRIGHT_HEADER_PATTERN = re.compile(
diff --git a/tools/push-to-trunk.sh b/tools/push-to-trunk.sh
index 761b733..3fb5b34 100755
--- a/tools/push-to-trunk.sh
+++ b/tools/push-to-trunk.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Copyright 2011 the V8 project authors. All rights reserved.
+# Copyright 2012 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:
@@ -31,18 +31,13 @@
BRANCHNAME=prepare-push
TRUNKBRANCH=trunk-push
-TEMP_BRANCH=v8-push-to-trunk-script-temporary-branch
-VERSION_FILE="src/version.cc"
PERSISTFILE_BASENAME=/tmp/v8-push-to-trunk-tempfile
-CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry"
-PATCH_FILE="$PERSISTFILE_BASENAME-patch"
-COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg"
-TOUCHED_FILES_FILE="$PERSISTFILE_BASENAME-touched-files"
-STEP=0
-
+CHROME_PATH=
########## Function definitions
+source $(dirname $BASH_SOURCE)/common-includes.sh
+
usage() {
cat << EOF
usage: $0 OPTIONS
@@ -54,71 +49,24 @@
-h Show this message
-s Specify the step where to start work. Default: 0.
-l Manually specify the git commit ID of the last push to trunk.
+ -c Specify the path to your Chromium src/ directory to automate the
+ V8 roll.
EOF
}
-die() {
- [[ -n "$1" ]] && echo "Error: $1"
- echo "Exiting."
- exit 1
-}
-
-confirm() {
- echo -n "$1 [Y/n] "
- read ANSWER
- if [[ -z "$ANSWER" || "$ANSWER" == "Y" || "$ANSWER" == "y" ]] ; then
- return 0
- else
- return 1
- fi
-}
-
-delete_branch() {
- local MATCH=$(git branch | grep $1 | awk '{print $NF}' )
- if [ "$MATCH" == "$1" ] ; then
- confirm "Branch $1 exists, do you want to delete it?"
- if [ $? -eq 0 ] ; then
- git branch -D $1 || die "Deleting branch '$1' failed."
- echo "Branch $1 deleted."
- else
- die "Can't continue. Please delete branch $1 and try again."
- fi
- fi
-}
-
-# Persist and restore variables to support canceling/resuming execution
-# of this script.
-persist() {
- local VARNAME=$1
- local FILE="$PERSISTFILE_BASENAME-$VARNAME"
- echo "${!VARNAME}" > $FILE
-}
-
-restore() {
- local VARNAME=$1
- local FILE="$PERSISTFILE_BASENAME-$VARNAME"
- local VALUE="$(cat $FILE)"
- eval "$VARNAME=\"$VALUE\""
-}
-
-restore_if_unset() {
- local VARNAME=$1
- [[ -z "${!VARNAME}" ]] && restore "$VARNAME"
- [[ -z "${!VARNAME}" ]] && die "Variable '$VARNAME' could not be restored."
-}
-
-
########## Option parsing
-while getopts ":hs:l:" OPTION ; do
+while getopts ":hs:l:c:" OPTION ; do
case $OPTION in
h) usage
exit 0
;;
- s) STEP=$OPTARG
+ s) START_STEP=$OPTARG
;;
l) LASTPUSH=$OPTARG
;;
+ c) CHROME_PATH=$OPTARG
+ ;;
?) echo "Illegal option: -$OPTARG"
usage
exit 1
@@ -129,46 +77,24 @@
########## Regular workflow
-# Cancel if this is not a git checkout.
-[[ -d .git ]] \
- || die "This is not a git checkout, this script won't work for you."
+initial_environment_checks
-# Cancel if EDITOR is unset or not executable.
-[[ -n "$EDITOR" && -x "$(which $EDITOR)" ]] \
- || die "Please set your EDITOR environment variable, you'll need it."
-
-if [ $STEP -le 0 ] ; then
- echo ">>> Step 0: Preparation"
- # Check for a clean workdir.
- [[ -z "$(git status -s -uno)" ]] \
- || die "Workspace is not clean. Please commit or undo your changes."
-
- # Persist current branch.
- CURRENT_BRANCH=$(git status -s -b -uno | grep "^##" | awk '{print $2}')
- persist "CURRENT_BRANCH"
- # Get ahold of a safe temporary branch and check it out.
- if [ "$CURRENT_BRANCH" != "$TEMP_BRANCH" ] ; then
- delete_branch $TEMP_BRANCH
- git checkout -b $TEMP_BRANCH
- fi
- # Delete branches if they exist.
- delete_branch $BRANCHNAME
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Preparation"
+ common_prepare
delete_branch $TRUNKBRANCH
fi
-if [ $STEP -le 1 ] ; then
- echo ">>> Step 1: Fetch unfetched revisions."
- git svn fetch || die "'git svn fetch' failed."
-fi
-
-if [ $STEP -le 2 ] ; then
- echo ">>> Step 2: Create a fresh branch."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a fresh branch."
git checkout -b $BRANCHNAME svn/bleeding_edge \
|| die "Creating branch $BRANCHNAME failed."
fi
-if [ $STEP -le 3 ] ; then
- echo ">>> Step 3: Detect commit ID of last push to trunk."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Detect commit ID of last push to trunk."
[[ -n "$LASTPUSH" ]] || LASTPUSH=$(git log -1 --format=%H ChangeLog)
LOOP=1
while [ $LOOP -eq 1 ] ; do
@@ -184,15 +110,11 @@
persist "LASTPUSH"
fi
-if [ $STEP -le 4 ] ; then
- echo ">>> Step 4: Prepare raw ChangeLog entry."
-# These version numbers are used again later for the trunk commit.
- MAJOR=$(grep "#define MAJOR_VERSION" "$VERSION_FILE" | awk '{print $NF}')
- persist "MAJOR"
- MINOR=$(grep "#define MINOR_VERSION" "$VERSION_FILE" | awk '{print $NF}')
- persist "MINOR"
- BUILD=$(grep "#define BUILD_NUMBER" "$VERSION_FILE" | awk '{print $NF}')
- persist "BUILD"
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Prepare raw ChangeLog entry."
+ # These version numbers are used again later for the trunk commit.
+ read_and_persist_version
DATE=$(date +%Y-%m-%d)
persist "DATE"
@@ -202,18 +124,25 @@
for commit in $COMMITS ; do
# Get the commit's title line.
git log -1 $commit --format="%w(80,8,8)%s" >> "$CHANGELOG_ENTRY_FILE"
- # Grep for "BUG=xxxx" lines in the commit message.
- git log -1 $commit --format="%b" | grep BUG= | grep -v "BUG=$" \
- | sed -e 's/^/ /' \
- >> "$CHANGELOG_ENTRY_FILE"
+ # Grep for "BUG=xxxx" lines in the commit message and convert them to
+ # "(issue xxxx)".
+ git log -1 $commit --format="%B" \
+ | grep "^BUG=" | grep -v "BUG=$" | grep -v "BUG=none$" \
+ | sed -e 's/^/ /' \
+ | sed -e 's/BUG=v8:\(.*\)$/(issue \1)/' \
+ | sed -e 's/BUG=\(.*\)$/(Chromium issue \1)/' \
+ >> "$CHANGELOG_ENTRY_FILE"
# Append the commit's author for reference.
git log -1 $commit --format="%w(80,8,8)(%an)" >> "$CHANGELOG_ENTRY_FILE"
echo "" >> "$CHANGELOG_ENTRY_FILE"
done
+ echo " Performance and stability improvements on all platforms." \
+ >> "$CHANGELOG_ENTRY_FILE"
fi
-if [ $STEP -le 5 ] ; then
- echo ">>> Step 5: Edit ChangeLog entry."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Edit ChangeLog entry."
echo -n "Please press <Return> to have your EDITOR open the ChangeLog entry, \
then edit its contents to your liking. When you're done, save the file and \
exit your EDITOR. "
@@ -221,7 +150,13 @@
$EDITOR "$CHANGELOG_ENTRY_FILE"
NEWCHANGELOG=$(mktemp)
# Eliminate any trailing newlines by going through a shell variable.
- CHANGELOGENTRY=$(cat "$CHANGELOG_ENTRY_FILE")
+ # Also (1) eliminate tabs, (2) fix too little and (3) too much indentation,
+ # and (4) eliminate trailing whitespace.
+ CHANGELOGENTRY=$(cat "$CHANGELOG_ENTRY_FILE" \
+ | sed -e 's/\t/ /g' \
+ | sed -e 's/^ \{1,7\}\([^ ]\)/ \1/g' \
+ | sed -e 's/^ \{9,80\}\([^ ]\)/ \1/g' \
+ | sed -e 's/ \+$//')
[[ -n "$CHANGELOGENTRY" ]] || die "Empty ChangeLog entry."
echo "$CHANGELOGENTRY" > "$NEWCHANGELOG"
echo "" >> "$NEWCHANGELOG" # Explicitly insert two empty lines.
@@ -230,8 +165,9 @@
mv "$NEWCHANGELOG" ChangeLog
fi
-if [ $STEP -le 6 ] ; then
- echo ">>> Step 6: Increment version number."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Increment version number."
restore_if_unset "BUILD"
NEWBUILD=$(($BUILD + 1))
confirm "Automatically increment BUILD_NUMBER? (Saying 'n' will fire up \
@@ -243,42 +179,26 @@
else
$EDITOR "$VERSION_FILE"
fi
- NEWMAJOR=$(grep "#define MAJOR_VERSION" "$VERSION_FILE" | awk '{print $NF}')
- persist "NEWMAJOR"
- NEWMINOR=$(grep "#define MINOR_VERSION" "$VERSION_FILE" | awk '{print $NF}')
- persist "NEWMINOR"
- NEWBUILD=$(grep "#define BUILD_NUMBER" "$VERSION_FILE" | awk '{print $NF}')
- persist "NEWBUILD"
+ read_and_persist_version "NEW"
fi
-if [ $STEP -le 7 ] ; then
- echo ">>> Step 7: Commit to local branch."
- restore_if_unset "NEWMAJOR"
- restore_if_unset "NEWMINOR"
- restore_if_unset "NEWBUILD"
- git commit -a -m "Prepare push to trunk. \
-Now working on version $NEWMAJOR.$NEWMINOR.$NEWBUILD." \
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to local branch."
+ restore_version_if_unset "NEW"
+ PREPARE_COMMIT_MSG="Prepare push to trunk. \
+Now working on version $NEWMAJOR.$NEWMINOR.$NEWBUILD."
+ persist "PREPARE_COMMIT_MSG"
+ git commit -a -m "$PREPARE_COMMIT_MSG" \
|| die "'git commit -a' failed."
fi
-if [ $STEP -le 8 ] ; then
- echo ">>> Step 8: Upload for code review."
- echo -n "Please enter the email address of a V8 reviewer for your patch: "
- read REVIEWER
- git cl upload -r $REVIEWER --send-mail \
- || die "'git cl upload' failed, please try again."
-fi
+upload_step
-if [ $STEP -le 9 ] ; then
- echo ">>> Step 9: Commit to the repository."
- echo "Please wait for an LGTM, then type \"LGTM<Return>\" to commit your \
-change. (If you need to iterate on the patch, do so in another shell.)"
- unset ANSWER
- while [ "$ANSWER" != "LGTM" ] ; do
- [[ -n "$ANSWER" ]] && echo "That was not 'LGTM'."
- echo -n "> "
- read ANSWER
- done
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to the repository."
+ wait_for_lgtm
# Re-read the ChangeLog entry (to pick up possible changes).
cat ChangeLog | awk --posix '{
if ($0 ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}:/) {
@@ -293,16 +213,24 @@
git cl dcommit || die "'git cl dcommit' failed, please try again."
fi
-if [ $STEP -le 10 ] ; then
- echo ">>> Step 10: NOP"
- # Present in the manual guide, not necessary (even harmful!) for this script.
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Fetch straggler commits that sneaked in \
+since this script was started."
+ git svn fetch || die "'git svn fetch' failed."
+ git checkout svn/bleeding_edge
+ restore_if_unset "PREPARE_COMMIT_MSG"
+ PREPARE_COMMIT_HASH=$(git log -1 --format=%H --grep="$PREPARE_COMMIT_MSG")
+ persist "PREPARE_COMMIT_HASH"
fi
-if [ $STEP -le 11 ] ; then
- echo ">>> Step 11: Squash commits into one."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Squash commits into one."
# Instead of relying on "git rebase -i", we'll just create a diff, because
# that's easier to automate.
- git diff svn/trunk > "$PATCH_FILE"
+ restore_if_unset "PREPARE_COMMIT_HASH"
+ git diff svn/trunk $PREPARE_COMMIT_HASH > "$PATCH_FILE"
# Convert the ChangeLog entry to commit message format:
# - remove date
# - remove indentation
@@ -324,54 +252,29 @@
need_space = 1;
}
}' > "$COMMITMSG_FILE" || die "Commit message editing failed."
- LOOP=1
- while [ $LOOP -eq 1 ] ; do
- echo "This is the trunk commit message:"
- echo "--------------------"
- cat "$COMMITMSG_FILE"
- echo -e "\n--------------------"
- confirm "Does this look good to you? (Saying 'n' will fire up your \
-EDITOR so you can change the commit message. When you're done, save the \
-file and exit your EDITOR.)"
- if [ $? -eq 0 ] ; then
- LOOP=0
- else
- $EDITOR "$COMMITMSG_FILE"
- fi
- done
rm -f "$CHANGELOG_ENTRY_FILE"
fi
-if [ $STEP -le 12 ] ; then
- echo ">>> Step 12: Create a new branch from trunk."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create a new branch from trunk."
git checkout -b $TRUNKBRANCH svn/trunk \
|| die "Checking out a new branch '$TRUNKBRANCH' failed."
fi
-if [ $STEP -le 13 ] ; then
- echo ">>> Step 13: Apply squashed changes."
- patch -p1 < "$PATCH_FILE" | tee >(awk '{print $NF}' >> "$TOUCHED_FILES_FILE")
- [[ $? -eq 0 ]] || die "Applying the patch to trunk failed."
- # Stage added and modified files.
- TOUCHED_FILES=$(cat "$TOUCHED_FILES_FILE")
- for FILE in $TOUCHED_FILES ; do
- git add "$FILE"
- done
- # Stage deleted files.
- DELETED_FILES=$(git status -s -uno --porcelain | grep "^ D" \
- | awk '{print $NF}')
- for FILE in $DELETED_FILES ; do
- git rm "$FILE"
- done
- rm -f "$PATCH_FILE"
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Apply squashed changes."
rm -f "$TOUCHED_FILES_FILE"
+ apply_patch "$PATCH_FILE"
+ stage_files
+ rm -f "$PATCH_FILE"
fi
-if [ $STEP -le 14 ] ; then
- echo ">>> Step 14: Set correct version for trunk."
- restore_if_unset "MAJOR"
- restore_if_unset "MINOR"
- restore_if_unset "BUILD"
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Set correct version for trunk."
+ restore_version_if_unset
sed -e "/#define MAJOR_VERSION/s/[0-9]*$/$MAJOR/" \
-e "/#define MINOR_VERSION/s/[0-9]*$/$MINOR/" \
-e "/#define BUILD_NUMBER/s/[0-9]*$/$BUILD/" \
@@ -380,52 +283,110 @@
-i "$VERSION_FILE" || die "Patching $VERSION_FILE failed."
fi
-if [ $STEP -le 15 ] ; then
- echo ">>> Step 15: Commit to local trunk branch."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to local trunk branch."
git add "$VERSION_FILE"
git commit -F "$COMMITMSG_FILE" || die "'git commit' failed."
rm -f "$COMMITMSG_FILE"
fi
-if [ $STEP -le 16 ] ; then
- echo ">>> Step 16: Sanity check."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Sanity check."
confirm "Please check if your local checkout is sane: Inspect $VERSION_FILE, \
compile, run tests. Do you want to commit this new trunk revision to the \
repository?"
[[ $? -eq 0 ]] || die "Execution canceled."
fi
-if [ $STEP -le 17 ] ; then
- echo ">>> Step 17. Commit to SVN."
- git svn dcommit || die "'git svn dcommit' failed."
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Commit to SVN."
+ git svn dcommit | tee >(grep -E "^Committed r[0-9]+" \
+ | sed -e 's/^Committed r\([0-9]\+\)/\1/' \
+ > "$TRUNK_REVISION_FILE") \
+ || die "'git svn dcommit' failed."
+ TRUNK_REVISION=$(cat "$TRUNK_REVISION_FILE")
+ persist "TRUNK_REVISION"
+ rm -f "$TRUNK_REVISION_FILE"
fi
-if [ $STEP -le 18 ] ; then
- echo ">>> Step 18: Tag the new revision."
- restore_if_unset "MAJOR"
- restore_if_unset "MINOR"
- restore_if_unset "BUILD"
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Tag the new revision."
+ restore_version_if_unset
git svn tag $MAJOR.$MINOR.$BUILD -m "Tagging version $MAJOR.$MINOR.$BUILD" \
|| die "'git svn tag' failed."
fi
-if [ $STEP -le 19 ] ; then
- echo ">>> Step 19: Cleanup."
- restore_if_unset "CURRENT_BRANCH"
- git checkout -f $CURRENT_BRANCH
- [[ "$TEMP_BRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TEMP_BRANCH
- [[ "$BRANCHNAME" != "$CURRENT_BRANCH" ]] && git branch -D $BRANCHNAME
- [[ "$TRUNKBRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TRUNKBRANCH
-fi
+if [ -n "$CHROME_PATH" ] ; then
-if [ $STEP -le 20 ] ; then
- echo ">>> Step 20: Done!"
- restore_if_unset "MAJOR"
- restore_if_unset "MINOR"
- restore_if_unset "BUILD"
- echo "Congratulations, you have successfully created the trunk revision \
-$MAJOR.$MINOR.$BUILD. Please don't forget to update the v8rel spreadsheet, \
-and to roll this new version into Chromium."
- # Clean up all temporary files.
- rm -f "$PERSISTFILE_BASENAME"*
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Switch to Chromium checkout."
+ V8_PATH=$(pwd)
+ persist "V8_PATH"
+ cd "$CHROME_PATH"
+ initial_environment_checks
+ # Check for a clean workdir.
+ [[ -z "$(git status -s -uno)" ]] \
+ || die "Workspace is not clean. Please commit or undo your changes."
+ # Assert that the DEPS file is there.
+ [[ -w "DEPS" ]] || die "DEPS file not present or not writable; \
+current directory is: $(pwd)."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Update the checkout and create a new branch."
+ git checkout master || die "'git checkout master' failed."
+ git pull || die "'git pull' failed, please try again."
+ restore_if_unset "TRUNK_REVISION"
+ git checkout -b "v8-roll-$TRUNK_REVISION" \
+ || die "Failed to checkout a new branch."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Create and upload CL."
+ # Patch DEPS file.
+ sed -r -e "/\"v8_revision\": /s/\"[0-9]+\"/\"$TRUNK_REVISION\"/" \
+ -i DEPS
+ restore_version_if_unset
+ echo -n "Please enter the email address of a reviewer for the roll CL: "
+ read REVIEWER
+ git commit -am "Update V8 to version $MAJOR.$MINOR.$BUILD.
+
+TBR=$REVIEWER" || die "'git commit' failed."
+ git cl upload --send-mail \
+ || die "'git cl upload' failed, please try again."
+ echo "CL uploaded."
+ fi
+
+ let CURRENT_STEP+=1
+ if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Returning to V8 checkout."
+ restore_if_unset "V8_PATH"
+ cd "$V8_PATH"
+ fi
+fi # if [ -n "$CHROME_PATH" ]
+
+let CURRENT_STEP+=1
+if [ $START_STEP -le $CURRENT_STEP ] ; then
+ echo ">>> Step $CURRENT_STEP: Done!"
+ restore_version_if_unset
+ restore_if_unset "TRUNK_REVISION"
+ if [ -n "$CHROME_PATH" ] ; then
+ echo "Congratulations, you have successfully created the trunk revision \
+$MAJOR.$MINOR.$BUILD and rolled it into Chromium. Please don't forget to \
+update the v8rel spreadsheet:"
+ else
+ echo "Congratulations, you have successfully created the trunk revision \
+$MAJOR.$MINOR.$BUILD. Please don't forget to roll this new version into \
+Chromium, and to update the v8rel spreadsheet:"
+ fi
+ echo -e "$MAJOR.$MINOR.$BUILD\ttrunk\t$TRUNK_REVISION"
+ common_cleanup
+ [[ "$TRUNKBRANCH" != "$CURRENT_BRANCH" ]] && git branch -D $TRUNKBRANCH
fi
diff --git a/tools/test-wrapper-gypbuild.py b/tools/test-wrapper-gypbuild.py
index ad5449a..fda4105 100755
--- a/tools/test-wrapper-gypbuild.py
+++ b/tools/test-wrapper-gypbuild.py
@@ -73,6 +73,8 @@
choices=PROGRESS_INDICATORS, default="mono")
result.add_option("--report", help="Print a summary of the tests to be run",
default=False, action="store_true")
+ result.add_option("--download-data", help="Download missing test suite data",
+ default=False, action="store_true")
result.add_option("-s", "--suite", help="A test suite",
default=[], action="append")
result.add_option("-t", "--timeout", help="Timeout in seconds",
@@ -131,18 +133,22 @@
def ProcessOptions(options):
- if options.arch_and_mode != None and options.arch_and_mode != "":
- tokens = options.arch_and_mode.split(".")
- options.arch = tokens[0]
- options.mode = tokens[1]
- options.mode = options.mode.split(',')
+ if options.arch_and_mode == ".":
+ options.arch = []
+ options.mode = []
+ else:
+ if options.arch_and_mode != None and options.arch_and_mode != "":
+ tokens = options.arch_and_mode.split(".")
+ options.arch = tokens[0]
+ options.mode = tokens[1]
+ options.mode = options.mode.split(',')
+ options.arch = options.arch.split(',')
for mode in options.mode:
if not mode in ['debug', 'release']:
print "Unknown mode %s" % mode
return False
- options.arch = options.arch.split(',')
for arch in options.arch:
- if not arch in ['ia32', 'x64', 'arm']:
+ if not arch in ['ia32', 'x64', 'arm', 'mips']:
print "Unknown architecture %s" % arch
return False
@@ -157,6 +163,8 @@
result += ['--progress=' + options.progress]
if options.report:
result += ['--report']
+ if options.download_data:
+ result += ['--download-data']
if options.suite != []:
for suite in options.suite:
result += ['--suite=../../test/' + suite]
@@ -165,7 +173,7 @@
if options.snapshot:
result += ['--snapshot']
if options.special_command:
- result += ['--special-command=' + options.special_command]
+ result += ['--special-command="%s"' % options.special_command]
if options.valgrind:
result += ['--valgrind']
if options.cat:
@@ -189,9 +197,9 @@
if options.crankshaft:
result += ['--crankshaft']
if options.shard_count != 1:
- result += ['--shard_count=%s' % options.shard_count]
+ result += ['--shard-count=%s' % options.shard_count]
if options.shard_run != 1:
- result += ['--shard_run=%s' % options.shard_run]
+ result += ['--shard-run=%s' % options.shard_run]
if options.noprof:
result += ['--noprof']
return result
@@ -232,6 +240,18 @@
env=env)
returncodes += child.wait()
+ if len(options.mode) == 0 and len(options.arch) == 0:
+ print ">>> running tests"
+ shellpath = workspace + '/' + options.outdir
+ env['LD_LIBRARY_PATH'] = shellpath + '/lib.target'
+ shell = shellpath + '/d8'
+ child = subprocess.Popen(' '.join(args_for_children +
+ ['--shell=' + shell]),
+ shell=True,
+ cwd=workspace,
+ env=env)
+ returncodes = child.wait()
+
return returncodes
diff --git a/tools/test.py b/tools/test.py
index ecc0062..0aacd99 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright 2008 the V8 project authors. All rights reserved.
+# Copyright 2012 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:
@@ -472,7 +472,7 @@
popen_args = args
prev_error_mode = SEM_INVALID_VALUE
if utils.IsWindows():
- popen_args = '"' + subprocess.list2cmdline(args) + '"'
+ popen_args = subprocess.list2cmdline(args)
if context.suppress_dialogs:
# Try to change the error mode to avoid dialogs on fatal errors. Don't
# touch any existing error mode flags by merging the existing error mode.
@@ -631,9 +631,15 @@
def GetBuildRequirements(self, path, context):
return self.GetConfiguration(context).GetBuildRequirements()
+ def DownloadData(self, context):
+ config = self.GetConfiguration(context)
+ if 'DownloadData' in dir(config):
+ config.DownloadData()
+
def AddTestsToList(self, result, current_path, path, context, mode):
- for v in self.GetConfiguration(context).VariantFlags():
- tests = self.GetConfiguration(context).ListTests(current_path, path, mode, v)
+ config = self.GetConfiguration(context)
+ for v in config.VariantFlags():
+ tests = config.ListTests(current_path, path, mode, v)
for t in tests: t.variant_flags = v
result += tests
@@ -655,6 +661,12 @@
result += test.GetBuildRequirements(rest, context)
return result
+ def DownloadData(self, path, context):
+ (name, rest) = CarCdr(path)
+ for test in self.tests:
+ if not name or name.match(test.GetName()):
+ test.DownloadData(context)
+
def ListTests(self, current_path, path, context, mode, variant_flags):
(name, rest) = CarCdr(path)
result = [ ]
@@ -674,8 +686,8 @@
'debug' : '_g',
'release' : '' }
FLAGS = {
- 'debug' : ['--enable-slow-asserts', '--debug-code', '--verify-heap'],
- 'release' : []}
+ 'debug' : ['--nobreak-on-abort', '--enable-slow-asserts', '--debug-code', '--verify-heap'],
+ 'release' : ['--nobreak-on-abort']}
TIMEOUT_SCALEFACTOR = {
'debug' : 4,
'release' : 1 }
@@ -711,7 +723,7 @@
def GetTimeout(self, testcase, mode):
result = self.timeout * TIMEOUT_SCALEFACTOR[mode]
if '--stress-opt' in self.GetVmFlags(testcase, mode):
- return result * 2
+ return result * 4
else:
return result
@@ -850,6 +862,9 @@
elif self.op == '==':
inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs))
return not inter.IsEmpty()
+ elif self.op == '!=':
+ inter = self.left.GetOutcomes(env, defs).Intersect(self.right.GetOutcomes(env, defs))
+ return inter.IsEmpty()
else:
assert self.op == '&&'
return self.left.Evaluate(env, defs) and self.right.Evaluate(env, defs)
@@ -932,6 +947,9 @@
elif self.Current(2) == '==':
self.AddToken('==')
self.Advance(2)
+ elif self.Current(2) == '!=':
+ self.AddToken('!=')
+ self.Advance(2)
else:
return None
return self.tokens
@@ -984,7 +1002,7 @@
return None
-BINARIES = ['==']
+BINARIES = ['==', '!=']
def ParseOperatorExpression(scan):
left = ParseAtomicExpression(scan)
if not left: return None
@@ -1006,7 +1024,7 @@
right = ParseOperatorExpression(scan)
if not right:
return None
- left= Operation(left, 'if', right)
+ left = Operation(left, 'if', right)
return left
@@ -1186,6 +1204,8 @@
default='scons')
result.add_option("--report", help="Print a summary of the tests to be run",
default=False, action="store_true")
+ result.add_option("--download-data", help="Download missing test suite data",
+ default=False, action="store_true")
result.add_option("-s", "--suite", help="A test suite",
default=[], action="append")
result.add_option("-t", "--timeout", help="Timeout in seconds",
@@ -1211,6 +1231,7 @@
dest="suppress_dialogs", default=True, action="store_true")
result.add_option("--no-suppress-dialogs", help="Display Windows dialogs for crashing tests",
dest="suppress_dialogs", action="store_false")
+ result.add_option("--mips-arch-variant", help="mips architecture variant: mips32r1/mips32r2", default="mips32r2");
result.add_option("--shell", help="Path to V8 shell", default="d8")
result.add_option("--isolates", help="Whether to test isolates", default=False, action="store_true")
result.add_option("--store-unexpected-output",
@@ -1272,6 +1293,9 @@
if options.snapshot:
options.scons_flags.append("snapshot=on")
global VARIANT_FLAGS
+ if options.mips_arch_variant:
+ options.scons_flags.append("mips_arch_variant=" + options.mips_arch_variant)
+
if options.stress_only:
VARIANT_FLAGS = [['--stress-opt', '--always-opt']]
if options.nostress:
@@ -1452,6 +1476,11 @@
root.GetTestStatus(context, sections, defs)
config = Configuration(sections, defs)
+ # Download missing test suite data if requested.
+ if options.download_data:
+ for path in paths:
+ root.DownloadData(path, context)
+
# List the tests
all_cases = [ ]
all_unused = [ ]
diff --git a/tools/tickprocessor-driver.js b/tools/tickprocessor-driver.js
index 4201e43..9af5ab6 100644
--- a/tools/tickprocessor-driver.js
+++ b/tools/tickprocessor-driver.js
@@ -52,6 +52,7 @@
var tickProcessor = new TickProcessor(
new (entriesProviders[params.platform])(params.nm),
params.separateIc,
+ params.callGraphSize,
params.ignoreUnknown,
params.stateFilter,
snapshotLogProcessor);
diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js
index 5f57835..05a3369 100644
--- a/tools/tickprocessor.js
+++ b/tools/tickprocessor.js
@@ -146,7 +146,12 @@
function TickProcessor(
- cppEntriesProvider, separateIc, ignoreUnknown, stateFilter, snapshotLogProcessor) {
+ cppEntriesProvider,
+ separateIc,
+ callGraphSize,
+ ignoreUnknown,
+ stateFilter,
+ snapshotLogProcessor) {
LogReader.call(this, {
'shared-library': { parsers: [null, parseInt, parseInt],
processor: this.processSharedLibrary },
@@ -181,6 +186,7 @@
'end-code-region': null });
this.cppEntriesProvider_ = cppEntriesProvider;
+ this.callGraphSize_ = callGraphSize;
this.ignoreUnknown_ = ignoreUnknown;
this.stateFilter_ = stateFilter;
this.snapshotLogProcessor_ = snapshotLogProcessor;
@@ -240,6 +246,7 @@
TickProcessor.CALL_PROFILE_CUTOFF_PCT = 2.0;
+TickProcessor.CALL_GRAPH_SIZE = 5;
/**
* @override
@@ -535,7 +542,7 @@
padLeft(rec.parentTotalPercent.toFixed(1), 5) + '% ' +
indentStr + rec.internalFuncName);
// Limit backtrace depth.
- if (indent < 10) {
+ if (indent < 2 * self.callGraphSize_) {
self.printHeavyProfile(rec.children, indent + 2);
}
// Delimit top-level functions.
@@ -764,6 +771,8 @@
'Show only ticks from OTHER VM state'],
'-e': ['stateFilter', TickProcessor.VmStates.EXTERNAL,
'Show only ticks from EXTERNAL VM state'],
+ '--call-graph-size': ['callGraphSize', TickProcessor.CALL_GRAPH_SIZE,
+ 'Set the call graph size'],
'--ignore-unknown': ['ignoreUnknown', true,
'Exclude ticks of unknown code entries from processing'],
'--separate-ic': ['separateIc', true,
@@ -792,6 +801,7 @@
snapshotLogFileName: null,
platform: 'unix',
stateFilter: null,
+ callGraphSize: 5,
ignoreUnknown: false,
separateIc: false,
nm: 'nm'
diff --git a/tools/utils.py b/tools/utils.py
index fb94d14..232314c 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -61,6 +61,8 @@
return 'openbsd'
elif id == 'SunOS':
return 'solaris'
+ elif id == 'NetBSD':
+ return 'netbsd'
else:
return None