Skip to content

Commit 723c992

Browse files
Merge pull request #35 from aws/mirelap-pr-fix-path-bug
Remove current path from the module path in ProfileEncoder.
2 parents 60ec179 + 90bef67 commit 723c992

File tree

3 files changed

+47
-6
lines changed

3 files changed

+47
-6
lines changed

codeguru_profiler_agent/sampling_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ 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 "/./" instead of just a "/" between the app directory and the module path.
92+
# To not let the path go into the module name, we are removing it later in the ProfileEncoder.
9093
stack_entries = _extract_stack(stack, max_depth)
9194

9295
if len(stack_entries) == max_depth:

codeguru_profiler_agent/sdk_reporter/profile_encoder.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,33 @@
1010
GZIP_BALANCED_COMPRESSION_LEVEL = 6
1111
DEFAULT_FRAME_COMPONENT_DELIMITER = ":"
1212

13-
1413
def _get_module_path(file_path, sys_paths):
1514
"""
1615
We tried to remove the python library root path in order to give a reasonable expression of the module path.
1716
For example, /tmp/bin/python/site-packages/great_app/simple_expansions/simple_interface.py
1817
will get turned into great_app.simple_expansions.simple_interface given that the syspath contains
1918
/tmp/bin/python/site-packages
19+
20+
We are making sure we're removing the current path.
21+
For example, '/Users/mirelap/Documents/workspace/JSON/aws-codeguru-profiler-python-demo-application/sample-demo-django-app/./polls/views.py'
22+
will get turned into `polls.views' given that the file path contains the current path.
23+
This should not happen usually, but we've found a case where the "/." is added when calling traceback.walk_stack(..)
24+
in a uwsgi application. Check sampling_utils.py file for details.
25+
26+
sampling_utils.py returns different values when calling traceback.walk_stack(..) for uwsgi vs non-uwsgi
27+
for Python 3.8.10-Python 3.9.2.
28+
Examples of results:
29+
- 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
30+
- 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
2031
"""
2132
module_path = file_path
2233

2334
if platform.system() == "Windows":
2435
# In Windows, separator can either be / or \ from experimental result
25-
file_path = file_path.replace("/", os.sep)
36+
module_path = module_path.replace("/", os.sep)
2637

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

3241
# remove suffix
3342
module_path = str(Path(module_path).with_suffix(""))
@@ -42,6 +51,15 @@ def _get_module_path(file_path, sys_paths):
4251
return module_path
4352

4453

54+
def _remove_prefix_path(module_path, sys_paths):
55+
current_path = str(Path().absolute())
56+
if current_path in module_path:
57+
return module_path.replace(current_path, "").replace("/./", "/")
58+
for root in sys_paths:
59+
if root in module_path:
60+
return module_path.replace(root, "")
61+
return module_path
62+
4563
class ProfileEncoder:
4664
"""
4765
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)