|
3 | 3 | import sys
|
4 | 4 |
|
5 | 5 | from test import help_utils
|
| 6 | +from collections import namedtuple |
6 | 7 |
|
7 | 8 | from codeguru_profiler_agent.sampling_utils import get_stacks
|
8 | 9 |
|
9 | 10 | DEFAULT_TRUNCATED_FRAME_NAME = "<Truncated>"
|
10 | 11 |
|
| 12 | +test_code = namedtuple('code', ['co_filename', 'co_name']) |
| 13 | +test_frame = namedtuple('frame', ['f_code', 'f_locals']) |
| 14 | +test_tb = namedtuple('tb', ['tb_frame', 'tb_lineno']) |
| 15 | + |
| 16 | + |
| 17 | +def make_frame(path, method, line_nbr, f_locals={}): |
| 18 | + return test_tb(test_frame(test_code(path, method), f_locals), line_nbr) |
| 19 | + |
11 | 20 |
|
12 | 21 | def is_frame_in_stacks(stacks, target_frame):
|
13 | 22 | for stack in stacks:
|
@@ -88,3 +97,52 @@ def test_it_does_not_include_zombie_threads(self):
|
88 | 97 |
|
89 | 98 | assert not is_frame_in_stacks(
|
90 | 99 | stacks, "dummy_parent_method")
|
| 100 | + |
| 101 | + def test_it_adds_operation_name_frame_for_boto(self): |
| 102 | + raw_stack = [ |
| 103 | + make_frame('path/to/foo.py', 'foo', 3), |
| 104 | + make_frame('site-packages/botocore/client.py', '_api_call', 3, {'py_operation_name': 'boto_api_call'}), |
| 105 | + make_frame('path/to/bar.py', 'bar', 3) |
| 106 | + ] |
| 107 | + with mock.patch( |
| 108 | + "traceback.walk_stack", |
| 109 | + side_effect= |
| 110 | + lambda end_frame: raw_stack |
| 111 | + ): |
| 112 | + stacks = get_stacks( |
| 113 | + threads_to_sample=sys._current_frames().items(), |
| 114 | + excluded_threads=set(), |
| 115 | + max_depth=100) |
| 116 | + assert len(stacks[0]) == 4 |
| 117 | + assert is_frame_in_stacks(stacks, "boto_api_call") |
| 118 | + |
| 119 | + def test_adding_boto_frame_does_not_exceed_maximum_depth(self): |
| 120 | + raw_stack = [ |
| 121 | + make_frame('site-packages/botocore/client.py', '_api_call', 34, {'py_operation_name': 'boto_api_call'}), |
| 122 | + make_frame('path/to/foo.py', 'bar', 12), |
| 123 | + |
| 124 | + ] |
| 125 | + for i in range(100): |
| 126 | + raw_stack.insert(0, make_frame('path/to/foo.py', 'bar' + str(i), 1)) |
| 127 | + with mock.patch( |
| 128 | + "traceback.walk_stack", |
| 129 | + side_effect= |
| 130 | + lambda end_frame: raw_stack |
| 131 | + ): |
| 132 | + stacks = get_stacks( |
| 133 | + threads_to_sample=sys._current_frames().items(), |
| 134 | + excluded_threads=set(), |
| 135 | + max_depth=100) |
| 136 | + assert len(stacks[0]) == 100 |
| 137 | + assert is_frame_in_stacks(stacks, "boto_api_call") |
| 138 | + |
| 139 | + def test_it_adds_operation_name_frame_for_real_boto_call(self): |
| 140 | + # Run a thread that will try to do a boto3 api call for 1 second then fail with a log |
| 141 | + # the function will call put_metric_data on a cloudwatch client |
| 142 | + # so get_stack should capture it. |
| 143 | + self.helper.new_thread_sending_boto_api_call(timeout_seconds=1) |
| 144 | + stacks = get_stacks( |
| 145 | + threads_to_sample=sys._current_frames().items(), |
| 146 | + excluded_threads=set(), |
| 147 | + max_depth=100) |
| 148 | + assert is_frame_in_stacks(stacks, "put_metric_data") |
0 commit comments