From 853e3377f5540ff04989b4dd58437515dc237d86 Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Thu, 24 Sep 2015 10:37:49 -0700 Subject: [PATCH] joyent/mdb_v8#36: Update mdb_v8 for V8 4.6.x This change makes mdb_v8's test suite pass with https://github.com/nodejs/node/tree/vee-eight-4.6, which is the branch where V8 4.6.x is integrated into node's master. There are two main changes. The first one is that Map's "inobject_properties" property has been renamed to "inobject_properties_or_constructor_function_index". Since mdb_v8's doesn't support new V8 primitives yet, we only care about using the proper name for this property, not about the fact that it has a different semantic for these new V8 primitives. The second one is that typed arrays, which are used to represent Buffer instances since node v4.0.0, store their underlying storage slightly differently. For V8 versions 4.6 and later, we now use the JSArrayBufferView and the JSArrayBuffer properties to get access to that underlying storage, as it seems more reliable and less likely to change than accessing the first elements' slot, whose representation has changed between V8 4.5 and 4.6. It used to be a ExternalUint8Array, and it is now a FixedTypedArray casted as a FixedTypedArrayBase. --- src/mdb_v8.c | 112 +++++++++++++++++++--- test/standalone/tst.postmortem_details.js | 43 +++++++-- 2 files changed, 130 insertions(+), 25 deletions(-) diff --git a/src/mdb_v8.c b/src/mdb_v8.c index 115bc1a..6d1b2ed 100644 --- a/src/mdb_v8.c +++ b/src/mdb_v8.c @@ -198,6 +198,7 @@ ssize_t V8_OFF_JSOBJECT_PROPERTIES; ssize_t V8_OFF_MAP_CONSTRUCTOR; ssize_t V8_OFF_MAP_CONSTRUCTOR_OR_BACKPOINTER; ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES; +ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES_OR_CTOR_FUN_INDEX; ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES; ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS; ssize_t V8_OFF_MAP_INSTANCE_SIZE; @@ -224,6 +225,9 @@ ssize_t V8_OFF_SLICEDSTRING_PARENT; ssize_t V8_OFF_SLICEDSTRING_OFFSET; ssize_t V8_OFF_STRING_LENGTH; ssize_t V8_OFF_JSTYPEDARRAY_LENGTH; +ssize_t V8_OFF_JSARRAYBUFFER_BACKINGSTORE; +ssize_t V8_OFF_JSARRAYBUFFERVIEW_BUFFER; +ssize_t V8_OFF_JSARRAYBUFFERVIEW_CONTENT_OFFSET; /* see node_string.h */ #define NODE_OFF_EXTSTR_DATA sizeof (uintptr_t) @@ -424,7 +428,17 @@ static v8_offset_t v8_offsets[] = { "Map", "constructor_or_backpointer", B_FALSE, V8_CONSTANT_ADDED_SINCE(4, 3)}, { &V8_OFF_MAP_INOBJECT_PROPERTIES, - "Map", "inobject_properties" }, + "Map", "inobject_properties", + B_FALSE, V8_CONSTANT_REMOVED_SINCE(4, 6) }, +#ifdef _LP64 + { &V8_OFF_MAP_INOBJECT_PROPERTIES_OR_CTOR_FUN_INDEX, + "Map", "inobject_properties_or_constructor_function_index", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 8 }, +#else + { &V8_OFF_MAP_INOBJECT_PROPERTIES_OR_CTOR_FUN_INDEX, + "Map", "inobject_properties_or_constructor_function_index", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 4 }, +#endif { &V8_OFF_MAP_INSTANCE_ATTRIBUTES, "Map", "instance_attributes" }, { &V8_OFF_MAP_INSTANCE_DESCRIPTORS, @@ -482,6 +496,26 @@ static v8_offset_t v8_offsets[] = { "JSTypedArray", "length", B_FALSE, V8_CONSTANT_FALLBACK(4, 5), 27 }, #endif +#ifdef _LP64 + { &V8_OFF_JSARRAYBUFFER_BACKINGSTORE, + "JSArrayBuffer", "backing_store", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 23 }, +#else + { &V8_OFF_JSARRAYBUFFER_BACKINGSTORE, + "JSArrayBuffer", "backing_store", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 11 }, +#endif + { &V8_OFF_JSARRAYBUFFERVIEW_BUFFER, + "JSArrayBufferView", "buffer" }, +#ifdef _LP64 + { &V8_OFF_JSARRAYBUFFERVIEW_CONTENT_OFFSET, + "JSArrayBufferView", "byte_offset", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 31 }, +#else + { &V8_OFF_JSARRAYBUFFERVIEW_CONTENT_OFFSET, + "JSArrayBufferView", "byte_offset", + B_FALSE, V8_CONSTANT_FALLBACK(4, 6), 15 }, +#endif }; static int v8_noffsets = sizeof (v8_offsets) / sizeof (v8_offsets[0]); @@ -877,6 +911,10 @@ autoconfigure(v8_cfg_t *cfgp) if (V8_OFF_MAP_CONSTRUCTOR_OR_BACKPOINTER != -1) V8_OFF_MAP_CONSTRUCTOR = V8_OFF_MAP_CONSTRUCTOR_OR_BACKPOINTER; + if (V8_OFF_MAP_INOBJECT_PROPERTIES_OR_CTOR_FUN_INDEX != -1) + V8_OFF_MAP_INOBJECT_PROPERTIES = + V8_OFF_MAP_INOBJECT_PROPERTIES_OR_CTOR_FUN_INDEX; + return (failed ? -1 : 0); } @@ -5420,6 +5458,8 @@ dcmd_nodebuffer(uintptr_t addr, uint_t flags, int argc, char *bufp = buf; size_t len = sizeof (buf); uintptr_t elts, rawbuf; + uintptr_t arraybuffer_view_buffer; + uintptr_t arraybufferview_content_offset; /* * The undocumented "-f" option allows users to override constructor @@ -5440,21 +5480,63 @@ dcmd_nodebuffer(uintptr_t addr, uint_t flags, int argc, } } - /* - * This works for Buffer instance in node < 4.0 because they use - * elements slots to reference the backing storage. It also works - * with Buffer in node >= 4.0 because they actually are typed arrays - * and typed arrays use elements slots to store the external data. - * We could use the "backing_store" member of the JSArrayBuffer - * associated to a typed array instead, but using elements for - * both "old" Buffer instances and new ones has the benefit of - * being able to reuse more code. - */ - if (read_heap_ptr(&elts, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) - return (DCMD_ERR); + if (strcmp(buf, "Buffer") == 0 || + V8_OFF_JSARRAYBUFFER_BACKINGSTORE == -1) { + /* + * This works for Buffer instances in node < 4.0 because they + * use elements slots to reference the backing storage. If + * the constructor name is not "Buffer" but "Uint8Array" and + * V8_OFF_JSARRAYBUFFER_BACKINGSTORE == -1, it means we are in + * the range of node versions >= 4.0 and <= 4.1 that ship with + * V8 4.5.x. For these versions, it also works because Buffer + * instances are actually typed arrays but their backing storage + * an ExternalUint8Arrayelements whose address is stored in the + * first element's slot. + */ + if (read_heap_ptr(&elts, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0) + return (DCMD_ERR); - if (obj_v8internal(elts, 0, &rawbuf) != 0) - return (DCMD_ERR); + if (obj_v8internal(elts, 0, &rawbuf) != 0) + return (DCMD_ERR); + } else { + /* + * The buffer instance's constructor name is Uint8Array, and + * V8_OFF_JSARRAYBUFFER_BACKINGSTORE != -1, which means that + * we're dealing with a node version that ships with V8 4.6 or + * later. For these versions, buffer instances store their data + * as a typed array, but this time instead of having the backing + * store as an ExternalUint8Array referenced from an element + * slot, it can be found at two different locations: + * + * 1. As a FixedTypedArray casted as a FixedTypedArrayBase in an + * element slot. + * + * 2. As the "backing_store" property of the corresponding + * JSArrayBuffer. + * + * The second way to retrieve the backing store seems like + * it will be less likely to change, and is thus the one we're + * using. + */ + if (V8_OFF_JSARRAYBUFFER_BACKINGSTORE == -1 || + V8_OFF_JSARRAYBUFFERVIEW_BUFFER == -1 || + V8_OFF_JSARRAYBUFFERVIEW_CONTENT_OFFSET == -1) + return (DCMD_ERR); + + if (read_heap_ptr(&arraybuffer_view_buffer, addr, + V8_OFF_JSARRAYBUFFERVIEW_BUFFER) != 0) + return (DCMD_ERR); + + if (read_heap_ptr(&rawbuf, arraybuffer_view_buffer, + V8_OFF_JSARRAYBUFFER_BACKINGSTORE) != 0) + return (DCMD_ERR); + + if (read_heap_smi(&arraybufferview_content_offset, addr, + V8_OFF_JSARRAYBUFFERVIEW_CONTENT_OFFSET) != 0) + return (DCMD_ERR); + + rawbuf += arraybufferview_content_offset; + } mdb_printf("%p\n", rawbuf); return (DCMD_OK); diff --git a/test/standalone/tst.postmortem_details.js b/test/standalone/tst.postmortem_details.js index 257c098..040fee5 100644 --- a/test/standalone/tst.postmortem_details.js +++ b/test/standalone/tst.postmortem_details.js @@ -14,9 +14,17 @@ var os = require('os'); var path = require('path'); var util = require('util'); -var NODE_MAJOR = Number(process.versions.node.split('.')[0]); +var NODE_VERSIONS = process.versions.node.split('.'); +var NODE_MAJOR = Number(NODE_VERSIONS[0]); assert.equal(isNaN(NODE_MAJOR), false); +var V8_VERSIONS = process.versions.v8.split('.'); +var V8_MAJOR = Number(V8_VERSIONS[0]); +assert.equal(isNaN(V8_MAJOR), false); + +var V8_MINOR = Number(V8_VERSIONS[1]); +assert.equal(isNaN(V8_MINOR), false); + /* * We're going to look specifically for this function and buffer in the core * file. @@ -135,9 +143,17 @@ gcore.on('exit', function (code) { assert.equal(testlines.length, 1); assert.equal(testlines[0], '0x' + buffer + ': Hello'); }); - verifiers.push(function verifyV8internal(testlines) { - assert.deepEqual(testlines, [ buffer ]); - }); + // Buffer instances are implemented as typed arrays in Node + // versions that ship with V8 >= 4.6. Typed arrays in these versions + // of V8 do not *directly* store their underlying buffer as an + // "internal" element, so ::v8internal would not output its address. + // It would instead output the address of a FixedTypedArrayBase + // instance. Thus, skip the test. + if (V8_MAJOR < 4 || V8_MAJOR === 4 && V8_MINOR < 6) { + verifiers.push(function verifyV8internal(testlines) { + assert.deepEqual(testlines, [ buffer ]); + }); + } verifiers.push(function verifyJsfunctionN(testlines) { assert.equal(testlines.length, 2); var parts = testlines[1].trim().split(/\s+/); @@ -212,12 +228,19 @@ gcore.on('exit', function (code) { mdb.stdin.write('::cat ' + tmpfile + ' | ::nodebuffer | ::eval "./ccccc"\n'); - mdb.stdin.write('!echo test: v8internal\n'); - mdb.stdin.write('::cat ' + tmpfile + - ' | ::v8print ! awk \'$2 == "elements"{' + - 'print $4 }\' > ' + tmpfile + '\n'); - mdb.stdin.write('::cat ' + tmpfile + ' | ::v8internal 0\n'); - + // Buffer instances are implemented as typed arrays in Node + // versions that ship with V8 >= 4.6. Typed arrays in these versions + // of V8 do not *directly* store their underlying buffer as an + // "internal" element, so ::v8internal would not output its address. + // It would instead output the address of a FixedTypedArrayBase + // instance. Thus, skip the test. + if (V8_MAJOR < 4 || V8_MAJOR === 4 && V8_MINOR < 6) { + mdb.stdin.write('!echo test: v8internal\n'); + mdb.stdin.write('::cat ' + tmpfile + + ' | ::v8print ! awk \'$2 == "elements"{' + + 'print $4 }\' > ' + tmpfile + '\n'); + mdb.stdin.write('::cat ' + tmpfile + ' | ::v8internal 0\n'); + } mdb.stdin.write('!echo test: jsfunctions -n\n'); mdb.stdin.write('::jsfunctions -n myTestFunction ! cat\n'); mdb.stdin.write('!echo test: jsfunctions -s\n');