Revision e851fef6

View differences:

configure
475 475
    o['variables']['node_use_dtrace'] = 'false'
476 476
    o['variables']['node_use_systemtap'] = 'false'
477 477

  
478
  # if we're on illumos based systems wrap the helper library into the
479
  # executable
480
  if flavor == 'solaris':
481
    o['variables']['node_use_mdb'] = 'true'
482
  else:
483
    o['variables']['node_use_mdb'] = 'false'
484

  
478 485
  if options.no_ifaddrs:
479 486
    o['defines'] += ['SUNOS_NO_IFADDRS']
480 487

  
deps/mdb_v8/mdb_v8.c
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];
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff