class NativeStacktraceProcessor(StacktraceProcessor): def __init__(self, *args, **kwargs): StacktraceProcessor.__init__(self, *args, **kwargs) debug_meta = self.data.get('debug_meta') self.sym = None if debug_meta: self.available = True self.debug_meta = debug_meta self.sdk_info = get_sdk_from_event(self.data) else: self.available = False def close(self): StacktraceProcessor.close(self) if self.sym is not None: self.sym.close() self.sym = None def preprocess_related_data(self): if not self.available: return False is_debug_build = self.debug_meta.get('is_debug_build') referenced_images = find_stacktrace_referenced_images( self.debug_meta['images'], [x.stacktrace for x in self.stacktrace_infos]) self.sym = Symbolizer(self.project, self.debug_meta['images'], referenced_images=referenced_images, is_debug_build=is_debug_build) # The symbolizer gets a reference to the debug meta's images so # when it resolves the missing vmaddrs it changes them in the data # dict. return self.sym.resolve_missing_vmaddrs() def find_best_instruction(self, frame, stacktrace_info, idx): """Given a frame, stacktrace info and frame index this returns the interpolated instruction address we then use for symbolication later. """ meta = None # We only need to provide meta information for frame zero if idx == 0: # The signal is useful information for symsynd in some situations # to disambiugate the first frame. If we can get this information # from the mechanism we want to pass it onwards. signal = None exc = self.data.get('sentry.interfaces.Exception') if exc is not None: mechanism = exc['values'][0].get('mechanism') if mechanism and 'posix_signal' in mechanism and \ 'signal' in mechanism['posix_signal']: signal = mechanism['posix_signal']['signal'] meta = { 'frame_number': 0, 'registers': stacktrace_info.stacktrace.get('registers'), 'signal': signal, } return self.sym.find_best_instruction(frame, meta=meta) def process_frame(self, frame, stacktrace_info, idx): # XXX: warn on missing availability? # Only process frames here that are of supported platforms and # have the mandatory requirements for if not self.available or \ self.get_effective_platform(frame) != 'cocoa' or \ 'instruction_addr' not in frame: return None errors = [] # Construct a raw frame that is used by the symbolizer # backend. We only assemble the bare minimum we need here. sym_input_frame = { 'object_name': frame.get('package'), 'instruction_addr': self.find_best_instruction(frame, stacktrace_info, idx), 'symbol_name': frame.get('function'), 'symbol_addr': frame.get('symbol_addr'), } in_app = self.sym.is_in_app(sym_input_frame) new_frames = [] raw_frame = dict(frame) raw_frame['in_app'] = in_app try: symbolicated_frames = self.sym.symbolize_frame( sym_input_frame, self.sdk_info, symbolize_inlined=True) if not symbolicated_frames: return None, [raw_frame], [] except SymbolicationFailed as e: errors = [] if e.is_user_fixable or e.is_sdk_failure: errors.append({ 'type': e.type, 'image_uuid': e.image_uuid, 'image_path': e.image_path, 'image_arch': e.image_arch, 'message': e.message, }) else: logger.debug('Failed to symbolicate with native backend', exc_info=True) return None, [raw_frame], errors for sfrm in symbolicated_frames: symbol = sfrm.get('symbol_name') or \ frame.get('function') or '<unknown>' function = demangle_symbol(symbol, simplified=True) new_frame = dict(frame) new_frame['function'] = function # If we demangled something, store the original in the # symbol portion of the frame if function != symbol: new_frame['symbol'] = symbol new_frame['abs_path'] = sfrm.get('filename') or None if new_frame['abs_path']: new_frame['filename'] = posixpath.basename( new_frame['abs_path']) if sfrm.get('line') is not None: new_frame['lineno'] = sfrm['line'] if sfrm.get('column') is not None: new_frame['colno'] = sfrm['column'] new_frame['package'] = sfrm['object_name'] \ or new_frame.get('package') new_frame['in_app'] = in_app new_frames.append(new_frame) return new_frames, [raw_frame], []