diff --git a/CHANGES.md b/CHANGES.md index 4fec3d0..d9e9ba1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ ## Unreleased changes +* #23 support V8 4.4.x and 4.5.x + * #25 fix ia32 build * #20 want ::jsfunctions -l diff --git a/src/mdb_v8.c b/src/mdb_v8.c index 1f664ba..c959a8a 100644 --- a/src/mdb_v8.c +++ b/src/mdb_v8.c @@ -110,6 +110,7 @@ intptr_t V8_NotStringTag; intptr_t V8_StringEncodingMask; intptr_t V8_TwoByteStringTag; intptr_t V8_AsciiStringTag; +intptr_t V8_OneByteStringTag; intptr_t V8_StringRepresentationMask; intptr_t V8_SeqStringTag; intptr_t V8_ConsStringTag; @@ -130,8 +131,8 @@ static intptr_t V8_DICT_SHIFT; static intptr_t V8_DICT_PREFIX_SIZE; static intptr_t V8_DICT_ENTRY_SIZE; static intptr_t V8_DICT_START_INDEX; -static intptr_t V8_FIELDINDEX_MASK; -static intptr_t V8_FIELDINDEX_SHIFT; +static intptr_t V8_PROPINDEX_MASK; +static intptr_t V8_PROPINDEX_SHIFT; static intptr_t V8_PROP_IDX_CONTENT; static intptr_t V8_PROP_IDX_FIRST; static intptr_t V8_PROP_TYPE_FIELD; @@ -168,6 +169,7 @@ intptr_t V8_CONTEXT_IDX_GLOBAL; intptr_t V8_SCOPEINFO_IDX_NPARAMS; intptr_t V8_SCOPEINFO_IDX_NSTACKLOCALS; +intptr_t V8_SCOPEINFO_OFFSET_STACK_LOCALS; intptr_t V8_SCOPEINFO_IDX_NCONTEXTLOCALS; intptr_t V8_SCOPEINFO_IDX_FIRST_VARS; @@ -225,13 +227,14 @@ ssize_t V8_OFF_STRING_LENGTH; #define V8_CONSTANT_OPTIONAL 1 #define V8_CONSTANT_HASFALLBACK 2 #define V8_CONSTANT_REMOVED 4 +#define V8_CONSTANT_ADDED 8 -#define V8_CONSTANT_MAJORSHIFT 3 +#define V8_CONSTANT_MAJORSHIFT 4 #define V8_CONSTANT_MAJORMASK ((1 << 4) - 1) #define V8_CONSTANT_MAJOR(flags) \ (((flags) >> V8_CONSTANT_MAJORSHIFT) & V8_CONSTANT_MAJORMASK) -#define V8_CONSTANT_MINORSHIFT 7 +#define V8_CONSTANT_MINORSHIFT 8 #define V8_CONSTANT_MINORMASK ((1 << 9) - 1) #define V8_CONSTANT_MINOR(flags) \ (((flags) >> V8_CONSTANT_MINORSHIFT) & V8_CONSTANT_MINORMASK) @@ -244,6 +247,10 @@ ssize_t V8_OFF_STRING_LENGTH; (V8_CONSTANT_REMOVED | \ ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) +#define V8_CONSTANT_ADDED_SINCE(maj, min) \ + (V8_CONSTANT_ADDED | \ + ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT)) + /* * Table of constants used directly by this file. */ @@ -266,7 +273,10 @@ static v8_constant_t v8_constants[] = { { &V8_NotStringTag, "v8dbg_NotStringTag" }, { &V8_StringEncodingMask, "v8dbg_StringEncodingMask" }, { &V8_TwoByteStringTag, "v8dbg_TwoByteStringTag" }, - { &V8_AsciiStringTag, "v8dbg_AsciiStringTag" }, + { &V8_AsciiStringTag, "v8dbg_AsciiStringTag", + V8_CONSTANT_REMOVED_SINCE(3, 29) }, + { &V8_OneByteStringTag, "v8dbg_OneByteStringTag", + V8_CONSTANT_ADDED_SINCE(3, 29) }, { &V8_StringRepresentationMask, "v8dbg_StringRepresentationMask" }, { &V8_SeqStringTag, "v8dbg_SeqStringTag" }, { &V8_ConsStringTag, "v8dbg_ConsStringTag" }, @@ -298,9 +308,9 @@ static v8_constant_t v8_constants[] = { V8_CONSTANT_FALLBACK(3, 11), 3 }, { &V8_DICT_START_INDEX, "v8dbg_dict_start_index", V8_CONSTANT_FALLBACK(3, 11), 3 }, - { &V8_FIELDINDEX_MASK, "v8dbg_fieldindex_mask", + { &V8_PROPINDEX_MASK, "v8dbg_propindex_mask", V8_CONSTANT_FALLBACK(3, 26), 0x3ff00000 }, - { &V8_FIELDINDEX_SHIFT, "v8dbg_fieldindex_shift", + { &V8_PROPINDEX_SHIFT, "v8dbg_propindex_shift", V8_CONSTANT_FALLBACK(3, 26), 20 }, { &V8_ISSHARED_SHIFT, "v8dbg_isshared_shift", V8_CONSTANT_FALLBACK(3, 11), 0 }, @@ -346,14 +356,17 @@ static v8_constant_t v8_constants[] = { V8_CONSTANT_FALLBACK(0, 0), 3 }, { &V8_SCOPEINFO_IDX_NPARAMS, "v8dbg_scopeinfo_idx_nparams", - V8_CONSTANT_FALLBACK(0, 0), 1 }, + V8_CONSTANT_FALLBACK(3, 7), 1 }, { &V8_SCOPEINFO_IDX_NSTACKLOCALS, "v8dbg_scopeinfo_idx_nstacklocals", - V8_CONSTANT_FALLBACK(0, 0), 2 }, + V8_CONSTANT_FALLBACK(3, 7), 2 }, + { &V8_SCOPEINFO_OFFSET_STACK_LOCALS, + "v8dbg_scopeinfo_offset_stack_locals", + V8_CONSTANT_FALLBACK(4, 4), 1 }, { &V8_SCOPEINFO_IDX_NCONTEXTLOCALS, "v8dbg_scopeinfo_idx_ncontextlocals", - V8_CONSTANT_FALLBACK(0, 0), 3 }, + V8_CONSTANT_FALLBACK(3, 7), 3 }, { &V8_SCOPEINFO_IDX_FIRST_VARS, "v8dbg_scopeinfo_idx_first_vars", - V8_CONSTANT_FALLBACK(0, 0), 4 }, + V8_CONSTANT_FALLBACK(4, 5), 6 }, }; static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]); @@ -363,6 +376,7 @@ typedef struct v8_offset { const char *v8o_class; const char *v8o_member; boolean_t v8o_optional; + uint32_t v8o_flags; } v8_offset_t; static v8_offset_t v8_offsets[] = { @@ -399,7 +413,11 @@ static v8_offset_t v8_offsets[] = { { &V8_OFF_JSREGEXP_DATA, "JSRegExp", "data", B_TRUE }, { &V8_OFF_MAP_CONSTRUCTOR, - "Map", "constructor" }, + "Map", "constructor", + B_FALSE, V8_CONSTANT_REMOVED_SINCE(4, 3)}, + { &V8_OFF_MAP_CONSTRUCTOR, + "Map", "constructor_or_backpointer", + B_FALSE, V8_CONSTANT_ADDED_SINCE(4, 3)}, { &V8_OFF_MAP_INOBJECT_PROPERTIES, "Map", "inobject_properties" }, { &V8_OFF_MAP_INSTANCE_ATTRIBUTES, @@ -539,6 +557,18 @@ v8_version_older(uintptr_t v8_major, uintptr_t v8_minor, uint32_t flags) { v8_minor < V8_CONSTANT_MINOR(flags))); } +/* + * Returns 1 if the V8 version v8_major.v8.minor is newer or equal than + * the V8 version represented by "flags". + * Returns 0 otherwise. + */ +static int +v8_version_at_least(uintptr_t v8_major, uintptr_t v8_minor, uint32_t flags) { + return (v8_major > V8_CONSTANT_MAJOR(flags) || + (v8_major == V8_CONSTANT_MAJOR(flags) && + v8_minor >= V8_CONSTANT_MINOR(flags))); +} + /* * Invoked when this dmod is initially loaded to load the set of classes, enums, * and other constants from the metadata in the target binary. @@ -551,6 +581,9 @@ autoconfigure(v8_cfg_t *cfgp) struct v8_constant *cnp; int ii; int failed = 0; + int constant_optional, constant_removed, constant_added; + int offset_optional, offset_removed, offset_added; + int v8_older, v8_at_least; assert(v8_classes == NULL); @@ -585,9 +618,16 @@ autoconfigure(v8_cfg_t *cfgp) continue; } - if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL) && - (!(cnp->v8c_flags & V8_CONSTANT_REMOVED) || - v8_version_older(v8_major, v8_minor, cnp->v8c_flags))) { + constant_optional = cnp->v8c_flags & V8_CONSTANT_OPTIONAL; + constant_removed = cnp->v8c_flags & V8_CONSTANT_REMOVED; + constant_added = cnp->v8c_flags & V8_CONSTANT_ADDED; + v8_older = v8_version_older(v8_major, v8_minor, cnp->v8c_flags); + v8_at_least = v8_version_at_least(v8_major, + v8_minor, cnp->v8c_flags); + + if (!constant_optional && + (!constant_removed || v8_older) && + (!constant_added || v8_at_least)) { mdb_warn("failed to read \"%s\"", cnp->v8c_symbol); failed++; continue; @@ -712,9 +752,20 @@ autoconfigure(v8_cfg_t *cfgp) continue; } - mdb_warn("couldn't find class \"%s\", field \"%s\"\n", - offp->v8o_class, offp->v8o_member); - failed++; + offset_optional = offp->v8o_flags & V8_CONSTANT_OPTIONAL; + offset_removed = offp->v8o_flags & V8_CONSTANT_REMOVED; + offset_added = offp->v8o_flags & V8_CONSTANT_ADDED; + v8_older = v8_version_older(v8_major, v8_minor, cnp->v8c_flags); + v8_at_least = v8_version_at_least(v8_major, + v8_minor, offp->v8o_flags); + + if (!offset_optional && + (!offset_removed || v8_older) && + (!offset_added || v8_at_least)) { + mdb_warn("couldn't find class \"%s\", field \"%s\"\n", + offp->v8o_class, offp->v8o_member); + failed++; + } } if (!((V8_OFF_SEQASCIISTR_CHARS != -1) ^ @@ -758,6 +809,24 @@ autoconfigure(v8_cfg_t *cfgp) if (V8_OFF_MAP_BIT_FIELD2 == -1) V8_OFF_MAP_BIT_FIELD2 = V8_OFF_MAP_INSTANCE_ATTRIBUTES + 3; + /* + * V8_SCOPEINFO_IDX_FIRST_VARS' value was 4 in V8 3.7 and up, + * then 5 when StrongModeFreeVariableCount was added with + * https://codereview.chromium.org/1005063002, and 6 when + * ContextGlobalCount was added with + * https://codereview.chromium.org/1218783005. + * Since the current V8_CONSTANT_FALLBACK macro doesn't allow + * us to specify different values for different V8 versions, + * these are hardcoded below. + */ + if (V8_SCOPEINFO_IDX_FIRST_VARS == -1) { + if (v8_major > 4 || (v8_major == 4 && v8_minor >= 3)) { + V8_SCOPEINFO_IDX_FIRST_VARS = 5; + } else if (v8_major > 3 || (v8_major == 3 && v8_minor >= 7)) { + V8_SCOPEINFO_IDX_FIRST_VARS = 4; + } + } + return (failed ? -1 : 0); } diff --git a/src/mdb_v8_context.c b/src/mdb_v8_context.c index 03f025d..5edd6ab 100644 --- a/src/mdb_v8_context.c +++ b/src/mdb_v8_context.c @@ -67,12 +67,13 @@ typedef struct { v8scopeinfo_vartype_t v8vti_vartype; const char *v8vti_label; intptr_t *v8vti_idx_countp; + intptr_t *v8vti_offset; } v8scopeinfo_vartype_info_t; static v8scopeinfo_vartype_info_t v8scopeinfo_vartypes[] = { { V8SV_PARAMS, "parameter", &V8_SCOPEINFO_IDX_NPARAMS }, { V8SV_STACKLOCALS, "stack local variable", - &V8_SCOPEINFO_IDX_NSTACKLOCALS }, + &V8_SCOPEINFO_IDX_NSTACKLOCALS, &V8_SCOPEINFO_OFFSET_STACK_LOCALS }, { V8SV_CONTEXTLOCALS, "context local variable", &V8_SCOPEINFO_IDX_NCONTEXTLOCALS }, }; @@ -385,13 +386,47 @@ v8scopeinfo_iter_vars(v8scopeinfo_t *sip, assert(vtip != NULL); nvars = v8scopeinfo_vartype_nvars(sip, scopevartype); + /* + * Skip to the start of the ScopeInfo's dynamic part. See mdb_v8_db.h + * for more details on the layout of ScopeInfo objects. + */ nskip = V8_SCOPEINFO_IDX_FIRST_VARS; + + /* + * Iterate over variable types so that we can add the offset from the + * beginning of the actual data (the dynamic part) to the region of the + * dynamic part that is specific to the variable type we're interested + * in. + */ for (i = 0; i < v8scopeinfo_nvartypes; i++) { ogrp = &v8scopeinfo_vartypes[i]; - if (*(ogrp->v8vti_idx_countp) >= *(vtip->v8vti_idx_countp)) { - continue; + + /* + * In the variable/dynamic part of a ScopeInfo layout, some + * variable types have static metadata, e.g stack local entries + * have a StackLocalFirstSlot, before the actual data. Add that + * offset for each variable type, including for the one we're + * interested in. + */ + if (v8scopeinfo_vartypes[i].v8vti_offset != NULL && + *(v8scopeinfo_vartypes[i].v8vti_offset) != -1) { + nskip += *(v8scopeinfo_vartypes[i].v8vti_offset); + } + + /* + * If the current variable type is the one we're interested in, + * do not add anything to the offset. We're done. + */ + if (*(ogrp->v8vti_idx_countp) == *(vtip->v8vti_idx_countp)) { + break; } + /* + * The data for the current variable type is before the one + * we're interested in in the variable part of the ScopeInfo + * layout. Add the number of entries for this variable type to + * the offset. + */ nskip += v8scopeinfo_vartype_nvars(sip, ogrp->v8vti_vartype); } diff --git a/src/mdb_v8_impl.h b/src/mdb_v8_impl.h index fac16e5..c4fd66a 100644 --- a/src/mdb_v8_impl.h +++ b/src/mdb_v8_impl.h @@ -62,6 +62,7 @@ extern intptr_t V8_SCOPEINFO_IDX_FIRST_VARS; extern intptr_t V8_SCOPEINFO_IDX_NCONTEXTLOCALS; extern intptr_t V8_SCOPEINFO_IDX_NPARAMS; extern intptr_t V8_SCOPEINFO_IDX_NSTACKLOCALS; +extern intptr_t V8_SCOPEINFO_OFFSET_STACK_LOCALS; extern intptr_t V8_HeapObjectTag; extern intptr_t V8_HeapObjectTagMask; diff --git a/src/v8dbg.h b/src/v8dbg.h index 22871be..38b1d67 100644 --- a/src/v8dbg.h +++ b/src/v8dbg.h @@ -49,7 +49,10 @@ #define V8_TYPE_STRING(type) (((type) & V8_IsNotStringMask) == V8_StringTag) #define V8_STRENC_ASCII(type) \ - (((type) & V8_StringEncodingMask) == V8_AsciiStringTag) + ((V8_AsciiStringTag != -1 && \ + ((type) & V8_StringEncodingMask) == V8_AsciiStringTag) || \ + (V8_OneByteStringTag != -1 && \ + ((type) & V8_StringEncodingMask) == V8_OneByteStringTag)) #define V8_STRREP_SEQ(type) \ (((type) & V8_StringRepresentationMask) == V8_SeqStringTag) @@ -72,6 +75,6 @@ ((V8_SMI_VALUE(x) & V8_PROP_TYPE_MASK) == V8_PROP_TYPE_FIELD) #define V8_PROP_FIELDINDEX(value) \ - ((V8_SMI_VALUE(value) & V8_FIELDINDEX_MASK) >> V8_FIELDINDEX_SHIFT) + ((V8_SMI_VALUE(value) & V8_PROPINDEX_MASK) >> V8_PROPINDEX_SHIFT) #endif /* _V8DBG_H */ diff --git a/test/standalone/tst.postmortem_details.js b/test/standalone/tst.postmortem_details.js index 7c91006..76f789f 100644 --- a/test/standalone/tst.postmortem_details.js +++ b/test/standalone/tst.postmortem_details.js @@ -65,7 +65,7 @@ gcore.on('exit', function (code) { var mdb = spawn('mdb', args, { stdio: 'pipe' }); - mdb.on('exit', function (code2) { + mdb.stdin.on('end', function (code2) { unlinkSync(tmpfile); var retained = '; core retained as ' + corefile; diff --git a/test/standalone/tst.postmortem_findjsobjects.js b/test/standalone/tst.postmortem_findjsobjects.js index 4bd10f4..8d4bed4 100644 --- a/test/standalone/tst.postmortem_findjsobjects.js +++ b/test/standalone/tst.postmortem_findjsobjects.js @@ -43,7 +43,7 @@ gcore.on('exit', function (code) { var mdb = spawn('mdb', args, { stdio: 'pipe' }); - mdb.on('exit', function (code2) { + mdb.stdin.on('end', function (code2) { var retained = '; core retained as ' + corefile; if (code2 != 0) {