The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / mdb_v8 / mdb_v8.c @ e851fef6

History | View | Annotate | Download (100 KB)

1
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2
 *
3
 * Permission is hereby granted, free of charge, to any person obtaining a copy
4
 * of this software and associated documentation files (the "Software"), to
5
 * deal in the Software without restriction, including without limitation the
6
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
 * sell copies of the Software, and to permit persons to whom the Software is
8
 * furnished to do so, subject to the following conditions:
9
 *
10
 * The above copyright notice and this permission notice shall be included in
11
 * all copies or substantial portions of the Software.
12
 *
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
 * IN THE SOFTWARE.
20
 */
21

    
22
/*
23
 * mdb(1M) module for debugging the V8 JavaScript engine.  This implementation
24
 * makes heavy use of metadata defined in the V8 binary for inspecting in-memory
25
 * structures.  Canned configurations can be manually loaded for V8 binaries
26
 * that predate this metadata.  See mdb_v8_cfg.c for details.
27
 */
28

    
29
/*
30
 * We hard-code our MDB_API_VERSION to be 3 to allow this module to be
31
 * compiled on systems with higher version numbers, but still allow the
32
 * resulting binary object to be used on older systems.  (We do not make use
33
 * of functionality present in versions later than 3.)  This is particularly
34
 * important for mdb_v8 because (1) it's used in particular to debug
35
 * application-level software and (2) it has a history of rapid evolution.
36
 */
37
#define        MDB_API_VERSION                3
38

    
39
#include <sys/mdb_modapi.h>
40
#include <assert.h>
41
#include <ctype.h>
42
#include <stdarg.h>
43
#include <stdio.h>
44
#include <string.h>
45
#include <libproc.h>
46
#include <sys/avl.h>
47
#include <alloca.h>
48

    
49
#include "v8dbg.h"
50
#include "v8cfg.h"
51

    
52
#define        offsetof(s, m)        ((size_t)(&(((s *)0)->m)))
53

    
54
/*
55
 * The "v8_class" and "v8_field" structures describe the C++ classes used to
56
 * represent V8 heap objects.
57
 */
58
typedef struct v8_class {
59
        struct v8_class *v8c_next;        /* list linkage */
60
        struct v8_class *v8c_parent;        /* parent class (inheritance) */
61
        struct v8_field *v8c_fields;        /* array of class fields */
62
        size_t                v8c_start;        /* offset of first class field */
63
        size_t                v8c_end;        /* offset of first subclass field */
64
        char                v8c_name[64];        /* heap object class name */
65
} v8_class_t;
66

    
67
typedef struct v8_field {
68
        struct v8_field        *v8f_next;        /* list linkage */
69
        ssize_t                v8f_offset;        /* field offset */
70
        char                 v8f_name[64];        /* field name */
71
        boolean_t        v8f_isbyte;        /* 1-byte int field */
72
        boolean_t        v8f_isstr;        /* NUL-terminated string */
73
} v8_field_t;
74

    
75
/*
76
 * Similarly, the "v8_enum" structure describes an enum from V8.
77
 */
78
typedef struct {
79
        char         v8e_name[64];
80
        uint_t        v8e_value;
81
} v8_enum_t;
82

    
83
/*
84
 * During configuration, the dmod updates these globals with the actual set of
85
 * classes, types, and frame types based on the debug metadata.
86
 */
87
static v8_class_t        *v8_classes;
88

    
89
static v8_enum_t        v8_types[128];
90
static int                 v8_next_type;
91

    
92
static v8_enum_t         v8_frametypes[16];
93
static int                 v8_next_frametype;
94

    
95
static int                v8_silent;
96

    
97
/*
98
 * The following constants describe offsets from the frame pointer that are used
99
 * to inspect each stack frame.  They're initialized from the debug metadata.
100
 */
101
static ssize_t        V8_OFF_FP_CONTEXT;
102
static ssize_t        V8_OFF_FP_MARKER;
103
static ssize_t        V8_OFF_FP_FUNCTION;
104
static ssize_t        V8_OFF_FP_ARGS;
105

    
106
/*
107
 * The following constants are used by macros defined in heap-dbg-common.h to
108
 * examine the types of various V8 heap objects.  In general, the macros should
109
 * be preferred to using the constants directly.  The values of these constants
110
 * are initialized from the debug metadata.
111
 */
112
static intptr_t        V8_FirstNonstringType;
113
static intptr_t        V8_IsNotStringMask;
114
static intptr_t        V8_StringTag;
115
static intptr_t        V8_NotStringTag;
116
static intptr_t        V8_StringEncodingMask;
117
static intptr_t        V8_TwoByteStringTag;
118
static intptr_t        V8_AsciiStringTag;
119
static intptr_t        V8_StringRepresentationMask;
120
static intptr_t        V8_SeqStringTag;
121
static intptr_t        V8_ConsStringTag;
122
static intptr_t        V8_ExternalStringTag;
123
static intptr_t        V8_FailureTag;
124
static intptr_t        V8_FailureTagMask;
125
static intptr_t        V8_HeapObjectTag;
126
static intptr_t        V8_HeapObjectTagMask;
127
static intptr_t        V8_SmiTag;
128
static intptr_t        V8_SmiTagMask;
129
static intptr_t        V8_SmiValueShift;
130
static intptr_t        V8_SmiShiftSize;
131
static intptr_t        V8_PointerSizeLog2;
132

    
133
static intptr_t        V8_ISSHARED_SHIFT;
134
static intptr_t        V8_DICT_SHIFT;
135
static intptr_t        V8_DICT_PREFIX_SIZE;
136
static intptr_t        V8_DICT_ENTRY_SIZE;
137
static intptr_t        V8_DICT_START_INDEX;
138
static intptr_t        V8_PROP_IDX_CONTENT;
139
static intptr_t        V8_PROP_IDX_FIRST;
140
static intptr_t        V8_PROP_TYPE_FIELD;
141
static intptr_t        V8_PROP_FIRST_PHANTOM;
142
static intptr_t        V8_PROP_TYPE_MASK;
143
static intptr_t        V8_PROP_DESC_KEY;
144
static intptr_t        V8_PROP_DESC_DETAILS;
145
static intptr_t        V8_PROP_DESC_VALUE;
146
static intptr_t        V8_PROP_DESC_SIZE;
147
static intptr_t        V8_TRANSITIONS_IDX_DESC;
148

    
149
static intptr_t V8_TYPE_JSOBJECT = -1;
150
static intptr_t V8_TYPE_JSARRAY = -1;
151
static intptr_t V8_TYPE_FIXEDARRAY = -1;
152

    
153
/*
154
 * Although we have this information in v8_classes, the following offsets are
155
 * defined explicitly because they're used directly in code below.
156
 */
157
static ssize_t V8_OFF_CODE_INSTRUCTION_SIZE;
158
static ssize_t V8_OFF_CODE_INSTRUCTION_START;
159
static ssize_t V8_OFF_CONSSTRING_FIRST;
160
static ssize_t V8_OFF_CONSSTRING_SECOND;
161
static ssize_t V8_OFF_EXTERNALSTRING_RESOURCE;
162
static ssize_t V8_OFF_FIXEDARRAY_DATA;
163
static ssize_t V8_OFF_FIXEDARRAY_LENGTH;
164
static ssize_t V8_OFF_HEAPNUMBER_VALUE;
165
static ssize_t V8_OFF_HEAPOBJECT_MAP;
166
static ssize_t V8_OFF_JSARRAY_LENGTH;
167
static ssize_t V8_OFF_JSDATE_VALUE;
168
static ssize_t V8_OFF_JSFUNCTION_SHARED;
169
static ssize_t V8_OFF_JSOBJECT_ELEMENTS;
170
static ssize_t V8_OFF_JSOBJECT_PROPERTIES;
171
static ssize_t V8_OFF_MAP_CONSTRUCTOR;
172
static ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES;
173
static ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES;
174
static ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS;
175
static ssize_t V8_OFF_MAP_INSTANCE_SIZE;
176
static ssize_t V8_OFF_MAP_BIT_FIELD3;
177
static ssize_t V8_OFF_MAP_TRANSITIONS;
178
static ssize_t V8_OFF_ODDBALL_TO_STRING;
179
static ssize_t V8_OFF_SCRIPT_LINE_ENDS;
180
static ssize_t V8_OFF_SCRIPT_NAME;
181
static ssize_t V8_OFF_SEQASCIISTR_CHARS;
182
static ssize_t V8_OFF_SEQONEBYTESTR_CHARS;
183
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE;
184
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION;
185
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME;
186
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH;
187
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_SCRIPT;
188
static ssize_t V8_OFF_SHAREDFUNCTIONINFO_NAME;
189
static ssize_t V8_OFF_STRING_LENGTH;
190

    
191
#define        NODE_OFF_EXTSTR_DATA                0x4        /* see node_string.h */
192

    
193
#define        V8_CONSTANT_OPTIONAL                1
194
#define        V8_CONSTANT_HASFALLBACK                2
195

    
196
#define        V8_CONSTANT_MAJORSHIFT                3
197
#define        V8_CONSTANT_MAJORMASK                ((1 << 4) - 1)
198
#define        V8_CONSTANT_MAJOR(flags)        \
199
        (((flags) >> V8_CONSTANT_MAJORSHIFT) & V8_CONSTANT_MAJORMASK)
200

    
201
#define        V8_CONSTANT_MINORSHIFT                7
202
#define        V8_CONSTANT_MINORMASK                ((1 << 9) - 1)
203
#define        V8_CONSTANT_MINOR(flags)        \
204
        (((flags) >> V8_CONSTANT_MINORSHIFT) & V8_CONSTANT_MINORMASK)
205

    
206
#define        V8_CONSTANT_FALLBACK(maj, min) \
207
        (V8_CONSTANT_OPTIONAL | V8_CONSTANT_HASFALLBACK | \
208
        ((maj) << V8_CONSTANT_MAJORSHIFT) | ((min) << V8_CONSTANT_MINORSHIFT))
209

    
210
/*
211
 * Table of constants used directly by this file.
212
 */
213
typedef struct v8_constant {
214
        intptr_t        *v8c_valp;
215
        const char        *v8c_symbol;
216
        uint32_t        v8c_flags;
217
        intptr_t        v8c_fallback;
218
} v8_constant_t;
219

    
220
static v8_constant_t v8_constants[] = {
221
        { &V8_OFF_FP_CONTEXT,                "v8dbg_off_fp_context"                },
222
        { &V8_OFF_FP_FUNCTION,                "v8dbg_off_fp_function"                },
223
        { &V8_OFF_FP_MARKER,                "v8dbg_off_fp_marker"                },
224
        { &V8_OFF_FP_ARGS,                "v8dbg_off_fp_args"                },
225

    
226
        { &V8_FirstNonstringType,        "v8dbg_FirstNonstringType"        },
227
        { &V8_IsNotStringMask,                "v8dbg_IsNotStringMask"                },
228
        { &V8_StringTag,                "v8dbg_StringTag"                },
229
        { &V8_NotStringTag,                "v8dbg_NotStringTag"                },
230
        { &V8_StringEncodingMask,        "v8dbg_StringEncodingMask"        },
231
        { &V8_TwoByteStringTag,                "v8dbg_TwoByteStringTag"        },
232
        { &V8_AsciiStringTag,                "v8dbg_AsciiStringTag"                },
233
        { &V8_StringRepresentationMask,        "v8dbg_StringRepresentationMask" },
234
        { &V8_SeqStringTag,                "v8dbg_SeqStringTag"                },
235
        { &V8_ConsStringTag,                "v8dbg_ConsStringTag"                },
236
        { &V8_ExternalStringTag,        "v8dbg_ExternalStringTag"        },
237
        { &V8_FailureTag,                "v8dbg_FailureTag"                },
238
        { &V8_FailureTagMask,                "v8dbg_FailureTagMask"                },
239
        { &V8_HeapObjectTag,                "v8dbg_HeapObjectTag"                },
240
        { &V8_HeapObjectTagMask,        "v8dbg_HeapObjectTagMask"        },
241
        { &V8_SmiTag,                        "v8dbg_SmiTag"                        },
242
        { &V8_SmiTagMask,                "v8dbg_SmiTagMask"                },
243
        { &V8_SmiValueShift,                "v8dbg_SmiValueShift"                },
244
        { &V8_SmiShiftSize,                "v8dbg_SmiShiftSize",
245
#ifdef _LP64
246
            V8_CONSTANT_FALLBACK(0, 0), 31 },
247
#else
248
            V8_CONSTANT_FALLBACK(0, 0), 0 },
249
#endif
250
        { &V8_PointerSizeLog2,                "v8dbg_PointerSizeLog2"                },
251

    
252
        { &V8_DICT_SHIFT,                "v8dbg_dict_shift",
253
            V8_CONSTANT_FALLBACK(3, 13), 24 },
254
        { &V8_DICT_PREFIX_SIZE,                "v8dbg_dict_prefix_size",
255
            V8_CONSTANT_FALLBACK(3, 11), 2 },
256
        { &V8_DICT_ENTRY_SIZE,                "v8dbg_dict_entry_size",
257
            V8_CONSTANT_FALLBACK(3, 11), 3 },
258
        { &V8_DICT_START_INDEX,                "v8dbg_dict_start_index",
259
            V8_CONSTANT_FALLBACK(3, 11), 3 },
260
        { &V8_ISSHARED_SHIFT,                "v8dbg_isshared_shift",
261
            V8_CONSTANT_FALLBACK(3, 11), 0 },
262
        { &V8_PROP_IDX_FIRST,                "v8dbg_prop_idx_first"                },
263
        { &V8_PROP_TYPE_FIELD,                "v8dbg_prop_type_field"                },
264
        { &V8_PROP_FIRST_PHANTOM,        "v8dbg_prop_type_first_phantom"        },
265
        { &V8_PROP_TYPE_MASK,                "v8dbg_prop_type_mask"                },
266
        { &V8_PROP_IDX_CONTENT,                "v8dbg_prop_idx_content",
267
            V8_CONSTANT_OPTIONAL },
268
        { &V8_PROP_DESC_KEY,                "v8dbg_prop_desc_key",
269
            V8_CONSTANT_FALLBACK(0, 0), 0 },
270
        { &V8_PROP_DESC_DETAILS,        "v8dbg_prop_desc_details",
271
            V8_CONSTANT_FALLBACK(0, 0), 1 },
272
        { &V8_PROP_DESC_VALUE,                "v8dbg_prop_desc_value",
273
            V8_CONSTANT_FALLBACK(0, 0), 2 },
274
        { &V8_PROP_DESC_SIZE,                "v8dbg_prop_desc_size",
275
            V8_CONSTANT_FALLBACK(0, 0), 3 },
276
        { &V8_TRANSITIONS_IDX_DESC,        "v8dbg_transitions_idx_descriptors",
277
            V8_CONSTANT_OPTIONAL },
278
};
279

    
280
static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]);
281

    
282
typedef struct v8_offset {
283
        ssize_t                *v8o_valp;
284
        const char        *v8o_class;
285
        const char        *v8o_member;
286
        boolean_t        v8o_optional;
287
} v8_offset_t;
288

    
289
static v8_offset_t v8_offsets[] = {
290
        { &V8_OFF_CODE_INSTRUCTION_SIZE,
291
            "Code", "instruction_size" },
292
        { &V8_OFF_CODE_INSTRUCTION_START,
293
            "Code", "instruction_start" },
294
        { &V8_OFF_CONSSTRING_FIRST,
295
            "ConsString", "first" },
296
        { &V8_OFF_CONSSTRING_SECOND,
297
            "ConsString", "second" },
298
        { &V8_OFF_EXTERNALSTRING_RESOURCE,
299
            "ExternalString", "resource" },
300
        { &V8_OFF_FIXEDARRAY_DATA,
301
            "FixedArray", "data" },
302
        { &V8_OFF_FIXEDARRAY_LENGTH,
303
            "FixedArray", "length" },
304
        { &V8_OFF_HEAPNUMBER_VALUE,
305
            "HeapNumber", "value" },
306
        { &V8_OFF_HEAPOBJECT_MAP,
307
            "HeapObject", "map" },
308
        { &V8_OFF_JSARRAY_LENGTH,
309
            "JSArray", "length" },
310
        { &V8_OFF_JSDATE_VALUE,
311
            "JSDate", "value", B_TRUE },
312
        { &V8_OFF_JSFUNCTION_SHARED,
313
            "JSFunction", "shared" },
314
        { &V8_OFF_JSOBJECT_ELEMENTS,
315
            "JSObject", "elements" },
316
        { &V8_OFF_JSOBJECT_PROPERTIES,
317
            "JSObject", "properties" },
318
        { &V8_OFF_MAP_CONSTRUCTOR,
319
            "Map", "constructor" },
320
        { &V8_OFF_MAP_INOBJECT_PROPERTIES,
321
            "Map", "inobject_properties" },
322
        { &V8_OFF_MAP_INSTANCE_ATTRIBUTES,
323
            "Map", "instance_attributes" },
324
        { &V8_OFF_MAP_INSTANCE_DESCRIPTORS,
325
            "Map", "instance_descriptors", B_TRUE },
326
        { &V8_OFF_MAP_TRANSITIONS,
327
            "Map", "transitions", B_TRUE },
328
        { &V8_OFF_MAP_INSTANCE_SIZE,
329
            "Map", "instance_size" },
330
        { &V8_OFF_MAP_BIT_FIELD3,
331
            "Map", "bit_field3", B_TRUE },
332
        { &V8_OFF_ODDBALL_TO_STRING,
333
            "Oddball", "to_string" },
334
        { &V8_OFF_SCRIPT_LINE_ENDS,
335
            "Script", "line_ends" },
336
        { &V8_OFF_SCRIPT_NAME,
337
            "Script", "name" },
338
        { &V8_OFF_SEQASCIISTR_CHARS,
339
            "SeqAsciiString", "chars", B_TRUE },
340
        { &V8_OFF_SEQONEBYTESTR_CHARS,
341
            "SeqOneByteString", "chars", B_TRUE },
342
        { &V8_OFF_SHAREDFUNCTIONINFO_CODE,
343
            "SharedFunctionInfo", "code" },
344
        { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION,
345
            "SharedFunctionInfo", "function_token_position" },
346
        { &V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME,
347
            "SharedFunctionInfo", "inferred_name" },
348
        { &V8_OFF_SHAREDFUNCTIONINFO_LENGTH,
349
            "SharedFunctionInfo", "length" },
350
        { &V8_OFF_SHAREDFUNCTIONINFO_NAME,
351
            "SharedFunctionInfo", "name" },
352
        { &V8_OFF_SHAREDFUNCTIONINFO_SCRIPT,
353
            "SharedFunctionInfo", "script" },
354
        { &V8_OFF_STRING_LENGTH,
355
            "String", "length" },
356
};
357

    
358
static int v8_noffsets = sizeof (v8_offsets) / sizeof (v8_offsets[0]);
359

    
360
static uintptr_t v8_major;
361
static uintptr_t v8_minor;
362
static uintptr_t v8_build;
363
static uintptr_t v8_patch;
364

    
365
static int autoconf_iter_symbol(mdb_symbol_t *, void *);
366
static v8_class_t *conf_class_findcreate(const char *);
367
static v8_field_t *conf_field_create(v8_class_t *, const char *, size_t);
368
static char *conf_next_part(char *, char *);
369
static int conf_update_parent(const char *);
370
static int conf_update_field(v8_cfg_t *, const char *);
371
static int conf_update_enum(v8_cfg_t *, const char *, const char *,
372
    v8_enum_t *);
373
static int conf_update_type(v8_cfg_t *, const char *);
374
static int conf_update_frametype(v8_cfg_t *, const char *);
375
static void conf_class_compute_offsets(v8_class_t *);
376

    
377
static int read_typebyte(uint8_t *, uintptr_t);
378
static int heap_offset(const char *, const char *, ssize_t *);
379

    
380
/*
381
 * Invoked when this dmod is initially loaded to load the set of classes, enums,
382
 * and other constants from the metadata in the target binary.
383
 */
384
static int
385
autoconfigure(v8_cfg_t *cfgp)
386
{
387
        v8_class_t *clp;
388
        v8_enum_t *ep;
389
        struct v8_constant *cnp;
390
        int ii;
391
        int failed = 0;
392

    
393
        assert(v8_classes == NULL);
394

    
395
        /*
396
         * Iterate all global symbols looking for metadata.
397
         */
398
        if (cfgp->v8cfg_iter(cfgp, autoconf_iter_symbol, cfgp) != 0) {
399
                mdb_warn("failed to autoconfigure V8 support\n");
400
                return (-1);
401
        }
402

    
403
        /*
404
         * By now we've configured all of the classes so we can update the
405
         * "start" and "end" fields in each class with information from its
406
         * parent class.
407
         */
408
        for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
409
                if (clp->v8c_end != (size_t)-1)
410
                        continue;
411

    
412
                conf_class_compute_offsets(clp);
413
        };
414

    
415
        /*
416
         * Load various constants used directly in the module.
417
         */
418
        for (ii = 0; ii < v8_nconstants; ii++) {
419
                cnp = &v8_constants[ii];
420

    
421
                if (cfgp->v8cfg_readsym(cfgp,
422
                    cnp->v8c_symbol, cnp->v8c_valp) != -1) {
423
                        continue;
424
                }
425

    
426
                if (!(cnp->v8c_flags & V8_CONSTANT_OPTIONAL)) {
427
                        mdb_warn("failed to read \"%s\"", cnp->v8c_symbol);
428
                        failed++;
429
                        continue;
430
                }
431

    
432
                if (!(cnp->v8c_flags & V8_CONSTANT_HASFALLBACK) ||
433
                    v8_major < V8_CONSTANT_MAJOR(cnp->v8c_flags) ||
434
                    (v8_major == V8_CONSTANT_MAJOR(cnp->v8c_flags) &&
435
                    v8_minor < V8_CONSTANT_MINOR(cnp->v8c_flags))) {
436
                        *cnp->v8c_valp = -1;
437
                        continue;
438
                }
439

    
440
                /*
441
                 * We have a fallback -- and we know that the version satisfies
442
                 * the fallback's version constraints; use the fallback value.
443
                 */
444
                *cnp->v8c_valp = cnp->v8c_fallback;
445
        }
446

    
447
        /*
448
         * Load type values for well-known classes that we use a lot.
449
         */
450
        for (ep = v8_types; ep->v8e_name[0] != '\0'; ep++) {
451
                if (strcmp(ep->v8e_name, "JSObject") == 0)
452
                        V8_TYPE_JSOBJECT = ep->v8e_value;
453

    
454
                if (strcmp(ep->v8e_name, "JSArray") == 0)
455
                        V8_TYPE_JSARRAY = ep->v8e_value;
456

    
457
                if (strcmp(ep->v8e_name, "FixedArray") == 0)
458
                        V8_TYPE_FIXEDARRAY = ep->v8e_value;
459
        }
460

    
461
        if (V8_TYPE_JSOBJECT == -1) {
462
                mdb_warn("couldn't find JSObject type\n");
463
                failed++;
464
        }
465

    
466
        if (V8_TYPE_JSARRAY == -1) {
467
                mdb_warn("couldn't find JSArray type\n");
468
                failed++;
469
        }
470

    
471
        if (V8_TYPE_FIXEDARRAY == -1) {
472
                mdb_warn("couldn't find FixedArray type\n");
473
                failed++;
474
        }
475

    
476
        /*
477
         * Finally, load various class offsets.
478
         */
479
        for (ii = 0; ii < v8_noffsets; ii++) {
480
                struct v8_offset *offp = &v8_offsets[ii];
481
                const char *klass = offp->v8o_class;
482

    
483
again:
484
                if (heap_offset(klass, offp->v8o_member, offp->v8o_valp) == 0)
485
                        continue;
486

    
487
                if (strcmp(klass, "FixedArray") == 0) {
488
                        /*
489
                         * The V8 included in node v0.6 uses a FixedArrayBase
490
                         * class to contain the "length" field, while the one
491
                         * in v0.4 has no such base class and stores the field
492
                         * directly in FixedArray; if we failed to derive
493
                         * the offset from FixedArray, try FixedArrayBase.
494
                         */
495
                        klass = "FixedArrayBase";
496
                        goto again;
497
                }
498

    
499
                if (offp->v8o_optional) {
500
                        *offp->v8o_valp = -1;
501
                        continue;
502
                }
503

    
504
                mdb_warn("couldn't find class \"%s\", field \"%s\"\n",
505
                    offp->v8o_class, offp->v8o_member);
506
                failed++;
507
        }
508

    
509
        if (!((V8_OFF_SEQASCIISTR_CHARS != -1) ^
510
            (V8_OFF_SEQONEBYTESTR_CHARS != -1))) {
511
                mdb_warn("expected exactly one of SeqAsciiString and "
512
                    "SeqOneByteString to be defined\n");
513
                failed++;
514
        }
515

    
516
        if (V8_OFF_SEQONEBYTESTR_CHARS != -1)
517
                V8_OFF_SEQASCIISTR_CHARS = V8_OFF_SEQONEBYTESTR_CHARS;
518

    
519
        return (failed ? -1 : 0);
520
}
521

    
522
/* ARGSUSED */
523
static int
524
autoconf_iter_symbol(mdb_symbol_t *symp, void *arg)
525
{
526
        v8_cfg_t *cfgp = arg;
527

    
528
        if (strncmp(symp->sym_name, "v8dbg_parent_",
529
            sizeof ("v8dbg_parent_") - 1) == 0)
530
                return (conf_update_parent(symp->sym_name));
531

    
532
        if (strncmp(symp->sym_name, "v8dbg_class_",
533
            sizeof ("v8dbg_class_") - 1) == 0)
534
                return (conf_update_field(cfgp, symp->sym_name));
535

    
536
        if (strncmp(symp->sym_name, "v8dbg_type_",
537
            sizeof ("v8dbg_type_") - 1) == 0)
538
                return (conf_update_type(cfgp, symp->sym_name));
539

    
540
        if (strncmp(symp->sym_name, "v8dbg_frametype_",
541
            sizeof ("v8dbg_frametype_") - 1) == 0)
542
                return (conf_update_frametype(cfgp, symp->sym_name));
543

    
544
        return (0);
545
}
546

    
547
/*
548
 * Extracts the next field of a string whose fields are separated by "__" (as
549
 * the V8 metadata symbols are).
550
 */
551
static char *
552
conf_next_part(char *buf, char *start)
553
{
554
        char *pp;
555

    
556
        if ((pp = strstr(start, "__")) == NULL) {
557
                mdb_warn("malformed symbol name: %s\n", buf);
558
                return (NULL);
559
        }
560

    
561
        *pp = '\0';
562
        return (pp + sizeof ("__") - 1);
563
}
564

    
565
static v8_class_t *
566
conf_class_findcreate(const char *name)
567
{
568
        v8_class_t *clp, *iclp, *prev = NULL;
569
        int cmp;
570

    
571
        for (iclp = v8_classes; iclp != NULL; iclp = iclp->v8c_next) {
572
                if ((cmp = strcmp(iclp->v8c_name, name)) == 0)
573
                        return (iclp);
574

    
575
                if (cmp > 0)
576
                        break;
577

    
578
                prev = iclp;
579
        }
580

    
581
        if ((clp = mdb_zalloc(sizeof (*clp), UM_NOSLEEP)) == NULL)
582
                return (NULL);
583

    
584
        (void) strlcpy(clp->v8c_name, name, sizeof (clp->v8c_name));
585
        clp->v8c_end = (size_t)-1;
586
        clp->v8c_next = iclp;
587

    
588
        if (prev != NULL) {
589
                prev->v8c_next = clp;
590
        } else {
591
                v8_classes = clp;
592
        }
593

    
594
        return (clp);
595
}
596

    
597
static v8_field_t *
598
conf_field_create(v8_class_t *clp, const char *name, size_t offset)
599
{
600
        v8_field_t *flp, *iflp;
601

    
602
        if ((flp = mdb_zalloc(sizeof (*flp), UM_NOSLEEP)) == NULL)
603
                return (NULL);
604

    
605
        (void) strlcpy(flp->v8f_name, name, sizeof (flp->v8f_name));
606
        flp->v8f_offset = offset;
607

    
608
        if (clp->v8c_fields == NULL || clp->v8c_fields->v8f_offset > offset) {
609
                flp->v8f_next = clp->v8c_fields;
610
                clp->v8c_fields = flp;
611
                return (flp);
612
        }
613

    
614
        for (iflp = clp->v8c_fields; iflp->v8f_next != NULL;
615
            iflp = iflp->v8f_next) {
616
                if (iflp->v8f_next->v8f_offset > offset)
617
                        break;
618
        }
619

    
620
        flp->v8f_next = iflp->v8f_next;
621
        iflp->v8f_next = flp;
622
        return (flp);
623
}
624

    
625
/*
626
 * Given a "v8dbg_parent_X__Y", symbol, update the parent of class X to class Y.
627
 * Note that neither class necessarily exists already.
628
 */
629
static int
630
conf_update_parent(const char *symbol)
631
{
632
        char *pp, *qq;
633
        char buf[128];
634
        v8_class_t *clp, *pclp;
635

    
636
        (void) strlcpy(buf, symbol, sizeof (buf));
637
        pp = buf + sizeof ("v8dbg_parent_") - 1;
638
        qq = conf_next_part(buf, pp);
639

    
640
        if (qq == NULL)
641
                return (-1);
642

    
643
        clp = conf_class_findcreate(pp);
644
        pclp = conf_class_findcreate(qq);
645

    
646
        if (clp == NULL || pclp == NULL) {
647
                mdb_warn("mdb_v8: out of memory\n");
648
                return (-1);
649
        }
650

    
651
        clp->v8c_parent = pclp;
652
        return (0);
653
}
654

    
655
/*
656
 * Given a "v8dbg_class_CLASS__FIELD__TYPE", symbol, save field "FIELD" into
657
 * class CLASS with the offset described by the symbol.  Note that CLASS does
658
 * not necessarily exist already.
659
 */
660
static int
661
conf_update_field(v8_cfg_t *cfgp, const char *symbol)
662
{
663
        v8_class_t *clp;
664
        v8_field_t *flp;
665
        intptr_t offset;
666
        char *pp, *qq, *tt;
667
        char buf[128];
668

    
669
        (void) strlcpy(buf, symbol, sizeof (buf));
670

    
671
        pp = buf + sizeof ("v8dbg_class_") - 1;
672
        qq = conf_next_part(buf, pp);
673

    
674
        if (qq == NULL || (tt = conf_next_part(buf, qq)) == NULL)
675
                return (-1);
676

    
677
        if (cfgp->v8cfg_readsym(cfgp, symbol, &offset) == -1) {
678
                mdb_warn("failed to read symbol \"%s\"", symbol);
679
                return (-1);
680
        }
681

    
682
        if ((clp = conf_class_findcreate(pp)) == NULL ||
683
            (flp = conf_field_create(clp, qq, (size_t)offset)) == NULL)
684
                return (-1);
685

    
686
        if (strcmp(tt, "int") == 0)
687
                flp->v8f_isbyte = B_TRUE;
688

    
689
        if (strcmp(tt, "char") == 0)
690
                flp->v8f_isstr = B_TRUE;
691

    
692
        return (0);
693
}
694

    
695
static int
696
conf_update_enum(v8_cfg_t *cfgp, const char *symbol, const char *name,
697
    v8_enum_t *enp)
698
{
699
        intptr_t value;
700

    
701
        if (cfgp->v8cfg_readsym(cfgp, symbol, &value) == -1) {
702
                mdb_warn("failed to read symbol \"%s\"", symbol);
703
                return (-1);
704
        }
705

    
706
        enp->v8e_value = (int)value;
707
        (void) strlcpy(enp->v8e_name, name, sizeof (enp->v8e_name));
708
        return (0);
709
}
710

    
711
/*
712
 * Given a "v8dbg_type_TYPENAME" constant, save the type name in v8_types.  Note
713
 * that this enum has multiple integer values with the same string label.
714
 */
715
static int
716
conf_update_type(v8_cfg_t *cfgp, const char *symbol)
717
{
718
        char *klass;
719
        v8_enum_t *enp;
720
        char buf[128];
721

    
722
        if (v8_next_type > sizeof (v8_types) / sizeof (v8_types[0])) {
723
                mdb_warn("too many V8 types\n");
724
                return (-1);
725
        }
726

    
727
        (void) strlcpy(buf, symbol, sizeof (buf));
728

    
729
        klass = buf + sizeof ("v8dbg_type_") - 1;
730
        if (conf_next_part(buf, klass) == NULL)
731
                return (-1);
732

    
733
        enp = &v8_types[v8_next_type++];
734
        return (conf_update_enum(cfgp, symbol, klass, enp));
735
}
736

    
737
/*
738
 * Given a "v8dbg_frametype_TYPENAME" constant, save the frame type in
739
 * v8_frametypes.
740
 */
741
static int
742
conf_update_frametype(v8_cfg_t *cfgp, const char *symbol)
743
{
744
        const char *frametype;
745
        v8_enum_t *enp;
746

    
747
        if (v8_next_frametype >
748
            sizeof (v8_frametypes) / sizeof (v8_frametypes[0])) {
749
                mdb_warn("too many V8 frame types\n");
750
                return (-1);
751
        }
752

    
753
        enp = &v8_frametypes[v8_next_frametype++];
754
        frametype = symbol + sizeof ("v8dbg_frametype_") - 1;
755
        return (conf_update_enum(cfgp, symbol, frametype, enp));
756
}
757

    
758
/*
759
 * Now that all classes have been loaded, update the "start" and "end" fields of
760
 * each class based on the values of its parent class.
761
 */
762
static void
763
conf_class_compute_offsets(v8_class_t *clp)
764
{
765
        v8_field_t *flp;
766

    
767
        assert(clp->v8c_start == 0);
768
        assert(clp->v8c_end == (size_t)-1);
769

    
770
        if (clp->v8c_parent != NULL) {
771
                if (clp->v8c_parent->v8c_end == (size_t)-1)
772
                        conf_class_compute_offsets(clp->v8c_parent);
773

    
774
                clp->v8c_start = clp->v8c_parent->v8c_end;
775
        }
776

    
777
        if (clp->v8c_fields == NULL) {
778
                clp->v8c_end = clp->v8c_start;
779
                return;
780
        }
781

    
782
        for (flp = clp->v8c_fields; flp->v8f_next != NULL; flp = flp->v8f_next)
783
                ;
784

    
785
        if (flp == NULL)
786
                clp->v8c_end = clp->v8c_start;
787
        else
788
                clp->v8c_end = flp->v8f_offset + sizeof (uintptr_t);
789
}
790

    
791
/*
792
 * Utility functions
793
 */
794
#define        JSSTR_NONE                0
795
#define        JSSTR_NUDE                JSSTR_NONE
796
#define        JSSTR_VERBOSE                0x1
797
#define        JSSTR_QUOTED                0x2
798

    
799
static int jsstr_print(uintptr_t, uint_t, char **, size_t *);
800
static boolean_t jsobj_is_undefined(uintptr_t addr);
801

    
802
static const char *
803
enum_lookup_str(v8_enum_t *enums, int val, const char *dflt)
804
{
805
        v8_enum_t *ep;
806

    
807
        for (ep = enums; ep->v8e_name[0] != '\0'; ep++) {
808
                if (ep->v8e_value == val)
809
                        return (ep->v8e_name);
810
        }
811

    
812
        return (dflt);
813
}
814

    
815
static void
816
enum_print(v8_enum_t *enums)
817
{
818
        v8_enum_t *itp;
819

    
820
        for (itp = enums; itp->v8e_name[0] != '\0'; itp++)
821
                mdb_printf("%-30s = 0x%02x\n", itp->v8e_name, itp->v8e_value);
822
}
823

    
824
/*
825
 * b[v]snprintf behave like [v]snprintf(3c), except that they update the buffer
826
 * and length arguments based on how much buffer space is used by the operation.
827
 * This makes it much easier to combine multiple calls in sequence without
828
 * worrying about buffer overflow.
829
 */
830
static size_t
831
bvsnprintf(char **bufp, size_t *buflenp, const char *format, va_list alist)
832
{
833
        size_t rv, len;
834

    
835
        if (*buflenp == 0)
836
                return (vsnprintf(NULL, 0, format, alist));
837

    
838
        rv = vsnprintf(*bufp, *buflenp, format, alist);
839

    
840
        len = MIN(rv, *buflenp);
841
        *buflenp -= len;
842
        *bufp += len;
843

    
844
        return (len);
845
}
846

    
847
static size_t
848
bsnprintf(char **bufp, size_t *buflenp, const char *format, ...)
849
{
850
        va_list alist;
851
        size_t rv;
852

    
853
        va_start(alist, format);
854
        rv = bvsnprintf(bufp, buflenp, format, alist);
855
        va_end(alist);
856

    
857
        return (rv);
858
}
859

    
860
static void
861
v8_warn(const char *format, ...)
862
{
863
        char buf[512];
864
        va_list alist;
865
        int len;
866

    
867
        if (v8_silent)
868
                return;
869

    
870
        va_start(alist, format);
871
        (void) vsnprintf(buf, sizeof (buf), format, alist);
872
        va_end(alist);
873

    
874
        /*
875
         * This is made slightly annoying because we need to effectively
876
         * preserve the original format string to allow for mdb to use the
877
         * new-line at the end to indicate that strerror should be elided.
878
         */
879
        if ((len = strlen(format)) > 0 && format[len - 1] == '\n') {
880
                buf[strlen(buf) - 1] = '\0';
881
                mdb_warn("%s\n", buf);
882
        } else {
883
                mdb_warn("%s", buf);
884
        }
885
}
886

    
887
/*
888
 * Returns in "offp" the offset of field "field" in C++ class "klass".
889
 */
890
static int
891
heap_offset(const char *klass, const char *field, ssize_t *offp)
892
{
893
        v8_class_t *clp;
894
        v8_field_t *flp;
895

    
896
        for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
897
                if (strcmp(klass, clp->v8c_name) == 0)
898
                        break;
899
        }
900

    
901
        if (clp == NULL)
902
                return (-1);
903

    
904
        for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
905
                if (strcmp(field, flp->v8f_name) == 0)
906
                        break;
907
        }
908

    
909
        if (flp == NULL)
910
                return (-1);
911

    
912
        *offp = V8_OFF_HEAP(flp->v8f_offset);
913
        return (0);
914
}
915

    
916
/*
917
 * Assuming "addr" is an instance of the C++ heap class "klass", read into *valp
918
 * the pointer-sized value of field "field".
919
 */
920
static int
921
read_heap_ptr(uintptr_t *valp, uintptr_t addr, ssize_t off)
922
{
923
        if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
924
                v8_warn("failed to read offset %d from %p", off, addr);
925
                return (-1);
926
        }
927

    
928
        return (0);
929
}
930

    
931
/*
932
 * Like read_heap_ptr, but assume the field is an SMI and store the actual value
933
 * into *valp rather than the encoded representation.
934
 */
935
static int
936
read_heap_smi(uintptr_t *valp, uintptr_t addr, ssize_t off)
937
{
938
        if (read_heap_ptr(valp, addr, off) != 0)
939
                return (-1);
940

    
941
        if (!V8_IS_SMI(*valp)) {
942
                v8_warn("expected SMI, got %p\n", *valp);
943
                return (-1);
944
        }
945

    
946
        *valp = V8_SMI_VALUE(*valp);
947

    
948
        return (0);
949
}
950

    
951
static int
952
read_heap_double(double *valp, uintptr_t addr, ssize_t off)
953
{
954
        if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
955
                v8_warn("failed to read heap value at %p", addr + off);
956
                return (-1);
957
        }
958

    
959
        return (0);
960
}
961

    
962
/*
963
 * Assuming "addr" refers to a FixedArray, return a newly-allocated array
964
 * representing its contents.
965
 */
966
static int
967
read_heap_array(uintptr_t addr, uintptr_t **retp, size_t *lenp, int flags)
968
{
969
        uint8_t type;
970
        uintptr_t len;
971

    
972
        if (!V8_IS_HEAPOBJECT(addr))
973
                return (-1);
974

    
975
        if (read_typebyte(&type, addr) != 0)
976
                return (-1);
977

    
978
        if (type != V8_TYPE_FIXEDARRAY)
979
                return (-1);
980

    
981
        if (read_heap_smi(&len, addr, V8_OFF_FIXEDARRAY_LENGTH) != 0)
982
                return (-1);
983

    
984
        *lenp = len;
985

    
986
        if (len == 0) {
987
                *retp = NULL;
988
                return (0);
989
        }
990

    
991
        if ((*retp = mdb_zalloc(len * sizeof (uintptr_t), flags)) == NULL)
992
                return (-1);
993

    
994
        if (mdb_vread(*retp, len * sizeof (uintptr_t),
995
            addr + V8_OFF_FIXEDARRAY_DATA) == -1) {
996
                if (!(flags & UM_GC))
997
                        mdb_free(*retp, len * sizeof (uintptr_t));
998

    
999
                return (-1);
1000
        }
1001

    
1002
        return (0);
1003
}
1004

    
1005
static int
1006
read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off)
1007
{
1008
        if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
1009
                v8_warn("failed to read heap value at %p", addr + off);
1010
                return (-1);
1011
        }
1012

    
1013
        return (0);
1014
}
1015

    
1016
/*
1017
 * Given a heap object, returns in *valp the byte describing the type of the
1018
 * object.  This is shorthand for first retrieving the Map at the start of the
1019
 * heap object and then retrieving the type byte from the Map object.
1020
 */
1021
static int
1022
read_typebyte(uint8_t *valp, uintptr_t addr)
1023
{
1024
        uintptr_t mapaddr;
1025
        ssize_t off = V8_OFF_HEAPOBJECT_MAP;
1026

    
1027
        if (mdb_vread(&mapaddr, sizeof (mapaddr), addr + off) == -1) {
1028
                v8_warn("failed to read type of %p", addr);
1029
                return (-1);
1030
        }
1031

    
1032
        if (!V8_IS_HEAPOBJECT(mapaddr)) {
1033
                v8_warn("object map is not a heap object\n");
1034
                return (-1);
1035
        }
1036

    
1037
        if (read_heap_byte(valp, mapaddr, V8_OFF_MAP_INSTANCE_ATTRIBUTES) == -1)
1038
                return (-1);
1039

    
1040
        return (0);
1041
}
1042

    
1043
/*
1044
 * Given a heap object, returns in *valp the size of the object.  For
1045
 * variable-size objects, returns an undefined value.
1046
 */
1047
static int
1048
read_size(size_t *valp, uintptr_t addr)
1049
{
1050
        uintptr_t mapaddr;
1051
        uint8_t size;
1052

    
1053
        if (read_heap_ptr(&mapaddr, addr, V8_OFF_HEAPOBJECT_MAP) != 0)
1054
                return (-1);
1055

    
1056
        if (!V8_IS_HEAPOBJECT(mapaddr)) {
1057
                v8_warn("heap object map is not itself a heap object\n");
1058
                return (-1);
1059
        }
1060

    
1061
        if (read_heap_byte(&size, mapaddr, V8_OFF_MAP_INSTANCE_SIZE) != 0)
1062
                return (-1);
1063

    
1064
        *valp = size << V8_PointerSizeLog2;
1065
        return (0);
1066
}
1067

    
1068
/*
1069
 * Assuming "addr" refers to a FixedArray that is implementing a
1070
 * StringDictionary, iterate over its contents calling the specified function
1071
 * with key and value.
1072
 */
1073
static int
1074
read_heap_dict(uintptr_t addr,
1075
    int (*func)(const char *, uintptr_t, void *), void *arg)
1076
{
1077
        uint8_t type;
1078
        uintptr_t len;
1079
        char buf[512];
1080
        char *bufp;
1081
        int rval = -1;
1082
        uintptr_t *dict, ndict, i;
1083
        const char *typename;
1084

    
1085
        if (read_heap_array(addr, &dict, &ndict, UM_SLEEP) != 0)
1086
                return (-1);
1087

    
1088
        if (V8_DICT_ENTRY_SIZE < 2) {
1089
                v8_warn("dictionary entry size (%d) is too small for a "
1090
                    "key and value\n", V8_DICT_ENTRY_SIZE);
1091
                goto out;
1092
        }
1093

    
1094
        for (i = V8_DICT_START_INDEX + V8_DICT_PREFIX_SIZE; i < ndict;
1095
            i += V8_DICT_ENTRY_SIZE) {
1096
                /*
1097
                 * The layout here is key, value, details. (This is hardcoded
1098
                 * in Dictionary<Shape, Key>::SetEntry().)
1099
                 */
1100
                if (jsobj_is_undefined(dict[i]))
1101
                        continue;
1102

    
1103
                if (read_typebyte(&type, dict[i]) != 0)
1104
                        goto out;
1105

    
1106
                typename = enum_lookup_str(v8_types, type, NULL);
1107

    
1108
                if (typename != NULL && strcmp(typename, "Oddball") == 0) {
1109
                        /*
1110
                         * In some cases, the key can (apparently) be a hole;
1111
                         * assume that any Oddball in the key field falls into
1112
                         * this case and skip over it.
1113
                         */
1114
                        continue;
1115
                }
1116

    
1117
                if (!V8_TYPE_STRING(type))
1118
                        goto out;
1119

    
1120
                bufp = buf;
1121
                len = sizeof (buf);
1122

    
1123
                if (jsstr_print(dict[i], JSSTR_NUDE, &bufp, &len) != 0)
1124
                        goto out;
1125

    
1126
                if (func(buf, dict[i + 1], arg) == -1)
1127
                        goto out;
1128
        }
1129

    
1130
        rval = 0;
1131
out:
1132
        mdb_free(dict, ndict * sizeof (uintptr_t));
1133

    
1134
        return (rval);
1135
}
1136

    
1137
/*
1138
 * Returns in "buf" a description of the type of "addr" suitable for printing.
1139
 */
1140
static int
1141
obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep)
1142
{
1143
        uint8_t typebyte;
1144
        uintptr_t strptr;
1145
        const char *typename;
1146

    
1147
        if (V8_IS_FAILURE(addr)) {
1148
                if (typep)
1149
                        *typep = 0;
1150
                (void) bsnprintf(bufp, lenp, "'Failure' object");
1151
                return (0);
1152
        }
1153

    
1154
        if (V8_IS_SMI(addr)) {
1155
                if (typep)
1156
                        *typep = 0;
1157
                (void) bsnprintf(bufp, lenp, "SMI: value = %d",
1158
                    V8_SMI_VALUE(addr));
1159
                return (0);
1160
        }
1161

    
1162
        if (read_typebyte(&typebyte, addr) != 0)
1163
                return (-1);
1164

    
1165
        if (typep)
1166
                *typep = typebyte;
1167

    
1168
        typename = enum_lookup_str(v8_types, typebyte, "<unknown>");
1169
        (void) bsnprintf(bufp, lenp, typename);
1170

    
1171
        if (strcmp(typename, "Oddball") == 0) {
1172
                if (read_heap_ptr(&strptr, addr,
1173
                    V8_OFF_ODDBALL_TO_STRING) != -1) {
1174
                        (void) bsnprintf(bufp, lenp, ": \"");
1175
                        (void) jsstr_print(strptr, JSSTR_NUDE, bufp, lenp);
1176
                        (void) bsnprintf(bufp, lenp, "\"");
1177
                }
1178
        }
1179

    
1180
        return (0);
1181
}
1182

    
1183
/*
1184
 * Print out the fields of the given object that come from the given class.
1185
 */
1186
static int
1187
obj_print_fields(uintptr_t baddr, v8_class_t *clp)
1188
{
1189
        v8_field_t *flp;
1190
        uintptr_t addr, value;
1191
        int rv;
1192
        char *bufp;
1193
        size_t len;
1194
        uint8_t type;
1195
        char buf[256];
1196

    
1197
        for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
1198
                bufp = buf;
1199
                len = sizeof (buf);
1200

    
1201
                addr = baddr + V8_OFF_HEAP(flp->v8f_offset);
1202

    
1203
                if (flp->v8f_isstr) {
1204
                        if (mdb_readstr(buf, sizeof (buf), addr) == -1) {
1205
                                mdb_printf("%p %s (unreadable)\n",
1206
                                    addr, flp->v8f_name);
1207
                                continue;
1208
                        }
1209

    
1210
                        mdb_printf("%p %s = \"%s\"\n",
1211
                            addr, flp->v8f_name, buf);
1212
                        continue;
1213
                }
1214

    
1215
                if (flp->v8f_isbyte) {
1216
                        uint8_t sv;
1217
                        if (mdb_vread(&sv, sizeof (sv), addr) == -1) {
1218
                                mdb_printf("%p %s (unreadable)\n",
1219
                                    addr, flp->v8f_name);
1220
                                continue;
1221
                        }
1222

    
1223
                        mdb_printf("%p %s = 0x%x\n", addr, flp->v8f_name, sv);
1224
                        continue;
1225
                }
1226

    
1227
                rv = mdb_vread((void *)&value, sizeof (value), addr);
1228

    
1229
                if (rv != sizeof (value) ||
1230
                    obj_jstype(value, &bufp, &len, &type) != 0) {
1231
                        mdb_printf("%p %s (unreadable)\n", addr, flp->v8f_name);
1232
                        continue;
1233
                }
1234

    
1235
                if (type != 0 && V8_TYPE_STRING(type)) {
1236
                        (void) bsnprintf(&bufp, &len, ": ");
1237
                        (void) jsstr_print(value, JSSTR_QUOTED, &bufp, &len);
1238
                }
1239

    
1240
                mdb_printf("%p %s = %p (%s)\n", addr, flp->v8f_name, value,
1241
                    buf);
1242
        }
1243

    
1244
        return (DCMD_OK);
1245
}
1246

    
1247
/*
1248
 * Print out all fields of the given object, starting with the root of the class
1249
 * hierarchy and working down the most specific type.
1250
 */
1251
static int
1252
obj_print_class(uintptr_t addr, v8_class_t *clp)
1253
{
1254
        int rv = 0;
1255

    
1256
        /*
1257
         * If we have no fields, we just print a simple inheritance hierarchy.
1258
         * If we have fields but our parent doesn't, our header includes the
1259
         * inheritance hierarchy.
1260
         */
1261
        if (clp->v8c_end == 0) {
1262
                mdb_printf("%s ", clp->v8c_name);
1263

    
1264
                if (clp->v8c_parent != NULL) {
1265
                        mdb_printf("< ");
1266
                        (void) obj_print_class(addr, clp->v8c_parent);
1267
                }
1268

    
1269
                return (0);
1270
        }
1271

    
1272
        mdb_printf("%p %s", addr, clp->v8c_name);
1273

    
1274
        if (clp->v8c_start == 0 && clp->v8c_parent != NULL) {
1275
                mdb_printf(" < ");
1276
                (void) obj_print_class(addr, clp->v8c_parent);
1277
        }
1278

    
1279
        mdb_printf(" {\n");
1280
        (void) mdb_inc_indent(4);
1281

    
1282
        if (clp->v8c_start > 0 && clp->v8c_parent != NULL)
1283
                rv = obj_print_class(addr, clp->v8c_parent);
1284

    
1285
        rv |= obj_print_fields(addr, clp);
1286
        (void) mdb_dec_indent(4);
1287
        mdb_printf("}\n");
1288

    
1289
        return (rv);
1290
}
1291

    
1292
/*
1293
 * Print the ASCII string for the given ASCII JS string, expanding ConsStrings
1294
 * and ExternalStrings as needed.
1295
 */
1296
static int jsstr_print_seq(uintptr_t, uint_t, char **, size_t *);
1297
static int jsstr_print_cons(uintptr_t, uint_t, char **, size_t *);
1298
static int jsstr_print_external(uintptr_t, uint_t, char **, size_t *);
1299

    
1300
static int
1301
jsstr_print(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
1302
{
1303
        uint8_t typebyte;
1304
        int err = 0;
1305
        char *lbufp;
1306
        size_t llen;
1307
        char buf[64];
1308
        boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
1309

    
1310
        if (read_typebyte(&typebyte, addr) != 0)
1311
                return (0);
1312

    
1313
        if (!V8_TYPE_STRING(typebyte)) {
1314
                (void) bsnprintf(bufp, lenp, "<not a string>");
1315
                return (0);
1316
        }
1317

    
1318
        if (!V8_STRENC_ASCII(typebyte)) {
1319
                (void) bsnprintf(bufp, lenp, "<two-byte string>");
1320
                return (0);
1321
        }
1322

    
1323
        if (verbose) {
1324
                lbufp = buf;
1325
                llen = sizeof (buf);
1326
                (void) obj_jstype(addr, &lbufp, &llen, NULL);
1327
                mdb_printf("%s\n", buf);
1328
                (void) mdb_inc_indent(4);
1329
        }
1330

    
1331
        if (V8_STRREP_SEQ(typebyte))
1332
                err = jsstr_print_seq(addr, flags, bufp, lenp);
1333
        else if (V8_STRREP_CONS(typebyte))
1334
                err = jsstr_print_cons(addr, flags, bufp, lenp);
1335
        else if (V8_STRREP_EXT(typebyte))
1336
                err = jsstr_print_external(addr, flags, bufp, lenp);
1337
        else {
1338
                (void) bsnprintf(bufp, lenp, "<unknown string type>");
1339
                err = -1;
1340
        }
1341

    
1342
        if (verbose)
1343
                (void) mdb_dec_indent(4);
1344

    
1345
        return (err);
1346
}
1347

    
1348
static int
1349
jsstr_print_seq(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
1350
{
1351
        /*
1352
         * To allow the caller to allocate a very large buffer for strings,
1353
         * we'll allocate a buffer sized based on our input, making it at
1354
         * least enough space for our ellipsis and at most 256K.
1355
         */
1356
        uintptr_t len, rlen, blen = *lenp + sizeof ("[...]") + 1;
1357
        char *buf = alloca(MIN(blen, 256 * 1024));
1358
        boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
1359
        boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
1360

    
1361
        if (read_heap_smi(&len, addr, V8_OFF_STRING_LENGTH) != 0)
1362
                return (-1);
1363

    
1364
        rlen = len <= blen - 1 ? len : blen - sizeof ("[...]");
1365

    
1366
        if (verbose)
1367
                mdb_printf("length: %d, will read: %d\n", len, rlen);
1368

    
1369
        buf[0] = '\0';
1370

    
1371
        if (rlen > 0 && mdb_readstr(buf, rlen + 1,
1372
            addr + V8_OFF_SEQASCIISTR_CHARS) == -1) {
1373
                v8_warn("failed to read SeqString data");
1374
                return (-1);
1375
        }
1376

    
1377
        if (rlen != len)
1378
                (void) strlcat(buf, "[...]", blen);
1379

    
1380
        if (verbose)
1381
                mdb_printf("value: \"%s\"\n", buf);
1382

    
1383
        (void) bsnprintf(bufp, lenp, "%s%s%s",
1384
            quoted ? "\"" : "", buf, quoted ? "\"" : "");
1385

    
1386
        return (0);
1387
}
1388

    
1389
static int
1390
jsstr_print_cons(uintptr_t addr, uint_t flags, char **bufp, size_t *lenp)
1391
{
1392
        boolean_t verbose = flags & JSSTR_VERBOSE ? B_TRUE : B_FALSE;
1393
        boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
1394
        uintptr_t ptr1, ptr2;
1395

    
1396
        if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0 ||
1397
            read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0)
1398
                return (-1);
1399

    
1400
        if (verbose) {
1401
                mdb_printf("ptr1: %p\n", ptr1);
1402
                mdb_printf("ptr2: %p\n", ptr2);
1403
        }
1404

    
1405
        if (quoted)
1406
                (void) bsnprintf(bufp, lenp, "\"");
1407

    
1408
        if (jsstr_print(ptr1, verbose, bufp, lenp) != 0)
1409
                return (-1);
1410

    
1411
        if (jsstr_print(ptr2, verbose, bufp, lenp) != 0)
1412
                return (-1);
1413

    
1414
        if (quoted)
1415
                (void) bsnprintf(bufp, lenp, "\"");
1416

    
1417
        return (0);
1418
}
1419

    
1420
static int
1421
jsstr_print_external(uintptr_t addr, uint_t flags, char **bufp,
1422
    size_t *lenp)
1423
{
1424
        uintptr_t ptr1, ptr2;
1425
        size_t blen = *lenp + 1;
1426
        char *buf = alloca(blen);
1427
        boolean_t quoted = flags & JSSTR_QUOTED ? B_TRUE : B_FALSE;
1428

    
1429
        if (flags & JSSTR_VERBOSE)
1430
                mdb_printf("assuming Node.js string\n");
1431

    
1432
        if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0)
1433
                return (-1);
1434

    
1435
        if (mdb_vread(&ptr2, sizeof (ptr2),
1436
            ptr1 + NODE_OFF_EXTSTR_DATA) == -1) {
1437
                v8_warn("failed to read node external pointer: %p",
1438
                    ptr1 + NODE_OFF_EXTSTR_DATA);
1439
                return (-1);
1440
        }
1441

    
1442
        if (mdb_readstr(buf, blen, ptr2) == -1) {
1443
                v8_warn("failed to read ExternalString data");
1444
                return (-1);
1445
        }
1446

    
1447
        if (buf[0] != '\0' && !isascii(buf[0])) {
1448
                v8_warn("failed to read ExternalString ascii data\n");
1449
                return (-1);
1450
        }
1451

    
1452
        (void) bsnprintf(bufp, lenp, "%s%s%s",
1453
            quoted ? "\"" : "", buf, quoted ? "\"" : "");
1454

    
1455
        return (0);
1456
}
1457

    
1458
/*
1459
 * Returns true if the given address refers to the "undefined" object.  Returns
1460
 * false on failure (since we shouldn't fail on the actual "undefined" value).
1461
 */
1462
static boolean_t
1463
jsobj_is_undefined(uintptr_t addr)
1464
{
1465
        uint8_t type;
1466
        uintptr_t strptr;
1467
        const char *typename;
1468
        char buf[16];
1469
        char *bufp = buf;
1470
        size_t len = sizeof (buf);
1471

    
1472
        v8_silent++;
1473

    
1474
        if (read_typebyte(&type, addr) != 0) {
1475
                v8_silent--;
1476
                return (B_FALSE);
1477
        }
1478

    
1479
        v8_silent--;
1480

    
1481
        typename = enum_lookup_str(v8_types, type, "<unknown>");
1482
        if (strcmp(typename, "Oddball") != 0)
1483
                return (B_FALSE);
1484

    
1485
        if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) == -1)
1486
                return (B_FALSE);
1487

    
1488
        if (jsstr_print(strptr, JSSTR_NUDE, &bufp, &len) != 0)
1489
                return (B_FALSE);
1490

    
1491
        return (strcmp(buf, "undefined") == 0);
1492
}
1493

    
1494
static int
1495
jsobj_properties(uintptr_t addr,
1496
    int (*func)(const char *, uintptr_t, void *), void *arg)
1497
{
1498
        uintptr_t ptr, map;
1499
        uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans;
1500
        size_t size, nprops, ndescs, ncontent, ntrans;
1501
        ssize_t ii, rndescs;
1502
        uint8_t type, ninprops;
1503
        int rval = -1;
1504
        size_t ps = sizeof (uintptr_t);
1505
        ssize_t off;
1506

    
1507
        /*
1508
         * Objects have either "fast" properties represented with a FixedArray
1509
         * or slow properties represented with a Dictionary.
1510
         */
1511
        if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1)
1512
                return (-1);
1513

    
1514
        if (read_typebyte(&type, ptr) != 0)
1515
                return (-1);
1516

    
1517
        if (type != V8_TYPE_FIXEDARRAY) {
1518
                /*
1519
                 * If our properties aren't a fixed array, we'll emit a member
1520
                 * that contains the type name, but with a NULL value.
1521
                 */
1522
                char buf[256];
1523

    
1524
                (void) mdb_snprintf(buf, sizeof (buf), "<%s>",
1525
                    enum_lookup_str(v8_types, type, "unknown"));
1526

    
1527
                return (func(buf, NULL, arg));
1528
        }
1529

    
1530
        /*
1531
         * To iterate the properties, we need to examine the instance
1532
         * descriptors of the associated Map object.  Depending on the version
1533
         * of V8, this might be found directly from the map -- or indirectly
1534
         * via the transitions array.
1535
         */
1536
        if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1)
1537
                goto err;
1538

    
1539
        if (V8_DICT_SHIFT != -1) {
1540
                uintptr_t bit_field3;
1541

    
1542
                if (mdb_vread(&bit_field3, sizeof (bit_field3),
1543
                    map + V8_OFF_MAP_BIT_FIELD3) == -1)
1544
                        goto err;
1545

    
1546
                if (V8_SMI_VALUE(bit_field3) & (1 << V8_DICT_SHIFT))
1547
                        return (read_heap_dict(ptr, func, arg));
1548
        } else if (V8_OFF_MAP_INSTANCE_DESCRIPTORS != -1) {
1549
                uintptr_t bit_field3;
1550

    
1551
                if (mdb_vread(&bit_field3, sizeof (bit_field3),
1552
                    map + V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1)
1553
                        goto err;
1554

    
1555
                if (V8_SMI_VALUE(bit_field3) == (1 << V8_ISSHARED_SHIFT)) {
1556
                        /*
1557
                         * On versions of V8 prior to that used in 0.10,
1558
                         * the instance descriptors were overloaded to also
1559
                         * be bit_field3 -- and there was no way from that
1560
                         * field to infer a dictionary type.  Because we
1561
                         * can't determine if the map is actually the
1562
                         * hash_table_map, we assume that if it's an object
1563
                         * that has kIsShared set, that it is in fact a
1564
                         * dictionary -- an assumption that is assuredly in
1565
                         * error in some cases.
1566
                         */
1567
                        return (read_heap_dict(ptr, func, arg));
1568
                }
1569
        }
1570

    
1571
        if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0)
1572
                goto err;
1573

    
1574
        if ((off = V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) {
1575
                if (V8_OFF_MAP_TRANSITIONS == -1 ||
1576
                    V8_TRANSITIONS_IDX_DESC == -1 ||
1577
                    V8_PROP_IDX_CONTENT != -1) {
1578
                        mdb_warn("missing instance_descriptors, but did "
1579
                            "not find expected transitions array metadata; "
1580
                            "cannot read properties\n");
1581
                        goto err;
1582
                }
1583

    
1584
                off = V8_OFF_MAP_TRANSITIONS;
1585
        }
1586

    
1587
        if (mdb_vread(&ptr, ps, map + off) == -1)
1588
                goto err;
1589

    
1590
        if (V8_OFF_MAP_INSTANCE_DESCRIPTORS == -1) {
1591
                if (read_heap_array(ptr, &trans, &ntrans, UM_SLEEP) != 0)
1592
                        goto err;
1593

    
1594
                ptr = trans[V8_TRANSITIONS_IDX_DESC];
1595
                mdb_free(trans, ntrans * sizeof (uintptr_t));
1596
        }
1597

    
1598
        if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0)
1599
                goto err;
1600

    
1601
        if (read_size(&size, addr) != 0)
1602
                size = 0;
1603

    
1604
        if (mdb_vread(&ninprops, 1, map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1)
1605
                goto err;
1606

    
1607
        if (V8_PROP_IDX_CONTENT != -1 && V8_PROP_IDX_CONTENT < ndescs &&
1608
            read_heap_array(descs[V8_PROP_IDX_CONTENT], &content,
1609
            &ncontent, UM_SLEEP) != 0)
1610
                goto err;
1611

    
1612
        if (V8_PROP_IDX_CONTENT == -1) {
1613
                /*
1614
                 * On node v0.8 and later, the content is not stored in an
1615
                 * orthogonal FixedArray, but rather with the descriptors.
1616
                 */
1617
                content = descs;
1618
                ncontent = ndescs;
1619
                rndescs = ndescs > V8_PROP_IDX_FIRST ?
1620
                    (ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0;
1621
        } else {
1622
                rndescs = ndescs - V8_PROP_IDX_FIRST;
1623
        }
1624

    
1625
        for (ii = 0; ii < rndescs; ii++) {
1626
                uintptr_t keyidx, validx, detidx, baseidx;
1627
                char buf[1024];
1628
                intptr_t val;
1629
                size_t len = sizeof (buf);
1630
                char *c = buf;
1631

    
1632
                if (V8_PROP_IDX_CONTENT != -1) {
1633
                        /*
1634
                         * In node versions prior to v0.8, this was hardcoded
1635
                         * in the V8 implementation, so we hardcode it here
1636
                         * as well.
1637
                         */
1638
                        keyidx = ii + V8_PROP_IDX_FIRST;
1639
                        validx = ii << 1;
1640
                        detidx = (ii << 1) + 1;
1641
                } else {
1642
                        baseidx = V8_PROP_IDX_FIRST + (ii * V8_PROP_DESC_SIZE);
1643
                        keyidx = baseidx + V8_PROP_DESC_KEY;
1644
                        validx = baseidx + V8_PROP_DESC_VALUE;
1645
                        detidx = baseidx + V8_PROP_DESC_DETAILS;
1646
                }
1647

    
1648
                if (detidx >= ncontent) {
1649
                        v8_warn("property descriptor %d: detidx (%d) "
1650
                            "out of bounds for content array (length %d)\n",
1651
                            ii, detidx, ncontent);
1652
                        continue;
1653
                }
1654

    
1655
                if (!V8_DESC_ISFIELD(content[detidx]))
1656
                        continue;
1657

    
1658
                if (keyidx >= ndescs) {
1659
                        v8_warn("property descriptor %d: keyidx (%d) "
1660
                            "out of bounds for descriptor array (length %d)\n",
1661
                            ii, keyidx, ndescs);
1662
                        continue;
1663
                }
1664

    
1665
                if (jsstr_print(descs[keyidx], JSSTR_NUDE, &c, &len) != 0)
1666
                        continue;
1667

    
1668
                val = (intptr_t)content[validx];
1669

    
1670
                if (!V8_IS_SMI(val)) {
1671
                        v8_warn("object %p: property descriptor %d: value "
1672
                            "index value is not an SMI: %p\n", addr, ii, val);
1673
                        continue;
1674
                }
1675

    
1676
                val = V8_SMI_VALUE(val) - ninprops;
1677

    
1678
                if (val < 0) {
1679
                        /* property is stored directly in the object */
1680
                        if (mdb_vread(&ptr, sizeof (ptr), addr + V8_OFF_HEAP(
1681
                            size + val * sizeof (uintptr_t))) == -1) {
1682
                                v8_warn("failed to read in-object "
1683
                                    "property at %p\n", addr + V8_OFF_HEAP(
1684
                                    size + val * sizeof (uintptr_t)));
1685
                                continue;
1686
                        }
1687
                } else {
1688
                        /* property should be in "props" array */
1689
                        if (val >= nprops) {
1690
                                v8_warn("property descriptor %d: value index "
1691
                                    "value (%d) out of bounds (%d)\n", ii, val,
1692
                                    nprops);
1693
                                continue;
1694
                        }
1695

    
1696
                        ptr = props[val];
1697
                }
1698

    
1699
                if (func(buf, ptr, arg) != 0)
1700
                        goto err;
1701
        }
1702

    
1703
        rval = 0;
1704
err:
1705
        if (props != NULL)
1706
                mdb_free(props, nprops * sizeof (uintptr_t));
1707

    
1708
        if (descs != NULL)
1709
                mdb_free(descs, ndescs * sizeof (uintptr_t));
1710

    
1711
        if (content != NULL && V8_PROP_IDX_CONTENT != -1)
1712
                mdb_free(content, ncontent * sizeof (uintptr_t));
1713

    
1714
        return (rval);
1715
}
1716

    
1717
/*
1718
 * Given the line endings table in "lendsp", computes the line number for the
1719
 * given token position and print the result into "buf".  If "lendsp" is
1720
 * undefined, prints the token position instead.
1721
 */
1722
static int
1723
jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen)
1724
{
1725
        uintptr_t size, bufsz, lower, upper, ii = 0;
1726
        uintptr_t *data;
1727

    
1728
        if (jsobj_is_undefined(lendsp)) {
1729
                /*
1730
                 * The token position is an SMI, but it comes in as its raw
1731
                 * value so we can more easily compare it to values in the line
1732
                 * endings table.  If we're just printing the position directly,
1733
                 * we must convert it here.
1734
                 */
1735
                mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos));
1736
                return (0);
1737
        }
1738

    
1739
        if (read_heap_smi(&size, lendsp, V8_OFF_FIXEDARRAY_LENGTH) != 0)
1740
                return (-1);
1741

    
1742
        bufsz = size * sizeof (data[0]);
1743

    
1744
        if ((data = mdb_alloc(bufsz, UM_NOSLEEP)) == NULL) {
1745
                v8_warn("failed to alloc %d bytes for FixedArray data", bufsz);
1746
                return (-1);
1747
        }
1748

    
1749
        if (mdb_vread(data, bufsz, lendsp + V8_OFF_FIXEDARRAY_DATA) != bufsz) {
1750
                v8_warn("failed to read FixedArray data");
1751
                mdb_free(data, bufsz);
1752
                return (-1);
1753
        }
1754

    
1755
        lower = 0;
1756
        upper = size - 1;
1757

    
1758
        if (tokpos > data[upper]) {
1759
                (void) strlcpy(buf, "position out of range", buflen);
1760
                mdb_free(data, bufsz);
1761
                return (0);
1762
        }
1763

    
1764
        if (tokpos <= data[0]) {
1765
                (void) strlcpy(buf, "line 1", buflen);
1766
                mdb_free(data, bufsz);
1767
                return (0);
1768
        }
1769

    
1770
        while (upper >= 1) {
1771
                ii = (lower + upper) >> 1;
1772
                if (tokpos > data[ii])
1773
                        lower = ii + 1;
1774
                else if (tokpos <= data[ii - 1])
1775
                        upper = ii - 1;
1776
                else
1777
                        break;
1778
        }
1779

    
1780
        (void) mdb_snprintf(buf, buflen, "line %d", ii + 1);
1781
        mdb_free(data, bufsz);
1782
        return (0);
1783
}
1784

    
1785
/*
1786
 * Given a SharedFunctionInfo object, prints into bufp a name of the function
1787
 * suitable for printing.  This function attempts to infer a name for anonymous
1788
 * functions.
1789
 */
1790
static int
1791
jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp)
1792
{
1793
        uintptr_t ptrp;
1794
        char *bufs = *bufp;
1795

    
1796
        if (read_heap_ptr(&ptrp, funcinfop,
1797
            V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0 ||
1798
            jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0)
1799
                return (-1);
1800

    
1801
        if (*bufp != bufs)
1802
                return (0);
1803

    
1804
        if (read_heap_ptr(&ptrp, funcinfop,
1805
            V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME) != 0) {
1806
                (void) bsnprintf(bufp, lenp, "<anonymous>");
1807
                return (0);
1808
        }
1809

    
1810
        (void) bsnprintf(bufp, lenp, "<anonymous> (as ");
1811
        bufs = *bufp;
1812

    
1813
        if (jsstr_print(ptrp, JSSTR_NUDE, bufp, lenp) != 0)
1814
                return (-1);
1815

    
1816
        if (*bufp == bufs)
1817
                (void) bsnprintf(bufp, lenp, "<anon>");
1818

    
1819
        (void) bsnprintf(bufp, lenp, ")");
1820

    
1821
        return (0);
1822
}
1823

    
1824
/*
1825
 * JavaScript-level object printing
1826
 */
1827
typedef struct jsobj_print {
1828
        char **jsop_bufp;
1829
        size_t *jsop_lenp;
1830
        int jsop_indent;
1831
        uint64_t jsop_depth;
1832
        boolean_t jsop_printaddr;
1833
        uintptr_t jsop_baseaddr;
1834
        int jsop_nprops;
1835
        const char *jsop_member;
1836
        boolean_t jsop_found;
1837
} jsobj_print_t;
1838

    
1839
static int jsobj_print_number(uintptr_t, jsobj_print_t *);
1840
static int jsobj_print_oddball(uintptr_t, jsobj_print_t *);
1841
static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *);
1842
static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *);
1843
static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *);
1844
static int jsobj_print_jsdate(uintptr_t, jsobj_print_t *);
1845

    
1846
static int
1847
jsobj_print(uintptr_t addr, jsobj_print_t *jsop)
1848
{
1849
        uint8_t type;
1850
        const char *klass;
1851
        char **bufp = jsop->jsop_bufp;
1852
        size_t *lenp = jsop->jsop_lenp;
1853

    
1854
        const struct {
1855
                char *name;
1856
                int (*func)(uintptr_t, jsobj_print_t *);
1857
        } table[] = {
1858
                { "HeapNumber", jsobj_print_number },
1859
                { "Oddball", jsobj_print_oddball },
1860
                { "JSObject", jsobj_print_jsobject },
1861
                { "JSArray", jsobj_print_jsarray },
1862
                { "JSFunction", jsobj_print_jsfunction },
1863
                { "JSDate", jsobj_print_jsdate },
1864
                { NULL }
1865
        }, *ent;
1866

    
1867
        if (jsop->jsop_baseaddr != NULL && jsop->jsop_member == NULL)
1868
                (void) bsnprintf(bufp, lenp, "%p: ", jsop->jsop_baseaddr);
1869

    
1870
        if (jsop->jsop_printaddr && jsop->jsop_member == NULL)
1871
                (void) bsnprintf(bufp, lenp, "%p: ", addr);
1872

    
1873
        if (V8_IS_SMI(addr)) {
1874
                (void) bsnprintf(bufp, lenp, "%d", V8_SMI_VALUE(addr));
1875
                return (0);
1876
        }
1877

    
1878
        if (!V8_IS_HEAPOBJECT(addr)) {
1879
                v8_warn("not a heap object: %p\n", addr);
1880
                return (-1);
1881
        }
1882

    
1883
        if (read_typebyte(&type, addr) != 0)
1884
                return (-1);
1885

    
1886
        if (V8_TYPE_STRING(type)) {
1887
                if (jsstr_print(addr, JSSTR_QUOTED, bufp, lenp) == -1)
1888
                        return (-1);
1889

    
1890
                return (0);
1891
        }
1892

    
1893
        klass = enum_lookup_str(v8_types, type, "<unknown>");
1894

    
1895
        for (ent = &table[0]; ent->name != NULL; ent++) {
1896
                if (strcmp(klass, ent->name) == 0)
1897
                        return (ent->func(addr, jsop));
1898
        }
1899

    
1900
        v8_warn("%p: unknown JavaScript object type \"%s\"\n", addr, klass);
1901
        return (-1);
1902
}
1903

    
1904
static int
1905
jsobj_print_number(uintptr_t addr, jsobj_print_t *jsop)
1906
{
1907
        char **bufp = jsop->jsop_bufp;
1908
        size_t *lenp = jsop->jsop_lenp;
1909
        double numval;
1910

    
1911
        if (read_heap_double(&numval, addr, V8_OFF_HEAPNUMBER_VALUE) == -1)
1912
                return (-1);
1913

    
1914
        if (numval == (long long)numval)
1915
                (void) bsnprintf(bufp, lenp, "%lld", (long long)numval);
1916
        else
1917
                (void) bsnprintf(bufp, lenp, "%e", numval);
1918

    
1919
        return (0);
1920
}
1921

    
1922
static int
1923
jsobj_print_oddball(uintptr_t addr, jsobj_print_t *jsop)
1924
{
1925
        char **bufp = jsop->jsop_bufp;
1926
        size_t *lenp = jsop->jsop_lenp;
1927
        uintptr_t strptr;
1928

    
1929
        if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) != 0)
1930
                return (-1);
1931

    
1932
        return (jsstr_print(strptr, JSSTR_NUDE, bufp, lenp));
1933
}
1934

    
1935
static int
1936
jsobj_print_prop(const char *desc, uintptr_t val, void *arg)
1937
{
1938
        jsobj_print_t *jsop = arg, descend;
1939
        char **bufp = jsop->jsop_bufp;
1940
        size_t *lenp = jsop->jsop_lenp;
1941

    
1942
        (void) bsnprintf(bufp, lenp, "%s\n%*s%s: ", jsop->jsop_nprops == 0 ?
1943
            "{" : "", jsop->jsop_indent + 4, "", desc);
1944

    
1945
        descend = *jsop;
1946
        descend.jsop_depth--;
1947
        descend.jsop_indent += 4;
1948

    
1949
        (void) jsobj_print(val, &descend);
1950
        (void) bsnprintf(bufp, lenp, ",");
1951

    
1952
        jsop->jsop_nprops++;
1953

    
1954
        return (0);
1955
}
1956

    
1957
static int
1958
jsobj_print_prop_member(const char *desc, uintptr_t val, void *arg)
1959
{
1960
        jsobj_print_t *jsop = arg, descend;
1961
        const char *member = jsop->jsop_member, *next = member;
1962
        int rv;
1963

    
1964
        for (; *next != '\0' && *next != '.' && *next != '['; next++)
1965
                continue;
1966

    
1967
        if (*member == '[') {
1968
                mdb_warn("cannot use array indexing on an object\n");
1969
                return (-1);
1970
        }
1971

    
1972
        if (strncmp(member, desc, next - member) != 0)
1973
                return (0);
1974

    
1975
        if (desc[next - member] != '\0')
1976
                return (0);
1977

    
1978
        /*
1979
         * This property matches the desired member; descend.
1980
         */
1981
        descend = *jsop;
1982

    
1983
        if (*next == '\0') {
1984
                descend.jsop_member = NULL;
1985
                descend.jsop_found = B_TRUE;
1986
        } else {
1987
                descend.jsop_member = *next == '.' ? next + 1 : next;
1988
        }
1989

    
1990
        rv = jsobj_print(val, &descend);
1991
        jsop->jsop_found = descend.jsop_found;
1992

    
1993
        return (rv);
1994
}
1995

    
1996
static int
1997
jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop)
1998
{
1999
        char **bufp = jsop->jsop_bufp;
2000
        size_t *lenp = jsop->jsop_lenp;
2001

    
2002
        if (jsop->jsop_member != NULL)
2003
                return (jsobj_properties(addr, jsobj_print_prop_member, jsop));
2004

    
2005
        if (jsop->jsop_depth == 0) {
2006
                (void) bsnprintf(bufp, lenp, "[...]");
2007
                return (0);
2008
        }
2009

    
2010
        jsop->jsop_nprops = 0;
2011

    
2012
        if (jsobj_properties(addr, jsobj_print_prop, jsop) != 0)
2013
                return (-1);
2014

    
2015
        if (jsop->jsop_nprops > 0) {
2016
                (void) bsnprintf(bufp, lenp, "\n%*s", jsop->jsop_indent, "");
2017
        } else if (jsop->jsop_nprops == 0) {
2018
                (void) bsnprintf(bufp, lenp, "{");
2019
        } else {
2020
                (void) bsnprintf(bufp, lenp, "{ /* unknown property */ ");
2021
        }
2022

    
2023
        (void) bsnprintf(bufp, lenp, "}");
2024

    
2025
        return (0);
2026
}
2027

    
2028
static int
2029
jsobj_print_jsarray_member(uintptr_t addr, jsobj_print_t *jsop)
2030
{
2031
        uintptr_t *elts;
2032
        jsobj_print_t descend;
2033
        uintptr_t ptr;
2034
        const char *member = jsop->jsop_member, *end, *p;
2035
        size_t elt = 0, place = 1, len, rv;
2036

    
2037
        if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
2038
            read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0)
2039
                return (-1);
2040

    
2041
        if (*member != '[') {
2042
                mdb_warn("expected bracketed array index; "
2043
                    "found '%s'\n", member);
2044
                return (-1);
2045
        }
2046

    
2047
        if ((end = strchr(member, ']')) == NULL) {
2048
                mdb_warn("missing array index terminator\n");
2049
                return (-1);
2050
        }
2051

    
2052
        /*
2053
         * We know where our array index ends; convert it to an integer
2054
         * by stepping through it from least significant digit to most.
2055
         */
2056
        for (p = end - 1; p > member; p--) {
2057
                if (*p < '0' || *p > '9') {
2058
                        mdb_warn("illegal array index at '%c'\n", *p);
2059
                        return (-1);
2060
                }
2061

    
2062
                elt += (*p - '0') * place;
2063
                place *= 10;
2064
        }
2065

    
2066
        if (place == 1) {
2067
                mdb_warn("missing array index\n");
2068
                return (-1);
2069
        }
2070

    
2071
        if (elt >= len) {
2072
                mdb_warn("array index %d exceeds size of %d\n", elt, len);
2073
                return (-1);
2074
        }
2075

    
2076
        descend = *jsop;
2077

    
2078
        switch (*(++end)) {
2079
        case '\0':
2080
                descend.jsop_member = NULL;
2081
                descend.jsop_found = B_TRUE;
2082
                break;
2083

    
2084
        case '.':
2085
                descend.jsop_member = end + 1;
2086
                break;
2087

    
2088
        case '[':
2089
                descend.jsop_member = end;
2090
                break;
2091

    
2092
        default:
2093
                mdb_warn("illegal character '%c' following "
2094
                    "array index terminator\n", *end);
2095
                return (-1);
2096
        }
2097

    
2098
        rv = jsobj_print(elts[elt], &descend);
2099
        jsop->jsop_found = descend.jsop_found;
2100

    
2101
        return (rv);
2102
}
2103

    
2104
static int
2105
jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop)
2106
{
2107
        char **bufp = jsop->jsop_bufp;
2108
        size_t *lenp = jsop->jsop_lenp;
2109
        int indent = jsop->jsop_indent;
2110
        jsobj_print_t descend;
2111
        uintptr_t ptr;
2112
        uintptr_t *elts;
2113
        size_t ii, len;
2114

    
2115
        if (jsop->jsop_member != NULL)
2116
                return (jsobj_print_jsarray_member(addr, jsop));
2117

    
2118
        if (jsop->jsop_depth == 0) {
2119
                (void) bsnprintf(bufp, lenp, "[...]");
2120
                return (0);
2121
        }
2122

    
2123
        if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
2124
            read_heap_array(ptr, &elts, &len, UM_SLEEP | UM_GC) != 0)
2125
                return (-1);
2126

    
2127
        if (len == 0) {
2128
                (void) bsnprintf(bufp, lenp, "[]");
2129
                return (0);
2130
        }
2131

    
2132
        descend = *jsop;
2133
        descend.jsop_depth--;
2134
        descend.jsop_indent += 4;
2135

    
2136
        if (len == 1) {
2137
                (void) bsnprintf(bufp, lenp, "[ ");
2138
                (void) jsobj_print(elts[0], &descend);
2139
                (void) bsnprintf(bufp, lenp, " ]");
2140
                return (0);
2141
        }
2142

    
2143
        (void) bsnprintf(bufp, lenp, "[\n");
2144

    
2145
        for (ii = 0; ii < len; ii++) {
2146
                (void) bsnprintf(bufp, lenp, "%*s", indent + 4, "");
2147
                (void) jsobj_print(elts[ii], &descend);
2148
                (void) bsnprintf(bufp, lenp, ",\n");
2149
        }
2150

    
2151
        (void) bsnprintf(bufp, lenp, "%*s", indent, "");
2152
        (void) bsnprintf(bufp, lenp, "]");
2153

    
2154
        return (0);
2155
}
2156

    
2157
static int
2158
jsobj_print_jsfunction(uintptr_t addr, jsobj_print_t *jsop)
2159
{
2160
        char **bufp = jsop->jsop_bufp;
2161
        size_t *lenp = jsop->jsop_lenp;
2162
        uintptr_t shared;
2163

    
2164
        if (read_heap_ptr(&shared, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
2165
                return (-1);
2166

    
2167
        (void) bsnprintf(bufp, lenp, "function ");
2168
        return (jsfunc_name(shared, bufp, lenp) != 0);
2169
}
2170

    
2171
static int
2172
jsobj_print_jsdate(uintptr_t addr, jsobj_print_t *jsop)
2173
{
2174
        char **bufp = jsop->jsop_bufp;
2175
        size_t *lenp = jsop->jsop_lenp;
2176
        char buf[128];
2177
        uintptr_t value;
2178
        uint8_t type;
2179
        double numval;
2180

    
2181
        if (V8_OFF_JSDATE_VALUE == -1) {
2182
                (void) bsnprintf(bufp, lenp, "<JSDate>", buf);
2183
                return (0);
2184
        }
2185

    
2186
        if (read_heap_ptr(&value, addr, V8_OFF_JSDATE_VALUE) != 0)
2187
                return (-1);
2188

    
2189
        if (read_typebyte(&type, value) != 0)
2190
                return (-1);
2191

    
2192
        if (strcmp(enum_lookup_str(v8_types, type, ""), "HeapNumber") != 0)
2193
                return (-1);
2194

    
2195
        if (read_heap_double(&numval, value, V8_OFF_HEAPNUMBER_VALUE) == -1)
2196
                return (-1);
2197

    
2198
        mdb_snprintf(buf, sizeof (buf), "%Y",
2199
            (time_t)((long long)numval / MILLISEC));
2200
        (void) bsnprintf(bufp, lenp, "%lld (%s)", (long long)numval, buf);
2201

    
2202
        return (0);
2203
}
2204

    
2205
/*
2206
 * dcmd implementations
2207
 */
2208

    
2209
/* ARGSUSED */
2210
static int
2211
dcmd_v8classes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2212
{
2213
        v8_class_t *clp;
2214

    
2215
        for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
2216
                mdb_printf("%s\n", clp->v8c_name);
2217

    
2218
        return (DCMD_OK);
2219
}
2220

    
2221
static int
2222
do_v8code(uintptr_t addr, boolean_t opt_d)
2223
{
2224
        uintptr_t instrlen;
2225
        ssize_t instroff = V8_OFF_CODE_INSTRUCTION_START;
2226

    
2227
        if (read_heap_ptr(&instrlen, addr, V8_OFF_CODE_INSTRUCTION_SIZE) != 0)
2228
                return (DCMD_ERR);
2229

    
2230
        mdb_printf("code: %p\n", addr);
2231
        mdb_printf("instructions: [%p, %p)\n", addr + instroff,
2232
            addr + instroff + instrlen);
2233

    
2234
        if (!opt_d)
2235
                return (DCMD_OK);
2236

    
2237
        mdb_set_dot(addr + instroff);
2238

    
2239
        do {
2240
                (void) mdb_inc_indent(8); /* gets reset by mdb_eval() */
2241

    
2242
                /*
2243
                 * This is absolutely awful. We want to disassemble the above
2244
                 * range of instructions.  Because we don't know how many there
2245
                 * are, we can't use "::dis".  We resort to evaluating "./i",
2246
                 * but then we need to advance "." by the size of the
2247
                 * instruction just printed.  The only way to do that is by
2248
                 * printing out "+", but we don't want that to show up, so we
2249
                 * redirect it to /dev/null.
2250
                 */
2251
                if (mdb_eval("/i") != 0 ||
2252
                    mdb_eval("+=p ! cat > /dev/null") != 0) {
2253
                        (void) mdb_dec_indent(8);
2254
                        v8_warn("failed to disassemble at %p", mdb_get_dot());
2255
                        return (DCMD_ERR);
2256
                }
2257
        } while (mdb_get_dot() < addr + instroff + instrlen);
2258

    
2259
        (void) mdb_dec_indent(8);
2260
        return (DCMD_OK);
2261
}
2262

    
2263
/* ARGSUSED */
2264
static int
2265
dcmd_v8code(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2266
{
2267
        boolean_t opt_d = B_FALSE;
2268

    
2269
        if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
2270
            NULL) != argc)
2271
                return (DCMD_USAGE);
2272

    
2273
        return (do_v8code(addr, opt_d));
2274
}
2275

    
2276
/* ARGSUSED */
2277
static int
2278
dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2279
{
2280
        uint8_t type;
2281
        uintptr_t funcinfop, scriptp, lendsp, tokpos, namep, codep;
2282
        char *bufp;
2283
        size_t len;
2284
        boolean_t opt_d = B_FALSE;
2285
        char buf[512];
2286

    
2287
        if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
2288
            NULL) != argc)
2289
                return (DCMD_USAGE);
2290

    
2291
        if (read_typebyte(&type, addr) != 0)
2292
                return (DCMD_ERR);
2293

    
2294
        if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) {
2295
                v8_warn("%p is not an instance of JSFunction\n", addr);
2296
                return (DCMD_ERR);
2297
        }
2298

    
2299
        if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
2300
            read_heap_ptr(&tokpos, funcinfop,
2301
            V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 ||
2302
            read_heap_ptr(&scriptp, funcinfop,
2303
            V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
2304
            read_heap_ptr(&namep, scriptp, V8_OFF_SCRIPT_NAME) != 0 ||
2305
            read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
2306
                return (DCMD_ERR);
2307

    
2308
        bufp = buf;
2309
        len = sizeof (buf);
2310
        if (jsfunc_name(funcinfop, &bufp, &len) != 0)
2311
                return (DCMD_ERR);
2312

    
2313
        mdb_printf("%p: JSFunction: %s\n", addr, buf);
2314

    
2315
        bufp = buf;
2316
        len = sizeof (buf);
2317
        mdb_printf("defined at ");
2318

    
2319
        if (jsstr_print(namep, JSSTR_NUDE, &bufp, &len) == 0)
2320
                mdb_printf("%s ", buf);
2321

    
2322
        if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf)) == 0)
2323
                mdb_printf("%s", buf);
2324

    
2325
        mdb_printf("\n");
2326

    
2327
        if (read_heap_ptr(&codep,
2328
            funcinfop, V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0)
2329
                return (DCMD_ERR);
2330

    
2331
        return (do_v8code(codep, opt_d));
2332
}
2333

    
2334
/* ARGSUSED */
2335
static int
2336
dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2337
{
2338
        enum_print(v8_frametypes);
2339
        return (DCMD_OK);
2340
}
2341

    
2342
static void
2343
dcmd_v8print_help(void)
2344
{
2345
        mdb_printf(
2346
            "Prints out \".\" (a V8 heap object) as an instance of its C++\n"
2347
            "class.  With no arguments, the appropriate class is detected\n"
2348
            "automatically.  The 'class' argument overrides this to print an\n"
2349
            "object as an instance of the given class.  The list of known\n"
2350
            "classes can be viewed with ::jsclasses.");
2351
}
2352

    
2353
/* ARGSUSED */
2354
static int
2355
dcmd_v8print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2356
{
2357
        const char *rqclass;
2358
        v8_class_t *clp;
2359
        char *bufp;
2360
        size_t len;
2361
        uint8_t type;
2362
        char buf[256];
2363

    
2364
        if (argc < 1) {
2365
                /*
2366
                 * If no type was specified, determine it automatically.
2367
                 */
2368
                bufp = buf;
2369
                len = sizeof (buf);
2370
                if (obj_jstype(addr, &bufp, &len, &type) != 0)
2371
                        return (DCMD_ERR);
2372

    
2373
                if (type == 0) {
2374
                        /* For SMI or Failure, just print out the type. */
2375
                        mdb_printf("%s\n", buf);
2376
                        return (DCMD_OK);
2377
                }
2378

    
2379
                if ((rqclass = enum_lookup_str(v8_types, type, NULL)) == NULL) {
2380
                        v8_warn("object has unknown type\n");
2381
                        return (DCMD_ERR);
2382
                }
2383
        } else {
2384
                if (argv[0].a_type != MDB_TYPE_STRING)
2385
                        return (DCMD_USAGE);
2386

    
2387
                rqclass = argv[0].a_un.a_str;
2388
        }
2389

    
2390
        for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
2391
                if (strcmp(rqclass, clp->v8c_name) == 0)
2392
                        break;
2393
        }
2394

    
2395
        if (clp == NULL) {
2396
                v8_warn("unknown class '%s'\n", rqclass);
2397
                return (DCMD_USAGE);
2398
        }
2399

    
2400
        return (obj_print_class(addr, clp));
2401
}
2402

    
2403
/* ARGSUSED */
2404
static int
2405
dcmd_v8type(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2406
{
2407
        char buf[64];
2408
        char *bufp = buf;
2409
        size_t len = sizeof (buf);
2410

    
2411
        if (obj_jstype(addr, &bufp, &len, NULL) != 0)
2412
                return (DCMD_ERR);
2413

    
2414
        mdb_printf("0x%p: %s\n", addr, buf);
2415
        return (DCMD_OK);
2416
}
2417

    
2418
/* ARGSUSED */
2419
static int
2420
dcmd_v8types(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2421
{
2422
        enum_print(v8_types);
2423
        return (DCMD_OK);
2424
}
2425

    
2426
static int
2427
load_current_context(uintptr_t *fpp, uintptr_t *raddrp)
2428
{
2429
        mdb_reg_t regfp, regip;
2430

    
2431
#ifdef __amd64
2432
        if (mdb_getareg(1, "rbp", &regfp) != 0 ||
2433
            mdb_getareg(1, "rip", &regip) != 0) {
2434
#else
2435
#ifdef __i386
2436
        if (mdb_getareg(1, "ebp", &regfp) != 0 ||
2437
            mdb_getareg(1, "eip", &regip) != 0) {
2438
#else
2439
#error Unrecognized microprocessor
2440
#endif
2441
#endif
2442
                v8_warn("failed to load current context");
2443
                return (-1);
2444
        }
2445

    
2446
        if (fpp != NULL)
2447
                *fpp = (uintptr_t)regfp;
2448

    
2449
        if (raddrp != NULL)
2450
                *raddrp = (uintptr_t)regip;
2451

    
2452
        return (0);
2453
}
2454

    
2455
static int
2456
do_jsframe_special(uintptr_t fptr, uintptr_t raddr, char *prop)
2457
{
2458
        uintptr_t ftype;
2459
        const char *ftypename;
2460

    
2461
        /*
2462
         * First see if this looks like a native frame rather than a JavaScript
2463
         * frame.  We check this by asking MDB to print the return address
2464
         * symbolically.  If that works, we assume this was NOT a V8 frame,
2465
         * since those are never in the symbol table.
2466
         */
2467
        if (mdb_snprintf(NULL, 0, "%A", raddr) > 1) {
2468
                if (prop != NULL)
2469
                        return (0);
2470

    
2471
                mdb_printf("%p %a\n", fptr, raddr);
2472
                return (0);
2473
        }
2474

    
2475
        /*
2476
         * Figure out what kind of frame this is using the same algorithm as
2477
         * V8's ComputeType function.  First, look for an ArgumentsAdaptorFrame.
2478
         */
2479
        if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_CONTEXT) != -1 &&
2480
            V8_IS_SMI(ftype) &&
2481
            (ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
2482
            NULL)) != NULL && strstr(ftypename, "ArgumentsAdaptor") != NULL) {
2483
                if (prop != NULL)
2484
                        return (0);
2485

    
2486
                mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
2487
                return (0);
2488
        }
2489

    
2490
        /*
2491
         * Other special frame types are indicated by a marker.
2492
         */
2493
        if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_MARKER) != -1 &&
2494
            V8_IS_SMI(ftype)) {
2495
                if (prop != NULL)
2496
                        return (0);
2497

    
2498
                ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
2499
                    NULL);
2500

    
2501
                if (ftypename != NULL)
2502
                        mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
2503
                else
2504
                        mdb_printf("%p %a\n", fptr, raddr);
2505

    
2506
                return (0);
2507
        }
2508

    
2509
        return (-1);
2510
}
2511

    
2512
static int
2513
do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose,
2514
    char *func, char *prop)
2515
{
2516
        uintptr_t funcp, funcinfop, tokpos, scriptp, lendsp, ptrp;
2517
        uintptr_t ii, nargs;
2518
        const char *typename;
2519
        char *bufp;
2520
        size_t len;
2521
        uint8_t type;
2522
        char buf[256];
2523

    
2524
        /*
2525
         * Check for non-JavaScript frames first.
2526
         */
2527
        if (func == NULL && do_jsframe_special(fptr, raddr, prop) == 0)
2528
                return (DCMD_OK);
2529

    
2530
        /*
2531
         * At this point we assume we're looking at a JavaScript frame.  As with
2532
         * native frames, fish the address out of the parent frame.
2533
         */
2534
        if (mdb_vread(&funcp, sizeof (funcp),
2535
            fptr + V8_OFF_FP_FUNCTION) == -1) {
2536
                v8_warn("failed to read stack at %p",
2537
                    fptr + V8_OFF_FP_FUNCTION);
2538
                return (DCMD_ERR);
2539
        }
2540

    
2541
        /*
2542
         * Check if this thing is really a JSFunction at all. For some frames,
2543
         * it's a Code object, presumably indicating some internal frame.
2544
         */
2545
        v8_silent++;
2546

    
2547
        if (read_typebyte(&type, funcp) != 0 ||
2548
            (typename = enum_lookup_str(v8_types, type, NULL)) == NULL) {
2549
                v8_silent--;
2550

    
2551
                if (func != NULL || prop != NULL)
2552
                        return (DCMD_OK);
2553

    
2554
                mdb_printf("%p %a\n", fptr, raddr);
2555
                return (DCMD_OK);
2556
        }
2557

    
2558
        v8_silent--;
2559

    
2560
        if (strcmp("Code", typename) == 0) {
2561
                if (func != NULL || prop != NULL)
2562
                        return (DCMD_OK);
2563

    
2564
                mdb_printf("%p %a internal (Code: %p)\n", fptr, raddr, funcp);
2565
                return (DCMD_OK);
2566
        }
2567

    
2568
        if (strcmp("JSFunction", typename) != 0) {
2569
                if (func != NULL || prop != NULL)
2570
                        return (DCMD_OK);
2571

    
2572
                mdb_printf("%p %a unknown (%s: %p)", fptr, raddr, typename,
2573
                    funcp);
2574
                return (DCMD_OK);
2575
        }
2576

    
2577
        if (read_heap_ptr(&funcinfop, funcp, V8_OFF_JSFUNCTION_SHARED) != 0)
2578
                return (DCMD_ERR);
2579

    
2580
        bufp = buf;
2581
        len = sizeof (buf);
2582
        if (jsfunc_name(funcinfop, &bufp, &len) != 0)
2583
                return (DCMD_ERR);
2584

    
2585
        if (func != NULL && strcmp(buf, func) != 0)
2586
                return (DCMD_OK);
2587

    
2588
        if (prop == NULL)
2589
                mdb_printf("%p %a %s (%p)\n", fptr, raddr, buf, funcp);
2590

    
2591
        if (!verbose && prop == NULL)
2592
                return (DCMD_OK);
2593

    
2594
        /*
2595
         * Although the token position is technically an SMI, we're going to
2596
         * byte-compare it to other SMI values so we don't want decode it here.
2597
         */
2598
        if (read_heap_ptr(&tokpos, funcinfop,
2599
            V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0)
2600
                return (DCMD_ERR);
2601

    
2602
        if (read_heap_ptr(&scriptp, funcinfop,
2603
            V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0)
2604
                return (DCMD_ERR);
2605

    
2606
        if (read_heap_ptr(&ptrp, scriptp, V8_OFF_SCRIPT_NAME) != 0)
2607
                return (DCMD_ERR);
2608

    
2609
        bufp = buf;
2610
        len = sizeof (buf);
2611
        (void) jsstr_print(ptrp, JSSTR_NUDE, &bufp, &len);
2612

    
2613
        if (prop != NULL && strcmp(prop, "file") == 0) {
2614
                mdb_printf("%s\n", buf);
2615
                return (DCMD_OK);
2616
        }
2617

    
2618
        if (prop == NULL) {
2619
                (void) mdb_inc_indent(4);
2620
                mdb_printf("file: %s\n", buf);
2621
        }
2622

    
2623
        if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
2624
                return (DCMD_ERR);
2625

    
2626
        (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf));
2627

    
2628
        if (prop != NULL && strcmp(prop, "posn") == 0) {
2629
                mdb_printf("%s\n", buf);
2630
                return (DCMD_OK);
2631
        }
2632

    
2633
        if (prop == NULL)
2634
                mdb_printf("posn: %s\n", buf);
2635

    
2636
        if (read_heap_smi(&nargs, funcinfop,
2637
            V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) {
2638
                for (ii = 0; ii < nargs; ii++) {
2639
                        uintptr_t argptr;
2640
                        char arg[10];
2641

    
2642
                        if (mdb_vread(&argptr, sizeof (argptr),
2643
                            fptr + V8_OFF_FP_ARGS + (nargs - ii - 1) *
2644
                            sizeof (uintptr_t)) == -1)
2645
                                continue;
2646

    
2647
                        (void) snprintf(arg, sizeof (arg), "arg%d", ii + 1);
2648

    
2649
                        if (prop != NULL) {
2650
                                if (strcmp(arg, prop) != 0)
2651
                                        continue;
2652

    
2653
                                mdb_printf("%p\n", argptr);
2654
                                return (DCMD_OK);
2655
                        }
2656

    
2657
                        bufp = buf;
2658
                        len = sizeof (buf);
2659
                        (void) obj_jstype(argptr, &bufp, &len, NULL);
2660

    
2661
                        mdb_printf("arg%d: %p (%s)\n", (ii + 1), argptr, buf);
2662
                }
2663
        }
2664

    
2665
        if (prop != NULL) {
2666
                mdb_warn("unknown frame property '%s'\n", prop);
2667
                return (DCMD_ERR);
2668
        }
2669

    
2670
        (void) mdb_dec_indent(4);
2671

    
2672
        return (DCMD_OK);
2673
}
2674

    
2675
typedef struct findjsobjects_prop {
2676
        struct findjsobjects_prop *fjsp_next;
2677
        char fjsp_desc[1];
2678
} findjsobjects_prop_t;
2679

    
2680
typedef struct findjsobjects_instance {
2681
        uintptr_t fjsi_addr;
2682
        struct findjsobjects_instance *fjsi_next;
2683
} findjsobjects_instance_t;
2684

    
2685
typedef struct findjsobjects_obj {
2686
        findjsobjects_prop_t *fjso_props;
2687
        findjsobjects_prop_t *fjso_last;
2688
        size_t fjso_nprops;
2689
        findjsobjects_instance_t fjso_instances;
2690
        int fjso_ninstances;
2691
        avl_node_t fjso_node;
2692
        struct findjsobjects_obj *fjso_next;
2693
        boolean_t fjso_malformed;
2694
        char fjso_constructor[80];
2695
} findjsobjects_obj_t;
2696

    
2697
typedef struct findjsobjects_stats {
2698
        int fjss_heapobjs;
2699
        int fjss_cached;
2700
        int fjss_typereads;
2701
        int fjss_jsobjs;
2702
        int fjss_objects;
2703
        int fjss_arrays;
2704
        int fjss_uniques;
2705
} findjsobjects_stats_t;
2706

    
2707
typedef struct findjsobjects_reference {
2708
        uintptr_t fjsrf_addr;
2709
        char *fjsrf_desc;
2710
        size_t fjsrf_index;
2711
        struct findjsobjects_reference *fjsrf_next;
2712
} findjsobjects_reference_t;
2713

    
2714
typedef struct findjsobjects_referent {
2715
        avl_node_t fjsr_node;
2716
        uintptr_t fjsr_addr;
2717
        findjsobjects_reference_t *fjsr_head;
2718
        findjsobjects_reference_t *fjsr_tail;
2719
        struct findjsobjects_referent *fjsr_next;
2720
} findjsobjects_referent_t;
2721

    
2722
typedef struct findjsobjects_state {
2723
        uintptr_t fjs_addr;
2724
        uintptr_t fjs_size;
2725
        boolean_t fjs_verbose;
2726
        boolean_t fjs_brk;
2727
        boolean_t fjs_allobjs;
2728
        boolean_t fjs_initialized;
2729
        boolean_t fjs_marking;
2730
        boolean_t fjs_referred;
2731
        avl_tree_t fjs_tree;
2732
        avl_tree_t fjs_referents;
2733
        findjsobjects_referent_t *fjs_head;
2734
        findjsobjects_referent_t *fjs_tail;
2735
        findjsobjects_obj_t *fjs_current;
2736
        findjsobjects_obj_t *fjs_objects;
2737
        findjsobjects_stats_t fjs_stats;
2738
} findjsobjects_state_t;
2739

    
2740
findjsobjects_obj_t *
2741
findjsobjects_alloc(uintptr_t addr)
2742
{
2743
        findjsobjects_obj_t *obj;
2744

    
2745
        obj = mdb_zalloc(sizeof (findjsobjects_obj_t), UM_SLEEP);
2746
        obj->fjso_instances.fjsi_addr = addr;
2747
        obj->fjso_ninstances = 1;
2748

    
2749
        return (obj);
2750
}
2751

    
2752
void
2753
findjsobjects_free(findjsobjects_obj_t *obj)
2754
{
2755
        findjsobjects_prop_t *prop, *next;
2756

    
2757
        for (prop = obj->fjso_props; prop != NULL; prop = next) {
2758
                next = prop->fjsp_next;
2759
                mdb_free(prop, sizeof (findjsobjects_prop_t) +
2760
                    strlen(prop->fjsp_desc));
2761
        }
2762

    
2763
        mdb_free(obj, sizeof (findjsobjects_obj_t));
2764
}
2765

    
2766
int
2767
findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs)
2768
{
2769
        findjsobjects_prop_t *lprop, *rprop;
2770
        int rv;
2771

    
2772
        lprop = lhs->fjso_props;
2773
        rprop = rhs->fjso_props;
2774

    
2775
        while (lprop != NULL && rprop != NULL) {
2776
                if ((rv = strcmp(lprop->fjsp_desc, rprop->fjsp_desc)) != 0)
2777
                        return (rv > 0 ? 1 : -1);
2778

    
2779
                lprop = lprop->fjsp_next;
2780
                rprop = rprop->fjsp_next;
2781
        }
2782

    
2783
        if (lprop != NULL)
2784
                return (1);
2785

    
2786
        if (rprop != NULL)
2787
                return (-1);
2788

    
2789
        if (lhs->fjso_nprops > rhs->fjso_nprops)
2790
                return (1);
2791

    
2792
        if (lhs->fjso_nprops < rhs->fjso_nprops)
2793
                return (-1);
2794

    
2795
        rv = strcmp(lhs->fjso_constructor, rhs->fjso_constructor);
2796

    
2797
        return (rv < 0 ? -1 : rv > 0 ? 1 : 0);
2798
}
2799

    
2800
int
2801
findjsobjects_cmp_referents(findjsobjects_referent_t *lhs,
2802
    findjsobjects_referent_t *rhs)
2803
{
2804
        if (lhs->fjsr_addr < rhs->fjsr_addr)
2805
                return (-1);
2806

    
2807
        if (lhs->fjsr_addr > rhs->fjsr_addr)
2808
                return (1);
2809

    
2810
        return (0);
2811
}
2812

    
2813
int
2814
findjsobjects_cmp_ninstances(const void *l, const void *r)
2815
{
2816
        findjsobjects_obj_t *lhs = *((findjsobjects_obj_t **)l);
2817
        findjsobjects_obj_t *rhs = *((findjsobjects_obj_t **)r);
2818
        size_t lprod = lhs->fjso_ninstances * lhs->fjso_nprops;
2819
        size_t rprod = rhs->fjso_ninstances * rhs->fjso_nprops;
2820

    
2821
        if (lprod < rprod)
2822
                return (-1);
2823

    
2824
        if (lprod > rprod)
2825
                return (1);
2826

    
2827
        if (lhs->fjso_ninstances < rhs->fjso_ninstances)
2828
                return (-1);
2829

    
2830
        if (lhs->fjso_ninstances > rhs->fjso_ninstances)
2831
                return (1);
2832

    
2833
        if (lhs->fjso_nprops < rhs->fjso_nprops)
2834
                return (-1);
2835

    
2836
        if (lhs->fjso_nprops > rhs->fjso_nprops)
2837
                return (1);
2838

    
2839
        return (0);
2840
}
2841

    
2842
/*ARGSUSED*/
2843
int
2844
findjsobjects_prop(const char *desc, uintptr_t val, void *arg)
2845
{
2846
        findjsobjects_state_t *fjs = arg;
2847
        findjsobjects_obj_t *current = fjs->fjs_current;
2848
        findjsobjects_prop_t *prop;
2849

    
2850
        if (desc == NULL)
2851
                desc = "<unknown>";
2852

    
2853
        prop = mdb_zalloc(sizeof (findjsobjects_prop_t) +
2854
            strlen(desc), UM_SLEEP);
2855

    
2856
        strcpy(prop->fjsp_desc, desc);
2857

    
2858
        if (current->fjso_last != NULL) {
2859
                current->fjso_last->fjsp_next = prop;
2860
        } else {
2861
                current->fjso_props = prop;
2862
        }
2863

    
2864
        current->fjso_last = prop;
2865
        current->fjso_nprops++;
2866
        current->fjso_malformed =
2867
            val == NULL && current->fjso_nprops == 1 && desc[0] == '<';
2868

    
2869
        return (0);
2870
}
2871

    
2872
static void
2873
findjsobjects_constructor(findjsobjects_obj_t *obj)
2874
{
2875
        char *bufp = obj->fjso_constructor;
2876
        size_t len = sizeof (obj->fjso_constructor);
2877
        uintptr_t map, funcinfop;
2878
        uintptr_t addr = obj->fjso_instances.fjsi_addr;
2879
        uint8_t type;
2880

    
2881
        v8_silent++;
2882

    
2883
        if (read_heap_ptr(&map, addr, V8_OFF_HEAPOBJECT_MAP) != 0 ||
2884
            read_heap_ptr(&addr, map, V8_OFF_MAP_CONSTRUCTOR) != 0)
2885
                goto out;
2886

    
2887
        if (read_typebyte(&type, addr) != 0)
2888
                goto out;
2889

    
2890
        if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0)
2891
                goto out;
2892

    
2893
        if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
2894
                goto out;
2895

    
2896
        if (jsfunc_name(funcinfop, &bufp, &len) != 0)
2897
                goto out;
2898
out:
2899
        v8_silent--;
2900
}
2901

    
2902
int
2903
findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
2904
{
2905
        uintptr_t limit;
2906
        findjsobjects_stats_t *stats = &fjs->fjs_stats;
2907
        uint8_t type;
2908
        int jsobject = V8_TYPE_JSOBJECT, jsarray = V8_TYPE_JSARRAY;
2909
        caddr_t range = mdb_alloc(size, UM_SLEEP);
2910
        uintptr_t base = addr, mapaddr;
2911

    
2912
        if (mdb_vread(range, size, addr) == -1)
2913
                return (0);
2914

    
2915
        for (limit = addr + size; addr < limit; addr++) {
2916
                findjsobjects_instance_t *inst;
2917
                findjsobjects_obj_t *obj;
2918
                avl_index_t where;
2919

    
2920
                if (V8_IS_SMI(addr))
2921
                        continue;
2922

    
2923
                if (!V8_IS_HEAPOBJECT(addr))
2924
                        continue;
2925

    
2926
                stats->fjss_heapobjs++;
2927

    
2928
                mapaddr = *((uintptr_t *)((uintptr_t)range +
2929
                    (addr - base) + V8_OFF_HEAPOBJECT_MAP));
2930

    
2931
                if (!V8_IS_HEAPOBJECT(mapaddr))
2932
                        continue;
2933

    
2934
                mapaddr += V8_OFF_MAP_INSTANCE_ATTRIBUTES;
2935
                stats->fjss_typereads++;
2936

    
2937
                if (mapaddr >= base && mapaddr < base + size) {
2938
                        stats->fjss_cached++;
2939

    
2940
                        type = *((uint8_t *)((uintptr_t)range +
2941
                            (mapaddr - base)));
2942
                } else {
2943
                        if (mdb_vread(&type, sizeof (uint8_t), mapaddr) == -1)
2944
                                continue;
2945
                }
2946

    
2947
                if (type != jsobject && type != jsarray)
2948
                        continue;
2949

    
2950
                stats->fjss_jsobjs++;
2951

    
2952
                fjs->fjs_current = findjsobjects_alloc(addr);
2953

    
2954
                if (type == jsobject) {
2955
                        if (jsobj_properties(addr,
2956
                            findjsobjects_prop, fjs) != 0) {
2957
                                findjsobjects_free(fjs->fjs_current);
2958
                                fjs->fjs_current = NULL;
2959
                                continue;
2960
                        }
2961

    
2962
                        findjsobjects_constructor(fjs->fjs_current);
2963
                        stats->fjss_objects++;
2964
                } else {
2965
                        uintptr_t ptr;
2966
                        size_t *nprops = &fjs->fjs_current->fjso_nprops;
2967
                        ssize_t len = V8_OFF_JSARRAY_LENGTH;
2968
                        ssize_t elems = V8_OFF_JSOBJECT_ELEMENTS;
2969
                        ssize_t flen = V8_OFF_FIXEDARRAY_LENGTH;
2970
                        uintptr_t nelems;
2971
                        uint8_t t;
2972

    
2973
                        if (read_heap_smi(nprops, addr, len) != 0 ||
2974
                            read_heap_ptr(&ptr, addr, elems) != 0 ||
2975
                            !V8_IS_HEAPOBJECT(ptr) ||
2976
                            read_typebyte(&t, ptr) != 0 ||
2977
                            t != V8_TYPE_FIXEDARRAY ||
2978
                            read_heap_smi(&nelems, ptr, flen) != 0 ||
2979
                            nelems < *nprops) {
2980
                                findjsobjects_free(fjs->fjs_current);
2981
                                fjs->fjs_current = NULL;
2982
                                continue;
2983
                        }
2984

    
2985
                        strcpy(fjs->fjs_current->fjso_constructor, "Array");
2986
                        stats->fjss_arrays++;
2987
                }
2988

    
2989
                /*
2990
                 * Now determine if we already have an object matching our
2991
                 * properties.  If we don't, we'll add our new object; if we
2992
                 * do we'll merely enqueue our instance.
2993
                 */
2994
                obj = avl_find(&fjs->fjs_tree, fjs->fjs_current, &where);
2995

    
2996
                if (obj == NULL) {
2997
                        avl_add(&fjs->fjs_tree, fjs->fjs_current);
2998
                        fjs->fjs_current->fjso_next = fjs->fjs_objects;
2999
                        fjs->fjs_objects = fjs->fjs_current;
3000
                        fjs->fjs_current = NULL;
3001
                        stats->fjss_uniques++;
3002
                        continue;
3003
                }
3004

    
3005
                findjsobjects_free(fjs->fjs_current);
3006
                fjs->fjs_current = NULL;
3007

    
3008
                inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP);
3009
                inst->fjsi_addr = addr;
3010
                inst->fjsi_next = obj->fjso_instances.fjsi_next;
3011
                obj->fjso_instances.fjsi_next = inst;
3012
                obj->fjso_ninstances++;
3013
        }
3014

    
3015
        mdb_free(range, size);
3016

    
3017
        return (0);
3018
}
3019

    
3020
static int
3021
findjsobjects_mapping(findjsobjects_state_t *fjs, const prmap_t *pmp,
3022
    const char *name)
3023
{
3024
        if (name != NULL && !(fjs->fjs_brk && (pmp->pr_mflags & MA_BREAK)))
3025
                return (0);
3026

    
3027
        if (fjs->fjs_addr != NULL && (fjs->fjs_addr < pmp->pr_vaddr ||
3028
            fjs->fjs_addr >= pmp->pr_vaddr + pmp->pr_size))
3029
                return (0);
3030

    
3031
        return (findjsobjects_range(fjs, pmp->pr_vaddr, pmp->pr_size));
3032
}
3033

    
3034
static void
3035
findjsobjects_references_add(findjsobjects_state_t *fjs, uintptr_t val,
3036
    const char *desc, size_t index)
3037
{
3038
        findjsobjects_referent_t search, *referent;
3039
        findjsobjects_reference_t *reference;
3040

    
3041
        search.fjsr_addr = val;
3042

    
3043
        if ((referent = avl_find(&fjs->fjs_referents, &search, NULL)) == NULL)
3044
                return;
3045

    
3046
        reference = mdb_zalloc(sizeof (*reference), UM_SLEEP | UM_GC);
3047
        reference->fjsrf_addr = fjs->fjs_addr;
3048

    
3049
        if (desc != NULL) {
3050
                reference->fjsrf_desc =
3051
                    mdb_alloc(strlen(desc) + 1, UM_SLEEP | UM_GC);
3052
                (void) strcpy(reference->fjsrf_desc, desc);
3053
        } else {
3054
                reference->fjsrf_index = index;
3055
        }
3056

    
3057
        if (referent->fjsr_head == NULL) {
3058
                referent->fjsr_head = reference;
3059
        } else {
3060
                referent->fjsr_tail->fjsrf_next = reference;
3061
        }
3062

    
3063
        referent->fjsr_tail = reference;
3064
}
3065

    
3066
static int
3067
findjsobjects_references_prop(const char *desc, uintptr_t val, void *arg)
3068
{
3069
        findjsobjects_references_add(arg, val, desc, -1);
3070

    
3071
        return (0);
3072
}
3073

    
3074
static void
3075
findjsobjects_references_array(findjsobjects_state_t *fjs,
3076
    findjsobjects_obj_t *obj)
3077
{
3078
        findjsobjects_instance_t *inst = &obj->fjso_instances;
3079
        uintptr_t *elts;
3080
        size_t i, len;
3081

    
3082
        for (; inst != NULL; inst = inst->fjsi_next) {
3083
                uintptr_t addr = inst->fjsi_addr, ptr;
3084

    
3085
                if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
3086
                    read_heap_array(ptr, &elts, &len, UM_SLEEP) != 0)
3087
                        continue;
3088

    
3089
                fjs->fjs_addr = addr;
3090

    
3091
                for (i = 0; i < len; i++)
3092
                        findjsobjects_references_add(fjs, elts[i], NULL, i);
3093

    
3094
                mdb_free(elts, len * sizeof (uintptr_t));
3095
        }
3096
}
3097

    
3098
static void
3099
findjsobjects_referent(findjsobjects_state_t *fjs, uintptr_t addr)
3100
{
3101
        findjsobjects_referent_t search, *referent;
3102

    
3103
        search.fjsr_addr = addr;
3104

    
3105
        if (avl_find(&fjs->fjs_referents, &search, NULL) != NULL) {
3106
                assert(fjs->fjs_marking);
3107
                mdb_warn("%p is already marked; ignoring\n", addr);
3108
                return;
3109
        }
3110

    
3111
        referent = mdb_zalloc(sizeof (findjsobjects_referent_t), UM_SLEEP);
3112
        referent->fjsr_addr = addr;
3113

    
3114
        avl_add(&fjs->fjs_referents, referent);
3115

    
3116
        if (fjs->fjs_tail != NULL) {
3117
                fjs->fjs_tail->fjsr_next = referent;
3118
        } else {
3119
                fjs->fjs_head = referent;
3120
        }
3121

    
3122
        fjs->fjs_tail = referent;
3123

    
3124
        if (fjs->fjs_marking)
3125
                mdb_printf("findjsobjects: marked %p\n", addr);
3126
}
3127

    
3128
static void
3129
findjsobjects_references(findjsobjects_state_t *fjs)
3130
{
3131
        findjsobjects_reference_t *reference;
3132
        findjsobjects_referent_t *referent;
3133
        avl_tree_t *referents = &fjs->fjs_referents;
3134
        findjsobjects_obj_t *obj;
3135
        void *cookie = NULL;
3136
        uintptr_t addr;
3137

    
3138
        fjs->fjs_referred = B_FALSE;
3139

    
3140
        v8_silent++;
3141

    
3142
        /*
3143
         * First traverse over all objects and arrays, looking for references
3144
         * to our designated referent(s).
3145
         */
3146
        for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
3147
                findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
3148

    
3149
                if (obj->fjso_nprops != 0 && obj->fjso_props == NULL) {
3150
                        findjsobjects_references_array(fjs, obj);
3151
                        continue;
3152
                }
3153

    
3154
                for (inst = head; inst != NULL; inst = inst->fjsi_next) {
3155
                        fjs->fjs_addr = inst->fjsi_addr;
3156

    
3157
                        (void) jsobj_properties(inst->fjsi_addr,
3158
                            findjsobjects_references_prop, fjs);
3159
                }
3160
        }
3161

    
3162
        v8_silent--;
3163
        fjs->fjs_addr = NULL;
3164

    
3165
        /*
3166
         * Now go over our referent(s), reporting any references that we have
3167
         * accumulated.
3168
         */
3169
        for (referent = fjs->fjs_head; referent != NULL;
3170
            referent = referent->fjsr_next) {
3171
                addr = referent->fjsr_addr;
3172

    
3173
                if ((reference = referent->fjsr_head) == NULL) {
3174
                        mdb_printf("%p is not referred to by a "
3175
                            "known object.\n", addr);
3176
                        continue;
3177
                }
3178

    
3179
                for (; reference != NULL; reference = reference->fjsrf_next) {
3180
                        mdb_printf("%p referred to by %p",
3181
                            addr, reference->fjsrf_addr);
3182

    
3183
                        if (reference->fjsrf_desc == NULL) {
3184
                                mdb_printf("[%d]\n", reference->fjsrf_index);
3185
                        } else {
3186
                                mdb_printf(".%s\n", reference->fjsrf_desc);
3187
                        }
3188
                }
3189
        }
3190

    
3191
        /*
3192
         * Finally, destroy our referent nodes.
3193
         */
3194
        while ((referent = avl_destroy_nodes(referents, &cookie)) != NULL)
3195
                mdb_free(referent, sizeof (findjsobjects_referent_t));
3196

    
3197
        fjs->fjs_head = NULL;
3198
        fjs->fjs_tail = NULL;
3199
}
3200

    
3201
static findjsobjects_instance_t *
3202
findjsobjects_instance(findjsobjects_state_t *fjs, uintptr_t addr,
3203
    findjsobjects_instance_t **headp)
3204
{
3205
        findjsobjects_obj_t *obj;
3206

    
3207
        for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
3208
                findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
3209

    
3210
                for (inst = head; inst != NULL; inst = inst->fjsi_next) {
3211
                        if (inst->fjsi_addr == addr) {
3212
                                *headp = head;
3213
                                return (inst);
3214
                        }
3215
                }
3216
        }
3217

    
3218
        return (NULL);
3219
}
3220

    
3221
/*ARGSUSED*/
3222
static void
3223
findjsobjects_match_all(findjsobjects_obj_t *obj, const char *ignored)
3224
{
3225
        mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
3226
}
3227

    
3228
static void
3229
findjsobjects_match_propname(findjsobjects_obj_t *obj, const char *propname)
3230
{
3231
        findjsobjects_prop_t *prop;
3232

    
3233
        for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) {
3234
                if (strcmp(prop->fjsp_desc, propname) == 0) {
3235
                        mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
3236
                        return;
3237
                }
3238
        }
3239
}
3240

    
3241
static void
3242
findjsobjects_match_constructor(findjsobjects_obj_t *obj,
3243
    const char *constructor)
3244
{
3245
        if (strcmp(constructor, obj->fjso_constructor) == 0)
3246
                mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
3247
}
3248

    
3249
static int
3250
findjsobjects_match(findjsobjects_state_t *fjs, uintptr_t addr,
3251
    uint_t flags, void (*func)(findjsobjects_obj_t *, const char *),
3252
    const char *match)
3253
{
3254
        findjsobjects_obj_t *obj;
3255

    
3256
        if (!(flags & DCMD_ADDRSPEC)) {
3257
                for (obj = fjs->fjs_objects; obj != NULL;
3258
                    obj = obj->fjso_next) {
3259
                        if (obj->fjso_malformed && !fjs->fjs_allobjs)
3260
                                continue;
3261

    
3262
                        func(obj, match);
3263
                }
3264

    
3265
                return (DCMD_OK);
3266
        }
3267

    
3268
        /*
3269
         * First, look for the specified address among the representative
3270
         * objects.
3271
         */
3272
        for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
3273
                if (obj->fjso_instances.fjsi_addr == addr) {
3274
                        func(obj, match);
3275
                        return (DCMD_OK);
3276
                }
3277
        }
3278

    
3279
        /*
3280
         * We didn't find it among the representative objects; iterate over
3281
         * all objects.
3282
         */
3283
        for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
3284
                findjsobjects_instance_t *head = &obj->fjso_instances, *inst;
3285

    
3286
                for (inst = head; inst != NULL; inst = inst->fjsi_next) {
3287
                        if (inst->fjsi_addr == addr) {
3288
                                func(obj, match);
3289
                                return (DCMD_OK);
3290
                        }
3291
                }
3292
        }
3293

    
3294
        mdb_warn("%p does not correspond to a known object\n", addr);
3295
        return (DCMD_ERR);
3296
}
3297

    
3298
static void
3299
findjsobjects_print(findjsobjects_obj_t *obj)
3300
{
3301
        int col = 19 + (sizeof (uintptr_t) * 2) + strlen("..."), len;
3302
        uintptr_t addr = obj->fjso_instances.fjsi_addr;
3303
        findjsobjects_prop_t *prop;
3304

    
3305
        mdb_printf("%?p %8d %8d ",
3306
            addr, obj->fjso_ninstances, obj->fjso_nprops);
3307

    
3308
        if (obj->fjso_constructor[0] != '\0') {
3309
                mdb_printf("%s%s", obj->fjso_constructor,
3310
                    obj->fjso_props != NULL ? ": " : "");
3311
                col += strlen(obj->fjso_constructor) + 2;
3312
        }
3313

    
3314
        for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) {
3315
                if (col + (len = strlen(prop->fjsp_desc) + 2) < 80) {
3316
                        mdb_printf("%s%s", prop->fjsp_desc,
3317
                            prop->fjsp_next != NULL ? ", " : "");
3318
                        col += len;
3319
                } else {
3320
                        mdb_printf("...");
3321
                        break;
3322
                }
3323
        }
3324

    
3325
        mdb_printf("\n", col);
3326
}
3327

    
3328
static void
3329
dcmd_findjsobjects_help(void)
3330
{
3331
        mdb_printf("%s\n\n",
3332
"Finds all JavaScript objects in the V8 heap via brute force iteration over\n"
3333
"all mapped anonymous memory.  (This can take up to several minutes on large\n"
3334
"dumps.)  The output consists of representative objects, the number of\n"
3335
"instances of that object and the number of properties on the object --\n"
3336
"followed by the constructor and first few properties of the objects.  Once\n"
3337
"run, subsequent calls to ::findjsobjects use cached data.  If provided an\n"
3338
"address (and in the absence of -r, described below), ::findjsobjects treats\n"
3339
"the address as that of a representative object, and lists all instances of\n"
3340
"that object (that is, all objects that have a matching property signature).");
3341

    
3342
        mdb_dec_indent(2);
3343
        mdb_printf("%<b>OPTIONS%</b>\n");
3344
        mdb_inc_indent(2);
3345

    
3346
        mdb_printf("%s\n",
3347
"  -b       Include the heap denoted by the brk(2) (normally excluded)\n"
3348
"  -c cons  Display representative objects with the specified constructor\n"
3349
"  -p prop  Display representative objects that have the specified property\n"
3350
"  -l       List all objects that match the representative object\n"
3351
"  -m       Mark specified object for later reference determination via -r\n"
3352
"  -r       Find references to the specified and/or marked object(s)\n"
3353
"  -v       Provide verbose statistics\n");
3354
}
3355

    
3356
static int
3357
dcmd_findjsobjects(uintptr_t addr,
3358
    uint_t flags, int argc, const mdb_arg_t *argv)
3359
{
3360
        static findjsobjects_state_t fjs;
3361
        static findjsobjects_stats_t *stats = &fjs.fjs_stats;
3362
        findjsobjects_obj_t *obj;
3363
        struct ps_prochandle *Pr;
3364
        boolean_t references = B_FALSE, listlike = B_FALSE;
3365
        const char *propname = NULL;
3366
        const char *constructor = NULL;
3367

    
3368
        fjs.fjs_verbose = B_FALSE;
3369
        fjs.fjs_brk = B_FALSE;
3370
        fjs.fjs_marking = B_FALSE;
3371
        fjs.fjs_allobjs = B_FALSE;
3372

    
3373
        if (mdb_getopts(argc, argv,
3374
            'a', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_allobjs,
3375
            'b', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_brk,
3376
            'c', MDB_OPT_STR, &constructor,
3377
            'l', MDB_OPT_SETBITS, B_TRUE, &listlike,
3378
            'm', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_marking,
3379
            'p', MDB_OPT_STR, &propname,
3380
            'r', MDB_OPT_SETBITS, B_TRUE, &references,
3381
            'v', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_verbose,
3382
            NULL) != argc)
3383
                return (DCMD_USAGE);
3384

    
3385
        if (!fjs.fjs_initialized) {
3386
                avl_create(&fjs.fjs_tree,
3387
                    (int(*)(const void *, const void *))findjsobjects_cmp,
3388
                    sizeof (findjsobjects_obj_t),
3389
                    offsetof(findjsobjects_obj_t, fjso_node));
3390

    
3391
                avl_create(&fjs.fjs_referents,
3392
                    (int(*)(const void *, const void *))
3393
                    findjsobjects_cmp_referents,
3394
                    sizeof (findjsobjects_referent_t),
3395
                    offsetof(findjsobjects_referent_t, fjsr_node));
3396

    
3397
                fjs.fjs_initialized = B_TRUE;
3398
        }
3399

    
3400
        if (avl_is_empty(&fjs.fjs_tree)) {
3401
                findjsobjects_obj_t **sorted;
3402
                int nobjs, i;
3403
                hrtime_t start = gethrtime();
3404

    
3405
                if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) {
3406
                        mdb_warn("couldn't read pshandle xdata");
3407
                        return (DCMD_ERR);
3408
                }
3409

    
3410
                v8_silent++;
3411

    
3412
                if (Pmapping_iter(Pr,
3413
                    (proc_map_f *)findjsobjects_mapping, &fjs) != 0) {
3414
                        v8_silent--;
3415
                        return (DCMD_ERR);
3416
                }
3417

    
3418
                if ((nobjs = avl_numnodes(&fjs.fjs_tree)) != 0) {
3419
                        /*
3420
                         * We have the objects -- now sort them.
3421
                         */
3422
                        sorted = mdb_alloc(nobjs * sizeof (void *),
3423
                            UM_SLEEP | UM_GC);
3424

    
3425
                        for (obj = fjs.fjs_objects, i = 0; obj != NULL;
3426
                            obj = obj->fjso_next, i++) {
3427
                                sorted[i] = obj;
3428
                        }
3429

    
3430
                        qsort(sorted, avl_numnodes(&fjs.fjs_tree),
3431
                            sizeof (void *), findjsobjects_cmp_ninstances);
3432

    
3433
                        for (i = 1, fjs.fjs_objects = sorted[0]; i < nobjs; i++)
3434
                                sorted[i - 1]->fjso_next = sorted[i];
3435

    
3436
                        sorted[nobjs - 1]->fjso_next = NULL;
3437
                }
3438

    
3439
                v8_silent--;
3440

    
3441
                if (fjs.fjs_verbose) {
3442
                        const char *f = "findjsobjects: %30s => %d\n";
3443
                        int elapsed = (int)((gethrtime() - start) / NANOSEC);
3444

    
3445
                        mdb_printf(f, "elapsed time (seconds)", elapsed);
3446
                        mdb_printf(f, "heap objects", stats->fjss_heapobjs);
3447
                        mdb_printf(f, "type reads", stats->fjss_typereads);
3448
                        mdb_printf(f, "cached reads", stats->fjss_cached);
3449
                        mdb_printf(f, "JavaScript objects", stats->fjss_jsobjs);
3450
                        mdb_printf(f, "processed objects", stats->fjss_objects);
3451
                        mdb_printf(f, "processed arrays", stats->fjss_arrays);
3452
                        mdb_printf(f, "unique objects", stats->fjss_uniques);
3453
                }
3454
        }
3455

    
3456
        if (listlike && !(flags & DCMD_ADDRSPEC)) {
3457
                if (propname != NULL || constructor != NULL) {
3458
                        char opt = propname != NULL ? 'p' : 'c';
3459

    
3460
                        mdb_warn("cannot specify -l with -%c; instead, pipe "
3461
                            "output of ::findjsobjects -%c to "
3462
                            "::findjsobjects -l\n", opt, opt);
3463
                        return (DCMD_ERR);
3464
                }
3465

    
3466
                return (findjsobjects_match(&fjs, addr, flags,
3467
                    findjsobjects_match_all, NULL));
3468
        }
3469

    
3470
        if (propname != NULL) {
3471
                if (constructor != NULL) {
3472
                        mdb_warn("cannot specify both a property name "
3473
                            "and a constructor\n");
3474
                        return (DCMD_ERR);
3475
                }
3476

    
3477
                return (findjsobjects_match(&fjs, addr, flags,
3478
                    findjsobjects_match_propname, propname));
3479
        }
3480

    
3481
        if (constructor != NULL) {
3482
                return (findjsobjects_match(&fjs, addr, flags,
3483
                    findjsobjects_match_constructor, constructor));
3484
        }
3485

    
3486
        if (references && !(flags & DCMD_ADDRSPEC) &&
3487
            avl_is_empty(&fjs.fjs_referents)) {
3488
                mdb_warn("must specify or mark an object to find references\n");
3489
                return (DCMD_ERR);
3490
        }
3491

    
3492
        if (fjs.fjs_marking && !(flags & DCMD_ADDRSPEC)) {
3493
                mdb_warn("must specify an object to mark\n");
3494
                return (DCMD_ERR);
3495
        }
3496

    
3497
        if (references && fjs.fjs_marking) {
3498
                mdb_warn("can't both mark an object and find its references\n");
3499
                return (DCMD_ERR);
3500
        }
3501

    
3502
        if (flags & DCMD_ADDRSPEC) {
3503
                findjsobjects_instance_t *inst, *head;
3504

    
3505
                /*
3506
                 * If we've been passed an address, it's to either list like
3507
                 * objects (-l), mark an object (-m) or find references to the
3508
                 * specified/marked objects (-r).  (Note that the absence of
3509
                 * any of these options implies -l.)
3510
                 */
3511
                inst = findjsobjects_instance(&fjs, addr, &head);
3512

    
3513
                if (inst == NULL) {
3514
                        mdb_warn("%p is not a valid object\n", addr);
3515
                        return (DCMD_ERR);
3516
                }
3517

    
3518
                if (!references && !fjs.fjs_marking) {
3519
                        for (inst = head; inst != NULL; inst = inst->fjsi_next)
3520
                                mdb_printf("%p\n", inst->fjsi_addr);
3521

    
3522
                        return (DCMD_OK);
3523
                }
3524

    
3525
                if (!listlike) {
3526
                        findjsobjects_referent(&fjs, inst->fjsi_addr);
3527
                } else {
3528
                        for (inst = head; inst != NULL; inst = inst->fjsi_next)
3529
                                findjsobjects_referent(&fjs, inst->fjsi_addr);
3530
                }
3531
        }
3532

    
3533
        if (references)
3534
                findjsobjects_references(&fjs);
3535

    
3536
        if (references || fjs.fjs_marking)
3537
                return (DCMD_OK);
3538

    
3539
        mdb_printf("%?s %8s %8s %s\n", "OBJECT",
3540
            "#OBJECTS", "#PROPS", "CONSTRUCTOR: PROPS");
3541

    
3542
        for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
3543
                if (obj->fjso_malformed && !fjs.fjs_allobjs)
3544
                        continue;
3545

    
3546
                findjsobjects_print(obj);
3547
        }
3548

    
3549
        return (DCMD_OK);
3550
}
3551

    
3552
/* ARGSUSED */
3553
static int
3554
dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3555
{
3556
        uintptr_t fptr, raddr;
3557
        boolean_t opt_v = B_FALSE, opt_i = B_FALSE;
3558
        char *opt_f = NULL, *opt_p = NULL;
3559

    
3560
        if (mdb_getopts(argc, argv,
3561
            'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
3562
            'i', MDB_OPT_SETBITS, B_TRUE, &opt_i,
3563
            'f', MDB_OPT_STR, &opt_f,
3564
            'p', MDB_OPT_STR, &opt_p, NULL) != argc)
3565
                return (DCMD_USAGE);
3566

    
3567
        /*
3568
         * As with $C, we assume we are given a *pointer* to the frame pointer
3569
         * for a frame, rather than the actual frame pointer for the frame of
3570
         * interest. This is needed to show the instruction pointer, which is
3571
         * actually stored with the next frame.  For debugging, this can be
3572
         * overridden with the "-i" option (for "immediate").
3573
         */
3574
        if (opt_i)
3575
                return (do_jsframe(addr, 0, opt_v, opt_f, opt_p));
3576

    
3577
        if (mdb_vread(&raddr, sizeof (raddr),
3578
            addr + sizeof (uintptr_t)) == -1) {
3579
                mdb_warn("failed to read return address from %p",
3580
                    addr + sizeof (uintptr_t));
3581
                return (DCMD_ERR);
3582
        }
3583

    
3584
        if (mdb_vread(&fptr, sizeof (fptr), addr) == -1) {
3585
                mdb_warn("failed to read frame pointer from %p", addr);
3586
                return (DCMD_ERR);
3587
        }
3588

    
3589
        if (fptr == NULL)
3590
                return (DCMD_OK);
3591

    
3592
        return (do_jsframe(fptr, raddr, opt_v, opt_f, opt_p));
3593
}
3594

    
3595
/* ARGSUSED */
3596
static int
3597
dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3598
{
3599
        char *buf, *bufp;
3600
        size_t bufsz = 262144, len = bufsz;
3601
        jsobj_print_t jsop;
3602
        boolean_t opt_b = B_FALSE;
3603
        int rv, i;
3604

    
3605
        bzero(&jsop, sizeof (jsop));
3606
        jsop.jsop_depth = 2;
3607
        jsop.jsop_printaddr = B_FALSE;
3608

    
3609
        i = mdb_getopts(argc, argv,
3610
            'a', MDB_OPT_SETBITS, B_TRUE, &jsop.jsop_printaddr,
3611
            'b', MDB_OPT_SETBITS, B_TRUE, &opt_b,
3612
            'd', MDB_OPT_UINT64, &jsop.jsop_depth, NULL);
3613

    
3614
        if (opt_b)
3615
                jsop.jsop_baseaddr = addr;
3616

    
3617
        do {
3618
                if (i != argc) {
3619
                        const mdb_arg_t *member = &argv[i++];
3620

    
3621
                        if (member->a_type != MDB_TYPE_STRING)
3622
                                return (DCMD_USAGE);
3623

    
3624
                        jsop.jsop_member = member->a_un.a_str;
3625
                }
3626

    
3627
                for (;;) {
3628
                        if ((buf = bufp =
3629
                            mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL)
3630
                                return (DCMD_ERR);
3631

    
3632
                        jsop.jsop_bufp = &bufp;
3633
                        jsop.jsop_lenp = &len;
3634

    
3635
                        rv = jsobj_print(addr, &jsop);
3636

    
3637
                        if (len > 0)
3638
                                break;
3639

    
3640
                        mdb_free(buf, bufsz);
3641
                        bufsz <<= 1;
3642
                        len = bufsz;
3643
                }
3644

    
3645
                if (jsop.jsop_member == NULL && rv != 0)
3646
                        return (DCMD_ERR);
3647

    
3648
                if (jsop.jsop_member && !jsop.jsop_found) {
3649
                        if (jsop.jsop_baseaddr)
3650
                                (void) mdb_printf("%p: ", jsop.jsop_baseaddr);
3651

    
3652
                        (void) mdb_printf("undefined%s",
3653
                            i < argc ? " " : "");
3654
                } else {
3655
                        (void) mdb_printf("%s%s", buf, i < argc &&
3656
                            !isspace(buf[strlen(buf) - 1]) ? " " : "");
3657
                }
3658

    
3659
                mdb_free(buf, bufsz);
3660
                jsop.jsop_found = B_FALSE;
3661
                jsop.jsop_baseaddr = NULL;
3662
        } while (i < argc);
3663

    
3664
        mdb_printf("\n");
3665

    
3666
        return (DCMD_OK);
3667
}
3668

    
3669
/* ARGSUSED */
3670
static int
3671
dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3672
{
3673
        v8_class_t *clp;
3674
        v8_field_t *flp;
3675
        const char *klass, *field;
3676
        uintptr_t offset = 0;
3677

    
3678
        /*
3679
         * We may be invoked with either two arguments (class and field name) or
3680
         * three (an offset to save).
3681
         */
3682
        if (argc != 2 && argc != 3)
3683
                return (DCMD_USAGE);
3684

    
3685
        if (argv[0].a_type != MDB_TYPE_STRING ||
3686
            argv[1].a_type != MDB_TYPE_STRING)
3687
                return (DCMD_USAGE);
3688

    
3689
        klass = argv[0].a_un.a_str;
3690
        field = argv[1].a_un.a_str;
3691

    
3692
        if (argc == 3) {
3693
                if (argv[2].a_type != MDB_TYPE_STRING)
3694
                        return (DCMD_USAGE);
3695

    
3696
                offset = mdb_strtoull(argv[2].a_un.a_str);
3697
        }
3698

    
3699
        for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
3700
                if (strcmp(clp->v8c_name, klass) == 0)
3701
                        break;
3702

    
3703
        if (clp == NULL) {
3704
                (void) mdb_printf("error: no such class: \"%s\"", klass);
3705
                return (DCMD_ERR);
3706
        }
3707

    
3708
        for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next)
3709
                if (strcmp(field, flp->v8f_name) == 0)
3710
                        break;
3711

    
3712
        if (flp == NULL) {
3713
                if (argc == 2) {
3714
                        mdb_printf("error: no such field in class \"%s\": "
3715
                            "\"%s\"", klass, field);
3716
                        return (DCMD_ERR);
3717
                }
3718

    
3719
                flp = conf_field_create(clp, field, offset);
3720
                if (flp == NULL) {
3721
                        mdb_warn("failed to create field");
3722
                        return (DCMD_ERR);
3723
                }
3724
        } else if (argc == 3) {
3725
                flp->v8f_offset = offset;
3726
        }
3727

    
3728
        mdb_printf("%s::%s at offset 0x%x\n", klass, field, flp->v8f_offset);
3729
        return (DCMD_OK);
3730
}
3731

    
3732
/* ARGSUSED */
3733
static int
3734
dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3735
{
3736
        uint8_t type;
3737
        uintptr_t *array;
3738
        size_t ii, len;
3739

    
3740
        if (read_typebyte(&type, addr) != 0)
3741
                return (DCMD_ERR);
3742

    
3743
        if (type != V8_TYPE_FIXEDARRAY) {
3744
                mdb_warn("%p is not an instance of FixedArray\n", addr);
3745
                return (DCMD_ERR);
3746
        }
3747

    
3748
        if (read_heap_array(addr, &array, &len, UM_SLEEP | UM_GC) != 0)
3749
                return (DCMD_ERR);
3750

    
3751
        for (ii = 0; ii < len; ii++)
3752
                mdb_printf("%p\n", array[ii]);
3753

    
3754
        return (DCMD_OK);
3755
}
3756

    
3757
/* ARGSUSED */
3758
static int
3759
dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3760
{
3761
        uintptr_t raddr;
3762
        boolean_t opt_v;
3763
        char *opt_f = NULL, *opt_p = NULL;
3764

    
3765
        if (mdb_getopts(argc, argv,
3766
            'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
3767
            'f', MDB_OPT_STR, &opt_f,
3768
            'p', MDB_OPT_STR, &opt_p,
3769
            NULL) != argc)
3770
                return (DCMD_USAGE);
3771

    
3772
        /*
3773
         * The "::jsframe" walker iterates the valid frame pointers, but the
3774
         * "::jsframe" dcmd looks at the frame after the one it was given, so we
3775
         * have to explicitly examine the top frame here.
3776
         */
3777
        if (!(flags & DCMD_ADDRSPEC)) {
3778
                if (load_current_context(&addr, &raddr) != 0 ||
3779
                    do_jsframe(addr, raddr, opt_v, opt_f, opt_p) != 0)
3780
                        return (DCMD_ERR);
3781
        }
3782

    
3783
        if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1)
3784
                return (DCMD_ERR);
3785

    
3786
        return (DCMD_OK);
3787
}
3788

    
3789
/* ARGSUSED */
3790
static int
3791
dcmd_v8str(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3792
{
3793
        boolean_t opt_v = B_FALSE;
3794
        char buf[512 * 1024];
3795
        char *bufp;
3796
        size_t len;
3797

    
3798
        if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
3799
            NULL) != argc)
3800
                return (DCMD_USAGE);
3801

    
3802
        bufp = buf;
3803
        len = sizeof (buf);
3804
        if (jsstr_print(addr, (opt_v ? JSSTR_VERBOSE : JSSTR_NONE) |
3805
            JSSTR_QUOTED, &bufp, &len) != 0)
3806
                return (DCMD_ERR);
3807

    
3808
        mdb_printf("%s\n", buf);
3809
        return (DCMD_OK);
3810
}
3811

    
3812
static void
3813
dcmd_v8load_help(void)
3814
{
3815
        v8_cfg_t *cfp, **cfgpp;
3816

    
3817
        mdb_printf(
3818
            "To traverse in-memory V8 structures, the V8 dmod requires\n"
3819
            "configuration that describes the layout of various V8 structures\n"
3820
            "in memory.  Normally, this information is pulled from metadata\n"
3821
            "in the target binary.  However, it's possible to use the module\n"
3822
            "with a binary not built with metadata by loading one of the\n"
3823
            "canned configurations.\n\n");
3824

    
3825
        mdb_printf("Available configurations:\n");
3826

    
3827
        (void) mdb_inc_indent(4);
3828

    
3829
        for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
3830
                cfp = *cfgpp;
3831
                mdb_printf("%-10s    %s\n", cfp->v8cfg_name, cfp->v8cfg_label);
3832
        }
3833

    
3834
        (void) mdb_dec_indent(4);
3835
}
3836

    
3837
/* ARGSUSED */
3838
static int
3839
dcmd_v8load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3840
{
3841
        v8_cfg_t *cfgp = NULL, **cfgpp;
3842

    
3843
        if (v8_classes != NULL) {
3844
                mdb_warn("v8 module already configured\n");
3845
                return (DCMD_ERR);
3846
        }
3847

    
3848
        if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
3849
                return (DCMD_USAGE);
3850

    
3851
        for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
3852
                cfgp = *cfgpp;
3853
                if (strcmp(argv->a_un.a_str, cfgp->v8cfg_name) == 0)
3854
                        break;
3855
        }
3856

    
3857
        if (cfgp == NULL || cfgp->v8cfg_name == NULL) {
3858
                mdb_warn("unknown configuration: \"%s\"\n", argv->a_un.a_str);
3859
                return (DCMD_ERR);
3860
        }
3861

    
3862
        if (autoconfigure(cfgp) == -1) {
3863
                mdb_warn("autoconfigure failed\n");
3864
                return (DCMD_ERR);
3865
        }
3866

    
3867
        mdb_printf("V8 dmod configured based on %s\n", cfgp->v8cfg_name);
3868
        return (DCMD_OK);
3869
}
3870

    
3871
static int
3872
walk_jsframes_init(mdb_walk_state_t *wsp)
3873
{
3874
        if (wsp->walk_addr != NULL)
3875
                return (WALK_NEXT);
3876

    
3877
        if (load_current_context(&wsp->walk_addr, NULL) != 0)
3878
                return (WALK_ERR);
3879

    
3880
        return (WALK_NEXT);
3881
}
3882

    
3883
static int
3884
walk_jsframes_step(mdb_walk_state_t *wsp)
3885
{
3886
        uintptr_t addr, next;
3887
        int rv;
3888

    
3889
        addr = wsp->walk_addr;
3890
        rv = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
3891

    
3892
        if (rv != WALK_NEXT)
3893
                return (rv);
3894

    
3895
        if (mdb_vread(&next, sizeof (next), addr) == -1)
3896
                return (WALK_ERR);
3897

    
3898
        if (next == NULL)
3899
                return (WALK_DONE);
3900

    
3901
        wsp->walk_addr = next;
3902
        return (WALK_NEXT);
3903
}
3904

    
3905
typedef struct jsprop_walk_data {
3906
        int jspw_nprops;
3907
        int jspw_current;
3908
        uintptr_t *jspw_props;
3909
} jsprop_walk_data_t;
3910

    
3911
/*ARGSUSED*/
3912
static int
3913
walk_jsprop_nprops(const char *desc, uintptr_t val, void *arg)
3914
{
3915
        jsprop_walk_data_t *jspw = arg;
3916
        jspw->jspw_nprops++;
3917

    
3918
        return (0);
3919
}
3920

    
3921
/*ARGSUSED*/
3922
static int
3923
walk_jsprop_props(const char *desc, uintptr_t val, void *arg)
3924
{
3925
        jsprop_walk_data_t *jspw = arg;
3926
        jspw->jspw_props[jspw->jspw_current++] = val;
3927

    
3928
        return (0);
3929
}
3930

    
3931
static int
3932
walk_jsprop_init(mdb_walk_state_t *wsp)
3933
{
3934
        jsprop_walk_data_t *jspw;
3935
        uintptr_t addr;
3936
        uint8_t type;
3937

    
3938
        if ((addr = wsp->walk_addr) == NULL) {
3939
                mdb_warn("'jsprop' does not support global walks\n");
3940
                return (WALK_ERR);
3941
        }
3942

    
3943
        if (!V8_IS_HEAPOBJECT(addr) || read_typebyte(&type, addr) != 0 ||
3944
            type != V8_TYPE_JSOBJECT) {
3945
                mdb_warn("%p is not a JSObject\n", addr);
3946
                return (WALK_ERR);
3947
        }
3948

    
3949
        jspw = mdb_zalloc(sizeof (jsprop_walk_data_t), UM_SLEEP | UM_GC);
3950

    
3951
        if (jsobj_properties(addr, walk_jsprop_nprops, jspw) == -1) {
3952
                mdb_warn("couldn't iterate over properties for %p\n", addr);
3953
                return (WALK_ERR);
3954
        }
3955

    
3956
        jspw->jspw_props = mdb_zalloc(jspw->jspw_nprops *
3957
            sizeof (uintptr_t), UM_SLEEP | UM_GC);
3958

    
3959
        if (jsobj_properties(addr, walk_jsprop_props, jspw) == -1) {
3960
                mdb_warn("couldn't iterate over properties for %p\n", addr);
3961
                return (WALK_ERR);
3962
        }
3963

    
3964
        jspw->jspw_current = 0;
3965
        wsp->walk_data = jspw;
3966

    
3967
        return (WALK_NEXT);
3968
}
3969

    
3970
static int
3971
walk_jsprop_step(mdb_walk_state_t *wsp)
3972
{
3973
        jsprop_walk_data_t *jspw = wsp->walk_data;
3974
        int rv;
3975

    
3976
        if (jspw->jspw_current >= jspw->jspw_nprops)
3977
                return (WALK_DONE);
3978

    
3979
        if ((rv = wsp->walk_callback(jspw->jspw_props[jspw->jspw_current++],
3980
            NULL, wsp->walk_cbdata)) != WALK_NEXT)
3981
                return (rv);
3982

    
3983
        return (WALK_NEXT);
3984
}
3985

    
3986
/*
3987
 * MDB linkage
3988
 */
3989

    
3990
static const mdb_dcmd_t v8_mdb_dcmds[] = {
3991
        /*
3992
         * Commands to inspect JavaScript-level state
3993
         */
3994
        { "jsframe", ":[-iv] [-f function] [-p property]",
3995
                "summarize a JavaScript stack frame", dcmd_jsframe },
3996
        { "jsprint", ":[-ab] [-d depth] [member]", "print a JavaScript object",
3997
                dcmd_jsprint },
3998
        { "jsstack", "[-v] [-f function] [-p property]",
3999
                "print a JavaScript stacktrace", dcmd_jsstack },
4000
        { "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript "
4001
                "objects", dcmd_findjsobjects, dcmd_findjsobjects_help },
4002

    
4003
        /*
4004
         * Commands to inspect V8-level state
4005
         */
4006
        { "v8array", ":", "print elements of a V8 FixedArray",
4007
                dcmd_v8array },
4008
        { "v8classes", NULL, "list known V8 heap object C++ classes",
4009
                dcmd_v8classes },
4010
        { "v8code", ":[-d]", "print information about a V8 Code object",
4011
                dcmd_v8code },
4012
        { "v8field", "classname fieldname offset",
4013
                "manually add a field to a given class", dcmd_v8field },
4014
        { "v8function", ":[-d]", "print JSFunction object details",
4015
                dcmd_v8function },
4016
        { "v8load", "version", "load canned config for a specific V8 version",
4017
                dcmd_v8load, dcmd_v8load_help },
4018
        { "v8frametypes", NULL, "list known V8 frame types",
4019
                dcmd_v8frametypes },
4020
        { "v8print", ":[class]", "print a V8 heap object",
4021
                dcmd_v8print, dcmd_v8print_help },
4022
        { "v8str", ":[-v]", "print the contents of a V8 string",
4023
                dcmd_v8str },
4024
        { "v8type", ":", "print the type of a V8 heap object",
4025
                dcmd_v8type },
4026
        { "v8types", NULL, "list known V8 heap object types",
4027
                dcmd_v8types },
4028

    
4029
        { NULL }
4030
};
4031

    
4032
static const mdb_walker_t v8_mdb_walkers[] = {
4033
        { "jsframe", "walk V8 JavaScript stack frames",
4034
                walk_jsframes_init, walk_jsframes_step },
4035
        { "jsprop", "walk property values for an object",
4036
                walk_jsprop_init, walk_jsprop_step },
4037
        { NULL }
4038
};
4039

    
4040
static mdb_modinfo_t v8_mdb = { MDB_API_VERSION, v8_mdb_dcmds, v8_mdb_walkers };
4041

    
4042
static void
4043
configure(void)
4044
{
4045
        char *success;
4046
        v8_cfg_t *cfgp = NULL;
4047
        GElf_Sym sym;
4048

    
4049
        if (mdb_readsym(&v8_major, sizeof (v8_major),
4050
            "_ZN2v88internal7Version6major_E") == -1 ||
4051
            mdb_readsym(&v8_minor, sizeof (v8_minor),
4052
            "_ZN2v88internal7Version6minor_E") == -1 ||
4053
            mdb_readsym(&v8_build, sizeof (v8_build),
4054
            "_ZN2v88internal7Version6build_E") == -1 ||
4055
            mdb_readsym(&v8_patch, sizeof (v8_patch),
4056
            "_ZN2v88internal7Version6patch_E") == -1) {
4057
                mdb_warn("failed to determine V8 version");
4058
                return;
4059
        }
4060

    
4061
        mdb_printf("V8 version: %d.%d.%d.%d\n",
4062
            v8_major, v8_minor, v8_build, v8_patch);
4063

    
4064
        /*
4065
         * First look for debug metadata embedded within the binary, which may
4066
         * be present in recent V8 versions built with postmortem metadata.
4067
         */
4068
        if (mdb_lookup_by_name("v8dbg_SmiTag", &sym) == 0) {
4069
                cfgp = &v8_cfg_target;
4070
                success = "Autoconfigured V8 support from target";
4071
        } else if (v8_major == 3 && v8_minor == 1 && v8_build == 8) {
4072
                cfgp = &v8_cfg_04;
4073
                success = "Configured V8 support based on node v0.4";
4074
        } else if (v8_major == 3 && v8_minor == 6 && v8_build == 6) {
4075
                cfgp = &v8_cfg_06;
4076
                success = "Configured V8 support based on node v0.6";
4077
        } else {
4078
                mdb_printf("mdb_v8: target has no debug metadata and "
4079
                    "no existing config found\n");
4080
                return;
4081
        }
4082

    
4083
        if (autoconfigure(cfgp) != 0) {
4084
                mdb_warn("failed to autoconfigure from target; "
4085
                    "commands may have incorrect results!\n");
4086
                return;
4087
        }
4088

    
4089
        mdb_printf("%s\n", success);
4090
}
4091

    
4092
static void
4093
enable_demangling(void)
4094
{
4095
        const char *symname = "_ZN2v88internal7Version6major_E";
4096
        GElf_Sym sym;
4097
        char buf[64];
4098

    
4099
        /*
4100
         * Try to determine whether C++ symbol demangling has been enabled.  If
4101
         * not, enable it.
4102
         */
4103
        if (mdb_lookup_by_name("_ZN2v88internal7Version6major_E", &sym) != 0)
4104
                return;
4105

    
4106
        (void) mdb_snprintf(buf, sizeof (buf), "%a", sym.st_value);
4107
        if (strstr(buf, symname) != NULL)
4108
                (void) mdb_eval("$G");
4109
}
4110

    
4111
const mdb_modinfo_t *
4112
_mdb_init(void)
4113
{
4114
        configure();
4115
        enable_demangling();
4116
        return (&v8_mdb);
4117
}