Skip to content

Commit 91e47a5

Browse files
committed
Implement basic leak checker in validation layer
It is based on the ZeCallCount from UR. There are 2 main reasons for moving the leak checker to level-zero: 1. Some calls to L0 are done through UMF (not by UR directly) which means those calls would not be checked. 2. If an application using UR calls L0 directly, those calls will also not be checked. This happens for example in SYCL tests, where special cases need to be made for direct L0 calls. Signed-off-by: Igor Chorazewicz <igor.chorazewicz@intel.com>
1 parent d7a44e0 commit 91e47a5

File tree

4 files changed

+393
-0
lines changed

4 files changed

+393
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
add_subdirectory(basic_leak)
12
add_subdirectory(events_checker)
23
add_subdirectory(parameter_validation)
34
add_subdirectory(template)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
target_sources(${TARGET_NAME}
2+
PRIVATE
3+
${CMAKE_CURRENT_LIST_DIR}/zel_basic_leak_checker.h
4+
${CMAKE_CURRENT_LIST_DIR}/zel_basic_leak_checker.cpp
5+
)
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/*
2+
* Copyright (C) 2024 Intel Corporation
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* @file zel_basic_leak_checker.cpp
7+
*
8+
*/
9+
#include "zel_basic_leak_checker.h"
10+
11+
#include <cassert>
12+
#include <iostream>
13+
#include <sstream>
14+
#include <iomanip>
15+
16+
namespace validation_layer
17+
{
18+
class basic_leakChecker basic_leak_checker;
19+
20+
basic_leakChecker::basic_leakChecker() {
21+
enablebasic_leak = getenv_tobool( "ZEL_ENABLE_BASIC_LEAK_CHECKER" );
22+
if(enablebasic_leak) {
23+
basic_leakChecker::ZEbasic_leakChecker *zeChecker = new basic_leakChecker::ZEbasic_leakChecker;
24+
basic_leakChecker::ZESbasic_leakChecker *zesChecker = new basic_leakChecker::ZESbasic_leakChecker;
25+
basic_leakChecker::ZETbasic_leakChecker *zetChecker = new basic_leakChecker::ZETbasic_leakChecker;
26+
basic_leak_checker.zeValidation = zeChecker;
27+
basic_leak_checker.zetValidation = zetChecker;
28+
basic_leak_checker.zesValidation = zesChecker;
29+
validation_layer::context.validationHandlers.push_back(&basic_leak_checker);
30+
}
31+
}
32+
33+
basic_leakChecker::~basic_leakChecker() {
34+
if(enablebasic_leak) {
35+
delete basic_leak_checker.zeValidation;
36+
delete basic_leak_checker.zetValidation;
37+
delete basic_leak_checker.zesValidation;
38+
}
39+
}
40+
41+
// The format of this table is such that each row accounts for a
42+
// specific type of objects, and all elements in the raw except the last
43+
// one are allocating objects of that type, while the last element is known
44+
// to deallocate objects of that type.
45+
//
46+
static std::vector<std::vector<std::string>> createDestroySet() {
47+
return {
48+
{"zeContextCreate", "zeContextDestroy"},
49+
{"zeCommandQueueCreate", "zeCommandQueueDestroy"},
50+
{"zeModuleCreate", "zeModuleDestroy"},
51+
{"zeKernelCreate", "zeKernelDestroy"},
52+
{"zeEventPoolCreate", "zeEventPoolDestroy"},
53+
{"zeCommandListCreateImmediate", "zeCommandListCreate", "zeCommandListDestroy"},
54+
{"zeEventCreate", "zeEventDestroy"},
55+
{"zeFenceCreate", "zeFenceDestroy"},
56+
{"zeImageCreate", "zeImageDestroy"},
57+
{"zeSamplerCreate", "zeSamplerDestroy"},
58+
{"zeMemAllocDevice", "zeMemAllocHost", "zeMemAllocShared", "zeMemFree"}};
59+
}
60+
61+
basic_leakChecker::ZEbasic_leakChecker::ZEbasic_leakChecker() {
62+
// initialize counts for all functions that should be tracked
63+
for (const auto &row : createDestroySet()) {
64+
for (const auto &name : row) {
65+
counts[name] = 0;
66+
}
67+
}
68+
}
69+
70+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeContextCreateEpilogue(ze_driver_handle_t, const ze_context_desc_t *, ze_context_handle_t*, ze_result_t result) {
71+
if (result == ZE_RESULT_SUCCESS) {
72+
countFunctionCall("zeContextCreate");
73+
}
74+
return result;
75+
}
76+
77+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeContextDestroyEpilogue(ze_context_handle_t, ze_result_t result) {
78+
if (result == ZE_RESULT_SUCCESS) {
79+
countFunctionCall("zeContextDestroy");
80+
}
81+
return result;
82+
}
83+
84+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeCommandQueueCreateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_command_queue_desc_t *, ze_command_queue_handle_t *, ze_result_t result) {
85+
if (result == ZE_RESULT_SUCCESS) {
86+
countFunctionCall("zeCommandQueueCreate");
87+
}
88+
return result;
89+
}
90+
91+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeCommandQueueDestroyEpilogue(ze_command_queue_handle_t, ze_result_t result) {
92+
if (result == ZE_RESULT_SUCCESS) {
93+
countFunctionCall("zeCommandQueueDestroy");
94+
}
95+
return result;
96+
}
97+
98+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeModuleCreateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_module_desc_t*, ze_module_handle_t*, ze_module_build_log_handle_t*, ze_result_t result) {
99+
if (result == ZE_RESULT_SUCCESS) {
100+
countFunctionCall("zeModuleCreate");
101+
}
102+
return result;
103+
}
104+
105+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeModuleDestroyEpilogue(ze_module_handle_t, ze_result_t result) {
106+
if (result == ZE_RESULT_SUCCESS) {
107+
countFunctionCall("zeModuleDestroy");
108+
}
109+
return result;
110+
}
111+
112+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeKernelCreateEpilogue(ze_module_handle_t, const ze_kernel_desc_t*, ze_kernel_handle_t*, ze_result_t result) {
113+
if (result == ZE_RESULT_SUCCESS) {
114+
countFunctionCall("zeKernelCreate");
115+
}
116+
return result;
117+
}
118+
119+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeKernelDestroyEpilogue(ze_kernel_handle_t, ze_result_t result) {
120+
if (result == ZE_RESULT_SUCCESS) {
121+
countFunctionCall("zeKernelDestroy");
122+
}
123+
return result;
124+
}
125+
126+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeEventPoolCreateEpilogue(ze_context_handle_t, const ze_event_pool_desc_t*, uint32_t, ze_device_handle_t*, ze_event_pool_handle_t*, ze_result_t result) {
127+
if (result == ZE_RESULT_SUCCESS) {
128+
countFunctionCall("zeEventPoolCreate");
129+
}
130+
return result;
131+
}
132+
133+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeEventPoolDestroyEpilogue(ze_event_pool_handle_t, ze_result_t result) {
134+
if (result == ZE_RESULT_SUCCESS) {
135+
countFunctionCall("zeEventPoolDestroy");
136+
}
137+
return result;
138+
}
139+
140+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeCommandListCreateImmediateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_command_queue_desc_t*, ze_command_list_handle_t*, ze_result_t result) {
141+
if (result == ZE_RESULT_SUCCESS) {
142+
countFunctionCall("zeCommandListCreateImmediate");
143+
}
144+
return result;
145+
}
146+
147+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeCommandListCreateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_command_list_desc_t*, ze_command_list_handle_t*, ze_result_t result) {
148+
if (result == ZE_RESULT_SUCCESS) {
149+
countFunctionCall("zeCommandListCreate");
150+
}
151+
return result;
152+
}
153+
154+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeCommandListDestroyEpilogue(ze_command_list_handle_t, ze_result_t result) {
155+
if (result == ZE_RESULT_SUCCESS) {
156+
countFunctionCall("zeCommandListDestroy");
157+
}
158+
return result;
159+
}
160+
161+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeEventCreateEpilogue(ze_event_pool_handle_t, const ze_event_desc_t *, ze_event_handle_t *, ze_result_t result) {
162+
if (result == ZE_RESULT_SUCCESS) {
163+
countFunctionCall("zeEventCreate");
164+
}
165+
return result;
166+
}
167+
168+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeEventDestroyEpilogue(ze_event_handle_t, ze_result_t result) {
169+
if (result == ZE_RESULT_SUCCESS) {
170+
countFunctionCall("zeEventDestroy");
171+
}
172+
return result;
173+
}
174+
175+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeFenceCreateEpilogue(ze_command_queue_handle_t, const ze_fence_desc_t *, ze_fence_handle_t*, ze_result_t result) {
176+
if (result == ZE_RESULT_SUCCESS) {
177+
countFunctionCall("zeFenceCreate");
178+
}
179+
return result;
180+
}
181+
182+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeFenceDestroyEpilogue(ze_fence_handle_t, ze_result_t result) {
183+
if (result == ZE_RESULT_SUCCESS) {
184+
countFunctionCall("zeFenceDestroy");
185+
}
186+
return result;
187+
}
188+
189+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeImageCreateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_image_desc_t*, ze_image_handle_t*, ze_result_t result) {
190+
if (result == ZE_RESULT_SUCCESS) {
191+
countFunctionCall("zeImageCreate");
192+
}
193+
return result;
194+
}
195+
196+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeImageDestroyEpilogue(ze_image_handle_t, ze_result_t result) {
197+
if (result == ZE_RESULT_SUCCESS) {
198+
countFunctionCall("zeImageDestroy");
199+
}
200+
return result;
201+
}
202+
203+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeSamplerCreateEpilogue(ze_context_handle_t, ze_device_handle_t, const ze_sampler_desc_t*, ze_sampler_handle_t*, ze_result_t result) {
204+
if (result == ZE_RESULT_SUCCESS) {
205+
countFunctionCall("zeSamplerCreate");
206+
}
207+
return result;
208+
}
209+
210+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeSamplerDestroyEpilogue(ze_sampler_handle_t, ze_result_t result) {
211+
if (result == ZE_RESULT_SUCCESS) {
212+
countFunctionCall("zeSamplerDestroy");
213+
}
214+
return result;
215+
}
216+
217+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeMemAllocDeviceEpilogue(ze_context_handle_t, const ze_device_mem_alloc_desc_t *, size_t, size_t, ze_device_handle_t, void **, ze_result_t result) {
218+
if (result == ZE_RESULT_SUCCESS) {
219+
countFunctionCall("zeMemAllocDevice");
220+
}
221+
return result;
222+
}
223+
224+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeMemAllocHostEpilogue(ze_context_handle_t, const ze_host_mem_alloc_desc_t *, size_t, size_t, void **, ze_result_t result) {
225+
if (result == ZE_RESULT_SUCCESS) {
226+
countFunctionCall("zeMemAllocHost");
227+
}
228+
return result;
229+
}
230+
231+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeMemAllocSharedEpilogue(ze_context_handle_t, const ze_device_mem_alloc_desc_t *, const ze_host_mem_alloc_desc_t *, size_t, size_t, ze_device_handle_t, void **, ze_result_t result) {
232+
if (result == ZE_RESULT_SUCCESS) {
233+
countFunctionCall("zeMemAllocShared");
234+
}
235+
return result;
236+
}
237+
238+
ze_result_t basic_leakChecker::ZEbasic_leakChecker::zeMemFreeEpilogue(ze_context_handle_t, void *, ze_result_t result) {
239+
if (result == ZE_RESULT_SUCCESS) {
240+
countFunctionCall("zeMemFree");
241+
}
242+
return result;
243+
}
244+
245+
void basic_leakChecker::ZEbasic_leakChecker::countFunctionCall(const std::string &functionName)
246+
{
247+
auto it = counts.find(functionName);
248+
249+
// make sure there is no insertion happening during program exeuction
250+
// as inserting to the map is not thread safe
251+
if (it == counts.end()) {
252+
return;
253+
}
254+
255+
it->second.fetch_add(1, std::memory_order_relaxed);
256+
}
257+
258+
basic_leakChecker::ZEbasic_leakChecker::~ZEbasic_leakChecker() {
259+
std::cerr << "Check balance of create/destroy calls\n";
260+
std::cerr << "----------------------------------------------------------\n";
261+
std::stringstream ss;
262+
for (const auto &Row : createDestroySet()) {
263+
int diff = 0;
264+
for (auto I = Row.begin(); I != Row.end();) {
265+
const char *ZeName = (*I).c_str();
266+
const auto &ZeCount = (counts)[*I];
267+
268+
bool First = (I == Row.begin());
269+
bool Last = (++I == Row.end());
270+
271+
if (Last) {
272+
ss << " \\--->";
273+
diff -= ZeCount;
274+
} else {
275+
diff += ZeCount;
276+
if (!First) {
277+
ss << " | ";
278+
std::cerr << ss.str() << "\n";
279+
ss.str("");
280+
ss.clear();
281+
}
282+
}
283+
ss << std::setw(30) << std::right << ZeName;
284+
ss << " = ";
285+
ss << std::setw(5) << std::left << ZeCount;
286+
}
287+
288+
if (diff) {
289+
ss << " ---> LEAK = " << diff;
290+
}
291+
292+
std::cerr << ss.str() << '\n';
293+
ss.str("");
294+
ss.clear();
295+
}
296+
}
297+
}

0 commit comments

Comments
 (0)