Last active
December 24, 2015 23:29
-
-
Save nox/6881145 to your computer and use it in GitHub Desktop.
Preliminary Erlang support for LLDB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding=utf-8 | |
# | |
# %CopyrightBegin% | |
# | |
# Copyright Ericsson AB 2013. All Rights Reserved. | |
# | |
# The contents of this file are subject to the Erlang Public License, | |
# Version 1.1, (the "License"); you may not use this file except in | |
# compliance with the License. You should have received a copy of the | |
# Erlang Public License along with this software. If not, it can be | |
# retrieved online at http://www.erlang.org/. | |
# | |
# Software distributed under the License is distributed on an "AS IS" | |
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | |
# the License for the specific language governing rights and limitations | |
# under the License. | |
# | |
# %CopyrightEnd% | |
# | |
import re | |
unquoted_atom_re = re.compile(u'^[a-zß-öø-ÿ][a-zA-Zß-öø-ÿ0-9@]*$') | |
def eterm_flat_summary(valobj, internal_dict): | |
val = valobj.unsigned | |
tag = val & 0x3 | |
if tag == 0x1: | |
return '#Cons<%#x>' % val | |
elif tag == 0x2: | |
return '#Boxed<%#x>' % val | |
elif tag == 0x3: | |
return imm_summary(valobj) | |
elif val == 0x0: | |
return '<the non-value>' | |
elif val == 0x4: | |
return '<the non-value debug>' | |
else: | |
return cp_summary(valobj) | |
def imm_summary(valobj): | |
val = valobj.unsigned | |
if (val & 0x3) != 3: | |
return '#NotImmediate<%#x>' % val | |
tag = val & 0xF | |
if tag == 0x3: | |
return pid_summary(valobj) | |
elif tag == 0x7: | |
return port_summary(valobj) | |
elif tag == 0xF: | |
return str(val >> 4) | |
elif tag == 0xB: | |
# Immediate2 | |
tag2 = val & 0x3F | |
if tag2 == 0x0B: | |
return atom_summary(valobj) | |
elif tag2 == 0x1B: | |
return '#Catch<%#x>' % val >> 6 | |
elif val == NIL(valobj.target): | |
return '[]' | |
return '#UnknownImmediate<%#x>' % val | |
# Continuation pointers | |
def cp_summary(valobj): | |
mfa = erts_lookup_function_info(valobj) | |
return '#Cp<TODO>' | |
# Pids and ports | |
def pid_summary(valobj): | |
val = valobj.unsigned | |
if (val & 0xF) == 0x3: | |
target = valobj.target | |
if etp_arch_bits(target) == 64 and not etp_halfword(target): | |
if etp_big_endian(target): | |
data = (val >> 36) & 0x0FFFFFFF | |
else: | |
data = (val >> 4) & 0x0FFFFFFF | |
else: | |
data = pixdata2data(valobj) | |
return '<0.%u.%u>' % (data & 0x7FFF, (data >> 15) & 0x1FFF) | |
else: | |
return '#NotPid<%#x>' % val | |
def port_summary(valobj): | |
val = valobj.unsigned | |
if (val & 0xF) == 0x7: | |
target = valobj.target | |
if etp_arch_bits(target) == 64 and not etp_halfword(target): | |
if etp_big_endian(target): | |
data = (val >> 36) & 0x0FFFFFFF | |
else: | |
data = (val >> 4) & 0x0FFFFFFF | |
else: | |
data = pixdata2data(valobj) | |
return '#Port<0.%u>' % data | |
else: | |
return '#NotPort<%#x>' % val | |
# Strings and atoms | |
def atom_summary(valobj): | |
val = valobj.unsigned | |
if (val & 0x3F) == 0x0B: | |
name = atom_name(atom_tab(valobj)) | |
if unquoted_atom_re.match(name): | |
return str(name) | |
else: | |
return quoted_name(name, "'") | |
else: | |
return '#NotAtom<%#x>' % val | |
def atom_name(entry): | |
name = entry.GetChildMemberWithName('name') | |
length = entry.GetChildMemberWithName('len').unsigned | |
data = name.GetPointeeData(0, length).uint8s | |
return unicode(''.join(map(chr, data)), 'utf-8') | |
def quoted_name(name, quote): | |
return quote + ''.join(map(lambda c: quoted_char(c, quote), name)) + quote | |
def quoted_char(c, quote): | |
point = ord(c) | |
if c == quote: | |
return '\\' + quote | |
elif point == 0x08: | |
return '\\b' | |
elif point == 0x09: | |
return '\\t' | |
elif point == 0x0A: | |
return '\\n' | |
elif point == 0x0B: | |
return '\\v' | |
elif point == 0x0C: | |
return '\\f' | |
elif point == 0x0D: | |
return '\\e' | |
elif point >= 0x20 and point <= 0x7E or point >= 0xA0: | |
return c | |
elif (point > 0xFF): | |
return '#NotChar<%#x>' % c | |
else: | |
return '\\%03o' % point | |
# Constants | |
MI_FUNCTIONS = 12 | |
MI_NUM_FUNCTIONS = 0 | |
def NIL(target): | |
if etp_arch_bits(target) == 32: | |
return 0xFFFFFFFB | |
else: | |
return 0xFFFFFFFFFFFFFFFB | |
# Types | |
def Atom(target): | |
return target.FindFirstType('Atom') | |
def Eterm(target): | |
return target.FindFirstType('Eterm') | |
def Range(target): | |
return target.FindFirstType('Range') | |
# Globals | |
def erts_atom_table(target): | |
return global_var('erts_atom_table', target) | |
def erts_proc(target): | |
return global_var('erts_proc', target) | |
def etp_arch_bits(target): | |
return global_var('etp_arch_bits', target).unsigned | |
def etp_big_endian(target): | |
return bool(global_var('etp_big_endian', target).unsigned) | |
def etp_halfword(target): | |
return bool(global_var('etp_halfword', target).unsigned) | |
def the_active_code_index(target): | |
return global_var('the_active_code_index', target) | |
# Functions | |
def atom_tab(valobj): | |
idx = valobj.unsigned | |
seg = erts_atom_table(valobj.target).GetChildMemberWithName('seg_table') | |
slot = offset(idx >> 16, seg) | |
entry = offset(idx >> 6 & 0x3FF, slot) | |
return entry.Cast(Atom(target).GetPointerType()) | |
def erts_lookup_function_info(valobj): | |
r = find_range(valobj) | |
if r is None: | |
return None | |
cp = valobj.unsigned | |
target = valobj.target | |
start = r.GetChildMemberWithName('start') | |
low = offset(MI_FUNCTIONS, start).address_of | |
high = offset(offset(MI_NUM_FUNCTIONS, start).unsigned, low).address_of | |
while low.unsigned < high.unsigned: | |
mid = offset((high.unsigned - low.unsigned) / 2, low).address_of | |
if pc < mid.deref.unsigned: | |
high = mid | |
elif pc < offset(1, mid).unsigned: | |
ptr = mid.deref.Cast(Eterm(target).GetPointerType()) | |
Mod = offset(2, ptr) | |
Name = offset(3, ptr) | |
Arity = offset(4, ptr) | |
return (Mod, Name, Arity) | |
else: | |
low = offset(1, mid).address_of | |
return None | |
def find_range(valobj): | |
cp = valobj.unsigned | |
target = valobj.target | |
active = the_active_code_index(target).unsigned | |
ranges = offset(active, global_var('r', target)) | |
n = ranges.GetChildMemberWithName('n') | |
low = ranges.GetChildMemberWithName('modules') | |
high = offset(n, low).address_of | |
range_pointer_type = Range(target).GetPointerType() | |
mid = ranges.GetChildMemberWithName('mid').Cast(range_pointer_type) | |
while low.unsigned < high.unsigned: | |
if pc.unsigned < mid.GetChildMemberWithName('start').unsigned: | |
high = mid | |
elif pc.unsigned > mid.GetChildMemberWithName('end').unsigned: | |
low = offset(1, mid).address_of | |
else: | |
return mid | |
mid = offset((high.unsigned - low.unsigned) / 2, low).address_of | |
return None | |
def pixdata2data(valobj): | |
pixdata = valobj.unsigned | |
proc = erts_proc(target) | |
ro = proc.GetChildMemberWithName('r').GetChildMemberWithName('o') | |
pix_mask = ro.GetChildMemberWithName('pix_mask').unsigned | |
pix_cl_mask = ro.GetChildMemberWithName('pix_cl_mask').unsigned | |
pix_cl_shift = ro.GetChildMemberWithName('pix_cl_shift').unsigned | |
pix_cli_mask = ro.GetChildMemberWithName('pix_cli_mask').unsigned | |
pix_cli_shift = ro.GetChildMemberWithName('pix_cli_shift').unsigned | |
data = pixdata & ~pix_mask | |
data |= (pixdata >> pix_cl_shift) & pix_cl_mask | |
data |= (pixdata & pix_cli_mask) << pix_cli_shift | |
return data | |
# LLDB utils | |
def global_var(name, target): | |
return target.FindGlobalVariables(name, 1)[0] | |
def offset(offset, valobj): | |
return valobj.GetChildAtIndex(offset, False, True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment