blob: c03a4c2655b83edacc1f3937c74deb67b15ee6b6 [file] [log] [blame]
// Copyright 2016 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
var CyberDWARFHeapPrinter = function(cdFileLocation) {
var BASIC_TYPE = 0,
DERIVED_TYPE = 1,
COMPOSITE_TYPE = 2,
SUBROUTINE_TYPE = 3,
SUBRANGE_INFO = 4,
SUBPROGRAM_TYPE = 5,
ENUMERATOR_TYPE = 6,
STRING_REFERENCE = 10;
var DW_TAG_array_type = 0x01,
DW_TAG_class_type = 0x02,
DW_TAG_entry_point = 0x03,
DW_TAG_enumeration_type = 0x04,
DW_TAG_formal_parameter = 0x05,
DW_TAG_imported_declaration = 0x08,
DW_TAG_label = 0x0a,
DW_TAG_lexical_block = 0x0b,
DW_TAG_member = 0x0d,
DW_TAG_pointer_type = 0x0f,
DW_TAG_reference_type = 0x10,
DW_TAG_compile_unit = 0x11,
DW_TAG_string_type = 0x12,
DW_TAG_structure_type = 0x13,
DW_TAG_subroutine_type = 0x15,
DW_TAG_typedef = 0x16,
DW_TAG_union_type = 0x17,
DW_TAG_unspecified_parameters = 0x18,
DW_TAG_variant = 0x19,
DW_TAG_common_block = 0x1a,
DW_TAG_common_inclusion = 0x1b,
DW_TAG_inheritance = 0x1c,
DW_TAG_inlined_subroutine = 0x1d,
DW_TAG_module = 0x1e,
DW_TAG_ptr_to_member_type = 0x1f,
DW_TAG_set_type = 0x20,
DW_TAG_subrange_type = 0x21,
DW_TAG_with_stmt = 0x22,
DW_TAG_access_declaration = 0x23,
DW_TAG_base_type = 0x24,
DW_TAG_catch_block = 0x25,
DW_TAG_const_type = 0x26,
DW_TAG_constant = 0x27,
DW_TAG_enumerator = 0x28,
DW_TAG_file_type = 0x29,
DW_TAG_friend = 0x2a,
DW_TAG_namelist = 0x2b,
DW_TAG_namelist_item = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
DW_TAG_template_type_parameter = 0x2f,
DW_TAG_template_value_parameter = 0x30,
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
DW_TAG_variable = 0x34,
DW_TAG_volatile_type = 0x35,
DW_TAG_dwarf_procedure = 0x36,
DW_TAG_restrict_type = 0x37,
DW_TAG_interface_type = 0x38,
DW_TAG_namespace = 0x39,
DW_TAG_imported_module = 0x3a,
DW_TAG_unspecified_type = 0x3b,
DW_TAG_partial_unit = 0x3c,
DW_TAG_imported_unit = 0x3d,
DW_TAG_condition = 0x3f,
DW_TAG_shared_type = 0x40,
DW_TAG_lo_user = 0x4080,
DW_TAG_hi_user = 0xffff;
var DW_ATE_address = 0x01,
DW_ATE_boolean = 0x02,
DW_ATE_complex_float = 0x03,
DW_ATE_float = 0x04,
DW_ATE_signed = 0x05,
DW_ATE_signed_char = 0x06,
DW_ATE_unsigned = 0x07,
DW_ATE_unsigned_char = 0x08,
DW_ATE_imaginary_float = 0x09,
DW_ATE_packed_decimal = 0x0a,
DW_ATE_numeric_string = 0x0b,
DW_ATE_edited = 0x0c,
DW_ATE_signed_fixed = 0x0d,
DW_ATE_unsigned_fixed = 0x0e,
DW_ATE_decimal_float = 0x0f,
DW_ATE_lo_user = 0x80,
DW_ATE_hi_user = 0xff;
var TYPE_ID_IDX = 0,
NAME_IDX = 1,
TAG_IDX = 2,
BASE_TYPE_IDX = 3,
OFFSET_IDX = 4,
SIZE_IDX = 5,
C_ELEMS_IDX = 7;
function TypedVariable(base_ptr, type_id) {
this.derives = [];
this.primary = null;
this.primary_type_id = "";
this.base_ptr = base_ptr;
this.type_id = type_id;
this.built = false;
this.resolved = false;
this.name = "";
this.value = "";
this.pointerCount = 0;
this.offset = 0;
this.size = 0;
this.standalone_id = 0;
this.dereference = true;
this.isMember = false;
this.isInherited = false
}
TypedVariable.prototype.toString = function() {
return "[TypedVariable < " + JSON.stringify(this.derives) + " > " + JSON.stringify(this.primary) + " (&" + this._getMyAddress() + ")]";
}
TypedVariable.prototype._initialBuild = function() {
if (this.built) {
return;
}
this.built = true;
this._buildDeriveChain();
this._processTypes();
}
TypedVariable.prototype._buildDeriveChain = function() {
var cur_td = type_id_to_type_descriptor(this.type_id, this.base_ptr);
var ptr = this.base_ptr;
while (cur_td[TYPE_ID_IDX] == 1 || cur_td[TYPE_ID_IDX] == 10) {
if (cur_td[TYPE_ID_IDX] == 1) {
this.derives.push(cur_td);
this.primary_type_id = cur_td[BASE_TYPE_IDX];
if (cur_td[TAG_IDX] == DW_TAG_member) {
ptr += cur_td[OFFSET_IDX];
}
cur_td = type_id_to_type_descriptor(cur_td[BASE_TYPE_IDX], ptr);
} else {
this.primary_type_id = cur_td[1];
cur_td = type_id_to_type_descriptor(cur_td[1], ptr);
}
}
this.primary = cur_td;
}
TypedVariable.prototype._processTypes = function() {
var name_vec = [];
for (var i in this.derives) {
switch (this.derives[i][TAG_IDX]) {
case DW_TAG_reference_type: {
name_vec.unshift("&");
this.pointerCount++;
} break;
case DW_TAG_pointer_type: {
name_vec.unshift("*");
this.pointerCount++;
} break;
case DW_TAG_const_type: {
name_vec.unshift("const");
} break;
case DW_TAG_inheritance: {
this.isInherited = true;
name_vec.unshift(">");
this.offset = this.derives[i][OFFSET_IDX] / 8;
} break;
case DW_TAG_member: {
this.isMember = true;
name_vec.push(":");
name_vec.push(this.derives[i][NAME_IDX])
this.offset = this.derives[i][OFFSET_IDX] / 8;
} break;
case DW_TAG_typedef: {
// Ignoring typedefs for the time being
} break;
case DW_TAG_volatile_type: {
name_vec.unshift("volatile");
}
default:
console.error("Unimplemented " + type_descriptor);
}
}
if (this.primary[TYPE_ID_IDX] == BASIC_TYPE) {
this.size = this.primary[4];
name_vec.unshift(this.primary[NAME_IDX]);
} else if (this.primary[TYPE_ID_IDX] == COMPOSITE_TYPE) {
this.size = this.primary[SIZE_IDX];
name_vec.unshift(this.primary[NAME_IDX]);
switch (this.primary[TAG_IDX]) {
case DW_TAG_structure_type: {
name_vec.unshift("struct");
} break;
case DW_TAG_class_type: {
name_vec.unshift("class");
} break;
case DW_TAG_enumeration_type: {
name_vec.unshift("class");
} break;
case DW_TAG_union_type: {
name_vec.unshift("union");
} break;
case DW_TAG_array_type: {
name_vec.unshift("array");
} break;
default:
console.error("Unimplemented for composite " + this.primary);
}
}
this.name = name_vec.join(" ");
}
TypedVariable.prototype._getMyAddress = function() {
var base_addr = this.base_ptr + this.offset;
for (var i = 0; i < this.pointerCount; i++) {
if (base_addr == 0) {
return 0;
}
base_addr = heap["u32"][base_addr >> 2];
}
return base_addr;
}
TypedVariable.prototype._resolveBaseTypeValue = function() {
this.resolved = true;
if (this.primary[TYPE_ID_IDX] == BASIC_TYPE) {
if (!this.dereference) {
switch (this.primary[TAG_IDX]) {
case DW_ATE_unsigned:
case DW_ATE_float:
case DW_ATE_signed: {
this.value = this.base_ptr;
}; break;
}
} else {
if (this.base_ptr == 0) {
this.value = null;
} else {
var heap_id = type_descriptor_to_heap_id(this.primary);
var ptr = this._getMyAddress();
this.value = heap[heap_id][ptr >> heap_shift[heap_id]];
}
}
} else {
this.value = {};
for (var i in this.primary[C_ELEMS_IDX]) {
var cur_elem = new TypedVariable(this._getMyAddress(), this.primary[C_ELEMS_IDX][i]);
cur_elem._initialBuild();
if (cur_elem.size > 0 && (cur_elem.isInherited || cur_elem.isMember)) {
var pointed_elem = new TypedVariable(cur_elem._getMyAddress(), cur_elem.primary_type_id);
pointed_elem._initialBuild();
this.value[cur_elem.name] = pointed_elem;
}
}
}
}
TypedVariable.prototype._buildForPrettyPrint = function(depth) {
if (typeof(depth) === "undefined") {
depth = 1;
}
if (depth < 1) {
return;
}
this._initialBuild();
this._resolveBaseTypeValue();
if (this.primary[TYPE_ID_IDX] != 2 || this._getMyAddress() == 0) {
return;
}
for (var i in this.value) {
this.value[i]._buildForPrettyPrint(depth - 1);
}
}
TypedVariable.prototype._prettyPrintToObjectHelper = function() {
if (typeof(this.value) !== "object") {
return this.value;
}
if (this.value == null) {
return "null";
}
var childNames = Object.keys(this.value);
var retval = {};
for (var i in childNames) {
if (this.value[childNames[i]].resolved) {
retval[childNames[i]] = this.value[childNames[i]]._prettyPrintToObjectHelper();
} else {
retval[childNames[i]] = "cd_tvptr(0x" + this.value[childNames[i]]._getMyAddress().toString(16) + ",'" + this.value[childNames[i]].type_id + "')";
}
}
return retval;
}
TypedVariable.prototype.prettyPrintToObject = function(depth) {
this._buildForPrettyPrint(depth);
var retval = {};
retval[this.name] = this._prettyPrintToObjectHelper();
return retval;
}
var heap = {};
var cyberdwarf = undefined;
var heap_shift = {
"u8" : 0,
"u16": 1,
"u32": 2,
"i8" : 0,
"i16": 1,
"i32": 2,
"f32": 2,
"f64": 3
}
function install_heap($heap) {
heap = {
"u8" : new Uint8Array($heap),
"u16": new Uint16Array($heap),
"u32": new Uint32Array($heap),
"i8" : new Int8Array($heap),
"i16": new Int16Array($heap),
"i32": new Int32Array($heap),
"f32": new Float32Array($heap),
"f64": new Float64Array($heap)
};
}
var symbols = {};
function install_cyberdwarf(data_cd) {
cyberdwarf = JSON.parse(data_cd)["cyberdwarf"];
invert_vtables();
}
function type_descriptor_to_heap_id(type_descriptor) {
var id = "";
switch (type_descriptor[TAG_IDX]) {
case DW_ATE_unsigned: case DW_ATE_unsigned_char: id = "u"; break;
case DW_ATE_signed: case DW_ATE_signed_char: id = "i"; break;
case DW_ATE_float: id = "f"; break;
}
id += type_descriptor[4];
return id;
}
function type_id_to_type_descriptor(type_id, ptr, dit) {
if (!isNaN(+type_id)) {
return cyberdwarf["types"][type_id];
}
if (typeof(type_id) === "string") {
if (dit) {
type_id = resolve_from_vtable(ptr, type_id);
}
type_id = cyberdwarf["type_name_map"][type_id];
}
return cyberdwarf["types"][type_id];
}
function invert_vtables() {
cyberdwarf["has_vtable"] = {};
for (var i in cyberdwarf["vtable_offsets"]) {
cyberdwarf["has_vtable"][cyberdwarf["vtable_offsets"][i]] = true;
}
}
function resolve_from_vtable(val, type_name) {
var lookup_name = "_ZTV" + type_name.substr(4);
if (cyberdwarf["has_vtable"][lookup_name]) {
var potential_vtable = "" + (heap["u32"][val >> 2] - 8);
if (cyberdwarf["vtable_offsets"][potential_vtable]) {
var ans = type_name.substr(0,4) + cyberdwarf["vtable_offsets"][potential_vtable].substr(4);
return ans;
}
}
return type_name;
}
var current_function = "";
function set_current_function(func_name) {
if (typeof(cyberdwarf["function_name_map"][func_name]) !== "undefined") {
func_name = cyberdwarf["function_name_map"][func_name];
}
if (func_name.substring(0,1) == "_") {
func_name = func_name.substring(1);
}
current_function = cyberdwarf["functions"][func_name];
}
function pretty_print_to_object(val, type_id, depth) {
install_heap(Module.HEAPU8.buffer);
var base_type = new TypedVariable(val, current_function[type_id]);
return base_type.prettyPrintToObject(depth);
}
function pretty_print_from_typename(val, type_name, depth) {
install_heap(Module.HEAPU8.buffer);
var base_type = new TypedVariable(val, type_name);
return base_type.prettyPrintToObject(depth);
}
function stack_decoder(val, name, depth, on_heap) {
var stack_frames = jsStackTrace().split("\n");
// To find the function that called us, look for the first reference to calling an EM_ASM block
var stack_offset = 0;
for (; stack_offset < stack_frames.length; stack_offset++) {
if (stack_frames[stack_offset].match("emscripten_asm_const")) break;
}
// Subtract 1 since we shouldn't have found it on the last frame
if (stack_offset == stack_frames.length - 1) {
console.error("Couldn't find the function the decoder was called from");
return {};
}
var func_finder = new RegExp("at (?:Object\\.|)([^\\s]+)", "g");
var result = func_finder.exec(stack_frames[stack_offset + 1]);
if (result.length > 1) {
set_current_function(result[1]);
}
var decoded = pretty_print_to_object(val, name, depth);
return decoded;
}
function initialize_debugger(cb) {
cdFileLocation = locateFile(cdFileLocation);
if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) {
var data = read_(cdFileLocation);
install_cyberdwarf(data);
} else {
var applyCDFile = function(data) {
var decoder = new TextDecoder("utf8");
install_cyberdwarf(decoder.decode(data));
console.info("Debugger ready");
if (typeof(cb) !== "undefined") {
cb();
}
}
var doBrowserLoad = function() {
readAsync(cdFileLocation, applyCDFile, function() {
throw 'could not load debug data ' + cdFileLocation;
});
}
// fetch it from the network ourselves
doBrowserLoad();
}
}
return {
"decode_from_stack": stack_decoder,
"initialize_debugger": initialize_debugger,
"set_current_function": set_current_function,
"decode_var_by_var_name": pretty_print_to_object,
"decode_var_by_type_name": pretty_print_from_typename
};
};
mergeInto(LibraryManager.library, { "cyberdwarf_Debugger": CyberDWARFHeapPrinter });