Completed
Push — master ( 836a98...37dfd2 )
by
unknown
44s
created

send_stacktrace_sentry()   C

Complexity

Conditions 7

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
c 3
b 0
f 0
dl 0
loc 48
rs 5.5
1
# coding: utf8
2
3
"""
4
This software is licensed under the Apache 2 license, quoted below.
5
6
Copyright 2014 Crystalnix Limited
7
8
Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
use this file except in compliance with the License. You may obtain a copy of
10
the License at
11
12
    http://www.apache.org/licenses/LICENSE-2.0
13
14
Unless required by applicable law or agreed to in writing, software
15
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
License for the specific language governing permissions and limitations under
18
the License.
19
"""
20
21
from builtins import str
22
23
import os
24
import re
25
26
from django.conf import settings
27
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError
28
29
from clom import clom
30
from raven import Client
31
from celery import signature
32
33
from crash.settings import MINIDUMP_STACKWALK_PATH, SYMBOLS_PATH
34
from crash.stacktrace_to_json import pipe_dump_to_json_dump
35
from omaha.models import Version
36
from sparkle.models import SparkleVersion
37
38
client = Client(getattr(settings, 'RAVEN_DSN_STACKTRACE', None), name=getattr(settings, 'HOST_NAME', None),
39
                release=getattr(settings, 'APP_VERSION', None))
40
41
42
class FileNotFoundError(Exception):
43
    pass
44
45
46
minidump_stackwalk = clom[MINIDUMP_STACKWALK_PATH].with_opts('-m')
47
48
49
def get_stacktrace(crashdump_path):
50
    if not os.path.isfile(crashdump_path):
51
        raise FileNotFoundError
52
53
    rezult = minidump_stackwalk(crashdump_path, SYMBOLS_PATH).shell()
54
    return rezult, rezult.stderr
55
56
57
def add_signature_to_frame(frame):
58
    frame = frame.copy()
59
    if 'function' in frame:
60
        # Remove spaces before all stars, ampersands, and commas
61
        function = re.sub(' (?=[\*&,])', '', frame['function'])
62
        # Ensure a space after commas
63
        function = re.sub(',(?! )', ', ', function)
64
        frame['function'] = function
65
        signature = function
66
    elif 'abs_path' in frame and 'lineno' in frame:
67
        signature = '%s#%d' % (frame['abs_path'], frame['lineno'])
68
    elif 'filename' in frame and 'module_offset' in frame:
69
        signature = '%s@%s' % (frame['filename'], frame['module_offset'])
70
    else:
71
        signature = '@%s' % frame['offset']
72
    frame['signature'] = signature
73
    frame['short_signature'] = re.sub('\(.*\)', '', signature)
74
    return frame
75
76
77
def parse_stacktrace(stacktrace):
78
    stacktrace_dict = pipe_dump_to_json_dump(str(stacktrace).splitlines())
79
    stacktrace_dict['crashing_thread']['frames'] = list(
80
        map(add_signature_to_frame,
81
            stacktrace_dict['crashing_thread']['frames']))
82
    return dict(stacktrace_dict)
83
84
85
def get_signature(stacktrace):
86
    try:
87
        frame = stacktrace['crashing_thread']['frames'][0]
88
        signature = frame['signature']
89
    except (KeyError, IndexError):
90
        signature = 'EMPTY: no frame data available'
91
    return signature
92
93
94
def get_os(stacktrace):
95
    return stacktrace.get('system_info', {}).get('os', '') if stacktrace else ''
96
97
98
def send_stacktrace_sentry(crash):
99
    stacktrace = crash.stacktrace_json
100
    exception = {
101
        "values": [
102
            {
103
                "type": stacktrace.get('crash_info', {}).get('type', 'unknown exception'),
104
                "value": stacktrace.get('crash_info', {}).get('crash_address', '0x0'),
105
                "stacktrace": stacktrace['crashing_thread']
106
            }
107
        ]
108
    }
109
110
    data = {'sentry.interfaces.Exception': exception}
111
112
    if crash.userid:
113
        data['sentry.interfaces.User'] = dict(id=crash.userid)
114
115
    extra = dict(
116
        crash_admin_panel_url='http://{}{}'.format(
117
            settings.HOST_NAME,
118
            '/admin/crash/crash/%s/' % crash.pk),
119
        crashdump_url=crash.upload_file_minidump.url,
120
    )
121
122
    tags = {}
123
    if crash.meta:
124
        extra.update(crash.meta)
125
        ver = crash.meta.get('ver')
126
        if ver:
127
            tags['ver'] = ver
128
    if crash.channel:
129
        tags['channel'] = crash.channel
130
    if crash.archive:
131
        extra['archive_url'] = crash.archive.url
132
133
    tags.update(stacktrace.get('system_info', {}))
134
135
    if crash.appid:
136
        tags['appid'] = crash.appid
137
138
    event_id = client.capture(
139
        'raven.events.Message',
140
        message=crash.signature,
141
        extra=extra,
142
        tags=tags,
143
        data=data
144
    )
145
    signature("tasks.get_sentry_link", args=(crash.pk, event_id)).apply_async(queue='private', countdown=1)
146
147
148
def parse_debug_meta_info(head, exception=Exception):
149
    head = head.decode()
150
    head_list = head.split(' ', 4)
151
    if head_list[0] != 'MODULE':
152
        raise exception(u"The file contains invalid data.")
153
    return dict(debug_id=head_list[-2],
154
                debug_file=head_list[-1])
155
156
157
def get_channel(build_number, os):
158
    try:
159
        if os == 'Mac OS X':      # We expect that sparkle supports only Mac platform
160
            version = SparkleVersion.objects.select_related('channel').get(short_version=build_number)
161
        else:                       # All other platforms will be related to Omaha
162
            version = Version.objects.select_related('channel').get(version=build_number)
163
    except (MultipleObjectsReturned, ObjectDoesNotExist, ValidationError):
164
        return 'undefined'
165
    return version.channel.name