Skip to content

add memory trace parser to mbed client #789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions mbed/mbed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1933,7 +1933,7 @@ def formaturl(url, format="default"):
return url

# Wrapper for the MbedTermnal functionality
def mbed_sterm(port, baudrate=9600, echo=True, reset=False, sterm=False):
def mbed_sterm(port, baudrate=9600, echo=True, reset=False, trace=None, sterm=False):
try:
from .mbed_terminal import MbedTerminal
except(ValueError): # relative import fails on Python 2 in non-package mode
Expand All @@ -1952,7 +1952,7 @@ def mbed_sterm(port, baudrate=9600, echo=True, reset=False, sterm=False):
mbed_serial = MbedTerminal(port, baudrate=baudrate, echo=echo)

try:
if not mbed_serial.terminal():
if not mbed_serial.terminal(trace=trace):
error("Unable to open serial terminal.\nMost likely your pyserial is dated and needs to be updated with 'pip install --upgrade pyserial")
except:
pass
Expand Down Expand Up @@ -3117,11 +3117,12 @@ def detect():
dict(name=['-b', '--baudrate'], help='Communication baudrate. Default: 9600'),
dict(name=['-e', '--echo'], help='Switch local echo on/off. Default: on'),
dict(name=['-r', '--reset'], action='store_true', help='Reset the targets (via SendBreak) before opening terminal.'),
dict(name=['-t', '--trace'], dest='trace', type=str, help='Enable mbed memory tracing output formatter and set reset string to watch for. Default: off'),
hidden_aliases=['term'],
help='Open serial terminal to connected target.\n\n',
description=(
"Open serial terminal to connected target (usually board), or connect to a user-specified COM port\n"))
def sterm(port=None, baudrate=None, echo=None, reset=False, sterm=True):
def sterm(port=None, baudrate=None, echo=None, reset=False, trace=None, sterm=True):
# Gather remaining arguments
args = remainder
# Find the root of the program
Expand All @@ -3133,7 +3134,7 @@ def sterm(port=None, baudrate=None, echo=None, reset=False, sterm=True):

if port:
action("Opening serial terminal to the specified COM port \"%s\"" % port)
mbed_sterm(port, baudrate=baudrate, echo=echo, reset=reset, sterm=sterm)
mbed_sterm(port, baudrate=baudrate, echo=echo, reset=reset, trace=trace, sterm=sterm)
else:
action("Detecting connected targets/boards to your system...")
targets = program.get_detected_targets()
Expand All @@ -3145,7 +3146,7 @@ def sterm(port=None, baudrate=None, echo=None, reset=False, sterm=True):
action("Opening serial terminal to unknown target at \"%s\"" % target['serial'])
else:
action("Opening serial terminal to \"%s\"" % target['name'])
mbed_sterm(target['serial'], baudrate=baudrate, echo=echo, reset=reset, sterm=sterm)
mbed_sterm(target['serial'], baudrate=baudrate, echo=echo, reset=reset, trace=trace, sterm=sterm)


# Generic config command
Expand Down
132 changes: 132 additions & 0 deletions mbed/mbed_debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# mbed debug trace transformer, handling memory trace information
# Author: Matthias L. Jugel (@thinkberg)
#
# Copyright (c) 2018 ubirch GmbH, All Rights Reserved
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied.

import re
import sys

from serial.tools.miniterm import Transform

def debug(s):
sys.stderr.write("D "+repr(s)+"\n")

class MbedTransform(Transform):
mem = {}
allocated = 0
reset = None
buffer = ""

def __init__(self, reset_str=None):
self.reset = reset_str
if self.reset is not None:
sys.stderr.write("memory tracing enabled: resetting on '{}'\n".format(self.reset))

self.r_malloc = re.compile("([^#]*)#m:0x([0-9a-f]+);0x([0-9a-f]+)-(\\d+)")
self.r_calloc = re.compile("([^#]*)#c:0x([0-9a-f]+);0x([0-9a-f]+)-(\\d+);(\\d+)")
self.r_realloc = re.compile("([^#]*)#r:0x([0-9a-f]+);0x([0-9a-f]+)-0x([0-9a-f]+);(\\d+)")
self.r_free = re.compile("([^#]*)#f:0x([0-9a-f]+);0x([0-9a-f]+)-0x([0-9a-f]+)")

def rx(self, rx_input):
# collect lines
out = ""
for c in rx_input:
if c == '\r' or c == '\n':
if len(self.buffer):
out += self.trace_line(self.buffer)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the place where it runs the data through the trace matcher. The transformer class collects data until it sees either \r or \n

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the reference

else:
out += "\n"
self.buffer = ""
continue
else:
self.buffer += c
continue
return out

def trace_line(self, line):
# strip newline and carriage return
line = line.rstrip('\n').rstrip('\r')

# if we detect the reset keyword, rest the map and memory counter
if self.reset is not None and self.reset in line:
self.mem = {}
self.allocated = 0
line += "\n\n\033[91m>> RESET HEAP COUNTERS >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\033[0m\n"
line += "\033[4m (NUM) TOTAL [ ADDRESS] CHANGE (COMMENT)\033[0m"
return line

# match malloc, realloc and free
m = self.r_malloc.search(line)
if m:
out = ""
if len(m.group(1)):
out += m.group(1)
if m.group(2) == "0x0":
out += "\033[1m!! (%03d) \033[31mmalloc failed\033[0m (%s)\n" % (len(self.mem), line)
else:
self.mem[m.group(2)] = int(m.group(4))
self.allocated += int(m.group(4))
out += "\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[31m+%-6d\033[0m (%s)" % \
(len(self.mem), self.allocated, int(m.group(2), 16), int(m.group(4)), line.replace(m.group(1), ""))
return out

m = self.r_calloc.search(line)
if m:
out = ""
if len(m.group(1)):
out += m.group(1)
if m.group(2) == "0x0":
out += "\033[1m!! (%03d) \033[31mcalloc failed\033[0m (%s)\n" % (len(self.mem), line)
else:
total = int(m.group(4)) * int(m.group(5))
self.mem[m.group(2)] = total
self.allocated += total
out += "\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[31m+%-6d\033[0m (%s)" % \
(len(self.mem), self.allocated, int(m.group(2), 16), total, line.replace(m.group(1), ""))
return out

m = self.r_realloc.search(line)
if m:
out = ""
if len(m.group(1)):
out += m.group(1)
diff = 0
if self.mem.has_key(m.group(2)):
diff = int(m.group(5)) - self.mem[m.group(2)]
self.mem[m.group(2)] = int(m.group(5))
else:
out += "\033[33m!! (%03d) WARN: realloc() without previous allocation\033[0m (%s)\n" % (len(self.mem), line)
self.allocated += diff
out += "\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[35m+%-6d\033[0m (%s)" % \
(len(self.mem), self.allocated, int(m.group(2), 16), diff, line.replace(m.group(1), ""))
return out

m = self.r_free.search(line)
if m:
out = ""
if len(m.group(1)):
out += m.group(1)
freed = 0
if self.mem.has_key(m.group(4)):
freed = self.mem[m.group(4)]
self.allocated -= freed
del self.mem[m.group(4)]
else:
out += "\033[33m!! (%03d) WARN: free(0x%s)\033[0m\n" % (len(self.mem), m.group(4))
out += "\033[1m== (%03d) \033[34m%8d\033[0m [%8x] \033[92m-%-6d\033[0m (%s)" % \
(len(self.mem), self.allocated, int(m.group(4), 16), freed, line.replace(m.group(1), ""))
return out

# print all other lines as is, so we can still use the log functionality
return line
12 changes: 10 additions & 2 deletions mbed/mbed_terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,15 @@ def __init__(self, port, baudrate=9600, echo=True, timeout=10):
self.serial = None
return

def terminal(self, print_header=True):
def terminal(self, print_header=True, trace=None):
try:
import serial.tools.miniterm as miniterm
except (IOError, ImportError, OSError):
try:
from .mbed_debug import MbedTransform
except(ValueError):
from mbed_debug import MbedTransform
except (IOError, ImportError, OSError) as e:
print(repr(e))
return False

term = miniterm.Miniterm(self.serial, echo=self.echo)
Expand All @@ -53,6 +58,9 @@ def terminal(self, print_header=True):
term.set_rx_encoding('UTF-8')
term.set_tx_encoding('UTF-8')

if trace:
term.rx_transformations += [MbedTransform(reset_str=trace)]

def console_print(text):
term.console.write('--- %s ---\n' % text)

Expand Down