Improve breakpoint generator of the debugger. (#1652)
Now the debugger generates a breakpoint for each function before its first executable statement. This allows inspecting the function arguments. Position (line and column) info is also added which simplifies finding of anonymus functions. Several tests were update to check more corner cases. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
@@ -251,9 +251,15 @@ function DebuggerClient(address)
|
||||
|
||||
result += ":" + breakpoint.line;
|
||||
|
||||
if (breakpoint.func.name)
|
||||
if (breakpoint.func.is_func)
|
||||
{
|
||||
result += " (in " + breakpoint.func.name + ")";
|
||||
result += " (in "
|
||||
+ (breakpoint.func.name ? breakpoint.func.name : "function")
|
||||
+ "() at line:"
|
||||
+ breakpoint.func.line
|
||||
+ ", col:"
|
||||
+ breakpoint.func.column
|
||||
+ ")";
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -464,6 +470,31 @@ function DebuggerClient(address)
|
||||
socket.send(message);
|
||||
}
|
||||
|
||||
function releaseFunction(message)
|
||||
{
|
||||
var byte_code_cp = decodeMessage("C", message, 1)[0];
|
||||
var func = functions[byte_code_cp];
|
||||
|
||||
for (var i in func.lines)
|
||||
{
|
||||
lineList.delete(i, func);
|
||||
|
||||
var breakpoint = func.lines[i];
|
||||
|
||||
assert(i == breakpoint.line);
|
||||
|
||||
if (breakpoint.activeIndex >= 0)
|
||||
{
|
||||
delete activeBreakpoints[breakpoint.activeIndex];
|
||||
}
|
||||
}
|
||||
|
||||
delete functions[byte_code_cp];
|
||||
|
||||
message[0] = JERRY_DEBUGGER_FREE_BYTE_CODE_CP;
|
||||
socket.send(message);
|
||||
}
|
||||
|
||||
this.encodeMessage = encodeMessage;
|
||||
|
||||
function ParseSource()
|
||||
@@ -473,8 +504,14 @@ function DebuggerClient(address)
|
||||
var sourceName = "";
|
||||
var sourceNameData = null;
|
||||
var functionName = null;
|
||||
var stack = [ { name: "", source: "", lines: [], offsets: [] } ];
|
||||
var newFunctions = [ ];
|
||||
var stack = [{ is_func: false,
|
||||
line: 1,
|
||||
column: 1,
|
||||
name: "",
|
||||
source: "",
|
||||
lines: [],
|
||||
offsets: [] }];
|
||||
var newFunctions = { };
|
||||
|
||||
this.receive = function(message)
|
||||
{
|
||||
@@ -520,7 +557,12 @@ function DebuggerClient(address)
|
||||
|
||||
case JERRY_DEBUGGER_PARSE_FUNCTION:
|
||||
{
|
||||
stack.push({ name: cesu8ToString(functionName),
|
||||
position = decodeMessage("II", message, 1);
|
||||
|
||||
stack.push({ is_func: true,
|
||||
line: position[0],
|
||||
column: position[1],
|
||||
name: cesu8ToString(functionName),
|
||||
source: source,
|
||||
sourceName: sourceName,
|
||||
lines: [],
|
||||
@@ -563,7 +605,7 @@ function DebuggerClient(address)
|
||||
lines = {}
|
||||
offsets = {}
|
||||
|
||||
func.firstLine = (func.lines.length > 0) ? func.lines[0] : -1;
|
||||
func.firstBreakpointLine = func.lines[0];
|
||||
|
||||
for (var i = 0; i < func.lines.length; i++)
|
||||
{
|
||||
@@ -576,7 +618,7 @@ function DebuggerClient(address)
|
||||
func.lines = lines;
|
||||
func.offsets = offsets;
|
||||
|
||||
newFunctions.push(func);
|
||||
newFunctions[func.byte_code_cp] = func;
|
||||
|
||||
if (stack.length > 0)
|
||||
{
|
||||
@@ -588,6 +630,21 @@ function DebuggerClient(address)
|
||||
break;
|
||||
}
|
||||
|
||||
case JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP:
|
||||
{
|
||||
var byte_code_cp = decodeMessage("C", message, 1)[0];
|
||||
|
||||
if (byte_code_cp in newFunctions)
|
||||
{
|
||||
delete newFunctions[byte_code_cp];
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseFunction(message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
abortConnection("unexpected message.");
|
||||
@@ -595,11 +652,11 @@ function DebuggerClient(address)
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < newFunctions.length; i++)
|
||||
for (var i in newFunctions)
|
||||
{
|
||||
var func = newFunctions[i];
|
||||
|
||||
functions[func.byte_code_cp] = func
|
||||
functions[i] = func;
|
||||
|
||||
for (var j in func.lines)
|
||||
{
|
||||
@@ -667,27 +724,7 @@ function DebuggerClient(address)
|
||||
|
||||
case JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP:
|
||||
{
|
||||
var byte_code_cp = decodeMessage("C", message, 1)[0];
|
||||
var func = functions[byte_code_cp];
|
||||
|
||||
for (var i in func.lines)
|
||||
{
|
||||
lineList.delete(i, func);
|
||||
|
||||
var breakpoint = func.lines[i];
|
||||
|
||||
assert(i == breakpoint.line);
|
||||
|
||||
if (breakpoint.activeIndex >= 0)
|
||||
{
|
||||
delete activeBreakpoints[breakpoint.activeIndex];
|
||||
}
|
||||
}
|
||||
|
||||
delete functions[byte_code_cp];
|
||||
|
||||
message[0] = JERRY_DEBUGGER_FREE_BYTE_CODE_CP;
|
||||
socket.send(message);
|
||||
releaseFunction(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -828,9 +865,9 @@ function DebuggerClient(address)
|
||||
{
|
||||
var func = functions[i];
|
||||
|
||||
if (func.name == str && func.firstLine >= 0)
|
||||
if (func.name == str)
|
||||
{
|
||||
insertBreakpoint(func.lines[func.firstLine]);
|
||||
insertBreakpoint(func.lines[func.firstBreakpointLine]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -975,7 +1012,16 @@ function DebuggerClient(address)
|
||||
sourceName = "<unknown>";
|
||||
}
|
||||
|
||||
appendLog("Function 0x" + Number(i).toString(16) + " '" + func.name + "' at " + sourceName + ":" + func.firstLine);
|
||||
appendLog("Function 0x"
|
||||
+ Number(i).toString(16)
|
||||
+ " '"
|
||||
+ func.name
|
||||
+ "' at "
|
||||
+ sourceName
|
||||
+ ":"
|
||||
+ func.line
|
||||
+ ","
|
||||
+ func.column);
|
||||
|
||||
for (var j in func.lines)
|
||||
{
|
||||
@@ -986,7 +1032,7 @@ function DebuggerClient(address)
|
||||
active = " (active: " + func.lines[j].active + ")";
|
||||
}
|
||||
|
||||
appendLog(" Breatpoint line: " + j + " at memory offset: " + func.lines[j].offset + active);
|
||||
appendLog(" Breakpoint line: " + j + " at memory offset: " + func.lines[j].offset + active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,13 @@ class JerryBreakpoint(object):
|
||||
|
||||
result += ":%d" % (self.line)
|
||||
|
||||
if self.function.name:
|
||||
result += " (in %s)" % (self.function.name)
|
||||
if self.function.is_func:
|
||||
result += " (in "
|
||||
if self.function.name:
|
||||
result += self.function.name
|
||||
else:
|
||||
result += "function"
|
||||
result += "() at line:%d, col:%d)" % (self.function.line, self.function.column)
|
||||
return result
|
||||
|
||||
def __repr__(self):
|
||||
@@ -106,17 +111,17 @@ class JerryBreakpoint(object):
|
||||
|
||||
class JerryFunction(object):
|
||||
|
||||
def __init__(self, byte_code_cp, source, source_name, name, lines, offsets):
|
||||
def __init__(self, is_func, byte_code_cp, source, source_name, line, column, name, lines, offsets):
|
||||
self.is_func = is_func
|
||||
self.byte_code_cp = byte_code_cp
|
||||
self.source = source
|
||||
self.source_name = source_name
|
||||
self.name = name
|
||||
self.lines = {}
|
||||
self.offsets = {}
|
||||
self.first_line = -1
|
||||
|
||||
if lines:
|
||||
self.first_line = lines[0]
|
||||
self.line = line
|
||||
self.column = column
|
||||
self.first_breakpoint_line = lines[0]
|
||||
|
||||
for i in range(len(lines)):
|
||||
line = lines[i]
|
||||
@@ -126,8 +131,8 @@ class JerryFunction(object):
|
||||
self.offsets[offset] = breakpoint
|
||||
|
||||
def __repr__(self):
|
||||
result = ("Function(byte_code_cp:0x%x, source_name:\"%s\", name:\"%s\", { "
|
||||
% (self.byte_code_cp, self.source_name, self.name))
|
||||
result = ("Function(byte_code_cp:0x%x, source_name:\"%s\", name:\"%s\", line:%d, column: %d { "
|
||||
% (self.byte_code_cp, self.source_name, self.name, self.line, self.column))
|
||||
|
||||
result += ','.join([str(breakpoint) for breakpoint in self.lines.values()])
|
||||
|
||||
@@ -465,6 +470,15 @@ class JerryDebugger(object):
|
||||
breakpoint.offset)
|
||||
self.send_message(message)
|
||||
|
||||
def send_bytecode_cp(self, byte_code_cp):
|
||||
message = struct.pack(self.byte_order + "BBIB" + self.cp_format,
|
||||
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||
WEBSOCKET_FIN_BIT + 1 + self.cp_size,
|
||||
0,
|
||||
JERRY_DEBUGGER_FREE_BYTE_CODE_CP,
|
||||
byte_code_cp)
|
||||
self.send_message(message)
|
||||
|
||||
def send_command(self, command):
|
||||
message = struct.pack(self.byte_order + "BBIB",
|
||||
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||
@@ -510,7 +524,11 @@ def parse_source(debugger, data):
|
||||
source_code = ""
|
||||
source_code_name = ""
|
||||
function_name = ""
|
||||
stack = [{"lines": [], "offsets": [], "name": ""}]
|
||||
stack = [{"line": 1,
|
||||
"column": 1,
|
||||
"name": "",
|
||||
"lines": [],
|
||||
"offsets": []}]
|
||||
new_function_list = {}
|
||||
|
||||
while True:
|
||||
@@ -537,8 +555,14 @@ def parse_source(debugger, data):
|
||||
|
||||
elif buffer_type == JERRY_DEBUGGER_PARSE_FUNCTION:
|
||||
logging.debug("Source name: %s, function name: %s" % (source_code_name, function_name))
|
||||
|
||||
position = struct.unpack(debugger.byte_order + debugger.idx_format + debugger.idx_format,
|
||||
data[3: 3 + 4 + 4])
|
||||
|
||||
stack.append({"source": source_code,
|
||||
"source_name": source_code_name,
|
||||
"line": position[0],
|
||||
"column": position[1],
|
||||
"name": function_name,
|
||||
"lines": [],
|
||||
"offsets": []})
|
||||
@@ -572,9 +596,12 @@ def parse_source(debugger, data):
|
||||
func_desc["source"] = source_code
|
||||
func_desc["source_name"] = source_code_name
|
||||
|
||||
function = JerryFunction(byte_code_cp,
|
||||
function = JerryFunction(len(stack) != 0,
|
||||
byte_code_cp,
|
||||
func_desc["source"],
|
||||
func_desc["source_name"],
|
||||
func_desc["line"],
|
||||
func_desc["column"],
|
||||
func_desc["name"],
|
||||
func_desc["lines"],
|
||||
func_desc["offsets"])
|
||||
@@ -585,6 +612,17 @@ def parse_source(debugger, data):
|
||||
logging.debug("Parse completed.")
|
||||
break
|
||||
|
||||
elif buffer_type == JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP:
|
||||
# Redefined functions are dropped during parsing.
|
||||
byte_code_cp = struct.unpack(debugger.byte_order + debugger.cp_format,
|
||||
data[3: 3 + debugger.cp_size])[0]
|
||||
|
||||
if byte_code_cp in new_function_list:
|
||||
del new_function_list[byte_code_cp]
|
||||
debugger.send_bytecode_cp(byte_code_cp)
|
||||
else:
|
||||
release_function(debugger, data)
|
||||
|
||||
else:
|
||||
logging.error("Parser error!")
|
||||
return
|
||||
@@ -612,14 +650,7 @@ def release_function(debugger, data):
|
||||
|
||||
del debugger.function_list[byte_code_cp]
|
||||
|
||||
message = struct.pack(debugger.byte_order + "BBIB" + debugger.cp_format,
|
||||
WEBSOCKET_BINARY_FRAME | WEBSOCKET_FIN_BIT,
|
||||
WEBSOCKET_FIN_BIT + 1 + debugger.cp_size,
|
||||
0,
|
||||
JERRY_DEBUGGER_FREE_BYTE_CODE_CP,
|
||||
byte_code_cp)
|
||||
|
||||
debugger.send_message(message)
|
||||
debugger.send_bytecode_cp(byte_code_cp)
|
||||
|
||||
logging.debug("Function {0x%x} byte-code released" % byte_code_cp)
|
||||
|
||||
@@ -656,10 +687,7 @@ def set_breakpoint(debugger, string):
|
||||
else:
|
||||
for function in debugger.function_list.values():
|
||||
if function.name == string:
|
||||
if function.first_line >= 0:
|
||||
enable_breakpoint(debugger, function.lines[function.first_line])
|
||||
else:
|
||||
print("Function %s has no breakpoints." % (string))
|
||||
enable_breakpoint(debugger, function.lines[function.first_breakpoint_line])
|
||||
found = True
|
||||
|
||||
if not found:
|
||||
|
||||
Reference in New Issue
Block a user