1 | // |
2 | // Copyright (c) Microsoft. All rights reserved. |
3 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. |
4 | // |
5 | |
6 | //---------------------------------------------------------- |
7 | // SPMIUtil.cpp - General utility functions |
8 | //---------------------------------------------------------- |
9 | |
10 | #include "standardpch.h" |
11 | #include "logging.h" |
12 | #include "spmiutil.h" |
13 | |
14 | static bool breakOnDebugBreakorAV = false; |
15 | |
16 | bool BreakOnDebugBreakorAV() |
17 | { |
18 | return breakOnDebugBreakorAV; |
19 | } |
20 | |
21 | void SetBreakOnDebugBreakOrAV(bool value) |
22 | { |
23 | breakOnDebugBreakorAV = value; |
24 | } |
25 | |
26 | void DebugBreakorAV(int val) |
27 | { |
28 | if (IsDebuggerPresent()) |
29 | { |
30 | if (val == 0) |
31 | __debugbreak(); |
32 | if (BreakOnDebugBreakorAV()) |
33 | __debugbreak(); |
34 | } |
35 | |
36 | int exception_code = EXCEPTIONCODE_DebugBreakorAV + val; |
37 | // assert((EXCEPTIONCODE_DebugBreakorAV <= exception_code) && (exception_code < EXCEPTIONCODE_DebugBreakorAV_MAX)) |
38 | LogException(exception_code, "DebugBreak or AV Exception %d" , val); |
39 | } |
40 | |
41 | char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* defaultValue) |
42 | { |
43 | char* retString = nullptr; |
44 | |
45 | // Figure out how much space we need to allocate |
46 | DWORD dwRetVal = ::GetEnvironmentVariableA(envVarName, nullptr, 0); |
47 | if (dwRetVal != 0) |
48 | { |
49 | retString = new char[dwRetVal]; |
50 | dwRetVal = ::GetEnvironmentVariableA(envVarName, retString, dwRetVal); |
51 | } |
52 | else |
53 | { |
54 | if (defaultValue != nullptr) |
55 | { |
56 | dwRetVal = (DWORD)strlen(defaultValue) + 1; // add one for null terminator |
57 | retString = new char[dwRetVal]; |
58 | memcpy_s(retString, dwRetVal, defaultValue, dwRetVal); |
59 | } |
60 | } |
61 | |
62 | return retString; |
63 | } |
64 | |
65 | WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue) |
66 | { |
67 | WCHAR* retString = nullptr; |
68 | |
69 | // Figure out how much space we need to allocate |
70 | DWORD dwRetVal = ::GetEnvironmentVariableW(envVarName, nullptr, 0); |
71 | if (dwRetVal != 0) |
72 | { |
73 | retString = new WCHAR[dwRetVal]; |
74 | dwRetVal = ::GetEnvironmentVariableW(envVarName, retString, dwRetVal); |
75 | } |
76 | else |
77 | { |
78 | if (defaultValue != nullptr) |
79 | { |
80 | dwRetVal = (DWORD)wcslen(defaultValue) + 1; // add one for null terminator |
81 | retString = new WCHAR[dwRetVal]; |
82 | memcpy_s(retString, dwRetVal * sizeof(WCHAR), defaultValue, dwRetVal * sizeof(WCHAR)); |
83 | } |
84 | } |
85 | |
86 | return retString; |
87 | } |
88 | |
89 | #ifdef FEATURE_PAL |
90 | // For some reason, the PAL doesn't have GetCommandLineA(). So write it. |
91 | LPSTR GetCommandLineA() |
92 | { |
93 | LPSTR pCmdLine = nullptr; |
94 | LPWSTR pwCmdLine = GetCommandLineW(); |
95 | |
96 | if (pwCmdLine != nullptr) |
97 | { |
98 | // Convert to ASCII |
99 | |
100 | int n = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, nullptr, 0, nullptr, nullptr); |
101 | if (n == 0) |
102 | { |
103 | LogError("MultiByteToWideChar failed %d" , GetLastError()); |
104 | return nullptr; |
105 | } |
106 | |
107 | pCmdLine = new char[n]; |
108 | |
109 | int n2 = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, pCmdLine, n, nullptr, nullptr); |
110 | if ((n2 == 0) || (n2 != n)) |
111 | { |
112 | LogError("MultiByteToWideChar failed %d" , GetLastError()); |
113 | return nullptr; |
114 | } |
115 | } |
116 | |
117 | return pCmdLine; |
118 | } |
119 | #endif // FEATURE_PAL |
120 | |
121 | bool LoadRealJitLib(HMODULE& jitLib, WCHAR* jitLibPath) |
122 | { |
123 | // Load Library |
124 | if (jitLib == NULL) |
125 | { |
126 | if (jitLibPath == nullptr) |
127 | { |
128 | LogError("LoadRealJitLib - No real jit path" ); |
129 | return false; |
130 | } |
131 | jitLib = ::LoadLibraryW(jitLibPath); |
132 | if (jitLib == NULL) |
133 | { |
134 | LogError("LoadRealJitLib - LoadLibrary failed to load '%ws' (0x%08x)" , jitLibPath, ::GetLastError()); |
135 | return false; |
136 | } |
137 | } |
138 | return true; |
139 | } |
140 | |
141 | void cleanupExecutableName(WCHAR* executableName) |
142 | { |
143 | WCHAR* quote = nullptr; |
144 | |
145 | // If there are any quotes in the file name convert them to spaces. |
146 | while ((quote = wcsstr(executableName, W("\"" ))) != nullptr) |
147 | { |
148 | *quote = W(' '); |
149 | } |
150 | |
151 | // Remove any illegal or annoying characters from the file name by converting them to underscores. |
152 | while ((quote = wcspbrk(executableName, W("=<>:\"/\\|?! *.," ))) != nullptr) |
153 | { |
154 | *quote = W('_'); |
155 | } |
156 | } |
157 | |
158 | void generateRandomSuffix(WCHAR* buffer, size_t length) |
159 | { |
160 | unsigned randNumber = 0; |
161 | #ifdef FEATURE_PAL |
162 | PAL_Random(&randNumber, sizeof(randNumber)); |
163 | #else // !FEATURE_PAL |
164 | rand_s(&randNumber); |
165 | #endif // !FEATURE_PAL |
166 | |
167 | swprintf_s(buffer, length, W("%08X" ), randNumber); |
168 | } |
169 | |
170 | // All lengths in this function exclude the terminal NULL. |
171 | WCHAR* getResultFileName(const WCHAR* folderPath, WCHAR* executableName, const WCHAR* extension) |
172 | { |
173 | cleanupExecutableName(executableName); |
174 | |
175 | size_t executableNameLength = wcslen(executableName); |
176 | size_t extensionLength = wcslen(extension); |
177 | size_t folderPathLength = wcslen(folderPath); |
178 | |
179 | size_t dataFileNameLength = folderPathLength + 1 + executableNameLength + 1 + extensionLength; |
180 | |
181 | const size_t maxAcceptablePathLength = |
182 | MAX_PATH - 50; // subtract 50 because excel doesn't like paths longer then 230. |
183 | |
184 | if (dataFileNameLength > maxAcceptablePathLength) |
185 | { |
186 | // The path name is too long; creating the file will fail. This can happen because we use the command line, |
187 | // which for ngen includes lots of environment variables, for example. |
188 | // Shorten the executable file name and add a random string to it to avoid collisions. |
189 | |
190 | const size_t randStringLength = 8; |
191 | |
192 | size_t lengthToBeDeleted = (dataFileNameLength - maxAcceptablePathLength) + randStringLength; |
193 | |
194 | if (executableNameLength <= lengthToBeDeleted) |
195 | { |
196 | LogError("getResultFileName - path to the output file is too long '%ws\\%ws.%ws(%d)'" , folderPath, |
197 | executableName, extension, dataFileNameLength); |
198 | return nullptr; |
199 | } |
200 | |
201 | executableNameLength -= lengthToBeDeleted; |
202 | executableName[executableNameLength] = 0; |
203 | |
204 | executableNameLength += randStringLength; |
205 | WCHAR randNumberString[randStringLength + 1]; |
206 | generateRandomSuffix(randNumberString, randStringLength + 1); |
207 | wcsncat_s(executableName, executableNameLength + 1, randNumberString, randStringLength); |
208 | |
209 | executableNameLength = wcslen(executableName); |
210 | |
211 | dataFileNameLength = folderPathLength + 1 + executableNameLength + 1 + extensionLength; |
212 | if (dataFileNameLength > maxAcceptablePathLength) |
213 | { |
214 | LogError("getResultFileName - were not able to short the result file name." , folderPath, executableName, |
215 | extension, dataFileNameLength); |
216 | return nullptr; |
217 | } |
218 | } |
219 | |
220 | WCHAR* dataFileName = new WCHAR[dataFileNameLength + 1]; |
221 | dataFileName[0] = 0; |
222 | wcsncat_s(dataFileName, dataFileNameLength + 1, folderPath, folderPathLength); |
223 | wcsncat_s(dataFileName, dataFileNameLength + 1, DIRECTORY_SEPARATOR_STR_W, 1); |
224 | wcsncat_s(dataFileName, dataFileNameLength + 1, executableName, executableNameLength); |
225 | wcsncat_s(dataFileName, dataFileNameLength + 1, extension, extensionLength); |
226 | return dataFileName; |
227 | } |
228 | |