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
14static bool breakOnDebugBreakorAV = false;
15
16bool BreakOnDebugBreakorAV()
17{
18 return breakOnDebugBreakorAV;
19}
20
21void SetBreakOnDebugBreakOrAV(bool value)
22{
23 breakOnDebugBreakorAV = value;
24}
25
26void 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
41char* 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
65WCHAR* 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.
91LPSTR 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
121bool 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
141void 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
158void 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.
171WCHAR* 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