def __init__(self, project, binary_images, referenced_images=None, cpu_name=None, on_dsym_file_referenced=None): if isinstance(binary_images, ImageLookup): self.image_lookup = binary_images else: self.image_lookup = ImageLookup(binary_images) self._symbolizer = SymsyndSymbolizer() to_load = referenced_images if to_load is None: to_load = self.image_lookup.get_uuids() self.dsym_paths = ProjectDSymFile.dsymcache.fetch_dsyms( project, to_load, on_dsym_file_referenced=on_dsym_file_referenced) self.cpu_name = cpu_name
class Symbolizer(object): """This symbolizer dispatches to both symsynd and the system symbols we have in the database and reports errors slightly differently. """ def __init__(self, project, binary_images, referenced_images=None, cpu_name=None, on_dsym_file_referenced=None): if isinstance(binary_images, ImageLookup): self.image_lookup = binary_images else: self.image_lookup = ImageLookup(binary_images) self._symbolizer = SymsyndSymbolizer() to_load = referenced_images if to_load is None: to_load = self.image_lookup.get_uuids() self.dsym_paths = ProjectDSymFile.dsymcache.fetch_dsyms( project, to_load, on_dsym_file_referenced=on_dsym_file_referenced) self.cpu_name = cpu_name def close(self): self._symbolizer.close() def _process_frame(self, frame, img): symbol = trim(frame['symbol'], MAX_SYM) function = trim(demangle_symbol(frame['symbol'], simplified=True), MAX_SYM) frame['function'] = function if function != symbol: frame['symbol'] = symbol else: frame['symbol'] = None frame['filename'] = trim(frame.get('filename'), 256) frame['abs_path'] = trim(frame.get('abs_path'), 256) return frame def is_image_from_app_bundle(self, img, sdk_info=None): fn = img['name'] is_mac_platform = ( sdk_info is not None and sdk_info['sdk_name'].lower() == 'macos') if not (fn.startswith(APP_BUNDLE_PATHS) or (SIM_PATH in fn and SIM_APP_PATH in fn) or (is_mac_platform and MAC_OS_PATH in fn)): return False return True def _is_support_framework(self, img): """True if the frame is from a framework that is known and app bundled. Those are frameworks which are specifically not frameworks that are ever in_app. """ return _support_framework.search(img['name']) is not None def _is_app_bundled_framework(self, img): fn = img['name'] return fn.startswith(APP_BUNDLE_PATHS) and '/Frameworks/' in fn def _is_app_frame(self, instruction_addr, img, sdk_info=None): """Given a frame derives the value of `in_app` by discarding the original value of the frame. """ # Anything that is outside the app bundle is definitely not a # frame from out app. if not self.is_image_from_app_bundle(img, sdk_info=sdk_info): return False # We also do not consider known support frameworks to be part of # the app if self._is_support_framework(img): return False # Otherwise, yeah, let's just say it's in_app return True def _is_optional_dsym(self, img, sdk_info=None): """Checks if this is a dsym that is optional.""" # Frames that are not in the app are not considered optional. In # theory we should never reach this anyways. if not self.is_image_from_app_bundle(img, sdk_info=sdk_info): return False # If we're dealing with an app bundled framework that is also # considered optional. if self._is_app_bundled_framework(img): return True # Frameworks that are known to sentry and bundled helpers are always # optional for now. In theory this should always be False here # because we should catch it with the last branch already. if self._is_support_framework(img): return True return False def _is_simulator_frame(self, frame, img): return _sim_platform_re.search(img['name']) is not None def _symbolize_app_frame(self, instruction_addr, img, sdk_info=None): dsym_path = self.dsym_paths.get(img['uuid']) if dsym_path is None: if self._is_optional_dsym(img, sdk_info=sdk_info): type = EventError.NATIVE_MISSING_OPTIONALLY_BUNDLED_DSYM else: type = EventError.NATIVE_MISSING_DSYM raise SymbolicationFailed(type=type, image=img) # cputype of image might be a variation of self.cpu_name # e.g.: armv7 instead of armv7f # (example error fat file does not contain armv7f) cpu_name = get_cpu_name(img['cpu_type'], img['cpu_subtype']) try: rv = self._symbolizer.symbolize( dsym_path, img['image_vmaddr'], img['image_addr'], instruction_addr, cpu_name, symbolize_inlined=True) except SymbolicationError as e: raise SymbolicationFailed( type=EventError.NATIVE_BAD_DSYM, message=six.text_type(e), image=img ) if not rv: raise SymbolicationFailed( type=EventError.NATIVE_MISSING_SYMBOL, image=img ) return [self._process_frame(nf, img) for nf in reversed(rv)] def _convert_symbolserver_match(self, instruction_addr, symbolserver_match, img): """Symbolizes a frame with system symbols only.""" if symbolserver_match is None: return [] symbol = symbolserver_match['symbol'] if symbol[:1] == '_': symbol = symbol[1:] return [self._process_frame(dict( symbol=symbol, filename=None, abs_path=None, lineno=0, colno=0, package=symbolserver_match['object_name']), img)] def symbolize_frame(self, instruction_addr, sdk_info=None, symbolserver_match=None): # If we do not have a CPU name we fail. We currently only support # a single cpu architecture. if self.cpu_name is None: raise SymbolicationFailed( type=EventError.NATIVE_INTERNAL_FAILURE, message='Found multiple architectures.' ) img = self.image_lookup.find_image(instruction_addr) if img is None: raise SymbolicationFailed( type=EventError.NATIVE_UNKNOWN_IMAGE ) # If we are dealing with a frame that is not bundled with the app # we look at system symbols. If that fails, we go to looking for # app symbols explicitly. if not self.is_image_from_app_bundle(img, sdk_info=sdk_info): return self._convert_symbolserver_match(instruction_addr, symbolserver_match, img) return self._symbolize_app_frame( instruction_addr, img, sdk_info=sdk_info) def is_in_app(self, instruction_addr, sdk_info=None): img = self.image_lookup.find_image(instruction_addr) return img is not None and self._is_app_frame( instruction_addr, img, sdk_info=sdk_info) def is_internal_function(self, function): return _internal_function_re.search(function) is not None