Skip to content

Instantly share code, notes, and snippets.

@mikezucc
Last active December 4, 2018 18:56
Show Gist options
  • Save mikezucc/3cb24a0bc36ef13997bd32210db17db1 to your computer and use it in GitHub Desktop.
Save mikezucc/3cb24a0bc36ef13997bd32210db17db1 to your computer and use it in GitHub Desktop.
Version 1 of the assertion reporting platform
import lldb
import json
import subprocess
from os.path import expanduser
import time
import os
import sys
home = expanduser("~")
def getBreakpointIndexesOfCFunction(cFunctionName, interpreter):
res = lldb.SBCommandReturnObject()
interpreter.HandleCommand('breakpoint list', res)
res.Succeeded()
output = res.GetOutput()
if cFunctionName in output:
splits = output.split('\n')
return [line.split(':')[0] for line in splits if cFunctionName in line]
return []
def deleteBreakpointAtCFunction(cFunctionName, interpreter):
res = lldb.SBCommandReturnObject()
breakpointIndexes = getBreakpointIndexesOfCFunction(cFunctionName, interpreter)
for breakpointIndex in breakpointIndexes:
interpreter.HandleCommand('breakpoint delete ' + breakpointIndex, res)
def setBreakpointAtCFunction(cFunctionName, interpreter):
res = lldb.SBCommandReturnObject()
# interpreter.HandleCommand('breakpoint set --name ' + cFunctionName + ' -C breakpointonassert', res)
interpreter.HandleCommand('breakpoint set --name ' + cFunctionName, res)
breakpointIndexes = getBreakpointIndexesOfCFunction(cFunctionName, interpreter)
breakpointID = breakpointIndexes[0]
interpreter.HandleCommand('breakpoint command add ' + breakpointID + ' -F "reportassert.BreakpointOnAssert"', res)
setCommand = res.Succeeded()
interpreter.HandleCommand('breakpoint modify --auto-continue true ' + breakpointID, res)
print 'Assert Breakpoint Definition: ' + str(setCommand and res.Succeeded())
def BreakpointOnAssert(frame, bp_loc, dict):
thread = frame.GetThread()
process = thread.GetProcess()
target = process.GetTarget()
debugger = target.GetDebugger()
interpreter = debugger.GetCommandInterpreter()
command_result = lldb.SBCommandReturnObject()
# Here we would actually want to reset to the GetAsync old value, however,
# I am over prodding the mysterious ethereus Beast that is LLDB PYTHON
debugger.SetAsync(False)
print "SENDING IP TO THE NSA YEET"
processedCallStack = []
for frame in thread.frames:
frameName = frame.GetFunction().GetDisplayName()
frameValuesList = frame.GetVariables(True, True, False, False)
callFrameVariables = []
for value in frameValuesList:
try:
valueType = value.GetTypeName()
if valueType is None:
continue
path = lldb.SBStream()
value.GetExpressionPath(path)
expressableName = path.GetData()
varNiceValue = value.GetValue()
if '*' in str(valueType):
debugResult = frame.EvaluateExpression('[' + str(expressableName) + ' debugDescription]')
if debugResult is not None:
varNiceValue = debugResult.GetSummary()
if varNiceValue is None:
varNiceValue = "nil or could not access address"
callFrameVariables.append({"name": expressableName, "type": valueType, "value": varNiceValue})
except Exception as e:
print e
x = 1
processedCallStack.append({"frame": str(frameName), "variables": callFrameVariables})
reportTime = time.strftime("%Y-%m-%d %H:%M")
report = { "callstack": processedCallStack,
"target": str(target),
"time": reportTime }
print report
jsonString = json.dumps(report)
with open(home + '/assertion_results', 'w+') as results_file:
results_file.write(jsonString)
# response = urllib.urlopen('http://localhost:3003/assert', data=jsonString.encode('utf8'))
# print response.read().decode('utf8')
# reportInvocation = Popen(["python", "~/post_assertion.py"], stdin=None, stdout=None, stderr=None, shell=True)
# subprocess.check_output('nix-shell . --run "python ' + home + '/post_assertion.py"', shell=True)
# req = urllib2.Request(url,
# headers = {
# "Content-Type": "application/json",
# "Accept": "*/*",
# }, data = jsonString)
# f = urllib2.urlopen(req)
# reportInvocation.wait()we
# print reportInvocation.returncode
devnull = open(os.devnull, 'wb') # Use this in Python < 3.3
# Python >= 3.3 has subprocess.DEVNULL
subprocess.Popen(['nohup', home + '/post_assertion.py'], stdout=devnull, stderr=devnull, preexec_fn=os.setpgrp, shell=True, env='$PATH')
debugger.SetAsync(True)
return True
def __lldb_init_module(debugger, internal_dict):
interpreter = debugger.GetCommandInterpreter()
res = lldb.SBCommandReturnObject()
interpreter.HandleCommand('command script import -r reportassert.py', res)
print 'Initialize Script: ' + str(res.Succeeded())
deleteBreakpointAtCFunction('BreakpointFindMe', interpreter)
setBreakpointAtCFunction('BreakpointFindMe', interpreter)
return None
@mikezucc
Copy link
Author

mikezucc commented Dec 1, 2018

Spent about 1 BILLION hours trying to axe kick my way through obscure LLDB email threads, stack overflows, and chisel/lldb's own test code to find out just how the F to make an actually "Synchronous" python command set. The most infuriating (funny) part of this was the constant usage of quotations to describe the synchronocity of the debugger context in Python by Jim Ingham. Like, mate, this was already not clear lol.

Any way out the billion ways to set up these commands out of nowhere that should silently run in the background, you need to use this exact configuration command script import -r reportassert.py script and breakpoint command add ' + breakpointID + ' -F "reportassert.BreakpointOnAssert" the -F usage otherwise XCode takes an absolute shit and hangs, looping waiting for your script to "return".

@mikezucc
Copy link
Author

mikezucc commented Dec 1, 2018

Also originally I thought the subprocess calls were what was hanging LLDB, thats why there are few different methods there

@mikezucc
Copy link
Author

mikezucc commented Dec 3, 2018

Latest: Recursively prints arguments/ visible variables in scope in each frame.
Next: Working on printing ivars

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment