Skip to content

Commit 4cd2dac

Browse files
AllanZynepbalceraarongreig
authored
[DeviceSanitizer] Memory overhead statistics (#1869)
Statistic memory overhead (usm redzone + shadow memory) on each context, enabled by exporting asan flag UR_LAYER_ASAN_OPTIONS=print_stats:1. Addtionally, move "AsanOptions" under "SanitizerInterceptor". Co-authored-by: Piotr Balcer <[email protected]> Co-authored-by: aarongreig <[email protected]>
1 parent 32e8de4 commit 4cd2dac

File tree

10 files changed

+439
-184
lines changed

10 files changed

+439
-184
lines changed

source/loader/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,16 @@ if(UR_ENABLE_SANITIZER)
144144
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_interceptor.cpp
145145
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_interceptor.hpp
146146
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_libdevice.hpp
147+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_options.cpp
148+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_options.hpp
147149
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_quarantine.cpp
148150
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_quarantine.hpp
149151
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_report.cpp
150152
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_report.hpp
151153
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.cpp
152154
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_shadow_setup.hpp
155+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_statistics.cpp
156+
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_statistics.hpp
153157
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.cpp
154158
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/asan_validator.hpp
155159
${CMAKE_CURRENT_SOURCE_DIR}/layers/sanitizer/common.hpp

source/loader/layers/sanitizer/asan_allocator.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct AllocInfo {
4545
StackTrace ReleaseStack;
4646

4747
void print();
48+
size_t getRedzoneSize() { return AllocSize - (UserEnd - UserBegin); }
4849
};
4950

5051
using AllocationMap = std::map<uptr, std::shared_ptr<AllocInfo>>;

source/loader/layers/sanitizer/asan_interceptor.cpp

Lines changed: 73 additions & 38 deletions
Large diffs are not rendered by default.

source/loader/layers/sanitizer/asan_interceptor.hpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "asan_allocator.hpp"
1616
#include "asan_buffer.hpp"
1717
#include "asan_libdevice.hpp"
18+
#include "asan_options.hpp"
19+
#include "asan_statistics.hpp"
1820
#include "common.hpp"
1921
#include "ur_sanitizer_layer.hpp"
2022

@@ -111,6 +113,8 @@ struct ContextInfo {
111113
std::vector<ur_device_handle_t> DeviceList;
112114
std::unordered_map<ur_device_handle_t, AllocInfoList> AllocInfosMap;
113115

116+
AsanStatsWrapper Stats;
117+
114118
explicit ContextInfo(ur_context_handle_t Context) : Handle(Context) {
115119
[[maybe_unused]] auto Result =
116120
getContext()->urDdiTable.Context.pfnRetain(Context);
@@ -163,7 +167,7 @@ struct DeviceGlobalInfo {
163167

164168
class SanitizerInterceptor {
165169
public:
166-
explicit SanitizerInterceptor(logger::Logger &logger);
170+
explicit SanitizerInterceptor();
167171

168172
~SanitizerInterceptor();
169173

@@ -233,17 +237,19 @@ class SanitizerInterceptor {
233237
return m_KernelMap[Kernel];
234238
}
235239

240+
const AsanOptions &getOptions() { return m_Options; }
241+
236242
private:
237243
ur_result_t updateShadowMemory(std::shared_ptr<ContextInfo> &ContextInfo,
238244
std::shared_ptr<DeviceInfo> &DeviceInfo,
239245
ur_queue_handle_t Queue);
240-
ur_result_t enqueueAllocInfo(ur_context_handle_t Context,
246+
ur_result_t enqueueAllocInfo(std::shared_ptr<ContextInfo> &ContextInfo,
241247
std::shared_ptr<DeviceInfo> &DeviceInfo,
242248
ur_queue_handle_t Queue,
243249
std::shared_ptr<AllocInfo> &AI);
244250

245251
/// Initialize Global Variables & Kernel Name at first Launch
246-
ur_result_t prepareLaunch(ur_context_handle_t Context,
252+
ur_result_t prepareLaunch(std::shared_ptr<ContextInfo> &ContextInfo,
247253
std::shared_ptr<DeviceInfo> &DeviceInfo,
248254
ur_queue_handle_t Queue,
249255
ur_kernel_handle_t Kernel,
@@ -273,7 +279,8 @@ class SanitizerInterceptor {
273279
ur_shared_mutex m_AllocationMapMutex;
274280

275281
std::unique_ptr<Quarantine> m_Quarantine;
276-
logger::Logger &logger;
282+
283+
AsanOptions m_Options;
277284

278285
std::unordered_set<ur_adapter_handle_t> m_Adapters;
279286
ur_shared_mutex m_AdaptersMutex;
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Part of the Unified-Runtime Project, under the Apache License v2.0 with LLVM Exceptions.
6+
* See LICENSE.TXT
7+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
*
9+
* @file asan_options.cpp
10+
*
11+
*/
12+
13+
#include "asan_options.hpp"
14+
#include "ur_sanitizer_layer.hpp"
15+
16+
#include <algorithm>
17+
#include <cstring>
18+
#include <stdexcept>
19+
20+
namespace ur_sanitizer_layer {
21+
22+
AsanOptions::AsanOptions() {
23+
std::optional<EnvVarMap> OptionsEnvMap;
24+
try {
25+
OptionsEnvMap = getenv_to_map("UR_LAYER_ASAN_OPTIONS");
26+
} catch (const std::invalid_argument &e) {
27+
std::stringstream SS;
28+
SS << "<SANITIZER>[ERROR]: ";
29+
SS << e.what();
30+
getContext()->logger.always(SS.str().c_str());
31+
die("Sanitizer failed to parse options.\n");
32+
}
33+
34+
if (!OptionsEnvMap.has_value()) {
35+
return;
36+
}
37+
38+
const char *TrueStrings[] = {"1", "true"};
39+
const char *FalseStrings[] = {"0", "false"};
40+
41+
auto InplaceToLower = [](std::string &S) {
42+
std::transform(S.begin(), S.end(), S.begin(),
43+
[](unsigned char C) { return std::tolower(C); });
44+
};
45+
auto IsTrue = [&](const std::string &S) {
46+
return std::any_of(std::begin(TrueStrings), std::end(TrueStrings),
47+
[&](const char *CS) { return S == CS; });
48+
};
49+
auto IsFalse = [&](const std::string &S) {
50+
return std::any_of(std::begin(FalseStrings), std::end(FalseStrings),
51+
[&](const char *CS) { return S == CS; });
52+
};
53+
54+
auto SetBoolOption = [&](const std::string &Name, bool &Opt) {
55+
auto KV = OptionsEnvMap->find(Name);
56+
if (KV != OptionsEnvMap->end()) {
57+
auto Value = KV->second.front();
58+
InplaceToLower(Value);
59+
if (IsTrue(Value)) {
60+
Opt = true;
61+
} else if (IsFalse(Value)) {
62+
Opt = false;
63+
} else {
64+
std::stringstream SS;
65+
SS << "\"" << Name << "\" is set to \"" << Value
66+
<< "\", which is not an valid setting. ";
67+
SS << "Acceptable input are: for enable, use:";
68+
for (auto &S : TrueStrings) {
69+
SS << " \"" << S << "\"";
70+
}
71+
SS << "; ";
72+
SS << "for disable, use:";
73+
for (auto &S : FalseStrings) {
74+
SS << " \"" << S << "\"";
75+
}
76+
SS << ".";
77+
getContext()->logger.error(SS.str().c_str());
78+
die("Sanitizer failed to parse options.\n");
79+
}
80+
}
81+
};
82+
83+
SetBoolOption("debug", Debug);
84+
SetBoolOption("detect_kernel_arguments", DetectKernelArguments);
85+
SetBoolOption("detect_locals", DetectLocals);
86+
SetBoolOption("detect_privates", DetectPrivates);
87+
SetBoolOption("print_stats", PrintStats);
88+
89+
auto KV = OptionsEnvMap->find("quarantine_size_mb");
90+
if (KV != OptionsEnvMap->end()) {
91+
const auto &Value = KV->second.front();
92+
try {
93+
auto temp_long = std::stoul(Value);
94+
if (temp_long > UINT32_MAX) {
95+
throw std::out_of_range("");
96+
}
97+
MaxQuarantineSizeMB = temp_long;
98+
} catch (...) {
99+
getContext()->logger.error("\"quarantine_size_mb\" should be "
100+
"an integer in range[0, {}].",
101+
UINT32_MAX);
102+
die("Sanitizer failed to parse options.\n");
103+
}
104+
}
105+
106+
KV = OptionsEnvMap->find("redzone");
107+
if (KV != OptionsEnvMap->end()) {
108+
const auto &Value = KV->second.front();
109+
try {
110+
MinRZSize = std::stoul(Value);
111+
if (MinRZSize < 16) {
112+
MinRZSize = 16;
113+
getContext()->logger.warning("Trying to set redzone size to a "
114+
"value less than 16 is ignored.");
115+
}
116+
} catch (...) {
117+
getContext()->logger.error(
118+
"\"redzone\" should be an integer in range[0, 16].");
119+
die("Sanitizer failed to parse options.\n");
120+
}
121+
}
122+
123+
KV = OptionsEnvMap->find("max_redzone");
124+
if (KV != OptionsEnvMap->end()) {
125+
const auto &Value = KV->second.front();
126+
try {
127+
MaxRZSize = std::stoul(Value);
128+
if (MaxRZSize > 2048) {
129+
MaxRZSize = 2048;
130+
getContext()->logger.warning(
131+
"Trying to set max redzone size to a "
132+
"value greater than 2048 is ignored.");
133+
}
134+
} catch (...) {
135+
getContext()->logger.error(
136+
"\"max_redzone\" should be an integer in range[0, 2048].");
137+
die("Sanitizer failed to parse options.\n");
138+
}
139+
}
140+
}
141+
142+
} // namespace ur_sanitizer_layer

source/loader/layers/sanitizer/asan_options.hpp

Lines changed: 3 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -12,156 +12,21 @@
1212

1313
#pragma once
1414

15-
#include "common/ur_util.hpp"
16-
#include "ur/ur.hpp"
17-
#include "ur_sanitizer_layer.hpp"
18-
19-
#include <algorithm>
20-
#include <cstring>
21-
#include <stdexcept>
15+
#include "common.hpp"
2216

2317
namespace ur_sanitizer_layer {
2418

2519
struct AsanOptions {
26-
public:
27-
AsanOptions(AsanOptions &other) = delete;
28-
void operator=(const AsanOptions &) = delete;
29-
30-
static AsanOptions &getInstance(logger::Logger &logger) {
31-
static AsanOptions instance(logger);
32-
return instance;
33-
}
34-
3520
bool Debug = false;
3621
uint64_t MinRZSize = 16;
3722
uint64_t MaxRZSize = 2048;
3823
uint32_t MaxQuarantineSizeMB = 0;
3924
bool DetectLocals = true;
4025
bool DetectPrivates = true;
26+
bool PrintStats = false;
4127
bool DetectKernelArguments = true;
4228

43-
private:
44-
AsanOptions(logger::Logger &logger) {
45-
std::optional<EnvVarMap> OptionsEnvMap;
46-
try {
47-
OptionsEnvMap = getenv_to_map("UR_LAYER_ASAN_OPTIONS");
48-
} catch (const std::invalid_argument &e) {
49-
std::stringstream SS;
50-
SS << "<SANITIZER>[ERROR]: ";
51-
SS << e.what();
52-
logger.always(SS.str().c_str());
53-
die("Sanitizer failed to parse options.\n");
54-
}
55-
56-
if (!OptionsEnvMap.has_value()) {
57-
return;
58-
}
59-
60-
const char *TrueStrings[] = {"1", "true"};
61-
const char *FalseStrings[] = {"0", "false"};
62-
63-
auto InplaceToLower = [](std::string &S) {
64-
std::transform(S.begin(), S.end(), S.begin(),
65-
[](unsigned char C) { return std::tolower(C); });
66-
};
67-
auto IsTrue = [&](const std::string &S) {
68-
return std::any_of(std::begin(TrueStrings), std::end(TrueStrings),
69-
[&](const char *CS) { return S == CS; });
70-
};
71-
auto IsFalse = [&](const std::string &S) {
72-
return std::any_of(std::begin(FalseStrings), std::end(FalseStrings),
73-
[&](const char *CS) { return S == CS; });
74-
};
75-
76-
auto SetBoolOption = [&](const std::string &Name, bool &Opt) {
77-
auto KV = OptionsEnvMap->find(Name);
78-
if (KV != OptionsEnvMap->end()) {
79-
auto Value = KV->second.front();
80-
InplaceToLower(Value);
81-
if (IsTrue(Value)) {
82-
Opt = true;
83-
} else if (IsFalse(Value)) {
84-
Opt = false;
85-
} else {
86-
std::stringstream SS;
87-
SS << "\"" << Name << "\" is set to \"" << Value
88-
<< "\", which is not an valid setting. ";
89-
SS << "Acceptable input are: for enable, use:";
90-
for (auto &S : TrueStrings) {
91-
SS << " \"" << S << "\"";
92-
}
93-
SS << "; ";
94-
SS << "for disable, use:";
95-
for (auto &S : FalseStrings) {
96-
SS << " \"" << S << "\"";
97-
}
98-
SS << ".";
99-
logger.error(SS.str().c_str());
100-
die("Sanitizer failed to parse options.\n");
101-
}
102-
}
103-
};
104-
105-
SetBoolOption("debug", Debug);
106-
SetBoolOption("detect_locals", DetectLocals);
107-
SetBoolOption("detect_privates", DetectPrivates);
108-
SetBoolOption("detect_kernel_arguments", DetectKernelArguments);
109-
110-
auto KV = OptionsEnvMap->find("quarantine_size_mb");
111-
if (KV != OptionsEnvMap->end()) {
112-
const auto &Value = KV->second.front();
113-
try {
114-
auto temp_long = std::stoul(Value);
115-
if (temp_long > UINT32_MAX) {
116-
throw std::out_of_range("");
117-
}
118-
MaxQuarantineSizeMB = temp_long;
119-
} catch (...) {
120-
logger.error("\"quarantine_size_mb\" should be "
121-
"an integer in range[0, {}].",
122-
UINT32_MAX);
123-
die("Sanitizer failed to parse options.\n");
124-
}
125-
}
126-
127-
KV = OptionsEnvMap->find("redzone");
128-
if (KV != OptionsEnvMap->end()) {
129-
const auto &Value = KV->second.front();
130-
try {
131-
MinRZSize = std::stoul(Value);
132-
if (MinRZSize < 16) {
133-
MinRZSize = 16;
134-
logger.warning("Trying to set redzone size to a "
135-
"value less than 16 is ignored.");
136-
}
137-
} catch (...) {
138-
logger.error(
139-
"\"redzone\" should be an integer in range[0, 16].");
140-
die("Sanitizer failed to parse options.\n");
141-
}
142-
}
143-
144-
KV = OptionsEnvMap->find("max_redzone");
145-
if (KV != OptionsEnvMap->end()) {
146-
const auto &Value = KV->second.front();
147-
try {
148-
MaxRZSize = std::stoul(Value);
149-
if (MaxRZSize > 2048) {
150-
MaxRZSize = 2048;
151-
logger.warning("Trying to set max redzone size to a "
152-
"value greater than 2048 is ignored.");
153-
}
154-
} catch (...) {
155-
logger.error(
156-
"\"max_redzone\" should be an integer in range[0, 2048].");
157-
die("Sanitizer failed to parse options.\n");
158-
}
159-
}
160-
}
29+
explicit AsanOptions();
16130
};
16231

163-
inline const AsanOptions &Options(logger::Logger &logger) {
164-
return AsanOptions::getInstance(logger);
165-
}
166-
16732
} // namespace ur_sanitizer_layer

0 commit comments

Comments
 (0)