From e089cf7ad10f2c85019d2ef46024029dd954cd10 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 6 Sep 2016 23:39:53 -0700 Subject: [PATCH 01/10] Implement react-native filename parsing on server --- src/sentry/interfaces/stacktrace.py | 12 ++++++++++++ tests/sentry/interfaces/test_stacktrace.py | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 45fd483d518295..965715e873e2b6 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -39,6 +39,9 @@ (\$\$[\w_]+?CGLIB\$\$)[a-fA-F0-9]+(_[0-9]+)? ''', re.X) +# file:///var/containers/Bundle/Application/{DEVICE_ID}/HelloWorld.app/main.jsbundle +_react_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.]+(\.app|CodePush)\/') + def trim_package(pkg): if not pkg: @@ -219,6 +222,11 @@ def handle_nan(value): return value +def strip_react_native_components(value): + # we maintain the leading prefix for compat + return _react_path_re.sub('/', value) + + class Frame(Interface): @classmethod def to_python(cls, data): @@ -234,6 +242,10 @@ def to_python(cls, data): if v is not None and not isinstance(v, six.string_types): raise InterfaceValidationError("Invalid value for '%s'" % name) + # JS sdk only sends filename + if filename and filename.startswith('file://'): + filename = strip_react_native_components(filename) + # absolute path takes priority over filename # (in the end both will get set) if not abs_path: diff --git a/tests/sentry/interfaces/test_stacktrace.py b/tests/sentry/interfaces/test_stacktrace.py index c9470e34da3a95..100435213ec6c0 100644 --- a/tests/sentry/interfaces/test_stacktrace.py +++ b/tests/sentry/interfaces/test_stacktrace.py @@ -103,6 +103,14 @@ def test_ignores_results_with_empty_path(self): assert frame.filename == 'http://foo.com' assert frame.abs_path == frame.filename + def test_normalizes_react_native_filename(self): + interface = Stacktrace.to_python(dict(frames=[{ + 'lineno': 1, + 'filename': 'file:///var/containers/Bundle/Application/06C10E06-2ABE-41A3-B7F2-3B7452C057B1/HelloWorld.app/main.jsbundle', + }])) + frame = interface.frames[0] + assert frame.filename == '/main.jsbundle' + def test_serialize_returns_frames(self): interface = Stacktrace.to_python(dict(frames=[{ 'lineno': 1, From 40d58f970cf9042411cfab24ce055ebe962d2644 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 6 Sep 2016 23:47:19 -0700 Subject: [PATCH 02/10] Normalize Electron paths --- src/sentry/interfaces/stacktrace.py | 13 ++++++++----- tests/sentry/interfaces/test_stacktrace.py | 8 ++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 965715e873e2b6..c6818d130e5f2f 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -39,8 +39,11 @@ (\$\$[\w_]+?CGLIB\$\$)[a-fA-F0-9]+(_[0-9]+)? ''', re.X) -# file:///var/containers/Bundle/Application/{DEVICE_ID}/HelloWorld.app/main.jsbundle -_react_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.]+(\.app|CodePush)\/') +# React-Native: +# file:///var/containers/Bundle/Application/{DEVICE_ID}/HelloWorld.app/main.jsbundle +# Electron: +# file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js +_js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.]+(\.app|CodePush)\/') def trim_package(pkg): @@ -222,9 +225,9 @@ def handle_nan(value): return value -def strip_react_native_components(value): +def strip_js_native_components(value): # we maintain the leading prefix for compat - return _react_path_re.sub('/', value) + return _js_native_path_re.sub('/', value) class Frame(Interface): @@ -244,7 +247,7 @@ def to_python(cls, data): # JS sdk only sends filename if filename and filename.startswith('file://'): - filename = strip_react_native_components(filename) + filename = strip_js_native_components(filename) # absolute path takes priority over filename # (in the end both will get set) diff --git a/tests/sentry/interfaces/test_stacktrace.py b/tests/sentry/interfaces/test_stacktrace.py index 100435213ec6c0..137ee00e757ce9 100644 --- a/tests/sentry/interfaces/test_stacktrace.py +++ b/tests/sentry/interfaces/test_stacktrace.py @@ -111,6 +111,14 @@ def test_normalizes_react_native_filename(self): frame = interface.frames[0] assert frame.filename == '/main.jsbundle' + def test_normalizes_electron_filename(self): + interface = Stacktrace.to_python(dict(frames=[{ + 'lineno': 1, + 'filename': 'file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js', + }])) + frame = interface.frames[0] + assert frame.filename == '/file1.js' + def test_serialize_returns_frames(self): interface = Stacktrace.to_python(dict(frames=[{ 'lineno': 1, From b1f477a93fcd616568acf1e7ead6b54216eb5113 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 6 Sep 2016 23:51:17 -0700 Subject: [PATCH 03/10] Less aggressive regexp --- src/sentry/interfaces/stacktrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index c6818d130e5f2f..de4c366cd55442 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -43,7 +43,7 @@ # file:///var/containers/Bundle/Application/{DEVICE_ID}/HelloWorld.app/main.jsbundle # Electron: # file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js -_js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.]+(\.app|CodePush)\/') +_js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app|CodePush)\/') def trim_package(pkg): From 6e64a75245618dcbe8d6646f8bf2693aa3b7b757 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 6 Sep 2016 23:51:21 -0700 Subject: [PATCH 04/10] Fix Electron test --- tests/sentry/interfaces/test_stacktrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sentry/interfaces/test_stacktrace.py b/tests/sentry/interfaces/test_stacktrace.py index 137ee00e757ce9..eaa2c0faafbc3d 100644 --- a/tests/sentry/interfaces/test_stacktrace.py +++ b/tests/sentry/interfaces/test_stacktrace.py @@ -117,7 +117,7 @@ def test_normalizes_electron_filename(self): 'filename': 'file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js', }])) frame = interface.frames[0] - assert frame.filename == '/file1.js' + assert frame.filename == '/Contents/app.asar/file1.js' def test_serialize_returns_frames(self): interface = Stacktrace.to_python(dict(frames=[{ From 12011aeeeaa15d641175b6d7fd9ecc3b63dff427 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:17:00 -0700 Subject: [PATCH 05/10] Improve Electron support --- src/sentry/interfaces/stacktrace.py | 5 ++++- tests/sentry/interfaces/test_stacktrace.py | 26 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index de4c366cd55442..b948f45046ae83 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -45,6 +45,8 @@ # file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js _js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app|CodePush)\/') +_js_electron_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app/Contents/Resources/app(\.asar)?/|/resources/app(\.asar)/)\/') + def trim_package(pkg): if not pkg: @@ -227,7 +229,8 @@ def handle_nan(value): def strip_js_native_components(value): # we maintain the leading prefix for compat - return _js_native_path_re.sub('/', value) + value = _js_native_path_re.sub('/', value) + value = _js_electron_re.sub('/', value) class Frame(Interface): diff --git a/tests/sentry/interfaces/test_stacktrace.py b/tests/sentry/interfaces/test_stacktrace.py index eaa2c0faafbc3d..0a1733b14e466f 100644 --- a/tests/sentry/interfaces/test_stacktrace.py +++ b/tests/sentry/interfaces/test_stacktrace.py @@ -111,7 +111,7 @@ def test_normalizes_react_native_filename(self): frame = interface.frames[0] assert frame.filename == '/main.jsbundle' - def test_normalizes_electron_filename(self): + def test_normalizes_electron_mac_asar_filename(self): interface = Stacktrace.to_python(dict(frames=[{ 'lineno': 1, 'filename': 'file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js', @@ -119,6 +119,30 @@ def test_normalizes_electron_filename(self): frame = interface.frames[0] assert frame.filename == '/Contents/app.asar/file1.js' + def test_normalizes_electron_mac_filename(self): + interface = Stacktrace.to_python(dict(frames=[{ + 'lineno': 1, + 'filename': 'file:///x/yy/zzz/Electron.app/Contents/app/file1.js', + }])) + frame = interface.frames[0] + assert frame.filename == '/file1.js' + + def test_normalizes_electron_windows_filename(self): + interface = Stacktrace.to_python(dict(frames=[{ + 'lineno': 1, + 'filename': 'file:///x/yy/zzz/Electron/resources/app/file1.js', + }])) + frame = interface.frames[0] + assert frame.filename == '/file1.js' + + def test_normalizes_electron_windows_asar_filename(self): + interface = Stacktrace.to_python(dict(frames=[{ + 'lineno': 1, + 'filename': 'file:///x/yy/zzz/Electron/resources/app.asar/file1.js', + }])) + frame = interface.frames[0] + assert frame.filename == '/file1.js' + def test_serialize_returns_frames(self): interface = Stacktrace.to_python(dict(frames=[{ 'lineno': 1, From 7f34037b3ac6119ff0883ef182e99c968a1c8a10 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:21:25 -0700 Subject: [PATCH 06/10] Fix test --- tests/sentry/interfaces/test_stacktrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sentry/interfaces/test_stacktrace.py b/tests/sentry/interfaces/test_stacktrace.py index 0a1733b14e466f..7687efff051311 100644 --- a/tests/sentry/interfaces/test_stacktrace.py +++ b/tests/sentry/interfaces/test_stacktrace.py @@ -117,7 +117,7 @@ def test_normalizes_electron_mac_asar_filename(self): 'filename': 'file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js', }])) frame = interface.frames[0] - assert frame.filename == '/Contents/app.asar/file1.js' + assert frame.filename == '/file1.js' def test_normalizes_electron_mac_filename(self): interface = Stacktrace.to_python(dict(frames=[{ From de55e574d423b890703c96fd11e08718e2d3772d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:27:11 -0700 Subject: [PATCH 07/10] Fix optional asar component --- src/sentry/interfaces/stacktrace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index b948f45046ae83..8b05d24c471e08 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -45,7 +45,7 @@ # file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js _js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app|CodePush)\/') -_js_electron_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app/Contents/Resources/app(\.asar)?/|/resources/app(\.asar)/)\/') +_js_electron_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app/Contents/Resources/app(\.asar)?/|/resources/app(\.asar)?/)\/') def trim_package(pkg): From 3631c58f809f2a3ba6bc069062e2177092515819 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:28:48 -0700 Subject: [PATCH 08/10] Return value --- src/sentry/interfaces/stacktrace.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 8b05d24c471e08..3fe05cecb7934b 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -231,6 +231,7 @@ def strip_js_native_components(value): # we maintain the leading prefix for compat value = _js_native_path_re.sub('/', value) value = _js_electron_re.sub('/', value) + return value class Frame(Interface): From e54594795ee18116a9e4efa2b40ca0aaa8c77d10 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:42:42 -0700 Subject: [PATCH 09/10] tweak order so electron happens first --- src/sentry/interfaces/stacktrace.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 3fe05cecb7934b..28408e2148a918 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -229,8 +229,9 @@ def handle_nan(value): def strip_js_native_components(value): # we maintain the leading prefix for compat - value = _js_native_path_re.sub('/', value) + # order matters here value = _js_electron_re.sub('/', value) + value = _js_native_path_re.sub('/', value) return value From 33b310dc155fa9b881354ccd6bdb9585889b449f Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 14 Sep 2016 14:43:04 -0700 Subject: [PATCH 10/10] simplify regexp --- src/sentry/interfaces/stacktrace.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/sentry/interfaces/stacktrace.py b/src/sentry/interfaces/stacktrace.py index 28408e2148a918..483fea890a99c4 100644 --- a/src/sentry/interfaces/stacktrace.py +++ b/src/sentry/interfaces/stacktrace.py @@ -43,9 +43,7 @@ # file:///var/containers/Bundle/Application/{DEVICE_ID}/HelloWorld.app/main.jsbundle # Electron: # file:///x/yy/zzz/Electron.app/Contents/app.asar/file1.js -_js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app|CodePush)\/') - -_js_electron_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app/Contents/Resources/app(\.asar)?/|/resources/app(\.asar)?/)\/') +_js_native_path_re = re.compile(r'^(file\:\/\/)?/.*\/[^\.\/]+(\.app|CodePush|\.app/Contents/Resources/app(\.asar)?/|/resources/app(\.asar)?/)\/') def trim_package(pkg): @@ -229,10 +227,7 @@ def handle_nan(value): def strip_js_native_components(value): # we maintain the leading prefix for compat - # order matters here - value = _js_electron_re.sub('/', value) - value = _js_native_path_re.sub('/', value) - return value + return _js_native_path_re.sub('/', value) class Frame(Interface):