Skip to content
Merged
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
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
]

dsym_requires = [
'symsynd>=0.3.0,<1.0.0',
'symsynd>=0.6.1,<1.0.0',
]


Expand Down
19 changes: 16 additions & 3 deletions src/sentry/lang/native/dsymcache.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import uuid
import time
import errno
import shutil
Expand Down Expand Up @@ -69,9 +70,21 @@ def fetch_dsym(self, project, image_uuid):
pass

with dsf.file.getfile() as sf:
with open(dsym + '_tmp', 'w') as df:
shutil.copyfileobj(sf, df)
os.rename(dsym + '_tmp', dsym)
suffix = '_%s' % uuid.uuid4()
done = False
try:
with open(dsym + suffix, 'w') as df:
shutil.copyfileobj(sf, df)
os.rename(dsym + suffix, dsym)
done = True
finally:
# Use finally here because it does not lie about the
# error on exit
if not done:
try:
os.remove(dsym + suffix)
except Exception:
pass

return base, dsym

Expand Down
14 changes: 8 additions & 6 deletions src/sentry/lang/native/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from sentry.models import Project
from sentry.plugins import Plugin2
from sentry.lang.native.symbolizer import make_symbolizer, have_symsynd
from sentry.lang.native.symbolizer import Symbolizer, have_symsynd


def exception_from_apple_error_or_diagnosis(error, diagnosis=None):
Expand Down Expand Up @@ -112,12 +112,14 @@ def preprocess_apple_crash_event(data):
if crashed_thread is None:
return

sym = make_symbolizer(project, crash_report['binary_images'],
threads=[crashed_thread])
with sym.driver:
bt = sym.symbolize_backtrace(crashed_thread['backtrace']['contents'])
system = crash_report.get('system')
sym = Symbolizer(project, crash_report['binary_images'],
threads=[crashed_thread])
with sym:
bt = sym.symbolize_backtrace(crashed_thread['backtrace']['contents'],
system)
inject_apple_backtrace(data, bt, crash.get('diagnosis'),
crash.get('error'), crash_report.get('system'))
crash.get('error'), system)

return data

Expand Down
63 changes: 63 additions & 0 deletions src/sentry/lang/native/symbolizer.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
try:
from symsynd.driver import Driver
from symsynd.report import ReportSymbolizer
from symsynd.macho.arch import get_cpu_name
from symsynd.demangle import demangle_symbol
have_symsynd = True
except ImportError:
have_symsynd = False

from sentry import options
from sentry.lang.native.dsymcache import dsymcache
from sentry.utils.safe import trim
from sentry.models import DSymSymbol
from sentry.models.dsymfile import MAX_SYM


def trim_frame(frame):
# This matches what's in stacktrace.py
frame['symbol_name'] = trim(frame.get('symbol_name'), MAX_SYM)
frame['filename'] = trim(frame.get('filename'), 256)
return frame


def find_system_symbol(img, instruction_addr, system_info=None):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Given the current index, anyone any objections to these two queries? Unfortunately they are sent individually for all N frames. I could union them up with a bit of extra work but the only way to really improve this would be a stored procedure i think.

"""Finds a system symbol."""
return DSymSymbol.objects.lookup_symbol(
instruction_addr=instruction_addr,
image_addr=img['image_addr'],
uuid=img['uuid'],
cpu_name=get_cpu_name(img['cpu_type'],
img['cpu_subtype']),
object_path=img['name'],
system_info=system_info
)


def make_symbolizer(project, binary_images, threads=None):
Expand Down Expand Up @@ -35,3 +60,41 @@ def make_symbolizer(project, binary_images, threads=None):

dsym_paths, loaded = dsymcache.fetch_dsyms(project, to_load)
return ReportSymbolizer(driver, dsym_paths, binary_images)


class Symbolizer(object):

def __init__(self, project, binary_images, threads=None):
self.symsynd_symbolizer = make_symbolizer(project, binary_images,
threads=threads)
self.images = dict((img['image_addr'], img) for img in binary_images)

def __enter__(self):
return self.symsynd_symbolizer.driver.__enter__()

def __exit__(self, *args):
return self.symsynd_symbolizer.driver.__exit__(*args)

def symbolize_frame(self, frame, system_info=None):
# Step one: try to symbolize with cached dsym files.
new_frame = self.symsynd_symbolizer.symbolize_frame(frame)
if new_frame is not None:
return trim_frame(new_frame)

# If that does not work, look up system symbols.
img = self.images.get(frame['object_addr'])
if img is not None:
symbol = find_system_symbol(img, frame['instruction_addr'],
system_info)
if symbol is not None:
symbol = demangle_symbol(symbol) or symbol
rv = dict(frame, symbol_name=symbol, filename=None,
line=0, column=0, uuid=img['uuid'])
return trim_frame(rv)

def symbolize_backtrace(self, backtrace, system_info=None):
rv = []
for frame in backtrace:
new_frame = self.symbolize_frame(frame, system_info)
rv.append(new_frame or frame)
return rv
Loading