1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  23  */
  24 
  25 /*
  26  * mdb(1M) module for debugging the V8 JavaScript engine.  This implementation
  27  * makes heavy use of metadata defined in the V8 binary for inspecting in-memory
  28  * structures.  Canned configurations can be manually loaded for V8 binaries
  29  * that predate this metadata.  See mdb_v8_cfg.c for details.
  30  */
  31 
  32 #include <sys/mdb_modapi.h>
  33 #include <assert.h>
  34 #include <ctype.h>
  35 #include <stdarg.h>
  36 #include <stdio.h>
  37 #include <string.h>
  38 #include <libproc.h>
  39 #include <sys/avl.h>
  40 
  41 #include "v8dbg.h"
  42 #include "v8cfg.h"
  43 
  44 #define offsetof(s, m)  ((size_t)(&(((s *)0)->m)))
  45 
  46 /*
  47  * The "v8_class" and "v8_field" structures describe the C++ classes used to
  48  * represent V8 heap objects.
  49  */
  50 typedef struct v8_class {
  51         struct v8_class *v8c_next;      /* list linkage */
  52         struct v8_class *v8c_parent;    /* parent class (inheritance) */
  53         struct v8_field *v8c_fields;    /* array of class fields */
  54         size_t          v8c_start;      /* offset of first class field */
  55         size_t          v8c_end;        /* offset of first subclass field */
  56         char            v8c_name[64];   /* heap object class name */
  57 } v8_class_t;
  58 
  59 typedef struct v8_field {
  60         struct v8_field *v8f_next;      /* list linkage */
  61         ssize_t         v8f_offset;     /* field offset */
  62         char            v8f_name[64];   /* field name */
  63         boolean_t       v8f_isbyte;     /* 1-byte int field */
  64 } v8_field_t;
  65 
  66 /*
  67  * Similarly, the "v8_enum" structure describes an enum from V8.
  68  */
  69 typedef struct {
  70         char    v8e_name[64];
  71         uint_t  v8e_value;
  72 } v8_enum_t;
  73 
  74 /*
  75  * During configuration, the dmod updates these globals with the actual set of
  76  * classes, types, and frame types based on the debug metadata.
  77  */
  78 static v8_class_t       *v8_classes;
  79 
  80 static v8_enum_t        v8_types[128];
  81 static int              v8_next_type;
  82 
  83 static v8_enum_t        v8_frametypes[16];
  84 static int              v8_next_frametype;
  85 
  86 static int              v8_silent;
  87 
  88 /*
  89  * The following constants describe offsets from the frame pointer that are used
  90  * to inspect each stack frame.  They're initialized from the debug metadata.
  91  */
  92 static ssize_t  V8_OFF_FP_CONTEXT;
  93 static ssize_t  V8_OFF_FP_MARKER;
  94 static ssize_t  V8_OFF_FP_FUNCTION;
  95 static ssize_t  V8_OFF_FP_ARGS;
  96 
  97 /*
  98  * The following constants are used by macros defined in heap-dbg-common.h to
  99  * examine the types of various V8 heap objects.  In general, the macros should
 100  * be preferred to using the constants directly.  The values of these constants
 101  * are initialized from the debug metadata.
 102  */
 103 static intptr_t V8_FirstNonstringType;
 104 static intptr_t V8_IsNotStringMask;
 105 static intptr_t V8_StringTag;
 106 static intptr_t V8_NotStringTag;
 107 static intptr_t V8_StringEncodingMask;
 108 static intptr_t V8_TwoByteStringTag;
 109 static intptr_t V8_AsciiStringTag;
 110 static intptr_t V8_StringRepresentationMask;
 111 static intptr_t V8_SeqStringTag;
 112 static intptr_t V8_ConsStringTag;
 113 static intptr_t V8_ExternalStringTag;
 114 static intptr_t V8_FailureTag;
 115 static intptr_t V8_FailureTagMask;
 116 static intptr_t V8_HeapObjectTag;
 117 static intptr_t V8_HeapObjectTagMask;
 118 static intptr_t V8_SmiTag;
 119 static intptr_t V8_SmiTagMask;
 120 static intptr_t V8_SmiValueShift;
 121 static intptr_t V8_PointerSizeLog2;
 122 
 123 static intptr_t V8_PROP_IDX_CONTENT;
 124 static intptr_t V8_PROP_IDX_FIRST;
 125 static intptr_t V8_PROP_TYPE_FIELD;
 126 static intptr_t V8_PROP_FIRST_PHANTOM;
 127 static intptr_t V8_PROP_TYPE_MASK;
 128 static intptr_t V8_PROP_DESC_KEY;
 129 static intptr_t V8_PROP_DESC_DETAILS;
 130 static intptr_t V8_PROP_DESC_VALUE;
 131 static intptr_t V8_PROP_DESC_SIZE;
 132 static intptr_t V8_TRANSITIONS_IDX_DESC;
 133 
 134 static intptr_t V8_TYPE_JSOBJECT = -1;
 135 static intptr_t V8_TYPE_FIXEDARRAY = -1;
 136 
 137 /*
 138  * Although we have this information in v8_classes, the following offsets are
 139  * defined explicitly because they're used directly in code below.
 140  */
 141 static ssize_t V8_OFF_CODE_INSTRUCTION_SIZE;
 142 static ssize_t V8_OFF_CODE_INSTRUCTION_START;
 143 static ssize_t V8_OFF_CONSSTRING_FIRST;
 144 static ssize_t V8_OFF_CONSSTRING_SECOND;
 145 static ssize_t V8_OFF_EXTERNALSTRING_RESOURCE;
 146 static ssize_t V8_OFF_FIXEDARRAY_DATA;
 147 static ssize_t V8_OFF_FIXEDARRAY_LENGTH;
 148 static ssize_t V8_OFF_HEAPNUMBER_VALUE;
 149 static ssize_t V8_OFF_HEAPOBJECT_MAP;
 150 static ssize_t V8_OFF_JSFUNCTION_SHARED;
 151 static ssize_t V8_OFF_JSOBJECT_ELEMENTS;
 152 static ssize_t V8_OFF_JSOBJECT_PROPERTIES;
 153 static ssize_t V8_OFF_MAP_CONSTRUCTOR;
 154 static ssize_t V8_OFF_MAP_INOBJECT_PROPERTIES;
 155 static ssize_t V8_OFF_MAP_INSTANCE_ATTRIBUTES;
 156 static ssize_t V8_OFF_MAP_INSTANCE_DESCRIPTORS;
 157 static ssize_t V8_OFF_MAP_INSTANCE_SIZE;
 158 static ssize_t V8_OFF_MAP_TRANSITIONS;
 159 static ssize_t V8_OFF_ODDBALL_TO_STRING;
 160 static ssize_t V8_OFF_SCRIPT_LINE_ENDS;
 161 static ssize_t V8_OFF_SCRIPT_NAME;
 162 static ssize_t V8_OFF_SEQASCIISTR_CHARS;
 163 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_CODE;
 164 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION;
 165 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME;
 166 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_LENGTH;
 167 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_SCRIPT;
 168 static ssize_t V8_OFF_SHAREDFUNCTIONINFO_NAME;
 169 static ssize_t V8_OFF_STRING_LENGTH;
 170 
 171 #define NODE_OFF_EXTSTR_DATA            0x4     /* see node_string.h */
 172 
 173 /*
 174  * Table of constants used directly by this file.
 175  */
 176 typedef struct v8_constant {
 177         intptr_t        *v8c_valp;
 178         const char      *v8c_symbol;
 179 } v8_constant_t;
 180 
 181 static v8_constant_t v8_constants[] = {
 182         { &V8_OFF_FP_CONTEXT,               "v8dbg_off_fp_context"          },
 183         { &V8_OFF_FP_FUNCTION,              "v8dbg_off_fp_function"         },
 184         { &V8_OFF_FP_MARKER,                "v8dbg_off_fp_marker"           },
 185         { &V8_OFF_FP_ARGS,          "v8dbg_off_fp_args"             },
 186 
 187         { &V8_FirstNonstringType,   "v8dbg_FirstNonstringType"      },
 188         { &V8_IsNotStringMask,              "v8dbg_IsNotStringMask"         },
 189         { &V8_StringTag,            "v8dbg_StringTag"               },
 190         { &V8_NotStringTag,         "v8dbg_NotStringTag"            },
 191         { &V8_StringEncodingMask,   "v8dbg_StringEncodingMask"      },
 192         { &V8_TwoByteStringTag,             "v8dbg_TwoByteStringTag"        },
 193         { &V8_AsciiStringTag,               "v8dbg_AsciiStringTag"          },
 194         { &V8_StringRepresentationMask,     "v8dbg_StringRepresentationMask" },
 195         { &V8_SeqStringTag,         "v8dbg_SeqStringTag"            },
 196         { &V8_ConsStringTag,                "v8dbg_ConsStringTag"           },
 197         { &V8_ExternalStringTag,    "v8dbg_ExternalStringTag"       },
 198         { &V8_FailureTag,           "v8dbg_FailureTag"              },
 199         { &V8_FailureTagMask,               "v8dbg_FailureTagMask"          },
 200         { &V8_HeapObjectTag,                "v8dbg_HeapObjectTag"           },
 201         { &V8_HeapObjectTagMask,    "v8dbg_HeapObjectTagMask"       },
 202         { &V8_SmiTag,                       "v8dbg_SmiTag"                  },
 203         { &V8_SmiTagMask,           "v8dbg_SmiTagMask"              },
 204         { &V8_SmiValueShift,                "v8dbg_SmiValueShift"           },
 205         { &V8_PointerSizeLog2,              "v8dbg_PointerSizeLog2"         },
 206 
 207         { &V8_PROP_IDX_FIRST,               "v8dbg_prop_idx_first"          },
 208         { &V8_PROP_TYPE_FIELD,              "v8dbg_prop_type_field"         },
 209         { &V8_PROP_FIRST_PHANTOM,   "v8dbg_prop_type_first_phantom" },
 210         { &V8_PROP_TYPE_MASK,               "v8dbg_prop_type_mask"          },
 211 };
 212 
 213 static int v8_nconstants = sizeof (v8_constants) / sizeof (v8_constants[0]);
 214 
 215 /*
 216  * Some constants are -- for a variety of reasons -- optional.
 217  */
 218 static v8_constant_t v8_optionals[] = {
 219         { &V8_PROP_IDX_CONTENT,             "v8dbg_prop_idx_content"        },
 220         { &V8_PROP_DESC_KEY,                "v8dbg_prop_desc_key"           },
 221         { &V8_PROP_DESC_DETAILS,    "v8dbg_prop_desc_details"       },
 222         { &V8_PROP_DESC_VALUE,              "v8dbg_prop_desc_value"         },
 223         { &V8_PROP_DESC_SIZE,               "v8dbg_prop_desc_size"          },
 224         { &V8_TRANSITIONS_IDX_DESC, "v8dbg_transitions_idx_descriptors" }
 225 };
 226 
 227 static int v8_noptionals = sizeof (v8_optionals) / sizeof (v8_optionals[0]);
 228 
 229 typedef struct v8_offset {
 230         ssize_t         *v8o_valp;
 231         const char      *v8o_class;
 232         const char      *v8o_member;
 233         boolean_t       v8o_optional;
 234 } v8_offset_t;
 235 
 236 static v8_offset_t v8_offsets[] = {
 237         { &V8_OFF_CODE_INSTRUCTION_SIZE,
 238             "Code", "instruction_size" },
 239         { &V8_OFF_CODE_INSTRUCTION_START,
 240             "Code", "instruction_start" },
 241         { &V8_OFF_CONSSTRING_FIRST,
 242             "ConsString", "first" },
 243         { &V8_OFF_CONSSTRING_SECOND,
 244             "ConsString", "second" },
 245         { &V8_OFF_EXTERNALSTRING_RESOURCE,
 246             "ExternalString", "resource" },
 247         { &V8_OFF_FIXEDARRAY_DATA,
 248             "FixedArray", "data" },
 249         { &V8_OFF_FIXEDARRAY_LENGTH,
 250             "FixedArray", "length" },
 251         { &V8_OFF_HEAPNUMBER_VALUE,
 252             "HeapNumber", "value" },
 253         { &V8_OFF_HEAPOBJECT_MAP,
 254             "HeapObject", "map" },
 255         { &V8_OFF_JSFUNCTION_SHARED,
 256             "JSFunction", "shared" },
 257         { &V8_OFF_JSOBJECT_ELEMENTS,
 258             "JSObject", "elements" },
 259         { &V8_OFF_JSOBJECT_PROPERTIES,
 260             "JSObject", "properties" },
 261         { &V8_OFF_MAP_CONSTRUCTOR,
 262             "Map", "constructor" },
 263         { &V8_OFF_MAP_INOBJECT_PROPERTIES,
 264             "Map", "inobject_properties" },
 265         { &V8_OFF_MAP_INSTANCE_ATTRIBUTES,
 266             "Map", "instance_attributes" },
 267         { &V8_OFF_MAP_INSTANCE_DESCRIPTORS,
 268             "Map", "instance_descriptors", B_TRUE },
 269         { &V8_OFF_MAP_TRANSITIONS,
 270             "Map", "transitions", B_TRUE },
 271         { &V8_OFF_MAP_INSTANCE_SIZE,
 272             "Map", "instance_size" },
 273         { &V8_OFF_ODDBALL_TO_STRING,
 274             "Oddball", "to_string" },
 275         { &V8_OFF_SCRIPT_LINE_ENDS,
 276             "Script", "line_ends" },
 277         { &V8_OFF_SCRIPT_NAME,
 278             "Script", "name" },
 279         { &V8_OFF_SEQASCIISTR_CHARS,
 280             "SeqAsciiString", "chars" },
 281         { &V8_OFF_SHAREDFUNCTIONINFO_CODE,
 282             "SharedFunctionInfo", "code" },
 283         { &V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION,
 284             "SharedFunctionInfo", "function_token_position" },
 285         { &V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME,
 286             "SharedFunctionInfo", "inferred_name" },
 287         { &V8_OFF_SHAREDFUNCTIONINFO_LENGTH,
 288             "SharedFunctionInfo", "length" },
 289         { &V8_OFF_SHAREDFUNCTIONINFO_NAME,
 290             "SharedFunctionInfo", "name" },
 291         { &V8_OFF_SHAREDFUNCTIONINFO_SCRIPT,
 292             "SharedFunctionInfo", "script" },
 293         { &V8_OFF_STRING_LENGTH,
 294             "String", "length" },
 295 };
 296 
 297 static int v8_noffsets = sizeof (v8_offsets) / sizeof (v8_offsets[0]);
 298 
 299 static int autoconf_iter_symbol(mdb_symbol_t *, void *);
 300 static v8_class_t *conf_class_findcreate(const char *);
 301 static v8_field_t *conf_field_create(v8_class_t *, const char *, size_t);
 302 static char *conf_next_part(char *, char *);
 303 static int conf_update_parent(const char *);
 304 static int conf_update_field(v8_cfg_t *, const char *);
 305 static int conf_update_enum(v8_cfg_t *, const char *, const char *,
 306     v8_enum_t *);
 307 static int conf_update_type(v8_cfg_t *, const char *);
 308 static int conf_update_frametype(v8_cfg_t *, const char *);
 309 static void conf_class_compute_offsets(v8_class_t *);
 310 
 311 static int read_typebyte(uint8_t *, uintptr_t);
 312 static int heap_offset(const char *, const char *, ssize_t *);
 313 
 314 /*
 315  * Invoked when this dmod is initially loaded to load the set of classes, enums,
 316  * and other constants from the metadata in the target binary.
 317  */
 318 static int
 319 autoconfigure(v8_cfg_t *cfgp)
 320 {
 321         v8_class_t *clp;
 322         v8_enum_t *ep;
 323         struct v8_constant *cnp;
 324         int ii;
 325         int failed = 0;
 326 
 327         assert(v8_classes == NULL);
 328 
 329         /*
 330          * Iterate all global symbols looking for metadata.
 331          */
 332         if (cfgp->v8cfg_iter(cfgp, autoconf_iter_symbol, cfgp) != 0) {
 333                 mdb_warn("failed to autoconfigure V8 support\n");
 334                 return (-1);
 335         }
 336 
 337         /*
 338          * By now we've configured all of the classes so we can update the
 339          * "start" and "end" fields in each class with information from its
 340          * parent class.
 341          */
 342         for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
 343                 if (clp->v8c_end != (size_t)-1)
 344                         continue;
 345 
 346                 conf_class_compute_offsets(clp);
 347         };
 348 
 349         /*
 350          * Load various constants used directly in the module.
 351          */
 352         for (ii = 0; ii < v8_nconstants; ii++) {
 353                 cnp = &v8_constants[ii];
 354 
 355                 if (cfgp->v8cfg_readsym(cfgp, cnp->v8c_symbol,
 356                     cnp->v8c_valp) == -1) {
 357                         mdb_warn("failed to read \"%s\"", cnp->v8c_symbol);
 358                         failed++;
 359                 }
 360         }
 361 
 362         /*
 363          * Load optional constants; if we fail to load an optional constant,
 364          * we'll set its value to be -1.
 365          */
 366         for (ii = 0; ii < v8_noptionals; ii++) {
 367                 cnp = &v8_optionals[ii];
 368 
 369                 if (cfgp->v8cfg_readsym(cfgp, cnp->v8c_symbol,
 370                     cnp->v8c_valp) == -1) {
 371                         *cnp->v8c_valp = -1;
 372                 }
 373         }
 374 
 375         /*
 376          * We need to do some cleanup here:  there are versions of node (in
 377          * particular, early v0.8 releases) in which v8dbg_prop_idx_content had
 378          * been removed but the descriptor variables had not yet been added;
 379          * we'll fill those in now with what we know those values to be.
 380          */
 381         if (V8_PROP_IDX_CONTENT == -1 && V8_PROP_DESC_SIZE == -1) {
 382                 V8_PROP_DESC_KEY = 0;
 383                 V8_PROP_DESC_DETAILS = 1;
 384                 V8_PROP_DESC_VALUE = 2;
 385                 V8_PROP_DESC_SIZE = 3;
 386         }
 387 
 388         /*
 389          * Load type values for well-known classes that we use a lot.
 390          */
 391         for (ep = v8_types; ep->v8e_name[0] != '\0'; ep++) {
 392                 if (strcmp(ep->v8e_name, "JSObject") == 0)
 393                         V8_TYPE_JSOBJECT = ep->v8e_value;
 394 
 395                 if (strcmp(ep->v8e_name, "FixedArray") == 0)
 396                         V8_TYPE_FIXEDARRAY = ep->v8e_value;
 397         }
 398 
 399         if (V8_TYPE_JSOBJECT == -1 || V8_TYPE_FIXEDARRAY == -1) {
 400                 mdb_warn("couldn't find %s type\n",
 401                     V8_TYPE_JSOBJECT == -1 ? "JSObject" : "FixedArray");
 402                 failed++;
 403         }
 404 
 405         /*
 406          * Finally, load various class offsets.
 407          */
 408         for (ii = 0; ii < v8_noffsets; ii++) {
 409                 struct v8_offset *offp = &v8_offsets[ii];
 410                 const char *klass = offp->v8o_class;
 411 
 412 again:
 413                 if (heap_offset(klass, offp->v8o_member, offp->v8o_valp) == 0)
 414                         continue;
 415 
 416                 if (strcmp(klass, "FixedArray") == 0) {
 417                         /*
 418                          * The V8 included in node v0.6 uses a FixedArrayBase
 419                          * class to contain the "length" field, while the one
 420                          * in v0.4 has no such base class and stores the field
 421                          * directly in FixedArray; if we failed to derive
 422                          * the offset from FixedArray, try FixedArrayBase.
 423                          */
 424                         klass = "FixedArrayBase";
 425                         goto again;
 426                 }
 427 
 428                 if (offp->v8o_optional) {
 429                         *offp->v8o_valp = -1;
 430                         continue;
 431                 }
 432 
 433                 mdb_warn("couldn't find class \"%s\", field \"%s\"\n",
 434                     offp->v8o_class, offp->v8o_member);
 435                 failed++;
 436         }
 437 
 438         return (failed ? -1 : 0);
 439 }
 440 
 441 /* ARGSUSED */
 442 static int
 443 autoconf_iter_symbol(mdb_symbol_t *symp, void *arg)
 444 {
 445         v8_cfg_t *cfgp = arg;
 446 
 447         if (strncmp(symp->sym_name, "v8dbg_parent_",
 448             sizeof ("v8dbg_parent_") - 1) == 0)
 449                 return (conf_update_parent(symp->sym_name));
 450 
 451         if (strncmp(symp->sym_name, "v8dbg_class_",
 452             sizeof ("v8dbg_class_") - 1) == 0)
 453                 return (conf_update_field(cfgp, symp->sym_name));
 454 
 455         if (strncmp(symp->sym_name, "v8dbg_type_",
 456             sizeof ("v8dbg_type_") - 1) == 0)
 457                 return (conf_update_type(cfgp, symp->sym_name));
 458 
 459         if (strncmp(symp->sym_name, "v8dbg_frametype_",
 460             sizeof ("v8dbg_frametype_") - 1) == 0)
 461                 return (conf_update_frametype(cfgp, symp->sym_name));
 462 
 463         return (0);
 464 }
 465 
 466 /*
 467  * Extracts the next field of a string whose fields are separated by "__" (as
 468  * the V8 metadata symbols are).
 469  */
 470 static char *
 471 conf_next_part(char *buf, char *start)
 472 {
 473         char *pp;
 474 
 475         if ((pp = strstr(start, "__")) == NULL) {
 476                 mdb_warn("malformed symbol name: %s\n", buf);
 477                 return (NULL);
 478         }
 479 
 480         *pp = '\0';
 481         return (pp + sizeof ("__") - 1);
 482 }
 483 
 484 static v8_class_t *
 485 conf_class_findcreate(const char *name)
 486 {
 487         v8_class_t *clp, *iclp, *prev = NULL;
 488         int cmp;
 489 
 490         for (iclp = v8_classes; iclp != NULL; iclp = iclp->v8c_next) {
 491                 if ((cmp = strcmp(iclp->v8c_name, name)) == 0)
 492                         return (iclp);
 493 
 494                 if (cmp > 0)
 495                         break;
 496 
 497                 prev = iclp;
 498         }
 499 
 500         if ((clp = mdb_zalloc(sizeof (*clp), UM_NOSLEEP)) == NULL)
 501                 return (NULL);
 502 
 503         (void) strlcpy(clp->v8c_name, name, sizeof (clp->v8c_name));
 504         clp->v8c_end = (size_t)-1;
 505         clp->v8c_next = iclp;
 506 
 507         if (prev != NULL) {
 508                 prev->v8c_next = clp;
 509         } else {
 510                 v8_classes = clp;
 511         }
 512 
 513         return (clp);
 514 }
 515 
 516 static v8_field_t *
 517 conf_field_create(v8_class_t *clp, const char *name, size_t offset)
 518 {
 519         v8_field_t *flp, *iflp;
 520 
 521         if ((flp = mdb_zalloc(sizeof (*flp), UM_NOSLEEP)) == NULL)
 522                 return (NULL);
 523 
 524         (void) strlcpy(flp->v8f_name, name, sizeof (flp->v8f_name));
 525         flp->v8f_offset = offset;
 526 
 527         if (clp->v8c_fields == NULL || clp->v8c_fields->v8f_offset > offset) {
 528                 flp->v8f_next = clp->v8c_fields;
 529                 clp->v8c_fields = flp;
 530                 return (flp);
 531         }
 532 
 533         for (iflp = clp->v8c_fields; iflp->v8f_next != NULL;
 534             iflp = iflp->v8f_next) {
 535                 if (iflp->v8f_next->v8f_offset > offset)
 536                         break;
 537         }
 538 
 539         flp->v8f_next = iflp->v8f_next;
 540         iflp->v8f_next = flp;
 541         return (flp);
 542 }
 543 
 544 /*
 545  * Given a "v8dbg_parent_X__Y", symbol, update the parent of class X to class Y.
 546  * Note that neither class necessarily exists already.
 547  */
 548 static int
 549 conf_update_parent(const char *symbol)
 550 {
 551         char *pp, *qq;
 552         char buf[128];
 553         v8_class_t *clp, *pclp;
 554 
 555         (void) strlcpy(buf, symbol, sizeof (buf));
 556         pp = buf + sizeof ("v8dbg_parent_") - 1;
 557         qq = conf_next_part(buf, pp);
 558 
 559         if (qq == NULL)
 560                 return (-1);
 561 
 562         clp = conf_class_findcreate(pp);
 563         pclp = conf_class_findcreate(qq);
 564 
 565         if (clp == NULL || pclp == NULL) {
 566                 mdb_warn("mdb_v8: out of memory\n");
 567                 return (-1);
 568         }
 569 
 570         clp->v8c_parent = pclp;
 571         return (0);
 572 }
 573 
 574 /*
 575  * Given a "v8dbg_class_CLASS__FIELD__TYPE", symbol, save field "FIELD" into
 576  * class CLASS with the offset described by the symbol.  Note that CLASS does
 577  * not necessarily exist already.
 578  */
 579 static int
 580 conf_update_field(v8_cfg_t *cfgp, const char *symbol)
 581 {
 582         v8_class_t *clp;
 583         v8_field_t *flp;
 584         intptr_t offset;
 585         char *pp, *qq, *tt;
 586         char buf[128];
 587 
 588         (void) strlcpy(buf, symbol, sizeof (buf));
 589 
 590         pp = buf + sizeof ("v8dbg_class_") - 1;
 591         qq = conf_next_part(buf, pp);
 592 
 593         if (qq == NULL || (tt = conf_next_part(buf, qq)) == NULL)
 594                 return (-1);
 595 
 596         if (cfgp->v8cfg_readsym(cfgp, symbol, &offset) == -1) {
 597                 mdb_warn("failed to read symbol \"%s\"", symbol);
 598                 return (-1);
 599         }
 600 
 601         if ((clp = conf_class_findcreate(pp)) == NULL ||
 602             (flp = conf_field_create(clp, qq, (size_t)offset)) == NULL)
 603                 return (-1);
 604 
 605         if (strcmp(tt, "int") == 0)
 606                 flp->v8f_isbyte = B_TRUE;
 607 
 608         return (0);
 609 }
 610 
 611 static int
 612 conf_update_enum(v8_cfg_t *cfgp, const char *symbol, const char *name,
 613     v8_enum_t *enp)
 614 {
 615         intptr_t value;
 616 
 617         if (cfgp->v8cfg_readsym(cfgp, symbol, &value) == -1) {
 618                 mdb_warn("failed to read symbol \"%s\"", symbol);
 619                 return (-1);
 620         }
 621 
 622         enp->v8e_value = (int)value;
 623         (void) strlcpy(enp->v8e_name, name, sizeof (enp->v8e_name));
 624         return (0);
 625 }
 626 
 627 /*
 628  * Given a "v8dbg_type_TYPENAME" constant, save the type name in v8_types.  Note
 629  * that this enum has multiple integer values with the same string label.
 630  */
 631 static int
 632 conf_update_type(v8_cfg_t *cfgp, const char *symbol)
 633 {
 634         char *klass;
 635         v8_enum_t *enp;
 636         char buf[128];
 637 
 638         if (v8_next_type > sizeof (v8_types) / sizeof (v8_types[0])) {
 639                 mdb_warn("too many V8 types\n");
 640                 return (-1);
 641         }
 642 
 643         (void) strlcpy(buf, symbol, sizeof (buf));
 644 
 645         klass = buf + sizeof ("v8dbg_type_") - 1;
 646         if (conf_next_part(buf, klass) == NULL)
 647                 return (-1);
 648 
 649         enp = &v8_types[v8_next_type++];
 650         return (conf_update_enum(cfgp, symbol, klass, enp));
 651 }
 652 
 653 /*
 654  * Given a "v8dbg_frametype_TYPENAME" constant, save the frame type in
 655  * v8_frametypes.
 656  */
 657 static int
 658 conf_update_frametype(v8_cfg_t *cfgp, const char *symbol)
 659 {
 660         const char *frametype;
 661         v8_enum_t *enp;
 662 
 663         if (v8_next_frametype >
 664             sizeof (v8_frametypes) / sizeof (v8_frametypes[0])) {
 665                 mdb_warn("too many V8 frame types\n");
 666                 return (-1);
 667         }
 668 
 669         enp = &v8_frametypes[v8_next_frametype++];
 670         frametype = symbol + sizeof ("v8dbg_frametype_") - 1;
 671         return (conf_update_enum(cfgp, symbol, frametype, enp));
 672 }
 673 
 674 /*
 675  * Now that all classes have been loaded, update the "start" and "end" fields of
 676  * each class based on the values of its parent class.
 677  */
 678 static void
 679 conf_class_compute_offsets(v8_class_t *clp)
 680 {
 681         v8_field_t *flp;
 682 
 683         assert(clp->v8c_start == 0);
 684         assert(clp->v8c_end == (size_t)-1);
 685 
 686         if (clp->v8c_parent != NULL) {
 687                 if (clp->v8c_parent->v8c_end == (size_t)-1)
 688                         conf_class_compute_offsets(clp->v8c_parent);
 689 
 690                 clp->v8c_start = clp->v8c_parent->v8c_end;
 691         }
 692 
 693         if (clp->v8c_fields == NULL) {
 694                 clp->v8c_end = clp->v8c_start;
 695                 return;
 696         }
 697 
 698         for (flp = clp->v8c_fields; flp->v8f_next != NULL; flp = flp->v8f_next)
 699                 ;
 700 
 701         if (flp == NULL)
 702                 clp->v8c_end = clp->v8c_start;
 703         else
 704                 clp->v8c_end = flp->v8f_offset + sizeof (uintptr_t);
 705 }
 706 
 707 /*
 708  * Utility functions
 709  */
 710 static int jsstr_print(uintptr_t, boolean_t, char **, size_t *);
 711 
 712 static const char *
 713 enum_lookup_str(v8_enum_t *enums, int val, const char *dflt)
 714 {
 715         v8_enum_t *ep;
 716 
 717         for (ep = enums; ep->v8e_name[0] != '\0'; ep++) {
 718                 if (ep->v8e_value == val)
 719                         return (ep->v8e_name);
 720         }
 721 
 722         return (dflt);
 723 }
 724 
 725 static void
 726 enum_print(v8_enum_t *enums)
 727 {
 728         v8_enum_t *itp;
 729 
 730         for (itp = enums; itp->v8e_name[0] != '\0'; itp++)
 731                 mdb_printf("%-30s = 0x%02x\n", itp->v8e_name, itp->v8e_value);
 732 }
 733 
 734 /*
 735  * b[v]snprintf behave like [v]snprintf(3c), except that they update the buffer
 736  * and length arguments based on how much buffer space is used by the operation.
 737  * This makes it much easier to combine multiple calls in sequence without
 738  * worrying about buffer overflow.
 739  */
 740 static size_t
 741 bvsnprintf(char **bufp, size_t *buflenp, const char *format, va_list alist)
 742 {
 743         size_t rv, len;
 744 
 745         if (*buflenp == 0)
 746                 return (vsnprintf(NULL, 0, format, alist));
 747 
 748         rv = vsnprintf(*bufp, *buflenp, format, alist);
 749 
 750         len = MIN(rv, *buflenp);
 751         *buflenp -= len;
 752         *bufp += len;
 753 
 754         return (len);
 755 }
 756 
 757 static size_t
 758 bsnprintf(char **bufp, size_t *buflenp, const char *format, ...)
 759 {
 760         va_list alist;
 761         size_t rv;
 762 
 763         va_start(alist, format);
 764         rv = bvsnprintf(bufp, buflenp, format, alist);
 765         va_end(alist);
 766 
 767         return (rv);
 768 }
 769 
 770 static void
 771 v8_warn(const char *format, ...)
 772 {
 773         char buf[512];
 774         va_list alist;
 775         int len;
 776 
 777         if (v8_silent)
 778                 return;
 779 
 780         va_start(alist, format);
 781         (void) vsnprintf(buf, sizeof (buf), format, alist);
 782         va_end(alist);
 783 
 784         /*
 785          * This is made slightly annoying because we need to effectively
 786          * preserve the original format string to allow for mdb to use the
 787          * new-line at the end to indicate that strerror should be elided.
 788          */
 789         if ((len = strlen(format)) > 0 && format[len - 1] == '\n') {
 790                 buf[strlen(buf) - 1] = '\0';
 791                 mdb_warn("%s\n", buf);
 792         } else {
 793                 mdb_warn("%s", buf);
 794         }
 795 }
 796 
 797 /*
 798  * Returns in "offp" the offset of field "field" in C++ class "klass".
 799  */
 800 static int
 801 heap_offset(const char *klass, const char *field, ssize_t *offp)
 802 {
 803         v8_class_t *clp;
 804         v8_field_t *flp;
 805 
 806         for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
 807                 if (strcmp(klass, clp->v8c_name) == 0)
 808                         break;
 809         }
 810 
 811         if (clp == NULL)
 812                 return (-1);
 813 
 814         for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
 815                 if (strcmp(field, flp->v8f_name) == 0)
 816                         break;
 817         }
 818 
 819         if (flp == NULL)
 820                 return (-1);
 821 
 822         *offp = V8_OFF_HEAP(flp->v8f_offset);
 823         return (0);
 824 }
 825 
 826 /*
 827  * Assuming "addr" is an instance of the C++ heap class "klass", read into *valp
 828  * the pointer-sized value of field "field".
 829  */
 830 static int
 831 read_heap_ptr(uintptr_t *valp, uintptr_t addr, ssize_t off)
 832 {
 833         if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
 834                 v8_warn("failed to read offset %d from %p", off, addr);
 835                 return (-1);
 836         }
 837 
 838         return (0);
 839 }
 840 
 841 /*
 842  * Like read_heap_ptr, but assume the field is an SMI and store the actual value
 843  * into *valp rather than the encoded representation.
 844  */
 845 static int
 846 read_heap_smi(uintptr_t *valp, uintptr_t addr, ssize_t off)
 847 {
 848         if (read_heap_ptr(valp, addr, off) != 0)
 849                 return (-1);
 850 
 851         if (!V8_IS_SMI(*valp)) {
 852                 v8_warn("expected SMI, got %p\n", *valp);
 853                 return (-1);
 854         }
 855 
 856         *valp = V8_SMI_VALUE(*valp);
 857 
 858         return (0);
 859 }
 860 
 861 static int
 862 read_heap_double(double *valp, uintptr_t addr, ssize_t off)
 863 {
 864         if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
 865                 v8_warn("failed to read heap value at %p", addr + off);
 866                 return (-1);
 867         }
 868 
 869         return (0);
 870 }
 871 
 872 /*
 873  * Assuming "addr" refers to a FixedArray, return a newly-allocated array
 874  * representing its contents.
 875  */
 876 static int
 877 read_heap_array(uintptr_t addr, uintptr_t **retp, size_t *lenp, int flags)
 878 {
 879         uint8_t type;
 880         uintptr_t len;
 881 
 882         if (!V8_IS_HEAPOBJECT(addr))
 883                 return (-1);
 884 
 885         if (read_typebyte(&type, addr) != 0)
 886                 return (-1);
 887 
 888         if (type != V8_TYPE_FIXEDARRAY)
 889                 return (-1);
 890 
 891         if (read_heap_smi(&len, addr, V8_OFF_FIXEDARRAY_LENGTH) != 0)
 892                 return (-1);
 893 
 894         *lenp = len;
 895 
 896         if (len == 0) {
 897                 *retp = NULL;
 898                 return (0);
 899         }
 900 
 901         if ((*retp = mdb_zalloc(len * sizeof (uintptr_t), flags)) == NULL)
 902                 return (-1);
 903 
 904         if (mdb_vread(*retp, len * sizeof (uintptr_t),
 905             addr + V8_OFF_FIXEDARRAY_DATA) == -1) {
 906                 if (flags != UM_GC)
 907                         mdb_free(*retp, len * sizeof (uintptr_t));
 908 
 909                 return (-1);
 910         }
 911 
 912         return (0);
 913 }
 914 
 915 static int
 916 read_heap_byte(uint8_t *valp, uintptr_t addr, ssize_t off)
 917 {
 918         if (mdb_vread(valp, sizeof (*valp), addr + off) == -1) {
 919                 v8_warn("failed to read heap value at %p", addr + off);
 920                 return (-1);
 921         }
 922 
 923         return (0);
 924 }
 925 
 926 /*
 927  * Given a heap object, returns in *valp the byte describing the type of the
 928  * object.  This is shorthand for first retrieving the Map at the start of the
 929  * heap object and then retrieving the type byte from the Map object.
 930  */
 931 static int
 932 read_typebyte(uint8_t *valp, uintptr_t addr)
 933 {
 934         uintptr_t mapaddr;
 935         ssize_t off = V8_OFF_HEAPOBJECT_MAP;
 936 
 937         if (mdb_vread(&mapaddr, sizeof (mapaddr), addr + off) == -1) {
 938                 v8_warn("failed to read type of %p", addr);
 939                 return (-1);
 940         }
 941 
 942         if (!V8_IS_HEAPOBJECT(mapaddr)) {
 943                 v8_warn("object map is not a heap object\n");
 944                 return (-1);
 945         }
 946 
 947         if (read_heap_byte(valp, mapaddr, V8_OFF_MAP_INSTANCE_ATTRIBUTES) == -1)
 948                 return (-1);
 949 
 950         return (0);
 951 }
 952 
 953 /*
 954  * Given a heap object, returns in *valp the size of the object.  For
 955  * variable-size objects, returns an undefined value.
 956  */
 957 static int
 958 read_size(size_t *valp, uintptr_t addr)
 959 {
 960         uintptr_t mapaddr;
 961         uint8_t size;
 962 
 963         if (read_heap_ptr(&mapaddr, addr, V8_OFF_HEAPOBJECT_MAP) != 0)
 964                 return (-1);
 965 
 966         if (!V8_IS_HEAPOBJECT(mapaddr)) {
 967                 v8_warn("heap object map is not itself a heap object\n");
 968                 return (-1);
 969         }
 970 
 971         if (read_heap_byte(&size, mapaddr, V8_OFF_MAP_INSTANCE_SIZE) != 0)
 972                 return (-1);
 973 
 974         *valp = size << V8_PointerSizeLog2;
 975         return (0);
 976 }
 977 
 978 /*
 979  * Returns in "buf" a description of the type of "addr" suitable for printing.
 980  */
 981 static int
 982 obj_jstype(uintptr_t addr, char **bufp, size_t *lenp, uint8_t *typep)
 983 {
 984         uint8_t typebyte;
 985         uintptr_t strptr;
 986         const char *typename;
 987 
 988         if (V8_IS_FAILURE(addr)) {
 989                 if (typep)
 990                         *typep = 0;
 991                 (void) bsnprintf(bufp, lenp, "'Failure' object");
 992                 return (0);
 993         }
 994 
 995         if (V8_IS_SMI(addr)) {
 996                 if (typep)
 997                         *typep = 0;
 998                 (void) bsnprintf(bufp, lenp, "SMI: value = %d",
 999                     V8_SMI_VALUE(addr));
1000                 return (0);
1001         }
1002 
1003         if (read_typebyte(&typebyte, addr) != 0)
1004                 return (-1);
1005 
1006         if (typep)
1007                 *typep = typebyte;
1008 
1009         typename = enum_lookup_str(v8_types, typebyte, "<unknown>");
1010         (void) bsnprintf(bufp, lenp, typename);
1011 
1012         if (strcmp(typename, "Oddball") == 0) {
1013                 if (read_heap_ptr(&strptr, addr,
1014                     V8_OFF_ODDBALL_TO_STRING) != -1) {
1015                         (void) bsnprintf(bufp, lenp, ": \"");
1016                         (void) jsstr_print(strptr, B_FALSE, bufp, lenp);
1017                         (void) bsnprintf(bufp, lenp, "\"");
1018                 }
1019         }
1020 
1021         return (0);
1022 }
1023 
1024 /*
1025  * Print out the fields of the given object that come from the given class.
1026  */
1027 static int
1028 obj_print_fields(uintptr_t baddr, v8_class_t *clp)
1029 {
1030         v8_field_t *flp;
1031         uintptr_t addr, value;
1032         int rv;
1033         char *bufp;
1034         size_t len;
1035         uint8_t type;
1036         char buf[256];
1037 
1038         for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next) {
1039                 bufp = buf;
1040                 len = sizeof (buf);
1041 
1042                 addr = baddr + V8_OFF_HEAP(flp->v8f_offset);
1043 
1044                 if (flp->v8f_isbyte) {
1045                         uint8_t sv;
1046                         if (mdb_vread(&sv, sizeof (sv), addr) == -1) {
1047                                 mdb_printf("%p %s (unreadable)\n",
1048                                     addr, flp->v8f_name);
1049                                 continue;
1050                         }
1051 
1052                         mdb_printf("%p %s = 0x%x\n", addr, flp->v8f_name, sv);
1053                         continue;
1054                 }
1055 
1056                 rv = mdb_vread((void *)&value, sizeof (value), addr);
1057 
1058                 if (rv != sizeof (value) ||
1059                     obj_jstype(value, &bufp, &len, &type) != 0) {
1060                         mdb_printf("%p %s (unreadable)\n", addr, flp->v8f_name);
1061                         continue;
1062                 }
1063 
1064                 if (type != 0 && V8_TYPE_STRING(type)) {
1065                         (void) bsnprintf(&bufp, &len, ": \"");
1066                         (void) jsstr_print(value, B_FALSE, &bufp, &len);
1067                         (void) bsnprintf(&bufp, &len, "\"");
1068                 }
1069 
1070                 mdb_printf("%p %s = %p (%s)\n", addr, flp->v8f_name, value,
1071                     buf);
1072         }
1073 
1074         return (DCMD_OK);
1075 }
1076 
1077 /*
1078  * Print out all fields of the given object, starting with the root of the class
1079  * hierarchy and working down the most specific type.
1080  */
1081 static int
1082 obj_print_class(uintptr_t addr, v8_class_t *clp)
1083 {
1084         int rv = 0;
1085 
1086         /*
1087          * If we have no fields, we just print a simple inheritance hierarchy.
1088          * If we have fields but our parent doesn't, our header includes the
1089          * inheritance hierarchy.
1090          */
1091         if (clp->v8c_end == 0) {
1092                 mdb_printf("%s ", clp->v8c_name);
1093 
1094                 if (clp->v8c_parent != NULL) {
1095                         mdb_printf("< ");
1096                         (void) obj_print_class(addr, clp->v8c_parent);
1097                 }
1098 
1099                 return (0);
1100         }
1101 
1102         mdb_printf("%p %s", addr, clp->v8c_name);
1103 
1104         if (clp->v8c_start == 0 && clp->v8c_parent != NULL) {
1105                 mdb_printf(" < ");
1106                 (void) obj_print_class(addr, clp->v8c_parent);
1107         }
1108 
1109         mdb_printf(" {\n");
1110         (void) mdb_inc_indent(4);
1111 
1112         if (clp->v8c_start > 0 && clp->v8c_parent != NULL)
1113                 rv = obj_print_class(addr, clp->v8c_parent);
1114 
1115         rv |= obj_print_fields(addr, clp);
1116         (void) mdb_dec_indent(4);
1117         mdb_printf("}\n");
1118 
1119         return (rv);
1120 }
1121 
1122 /*
1123  * Print the ASCII string for the given ASCII JS string, expanding ConsStrings
1124  * and ExternalStrings as needed.
1125  */
1126 static int jsstr_print_seq(uintptr_t, boolean_t, char **, size_t *);
1127 static int jsstr_print_cons(uintptr_t, boolean_t, char **, size_t *);
1128 static int jsstr_print_external(uintptr_t, boolean_t, char **, size_t *);
1129 
1130 static int
1131 jsstr_print(uintptr_t addr, boolean_t verbose, char **bufp, size_t *lenp)
1132 {
1133         uint8_t typebyte;
1134         int err = 0;
1135         char *lbufp;
1136         size_t llen;
1137         char buf[64];
1138 
1139         if (read_typebyte(&typebyte, addr) != 0)
1140                 return (0);
1141 
1142         if (!V8_TYPE_STRING(typebyte)) {
1143                 (void) bsnprintf(bufp, lenp, "<not a string>");
1144                 return (0);
1145         }
1146 
1147         if (!V8_STRENC_ASCII(typebyte)) {
1148                 (void) bsnprintf(bufp, lenp, "<two-byte string>");
1149                 return (0);
1150         }
1151 
1152         if (verbose) {
1153                 lbufp = buf;
1154                 llen = sizeof (buf);
1155                 (void) obj_jstype(addr, &lbufp, &llen, NULL);
1156                 mdb_printf("%s\n", buf);
1157                 (void) mdb_inc_indent(4);
1158         }
1159 
1160         if (V8_STRREP_SEQ(typebyte))
1161                 err = jsstr_print_seq(addr, verbose, bufp, lenp);
1162         else if (V8_STRREP_CONS(typebyte))
1163                 err = jsstr_print_cons(addr, verbose, bufp, lenp);
1164         else if (V8_STRREP_EXT(typebyte))
1165                 err = jsstr_print_external(addr, verbose, bufp, lenp);
1166         else {
1167                 (void) bsnprintf(bufp, lenp, "<unknown string type>");
1168                 err = -1;
1169         }
1170 
1171         if (verbose)
1172                 (void) mdb_dec_indent(4);
1173 
1174         return (err);
1175 }
1176 
1177 static int
1178 jsstr_print_seq(uintptr_t addr, boolean_t verbose, char **bufp, size_t *lenp)
1179 {
1180         uintptr_t len, rlen;
1181         char buf[256];
1182 
1183         if (read_heap_smi(&len, addr, V8_OFF_STRING_LENGTH) != 0)
1184                 return (-1);
1185 
1186         rlen = len <= sizeof (buf) - 1 ? len : sizeof (buf) - sizeof ("[...]");
1187 
1188         if (verbose)
1189                 mdb_printf("length: %d, will read: %d\n", len, rlen);
1190 
1191         buf[0] = '\0';
1192 
1193         if (rlen > 0 && mdb_readstr(buf, rlen + 1,
1194             addr + V8_OFF_SEQASCIISTR_CHARS) == -1) {
1195                 v8_warn("failed to read SeqString data");
1196                 return (-1);
1197         }
1198 
1199         if (rlen != len)
1200                 (void) strlcat(buf, "[...]", sizeof (buf));
1201 
1202         if (verbose)
1203                 mdb_printf("value: \"%s\"\n", buf);
1204 
1205         (void) bsnprintf(bufp, lenp, "%s", buf);
1206         return (0);
1207 }
1208 
1209 static int
1210 jsstr_print_cons(uintptr_t addr, boolean_t verbose, char **bufp, size_t *lenp)
1211 {
1212         uintptr_t ptr1, ptr2;
1213 
1214         if (read_heap_ptr(&ptr1, addr, V8_OFF_CONSSTRING_FIRST) != 0 ||
1215             read_heap_ptr(&ptr2, addr, V8_OFF_CONSSTRING_SECOND) != 0)
1216                 return (-1);
1217 
1218         if (verbose) {
1219                 mdb_printf("ptr1: %p\n", ptr1);
1220                 mdb_printf("ptr2: %p\n", ptr2);
1221         }
1222 
1223         if (jsstr_print(ptr1, verbose, bufp, lenp) != 0)
1224                 return (-1);
1225 
1226         return (jsstr_print(ptr2, verbose, bufp, lenp));
1227 }
1228 
1229 static int
1230 jsstr_print_external(uintptr_t addr, boolean_t verbose, char **bufp,
1231     size_t *lenp)
1232 {
1233         uintptr_t ptr1, ptr2;
1234         char buf[256];
1235 
1236         if (verbose)
1237                 mdb_printf("assuming Node.js string\n");
1238 
1239         if (read_heap_ptr(&ptr1, addr, V8_OFF_EXTERNALSTRING_RESOURCE) != 0)
1240                 return (-1);
1241 
1242         if (mdb_vread(&ptr2, sizeof (ptr2),
1243             ptr1 + NODE_OFF_EXTSTR_DATA) == -1) {
1244                 v8_warn("failed to read node external pointer: %p",
1245                     ptr1 + NODE_OFF_EXTSTR_DATA);
1246                 return (-1);
1247         }
1248 
1249         if (mdb_readstr(buf, sizeof (buf), ptr2) == -1) {
1250                 v8_warn("failed to read ExternalString data");
1251                 return (-1);
1252         }
1253 
1254         if (buf[0] != '\0' && !isascii(buf[0])) {
1255                 v8_warn("failed to read ExternalString ascii data\n");
1256                 return (-1);
1257         }
1258 
1259         (void) bsnprintf(bufp, lenp, "%s", buf);
1260         return (0);
1261 }
1262 
1263 /*
1264  * Returns true if the given address refers to the "undefined" object.  Returns
1265  * false on failure (since we shouldn't fail on the actual "undefined" value).
1266  */
1267 static boolean_t
1268 jsobj_is_undefined(uintptr_t addr)
1269 {
1270         uint8_t type;
1271         uintptr_t strptr;
1272         const char *typename;
1273         char buf[16];
1274         char *bufp = buf;
1275         size_t len = sizeof (buf);
1276 
1277         v8_silent++;
1278 
1279         if (read_typebyte(&type, addr) != 0) {
1280                 v8_silent--;
1281                 return (B_FALSE);
1282         }
1283 
1284         v8_silent--;
1285 
1286         typename = enum_lookup_str(v8_types, type, "<unknown>");
1287         if (strcmp(typename, "Oddball") != 0)
1288                 return (B_FALSE);
1289 
1290         if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) == -1)
1291                 return (B_FALSE);
1292 
1293         if (jsstr_print(strptr, B_FALSE, &bufp, &len) != 0)
1294                 return (B_FALSE);
1295 
1296         return (strcmp(buf, "undefined") == 0);
1297 }
1298 
1299 static int
1300 jsobj_properties(uintptr_t addr,
1301     int (*func)(const char *, uintptr_t, void *), void *arg)
1302 {
1303         uintptr_t ptr, map;
1304         uintptr_t *props = NULL, *descs = NULL, *content = NULL, *trans;
1305         size_t size, nprops, ndescs, ncontent, ntrans;
1306         ssize_t ii, rndescs;
1307         uint8_t type, ninprops;
1308         int rval = -1;
1309         size_t ps = sizeof (uintptr_t);
1310         ssize_t off;
1311 
1312         /*
1313          * Objects have either "fast" properties represented with a FixedArray
1314          * or slow properties represented with a Dictionary.  We only support
1315          * the former, so we check that up front.
1316          */
1317         if (mdb_vread(&ptr, ps, addr + V8_OFF_JSOBJECT_PROPERTIES) == -1)
1318                 return (-1);
1319 
1320         if (read_typebyte(&type, ptr) != 0)
1321                 return (-1);
1322 
1323         if (type != V8_TYPE_FIXEDARRAY)
1324                 return (func(NULL, 0, arg));
1325 
1326         if (read_heap_array(ptr, &props, &nprops, UM_SLEEP) != 0)
1327                 return (-1);
1328 
1329         /*
1330          * To iterate the properties, we need to examine the instance
1331          * descriptors of the associated Map object.  Depending on the version
1332          * of V8, this might be found directly from the map -- or indirectly
1333          * via the transitions array.
1334          */
1335         if (mdb_vread(&map, ps, addr + V8_OFF_HEAPOBJECT_MAP) == -1)
1336                 goto err;
1337 
1338         if ((off = V8_OFF_MAP_INSTANCE_DESCRIPTORS) == -1) {
1339                 if (V8_OFF_MAP_TRANSITIONS == -1 ||
1340                     V8_TRANSITIONS_IDX_DESC == -1 ||
1341                     V8_PROP_IDX_CONTENT != -1) {
1342                         mdb_warn("missing instance_descriptors, but did "
1343                             "not find expected transitions array metadata; "
1344                             "cannot read properties\n");
1345                         goto err;
1346                 }
1347 
1348                 off = V8_OFF_MAP_TRANSITIONS;
1349         }
1350 
1351         if (mdb_vread(&ptr, ps, map + off) == -1)
1352                 goto err;
1353 
1354         if (V8_OFF_MAP_TRANSITIONS != -1) {
1355                 if (read_heap_array(ptr, &trans, &ntrans, UM_SLEEP) != 0)
1356                         goto err;
1357 
1358                 ptr = trans[V8_TRANSITIONS_IDX_DESC];
1359                 mdb_free(trans, ntrans * sizeof (uintptr_t));
1360         }
1361 
1362         if (read_heap_array(ptr, &descs, &ndescs, UM_SLEEP) != 0)
1363                 goto err;
1364 
1365         if (read_size(&size, addr) != 0)
1366                 size = 0;
1367 
1368         if (mdb_vread(&ninprops, 1, map + V8_OFF_MAP_INOBJECT_PROPERTIES) == -1)
1369                 goto err;
1370 
1371         if (V8_PROP_IDX_CONTENT != -1 && V8_PROP_IDX_CONTENT < ndescs &&
1372             read_heap_array(descs[V8_PROP_IDX_CONTENT], &content,
1373             &ncontent, UM_SLEEP) != 0)
1374                 goto err;
1375 
1376         if ((V8_PROP_IDX_CONTENT == -1 && V8_PROP_DESC_SIZE == -1) ||
1377             (V8_PROP_IDX_CONTENT != -1 && V8_PROP_DESC_SIZE != -1)) {
1378                 /*
1379                  * Exactly one of these must be set; if they're both unset or
1380                  * both set (that is, if the -- ahem -- logical xor of being
1381                  * set evaluates to false), we have a misconfiguration.
1382                  */
1383                 mdb_warn("v8dbg_prop_idx_content (%ld) is inconsistent with "
1384                     "v8dbg_prop_desc_size (%ld); cannot read properties\n",
1385                     V8_PROP_IDX_CONTENT, V8_PROP_DESC_SIZE);
1386                 goto err;
1387         }
1388 
1389         if (V8_PROP_IDX_CONTENT == -1) {
1390                 /*
1391                  * On node v0.8 and later, the content is not stored in an
1392                  * orthogonal FixedArray, but rather with the descriptors.
1393                  */
1394                 content = descs;
1395                 ncontent = ndescs;
1396                 rndescs = ndescs > V8_PROP_IDX_FIRST ?
1397                     (ndescs - V8_PROP_IDX_FIRST) / V8_PROP_DESC_SIZE : 0;
1398         } else {
1399                 rndescs = ndescs - V8_PROP_IDX_FIRST;
1400         }
1401 
1402         for (ii = 0; ii < rndescs; ii++) {
1403                 uintptr_t keyidx, validx, detidx, baseidx;
1404                 char buf[1024];
1405                 intptr_t val;
1406                 uint_t len = sizeof (buf);
1407                 char *c = buf;
1408 
1409                 if (V8_PROP_IDX_CONTENT != -1) {
1410                         /*
1411                          * In node versions prior to v0.8, this was hardcoded
1412                          * in the V8 implementation, so we hardcode it here
1413                          * as well.
1414                          */
1415                         keyidx = ii + V8_PROP_IDX_FIRST;
1416                         validx = ii << 1;
1417                         detidx = (ii << 1) + 1;
1418                 } else {
1419                         baseidx = V8_PROP_IDX_FIRST + (ii * V8_PROP_DESC_SIZE);
1420                         keyidx = baseidx + V8_PROP_DESC_KEY;
1421                         validx = baseidx + V8_PROP_DESC_VALUE;
1422                         detidx = baseidx + V8_PROP_DESC_DETAILS;
1423                 }
1424 
1425                 if (detidx >= ncontent) {
1426                         v8_warn("property descriptor %d: detidx (%d) "
1427                             "out of bounds for content array (length %d)\n",
1428                             ii, detidx, ncontent);
1429                         continue;
1430                 }
1431 
1432                 if (content != descs && !V8_DESC_ISFIELD(content[detidx]))
1433                         continue;
1434 
1435                 if (keyidx >= ndescs) {
1436                         v8_warn("property descriptor %d: keyidx (%d) "
1437                             "out of bounds for descriptor array (length %d)\n",
1438                             ii, keyidx, ndescs);
1439                         continue;
1440                 }
1441 
1442                 if (jsstr_print(descs[keyidx], B_FALSE, &c, &len) != 0)
1443                         continue;
1444 
1445                 val = (intptr_t)content[validx];
1446 
1447                 if (!V8_IS_SMI(val)) {
1448                         v8_warn("property descriptor %d: value index value "
1449                             "is not an SMI: %p\n", ii, val);
1450                         continue;
1451                 }
1452 
1453                 val = V8_SMI_VALUE(val) - ninprops;
1454 
1455                 if (val < 0) {
1456                         /* property is stored directly in the object */
1457                         if (mdb_vread(&ptr, sizeof (ptr), addr + V8_OFF_HEAP(
1458                             size + val * sizeof (uintptr_t))) == -1) {
1459                                 v8_warn("failed to read in-object "
1460                                     "property at %p\n", addr + V8_OFF_HEAP(
1461                                     size + val * sizeof (uintptr_t)));
1462                                 continue;
1463                         }
1464                 } else {
1465                         /* property should be in "props" array */
1466                         if (val >= nprops) {
1467                                 v8_warn("property descriptor %d: value index "
1468                                     "value (%d) out of bounds (%d)\n", ii, val,
1469                                     nprops);
1470                                 continue;
1471                         }
1472 
1473                         ptr = props[val];
1474                 }
1475 
1476                 if (func(buf, ptr, arg) != 0)
1477                         goto err;
1478         }
1479 
1480         rval = 0;
1481 err:
1482         if (props != NULL)
1483                 mdb_free(props, nprops * sizeof (uintptr_t));
1484 
1485         if (descs != NULL)
1486                 mdb_free(descs, ndescs * sizeof (uintptr_t));
1487 
1488         if (content != NULL && V8_PROP_IDX_CONTENT != -1)
1489                 mdb_free(content, ncontent * sizeof (uintptr_t));
1490 
1491         return (rval);
1492 }
1493 
1494 /*
1495  * Given the line endings table in "lendsp", computes the line number for the
1496  * given token position and print the result into "buf".  If "lendsp" is
1497  * undefined, prints the token position instead.
1498  */
1499 static int
1500 jsfunc_lineno(uintptr_t lendsp, uintptr_t tokpos, char *buf, size_t buflen)
1501 {
1502         uintptr_t size, bufsz, lower, upper, ii = 0;
1503         uintptr_t *data;
1504 
1505         if (jsobj_is_undefined(lendsp)) {
1506                 /*
1507                  * The token position is an SMI, but it comes in as its raw
1508                  * value so we can more easily compare it to values in the line
1509                  * endings table.  If we're just printing the position directly,
1510                  * we must convert it here.
1511                  */
1512                 mdb_snprintf(buf, buflen, "position %d", V8_SMI_VALUE(tokpos));
1513                 return (0);
1514         }
1515 
1516         if (read_heap_smi(&size, lendsp, V8_OFF_FIXEDARRAY_LENGTH) != 0)
1517                 return (-1);
1518 
1519         bufsz = size * sizeof (data[0]);
1520 
1521         if ((data = mdb_alloc(bufsz, UM_NOSLEEP)) == NULL) {
1522                 v8_warn("failed to alloc %d bytes for FixedArray data", bufsz);
1523                 return (-1);
1524         }
1525 
1526         if (mdb_vread(data, bufsz, lendsp + V8_OFF_FIXEDARRAY_DATA) != bufsz) {
1527                 v8_warn("failed to read FixedArray data");
1528                 mdb_free(data, bufsz);
1529                 return (-1);
1530         }
1531 
1532         lower = 0;
1533         upper = size - 1;
1534 
1535         if (tokpos > data[upper]) {
1536                 (void) strlcpy(buf, "position out of range", buflen);
1537                 mdb_free(data, bufsz);
1538                 return (0);
1539         }
1540 
1541         if (tokpos <= data[0]) {
1542                 (void) strlcpy(buf, "line 1", buflen);
1543                 mdb_free(data, bufsz);
1544                 return (0);
1545         }
1546 
1547         while (upper >= 1) {
1548                 ii = (lower + upper) >> 1;
1549                 if (tokpos > data[ii])
1550                         lower = ii + 1;
1551                 else if (tokpos <= data[ii - 1])
1552                         upper = ii - 1;
1553                 else
1554                         break;
1555         }
1556 
1557         (void) mdb_snprintf(buf, buflen, "line %d", ii + 1);
1558         mdb_free(data, bufsz);
1559         return (0);
1560 }
1561 
1562 /*
1563  * Given a SharedFunctionInfo object, prints into bufp a name of the function
1564  * suitable for printing.  This function attempts to infer a name for anonymous
1565  * functions.
1566  */
1567 static int
1568 jsfunc_name(uintptr_t funcinfop, char **bufp, size_t *lenp)
1569 {
1570         uintptr_t ptrp;
1571         char *bufs = *bufp;
1572 
1573         if (read_heap_ptr(&ptrp, funcinfop,
1574             V8_OFF_SHAREDFUNCTIONINFO_NAME) != 0 ||
1575             jsstr_print(ptrp, B_FALSE, bufp, lenp) != 0)
1576                 return (-1);
1577 
1578         if (*bufp != bufs)
1579                 return (0);
1580 
1581         if (read_heap_ptr(&ptrp, funcinfop,
1582             V8_OFF_SHAREDFUNCTIONINFO_INFERRED_NAME) != 0) {
1583                 (void) bsnprintf(bufp, lenp, "<anonymous>");
1584                 return (0);
1585         }
1586 
1587         (void) bsnprintf(bufp, lenp, "<anonymous> (as ");
1588         bufs = *bufp;
1589 
1590         if (jsstr_print(ptrp, B_FALSE, bufp, lenp) != 0)
1591                 return (-1);
1592 
1593         if (*bufp == bufs)
1594                 (void) bsnprintf(bufp, lenp, "<anon>");
1595 
1596         (void) bsnprintf(bufp, lenp, ")");
1597 
1598         return (0);
1599 }
1600 
1601 /*
1602  * JavaScript-level object printing
1603  */
1604 typedef struct jsobj_print {
1605         char **jsop_bufp;
1606         size_t *jsop_lenp;
1607         int jsop_indent;
1608         uint64_t jsop_depth;
1609         boolean_t jsop_printaddr;
1610         int jsop_nprops;
1611 } jsobj_print_t;
1612 
1613 static int jsobj_print_number(uintptr_t, jsobj_print_t *);
1614 static int jsobj_print_oddball(uintptr_t, jsobj_print_t *);
1615 static int jsobj_print_jsobject(uintptr_t, jsobj_print_t *);
1616 static int jsobj_print_jsarray(uintptr_t, jsobj_print_t *);
1617 static int jsobj_print_jsfunction(uintptr_t, jsobj_print_t *);
1618 
1619 static int
1620 jsobj_print(uintptr_t addr, jsobj_print_t *jsop)
1621 {
1622         uint8_t type;
1623         const char *klass;
1624         char **bufp = jsop->jsop_bufp;
1625         size_t *lenp = jsop->jsop_lenp;
1626 
1627         const struct {
1628                 char *name;
1629                 int (*func)(uintptr_t, jsobj_print_t *);
1630         } table[] = {
1631                 { "HeapNumber", jsobj_print_number },
1632                 { "Oddball", jsobj_print_oddball },
1633                 { "JSObject", jsobj_print_jsobject },
1634                 { "JSArray", jsobj_print_jsarray },
1635                 { "JSFunction", jsobj_print_jsfunction },
1636                 { NULL }
1637         }, *ent;
1638 
1639         if (jsop->jsop_printaddr)
1640                 (void) bsnprintf(bufp, lenp, "%p: ", addr);
1641 
1642         if (V8_IS_SMI(addr)) {
1643                 (void) bsnprintf(bufp, lenp, "%d", V8_SMI_VALUE(addr));
1644                 return (0);
1645         }
1646 
1647         if (!V8_IS_HEAPOBJECT(addr)) {
1648                 v8_warn("not a heap object: %p\n", addr);
1649                 return (-1);
1650         }
1651 
1652         if (read_typebyte(&type, addr) != 0)
1653                 return (-1);
1654 
1655         if (V8_TYPE_STRING(type))
1656                 return (jsstr_print(addr, B_FALSE, bufp, lenp));
1657 
1658         klass = enum_lookup_str(v8_types, type, "<unknown>");
1659 
1660         for (ent = &table[0]; ent->name != NULL; ent++) {
1661                 if (strcmp(klass, ent->name) == 0)
1662                         return (ent->func(addr, jsop));
1663         }
1664 
1665         v8_warn("unknown JavaScript object type \"%s\"\n", klass);
1666         return (-1);
1667 }
1668 
1669 static int
1670 jsobj_print_number(uintptr_t addr, jsobj_print_t *jsop)
1671 {
1672         char **bufp = jsop->jsop_bufp;
1673         size_t *lenp = jsop->jsop_lenp;
1674         double numval;
1675 
1676         if (read_heap_double(&numval, addr, V8_OFF_HEAPNUMBER_VALUE) == -1)
1677                 return (-1);
1678 
1679         if (numval == (long long)numval)
1680                 (void) bsnprintf(bufp, lenp, "%lld", (long long)numval);
1681         else
1682                 (void) bsnprintf(bufp, lenp, "%e", numval);
1683 
1684         return (0);
1685 }
1686 
1687 static int
1688 jsobj_print_oddball(uintptr_t addr, jsobj_print_t *jsop)
1689 {
1690         char **bufp = jsop->jsop_bufp;
1691         size_t *lenp = jsop->jsop_lenp;
1692         uintptr_t strptr;
1693 
1694         if (read_heap_ptr(&strptr, addr, V8_OFF_ODDBALL_TO_STRING) != 0)
1695                 return (-1);
1696 
1697         return (jsstr_print(strptr, B_FALSE, bufp, lenp));
1698 }
1699 
1700 static int
1701 jsobj_print_prop(const char *desc, uintptr_t val, void *arg)
1702 {
1703         jsobj_print_t *jsop = arg, descend;
1704         char **bufp = jsop->jsop_bufp;
1705         size_t *lenp = jsop->jsop_lenp;
1706 
1707         if (desc == NULL) {
1708                 jsop->jsop_nprops = -1;
1709                 return (0);
1710         }
1711 
1712         (void) bsnprintf(bufp, lenp, "%s\n%*s%s: ", jsop->jsop_nprops == 0 ?
1713             "{" : "", jsop->jsop_indent + 4, "", desc);
1714 
1715         descend = *jsop;
1716         descend.jsop_depth--;
1717         descend.jsop_indent += 4;
1718 
1719         (void) jsobj_print(val, &descend);
1720         (void) bsnprintf(bufp, lenp, ",");
1721 
1722         jsop->jsop_nprops++;
1723 
1724         return (0);
1725 }
1726 
1727 static int
1728 jsobj_print_jsobject(uintptr_t addr, jsobj_print_t *jsop)
1729 {
1730         char **bufp = jsop->jsop_bufp;
1731         size_t *lenp = jsop->jsop_lenp;
1732 
1733         if (jsop->jsop_depth == 0) {
1734                 (void) bsnprintf(bufp, lenp, "[...]");
1735                 return (0);
1736         }
1737 
1738         jsop->jsop_nprops = 0;
1739 
1740         if (jsobj_properties(addr, jsobj_print_prop, jsop) != 0)
1741                 return (-1);
1742 
1743         if (jsop->jsop_nprops > 0) {
1744                 (void) bsnprintf(bufp, lenp, "\n%*s", jsop->jsop_indent, "");
1745         } else if (jsop->jsop_nprops == 0) {
1746                 (void) bsnprintf(bufp, lenp, "{");
1747         } else {
1748                 (void) bsnprintf(bufp, lenp, "{ /* unknown property */ ");
1749         }
1750 
1751         (void) bsnprintf(bufp, lenp, "}");
1752 
1753         return (0);
1754 }
1755 
1756 static int
1757 jsobj_print_jsarray(uintptr_t addr, jsobj_print_t *jsop)
1758 {
1759         char **bufp = jsop->jsop_bufp;
1760         size_t *lenp = jsop->jsop_lenp;
1761         int indent = jsop->jsop_indent;
1762         jsobj_print_t descend;
1763         uintptr_t ptr;
1764         uintptr_t *elts;
1765         size_t ii, len;
1766 
1767         if (jsop->jsop_depth == 0) {
1768                 (void) bsnprintf(bufp, lenp, "[...]");
1769                 return (0);
1770         }
1771 
1772         if (read_heap_ptr(&ptr, addr, V8_OFF_JSOBJECT_ELEMENTS) != 0 ||
1773             read_heap_array(ptr, &elts, &len, UM_GC) != 0)
1774                 return (-1);
1775 
1776         if (len == 0) {
1777                 (void) bsnprintf(bufp, lenp, "[]");
1778                 return (0);
1779         }
1780 
1781         descend = *jsop;
1782         descend.jsop_depth--;
1783         descend.jsop_indent += 4;
1784 
1785         if (len == 1) {
1786                 (void) bsnprintf(bufp, lenp, "[ ");
1787                 (void) jsobj_print(elts[0], &descend);
1788                 (void) bsnprintf(bufp, lenp, " ]");
1789                 return (0);
1790         }
1791 
1792         (void) bsnprintf(bufp, lenp, "[\n");
1793 
1794         for (ii = 0; ii < len; ii++) {
1795                 (void) bsnprintf(bufp, lenp, "%*s", indent + 4, "");
1796                 (void) jsobj_print(elts[ii], &descend);
1797                 (void) bsnprintf(bufp, lenp, ",\n");
1798         }
1799 
1800         (void) bsnprintf(bufp, lenp, "%*s", indent, "");
1801         (void) bsnprintf(bufp, lenp, "]");
1802 
1803         return (0);
1804 }
1805 
1806 static int
1807 jsobj_print_jsfunction(uintptr_t addr, jsobj_print_t *jsop)
1808 {
1809         char **bufp = jsop->jsop_bufp;
1810         size_t *lenp = jsop->jsop_lenp;
1811         uintptr_t shared;
1812 
1813         if (read_heap_ptr(&shared, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
1814                 return (-1);
1815 
1816         (void) bsnprintf(bufp, lenp, "function ");
1817         return (jsfunc_name(shared, bufp, lenp) != 0);
1818 }
1819 
1820 /*
1821  * dcmd implementations
1822  */
1823 
1824 /* ARGSUSED */
1825 static int
1826 dcmd_v8classes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1827 {
1828         v8_class_t *clp;
1829 
1830         for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
1831                 mdb_printf("%s\n", clp->v8c_name);
1832 
1833         return (DCMD_OK);
1834 }
1835 
1836 static int
1837 do_v8code(uintptr_t addr, boolean_t opt_d)
1838 {
1839         uintptr_t instrlen;
1840         ssize_t instroff = V8_OFF_CODE_INSTRUCTION_START;
1841 
1842         if (read_heap_ptr(&instrlen, addr, V8_OFF_CODE_INSTRUCTION_SIZE) != 0)
1843                 return (DCMD_ERR);
1844 
1845         mdb_printf("code: %p\n", addr);
1846         mdb_printf("instructions: [%p, %p)\n", addr + instroff,
1847             addr + instroff + instrlen);
1848 
1849         if (!opt_d)
1850                 return (DCMD_OK);
1851 
1852         mdb_set_dot(addr + instroff);
1853 
1854         do {
1855                 (void) mdb_inc_indent(8); /* gets reset by mdb_eval() */
1856 
1857                 /*
1858                  * This is absolutely awful. We want to disassemble the above
1859                  * range of instructions.  Because we don't know how many there
1860                  * are, we can't use "::dis".  We resort to evaluating "./i",
1861                  * but then we need to advance "." by the size of the
1862                  * instruction just printed.  The only way to do that is by
1863                  * printing out "+", but we don't want that to show up, so we
1864                  * redirect it to /dev/null.
1865                  */
1866                 if (mdb_eval("/i") != 0 ||
1867                     mdb_eval("+=p ! cat > /dev/null") != 0) {
1868                         (void) mdb_dec_indent(8);
1869                         v8_warn("failed to disassemble at %p", mdb_get_dot());
1870                         return (DCMD_ERR);
1871                 }
1872         } while (mdb_get_dot() < addr + instroff + instrlen);
1873 
1874         (void) mdb_dec_indent(8);
1875         return (DCMD_OK);
1876 }
1877 
1878 /* ARGSUSED */
1879 static int
1880 dcmd_v8code(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1881 {
1882         boolean_t opt_d = B_FALSE;
1883 
1884         if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
1885             NULL) != argc)
1886                 return (DCMD_USAGE);
1887 
1888         return (do_v8code(addr, opt_d));
1889 }
1890 
1891 /* ARGSUSED */
1892 static int
1893 dcmd_v8function(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1894 {
1895         uint8_t type;
1896         uintptr_t funcinfop, scriptp, lendsp, tokpos, namep, codep;
1897         char *bufp;
1898         uint_t len;
1899         boolean_t opt_d = B_FALSE;
1900         char buf[512];
1901 
1902         if (mdb_getopts(argc, argv, 'd', MDB_OPT_SETBITS, B_TRUE, &opt_d,
1903             NULL) != argc)
1904                 return (DCMD_USAGE);
1905 
1906         if (read_typebyte(&type, addr) != 0)
1907                 return (DCMD_ERR);
1908 
1909         if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0) {
1910                 v8_warn("%p is not an instance of JSFunction\n", addr);
1911                 return (DCMD_ERR);
1912         }
1913 
1914         if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0 ||
1915             read_heap_ptr(&tokpos, funcinfop,
1916             V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0 ||
1917             read_heap_ptr(&scriptp, funcinfop,
1918             V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0 ||
1919             read_heap_ptr(&namep, scriptp, V8_OFF_SCRIPT_NAME) != 0 ||
1920             read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
1921                 return (DCMD_ERR);
1922 
1923         bufp = buf;
1924         len = sizeof (buf);
1925         if (jsfunc_name(funcinfop, &bufp, &len) != 0)
1926                 return (DCMD_ERR);
1927 
1928         mdb_printf("%p: JSFunction: %s\n", addr, buf);
1929 
1930         bufp = buf;
1931         len = sizeof (buf);
1932         mdb_printf("defined at ");
1933 
1934         if (jsstr_print(namep, B_FALSE, &bufp, &len) == 0)
1935                 mdb_printf("%s ", buf);
1936 
1937         if (jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf)) == 0)
1938                 mdb_printf("%s", buf);
1939 
1940         mdb_printf("\n");
1941 
1942         if (read_heap_ptr(&codep,
1943             funcinfop, V8_OFF_SHAREDFUNCTIONINFO_CODE) != 0)
1944                 return (DCMD_ERR);
1945 
1946         return (do_v8code(codep, opt_d));
1947 }
1948 
1949 /* ARGSUSED */
1950 static int
1951 dcmd_v8frametypes(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1952 {
1953         enum_print(v8_frametypes);
1954         return (DCMD_OK);
1955 }
1956 
1957 static void
1958 dcmd_v8print_help(void)
1959 {
1960         mdb_printf(
1961             "Prints out \".\" (a V8 heap object) as an instance of its C++\n"
1962             "class.  With no arguments, the appropriate class is detected\n"
1963             "automatically.  The 'class' argument overrides this to print an\n"
1964             "object as an instance of the given class.  The list of known\n"
1965             "classes can be viewed with ::jsclasses.");
1966 }
1967 
1968 /* ARGSUSED */
1969 static int
1970 dcmd_v8print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1971 {
1972         const char *rqclass;
1973         v8_class_t *clp;
1974         char *bufp;
1975         size_t len;
1976         uint8_t type;
1977         char buf[256];
1978 
1979         if (argc < 1) {
1980                 /*
1981                  * If no type was specified, determine it automatically.
1982                  */
1983                 bufp = buf;
1984                 len = sizeof (buf);
1985                 if (obj_jstype(addr, &bufp, &len, &type) != 0)
1986                         return (DCMD_ERR);
1987 
1988                 if (type == 0) {
1989                         /* For SMI or Failure, just print out the type. */
1990                         mdb_printf("%s\n", buf);
1991                         return (DCMD_OK);
1992                 }
1993 
1994                 if ((rqclass = enum_lookup_str(v8_types, type, NULL)) == NULL) {
1995                         v8_warn("object has unknown type\n");
1996                         return (DCMD_ERR);
1997                 }
1998         } else {
1999                 if (argv[0].a_type != MDB_TYPE_STRING)
2000                         return (DCMD_USAGE);
2001 
2002                 rqclass = argv[0].a_un.a_str;
2003         }
2004 
2005         for (clp = v8_classes; clp != NULL; clp = clp->v8c_next) {
2006                 if (strcmp(rqclass, clp->v8c_name) == 0)
2007                         break;
2008         }
2009 
2010         if (clp == NULL) {
2011                 v8_warn("unknown class '%s'\n", rqclass);
2012                 return (DCMD_USAGE);
2013         }
2014 
2015         return (obj_print_class(addr, clp));
2016 }
2017 
2018 /* ARGSUSED */
2019 static int
2020 dcmd_v8type(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2021 {
2022         char buf[64];
2023         char *bufp = buf;
2024         size_t len = sizeof (buf);
2025 
2026         if (obj_jstype(addr, &bufp, &len, NULL) != 0)
2027                 return (DCMD_ERR);
2028 
2029         mdb_printf("0x%p: %s\n", addr, buf);
2030         return (DCMD_OK);
2031 }
2032 
2033 /* ARGSUSED */
2034 static int
2035 dcmd_v8types(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2036 {
2037         enum_print(v8_types);
2038         return (DCMD_OK);
2039 }
2040 
2041 static int
2042 load_current_context(uintptr_t *fpp, uintptr_t *raddrp)
2043 {
2044         mdb_reg_t regfp, regip;
2045 
2046         if (mdb_getareg(1, "ebp", &regfp) != 0 ||
2047             mdb_getareg(1, "eip", &regip) != 0) {
2048                 v8_warn("failed to load current context");
2049                 return (-1);
2050         }
2051 
2052         if (fpp != NULL)
2053                 *fpp = (uintptr_t)regfp;
2054 
2055         if (raddrp != NULL)
2056                 *raddrp = (uintptr_t)regip;
2057 
2058         return (0);
2059 }
2060 
2061 static int
2062 do_jsframe_special(uintptr_t fptr, uintptr_t raddr)
2063 {
2064         uintptr_t ftype;
2065         const char *ftypename;
2066 
2067         /*
2068          * First see if this looks like a native frame rather than a JavaScript
2069          * frame.  We check this by asking MDB to print the return address
2070          * symbolically.  If that works, we assume this was NOT a V8 frame,
2071          * since those are never in the symbol table.
2072          */
2073         if (mdb_snprintf(NULL, 0, "%A", raddr) > 1) {
2074                 mdb_printf("%p %a\n", fptr, raddr);
2075                 return (0);
2076         }
2077 
2078         /*
2079          * Figure out what kind of frame this is using the same algorithm as
2080          * V8's ComputeType function.  First, look for an ArgumentsAdaptorFrame.
2081          */
2082         if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_CONTEXT) != -1 &&
2083             V8_IS_SMI(ftype) &&
2084             (ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
2085             NULL)) != NULL && strstr(ftypename, "ArgumentsAdaptor") != NULL) {
2086                 mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
2087                 return (0);
2088         }
2089 
2090         /*
2091          * Other special frame types are indicated by a marker.
2092          */
2093         if (mdb_vread(&ftype, sizeof (ftype), fptr + V8_OFF_FP_MARKER) != -1 &&
2094             V8_IS_SMI(ftype)) {
2095                 ftypename = enum_lookup_str(v8_frametypes, V8_SMI_VALUE(ftype),
2096                     NULL);
2097 
2098                 if (ftypename != NULL)
2099                         mdb_printf("%p %a <%s>\n", fptr, raddr, ftypename);
2100                 else
2101                         mdb_printf("%p %a\n", fptr, raddr);
2102 
2103                 return (0);
2104         }
2105 
2106         return (-1);
2107 }
2108 
2109 static int
2110 do_jsframe(uintptr_t fptr, uintptr_t raddr, boolean_t verbose)
2111 {
2112         uintptr_t funcp, funcinfop, tokpos, scriptp, lendsp, ptrp;
2113         uintptr_t ii, nargs;
2114         const char *typename;
2115         char *bufp;
2116         size_t len;
2117         uint8_t type;
2118         char buf[256];
2119 
2120         /*
2121          * Check for non-JavaScript frames first.
2122          */
2123         if (do_jsframe_special(fptr, raddr) == 0)
2124                 return (DCMD_OK);
2125 
2126         /*
2127          * At this point we assume we're looking at a JavaScript frame.  As with
2128          * native frames, fish the address out of the parent frame.
2129          */
2130         if (mdb_vread(&funcp, sizeof (funcp),
2131             fptr + V8_OFF_FP_FUNCTION) == -1) {
2132                 v8_warn("failed to read stack at %p",
2133                     fptr + V8_OFF_FP_FUNCTION);
2134                 return (DCMD_ERR);
2135         }
2136 
2137         /*
2138          * Check if this thing is really a JSFunction at all. For some frames,
2139          * it's a Code object, presumably indicating some internal frame.
2140          */
2141         v8_silent++;
2142 
2143         if (read_typebyte(&type, funcp) != 0 ||
2144             (typename = enum_lookup_str(v8_types, type, NULL)) == NULL) {
2145                 v8_silent--;
2146                 mdb_printf("%p %a\n", fptr, raddr);
2147                 return (DCMD_OK);
2148         }
2149 
2150         v8_silent--;
2151 
2152         if (strcmp("Code", typename) == 0) {
2153                 mdb_printf("%p %a internal (Code: %p)\n", fptr, raddr, funcp);
2154                 return (DCMD_OK);
2155         }
2156 
2157         if (strcmp("JSFunction", typename) != 0) {
2158                 mdb_printf("%p %a unknown (%s: %p)", fptr, raddr, typename,
2159                     funcp);
2160                 return (DCMD_OK);
2161         }
2162 
2163         if (read_heap_ptr(&funcinfop, funcp, V8_OFF_JSFUNCTION_SHARED) != 0)
2164                 return (DCMD_ERR);
2165 
2166         bufp = buf;
2167         len = sizeof (buf);
2168         if (jsfunc_name(funcinfop, &bufp, &len) != 0)
2169                 return (DCMD_ERR);
2170 
2171         mdb_printf("%p %a %s (%p)\n", fptr, raddr, buf, funcp);
2172 
2173         if (!verbose)
2174                 return (DCMD_OK);
2175 
2176         /*
2177          * Although the token position is technically an SMI, we're going to
2178          * byte-compare it to other SMI values so we don't want decode it here.
2179          */
2180         if (read_heap_ptr(&tokpos, funcinfop,
2181             V8_OFF_SHAREDFUNCTIONINFO_FUNCTION_TOKEN_POSITION) != 0)
2182                 return (DCMD_ERR);
2183 
2184         if (read_heap_ptr(&scriptp, funcinfop,
2185             V8_OFF_SHAREDFUNCTIONINFO_SCRIPT) != 0)
2186                 return (DCMD_ERR);
2187 
2188         if (read_heap_ptr(&ptrp, scriptp, V8_OFF_SCRIPT_NAME) != 0)
2189                 return (DCMD_ERR);
2190 
2191         bufp = buf;
2192         len = sizeof (buf);
2193         (void) jsstr_print(ptrp, B_FALSE, &bufp, &len);
2194 
2195         (void) mdb_inc_indent(4);
2196         mdb_printf("file: %s\n", buf);
2197 
2198         if (read_heap_ptr(&lendsp, scriptp, V8_OFF_SCRIPT_LINE_ENDS) != 0)
2199                 return (DCMD_ERR);
2200 
2201         (void) jsfunc_lineno(lendsp, tokpos, buf, sizeof (buf));
2202 
2203         mdb_printf("posn: %s\n", buf);
2204 
2205         if (read_heap_smi(&nargs, funcinfop,
2206             V8_OFF_SHAREDFUNCTIONINFO_LENGTH) == 0) {
2207                 for (ii = 0; ii < nargs; ii++) {
2208                         uintptr_t argptr;
2209 
2210                         if (mdb_vread(&argptr, sizeof (argptr),
2211                             fptr + V8_OFF_FP_ARGS + (nargs - ii - 1) *
2212                             sizeof (uintptr_t)) == -1)
2213                                 continue;
2214 
2215                         bufp = buf;
2216                         len = sizeof (buf);
2217                         (void) obj_jstype(argptr, &bufp, &len, NULL);
2218 
2219                         mdb_printf("arg%d: %p (%s)\n", (ii + 1), argptr, buf);
2220                 }
2221         }
2222 
2223         (void) mdb_dec_indent(4);
2224 
2225         return (DCMD_OK);
2226 }
2227 
2228 typedef struct findjsobjects_prop {
2229         struct findjsobjects_prop *fjsp_next;
2230         char fjsp_desc[1];
2231 } findjsobjects_prop_t;
2232 
2233 typedef struct findjsobjects_instance {
2234         uintptr_t fjsi_addr;
2235         struct findjsobjects_instance *fjsi_next;
2236 } findjsobjects_instance_t;
2237 
2238 typedef struct findjsobjects_obj {
2239         findjsobjects_prop_t *fjso_props;
2240         findjsobjects_prop_t *fjso_last;
2241         int fjso_nprops;
2242         findjsobjects_instance_t fjso_instances;
2243         int fjso_ninstances;
2244         avl_node_t fjso_node;
2245         struct findjsobjects_obj *fjso_next;
2246 } findjsobjects_obj_t;
2247 
2248 typedef struct findjsobjects_stats {
2249         int fjss_heapobjs;
2250         int fjss_jsobjs;
2251         int fjss_objects;
2252         int fjss_uniques;
2253 } findjsobjects_stats_t;
2254 
2255 typedef struct findjsobjects_state {
2256         uintptr_t fjs_addr;
2257         uintptr_t fjs_size;
2258         boolean_t fjs_verbose;
2259         boolean_t fjs_brk;
2260         boolean_t fjs_initialized;
2261         uintptr_t fjs_referent;
2262         boolean_t fjs_referred;
2263         avl_tree_t fjs_tree;
2264         findjsobjects_obj_t *fjs_current;
2265         findjsobjects_obj_t *fjs_objects;
2266         findjsobjects_stats_t fjs_stats;
2267 } findjsobjects_state_t;
2268 
2269 findjsobjects_obj_t *
2270 findjsobjects_alloc(uintptr_t addr)
2271 {
2272         findjsobjects_obj_t *obj;
2273 
2274         obj = mdb_zalloc(sizeof (findjsobjects_obj_t), UM_SLEEP);
2275         obj->fjso_instances.fjsi_addr = addr;
2276         obj->fjso_ninstances = 1;
2277 
2278         return (obj);
2279 }
2280 
2281 void
2282 findjsobjects_free(findjsobjects_obj_t *obj)
2283 {
2284         findjsobjects_prop_t *prop, *next;
2285 
2286         for (prop = obj->fjso_props; prop != NULL; prop = next) {
2287                 next = prop->fjsp_next;
2288                 mdb_free(prop, sizeof (findjsobjects_prop_t) +
2289                     strlen(prop->fjsp_desc));
2290         }
2291 
2292         mdb_free(obj, sizeof (findjsobjects_obj_t));
2293 }
2294 
2295 int
2296 findjsobjects_cmp(findjsobjects_obj_t *lhs, findjsobjects_obj_t *rhs)
2297 {
2298         findjsobjects_prop_t *lprop, *rprop;
2299         int rv;
2300 
2301         lprop = lhs->fjso_props;
2302         rprop = rhs->fjso_props;
2303 
2304         for (;;) {
2305                 if (lprop == NULL || rprop == NULL)
2306                         return (lprop != NULL ? 1 : rprop != NULL ? -1 : 0);
2307 
2308                 if ((rv = strcmp(lprop->fjsp_desc, rprop->fjsp_desc)) != 0)
2309                         return (rv > 0 ? 1 : -1);
2310 
2311                 lprop = lprop->fjsp_next;
2312                 rprop = rprop->fjsp_next;
2313         }
2314 }
2315 
2316 int
2317 findjsobjects_cmp_ninstances(const void *l, const void *r)
2318 {
2319         findjsobjects_obj_t *lhs = *((findjsobjects_obj_t **)l);
2320         findjsobjects_obj_t *rhs = *((findjsobjects_obj_t **)r);
2321 
2322         if (lhs->fjso_ninstances < rhs->fjso_ninstances)
2323                 return (-1);
2324 
2325         if (lhs->fjso_ninstances > rhs->fjso_ninstances)
2326                 return (1);
2327 
2328         if (lhs->fjso_nprops < rhs->fjso_nprops)
2329                 return (-1);
2330 
2331         if (lhs->fjso_nprops > rhs->fjso_nprops)
2332                 return (1);
2333 
2334         return (0);
2335 }
2336 
2337 /*ARGSUSED*/
2338 int
2339 findjsobjects_prop(const char *desc, uintptr_t val, void *arg)
2340 {
2341         findjsobjects_state_t *fjs = arg;
2342         findjsobjects_obj_t *current = fjs->fjs_current;
2343         findjsobjects_prop_t *prop;
2344 
2345         if (desc == NULL)
2346                 desc = "<unknown>";
2347 
2348         prop = mdb_zalloc(sizeof (findjsobjects_prop_t) +
2349             strlen(desc), UM_SLEEP);
2350 
2351         strcpy(prop->fjsp_desc, desc);
2352 
2353         if (current->fjso_last != NULL) {
2354                 current->fjso_last->fjsp_next = prop;
2355         } else {
2356                 current->fjso_props = prop;
2357         }
2358 
2359         current->fjso_last = prop;
2360         current->fjso_nprops++;
2361 
2362         return (0);
2363 }
2364 
2365 int
2366 findjsobjects_range(findjsobjects_state_t *fjs, uintptr_t addr, uintptr_t size)
2367 {
2368         uintptr_t limit;
2369         findjsobjects_stats_t *stats = &fjs->fjs_stats;
2370         uint8_t type;
2371         int jsobject = V8_TYPE_JSOBJECT;
2372 
2373 
2374         for (limit = addr + size; addr < limit; addr++) {
2375                 findjsobjects_instance_t *inst;
2376                 findjsobjects_obj_t *obj;
2377                 avl_index_t where;
2378 
2379                 if (V8_IS_SMI(addr))
2380                         continue;
2381 
2382                 if (!V8_IS_HEAPOBJECT(addr))
2383                         continue;
2384 
2385                 stats->fjss_heapobjs++;
2386 
2387                 if (read_typebyte(&type, addr) == -1)
2388                         continue;
2389 
2390                 if (type != jsobject)
2391                         continue;
2392 
2393                 stats->fjss_jsobjs++;
2394 
2395                 fjs->fjs_current = findjsobjects_alloc(addr);
2396 
2397                 if (jsobj_properties(addr, findjsobjects_prop, fjs) != 0) {
2398                         findjsobjects_free(fjs->fjs_current);
2399                         fjs->fjs_current = NULL;
2400                         continue;
2401                 }
2402 
2403                 /*
2404                  * Now determine if we already have an object matching our
2405                  * properties.  If we don't, we'll add our new object; if we
2406                  * do we'll merely enqeuue our instance.
2407                  */
2408                 obj = avl_find(&fjs->fjs_tree, fjs->fjs_current, &where);
2409                 stats->fjss_objects++;
2410 
2411                 if (obj == NULL) {
2412                         avl_add(&fjs->fjs_tree, fjs->fjs_current);
2413                         fjs->fjs_current->fjso_next = fjs->fjs_objects;
2414                         fjs->fjs_objects = fjs->fjs_current;
2415                         fjs->fjs_current = NULL;
2416                         stats->fjss_uniques++;
2417                         continue;
2418                 }
2419 
2420                 findjsobjects_free(fjs->fjs_current);
2421                 fjs->fjs_current = NULL;
2422 
2423                 inst = mdb_alloc(sizeof (findjsobjects_instance_t), UM_SLEEP);
2424                 inst->fjsi_addr = addr;
2425                 inst->fjsi_next = obj->fjso_instances.fjsi_next;
2426                 obj->fjso_instances.fjsi_next = inst;
2427                 obj->fjso_ninstances++;
2428         }
2429 
2430         return (0);
2431 }
2432 
2433 static int
2434 findjsobjects_mapping(findjsobjects_state_t *fjs, const prmap_t *pmp,
2435     const char *name)
2436 {
2437         if (name != NULL && !(fjs->fjs_brk && (pmp->pr_mflags & MA_BREAK)))
2438                 return (0);
2439 
2440         if (fjs->fjs_addr != NULL && (fjs->fjs_addr < pmp->pr_vaddr ||
2441             fjs->fjs_addr >= pmp->pr_vaddr + pmp->pr_size))
2442                 return (0);
2443 
2444         return (findjsobjects_range(fjs, pmp->pr_vaddr, pmp->pr_size));
2445 }
2446 
2447 static int
2448 findjsobjects_references_prop(const char *desc, uintptr_t val, void *arg)
2449 {
2450         findjsobjects_state_t *fjs = arg;
2451 
2452         if (val == fjs->fjs_referent) {
2453                 mdb_printf("%p referred to by %p.%s\n", fjs->fjs_referent,
2454                     fjs->fjs_addr, desc);
2455                 fjs->fjs_referred = B_TRUE;
2456                 return (0);
2457         }
2458 
2459         return (0);
2460 }
2461 
2462 static void
2463 findjsobjects_references(findjsobjects_state_t *fjs, uintptr_t addr)
2464 {
2465         findjsobjects_instance_t *inst;
2466         findjsobjects_obj_t *obj;
2467 
2468         fjs->fjs_referent = addr;
2469         fjs->fjs_referred = B_FALSE;
2470 
2471         v8_silent++;
2472 
2473         for (obj = fjs->fjs_objects; obj != NULL; obj = obj->fjso_next) {
2474                 for (inst = &obj->fjso_instances;
2475                     inst != NULL; inst = inst->fjsi_next) {
2476                         fjs->fjs_addr = inst->fjsi_addr;
2477                         (void) jsobj_properties(inst->fjsi_addr,
2478                             findjsobjects_references_prop, fjs);
2479                 }
2480         }
2481 
2482         v8_silent--;
2483 
2484         if (!fjs->fjs_referred)
2485                 mdb_printf("%p is not referred to by a known object.\n", addr);
2486 
2487         fjs->fjs_addr = NULL;
2488 }
2489 
2490 static char *
2491 findjsobjects_constructor(findjsobjects_obj_t *obj)
2492 {
2493         static char buf[80];
2494         char *bufp = buf, *rval = NULL;
2495         unsigned int len = sizeof (buf);
2496         uintptr_t map, funcinfop;
2497         uintptr_t addr = obj->fjso_instances.fjsi_addr;
2498         uint8_t type;
2499 
2500         v8_silent++;
2501 
2502         if (read_heap_ptr(&map, addr, V8_OFF_HEAPOBJECT_MAP) != 0 ||
2503             read_heap_ptr(&addr, map, V8_OFF_MAP_CONSTRUCTOR) != 0)
2504                 goto out;
2505 
2506         if (read_typebyte(&type, addr) != 0)
2507                 goto out;
2508 
2509         if (strcmp(enum_lookup_str(v8_types, type, ""), "JSFunction") != 0)
2510                 goto out;
2511 
2512         if (read_heap_ptr(&funcinfop, addr, V8_OFF_JSFUNCTION_SHARED) != 0)
2513                 goto out;
2514 
2515         if (jsfunc_name(funcinfop, &bufp, &len) != 0)
2516                 goto out;
2517 
2518         rval = buf;
2519 out:
2520         v8_silent--;
2521 
2522         return (rval);
2523 }
2524 
2525 static void
2526 findjsobjects_print(findjsobjects_obj_t *obj)
2527 {
2528         int col = 17 + (sizeof (uintptr_t) * 2) + strlen("..."), len;
2529         uintptr_t addr = obj->fjso_instances.fjsi_addr;
2530         char *buf = findjsobjects_constructor(obj);
2531         findjsobjects_prop_t *prop;
2532 
2533         mdb_printf("%?p %8d %6d ",
2534             addr, obj->fjso_ninstances, obj->fjso_nprops);
2535 
2536         if (buf != NULL) {
2537                 mdb_printf("%s: ", buf);
2538                 col += strlen(buf) + 2;
2539         }
2540 
2541         for (prop = obj->fjso_props; prop != NULL; prop = prop->fjsp_next) {
2542                 if (col + (len = strlen(prop->fjsp_desc) + 2) < 80) {
2543                         mdb_printf("%s%s", prop->fjsp_desc,
2544                             prop->fjsp_next != NULL ? ", " : "");
2545                         col += len;
2546                 } else {
2547                         mdb_printf("...");
2548                         break;
2549                 }
2550         }
2551 
2552         mdb_printf("\n", col);
2553 }
2554 
2555 static void
2556 dcmd_findjsobjects_help(void)
2557 {
2558         mdb_printf("%s\n\n",
2559 "Finds all JavaScript objects in the V8 heap via brute force iteration over\n"
2560 "all mapped anonymous memory.  (This can take up to several minutes on large\n"
2561 "dumps.)  The output consists of representative objects, the number of\n"
2562 "instances of that object and the number of properties on the object --\n"
2563 "followed by the constructor and first few properties of the objects.  Once\n"
2564 "run, subsequent calls to ::findjsobjects use cached data.  If provided an\n"
2565 "address (and in the absence of -r, described below), ::findjsobjects treats\n"
2566 "the address as that of a representative object, and emits all instances of\n"
2567 "that object (that is, all objects that have a matching property signature).");
2568 
2569         mdb_dec_indent(2);
2570         mdb_printf("%<b>OPTIONS%</b>\n");
2571         mdb_inc_indent(2);
2572 
2573         mdb_printf("%s\n",
2574 "  -b       Include the heap denoted by the brk(2) (normally excluded)\n"
2575 "  -c cons  Display representative objects with the specified constructor\n"
2576 "  -p prop  Display representative objects that have the specified property\n"
2577 "  -r       Find references to the specified object\n"
2578 "  -v       Provide verbose statistics\n");
2579 }
2580 
2581 static int
2582 dcmd_findjsobjects(uintptr_t addr,
2583     uint_t flags, int argc, const mdb_arg_t *argv)
2584 {
2585         static findjsobjects_state_t fjs;
2586         static findjsobjects_stats_t *stats = &fjs.fjs_stats;
2587         findjsobjects_obj_t *obj;
2588         findjsobjects_prop_t *prop;
2589         struct ps_prochandle *Pr;
2590         boolean_t references = B_FALSE;
2591         const char *propname = NULL;
2592         const char *constructor = NULL;
2593 
2594         fjs.fjs_verbose = B_FALSE;
2595         fjs.fjs_brk = B_FALSE;
2596 
2597         if (mdb_getopts(argc, argv,
2598             'b', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_brk,
2599             'c', MDB_OPT_STR, &constructor,
2600             'p', MDB_OPT_STR, &propname,
2601             'r', MDB_OPT_SETBITS, B_TRUE, &references,
2602             'v', MDB_OPT_SETBITS, B_TRUE, &fjs.fjs_verbose,
2603             NULL) != argc)
2604                 return (DCMD_USAGE);
2605 
2606         if (!fjs.fjs_initialized) {
2607                 avl_create(&fjs.fjs_tree,
2608                     (int(*)(const void *, const void *))findjsobjects_cmp,
2609                     sizeof (findjsobjects_obj_t),
2610                     offsetof(findjsobjects_obj_t, fjso_node));
2611                 fjs.fjs_initialized = B_TRUE;
2612         }
2613 
2614         if (avl_is_empty(&fjs.fjs_tree)) {
2615                 findjsobjects_obj_t **sorted;
2616                 int nobjs, i;
2617                 hrtime_t start = gethrtime();
2618 
2619                 if (mdb_get_xdata("pshandle", &Pr, sizeof (Pr)) == -1) {
2620                         mdb_warn("couldn't read pshandle xdata");
2621                         return (DCMD_ERR);
2622                 }
2623 
2624                 v8_silent++;
2625 
2626                 if (Pmapping_iter(Pr,
2627                     (proc_map_f *)findjsobjects_mapping, &fjs) != 0) {
2628                         v8_silent--;
2629                         return (DCMD_ERR);
2630                 }
2631 
2632                 nobjs = avl_numnodes(&fjs.fjs_tree);
2633 
2634                 /*
2635                  * We have the objects -- now sort them.
2636                  */
2637                 sorted = mdb_alloc(nobjs * sizeof (void *), UM_GC);
2638 
2639                 for (obj = fjs.fjs_objects, i = 0; obj != NULL;
2640                     obj = obj->fjso_next, i++) {
2641                         sorted[i] = obj;
2642                 }
2643 
2644                 qsort(sorted, avl_numnodes(&fjs.fjs_tree), sizeof (void *),
2645                     findjsobjects_cmp_ninstances);
2646 
2647                 for (i = 1, fjs.fjs_objects = sorted[0]; i < nobjs; i++)
2648                         sorted[i - 1]->fjso_next = sorted[i];
2649 
2650                 sorted[nobjs - 1]->fjso_next = NULL;
2651 
2652                 v8_silent--;
2653 
2654                 if (fjs.fjs_verbose) {
2655                         const char *f = "findjsobjects: %30s => %d\n";
2656                         int elapsed = (int)((gethrtime() - start) / NANOSEC);
2657 
2658                         mdb_printf(f, "elapsed time (seconds)", elapsed);
2659                         mdb_printf(f, "heap objects", stats->fjss_heapobjs);
2660                         mdb_printf(f, "JavaScript objects", stats->fjss_jsobjs);
2661                         mdb_printf(f, "processed objects", stats->fjss_objects);
2662                         mdb_printf(f, "unique objects", stats->fjss_uniques);
2663                 }
2664         }
2665 
2666         if (propname != NULL) {
2667                 if (flags & DCMD_ADDRSPEC) {
2668                         mdb_warn("cannot specify an object when "
2669                             "specifying a property name\n");
2670                         return (DCMD_ERR);
2671                 }
2672 
2673                 if (constructor != NULL) {
2674                         mdb_warn("cannot specify both a property name "
2675                             "and a constructor\n");
2676                         return (DCMD_ERR);
2677                 }
2678 
2679                 for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
2680                         for (prop = obj->fjso_props; prop != NULL;
2681                             prop = prop->fjsp_next) {
2682                                 if (strcmp(prop->fjsp_desc, propname) == 0)
2683                                         break;
2684                         }
2685 
2686                         if (prop == NULL)
2687                                 continue;
2688 
2689                         mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
2690                 }
2691 
2692                 return (DCMD_OK);
2693         }
2694 
2695         if (constructor != NULL) {
2696                 if (flags & DCMD_ADDRSPEC) {
2697                         mdb_warn("cannot specify an object when "
2698                             "specifying a constructor\n");
2699                         return (DCMD_ERR);
2700                 }
2701 
2702                 for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
2703                         char *cons = findjsobjects_constructor(obj);
2704 
2705                         if (cons == NULL || strcmp(constructor, cons) != 0)
2706                                 continue;
2707 
2708                         mdb_printf("%p\n", obj->fjso_instances.fjsi_addr);
2709                 }
2710 
2711                 return (DCMD_OK);
2712         }
2713 
2714         if (references && !(flags & DCMD_ADDRSPEC)) {
2715                 mdb_warn("must specify an object to find references\n");
2716                 return (DCMD_ERR);
2717         }
2718 
2719         if (flags & DCMD_ADDRSPEC) {
2720                 /*
2721                  * If we've been passed an address, we're either looking for
2722                  * similar objects or for references (if -r has been set).
2723                  */
2724                 if (references) {
2725                         findjsobjects_references(&fjs, addr);
2726                         return (DCMD_OK);
2727                 }
2728 
2729                 for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next) {
2730                         findjsobjects_instance_t *inst, *h;
2731 
2732                         h = &obj->fjso_instances;
2733 
2734                         for (inst = h; inst != NULL; inst = inst->fjsi_next) {
2735                                 if (inst->fjsi_addr == addr)
2736                                         break;
2737                         }
2738 
2739                         if (inst == NULL)
2740                                 continue;
2741 
2742                         for (inst = h; inst != NULL; inst = inst->fjsi_next)
2743                                 mdb_printf("%p\n", inst->fjsi_addr);
2744 
2745                         return (DCMD_OK);
2746                 }
2747 
2748                 mdb_warn("%p is not a valid object\n", addr);
2749                 return (DCMD_ERR);
2750         }
2751 
2752         mdb_printf("%-?s %8s %6s %s\n", "OBJECT",
2753             "#OBJECTS", "#PROPS", "CONSTRUCTOR: PROPS");
2754 
2755         for (obj = fjs.fjs_objects; obj != NULL; obj = obj->fjso_next)
2756                 findjsobjects_print(obj);
2757 
2758         return (DCMD_OK);
2759 }
2760 
2761 /* ARGSUSED */
2762 static int
2763 dcmd_jsframe(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2764 {
2765         uintptr_t fptr, raddr;
2766         boolean_t opt_v = B_FALSE, opt_i = B_FALSE;
2767 
2768         if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
2769             'i', MDB_OPT_SETBITS, B_TRUE, &opt_i, NULL) != argc)
2770                 return (DCMD_USAGE);
2771 
2772         /*
2773          * As with $C, we assume we are given a *pointer* to the frame pointer
2774          * for a frame, rather than the actual frame pointer for the frame of
2775          * interest. This is needed to show the instruction pointer, which is
2776          * actually stored with the next frame.  For debugging, this can be
2777          * overridden with the "-i" option (for "immediate").
2778          */
2779         if (opt_i)
2780                 return (do_jsframe(addr, 0, opt_v));
2781 
2782         if (mdb_vread(&raddr, sizeof (raddr),
2783             addr + sizeof (uintptr_t)) == -1) {
2784                 mdb_warn("failed to read return address from %p",
2785                     addr + sizeof (uintptr_t));
2786                 return (DCMD_ERR);
2787         }
2788 
2789         if (mdb_vread(&fptr, sizeof (fptr), addr) == -1) {
2790                 mdb_warn("failed to read frame pointer from %p", addr);
2791                 return (DCMD_ERR);
2792         }
2793 
2794         if (fptr == NULL)
2795                 return (DCMD_OK);
2796 
2797         return (do_jsframe(fptr, raddr, opt_v));
2798 }
2799 
2800 /* ARGSUSED */
2801 static int
2802 dcmd_jsprint(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2803 {
2804         char *buf, *bufp;
2805         size_t bufsz = 262144, len = bufsz;
2806         jsobj_print_t jsop;
2807         int rv;
2808 
2809         bzero(&jsop, sizeof (jsop));
2810         jsop.jsop_depth = 2;
2811         jsop.jsop_printaddr = B_FALSE;
2812 
2813         if (mdb_getopts(argc, argv,
2814             'a', MDB_OPT_SETBITS, B_TRUE, &jsop.jsop_printaddr,
2815             'd', MDB_OPT_UINT64, &jsop.jsop_depth, NULL) != argc)
2816                 return (DCMD_USAGE);
2817 
2818         if ((buf = bufp = mdb_zalloc(bufsz, UM_NOSLEEP)) == NULL)
2819                 return (DCMD_ERR);
2820 
2821         jsop.jsop_bufp = &bufp;
2822         jsop.jsop_lenp = &len;
2823 
2824         rv = jsobj_print(addr, &jsop);
2825         (void) mdb_printf("%s\n", buf);
2826         mdb_free(buf, bufsz);
2827         return (rv == 0 ? DCMD_OK : DCMD_ERR);
2828 }
2829 
2830 /* ARGSUSED */
2831 static int
2832 dcmd_v8field(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2833 {
2834         v8_class_t *clp;
2835         v8_field_t *flp;
2836         const char *klass, *field;
2837         uintptr_t offset = 0;
2838 
2839         /*
2840          * We may be invoked with either two arguments (class and field name) or
2841          * three (an offset to save).
2842          */
2843         if (argc != 2 && argc != 3)
2844                 return (DCMD_USAGE);
2845 
2846         if (argv[0].a_type != MDB_TYPE_STRING ||
2847             argv[1].a_type != MDB_TYPE_STRING)
2848                 return (DCMD_USAGE);
2849 
2850         klass = argv[0].a_un.a_str;
2851         field = argv[1].a_un.a_str;
2852 
2853         if (argc == 3) {
2854                 if (argv[2].a_type != MDB_TYPE_STRING)
2855                         return (DCMD_USAGE);
2856 
2857                 offset = mdb_strtoull(argv[2].a_un.a_str);
2858         }
2859 
2860         for (clp = v8_classes; clp != NULL; clp = clp->v8c_next)
2861                 if (strcmp(clp->v8c_name, klass) == 0)
2862                         break;
2863 
2864         if (clp == NULL) {
2865                 (void) mdb_printf("error: no such class: \"%s\"", klass);
2866                 return (DCMD_ERR);
2867         }
2868 
2869         for (flp = clp->v8c_fields; flp != NULL; flp = flp->v8f_next)
2870                 if (strcmp(field, flp->v8f_name) == 0)
2871                         break;
2872 
2873         if (flp == NULL) {
2874                 if (argc == 2) {
2875                         mdb_printf("error: no such field in class \"%s\": "
2876                             "\"%s\"", klass, field);
2877                         return (DCMD_ERR);
2878                 }
2879 
2880                 flp = conf_field_create(clp, field, offset);
2881                 if (flp == NULL) {
2882                         mdb_warn("failed to create field");
2883                         return (DCMD_ERR);
2884                 }
2885         } else if (argc == 3) {
2886                 flp->v8f_offset = offset;
2887         }
2888 
2889         mdb_printf("%s::%s at offset 0x%x\n", klass, field, flp->v8f_offset);
2890         return (DCMD_OK);
2891 }
2892 
2893 /* ARGSUSED */
2894 static int
2895 dcmd_v8array(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2896 {
2897         uint8_t type;
2898         uintptr_t *array;
2899         size_t ii, len;
2900 
2901         if (read_typebyte(&type, addr) != 0)
2902                 return (DCMD_ERR);
2903 
2904         if (type != V8_TYPE_FIXEDARRAY) {
2905                 mdb_warn("%p is not an instance of FixedArray\n", addr);
2906                 return (DCMD_ERR);
2907         }
2908 
2909         if (read_heap_array(addr, &array, &len, UM_GC) != 0)
2910                 return (DCMD_ERR);
2911 
2912         for (ii = 0; ii < len; ii++)
2913                 mdb_printf("%p\n", array[ii]);
2914 
2915         return (DCMD_OK);
2916 }
2917 
2918 /* ARGSUSED */
2919 static int
2920 dcmd_jsstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2921 {
2922         uintptr_t raddr;
2923         boolean_t opt_v;
2924 
2925         if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
2926             NULL) != argc)
2927                 return (DCMD_USAGE);
2928 
2929         /*
2930          * The "::jsframe" walker iterates the valid frame pointers, but the
2931          * "::jsframe" dcmd looks at the frame after the one it was given, so we
2932          * have to explicitly examine the top frame here.
2933          */
2934         if (!(flags & DCMD_ADDRSPEC)) {
2935                 if (load_current_context(&addr, &raddr) != 0 ||
2936                     do_jsframe(addr, raddr, opt_v) != 0)
2937                         return (DCMD_ERR);
2938         }
2939 
2940         if (mdb_pwalk_dcmd("jsframe", "jsframe", argc, argv, addr) == -1)
2941                 return (DCMD_ERR);
2942 
2943         return (DCMD_OK);
2944 }
2945 
2946 /* ARGSUSED */
2947 static int
2948 dcmd_v8str(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2949 {
2950         boolean_t opt_v = B_FALSE;
2951         char buf[256];
2952         char *bufp;
2953         size_t len;
2954 
2955         if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, B_TRUE, &opt_v,
2956             NULL) != argc)
2957                 return (DCMD_USAGE);
2958 
2959         bufp = buf;
2960         len = sizeof (buf);
2961         if (jsstr_print(addr, opt_v, &bufp, &len) != 0)
2962                 return (DCMD_ERR);
2963 
2964         mdb_printf("%s\n", buf);
2965         return (DCMD_OK);
2966 }
2967 
2968 static void
2969 dcmd_v8load_help(void)
2970 {
2971         v8_cfg_t *cfp, **cfgpp;
2972 
2973         mdb_printf(
2974             "To traverse in-memory V8 structures, the V8 dmod requires\n"
2975             "configuration that describes the layout of various V8 structures\n"
2976             "in memory.  Normally, this information is pulled from metadata\n"
2977             "in the target binary.  However, it's possible to use the module\n"
2978             "with a binary not built with metadata by loading one of the\n"
2979             "canned configurations.\n\n");
2980 
2981         mdb_printf("Available configurations:\n");
2982 
2983         (void) mdb_inc_indent(4);
2984 
2985         for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
2986                 cfp = *cfgpp;
2987                 mdb_printf("%-10s    %s\n", cfp->v8cfg_name, cfp->v8cfg_label);
2988         }
2989 
2990         (void) mdb_dec_indent(4);
2991 }
2992 
2993 /* ARGSUSED */
2994 static int
2995 dcmd_v8load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2996 {
2997         v8_cfg_t *cfgp = NULL, **cfgpp;
2998 
2999         if (v8_classes != NULL) {
3000                 mdb_warn("v8 module already configured\n");
3001                 return (DCMD_ERR);
3002         }
3003 
3004         if (argc < 1 || argv->a_type != MDB_TYPE_STRING)
3005                 return (DCMD_USAGE);
3006 
3007         for (cfgpp = v8_cfgs; *cfgpp != NULL; cfgpp++) {
3008                 cfgp = *cfgpp;
3009                 if (strcmp(argv->a_un.a_str, cfgp->v8cfg_name) == 0)
3010                         break;
3011         }
3012 
3013         if (cfgp == NULL || cfgp->v8cfg_name == NULL) {
3014                 mdb_warn("unknown configuration: \"%s\"\n", argv->a_un.a_str);
3015                 return (DCMD_ERR);
3016         }
3017 
3018         if (autoconfigure(cfgp) == -1) {
3019                 mdb_warn("autoconfigure failed\n");
3020                 return (DCMD_ERR);
3021         }
3022 
3023         mdb_printf("V8 dmod configured based on %s\n", cfgp->v8cfg_name);
3024         return (DCMD_OK);
3025 }
3026 
3027 static int
3028 walk_jsframes_init(mdb_walk_state_t *wsp)
3029 {
3030         if (wsp->walk_addr != NULL)
3031                 return (WALK_NEXT);
3032 
3033         if (load_current_context(&wsp->walk_addr, NULL) != 0)
3034                 return (WALK_ERR);
3035 
3036         return (WALK_NEXT);
3037 }
3038 
3039 static int
3040 walk_jsframes_step(mdb_walk_state_t *wsp)
3041 {
3042         uintptr_t addr, next;
3043         int rv;
3044 
3045         addr = wsp->walk_addr;
3046         rv = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
3047 
3048         if (rv != WALK_NEXT)
3049                 return (rv);
3050 
3051         if (mdb_vread(&next, sizeof (next), addr) == -1)
3052                 return (WALK_ERR);
3053 
3054         if (next == NULL)
3055                 return (WALK_DONE);
3056 
3057         wsp->walk_addr = next;
3058         return (WALK_NEXT);
3059 }
3060 
3061 /*
3062  * MDB linkage
3063  */
3064 
3065 static const mdb_dcmd_t v8_mdb_dcmds[] = {
3066         /*
3067          * Commands to inspect JavaScript-level state
3068          */
3069         { "jsframe", ":[-v]", "summarize a JavaScript stack frame",
3070                 dcmd_jsframe },
3071         { "jsprint", ":[-a] [-d depth]", "print a JavaScript object",
3072                 dcmd_jsprint },
3073         { "jsstack", "[-v]", "print a JavaScript stacktrace",
3074                 dcmd_jsstack },
3075         { "findjsobjects", "?[-vb] [-r | -c cons | -p prop]", "find JavaScript "
3076                 "objects", dcmd_findjsobjects, dcmd_findjsobjects_help },
3077 
3078         /*
3079          * Commands to inspect V8-level state
3080          */
3081         { "v8array", ":", "print elements of a V8 FixedArray",
3082                 dcmd_v8array },
3083         { "v8classes", NULL, "list known V8 heap object C++ classes",
3084                 dcmd_v8classes },
3085         { "v8code", ":[-d]", "print information about a V8 Code object",
3086                 dcmd_v8code },
3087         { "v8field", "classname fieldname offset",
3088                 "manually add a field to a given class", dcmd_v8field },
3089         { "v8function", ":[-d]", "print JSFunction object details",
3090                 dcmd_v8function },
3091         { "v8load", "version", "load canned config for a specific V8 version",
3092                 dcmd_v8load, dcmd_v8load_help },
3093         { "v8frametypes", NULL, "list known V8 frame types",
3094                 dcmd_v8frametypes },
3095         { "v8print", ":[class]", "print a V8 heap object",
3096                 dcmd_v8print, dcmd_v8print_help },
3097         { "v8str", ":[-v]", "print the contents of a V8 string",
3098                 dcmd_v8str },
3099         { "v8type", ":", "print the type of a V8 heap object",
3100                 dcmd_v8type },
3101         { "v8types", NULL, "list known V8 heap object types",
3102                 dcmd_v8types },
3103 
3104         { NULL }
3105 };
3106 
3107 static const mdb_walker_t v8_mdb_walkers[] = {
3108         { "jsframe", "walk V8 JavaScript stack frames",
3109                 walk_jsframes_init, walk_jsframes_step },
3110         { NULL }
3111 };
3112 
3113 static mdb_modinfo_t v8_mdb = { MDB_API_VERSION, v8_mdb_dcmds, v8_mdb_walkers };
3114 
3115 static void
3116 configure(void)
3117 {
3118         uintptr_t v8major, v8minor, v8build, v8patch;
3119         GElf_Sym sym;
3120 
3121         if (mdb_readsym(&v8major, sizeof (v8major),
3122             "_ZN2v88internal7Version6major_E") == -1 ||
3123             mdb_readsym(&v8minor, sizeof (v8minor),
3124             "_ZN2v88internal7Version6minor_E") == -1 ||
3125             mdb_readsym(&v8build, sizeof (v8build),
3126             "_ZN2v88internal7Version6build_E") == -1 ||
3127             mdb_readsym(&v8patch, sizeof (v8patch),
3128             "_ZN2v88internal7Version6patch_E") == -1) {
3129                 mdb_warn("failed to determine V8 version");
3130                 return;
3131         }
3132 
3133         mdb_printf("V8 version: %d.%d.%d.%d\n", v8major, v8minor, v8build,
3134             v8patch);
3135 
3136         /*
3137          * First look for debug metadata embedded within the binary, which may
3138          * be present in recent V8 versions built with postmortem metadata.
3139          */
3140         if (mdb_lookup_by_name("v8dbg_SmiTag", &sym) == 0) {
3141                 if (autoconfigure(&v8_cfg_target) != 0) {
3142                         mdb_warn("failed to autoconfigure from target; "
3143                             "commands may have incorrect results!\n");
3144                 } else {
3145                         mdb_printf("Autoconfigured V8 support from target\n");
3146                 }
3147 
3148                 return;
3149         }
3150 
3151         if (v8major == 3 && v8minor == 1 && v8build == 8 &&
3152             autoconfigure(&v8_cfg_04) == 0) {
3153                 mdb_printf("Configured V8 support based on node v0.4\n");
3154                 return;
3155         }
3156 
3157         if (v8major == 3 && v8minor == 6 && v8build == 6 &&
3158             autoconfigure(&v8_cfg_06) == 0) {
3159                 mdb_printf("Configured V8 support based on node v0.6\n");
3160                 return;
3161         }
3162 
3163         mdb_printf("mdb_v8: target has no debug metadata and no existing "
3164             "config found\n");
3165 }
3166 
3167 static void
3168 enable_demangling(void)
3169 {
3170         const char *symname = "_ZN2v88internal7Version6major_E";
3171         GElf_Sym sym;
3172         char buf[64];
3173 
3174         /*
3175          * Try to determine whether C++ symbol demangling has been enabled.  If
3176          * not, enable it.
3177          */
3178         if (mdb_lookup_by_name("_ZN2v88internal7Version6major_E", &sym) != 0)
3179                 return;
3180 
3181         (void) mdb_snprintf(buf, sizeof (buf), "%a", sym.st_value);
3182         if (strstr(buf, symname) != NULL)
3183                 (void) mdb_eval("$G");
3184 }
3185 
3186 const mdb_modinfo_t *
3187 _mdb_init(void)
3188 {
3189         configure();
3190         enable_demangling();
3191         return (&v8_mdb);
3192 }