예제 #1
0
파일: plugin.py 프로젝트: gilsonbp/sentry
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 process_frame(self, frame):
        # 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 \
           'image_addr' not in frame or \
           'instruction_addr' not in frame or \
           'symbol_addr' not in frame:
            return None

        errors = []

        # Construct a raw frame that is used by the symbolizer
        # backend.
        sym_frame = {
            'object_name': frame.get('package'),
            'object_addr': frame['image_addr'],
            'instruction_addr': frame['instruction_addr'],
            'symbol_name': frame.get('function'),
            'symbol_addr': frame['symbol_addr'],
        }
        new_frame = dict(frame)
        raw_frame = dict(frame)

        try:
            sfrm = self.sym.symbolize_frame(sym_frame, self.sdk_info)
        except SymbolicationFailed as e:
            if e.is_user_fixable or e.is_sdk_failure:
                errors.append({
                    'type': EventError.NATIVE_INTERNAL_FAILURE,
                    '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)
        else:
            symbol = sfrm.get('symbol_name') or \
                new_frame.get('function') or '<unknown>'
            function = demangle_symbol(symbol, simplified=True)

            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']
            else:
                new_frame['instruction_offset'] = \
                    parse_addr(sfrm['instruction_addr']) - \
                    parse_addr(sfrm['symbol_addr'])
            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['symbol_addr'] = '0x%x' % \
                parse_addr(sfrm['symbol_addr'])
            new_frame['instruction_addr'] = '0x%x' % parse_addr(
                sfrm['instruction_addr'])

        in_app = self.sym.is_in_app(sym_frame)
        new_frame['in_app'] = raw_frame['in_app'] = in_app
        return [new_frame], [raw_frame], errors
예제 #2
0
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], []
예제 #3
0
class NativeStacktraceProcessor(StacktraceProcessor):
    def __init__(self, *args, **kwargs):
        StacktraceProcessor.__init__(self, *args, **kwargs)
        debug_meta = self.data.get('debug_meta')
        self.cpu_name = cpu_name_from_data(self.data)
        self.sym = None
        self.dsyms_referenced = set()
        if debug_meta:
            self.available = True
            self.debug_meta = debug_meta
            self.sdk_info = get_sdk_from_event(self.data)
            self.image_lookup = ImageLookup(self.debug_meta['images'])
        else:
            self.available = False

    def close(self):
        StacktraceProcessor.close(self)
        if self.dsyms_referenced:
            metrics.incr('dsyms.processed',
                         amount=len(self.dsyms_referenced),
                         instance=self.project.id)
        if self.sym is not None:
            self.sym.close()
            self.sym = None

    def find_best_instruction(self, processable_frame):
        """Given a frame, stacktrace info and frame index this returns the
        interpolated instruction address we then use for symbolication later.
        """
        if self.cpu_name is None:
            return parse_addr(processable_frame['instruction_addr'])
        meta = None

        # We only need to provide meta information for frame zero
        if processable_frame.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':
                processable_frame.stacktrace_info.stacktrace.get('registers'),
                'signal':
                signal,
            }

        return find_best_instruction(processable_frame['instruction_addr'],
                                     self.cpu_name,
                                     meta=meta)

    def handles_frame(self, frame, stacktrace_info):
        platform = frame.get('platform') or self.data.get('platform')
        return (platform == 'cocoa' and self.available
                and 'instruction_addr' in frame)

    def preprocess_frame(self, processable_frame):
        instr_addr = self.find_best_instruction(processable_frame)
        img = self.image_lookup.find_image(instr_addr)

        processable_frame.data = {
            'instruction_addr': instr_addr,
            'image': img,
            'image_uuid': img['uuid'] if img is not None else None,
            'symbolserver_match': None,
        }

        if img is not None:
            processable_frame.set_cache_key_from_values((
                FRAME_CACHE_VERSION,
                # Because the images can move around, we want to rebase
                # the address for the cache key to be within the image
                # the same way as we do it in the symbolizer.
                rebase_addr(instr_addr, img),
                img['uuid'].lower(),
                img['cpu_type'],
                img['cpu_subtype'],
                img['image_size'],
            ))

    def preprocess_step(self, processing_task):
        if not self.available:
            return False

        referenced_images = set(
            pf.data['image_uuid']
            for pf in processing_task.iter_processable_frames(self)
            if pf.cache_value is None and pf.data['image_uuid'] is not None)

        def on_referenced(dsym_file):
            app_info = version_build_from_data(self.data)
            if app_info is not None:
                dsym_app = DSymApp.objects.create_or_update_app(
                    sync_id=None,
                    app_id=app_info.id,
                    project=self.project,
                    data={'name': app_info.name},
                    platform=DSymPlatform.APPLE,
                )
                try:
                    with transaction.atomic():
                        version_dsym_file, created = VersionDSymFile.objects.get_or_create(
                            dsym_file=dsym_file,
                            dsym_app=dsym_app,
                            version=app_info.version,
                            build=app_info.build,
                        )
                except IntegrityError:
                    # XXX: this can currently happen because we only
                    # support one app per dsym file.  Since this can
                    # happen in some cases anyways we ignore it.
                    pass

        self.sym = Symbolizer(self.project,
                              self.image_lookup,
                              cpu_name=self.cpu_name,
                              referenced_images=referenced_images,
                              on_dsym_file_referenced=on_referenced)

        # 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.
        data = self.sym.resolve_missing_vmaddrs()

        if options.get('symbolserver.enabled'):
            self.fetch_system_symbols(processing_task)

        return data

    def fetch_system_symbols(self, processing_task):
        to_lookup = []
        pf_list = []
        for pf in processing_task.iter_processable_frames(self):
            img = pf.data['image']
            if pf.cache_value is not None or img is None or \
               self.sym.is_frame_from_app_bundle(pf.frame, img):
                continue
            to_lookup.append({
                'object_uuid':
                img['uuid'],
                'object_name':
                img['name'],
                'addr':
                '0x%x' % rebase_addr(pf.data['instruction_addr'], img)
            })
            pf_list.append(pf)

        if not to_lookup:
            return

        rv = lookup_system_symbols(to_lookup, self.sdk_info, self.sym.cpu_name)
        if rv is not None:
            for symrv, pf in zip(rv, pf_list):
                if symrv is None:
                    continue
                pf.data['symbolserver_match'] = symrv

    def process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        errors = []

        new_frames = []
        raw_frame = dict(frame)
        if processable_frame.cache_value is None:
            # 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': processable_frame.data['instruction_addr'],
                'symbol_name': frame.get('function'),
            }
            in_app = self.sym.is_in_app(sym_input_frame)
            raw_frame['in_app'] = in_app
            img_uuid = processable_frame.data['image_uuid']
            if img_uuid is not None:
                self.dsyms_referenced.add(img_uuid)
            try:
                symbolicated_frames = self.sym.symbolize_frame(
                    sym_input_frame,
                    self.sdk_info,
                    symbolserver_match=processable_frame.
                    data['symbolserver_match'],
                    symbolize_inlined=True)
                if not symbolicated_frames:
                    return None, [raw_frame], []
            except SymbolicationFailed as e:
                reprocessing_active = False
                if self.project:
                    reprocessing_active = bool(
                        self.project.get_option('sentry:reprocessing_active',
                                                False))
                # User fixable but fatal errors are reported as processing
                # issues but only if the feature is activated.
                if reprocessing_active and e.is_user_fixable and e.is_fatal:
                    report_processing_issue(self.data,
                                            scope='native',
                                            object='dsym:%s' % e.image_uuid,
                                            type=e.type,
                                            data={
                                                'image_path': e.image_path,
                                                'image_uuid': e.image_uuid,
                                                'image_arch': e.image_arch,
                                                'message': e.message,
                                            })

                # This in many ways currently does not really do anything.
                # The reason is that once a processing issue is reported
                # the event will only be stored as a raw event and no
                # group will be generated.  As a result it also means that
                # we will not have any user facing event or error showing
                # up at all.  We want to keep this here though in case we
                # do not want to report some processing issues (eg:
                # optional dsyms)
                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 [raw_frame], [raw_frame], errors

            processable_frame.set_cache_value([in_app, symbolicated_frames])
        else:
            in_app, symbolicated_frames = processable_frame.cache_value
            raw_frame['in_app'] = in_app

        for sfrm in symbolicated_frames:
            symbol = sfrm.get('symbol_name') or \
                frame.get('function') or NATIVE_UNKNOWN_STRING
            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], []
예제 #4
0
class NativeStacktraceProcessor(StacktraceProcessor):

    def __init__(self, *args, **kwargs):
        StacktraceProcessor.__init__(self, *args, **kwargs)
        debug_meta = self.data.get('debug_meta')
        self.cpu_name = cpu_name_from_data(self.data)
        self.sym = None
        if debug_meta:
            self.available = True
            self.debug_meta = debug_meta
            self.sdk_info = get_sdk_from_event(self.data)
            self.image_lookup = ImageLookup(self.debug_meta['images'])
        else:
            self.available = False

    def close(self):
        StacktraceProcessor.close(self)
        if self.sym is not None:
            self.sym.close()
            self.sym = None

    def find_best_instruction(self, processable_frame):
        """Given a frame, stacktrace info and frame index this returns the
        interpolated instruction address we then use for symbolication later.
        """
        if self.cpu_name is None:
            return parse_addr(processable_frame['instruction_addr'])
        meta = None

        # We only need to provide meta information for frame zero
        if processable_frame.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': processable_frame.stacktrace_info.stacktrace.get('registers'),
                'signal': signal,
            }

        return find_best_instruction(
            processable_frame['instruction_addr'],
            self.cpu_name, meta=meta)

    def handles_frame(self, frame, stacktrace_info):
        platform = frame.get('platform') or self.data.get('platform')
        return (
            platform == 'cocoa' and
            self.available and
            'instruction_addr' in frame
        )

    def preprocess_frame(self, processable_frame):
        instr_addr = self.find_best_instruction(processable_frame)
        img = self.image_lookup.find_image(instr_addr)

        processable_frame.data = {
            'instruction_addr': instr_addr,
            'image': img,
            'image_uuid': img['uuid'] if img is not None else None,
            'symbolserver_match': None,
        }

        if img is not None:
            processable_frame.set_cache_key_from_values((
                FRAME_CACHE_VERSION,
                # Because the images can move around, we want to rebase
                # the address for the cache key to be within the image
                # the same way as we do it in the symbolizer.
                rebase_addr(instr_addr, img),
                img['uuid'].lower(),
                img['cpu_type'],
                img['cpu_subtype'],
                img['image_size'],
            ))

    def preprocess_step(self, processing_task):
        if not self.available:
            return False

        referenced_images = set(
            pf.data['image_uuid']
            for pf in processing_task.iter_processable_frames(self)
            if pf.cache_value is None and pf.data['image_uuid'] is not None)

        def on_referenced(dsym_file):
            app_info = version_build_from_data(self.data)
            if app_info is not None:
                dsym_app = DSymApp.objects.create_or_update_app(
                    sync_id=None,
                    app_id=app_info.id,
                    project=self.project,
                    data={'name': app_info.name},
                    platform=DSymPlatform.APPLE,
                )
                version_dsym_file, created = VersionDSymFile.objects.get_or_create(
                    dsym_file=dsym_file,
                    dsym_app=dsym_app,
                    version=app_info.version,
                    build=app_info.build,
                )

        self.sym = Symbolizer(self.project, self.image_lookup,
                              cpu_name=self.cpu_name,
                              referenced_images=referenced_images,
                              on_dsym_file_referenced=on_referenced)

        # 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.
        data = self.sym.resolve_missing_vmaddrs()

        if options.get('symbolserver.enabled'):
            self.fetch_system_symbols(processing_task)

        return data

    def fetch_system_symbols(self, processing_task):
        to_lookup = []
        pf_list = []
        for pf in processing_task.iter_processable_frames(self):
            img = pf.data['image']
            if pf.cache_value is not None or img is None or \
               self.sym.is_frame_from_app_bundle(pf.frame, img):
                continue
            to_lookup.append({
                'object_uuid': img['uuid'],
                'object_name': img['name'],
                'addr': '0x%x' % rebase_addr(pf.data['instruction_addr'], img)
            })
            pf_list.append(pf)

        if not to_lookup:
            return

        rv = lookup_system_symbols(to_lookup, self.sdk_info,
                                   self.sym.cpu_name)
        if rv is not None:
            for symrv, pf in zip(rv, pf_list):
                if symrv is None:
                    continue
                pf.data['symbolserver_match'] = symrv

    def process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        errors = []

        new_frames = []
        raw_frame = dict(frame)
        if processable_frame.cache_value is None:
            # 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': processable_frame.data['instruction_addr'],
                'symbol_name': frame.get('function'),
            }
            in_app = self.sym.is_in_app(sym_input_frame)
            raw_frame['in_app'] = in_app
            try:
                symbolicated_frames = self.sym.symbolize_frame(
                    sym_input_frame, self.sdk_info,
                    symbolserver_match=processable_frame.data['symbolserver_match'],
                    symbolize_inlined=True)
                if not symbolicated_frames:
                    return None, [raw_frame], []
            except SymbolicationFailed as e:
                reprocessing_active = False
                if self.project:
                    reprocessing_active = bool(
                        self.project.get_option('sentry:reprocessing_active', False)
                    )
                # User fixable but fatal errors are reported as processing
                # issues but only if the feature is activated.
                if reprocessing_active and e.is_user_fixable and e.is_fatal:
                    report_processing_issue(self.data,
                        scope='native',
                        object='dsym:%s' % e.image_uuid,
                        type=e.type,
                        data={
                            'image_path': e.image_path,
                            'image_uuid': e.image_uuid,
                            'image_arch': e.image_arch,
                            'message': e.message,
                        }
                    )

                # This in many ways currently does not really do anything.
                # The reason is that once a processing issue is reported
                # the event will only be stored as a raw event and no
                # group will be generated.  As a result it also means that
                # we will not have any user facing event or error showing
                # up at all.  We want to keep this here though in case we
                # do not want to report some processing issues (eg:
                # optional dsyms)
                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 [raw_frame], [raw_frame], errors

            processable_frame.set_cache_value([in_app, symbolicated_frames])
        else:
            in_app, symbolicated_frames = processable_frame.cache_value
            raw_frame['in_app'] = in_app

        for sfrm in symbolicated_frames:
            symbol = sfrm.get('symbol_name') or \
                frame.get('function') or NATIVE_UNKNOWN_STRING
            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], []
예제 #5
0
파일: plugin.py 프로젝트: sunyinhui/sentry
class NativeStacktraceProcessor(StacktraceProcessor):
    def __init__(self, *args, **kwargs):
        StacktraceProcessor.__init__(self, *args, **kwargs)
        debug_meta = self.data.get('debug_meta')
        self.cpu_name = cpu_name_from_data(self.data)
        self.sym = None
        if debug_meta:
            self.available = True
            self.debug_meta = debug_meta
            self.sdk_info = get_sdk_from_event(self.data)
            self.image_lookup = ImageLookup(self.debug_meta['images'])
        else:
            self.available = False

    def close(self):
        StacktraceProcessor.close(self)
        if self.sym is not None:
            self.sym.close()
            self.sym = None

    def find_best_instruction(self, processable_frame):
        """Given a frame, stacktrace info and frame index this returns the
        interpolated instruction address we then use for symbolication later.
        """
        if self.cpu_name is None:
            return parse_addr(processable_frame['instruction_addr'])
        meta = None

        # We only need to provide meta information for frame zero
        if processable_frame.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':
                processable_frame.stacktrace_info.stacktrace.get('registers'),
                'signal':
                signal,
            }

        return find_best_instruction(processable_frame['instruction_addr'],
                                     self.cpu_name,
                                     meta=meta)

    def handles_frame(self, frame, stacktrace_info):
        platform = frame.get('platform') or self.data.get('platform')
        return (platform == 'cocoa' and self.available
                and 'instruction_addr' in frame)

    def preprocess_frame(self, processable_frame):
        instr_addr = self.find_best_instruction(processable_frame)
        img = self.image_lookup.find_image(instr_addr)

        processable_frame.data = {
            'instruction_addr': instr_addr,
            'image_uuid': img['uuid'] if img is not None else None,
        }

        if img is not None:
            processable_frame.set_cache_key_from_values((
                FRAME_CACHE_VERSION,
                # Because the images can move around, we want to rebase
                # the address for the cache key to be within the image
                # the same way as we do it in the symbolizer.
                (parse_addr(img['image_vmaddr']) + instr_addr -
                 parse_addr(img['image_addr'])),
                img['uuid'].lower(),
                img['cpu_type'],
                img['cpu_subtype'],
                img['image_size'],
            ))

    def preprocess_step(self, processing_task):
        if not self.available:
            return False

        referenced_images = set(
            pf.data['image_uuid']
            for pf in processing_task.iter_processable_frames(self)
            if pf.cache_value is None and pf.data['image_uuid'] is not None)

        self.sym = Symbolizer(self.project,
                              self.image_lookup,
                              cpu_name=self.cpu_name,
                              referenced_images=referenced_images)

        # 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 process_frame(self, processable_frame, processing_task):
        frame = processable_frame.frame
        errors = []

        new_frames = []
        raw_frame = dict(frame)

        if processable_frame.cache_value is None:
            # 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': processable_frame.data['instruction_addr'],
                'symbol_name': frame.get('function'),
            }
            in_app = self.sym.is_in_app(sym_input_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

            processable_frame.set_cache_value([in_app, symbolicated_frames])
        else:
            in_app, symbolicated_frames = processable_frame.cache_value
            raw_frame['in_app'] = in_app

        for sfrm in symbolicated_frames:
            symbol = sfrm.get('symbol_name') or \
                frame.get('function') or NATIVE_UNKNOWN_STRING
            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], []