Skip to content

Commit 655f74e

Browse files
Remove current path from the module path in ProfileEncoder.
Running the uwsgi sample application with Python 3.8.10-3.9.2 showed incorrect module path in the UI. This was narrowed down to traceback returning different results (a path with "/."). This change makes sure the path is not leaked out.
1 parent 60ec179 commit 655f74e

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

codeguru_profiler_agent/sampling_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ def _maybe_append_synthetic_frame(result, frame, line_no):
8787

8888
def _extract_frames(end_frame, max_depth):
8989
stack = list(traceback.walk_stack(end_frame))[::-1][0:max_depth]
90+
# When running the sample app with uwsgi for Python 3.8.10-3.9.2, the traceback command
91+
# returns a file path that contains "/.".
92+
# To not let the path go into the module name, we are removing it later in the ProfileEncoder.
93+
# Examples:
94+
# - file '/Users/mirelap/Documents/workspace/JSON/aws-codeguru-profiler-python-demo-application/sample-demo-django-app/./polls/views.py', line 104, code get_queryset>, 104
95+
# - file '/Users/mirelap/Documents/workspace/JSON/aws-codeguru-profiler-python-demo-application/sample-demo-django-app/polls/views.py', line 104, code get_queryset>, 104
9096
stack_entries = _extract_stack(stack, max_depth)
9197

9298
if len(stack_entries) == max_depth:

codeguru_profiler_agent/sdk_reporter/profile_encoder.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ def _get_module_path(file_path, sys_paths):
1717
For example, /tmp/bin/python/site-packages/great_app/simple_expansions/simple_interface.py
1818
will get turned into great_app.simple_expansions.simple_interface given that the syspath contains
1919
/tmp/bin/python/site-packages
20+
21+
We are making sure we're removing the current path.
22+
For example, '/Users/mirelap/Documents/workspace/JSON/aws-codeguru-profiler-python-demo-application/sample-demo-django-app/./polls/views.py'
23+
will get turned into `polls.views' given that the file path contains the current path.
24+
This should not happen usually, but we've found a case where the "/." is added when calling traceback.walk_stack(..)
25+
in a uwsgi application. Check sampling_utils.py file for details.
2026
"""
2127
module_path = file_path
2228

2329
if platform.system() == "Windows":
2430
# In Windows, separator can either be / or \ from experimental result
25-
file_path = file_path.replace("/", os.sep)
31+
module_path = module_path.replace("/", os.sep)
2632

27-
for root in sys_paths:
28-
if root in file_path:
29-
module_path = file_path.replace(root, "")
30-
break
33+
# remove prefix path
34+
module_path = _remove_prefix_path(module_path, sys_paths)
3135

3236
# remove suffix
3337
module_path = str(Path(module_path).with_suffix(""))
@@ -42,6 +46,15 @@ def _get_module_path(file_path, sys_paths):
4246
return module_path
4347

4448

49+
def _remove_prefix_path(module_path, sys_paths):
50+
current_path = str(Path().absolute())
51+
if current_path in module_path:
52+
return module_path.replace(current_path, "").replace("/.", "")
53+
for root in sys_paths:
54+
if root in module_path:
55+
return module_path.replace(root, "")
56+
return module_path
57+
4558
class ProfileEncoder:
4659
"""
4760
Encodes a given Profile into the JSON version of the ion-based profile format

test/unit/sdk_reporter/test_sdk_profile_encoder.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import io
1414
import gzip
1515
from datetime import timedelta
16+
from pathlib import Path
1617

1718
from codeguru_profiler_agent.metrics.timer import Timer
1819
from codeguru_profiler_agent.model.profile import Profile
@@ -329,6 +330,25 @@ def test_it_gzips_the_result_before_writing_to_the_stream(self):
329330
assert (len(uncompressed_result) > 0)
330331

331332

333+
class TestModulePathExtractorWithCurrentPath:
334+
@before
335+
def before(self):
336+
self.current_path = str(Path().absolute())
337+
self.subject = ProfileEncoder(gzip=False, environment=environment).ModulePathExtractor(sys_path=[])
338+
339+
def test_it_removes_current_path(self):
340+
file_path = self.current_path + '/polls/views.py'
341+
assert self.subject.get_module_path(file_path) == "polls.views"
342+
343+
def test_it_removes_current_path_and_slash_and_dot(self):
344+
file_path = self.current_path + '/./polls/views.py'
345+
assert self.subject.get_module_path(file_path) == "polls.views"
346+
347+
def test_it_does_nothing_when_file_path_has_no_current_path(self):
348+
file_path ='/polls/views.py'
349+
assert self.subject.get_module_path(file_path) == "polls.views"
350+
351+
332352
class TestModulePathExtractor:
333353
@before
334354
def before(self):

0 commit comments

Comments
 (0)