pybinaryguard 1.0.0__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.
- pybinaryguard/__init__.py +78 -0
- pybinaryguard/__main__.py +7 -0
- pybinaryguard/_compat/__init__.py +0 -0
- pybinaryguard/agent/__init__.py +63 -0
- pybinaryguard/agent/guard.py +232 -0
- pybinaryguard/agent/recommender.py +283 -0
- pybinaryguard/agent/schema.py +200 -0
- pybinaryguard/agent/simulator.py +430 -0
- pybinaryguard/agent/tool_interface.py +474 -0
- pybinaryguard/analyzers/__init__.py +209 -0
- pybinaryguard/analyzers/base.py +40 -0
- pybinaryguard/analyzers/dependency_analyzer.py +336 -0
- pybinaryguard/analyzers/elf_analyzer.py +754 -0
- pybinaryguard/analyzers/symbol_analyzer.py +280 -0
- pybinaryguard/analyzers/wheel_analyzer.py +308 -0
- pybinaryguard/cli/__init__.py +5 -0
- pybinaryguard/cli/commands.py +414 -0
- pybinaryguard/cli/formatters.py +720 -0
- pybinaryguard/cli/main.py +250 -0
- pybinaryguard/diagnostics/__init__.py +13 -0
- pybinaryguard/diagnostics/explainer.py +356 -0
- pybinaryguard/diagnostics/findings.py +146 -0
- pybinaryguard/diagnostics/suggestions.py +508 -0
- pybinaryguard/frameworks/__init__.py +20 -0
- pybinaryguard/frameworks/onnxruntime.py +214 -0
- pybinaryguard/frameworks/pytorch.py +223 -0
- pybinaryguard/frameworks/tensorflow.py +266 -0
- pybinaryguard/frameworks/tensorrt.py +189 -0
- pybinaryguard/models/__init__.py +19 -0
- pybinaryguard/models/enums.py +102 -0
- pybinaryguard/models/finding.py +109 -0
- pybinaryguard/models/package.py +121 -0
- pybinaryguard/models/system.py +118 -0
- pybinaryguard/plugins/__init__.py +19 -0
- pybinaryguard/plugins/contrib/__init__.py +7 -0
- pybinaryguard/plugins/contrib/gstreamer.py +109 -0
- pybinaryguard/plugins/contrib/jetson.py +385 -0
- pybinaryguard/plugins/contrib/opencv.py +306 -0
- pybinaryguard/plugins/contrib/tensorrt.py +426 -0
- pybinaryguard/plugins/hooks.py +273 -0
- pybinaryguard/plugins/loader.py +190 -0
- pybinaryguard/predictor/__init__.py +23 -0
- pybinaryguard/predictor/dependency_graph.py +226 -0
- pybinaryguard/predictor/linker_simulator.py +161 -0
- pybinaryguard/predictor/predictor.py +144 -0
- pybinaryguard/predictor/resolver.py +282 -0
- pybinaryguard/probes/__init__.py +73 -0
- pybinaryguard/probes/base.py +45 -0
- pybinaryguard/probes/board_probe.py +248 -0
- pybinaryguard/probes/cpu_probe.py +176 -0
- pybinaryguard/probes/glibc_probe.py +215 -0
- pybinaryguard/probes/gpu_probe.py +430 -0
- pybinaryguard/probes/library_probe.py +132 -0
- pybinaryguard/probes/os_probe.py +227 -0
- pybinaryguard/probes/python_probe.py +135 -0
- pybinaryguard/probes/toolchain_probe.py +89 -0
- pybinaryguard/probes/venv_probe.py +111 -0
- pybinaryguard/profiles/__init__.py +12 -0
- pybinaryguard/profiles/engine.py +343 -0
- pybinaryguard/rules/__init__.py +24 -0
- pybinaryguard/rules/base.py +57 -0
- pybinaryguard/rules/builtin/__init__.py +136 -0
- pybinaryguard/rules/builtin/arch_rules.py +86 -0
- pybinaryguard/rules/builtin/board_profile_rules.py +282 -0
- pybinaryguard/rules/builtin/container_rules.py +170 -0
- pybinaryguard/rules/builtin/cpu_rules.py +204 -0
- pybinaryguard/rules/builtin/cuda_rules.py +760 -0
- pybinaryguard/rules/builtin/dependency_rules.py +278 -0
- pybinaryguard/rules/builtin/framework_rules.py +252 -0
- pybinaryguard/rules/builtin/glibc_rules.py +320 -0
- pybinaryguard/rules/builtin/numpy_rules.py +110 -0
- pybinaryguard/rules/builtin/predictive_rules.py +165 -0
- pybinaryguard/rules/builtin/python_abi_rules.py +259 -0
- pybinaryguard/rules/builtin/source_build_rules.py +150 -0
- pybinaryguard/rules/builtin/venv_rules.py +159 -0
- pybinaryguard/rules/engine.py +123 -0
- pybinaryguard/scanner.py +904 -0
- pybinaryguard/scoring/__init__.py +19 -0
- pybinaryguard/scoring/engine.py +416 -0
- pybinaryguard/snapshot/__init__.py +20 -0
- pybinaryguard/snapshot/generator.py +168 -0
- pybinaryguard/snapshot/lockfile.py +152 -0
- pybinaryguard/snapshot/verifier.py +315 -0
- pybinaryguard/validators/__init__.py +7 -0
- pybinaryguard/validators/import_validator.py +231 -0
- pybinaryguard-1.0.0.dist-info/METADATA +876 -0
- pybinaryguard-1.0.0.dist-info/RECORD +91 -0
- pybinaryguard-1.0.0.dist-info/WHEEL +5 -0
- pybinaryguard-1.0.0.dist-info/entry_points.txt +8 -0
- pybinaryguard-1.0.0.dist-info/licenses/LICENSE +21 -0
- pybinaryguard-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""ONNX Runtime framework deep inspection.
|
|
2
|
+
|
|
3
|
+
Validates ONNX Runtime execution providers, version compatibility,
|
|
4
|
+
and hardware acceleration support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import Dict, List, Optional, Set
|
|
11
|
+
|
|
12
|
+
from pybinaryguard.models.package import PackageBinaryInfo
|
|
13
|
+
from pybinaryguard.models.system import SystemProfile
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def check_onnx_runtime_providers(
|
|
17
|
+
package: PackageBinaryInfo,
|
|
18
|
+
profile: SystemProfile
|
|
19
|
+
) -> List[Dict[str, str]]:
|
|
20
|
+
"""Check ONNX Runtime execution providers and compatibility.
|
|
21
|
+
|
|
22
|
+
Validates:
|
|
23
|
+
- Available execution providers (CUDA, TensorRT, OpenVINO, etc.)
|
|
24
|
+
- CUDA provider compatibility with system
|
|
25
|
+
- Missing hardware-specific providers
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
package: PackageBinaryInfo for onnxruntime package
|
|
29
|
+
profile: System profile
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List of compatibility issues and recommendations
|
|
33
|
+
"""
|
|
34
|
+
issues: List[Dict[str, str]] = []
|
|
35
|
+
|
|
36
|
+
if not package.name.lower().startswith("onnxruntime"):
|
|
37
|
+
return issues
|
|
38
|
+
|
|
39
|
+
# Detect available execution providers
|
|
40
|
+
providers = detect_execution_providers(package)
|
|
41
|
+
|
|
42
|
+
# Check if GPU package on GPU system
|
|
43
|
+
has_gpu = profile.cuda_runtime_version is not None
|
|
44
|
+
has_cuda_provider = "CUDAExecutionProvider" in providers
|
|
45
|
+
|
|
46
|
+
if has_gpu and not has_cuda_provider:
|
|
47
|
+
issues.append({
|
|
48
|
+
"issue": "missing_cuda_provider",
|
|
49
|
+
"severity": "warning",
|
|
50
|
+
"message": "NVIDIA GPU detected but CUDA execution provider not available",
|
|
51
|
+
"recommendation": "Install onnxruntime-gpu instead of onnxruntime for GPU acceleration",
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if has_cuda_provider and not has_gpu:
|
|
55
|
+
issues.append({
|
|
56
|
+
"issue": "cuda_provider_no_gpu",
|
|
57
|
+
"severity": "info",
|
|
58
|
+
"message": "CUDA execution provider available but no NVIDIA GPU detected",
|
|
59
|
+
"recommendation": "Install onnxruntime (CPU) to reduce package size",
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
# Check CUDA provider version compatibility
|
|
63
|
+
if has_cuda_provider and profile.cuda_runtime_version:
|
|
64
|
+
cuda_compat_issues = check_onnx_cuda_compatibility(
|
|
65
|
+
package.version,
|
|
66
|
+
profile.cuda_runtime_version
|
|
67
|
+
)
|
|
68
|
+
if cuda_compat_issues:
|
|
69
|
+
issues.append({
|
|
70
|
+
"issue": "onnx_cuda_version_mismatch",
|
|
71
|
+
"severity": "warning",
|
|
72
|
+
"message": cuda_compat_issues,
|
|
73
|
+
"recommendation": "Check ONNX Runtime CUDA compatibility matrix",
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
# Check for TensorRT provider on Jetson
|
|
77
|
+
if profile.board_name and "jetson" in profile.board_name.lower():
|
|
78
|
+
if "TensorrtExecutionProvider" not in providers:
|
|
79
|
+
issues.append({
|
|
80
|
+
"issue": "missing_tensorrt_provider_jetson",
|
|
81
|
+
"severity": "info",
|
|
82
|
+
"message": "TensorRT execution provider not detected on Jetson device",
|
|
83
|
+
"recommendation": "Build ONNX Runtime with TensorRT support for optimal performance",
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return issues
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def detect_execution_providers(package: PackageBinaryInfo) -> Set[str]:
|
|
90
|
+
"""Detect available ONNX Runtime execution providers.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
package: PackageBinaryInfo for onnxruntime package
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Set of detected execution provider names
|
|
97
|
+
"""
|
|
98
|
+
providers: Set[str] = set()
|
|
99
|
+
|
|
100
|
+
# Always has CPU provider
|
|
101
|
+
providers.add("CPUExecutionProvider")
|
|
102
|
+
|
|
103
|
+
for so in package.shared_objects:
|
|
104
|
+
if not so.path:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
path_lower = so.path.lower()
|
|
108
|
+
|
|
109
|
+
# CUDA provider
|
|
110
|
+
if "cuda" in path_lower:
|
|
111
|
+
providers.add("CUDAExecutionProvider")
|
|
112
|
+
|
|
113
|
+
# TensorRT provider
|
|
114
|
+
if "tensorrt" in path_lower:
|
|
115
|
+
providers.add("TensorrtExecutionProvider")
|
|
116
|
+
|
|
117
|
+
# OpenVINO provider
|
|
118
|
+
if "openvino" in path_lower:
|
|
119
|
+
providers.add("OpenVINOExecutionProvider")
|
|
120
|
+
|
|
121
|
+
# DirectML provider (Windows)
|
|
122
|
+
if "directml" in path_lower:
|
|
123
|
+
providers.add("DmlExecutionProvider")
|
|
124
|
+
|
|
125
|
+
# CoreML provider (macOS)
|
|
126
|
+
if "coreml" in path_lower:
|
|
127
|
+
providers.add("CoreMLExecutionProvider")
|
|
128
|
+
|
|
129
|
+
return providers
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def check_onnx_cuda_compatibility(
|
|
133
|
+
onnx_version: Optional[str],
|
|
134
|
+
cuda_version: Optional[str]
|
|
135
|
+
) -> Optional[str]:
|
|
136
|
+
"""Check ONNX Runtime GPU version compatibility with CUDA.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
onnx_version: ONNX Runtime version (e.g., "1.16.0")
|
|
140
|
+
cuda_version: CUDA version (e.g., "12.1")
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Error message if incompatible, None if compatible
|
|
144
|
+
"""
|
|
145
|
+
if not onnx_version or not cuda_version:
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
# Compatibility matrix (ONNX Runtime -> CUDA versions)
|
|
149
|
+
compatibility_matrix = {
|
|
150
|
+
"1.16": ["11.8", "12.2"],
|
|
151
|
+
"1.15": ["11.8", "12.1"],
|
|
152
|
+
"1.14": ["11.6", "11.7", "11.8"],
|
|
153
|
+
"1.13": ["11.6", "11.7"],
|
|
154
|
+
"1.12": ["11.4", "11.6"],
|
|
155
|
+
"1.11": ["11.4"],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
onnx_major_minor = ".".join(onnx_version.split(".")[:2])
|
|
160
|
+
cuda_major_minor = ".".join(cuda_version.split(".")[:2])
|
|
161
|
+
|
|
162
|
+
supported_cuda = compatibility_matrix.get(onnx_major_minor, [])
|
|
163
|
+
|
|
164
|
+
if supported_cuda and cuda_major_minor not in supported_cuda:
|
|
165
|
+
return (
|
|
166
|
+
f"ONNX Runtime {onnx_major_minor} supports CUDA {', '.join(supported_cuda)}, "
|
|
167
|
+
f"but system has CUDA {cuda_version}"
|
|
168
|
+
)
|
|
169
|
+
except (ValueError, IndexError):
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def check_onnx_opset_compatibility(
|
|
176
|
+
model_opset: int,
|
|
177
|
+
onnxruntime_version: Optional[str]
|
|
178
|
+
) -> Optional[str]:
|
|
179
|
+
"""Check if ONNX model opset is supported by ONNX Runtime version.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
model_opset: ONNX model opset version
|
|
183
|
+
onnxruntime_version: ONNX Runtime version
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Error message if incompatible, None if compatible
|
|
187
|
+
"""
|
|
188
|
+
if not onnxruntime_version:
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
# Maximum supported opset by ONNX Runtime version
|
|
192
|
+
max_opset_by_version = {
|
|
193
|
+
"1.16": 19,
|
|
194
|
+
"1.15": 18,
|
|
195
|
+
"1.14": 18,
|
|
196
|
+
"1.13": 17,
|
|
197
|
+
"1.12": 16,
|
|
198
|
+
"1.11": 15,
|
|
199
|
+
"1.10": 15,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
ort_major_minor = ".".join(onnxruntime_version.split(".")[:2])
|
|
204
|
+
max_opset = max_opset_by_version.get(ort_major_minor, 19)
|
|
205
|
+
|
|
206
|
+
if model_opset > max_opset:
|
|
207
|
+
return (
|
|
208
|
+
f"Model requires ONNX opset {model_opset}, "
|
|
209
|
+
f"but ONNX Runtime {ort_major_minor} only supports up to opset {max_opset}"
|
|
210
|
+
)
|
|
211
|
+
except (ValueError, IndexError):
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
return None
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""PyTorch framework deep inspection.
|
|
2
|
+
|
|
3
|
+
Validates PyTorch CUDA ABI compatibility, build configuration, and
|
|
4
|
+
version-specific requirements beyond generic binary checks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
from pybinaryguard.models.package import PackageBinaryInfo
|
|
13
|
+
from pybinaryguard.models.system import SystemProfile
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def detect_pytorch_build(package: PackageBinaryInfo) -> Dict[str, Optional[str]]:
|
|
17
|
+
"""Detect PyTorch build configuration from binary metadata.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
package: PackageBinaryInfo for torch package
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Dictionary with detected build info:
|
|
24
|
+
- cuda_version: CUDA version (e.g., "11.8", "12.1")
|
|
25
|
+
- cxx11_abi: C++11 ABI status ("enabled", "disabled", "unknown")
|
|
26
|
+
- cpu_only: Whether this is CPU-only build
|
|
27
|
+
- rocm_version: ROCm version if AMD build
|
|
28
|
+
"""
|
|
29
|
+
result: Dict[str, Optional[str]] = {
|
|
30
|
+
"cuda_version": None,
|
|
31
|
+
"cxx11_abi": None,
|
|
32
|
+
"cpu_only": False,
|
|
33
|
+
"rocm_version": None,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Check package CUDA version from metadata
|
|
37
|
+
if package.cuda_version:
|
|
38
|
+
result["cuda_version"] = package.cuda_version
|
|
39
|
+
|
|
40
|
+
# Check for CPU-only build from package name patterns
|
|
41
|
+
if any(so.path and "cpu" in so.path.lower() for so in package.shared_objects):
|
|
42
|
+
result["cpu_only"] = True
|
|
43
|
+
|
|
44
|
+
# Check for ROCm (AMD GPU) build
|
|
45
|
+
for so in package.shared_objects:
|
|
46
|
+
if so.path and "rocm" in so.path.lower():
|
|
47
|
+
# Try to extract ROCm version
|
|
48
|
+
match = re.search(r"rocm[_-]?(\d+\.\d+)", so.path.lower())
|
|
49
|
+
if match:
|
|
50
|
+
result["rocm_version"] = match.group(1)
|
|
51
|
+
|
|
52
|
+
# Detect C++11 ABI from GLIBCXX symbols
|
|
53
|
+
# PyTorch with new ABI uses GLIBCXX_3.4.21+, old ABI uses GLIBCXX_3.4.19
|
|
54
|
+
max_glibcxx = None
|
|
55
|
+
for so in package.shared_objects:
|
|
56
|
+
for symbol in so.symbols:
|
|
57
|
+
if symbol.startswith("GLIBCXX_3.4."):
|
|
58
|
+
version_str = symbol.replace("GLIBCXX_3.4.", "")
|
|
59
|
+
try:
|
|
60
|
+
version_num = int(version_str)
|
|
61
|
+
if max_glibcxx is None or version_num > max_glibcxx:
|
|
62
|
+
max_glibcxx = version_num
|
|
63
|
+
except ValueError:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
if max_glibcxx is not None:
|
|
67
|
+
# GLIBCXX >= 3.4.21 indicates C++11 ABI
|
|
68
|
+
result["cxx11_abi"] = "enabled" if max_glibcxx >= 21 else "disabled"
|
|
69
|
+
else:
|
|
70
|
+
result["cxx11_abi"] = "unknown"
|
|
71
|
+
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def check_pytorch_cuda_abi(
|
|
76
|
+
package: PackageBinaryInfo,
|
|
77
|
+
profile: SystemProfile
|
|
78
|
+
) -> List[Dict[str, str]]:
|
|
79
|
+
"""Check PyTorch CUDA ABI compatibility.
|
|
80
|
+
|
|
81
|
+
Validates:
|
|
82
|
+
- CUDA version matches system runtime
|
|
83
|
+
- C++11 ABI compatibility with system
|
|
84
|
+
- Compute capability support
|
|
85
|
+
- cuDNN version requirements
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
package: PackageBinaryInfo for torch package
|
|
89
|
+
profile: System profile
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
List of compatibility issues found
|
|
93
|
+
"""
|
|
94
|
+
issues: List[Dict[str, str]] = []
|
|
95
|
+
|
|
96
|
+
if package.name.lower() != "torch":
|
|
97
|
+
return issues
|
|
98
|
+
|
|
99
|
+
build_info = detect_pytorch_build(package)
|
|
100
|
+
|
|
101
|
+
# Check CUDA version compatibility
|
|
102
|
+
if build_info["cuda_version"] and profile.cuda_runtime_version:
|
|
103
|
+
pkg_cuda_major = int(build_info["cuda_version"].split(".")[0])
|
|
104
|
+
sys_cuda_major = int(profile.cuda_runtime_version.split(".")[0])
|
|
105
|
+
|
|
106
|
+
if pkg_cuda_major != sys_cuda_major:
|
|
107
|
+
issues.append({
|
|
108
|
+
"issue": "cuda_major_mismatch",
|
|
109
|
+
"severity": "critical",
|
|
110
|
+
"message": (
|
|
111
|
+
f"PyTorch built for CUDA {build_info['cuda_version']} but "
|
|
112
|
+
f"system has CUDA {profile.cuda_runtime_version}"
|
|
113
|
+
),
|
|
114
|
+
"recommendation": f"Install PyTorch built for CUDA {sys_cuda_major}.x",
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
# Check for CPU-only PyTorch on GPU system
|
|
118
|
+
if build_info["cpu_only"] and profile.cuda_runtime_version:
|
|
119
|
+
issues.append({
|
|
120
|
+
"issue": "cpu_build_on_gpu_system",
|
|
121
|
+
"severity": "warning",
|
|
122
|
+
"message": "CPU-only PyTorch detected on system with NVIDIA GPU",
|
|
123
|
+
"recommendation": "Install GPU-enabled PyTorch to utilize available hardware",
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
# Check compute capability for PyTorch 2.x
|
|
127
|
+
if package.version and package.version.startswith("2."):
|
|
128
|
+
if profile.cuda_compute_capability:
|
|
129
|
+
compute_major = int(profile.cuda_compute_capability.split(".")[0])
|
|
130
|
+
# PyTorch 2.x requires compute capability >= 3.5
|
|
131
|
+
if compute_major < 3 or (compute_major == 3 and int(profile.cuda_compute_capability.split(".")[1]) < 5):
|
|
132
|
+
issues.append({
|
|
133
|
+
"issue": "compute_capability_too_low",
|
|
134
|
+
"severity": "critical",
|
|
135
|
+
"message": (
|
|
136
|
+
f"PyTorch 2.x requires compute capability >= 3.5, "
|
|
137
|
+
f"but GPU has {profile.cuda_compute_capability}"
|
|
138
|
+
),
|
|
139
|
+
"recommendation": "Downgrade to PyTorch 1.x or upgrade GPU hardware",
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
# Check C++11 ABI compatibility
|
|
143
|
+
if build_info["cxx11_abi"] == "disabled":
|
|
144
|
+
issues.append({
|
|
145
|
+
"issue": "old_cxx11_abi",
|
|
146
|
+
"severity": "info",
|
|
147
|
+
"message": "PyTorch built with old C++11 ABI (pre-GCC 5.1)",
|
|
148
|
+
"recommendation": (
|
|
149
|
+
"Consider rebuilding extensions with _GLIBCXX_USE_CXX11_ABI=0 "
|
|
150
|
+
"if encountering symbol errors"
|
|
151
|
+
),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
return issues
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def check_pytorch_torchvision_compatibility(
|
|
158
|
+
torch_version: Optional[str],
|
|
159
|
+
torchvision_version: Optional[str]
|
|
160
|
+
) -> Optional[str]:
|
|
161
|
+
"""Check if torchvision version is compatible with torch version.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
torch_version: PyTorch version (e.g., "2.1.0")
|
|
165
|
+
torchvision_version: torchvision version (e.g., "0.16.0")
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Error message if incompatible, None if compatible
|
|
169
|
+
"""
|
|
170
|
+
if not torch_version or not torchvision_version:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
# Compatibility matrix (PyTorch -> torchvision major.minor)
|
|
174
|
+
compatibility_matrix = {
|
|
175
|
+
"2.2": "0.17",
|
|
176
|
+
"2.1": "0.16",
|
|
177
|
+
"2.0": "0.15",
|
|
178
|
+
"1.13": "0.14",
|
|
179
|
+
"1.12": "0.13",
|
|
180
|
+
"1.11": "0.12",
|
|
181
|
+
"1.10": "0.11",
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
torch_major_minor = ".".join(torch_version.split(".")[:2])
|
|
186
|
+
torchvision_major_minor = ".".join(torchvision_version.split(".")[:2])
|
|
187
|
+
|
|
188
|
+
expected_torchvision = compatibility_matrix.get(torch_major_minor)
|
|
189
|
+
if expected_torchvision and torchvision_major_minor != expected_torchvision:
|
|
190
|
+
return (
|
|
191
|
+
f"PyTorch {torch_major_minor} expects torchvision {expected_torchvision}.x, "
|
|
192
|
+
f"but found {torchvision_version}"
|
|
193
|
+
)
|
|
194
|
+
except (ValueError, IndexError):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def detect_pytorch_distributed_backend(package: PackageBinaryInfo) -> List[str]:
|
|
201
|
+
"""Detect available distributed training backends in PyTorch.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
package: PackageBinaryInfo for torch package
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
List of available backends: ["nccl", "gloo", "mpi"]
|
|
208
|
+
"""
|
|
209
|
+
backends = []
|
|
210
|
+
|
|
211
|
+
for so in package.shared_objects:
|
|
212
|
+
if not so.path:
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
path_lower = so.path.lower()
|
|
216
|
+
if "nccl" in path_lower:
|
|
217
|
+
backends.append("nccl")
|
|
218
|
+
elif "gloo" in path_lower:
|
|
219
|
+
backends.append("gloo")
|
|
220
|
+
elif "mpi" in path_lower:
|
|
221
|
+
backends.append("mpi")
|
|
222
|
+
|
|
223
|
+
return list(set(backends)) # Remove duplicates
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""TensorFlow framework deep inspection.
|
|
2
|
+
|
|
3
|
+
Validates TensorFlow CUDA compatibility, compute capability requirements,
|
|
4
|
+
and build configuration.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from typing import Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from pybinaryguard.models.package import PackageBinaryInfo
|
|
13
|
+
from pybinaryguard.models.system import SystemProfile
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def check_tensorflow_compute_capability(
|
|
17
|
+
package: PackageBinaryInfo,
|
|
18
|
+
profile: SystemProfile
|
|
19
|
+
) -> List[Dict[str, str]]:
|
|
20
|
+
"""Check TensorFlow compute capability requirements.
|
|
21
|
+
|
|
22
|
+
Validates:
|
|
23
|
+
- Minimum compute capability for TensorFlow version
|
|
24
|
+
- CUDA version compatibility
|
|
25
|
+
- cuDNN version requirements
|
|
26
|
+
- AVX instruction set availability
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
package: PackageBinaryInfo for tensorflow package
|
|
30
|
+
profile: System profile
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
List of compatibility issues found
|
|
34
|
+
"""
|
|
35
|
+
issues: List[Dict[str, str]] = []
|
|
36
|
+
|
|
37
|
+
if not package.name.lower().startswith("tensorflow"):
|
|
38
|
+
return issues
|
|
39
|
+
|
|
40
|
+
tf_version = package.version
|
|
41
|
+
if not tf_version:
|
|
42
|
+
return issues
|
|
43
|
+
|
|
44
|
+
# Check compute capability requirements
|
|
45
|
+
if profile.cuda_compute_capability:
|
|
46
|
+
min_compute = get_tensorflow_min_compute_capability(tf_version)
|
|
47
|
+
|
|
48
|
+
if min_compute:
|
|
49
|
+
try:
|
|
50
|
+
gpu_compute = float(profile.cuda_compute_capability)
|
|
51
|
+
if gpu_compute < min_compute:
|
|
52
|
+
issues.append({
|
|
53
|
+
"issue": "tensorflow_compute_capability_too_low",
|
|
54
|
+
"severity": "critical",
|
|
55
|
+
"message": (
|
|
56
|
+
f"TensorFlow {tf_version} requires compute capability >= {min_compute}, "
|
|
57
|
+
f"but GPU has {profile.cuda_compute_capability}"
|
|
58
|
+
),
|
|
59
|
+
"recommendation": f"Upgrade GPU or use TensorFlow version supporting compute {gpu_compute}",
|
|
60
|
+
})
|
|
61
|
+
except ValueError:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# Check CUDA version for GPU builds
|
|
65
|
+
if profile.cuda_runtime_version and not is_tensorflow_cpu_only(package):
|
|
66
|
+
cuda_compat = check_tensorflow_cuda_compatibility(tf_version, profile.cuda_runtime_version)
|
|
67
|
+
if cuda_compat:
|
|
68
|
+
issues.append({
|
|
69
|
+
"issue": "tensorflow_cuda_mismatch",
|
|
70
|
+
"severity": "critical",
|
|
71
|
+
"message": cuda_compat,
|
|
72
|
+
"recommendation": "Install TensorFlow version compatible with your CUDA version",
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
# Check AVX instruction set (TensorFlow 1.6+ requires AVX)
|
|
76
|
+
if not profile.cpu_flags or "avx" not in profile.cpu_flags.lower():
|
|
77
|
+
major_version = int(tf_version.split(".")[0])
|
|
78
|
+
if major_version >= 1:
|
|
79
|
+
issues.append({
|
|
80
|
+
"issue": "tensorflow_missing_avx",
|
|
81
|
+
"severity": "critical",
|
|
82
|
+
"message": "TensorFlow requires AVX instruction set, but CPU doesn't support it",
|
|
83
|
+
"recommendation": "Build TensorFlow from source without AVX or upgrade CPU",
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return issues
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_tensorflow_min_compute_capability(tf_version: str) -> Optional[float]:
|
|
90
|
+
"""Get minimum compute capability required for TensorFlow version.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
tf_version: TensorFlow version (e.g., "2.15.0")
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Minimum compute capability as float, or None if unknown
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
major = int(tf_version.split(".")[0])
|
|
100
|
+
minor = int(tf_version.split(".")[1])
|
|
101
|
+
|
|
102
|
+
# TensorFlow 2.11+ requires compute capability >= 3.5
|
|
103
|
+
if major >= 2 and minor >= 11:
|
|
104
|
+
return 3.5
|
|
105
|
+
|
|
106
|
+
# TensorFlow 2.x requires compute capability >= 3.0
|
|
107
|
+
if major >= 2:
|
|
108
|
+
return 3.0
|
|
109
|
+
|
|
110
|
+
# TensorFlow 1.x requires compute capability >= 3.0
|
|
111
|
+
return 3.0
|
|
112
|
+
except (ValueError, IndexError):
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def check_tensorflow_cuda_compatibility(
|
|
117
|
+
tf_version: str,
|
|
118
|
+
cuda_version: str
|
|
119
|
+
) -> Optional[str]:
|
|
120
|
+
"""Check TensorFlow and CUDA version compatibility.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
tf_version: TensorFlow version (e.g., "2.15.0")
|
|
124
|
+
cuda_version: CUDA version (e.g., "12.2")
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Error message if incompatible, None if compatible
|
|
128
|
+
"""
|
|
129
|
+
# Compatibility matrix (TensorFlow -> CUDA versions)
|
|
130
|
+
compatibility_matrix = {
|
|
131
|
+
"2.15": ["12.2"],
|
|
132
|
+
"2.14": ["11.8"],
|
|
133
|
+
"2.13": ["11.8"],
|
|
134
|
+
"2.12": ["11.8"],
|
|
135
|
+
"2.11": ["11.2"],
|
|
136
|
+
"2.10": ["11.2"],
|
|
137
|
+
"2.9": ["11.2"],
|
|
138
|
+
"2.8": ["11.2"],
|
|
139
|
+
"2.7": ["11.2"],
|
|
140
|
+
"2.6": ["11.2"],
|
|
141
|
+
"2.5": ["11.2"],
|
|
142
|
+
"2.4": ["11.0"],
|
|
143
|
+
"2.3": ["10.1"],
|
|
144
|
+
"2.2": ["10.1"],
|
|
145
|
+
"2.1": ["10.0"],
|
|
146
|
+
"2.0": ["10.0"],
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
tf_major_minor = ".".join(tf_version.split(".")[:2])
|
|
151
|
+
cuda_major_minor = ".".join(cuda_version.split(".")[:2])
|
|
152
|
+
|
|
153
|
+
supported_cuda = compatibility_matrix.get(tf_major_minor, [])
|
|
154
|
+
|
|
155
|
+
if supported_cuda and cuda_major_minor not in supported_cuda:
|
|
156
|
+
return (
|
|
157
|
+
f"TensorFlow {tf_major_minor} requires CUDA {', '.join(supported_cuda)}, "
|
|
158
|
+
f"but system has CUDA {cuda_version}"
|
|
159
|
+
)
|
|
160
|
+
except (ValueError, IndexError):
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def is_tensorflow_cpu_only(package: PackageBinaryInfo) -> bool:
|
|
167
|
+
"""Detect if TensorFlow is CPU-only build.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
package: PackageBinaryInfo for tensorflow package
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
True if CPU-only build
|
|
174
|
+
"""
|
|
175
|
+
# Check package name
|
|
176
|
+
if "cpu" in package.name.lower():
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
# Check for CUDA-related shared objects
|
|
180
|
+
for so in package.shared_objects:
|
|
181
|
+
if so.path and "cuda" in so.path.lower():
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def detect_tensorflow_gpu_support(package: PackageBinaryInfo) -> Dict[str, bool]:
|
|
188
|
+
"""Detect TensorFlow GPU support features.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
package: PackageBinaryInfo for tensorflow package
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Dictionary with GPU support flags
|
|
195
|
+
"""
|
|
196
|
+
support = {
|
|
197
|
+
"cuda": False,
|
|
198
|
+
"tensorrt": False,
|
|
199
|
+
"rocm": False,
|
|
200
|
+
"xla": False,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for so in package.shared_objects:
|
|
204
|
+
if not so.path:
|
|
205
|
+
continue
|
|
206
|
+
|
|
207
|
+
path_lower = so.path.lower()
|
|
208
|
+
|
|
209
|
+
if "cuda" in path_lower:
|
|
210
|
+
support["cuda"] = True
|
|
211
|
+
if "tensorrt" in path_lower:
|
|
212
|
+
support["tensorrt"] = True
|
|
213
|
+
if "rocm" in path_lower:
|
|
214
|
+
support["rocm"] = True
|
|
215
|
+
if "xla" in path_lower:
|
|
216
|
+
support["xla"] = True
|
|
217
|
+
|
|
218
|
+
return support
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def check_tensorflow_lite_compatibility(
|
|
222
|
+
tflite_model_schema: int,
|
|
223
|
+
tflite_runtime_version: Optional[str]
|
|
224
|
+
) -> Optional[str]:
|
|
225
|
+
"""Check TFLite model schema compatibility with runtime.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
tflite_model_schema: TFLite model schema version
|
|
229
|
+
tflite_runtime_version: TFLite runtime version
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Error message if incompatible, None if compatible
|
|
233
|
+
"""
|
|
234
|
+
if not tflite_runtime_version:
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
# TFLite schema version by TensorFlow version
|
|
238
|
+
schema_by_version = {
|
|
239
|
+
"2.15": 3,
|
|
240
|
+
"2.14": 3,
|
|
241
|
+
"2.13": 3,
|
|
242
|
+
"2.12": 3,
|
|
243
|
+
"2.11": 3,
|
|
244
|
+
"2.10": 3,
|
|
245
|
+
"2.9": 3,
|
|
246
|
+
"2.8": 3,
|
|
247
|
+
"2.7": 3,
|
|
248
|
+
"2.6": 3,
|
|
249
|
+
"2.5": 3,
|
|
250
|
+
"2.4": 3,
|
|
251
|
+
"2.3": 3,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try:
|
|
255
|
+
tflite_major_minor = ".".join(tflite_runtime_version.split(".")[:2])
|
|
256
|
+
max_schema = schema_by_version.get(tflite_major_minor, 3)
|
|
257
|
+
|
|
258
|
+
if tflite_model_schema > max_schema:
|
|
259
|
+
return (
|
|
260
|
+
f"TFLite model schema version {tflite_model_schema} is newer than "
|
|
261
|
+
f"runtime version {tflite_runtime_version} (max schema: {max_schema})"
|
|
262
|
+
)
|
|
263
|
+
except (ValueError, IndexError):
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
return None
|