Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profile_trace testing #858

Merged
merged 27 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
00b7966
Include isort stdlibs for determining stdlib modules
hmstepanek Jun 21, 2023
5d48e3e
Use isort & sys to eliminate std & builtin modules
hmstepanek Jun 2, 2023
4d59962
Handle importlib_metadata.version being a callable
hmstepanek Jun 21, 2023
c0e45f4
Merge branch 'main' into fix-local-scoped-package-reporting
hmstepanek Jun 21, 2023
ce4f37a
Merge branch 'main' into fix-local-scoped-package-reporting
mergify[bot] Jun 22, 2023
82307dd
Merge branch 'main' into fix-local-scoped-package-reporting
mergify[bot] Jun 22, 2023
f681ada
Merge branch 'main' into fix-local-scoped-package-reporting
mergify[bot] Jun 22, 2023
a0317e5
Merge branch 'main' into fix-local-scoped-package-reporting
mergify[bot] Jun 22, 2023
9794330
Merge branch 'main' into fix-local-scoped-package-reporting
mergify[bot] Jun 23, 2023
647ce67
Add isort into third party notices
hmstepanek Jun 26, 2023
1f0d5c5
Merge branch 'fix-local-scoped-package-reporting' of github.com:newre…
hmstepanek Jun 26, 2023
6ecb71e
[Mega-Linter] Apply linters fixes
hmstepanek Jun 26, 2023
17c52bd
Remove Python 2.7 and pypy2 testing (#835)
lrafeei Jun 21, 2023
d98e4a9
Containerized CI Pipeline (#836)
TimPansino Jun 22, 2023
7d2f89e
Fix CI Image Tagging (#838)
TimPansino Jun 22, 2023
075ea20
Temporarily Restore Old CI Pipeline (#841)
TimPansino Jun 22, 2023
6527064
Rework CI Pipeline (#839)
TimPansino Jun 22, 2023
1389876
Fix Tests on New CI (#843)
TimPansino Jun 23, 2023
6375813
Merge branch 'fix-local-scoped-package-reporting' of github.com:newre…
hmstepanek Jun 26, 2023
c64b670
Trigger tests
hmstepanek Jun 26, 2023
0264905
Add testing for profile trace.
umaannamalai Jun 27, 2023
44b16da
Merge branch 'main' into add-profile-trace-testing
umaannamalai Jun 27, 2023
3fdffbd
[Mega-Linter] Apply linters fixes
umaannamalai Jun 27, 2023
9f5a233
Ignore __call__ from coverage on profile_trace.
umaannamalai Jun 29, 2023
0686cd8
[Mega-Linter] Apply linters fixes
umaannamalai Jun 29, 2023
b3c11a1
Merge branch 'main' into add-profile-trace-testing
umaannamalai Jun 29, 2023
d68353d
Merge branch 'main' into add-profile-trace-testing
umaannamalai Jun 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 19 additions & 31 deletions newrelic/api/profile_trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,27 @@
# limitations under the License.

import functools
import sys
import os
import sys

from newrelic.packages import six

from newrelic.api.time_trace import current_trace
from newrelic import __file__ as AGENT_PACKAGE_FILE
from newrelic.api.function_trace import FunctionTrace
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object
from newrelic.api.time_trace import current_trace
from newrelic.common.object_names import callable_name
from newrelic.common.object_wrapper import FunctionWrapper, wrap_object
from newrelic.packages import six

from newrelic import __file__ as AGENT_PACKAGE_FILE
AGENT_PACKAGE_DIRECTORY = os.path.dirname(AGENT_PACKAGE_FILE) + '/'
AGENT_PACKAGE_DIRECTORY = os.path.dirname(AGENT_PACKAGE_FILE) + "/"


class ProfileTrace(object):

def __init__(self, depth):
self.function_traces = []
self.maximum_depth = depth
self.current_depth = 0

def __call__(self, frame, event, arg):

if event not in ['call', 'c_call', 'return', 'c_return',
'exception', 'c_exception']:
def __call__(self, frame, event, arg): # pragma: no cover
if event not in ["call", "c_call", "return", "c_return", "exception", "c_exception"]:
return

parent = current_trace()
Expand All @@ -49,8 +45,7 @@ def __call__(self, frame, event, arg):
# coroutine systems based on greenlets so don't run
# if we detect may be using greenlets.

if (hasattr(sys, '_current_frames') and
parent.thread_id not in sys._current_frames()):
if hasattr(sys, "_current_frames") and parent.thread_id not in sys._current_frames():
return

co = frame.f_code
Expand Down Expand Up @@ -84,7 +79,7 @@ def _callable():
except Exception:
pass

if event in ['call', 'c_call']:
if event in ["call", "c_call"]:
# Skip the outermost as we catch that with the root
# function traces for the profile trace.

Expand All @@ -100,19 +95,17 @@ def _callable():
self.function_traces.append(None)
return

if event == 'call':
if event == "call":
func = _callable()
if func:
name = callable_name(func)
else:
name = '%s:%s#%s' % (func_filename, func_name,
func_line_no)
name = "%s:%s#%s" % (func_filename, func_name, func_line_no)
else:
func = arg
name = callable_name(arg)
if not name:
name = '%s:@%s#%s' % (func_filename, func_name,
func_line_no)
name = "%s:@%s#%s" % (func_filename, func_name, func_line_no)

function_trace = FunctionTrace(name=name, parent=parent)
function_trace.__enter__()
Expand All @@ -127,7 +120,7 @@ def _callable():
self.function_traces.append(function_trace)
self.current_depth += 1

elif event in ['return', 'c_return', 'c_exception']:
elif event in ["return", "c_return", "c_exception"]:
if not self.function_traces:
return

Expand All @@ -143,9 +136,7 @@ def _callable():
self.current_depth -= 1


def ProfileTraceWrapper(wrapped, name=None, group=None, label=None,
params=None, depth=3):

def ProfileTraceWrapper(wrapped, name=None, group=None, label=None, params=None, depth=3):
def wrapper(wrapped, instance, args, kwargs):
parent = current_trace()

Expand Down Expand Up @@ -192,7 +183,7 @@ def wrapper(wrapped, instance, args, kwargs):
_params = params

with FunctionTrace(_name, _group, _label, _params, parent=parent, source=wrapped):
if not hasattr(sys, 'getprofile'):
if not hasattr(sys, "getprofile"):
return wrapped(*args, **kwargs)

profiler = sys.getprofile()
Expand All @@ -212,11 +203,8 @@ def wrapper(wrapped, instance, args, kwargs):


def profile_trace(name=None, group=None, label=None, params=None, depth=3):
return functools.partial(ProfileTraceWrapper, name=name,
group=group, label=label, params=params, depth=depth)
return functools.partial(ProfileTraceWrapper, name=name, group=group, label=label, params=params, depth=depth)


def wrap_profile_trace(module, object_path, name=None,
group=None, label=None, params=None, depth=3):
return wrap_object(module, object_path, ProfileTraceWrapper,
(name, group, label, params, depth))
def wrap_profile_trace(module, object_path, name=None, group=None, label=None, params=None, depth=3):
return wrap_object(module, object_path, ProfileTraceWrapper, (name, group, label, params, depth))
88 changes: 88 additions & 0 deletions tests/agent_features/test_profile_trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright 2010 New Relic, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from testing_support.validators.validate_transaction_metrics import (
validate_transaction_metrics,
)

from newrelic.api.background_task import background_task
from newrelic.api.profile_trace import ProfileTraceWrapper, profile_trace


def test_profile_trace_wrapper():
def _test():
def nested_fn():
pass

nested_fn()

wrapped_test = ProfileTraceWrapper(_test)
wrapped_test()


@validate_transaction_metrics("test_profile_trace:test_profile_trace_empty_args", background_task=True)
@background_task()
def test_profile_trace_empty_args():
@profile_trace()
def _test():
pass

_test()


_test_profile_trace_defined_args_scoped_metrics = [("Custom/TestTrace", 1)]


@validate_transaction_metrics(
"test_profile_trace:test_profile_trace_defined_args",
scoped_metrics=_test_profile_trace_defined_args_scoped_metrics,
background_task=True,
)
@background_task()
def test_profile_trace_defined_args():
@profile_trace(name="TestTrace", group="Custom", label="Label", params={"key": "value"}, depth=7)
def _test():
pass

_test()


_test_profile_trace_callable_args_scoped_metrics = [("Function/TestProfileTrace", 1)]


@validate_transaction_metrics(
"test_profile_trace:test_profile_trace_callable_args",
scoped_metrics=_test_profile_trace_callable_args_scoped_metrics,
background_task=True,
)
@background_task()
def test_profile_trace_callable_args():
def name_callable():
return "TestProfileTrace"

def group_callable():
return "Function"

def label_callable():
return "HSM"

def params_callable():
return {"account_id": "12345"}

@profile_trace(name=name_callable, group=group_callable, label=label_callable, params=params_callable, depth=0)
def _test():
pass

_test()