Skip to content

Commit

Permalink
Merge pull request #35 from chelsee/master
Browse files Browse the repository at this point in the history
Changes to Debugger
  • Loading branch information
pg-cse authored Dec 4, 2018
2 parents a258b98 + b710323 commit b2fa7b6
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 40 deletions.
70 changes: 54 additions & 16 deletions Debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import argparse
import os
from pathlib import Path
import socket

__author__ = "CSE"
__copyright__ = "Copyright 2015, CSE"
Expand Down Expand Up @@ -73,49 +75,84 @@ def parseCommandLineArgs():
help="This is required if -s option was used on the linker. That will allow "
"binary to be loader at correct address specified inside the binary")

parser.add_argument("-bp", "--breakpoint",
required=False,
type=str,
default='',
help="This is an optional argument. If present, debugger will load breakpoints "
"previously defined and stored in the specified file.")

args = parser.parse_args()

return args


def validatePaths(argsWithPaths):
def validateFilePath(filename, ext=None):
"""
This function will simply validate that the input path exists and that the output path
is free for the system to use
:param argsWithPaths: An input parsed object as provided by argparse.parse_args()
:return: This does not return. Simply raises ValueError in cases where paths are not valid.
This function will simply validate file existence and file extension.
If either is not true, then will raise an error.
:param filename: str, file path to be validated
:param ext: str, expected extension
:return: boolean True,
"""
gotSymbols = False
if not os.path.exists(argsWithPaths.input):
raise ValueError("ERROR: file {} does not exists.".format(argsWithPaths.input,))
if not os.path.exists(filename):
raise ValueError("ERROR: File {} does not exists.".format(filename,))
else:
if os.path.exists(argsWithPaths.input.split(".")[0] + ".sym"):
gotSymbols = True
fileExt = filename.split(".")[-1]
if fileExt != ext:
raise ValueError("ERROR: Incorrect file extension on file {}".format(filename,))

return True


return gotSymbols
def validateBreakPointFile(usable_args):
"""
This function will check if user provided a breakpoint file.
If user provided a file that does not exist, function will create the file.
If user did not provide a file, no breakpoint file will be used.
:param usable_args: An input parsed object as provided by argparse.parse_args()
:return inputBreakpointFile: str, name of the breakpointFile that will be used by Debugger
"""
inputBreakpointFile = usable_args.breakpoint

if not os.path.exists(inputBreakpointFile):
Path(inputBreakpointFile).touch()
return inputBreakpointFile
else:
return inputBreakpointFile


if __name__ == '__main__':
usableArgs = parseCommandLineArgs()

goodInputFile = False
gotSymbols = False
symbolsFile = None
breakpointFile = ''

if usableArgs.input is not None:
gotSymbols = validatePaths(usableArgs) # Make sure the parsed info is usable before using it!
symbolsFile = usableArgs.input.split(".")[0] + ".sym" if gotSymbols else ""
# Make sure the parsed info is usable before using it!
goodInputFile = validateFilePath(usableArgs.input, ext="bin")
gotSymbols = validateFilePath(usableArgs.input.split(".")[0] + ".sym", ext="sym")
breakpointFile = validateBreakPointFile(usableArgs)
else:
usableArgs.input = FIRMWARE_BINARY_FILE_PATH
usableArgs.software = False
usableArgs.address = FIRMWARE_LOAD_ADDRESS

if gotSymbols:
symbolsFile = usableArgs.input.split(".")[0] + ".sym"

print("Debug session about to begin, following options will be used")
print(" input file: {}".format(usableArgs.input,))
if gotSymbols:
print(" symbols file: {}".format(symbolsFile,))
print(" symbols file: {}".format(symbolsFile,))
if usableArgs.output is not None:
print(" output file: {}".format(usableArgs.output,))
print(" output file: {}".format(usableArgs.output,))
if breakpointFile is not '':
print(" breakpoint file: {}".format(breakpointFile,))
else:
print(" session starting without breakpoint file")

if usableArgs.address is None:
usableArgs.address = DEFAULT_LOAD_ADDRESS
Expand All @@ -124,7 +161,8 @@ def validatePaths(argsWithPaths):
outputFile=usableArgs.output,
loadAddress=usableArgs.address,
softwareLoader=usableArgs.software,
symbolsFile=symbolsFile)
symbolsFile=symbolsFile,
breakpointFile=breakpointFile)
if usableArgs.output is not None and os.path.exists(usableArgs.output[0]):
# The assembler did the job correctly and the out file has been written to disk!
print("Debug session is over, output file has been written to {}". format(usableArgs.output,))
99 changes: 75 additions & 24 deletions ToolChain/Debugger/Debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,28 +58,29 @@ class Debugger:
will create an execution environment load the input file to that environment and start execution as
required.
"""

outputFile = None
capua = None
breakPoints = None
breakPoints = []
symbols = None
breakpointFile = None

def __init__(self, inputFile=None,
outputFile=None,
loadAddress=DEFAULT_LOAD_ADDRESS,
softwareLoader=False,
symbolsFile=None):
symbolsFile=None,
breakpointFile=None):
"""
Building the debugger
:param inputFile: The input file that needs to be loaded in memory
:param outputFile: str, the name of the output file, output file is used for logging
:param loadAddress: int, the address a which the binary will be loaded. This is required to resolve ref address
:param softwareLoader: bool, is the loading done with the "software" option
:param symbolsFile: str, the path to the symbols file to be loaded
:param breakpointFile: str, path to breakpoints stored after adding breakpoints in debugger
:return:
"""

self.breakPoints = []
self.breakpointFile = breakpointFile

# First thing we need is to setup logging facilities
self.setupLoggingFacilities(outputFile)
Expand All @@ -93,27 +94,62 @@ def __init__(self, inputFile=None,
self.loadProgram(inputFile=inputFile, loadAddress=loadAddress, softwareLoader=softwareLoader)

# If we have the symbols, load them into the appropriate member
if symbolsFile != "" and symbolsFile is not None:
if symbolsFile is not None:
self.symbols = {}
self.loadSymbols(symbolsFile=symbolsFile)

if self.breakpointFile is not '':
self.breakPoints = self.loadBreakpoints(self.breakpointFile)

# At this point, debugging session is ready to be used
self.debugLog("Debugging session is ready to be used. Have fun!")
self.debug(inputFile=inputFile)
self.tearDownLoggingFacilities()

def loadBreakpoints(self, breakpointFile):
"""
This will simply load the breakpoints from file
:param breakpointFile: str, path to file storing breakpoints
:return: list, breakpoints contained in the breakpoint file
"""
self.debugLog("Loading breakpoints from file {}".format(breakpointFile, ))

try:
with open(breakpointFile, "r") as file:
breakpointFileContent = file.readlines()
except FileNotFoundError as e:
raise FileNotFoundError("Error, file {} not found during breakpoint loading".format(breakpointFile,))

breakPoints = []
lineNumber =1

for line in breakpointFileContent:
if line != "":
line = line.strip("\n")
try:
breakPoints.append(int(line))
lineNumber += 1
except ValueError as e:
raise ValueError("Error, invalid address at line {} in {}. "
"Address must be an integer.".format(lineNumber, breakpointFile,))

self.debugLog("Done loading breakpoints")

return breakPoints

def loadSymbols(self, symbolsFile=None):
"""
This will simply load the symbols so that they can be used
:param symbolsFile: str, path to the symbols file
:return:
"""

self.debugLog("Loading symbols from file {}".format(symbolsFile,))

file = open(symbolsFile, "r")
content = file.readlines()
file.close()
try:
with open(symbolsFile, "r") as file:
content = file.readlines()
except FileNotFoundError as e:
raise FileNotFoundError("Error, symbols file {} not found during symbols loading".format(symbolsFile,))

for line in content:
if line != "":
Expand All @@ -129,7 +165,6 @@ def translateSymbolToAddress(self, symbol=None):
:param symbol: str, the symbol to be translated
:return: int, the address where to find the symbol, none if symbol is can't be resolved
"""

if "." not in symbol:
# User is trying to go without file resolution
found = None
Expand Down Expand Up @@ -196,7 +231,6 @@ def loadProgram(self, inputFile=None, loadAddress=DEFAULT_LOAD_ADDRESS, software
:param softwareLoader: bool, is the loading done with the "software" option
:return:
"""

# First we get the content of the binary file that needs to be loaded into memory
content = self.getBinary(inputFile)

Expand Down Expand Up @@ -228,9 +262,11 @@ def getBinary(self, inputFile=None):
"""
content = b""

binFile = open(inputFile, "rb")
content = binFile.read()
binFile.close()
try:
with open(inputFile, "rb") as binFile:
content = binFile.read()
except FileNotFoundError as e:
raise FileNotFoundError("Error, file {} not found while retrieving binary data".format(inputFile,))

return content

Expand All @@ -240,7 +276,6 @@ def debug(self, inputFile=None):
debugging session.
:return:
"""

while True:
self.debugLog("\nNext instruction to be executed:")
self.displayNextInstruction()
Expand All @@ -266,7 +301,6 @@ def displayXInstructionAtAddress(self, address=None, x: int=None):
:param x: int, how many instructions do we want to display
:return:
"""

# We have to validate the user used a symbol for the address
if type(address) is not int:
try:
Expand Down Expand Up @@ -362,7 +396,6 @@ def displayCPUInformation(self, register: str=None):
This will display cpu register to the console
:return:
"""

# Display all registers to the console
self.debugLog("Register information for {}".format(self.capua.eu.name,))
self.debugLog("{} = {} {}".format("A ", self.capua.eu.A, hex(self.capua.eu.A),))
Expand Down Expand Up @@ -397,7 +430,6 @@ def displayMemoryInFormat(self, address=None, length: int=4, displayFormat: str=
:param displayFormat:
:return: Nothing, simply display the thing
"""

# We have to validate the user used a symbol for the address
try:
address = int(address, 16)
Expand Down Expand Up @@ -473,7 +505,6 @@ def runUserCommand(self, command: str=None):
:param command: str, a string that needs to be parsed
:return:
"""

brokenCommand = command.split()

if len(brokenCommand) == 0:
Expand Down Expand Up @@ -554,11 +585,14 @@ def runToBreakPoint(self):
def removeBreakPoint(self, number: int=None):
"""
This will remove a single breakpoint from the list of breakpoint
and updates the breakpoints in file
:param number: breakpoint number to remove
:return:
"""
self.breakPoints.remove(self.breakPoints[number])
self.breakPoints.sort()
if self.breakpointFile is not '':
self.writeBreakPointFile()

def displayBreakPoints(self):
"""
Expand All @@ -577,6 +611,7 @@ def displayBreakPoints(self):
def addBreakPoint(self, address=None):
"""
This will simply add a break point into the break point list
and updates the breakpoints in file
:param address: a valid capua address written in hexadecimal
:return:
"""
Expand All @@ -589,8 +624,25 @@ def addBreakPoint(self, address=None):
self.debugLog("Error while processing address or symbol {}".format(address,))
return

self.breakPoints.append(address)
self.breakPoints.sort()
if address not in self.breakPoints:
self.breakPoints.append(address)
self.breakPoints.sort()
if self.breakpointFile is not '':
self.writeBreakPointFile()
else:
self.debugLog("Error breakpoint already exist")

def writeBreakPointFile(self):
"""
This function writes the breakpoints stored in self.breakpoints to a file
:return:
"""
try:
with open(self.breakpointFile, "w") as file:
for item in self.breakPoints:
file.write("%s\n" % item)
except PermissionError as e:
raise PermissionError("Error, unable to update breakpoints. Permission denied {}".format(self.breakpointFile,))

def debugLog(self, message:str="", screenDisplay:bool=True):
"""
Expand All @@ -599,8 +651,7 @@ def debugLog(self, message:str="", screenDisplay:bool=True):
:param screenDisplay: If true, the message will be sent to the display
:return: Nothing
"""

if self.outputFile is not None:
self.outputFile.write(message)
if screenDisplay:
print(message)
print(message)

0 comments on commit b2fa7b6

Please sign in to comment.