def test_merge_symbolicator_image_errors(code_file, error): raw_image = { "instruction_addr": 0xFEEBEE, "other": "foo", "code_file": code_file } sdk_info = {"sdk_name": "macos"} complete_image = { "debug_status": "found", "unwind_status": "missing", "other2": "bar", "arch": "unknown", } errors = [] merge_symbolicator_image(raw_image, complete_image, sdk_info, errors.append) e, = errors assert e.image_name == "foo" assert e.type == error assert raw_image == { "instruction_addr": 0xFEEBEE, "other": "foo", "other2": "bar", "code_file": code_file, }
def merge_symbolicator_minidump_response(data, response): sdk_info = get_sdk_from_event(data) # TODO(markus): Add OS context here when `merge_process_state_event` is no # longer called for symbolicator projects images = [] set_path(data, 'debug_meta', 'images', value=images) for complete_image in response['modules']: image = {} merge_symbolicator_image( image, complete_image, sdk_info, lambda e: handle_symbolication_failed(e, data=data) ) images.append(image) data_threads = [] data['threads'] = {'values': data_threads} data_exception = get_path(data, 'exception', 'values', 0) for complete_stacktrace in response['stacktraces']: is_requesting = complete_stacktrace.get('is_requesting') thread_id = complete_stacktrace.get('thread_id') data_thread = { 'id': thread_id, 'crashed': is_requesting, } data_threads.append(data_thread) if is_requesting: data_stacktrace = get_path(data_exception, 'stacktrace') assert isinstance(data_stacktrace, dict), data_stacktrace # Make exemption specifically for unreal portable callstacks # TODO(markus): Allow overriding stacktrace more generically # (without looking into unreal context) once we no longer parse # minidump in the endpoint (right now we can't distinguish that # from user json). if data_stacktrace['frames'] and is_unreal_exception_stacktrace(data): continue del data_stacktrace['frames'][:] else: data_thread['stacktrace'] = data_stacktrace = {'frames': []} if complete_stacktrace.get('registers'): data_stacktrace['registers'] = complete_stacktrace['registers'] for complete_frame in reversed(complete_stacktrace['frames']): new_frame = {} merge_symbolicated_frame(new_frame, complete_frame) data_stacktrace['frames'].append(new_frame)
def merge_symbolicator_minidump_response(data, response): sdk_info = get_sdk_from_event(data) # TODO(markus): Add OS context here when `merge_process_state_event` is no # longer called for symbolicator projects images = [] set_path(data, 'debug_meta', 'images', value=images) for complete_image in response['modules']: image = {} merge_symbolicator_image( image, complete_image, sdk_info, lambda e: handle_symbolication_failed(e, data=data)) images.append(image) data_threads = [] data['threads'] = {'values': data_threads} data_exception = get_path(data, 'exception', 'values', 0) for complete_stacktrace in response['stacktraces']: is_requesting = complete_stacktrace.get('is_requesting') thread_id = complete_stacktrace.get('thread_id') data_thread = { 'id': thread_id, 'crashed': is_requesting, } data_threads.append(data_thread) if is_requesting: data_stacktrace = get_path(data_exception, 'stacktrace') assert isinstance(data_stacktrace, dict), data_stacktrace # Make exemption specifically for unreal portable callstacks # TODO(markus): Allow overriding stacktrace more generically # (without looking into unreal context) once we no longer parse # minidump in the endpoint (right now we can't distinguish that # from user json). if data_stacktrace['frames'] and is_unreal_exception_stacktrace( data): continue data_stacktrace['frames'] = [] else: data_thread['stacktrace'] = data_stacktrace = {'frames': []} if complete_stacktrace.get('registers'): data_stacktrace['registers'] = complete_stacktrace['registers'] for complete_frame in reversed(complete_stacktrace['frames']): new_frame = {} merge_symbolicated_frame(new_frame, complete_frame) data_stacktrace['frames'].append(new_frame)
def test_merge_symbolicator_image_remove_unknown_arch(): raw_image = {"instruction_addr": 0xFEEBEE} sdk_info = {"sdk_name": "linux"} complete_image = { "debug_status": "found", "unwind_status": "found", "arch": "unknown", } errors = [] merge_symbolicator_image(raw_image, complete_image, sdk_info, errors.append) assert not errors assert raw_image == {"instruction_addr": 0xFEEBEE}
def test_merge_symbolicator_image_basic_success(): raw_image = {"instruction_addr": 0xFEEBEE, "other": "foo"} sdk_info = {"sdk_name": "linux"} complete_image = { "debug_status": "found", "unwind_status": "found", "other2": "bar", "arch": "foo", } errors = [] merge_symbolicator_image(raw_image, complete_image, sdk_info, errors.append) assert not errors assert raw_image == { "instruction_addr": 0xFEEBEE, "other": "foo", "other2": "bar", "arch": "foo", }
def test_merge_symbolicator_image_errors(code_file, error): raw_image = {"instruction_addr": 0xFEEBEE, "other": "foo", "code_file": code_file} sdk_info = {"sdk_name": "macos"} complete_image = { "debug_status": "found", "unwind_status": "missing", "other2": "bar", "arch": "unknown", } errors = [] merge_symbolicator_image(raw_image, complete_image, sdk_info, errors.append) e, = errors assert e.image_name == "foo" assert e.type == error assert raw_image == { "instruction_addr": 0xFEEBEE, "other": "foo", "other2": "bar", "code_file": code_file, }
def run_symbolicator(self, processing_task): # TODO(markus): Make this work with minidumps. An unprocessed minidump # event will not contain unsymbolicated frames, because the minidump # upload already happened in store. # It will also presumably not contain images, so `self.available` will # already be `False`. if not self.available: return request_id_cache_key = request_id_cache_key_for_event(self.data) stacktraces = [] processable_stacktraces = [] has_frames = False for stacktrace_info, pf_list in processing_task.iter_processable_stacktraces( ): registers = stacktrace_info.stacktrace.get('registers') or {} # The filtering condition of this list comprehension is copied # from `iter_processable_frames`. # # We cannot reuse `iter_processable_frames` because the # symbolicator currently expects a list of stacktraces, not # flat frames. # # Right now we can't even filter out frames (e.g. using a frame # cache locally). The stacktraces have to be as complete as # possible because the symbolicator assumes the first frame of # a stacktrace to be the crashing frame. This assumption is # already violated because the SDK might chop off frames though # (which is less likely to be the case though). pf_list = [pf for pf in reversed(pf_list) if pf.processor == self] frames = [] for pf in pf_list: frame = {'instruction_addr': pf['instruction_addr']} if pf.get('trust') is not None: frame['trust'] = pf['trust'] frames.append(frame) has_frames = True stacktraces.append({'registers': registers, 'frames': frames}) processable_stacktraces.append(pf_list) if not has_frames: return rv = run_symbolicator(project=self.project, request_id_cache_key=request_id_cache_key, stacktraces=stacktraces, modules=self.images, signal=self.signal) if not rv: handle_symbolication_failed( SymbolicationFailed( type=EventError.NATIVE_SYMBOLICATOR_FAILED), data=self.data, ) return # TODO(markus): Set signal and os context from symbolicator response, # for minidumps assert len(self.images) == len(rv['modules']), (self.images, rv) for image, complete_image in zip(self.images, rv['modules']): merge_symbolicator_image( image, complete_image, self.sdk_info, lambda e: handle_symbolication_failed(e, data=self.data)) assert len(stacktraces) == len(rv['stacktraces']) for pf_list, symbolicated_stacktrace in zip(processable_stacktraces, rv['stacktraces']): for symbolicated_frame in symbolicated_stacktrace.get( 'frames') or (): pf = pf_list[symbolicated_frame['original_index']] pf.data['symbolicator_match'].append(symbolicated_frame)
def merge_symbolicator_minidump_response(data, response): sdk_info = get_sdk_from_event(data) data['platform'] = 'native' if response.get('crashed') is not None: data['level'] = 'fatal' if response['crashed'] else 'info' if response.get('timestamp'): data['timestamp'] = float(response['timestamp']) if response.get('system_info'): merge_symbolicator_minidump_system_info(data, response['system_info']) images = [] set_path(data, 'debug_meta', 'images', value=images) for complete_image in response['modules']: image = {} merge_symbolicator_image( image, complete_image, sdk_info, lambda e: handle_symbolication_failed(e, data=data)) images.append(image) # Extract the crash reason and infos data_exception = get_path(data, 'exception', 'values', 0) exc_value = ('Assertion Error: %s' % response.get('assertion') if response.get('assertion') else 'Fatal Error: %s' % response.get('crash_reason')) data_exception['value'] = exc_value data_exception['type'] = response.get('crash_reason') data_threads = [] if response['stacktraces']: data['threads'] = {'values': data_threads} else: error = SymbolicationFailed(message='minidump has no thread list', type=EventError.NATIVE_SYMBOLICATOR_FAILED) handle_symbolication_failed(error, data=data) for complete_stacktrace in response['stacktraces']: is_requesting = complete_stacktrace.get('is_requesting') thread_id = complete_stacktrace.get('thread_id') data_thread = { 'id': thread_id, 'crashed': is_requesting, } data_threads.append(data_thread) if is_requesting: data_exception['thread_id'] = thread_id data_stacktrace = data_exception.setdefault('stacktrace', {}) # Make exemption specifically for unreal portable callstacks # TODO(markus): Allow overriding stacktrace more generically # (without looking into unreal context) once we no longer parse # minidump in the endpoint (right now we can't distinguish that # from user json). if data_stacktrace.get( 'frames') and is_unreal_exception_stacktrace(data): continue data_stacktrace['frames'] = [] else: data_thread['stacktrace'] = data_stacktrace = {'frames': []} if complete_stacktrace.get('registers'): data_stacktrace['registers'] = complete_stacktrace['registers'] for complete_frame in reversed(complete_stacktrace['frames']): new_frame = {} merge_symbolicated_frame(new_frame, complete_frame) data_stacktrace['frames'].append(new_frame)
def test_merge_symbolicator_image_empty(): errors = [] merge_symbolicator_image({}, {}, None, errors.append) assert not errors
def run_symbolicator(self, processing_task): # TODO(markus): Make this work with minidumps. An unprocessed minidump # event will not contain unsymbolicated frames, because the minidump # upload already happened in store. # It will also presumably not contain images, so `self.available` will # already be `False`. if not self.available: return request_id_cache_key = request_id_cache_key_for_event(self.data) stacktraces = [] processable_stacktraces = [] has_frames = False for stacktrace_info, pf_list in processing_task.iter_processable_stacktraces(): registers = stacktrace_info.stacktrace.get('registers') or {} # The filtering condition of this list comprehension is copied # from `iter_processable_frames`. # # We cannot reuse `iter_processable_frames` because the # symbolicator currently expects a list of stacktraces, not # flat frames. # # Right now we can't even filter out frames (e.g. using a frame # cache locally). The stacktraces have to be as complete as # possible because the symbolicator assumes the first frame of # a stacktrace to be the crashing frame. This assumption is # already violated because the SDK might chop off frames though # (which is less likely to be the case though). pf_list = [ pf for pf in reversed(pf_list) if pf.processor == self ] frames = [] for pf in pf_list: frame = {'instruction_addr': pf['instruction_addr']} if pf.get('trust') is not None: frame['trust'] = pf['trust'] frames.append(frame) has_frames = True stacktraces.append({ 'registers': registers, 'frames': frames }) processable_stacktraces.append(pf_list) if not has_frames: return rv = run_symbolicator( project=self.project, request_id_cache_key=request_id_cache_key, stacktraces=stacktraces, modules=self.images, signal=self.signal ) if not handle_symbolicator_response_status(self.data, rv): return # TODO(markus): Set signal and os context from symbolicator response, # for minidumps assert len(self.images) == len(rv['modules']), (self.images, rv) for image, complete_image in zip(self.images, rv['modules']): merge_symbolicator_image( image, complete_image, self.sdk_info, lambda e: handle_symbolication_failed(e, data=self.data) ) assert len(stacktraces) == len(rv['stacktraces']) for pf_list, symbolicated_stacktrace in zip( processable_stacktraces, rv['stacktraces'] ): for symbolicated_frame in symbolicated_stacktrace.get('frames') or (): pf = pf_list[symbolicated_frame['original_index']] pf.data['symbolicator_match'].append(symbolicated_frame)
def merge_symbolicator_minidump_response(data, response): data['platform'] = 'native' if response.get('crashed') is not None: data['level'] = 'fatal' if response['crashed'] else 'info' validate_and_set_timestamp(data, response.get('timestamp')) if response.get('system_info'): merge_symbolicator_minidump_system_info(data, response['system_info']) sdk_info = get_sdk_from_event(data) images = [] set_path(data, 'debug_meta', 'images', value=images) for complete_image in response['modules']: image = {} merge_symbolicator_image( image, complete_image, sdk_info, lambda e: write_error(e, data) ) images.append(image) # Extract the crash reason and infos data_exception = get_path(data, 'exception', 'values', 0) exc_value = ( 'Assertion Error: %s' % response.get('assertion') if response.get('assertion') else 'Fatal Error: %s' % response.get('crash_reason') ) data_exception['value'] = exc_value data_exception['type'] = response.get('crash_reason') data_threads = [] if response['stacktraces']: data['threads'] = {'values': data_threads} else: error = SymbolicationFailed(message='minidump has no thread list', type=EventError.NATIVE_SYMBOLICATOR_FAILED) write_error(error, data) for complete_stacktrace in response['stacktraces']: is_requesting = complete_stacktrace.get('is_requesting') thread_id = complete_stacktrace.get('thread_id') data_thread = { 'id': thread_id, 'crashed': is_requesting, } data_threads.append(data_thread) if is_requesting: data_exception['thread_id'] = thread_id data_stacktrace = data_exception.setdefault('stacktrace', {}) data_stacktrace['frames'] = [] else: data_thread['stacktrace'] = data_stacktrace = {'frames': []} if complete_stacktrace.get('registers'): data_stacktrace['registers'] = complete_stacktrace['registers'] for complete_frame in reversed(complete_stacktrace['frames']): new_frame = {} merge_symbolicated_frame(new_frame, complete_frame) data_stacktrace['frames'].append(new_frame)
def process_payload(data): project = Project.objects.get_from_cache(id=data['project']) task_id_cache_key = task_id_cache_key_for_event(data) symbolicator = Symbolicator( project=project, task_id_cache_key=task_id_cache_key ) stacktrace_infos = [ stacktrace for stacktrace in find_stacktraces_in_data(data) if any(is_native_platform(x) for x in stacktrace.platforms) ] stacktraces = [ { 'registers': sinfo.stacktrace.get('registers') or {}, 'frames': [ f for f in reversed(sinfo.stacktrace.get('frames') or ()) if _handles_frame(data, f) ] } for sinfo in stacktrace_infos ] if not any(stacktrace['frames'] for stacktrace in stacktraces): return modules = native_images_from_data(data) response = symbolicator.process_payload(stacktraces=stacktraces, modules=modules) assert len(modules) == len(response['modules']), (modules, response) sdk_info = get_sdk_from_event(data) for raw_image, complete_image in zip(modules, response['modules']): merge_symbolicator_image( raw_image, complete_image, sdk_info, lambda e: write_error( e, data)) assert len(stacktraces) == len(response['stacktraces']), (stacktraces, response) for sinfo, complete_stacktrace in zip(stacktrace_infos, response['stacktraces']): complete_frames_by_idx = {} for complete_frame in complete_stacktrace.get('frames') or (): complete_frames_by_idx \ .setdefault(complete_frame['original_index'], []) \ .append(complete_frame) new_frames = [] native_frames_idx = 0 for raw_frame in reversed(sinfo.stacktrace['frames']): if not _handles_frame(data, raw_frame): new_frames.append(raw_frame) continue for complete_frame in complete_frames_by_idx.get(native_frames_idx) or (): merged_frame = dict(raw_frame) merge_symbolicated_frame(merged_frame, complete_frame) if merged_frame.get('package'): raw_frame['package'] = merged_frame['package'] new_frames.append(merged_frame) native_frames_idx += 1 if sinfo.container is not None and native_frames_idx > 0: sinfo.container['raw_stacktrace'] = { 'frames': list(sinfo.stacktrace['frames']), 'registers': sinfo.stacktrace.get('registers') } new_frames.reverse() sinfo.stacktrace['frames'] = new_frames return data