cavisson-pythonagent 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cavisson_pythonagent-0.0.1.dist-info/METADATA +32 -0
- cavisson_pythonagent-0.0.1.dist-info/RECORD +180 -0
- cavisson_pythonagent-0.0.1.dist-info/WHEEL +5 -0
- cavisson_pythonagent-0.0.1.dist-info/licenses/LICENSE +19 -0
- cavisson_pythonagent-0.0.1.dist-info/top_level.txt +1 -0
- pythonagent/__init__.py +22 -0
- pythonagent/agent/__init__.py +219 -0
- pythonagent/agent/internal/__init__.py +1 -0
- pythonagent/agent/internal/agent.py +1651 -0
- pythonagent/agent/internal/framesinfo.py +152 -0
- pythonagent/agent/internal/heap_dump.py +39 -0
- pythonagent/agent/internal/intercept_module.py +70 -0
- pythonagent/agent/internal/logs.py +122 -0
- pythonagent/agent/internal/metadata/__init__.py +0 -0
- pythonagent/agent/internal/metadata/agent_meta_data.py +276 -0
- pythonagent/agent/internal/proc_compat.py +75 -0
- pythonagent/agent/internal/profile.py +47 -0
- pythonagent/agent/internal/provider.py +83 -0
- pythonagent/agent/internal/thread_dump.py +85 -0
- pythonagent/agent/internal/udp.py +245 -0
- pythonagent/agent/internal/udp_message.py +800 -0
- pythonagent/agent/probes/Instrumentation/__init__.py +340 -0
- pythonagent/agent/probes/Instrumentation/find.py +243 -0
- pythonagent/agent/probes/Instrumentation/module_version_resolver.py +155 -0
- pythonagent/agent/probes/Instrumentation/new_parser.py +64 -0
- pythonagent/agent/probes/Instrumentation/parser.py +129 -0
- pythonagent/agent/probes/__init__.py +219 -0
- pythonagent/agent/probes/base.py +303 -0
- pythonagent/agent/probes/cache/__init__.py +51 -0
- pythonagent/agent/probes/cache/redis.py +119 -0
- pythonagent/agent/probes/cache/redis_asyncio.py +83 -0
- pythonagent/agent/probes/coroutines/__init__.py +1 -0
- pythonagent/agent/probes/coroutines/asyncio.py +63 -0
- pythonagent/agent/probes/elasticdb/__init__.py +7 -0
- pythonagent/agent/probes/elasticdb/aelastic.py +54 -0
- pythonagent/agent/probes/frameworks/__init__.py +27 -0
- pythonagent/agent/probes/frameworks/agentprofiler.py +105 -0
- pythonagent/agent/probes/frameworks/aiohttp_web.py +155 -0
- pythonagent/agent/probes/frameworks/aisess.py +151 -0
- pythonagent/agent/probes/frameworks/asgi.py +340 -0
- pythonagent/agent/probes/frameworks/bottle.py +27 -0
- pythonagent/agent/probes/frameworks/cherry.py +25 -0
- pythonagent/agent/probes/frameworks/django.py +128 -0
- pythonagent/agent/probes/frameworks/falcon.py +21 -0
- pythonagent/agent/probes/frameworks/fastapi.py +35 -0
- pythonagent/agent/probes/frameworks/flask.py +30 -0
- pythonagent/agent/probes/frameworks/pyramid.py +56 -0
- pythonagent/agent/probes/frameworks/test.py +108 -0
- pythonagent/agent/probes/frameworks/tornado_async_web.py +117 -0
- pythonagent/agent/probes/frameworks/tornado_web.py +133 -0
- pythonagent/agent/probes/frameworks/wsgi.py +353 -0
- pythonagent/agent/probes/grpc/__init__.py +76 -0
- pythonagent/agent/probes/grpc/client_interceptor.py +132 -0
- pythonagent/agent/probes/grpc/server_interceptor.py +129 -0
- pythonagent/agent/probes/havoc/__init__.py +0 -0
- pythonagent/agent/probes/havoc/custom_memory_stress.py +186 -0
- pythonagent/agent/probes/havoc/custom_thread_stress.py +187 -0
- pythonagent/agent/probes/havoc/havoc_constants.py +218 -0
- pythonagent/agent/probes/havoc/havoc_manager.py +981 -0
- pythonagent/agent/probes/http/__init__.py +49 -0
- pythonagent/agent/probes/http/aiohttp_client.py +59 -0
- pythonagent/agent/probes/http/boto.py +12 -0
- pythonagent/agent/probes/http/httplib.py +110 -0
- pythonagent/agent/probes/http/httpx_client.py +116 -0
- pythonagent/agent/probes/http/requests.py +15 -0
- pythonagent/agent/probes/http/tornado_httpclient.py +85 -0
- pythonagent/agent/probes/http/urllib3.py +16 -0
- pythonagent/agent/probes/langchain/__init__.py +21 -0
- pythonagent/agent/probes/langchain/base_tool.py +95 -0
- pythonagent/agent/probes/langchain/langchain_community.py +136 -0
- pythonagent/agent/probes/langchain/langchain_core.py +32 -0
- pythonagent/agent/probes/langchain/langchain_openai.py +110 -0
- pythonagent/agent/probes/logging/__init__.py +106 -0
- pythonagent/agent/probes/message_brokers/__init__.py +4 -0
- pythonagent/agent/probes/message_brokers/pika.py +126 -0
- pythonagent/agent/probes/mongodb/__init__.py +6 -0
- pythonagent/agent/probes/mongodb/pymongo.py +286 -0
- pythonagent/agent/probes/openai/__init__.py +3 -0
- pythonagent/agent/probes/openai/openai.py +797 -0
- pythonagent/agent/probes/span.py +101 -0
- pythonagent/agent/probes/sql/__init__.py +13 -0
- pythonagent/agent/probes/sql/botocores3.py +51 -0
- pythonagent/agent/probes/sql/dbapi.py +285 -0
- pythonagent/agent/probes/sql/dynamodb.py +90 -0
- pythonagent/agent/probes/sql/mysql_connector.py +24 -0
- pythonagent/agent/probes/sql/mysql_connector_cext.py +24 -0
- pythonagent/agent/probes/sql/mysqldb.py +43 -0
- pythonagent/agent/probes/sql/psycopg2.py +174 -0
- pythonagent/agent/probes/sql/pymysql.py +25 -0
- pythonagent/bootstrap/__init__.py +0 -0
- pythonagent/bootstrap/cav_gunicorn.py +26 -0
- pythonagent/bootstrap/cavagent_lambda_wrapper.py +291 -0
- pythonagent/bootstrap/run.py +47 -0
- pythonagent/bootstrap/sitecustomize.py +287 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/instrumentationprofile.json +26 -0
- pythonagent/cavisson/netdiagnostics/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/instrumentationprofile.json +42 -0
- pythonagent/cavisson/netdiagnostics/python/CavAgent/interceptor_points.txt +29 -0
- pythonagent/cavisson/netdiagnostics/python/config/ndsettings.conf +6 -0
- pythonagent/config.py +279 -0
- pythonagent/find.py +72 -0
- pythonagent/find_mod_cls_name.py +54 -0
- pythonagent/lang.py +131 -0
- pythonagent/lib.py +91 -0
- pythonagent/main/__init__.py +0 -0
- pythonagent/main/pytrace/__init__.py +79 -0
- pythonagent/main/pytrace/commands/__init__.py +0 -0
- pythonagent/main/pytrace/commands/auto_discovery.py +25 -0
- pythonagent/main/pytrace/commands/run.py +401 -0
- pythonagent/main/pytrace/pytrace.py +133 -0
- pythonagent/main/wsgi.py +6 -0
- pythonagent/main.py +27 -0
- pythonagent/run.py +46 -0
- pythonagent/sqins.py +8 -0
- pythonagent/test.py +73 -0
- pythonagent/utils.py +168 -0
- pythonagent/vendor/__init__.py +0 -0
- pythonagent/vendor/pympler/__init__.py +1 -0
- pythonagent/vendor/pympler/asizeof.py +2810 -0
- pythonagent/vendor/pympler/charts.py +62 -0
- pythonagent/vendor/pympler/classtracker.py +590 -0
- pythonagent/vendor/pympler/classtracker_stats.py +780 -0
- pythonagent/vendor/pympler/garbagegraph.py +80 -0
- pythonagent/vendor/pympler/mprofile.py +97 -0
- pythonagent/vendor/pympler/muppy.py +275 -0
- pythonagent/vendor/pympler/panels.py +115 -0
- pythonagent/vendor/pympler/process.py +238 -0
- pythonagent/vendor/pympler/py.typed +0 -0
- pythonagent/vendor/pympler/refbrowser.py +451 -0
- pythonagent/vendor/pympler/refgraph.py +350 -0
- pythonagent/vendor/pympler/summary.py +321 -0
- pythonagent/vendor/pympler/tracker.py +267 -0
- pythonagent/vendor/pympler/util/__init__.py +0 -0
- pythonagent/vendor/pympler/util/bottle.py +3809 -0
- pythonagent/vendor/pympler/util/compat.py +23 -0
- pythonagent/vendor/pympler/util/stringutils.py +77 -0
- pythonagent/vendor/pympler/web.py +346 -0
- pythonagent/vendor/werkzeug/__init__.py +20 -0
- pythonagent/vendor/werkzeug/_compat.py +228 -0
- pythonagent/vendor/werkzeug/_internal.py +473 -0
- pythonagent/vendor/werkzeug/_reloader.py +341 -0
- pythonagent/vendor/werkzeug/datastructures.py +3120 -0
- pythonagent/vendor/werkzeug/debug/__init__.py +498 -0
- pythonagent/vendor/werkzeug/debug/console.py +218 -0
- pythonagent/vendor/werkzeug/debug/repr.py +297 -0
- pythonagent/vendor/werkzeug/debug/tbtools.py +628 -0
- pythonagent/vendor/werkzeug/exceptions.py +829 -0
- pythonagent/vendor/werkzeug/filesystem.py +64 -0
- pythonagent/vendor/werkzeug/formparser.py +584 -0
- pythonagent/vendor/werkzeug/http.py +1307 -0
- pythonagent/vendor/werkzeug/local.py +420 -0
- pythonagent/vendor/werkzeug/middleware/__init__.py +25 -0
- pythonagent/vendor/werkzeug/middleware/dispatcher.py +66 -0
- pythonagent/vendor/werkzeug/middleware/http_proxy.py +219 -0
- pythonagent/vendor/werkzeug/middleware/lint.py +408 -0
- pythonagent/vendor/werkzeug/middleware/profiler.py +132 -0
- pythonagent/vendor/werkzeug/middleware/proxy_fix.py +169 -0
- pythonagent/vendor/werkzeug/middleware/shared_data.py +293 -0
- pythonagent/vendor/werkzeug/posixemulation.py +117 -0
- pythonagent/vendor/werkzeug/routing.py +2210 -0
- pythonagent/vendor/werkzeug/security.py +249 -0
- pythonagent/vendor/werkzeug/serving.py +1117 -0
- pythonagent/vendor/werkzeug/test.py +1123 -0
- pythonagent/vendor/werkzeug/testapp.py +241 -0
- pythonagent/vendor/werkzeug/urls.py +1138 -0
- pythonagent/vendor/werkzeug/useragents.py +202 -0
- pythonagent/vendor/werkzeug/utils.py +778 -0
- pythonagent/vendor/werkzeug/wrappers/__init__.py +36 -0
- pythonagent/vendor/werkzeug/wrappers/accept.py +50 -0
- pythonagent/vendor/werkzeug/wrappers/auth.py +33 -0
- pythonagent/vendor/werkzeug/wrappers/base_request.py +673 -0
- pythonagent/vendor/werkzeug/wrappers/base_response.py +700 -0
- pythonagent/vendor/werkzeug/wrappers/common_descriptors.py +341 -0
- pythonagent/vendor/werkzeug/wrappers/cors.py +100 -0
- pythonagent/vendor/werkzeug/wrappers/etag.py +304 -0
- pythonagent/vendor/werkzeug/wrappers/json.py +145 -0
- pythonagent/vendor/werkzeug/wrappers/request.py +49 -0
- pythonagent/vendor/werkzeug/wrappers/response.py +84 -0
- pythonagent/vendor/werkzeug/wrappers/user_agent.py +14 -0
- pythonagent/vendor/werkzeug/wsgi.py +1000 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Purpose: given an absolute file path (as found in sys.modules - application files and
|
|
2
|
+
# site-packages dependencies alike), resolve the installed distribution that file belongs to: its
|
|
3
|
+
# name, its version, and whether it IS the monitored application itself rather than a dependency.
|
|
4
|
+
#
|
|
5
|
+
# Used by find.py to report a jar-name-equivalent field per discovered module/class, mirroring the
|
|
6
|
+
# sibling NodeJS agent's moduleVersionResolver.js (itself mirroring the Java agent's per-class
|
|
7
|
+
# origin-jar reporting). NodeJS resolves ownership by walking up from a file to the nearest
|
|
8
|
+
# package.json - Python has no such per-directory manifest, so this instead builds a one-time
|
|
9
|
+
# reverse index from every installed distribution's own file list (RECORD), then falls back to a
|
|
10
|
+
# stdlib/site-packages boundary check to decide whether an unresolved file is the application's own
|
|
11
|
+
# code. Both the index and the boundary set are memoized, so a lookup after the first is O(1).
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import logging
|
|
16
|
+
import sysconfig
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from importlib.metadata import distributions # Python 3.8+
|
|
20
|
+
except ImportError:
|
|
21
|
+
try:
|
|
22
|
+
from importlib_metadata import distributions # backport for older Python 3
|
|
23
|
+
except ImportError:
|
|
24
|
+
distributions = None
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger('pythonagent.agent')
|
|
27
|
+
|
|
28
|
+
_file_to_package = None
|
|
29
|
+
_boundary_dirs = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _normalize(path):
|
|
33
|
+
return os.path.normcase(os.path.abspath(path))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _build_file_to_package_index():
|
|
37
|
+
index = {}
|
|
38
|
+
if distributions is None:
|
|
39
|
+
return index
|
|
40
|
+
for dist in distributions():
|
|
41
|
+
try:
|
|
42
|
+
name = dist.metadata['Name'] if dist.metadata else None
|
|
43
|
+
version = dist.version
|
|
44
|
+
files = dist.files or []
|
|
45
|
+
except Exception:
|
|
46
|
+
continue
|
|
47
|
+
if not name or not version:
|
|
48
|
+
continue
|
|
49
|
+
for f in files:
|
|
50
|
+
try:
|
|
51
|
+
index[_normalize(str(dist.locate_file(f)))] = (name, version)
|
|
52
|
+
except Exception:
|
|
53
|
+
continue
|
|
54
|
+
return index
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _get_file_to_package_index():
|
|
58
|
+
global _file_to_package
|
|
59
|
+
if _file_to_package is None:
|
|
60
|
+
try:
|
|
61
|
+
_file_to_package = _build_file_to_package_index()
|
|
62
|
+
except Exception as e:
|
|
63
|
+
logger.warning('module_version_resolver | Error building package file index : %s', e)
|
|
64
|
+
_file_to_package = {}
|
|
65
|
+
return _file_to_package
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _build_boundary_dirs():
|
|
69
|
+
dirs = set()
|
|
70
|
+
try:
|
|
71
|
+
paths = sysconfig.get_paths()
|
|
72
|
+
for key in ('stdlib', 'platstdlib', 'purelib', 'platlib'):
|
|
73
|
+
p = paths.get(key)
|
|
74
|
+
if p:
|
|
75
|
+
dirs.add(_normalize(p))
|
|
76
|
+
except Exception:
|
|
77
|
+
pass
|
|
78
|
+
try:
|
|
79
|
+
import site
|
|
80
|
+
if hasattr(site, 'getsitepackages'):
|
|
81
|
+
for p in site.getsitepackages():
|
|
82
|
+
dirs.add(_normalize(p))
|
|
83
|
+
if hasattr(site, 'getusersitepackages'):
|
|
84
|
+
dirs.add(_normalize(site.getusersitepackages()))
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
return dirs
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _get_boundary_dirs():
|
|
91
|
+
global _boundary_dirs
|
|
92
|
+
if _boundary_dirs is None:
|
|
93
|
+
try:
|
|
94
|
+
_boundary_dirs = _build_boundary_dirs()
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.warning('module_version_resolver | Error building stdlib/site-packages boundary : %s', e)
|
|
97
|
+
_boundary_dirs = set()
|
|
98
|
+
return _boundary_dirs
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _is_under_boundary(abs_path):
|
|
102
|
+
for boundary_dir in _get_boundary_dirs():
|
|
103
|
+
if abs_path == boundary_dir or abs_path.startswith(boundary_dir + os.sep):
|
|
104
|
+
return True
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def resolve(file_path):
|
|
109
|
+
"""Resolve the installed distribution owning file_path.
|
|
110
|
+
|
|
111
|
+
@return dict with 'name', 'version', 'isApp' - name/version are None when the file is the
|
|
112
|
+
application's own code (isApp True) or when nothing could be resolved at all (isApp False,
|
|
113
|
+
e.g. a stdlib module or a distribution with no readable RECORD); returns None only when
|
|
114
|
+
file_path itself is unusable.
|
|
115
|
+
"""
|
|
116
|
+
if not file_path:
|
|
117
|
+
return None
|
|
118
|
+
try:
|
|
119
|
+
abs_path = _normalize(file_path)
|
|
120
|
+
except Exception:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
match = _get_file_to_package_index().get(abs_path)
|
|
124
|
+
if match:
|
|
125
|
+
return {'name': match[0], 'version': match[1], 'isApp': False}
|
|
126
|
+
|
|
127
|
+
if _is_under_boundary(abs_path):
|
|
128
|
+
# Inside stdlib/site-packages but not owned by any distribution we could index (e.g. a
|
|
129
|
+
# namespace package, or an editable/legacy install with no RECORD) - not app code, but no
|
|
130
|
+
# name/version to report either.
|
|
131
|
+
return {'name': None, 'version': None, 'isApp': False}
|
|
132
|
+
|
|
133
|
+
# Outside stdlib and site-packages entirely: this is the monitored application's own code.
|
|
134
|
+
return {'name': None, 'version': None, 'isApp': True}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def format_jar_field(pkg_info):
|
|
138
|
+
"""Format a resolve() result into the auto-discovery jar-equivalent field, mirroring
|
|
139
|
+
autoDiscovery.js's pkgField: "<name>@<version>", suffixed "(app)" when the file belongs to the
|
|
140
|
+
monitored application itself, or '' when nothing could be resolved."""
|
|
141
|
+
if not pkg_info:
|
|
142
|
+
return ''
|
|
143
|
+
name = pkg_info.get('name') or ''
|
|
144
|
+
version = pkg_info.get('version') or ''
|
|
145
|
+
field = (name + '@' + version) if (name and version) else ''
|
|
146
|
+
if pkg_info.get('isApp'):
|
|
147
|
+
field += '(app)'
|
|
148
|
+
return field
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def clear_cache():
|
|
152
|
+
"""Exposed for tests/diagnostics only - not used by production callers."""
|
|
153
|
+
global _file_to_package, _boundary_dirs
|
|
154
|
+
_file_to_package = None
|
|
155
|
+
_boundary_dirs = None
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def create_module_list(filePath):
|
|
5
|
+
try:
|
|
6
|
+
f = open(filePath, "r")
|
|
7
|
+
except:
|
|
8
|
+
return []
|
|
9
|
+
x = f.read()
|
|
10
|
+
if x[1] == "[" and x[-1] != "]":
|
|
11
|
+
x += "]"
|
|
12
|
+
obj2 = json.loads(x)
|
|
13
|
+
list_of_modules = []
|
|
14
|
+
for item in obj2:
|
|
15
|
+
module_name_value = item.get("modulename", None)
|
|
16
|
+
module_name_split = module_name_value.split("|")
|
|
17
|
+
module_name = module_name_split[0]
|
|
18
|
+
|
|
19
|
+
if module_name not in list_of_modules:
|
|
20
|
+
list_of_modules.append(module_name)
|
|
21
|
+
return list_of_modules
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def create_method_list(filePath):
|
|
25
|
+
print("filePath", filePath)
|
|
26
|
+
try:
|
|
27
|
+
f = open(filePath, "r")
|
|
28
|
+
except:
|
|
29
|
+
return []
|
|
30
|
+
|
|
31
|
+
x = f.read()
|
|
32
|
+
|
|
33
|
+
if x[1] == "[" and x[-1] != "]":
|
|
34
|
+
x += "]"
|
|
35
|
+
|
|
36
|
+
obj2 = json.loads(x)
|
|
37
|
+
LOM = []
|
|
38
|
+
for item in obj2:
|
|
39
|
+
module_name_value = item.get("modulename", None)
|
|
40
|
+
module_name_split = module_name_value.split("|")
|
|
41
|
+
module_name = module_name_split[0]
|
|
42
|
+
|
|
43
|
+
if item.get("classes", None) != "":
|
|
44
|
+
classes_split = item.get("classes").split("|")
|
|
45
|
+
class_name = classes_split[0]
|
|
46
|
+
class_level_methods_str = classes_split[1]
|
|
47
|
+
class_level_methods = class_level_methods_str.split(",")
|
|
48
|
+
for class_level_method in class_level_methods:
|
|
49
|
+
LOM.append(module_name + "." + class_name + "." + class_level_method)
|
|
50
|
+
else:
|
|
51
|
+
module_level_methods_str = module_name_split[1]
|
|
52
|
+
module_level_methods = module_level_methods_str.split(",")
|
|
53
|
+
for module_level_method in module_level_methods:
|
|
54
|
+
LOM.append(module_name + "." + module_level_method)
|
|
55
|
+
|
|
56
|
+
list_of_methods = [str(r) for r in LOM]
|
|
57
|
+
|
|
58
|
+
return list_of_methods
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
abc = create_method_list("k.json")
|
|
63
|
+
print(abc)
|
|
64
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
#import jsonpickle
|
|
4
|
+
#from json import JSONEncoder
|
|
5
|
+
|
|
6
|
+
_logger = logging.getLogger('pythonagent.agent')
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from types import SimpleNamespace as Namespace
|
|
10
|
+
except ImportError:
|
|
11
|
+
# Python 2.x fallback
|
|
12
|
+
from argparse import Namespace
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
#print("Encode Object into JSON formatted Data using jsonpickle")
|
|
18
|
+
def flatten_hook(obj):
|
|
19
|
+
for key, value in obj.items():
|
|
20
|
+
if isinstance(value, str):
|
|
21
|
+
try:
|
|
22
|
+
obj[key] = json.loads(value, object_hook=flatten_hook)
|
|
23
|
+
except ValueError:
|
|
24
|
+
pass
|
|
25
|
+
return obj
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_method_json(filePath):
|
|
29
|
+
try:
|
|
30
|
+
f = open(filePath, "r")
|
|
31
|
+
except:
|
|
32
|
+
return {}
|
|
33
|
+
x = f.read()
|
|
34
|
+
if x[1] == "[" and x[-1] != "]":
|
|
35
|
+
x += "]"
|
|
36
|
+
obj2 = json.loads(x)
|
|
37
|
+
#obj2 = json.loads(f.read(),object_hook=flatten_hook)
|
|
38
|
+
|
|
39
|
+
return obj2
|
|
40
|
+
|
|
41
|
+
def create_method_array(filePath):
|
|
42
|
+
#print('reading the profile at path: ',filePath)
|
|
43
|
+
try:
|
|
44
|
+
f = open(filePath, "r")
|
|
45
|
+
except:
|
|
46
|
+
return []
|
|
47
|
+
|
|
48
|
+
x = f.read()
|
|
49
|
+
if x[1] == "[" and x[-1] != "]":
|
|
50
|
+
x += "]"
|
|
51
|
+
obj2 = json.loads(x)
|
|
52
|
+
#print('Exception occure to open profiler file')
|
|
53
|
+
#obj2 = json.loads(f.read(),object_hook=flatten_hook)
|
|
54
|
+
|
|
55
|
+
#list of methods
|
|
56
|
+
LOM = []
|
|
57
|
+
|
|
58
|
+
for module in obj2["modules"]:
|
|
59
|
+
|
|
60
|
+
#module = module.split("/")[-1].replace(".py","")
|
|
61
|
+
# print(module+"--------------------------------------------")
|
|
62
|
+
for MLM in module["moduleLevelMethod"]:
|
|
63
|
+
#print(module["moduleName"])
|
|
64
|
+
LOM.append(module["moduleName"]+"."+MLM["name"])
|
|
65
|
+
for each_class in module["classes"]:
|
|
66
|
+
for CLM in each_class["classLevelMethods"]:
|
|
67
|
+
LOM.append(module["moduleName"]+"."+each_class["className"]+"."+CLM["name"])
|
|
68
|
+
|
|
69
|
+
# print(LOM)
|
|
70
|
+
|
|
71
|
+
arr = [str(r) for r in LOM]
|
|
72
|
+
|
|
73
|
+
#print(arr)
|
|
74
|
+
return arr
|
|
75
|
+
|
|
76
|
+
#abc = create_method_json("/home/cavisson/methodTest/instrumentationprofile.json")["modules"]
|
|
77
|
+
|
|
78
|
+
#print(abc["moduleName"])
|
|
79
|
+
|
|
80
|
+
def create_method_list(filePath):
|
|
81
|
+
print("filePath", filePath)
|
|
82
|
+
try:
|
|
83
|
+
f = open(filePath, "r")
|
|
84
|
+
except:
|
|
85
|
+
return []
|
|
86
|
+
|
|
87
|
+
LOM = set()
|
|
88
|
+
|
|
89
|
+
string_of_profiles = f.read()
|
|
90
|
+
list_of_profiles = string_of_profiles.split("\n\n\n")
|
|
91
|
+
|
|
92
|
+
for profile in list_of_profiles:
|
|
93
|
+
try:
|
|
94
|
+
obj2 = json.loads(profile)
|
|
95
|
+
except:
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
for item in obj2:
|
|
99
|
+
try:
|
|
100
|
+
module_name_value = item.get("modulename", None)
|
|
101
|
+
module_name_split = module_name_value.split("|")
|
|
102
|
+
module_name = module_name_split[0]
|
|
103
|
+
|
|
104
|
+
if item.get("classes", None) != "":
|
|
105
|
+
classes_split = item.get("classes").split("|")
|
|
106
|
+
class_name = classes_split[0]
|
|
107
|
+
class_level_methods_str = classes_split[1]
|
|
108
|
+
class_level_methods = class_level_methods_str.split(",")
|
|
109
|
+
for class_level_method in class_level_methods:
|
|
110
|
+
LOM.add(module_name + "." + class_name + "." + class_level_method)
|
|
111
|
+
else:
|
|
112
|
+
module_level_methods_str = module_name_split[1]
|
|
113
|
+
module_level_methods = module_level_methods_str.split(",")
|
|
114
|
+
for module_level_method in module_level_methods:
|
|
115
|
+
LOM.add(module_name + "." + module_level_method)
|
|
116
|
+
except Exception:
|
|
117
|
+
# A single malformed profile entry (e.g. missing "modulename",
|
|
118
|
+
# or "classes" empty with no "|" in "modulename") must not abort
|
|
119
|
+
# parsing of the rest of the profile -- that previously dropped
|
|
120
|
+
# every remaining module (including app modules) from a large
|
|
121
|
+
# "complete" profile and silently fell back to the stale local
|
|
122
|
+
# instrumentationprofile.json.
|
|
123
|
+
_logger.warning("Skipping malformed instrumentation profile entry: %r", item)
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
list_of_methods = [str(r) for r in LOM]
|
|
127
|
+
|
|
128
|
+
return list_of_methods
|
|
129
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
from . import frameworks, logging, http, sql,Instrumentation
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from importlib.machinery import ModuleSpec
|
|
6
|
+
except ImportError:
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from importlib import reload
|
|
11
|
+
except ImportError:
|
|
12
|
+
from imp import reload
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
URL_PROPERTY_MAX_LEN = 100
|
|
16
|
+
HOST_PROPERTY_MAX_LEN = 50
|
|
17
|
+
DB_NAME_PROPERTY_MAX_LEN = 50
|
|
18
|
+
VENDOR_PROPERTY_MAX_LEN = 50
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#from pythonagent.agent.internal.intercept_module import BT
|
|
22
|
+
#BT_INTERCEPTORS = BT
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# BT_INTERCEPTORS = (
|
|
28
|
+
# # Entry points
|
|
29
|
+
# ('flask', frameworks.intercept_flask),
|
|
30
|
+
# ('django.core.handlers.wsgi', frameworks.intercept_django_wsgi_handler),
|
|
31
|
+
# ('django.core.handlers.base', frameworks.intercept_django_base_handler),
|
|
32
|
+
#
|
|
33
|
+
# # HTTP exit calls
|
|
34
|
+
# ('httplib', http.intercept_httplib),
|
|
35
|
+
# ('http.client', http.intercept_httplib),
|
|
36
|
+
# ('urllib3', http.intercept_urllib3),
|
|
37
|
+
# ('requests', http.intercept_requests),
|
|
38
|
+
#
|
|
39
|
+
# # Logging
|
|
40
|
+
# ('logging', logging.intercept_logging),
|
|
41
|
+
#
|
|
42
|
+
# # SQL exit calls
|
|
43
|
+
# ('psycopg2', sql.psycopg2.intercept_psycopg2_connection),
|
|
44
|
+
# ('pymysql.connections', sql.pymysql.intercept_pymysql_connections),
|
|
45
|
+
# ('mysql.connector.connection', sql.mysql_connector.intercept_mysql_connector_connection),
|
|
46
|
+
# ('MySQLdb.connections', sql.mysqldb.intercept_MySQLdb_connection),
|
|
47
|
+
# )
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def add_hook(agent):
|
|
51
|
+
"""Add the module interceptor hook for pythonagent, if it's not already registered.
|
|
52
|
+
|
|
53
|
+
Reuses the existing ModuleInterceptor for this agent when one is already present in
|
|
54
|
+
sys.meta_path. This prevents accumulating N interceptors after N re-bootstraps, which
|
|
55
|
+
would cause O(N) overhead on every module import and N-times callback execution.
|
|
56
|
+
"""
|
|
57
|
+
for finder in sys.meta_path:
|
|
58
|
+
if isinstance(finder, ModuleInterceptor) and finder.agent is agent:
|
|
59
|
+
return finder # reuse — prevents sys.meta_path accumulation
|
|
60
|
+
interceptor = ModuleInterceptor(agent)
|
|
61
|
+
sys.meta_path.insert(0, interceptor)
|
|
62
|
+
return interceptor
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ModuleInterceptor(object):
|
|
66
|
+
"""Intercepts finding and loading modules in order to monkey patch them on load.
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, agent):
|
|
71
|
+
super(ModuleInterceptor, self).__init__()
|
|
72
|
+
self.agent = agent
|
|
73
|
+
self.module_hooks = {}
|
|
74
|
+
self.intercepted_modules = set()
|
|
75
|
+
|
|
76
|
+
def find_spec(self, full_name, path, target=None):
|
|
77
|
+
#print("FIND SPEC {} ".format(full_name))
|
|
78
|
+
if full_name in self.module_hooks:
|
|
79
|
+
top = full_name.split('.')[0]
|
|
80
|
+
stdlib_names = getattr(sys, 'stdlib_module_names', None) or Instrumentation._FALLBACK_STDLIB_NAMES
|
|
81
|
+
if top in stdlib_names:
|
|
82
|
+
self.module_hooks.pop(full_name, None)
|
|
83
|
+
return None
|
|
84
|
+
return ModuleSpec(full_name, self)
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def find_module(self, full_name, path=None):
|
|
88
|
+
#print("FIND MODULE", full_name)
|
|
89
|
+
#self.agent.logger.info("Finding module {0}".format(full_name))
|
|
90
|
+
self.agent.logger.info('Modulename: ModuleInterceptor class || full_name {0}'.format(full_name))
|
|
91
|
+
if full_name in self.module_hooks:
|
|
92
|
+
top = full_name.split('.')[0]
|
|
93
|
+
stdlib_names = getattr(sys, 'stdlib_module_names', None) or Instrumentation._FALLBACK_STDLIB_NAMES
|
|
94
|
+
if top in stdlib_names:
|
|
95
|
+
self.module_hooks.pop(full_name, None)
|
|
96
|
+
return None
|
|
97
|
+
return self
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def load_module(self, name):
|
|
101
|
+
#print("LOAD MODULE {}".format(name))
|
|
102
|
+
# Remove the module from the list of hooks so that we never see it again.
|
|
103
|
+
#self.agent.logger.info('Modulename: ModuleInterceptor class || function name {0}'.format(name))
|
|
104
|
+
#self.agent.logger.info("Loader loading load module {0}".format(name))
|
|
105
|
+
# for module in custom_methods_to_instrument_json["modules"]:
|
|
106
|
+
# if name == module["moduleName"]:
|
|
107
|
+
# hooks = Instrumentation.intercept_instrumented_method
|
|
108
|
+
|
|
109
|
+
# hooks = self.module_hooks.pop(name, [])
|
|
110
|
+
# print("hooks for module: ",name," ->",hooks)
|
|
111
|
+
|
|
112
|
+
#if name in all_module_names:
|
|
113
|
+
# hooks = Instrumentation.intercept_instrumented_method
|
|
114
|
+
# all_module_names.remove(name)
|
|
115
|
+
#else:
|
|
116
|
+
# hooks = self.module_hooks.pop(name, [])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
hooks = self.module_hooks.pop(name, [])
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
#self.agent.logger.info("hooks for module: {0} and hook {1}".format(name,hooks))
|
|
128
|
+
#self.agent.logger.info('Modulename: ModuleInterceptor class || hooks for module {0}'.format(hooks))
|
|
129
|
+
"""
|
|
130
|
+
if name in sys.modules:
|
|
131
|
+
# Already been loaded. Return it as is.
|
|
132
|
+
return sys.modules[name]
|
|
133
|
+
"""
|
|
134
|
+
#self.agent.logger.debug('Intercepting import %s', name)
|
|
135
|
+
#self.agent.logger.info(("Intercepting import {0}".format(name)))
|
|
136
|
+
#self.agent.logger.info('Modulename: ModuleInterceptor class || intercepting import {0}'.format(name))
|
|
137
|
+
try:
|
|
138
|
+
__import__(name) # __import__('a.b.c') returns <module a>, not <module a.b.c>
|
|
139
|
+
except ImportError:
|
|
140
|
+
raise # propagate missing-module errors as-is
|
|
141
|
+
except Exception as e:
|
|
142
|
+
# A non-ImportError from module initialisation (e.g. AttributeError from
|
|
143
|
+
# a version mismatch in dns/query.py when httpcore._backends is absent).
|
|
144
|
+
# Callers such as mysql.connector.pooling use "except ImportError" guards —
|
|
145
|
+
# wrapping here lets those guards work correctly instead of crashing the app.
|
|
146
|
+
raise ImportError(
|
|
147
|
+
"pythonagent: {!r} raised {} during load: {}".format(
|
|
148
|
+
name, type(e).__name__, e)
|
|
149
|
+
) from e
|
|
150
|
+
module = sys.modules.get(name) # ...so get <module a.b.c> from sys.modules
|
|
151
|
+
if module is None:
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
self._intercept_module(module, hooks)
|
|
155
|
+
|
|
156
|
+
return module
|
|
157
|
+
|
|
158
|
+
def call_on_import(self, module_name, cb):
|
|
159
|
+
# Never intercept stdlib module imports — adding them to module_hooks causes
|
|
160
|
+
# load_module to call __import__ re-entrantly, which corrupts Python 3.12
|
|
161
|
+
# per-module import locks (_bootstrap._weakref = None → AttributeError).
|
|
162
|
+
top = module_name.split('.')[0]
|
|
163
|
+
stdlib_names = getattr(sys, 'stdlib_module_names', None) or Instrumentation._FALLBACK_STDLIB_NAMES
|
|
164
|
+
if top in stdlib_names and top not in {"http", "urllib"}:
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
#print("agent/probes/init.call_on_import() ...")
|
|
168
|
+
#self.agent.logger.info('Modulename: ModuleInterceptor class || module_name is {0} and callback is {1}'.format(module_name,cb))
|
|
169
|
+
#self.agent.logger.info("modulename: %s", module_name, " callback: %s", cb)
|
|
170
|
+
#self.agent.logger.info('Modulename: ModuleInterceptor class inside call_on_import function callback is {0}'.format(cb))
|
|
171
|
+
|
|
172
|
+
# print(custom_methods_to_instrument_json["modules"])
|
|
173
|
+
# print("hooks for module: ",name," ->",hooks)
|
|
174
|
+
|
|
175
|
+
#if module_name in all_module_names:
|
|
176
|
+
# hooks = Instrumentation.intercept_instrumented_method
|
|
177
|
+
# self._intercept_module(sys.modules[module_name], [hooks])
|
|
178
|
+
|
|
179
|
+
if module_name in sys.modules:
|
|
180
|
+
#print("FOUND IN SYS MODULES")
|
|
181
|
+
#print("module" ,module_name, " found in sys.modules. Calling intercept module")
|
|
182
|
+
self._intercept_module(sys.modules[module_name], [cb])
|
|
183
|
+
self.module_hooks.setdefault(module_name, [])
|
|
184
|
+
if cb not in self.module_hooks[module_name]:
|
|
185
|
+
self.module_hooks[module_name].append(cb)
|
|
186
|
+
else:
|
|
187
|
+
#print("NOT FOUND IN SYS MODULES")
|
|
188
|
+
#print("module" ,module_name, " not found in sys.modules. Will be called from load modules")
|
|
189
|
+
self.module_hooks.setdefault(module_name, [])
|
|
190
|
+
if cb not in self.module_hooks[module_name]:
|
|
191
|
+
self.module_hooks[module_name].append(cb)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
#print('self.module_hooks value',self.module_hooks)
|
|
195
|
+
#if module_name in all_module_names:
|
|
196
|
+
# hooks = Instrumentation.intercept_instrumented_method
|
|
197
|
+
# __import__(module_name)
|
|
198
|
+
# self._intercept_module(sys.modules[module_name], [hooks])
|
|
199
|
+
|
|
200
|
+
def _intercept_module(self, module, hooks):
|
|
201
|
+
#print("intercept module : {} hooks {}".format(module, hooks))
|
|
202
|
+
try:
|
|
203
|
+
for hook in hooks:
|
|
204
|
+
#self.agent.logger.debug("Running {0} hook {1}".format(module.__name__, hook))
|
|
205
|
+
hook(self.agent, module)
|
|
206
|
+
self.intercepted_modules.add(module)
|
|
207
|
+
#print("self.intercepted_modules", self.intercepted_modules)
|
|
208
|
+
#print("HOOK CALLED")
|
|
209
|
+
except:
|
|
210
|
+
#print("exception in _intercept_module")
|
|
211
|
+
self.agent.logger.exception("Exception in {0} hook.".format(module.__name__))
|
|
212
|
+
|
|
213
|
+
# Re-import to ensure the module hasn't been partially patched.
|
|
214
|
+
self.agent.logger.debug("Re-importing {0} after error in module hook".format( module.__name__))
|
|
215
|
+
reload(module)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
|