12
12
RepoTreesIntegration ,
13
13
get_extension ,
14
14
)
15
+ from sentry .issues .auto_source_code_config .constants import (
16
+ EXTRACT_FILENAME_FROM_MODULE_AND_ABS_PATH ,
17
+ )
15
18
from sentry .models .organization import Organization
16
19
from sentry .models .project import Project
17
20
from sentry .models .repository import Repository
@@ -51,6 +54,14 @@ class NeedsExtension(Exception):
51
54
pass
52
55
53
56
57
+ class MissingModuleOrAbsPath (Exception ):
58
+ pass
59
+
60
+
61
+ class DoesNotFollowJavaPackageNamingConvention (Exception ):
62
+ pass
63
+
64
+
54
65
def derive_code_mappings (
55
66
organization : Organization ,
56
67
frame : Mapping [str , Any ],
@@ -73,7 +84,10 @@ def derive_code_mappings(
73
84
# XXX: Look at sentry.interfaces.stacktrace and maybe use that
74
85
class FrameInfo :
75
86
def __init__ (self , frame : Mapping [str , Any ], platform : str | None = None ) -> None :
76
- # XXX: platform will be used in a following PR
87
+ if platform in EXTRACT_FILENAME_FROM_MODULE_AND_ABS_PATH :
88
+ self .frame_info_from_module (frame )
89
+ return
90
+
77
91
frame_file_path = frame ["filename" ]
78
92
frame_file_path = self .transformations (frame_file_path )
79
93
@@ -123,6 +137,15 @@ def transformations(self, frame_file_path: str) -> str:
123
137
124
138
return frame_file_path
125
139
140
+ def frame_info_from_module (self , frame : Mapping [str , Any ]) -> None :
141
+ if frame .get ("module" ) and frame .get ("abs_path" ):
142
+ stack_root , filepath = get_path_from_module (frame ["module" ], frame ["abs_path" ])
143
+ self .stack_root = stack_root
144
+ self .raw_path = filepath
145
+ self .normalized_path = filepath
146
+ else :
147
+ raise MissingModuleOrAbsPath ("Investigate why the data is missing." )
148
+
126
149
def __repr__ (self ) -> str :
127
150
return f"FrameInfo: { self .raw_path } "
128
151
@@ -214,8 +237,12 @@ def _stacktrace_buckets(
214
237
buckets [frame_filename .stack_root ].append (frame_filename )
215
238
except UnsupportedFrameInfo :
216
239
logger .warning ("Frame's filepath not supported: %s" , frame .get ("filename" ))
240
+ except MissingModuleOrAbsPath :
241
+ logger .warning ("Do not panic. I'm collecting this data." )
217
242
except NeedsExtension :
218
243
logger .warning ("Needs extension: %s" , frame .get ("filename" ))
244
+ except DoesNotFollowJavaPackageNamingConvention :
245
+ pass
219
246
except Exception :
220
247
logger .exception ("Unable to split stacktrace path into buckets" )
221
248
@@ -507,8 +534,10 @@ def find_roots(frame_filename: FrameInfo, source_path: str) -> tuple[str, str]:
507
534
return (stack_root , "" )
508
535
elif source_path .endswith (stack_path ): # "Packaged" logic
509
536
source_prefix = source_path .rpartition (stack_path )[0 ]
510
- package_dir = stack_path .split ("/" )[0 ]
511
- return (f"{ stack_root } { package_dir } /" , f"{ source_prefix } { package_dir } /" )
537
+ return (
538
+ f"{ stack_root } { frame_filename .stack_root } /" ,
539
+ f"{ source_prefix } { frame_filename .stack_root } /" ,
540
+ )
512
541
elif stack_path .endswith (source_path ):
513
542
stack_prefix = stack_path .rpartition (source_path )[0 ]
514
543
return (f"{ stack_root } { stack_prefix } " , "" )
@@ -541,3 +570,27 @@ def find_roots(frame_filename: FrameInfo, source_path: str) -> tuple[str, str]:
541
570
# validate_source_url should have ensured the file names match
542
571
# so if we get here something went wrong and there is a bug
543
572
raise UnexpectedPathException ("Could not find common root from paths" )
573
+
574
+
575
+ # Based on # https://github.com/getsentry/symbolicator/blob/450f1d6a8c346405454505ed9ca87e08a6ff34b7/crates/symbolicator-proguard/src/symbolication.rs#L450-L485
576
+ def get_path_from_module (module : str , abs_path : str ) -> tuple [str , str ]:
577
+ """This attempts to generate a modified module and a real path from a Java module name and filename.
578
+ Returns a tuple of (stack_root, source_path).
579
+ """
580
+ # An `abs_path` is valid if it contains a `.` and doesn't contain a `$`.
581
+ if "$" in abs_path or "." not in abs_path :
582
+ # Split the module at the first '$' character and take the part before it
583
+ # If there's no '$', use the entire module
584
+ file_path = module .split ("$" , 1 )[0 ] if "$" in module else module
585
+ stack_root = module .rsplit ("." , 1 )[0 ].replace ("." , "/" )
586
+ return stack_root , file_path .replace ("." , "/" )
587
+
588
+ if "." not in module :
589
+ raise DoesNotFollowJavaPackageNamingConvention
590
+
591
+ # If module has a dot, take everything before the last dot
592
+ # com.example.foo.Bar$InnerClass -> com/example/foo/
593
+ stack_root = module .rsplit ("." , 1 )[0 ].replace ("." , "/" )
594
+ file_path = f"{ stack_root } /{ abs_path } "
595
+
596
+ return stack_root , file_path
0 commit comments