Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
688e2b0
Use STL for MethodCallSummarizer
huoyaoyuan May 22, 2025
6df1501
Use std::string for manipulating file name
huoyaoyuan Jun 8, 2025
0ba0e96
Use std::string for global strings
huoyaoyuan Jun 8, 2025
ff5058e
Use unique_ptr for destruction
huoyaoyuan Jun 8, 2025
01f4be7
Use RAII for logger
huoyaoyuan Jun 8, 2025
0a95bc9
Load JIT with std::string
huoyaoyuan Jun 8, 2025
84b95f3
Enable C++ 17 for superpmi
huoyaoyuan Jun 8, 2025
9d8e705
Use std::filesystem for path manipulation
huoyaoyuan Jun 8, 2025
137e9e3
Use dlsym since dlopen has been used
huoyaoyuan Jun 8, 2025
65cdfa5
Port changes to other shims
huoyaoyuan Jun 8, 2025
67d2f11
Update g_dataFileName
huoyaoyuan Jun 8, 2025
ce5d486
Cleanup
huoyaoyuan Jun 8, 2025
93224cf
Don't replace . in extension
huoyaoyuan Jun 10, 2025
08d99e4
Limit result file path
huoyaoyuan Jun 10, 2025
b22e3a9
Add comment about destructor
huoyaoyuan Jun 10, 2025
407af70
Fix sign/unsigned mismatch
huoyaoyuan Jun 10, 2025
af049c7
Merge branch 'main' into superpmi/stl1
huoyaoyuan Aug 12, 2025
9f1fcc8
Remove C++ 17 and fstream usage
huoyaoyuan Aug 12, 2025
70152a8
Remove usages of std::filesystem::path
huoyaoyuan Aug 12, 2025
4db4066
Don't introduce fstream
huoyaoyuan Aug 12, 2025
58183b3
Cleanup UNIX usage
huoyaoyuan Aug 12, 2025
b3ce44d
Mark method as private
huoyaoyuan Aug 13, 2025
7e81613
make_unique is not available in C++ 11
huoyaoyuan Aug 13, 2025
c169695
Update LoadLibrary error checking
huoyaoyuan Aug 13, 2025
f0f1857
Handle long cmdline
huoyaoyuan Aug 13, 2025
b1d2987
Use getline instead
huoyaoyuan Aug 13, 2025
3d10f49
Merge branch 'main' into superpmi/stl1
huoyaoyuan Aug 14, 2025
899f12d
Handle \0 delimiter
huoyaoyuan Aug 14, 2025
16f6ee1
Update src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp
huoyaoyuan Aug 14, 2025
dd225a6
Apply suggestions from code review
huoyaoyuan Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/tools/superpmi/superpmi-shared/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ void Logger::Shutdown()
// Opens a log file at the given path and enables file-based logging, if the given path is valid.
//
/* static */
void Logger::OpenLogFile(char* logFilePath)
void Logger::OpenLogFile(const char* logFilePath)
{
if (s_logFile == INVALID_HANDLE_VALUE && logFilePath != nullptr)
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/tools/superpmi/superpmi-shared/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Logger
static void Initialize();
static void Shutdown();

static void OpenLogFile(char* logFilePath);
static void OpenLogFile(const char* logFilePath);
static void CloseLogFile();

static UINT32 ParseLogLevelString(const char* specifierStr);
Expand Down
164 changes: 75 additions & 89 deletions src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "logging.h"
#include "spmiutil.h"

#include <sstream>
#include <iomanip>
#include <minipal/debugger.h>
#include <minipal/random.h>

Expand Down Expand Up @@ -99,86 +101,103 @@ WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR*
return retString;
}

#ifdef TARGET_UNIX
// For some reason, the PAL doesn't have GetCommandLineA(). So write it.
LPSTR GetCommandLineA()
const char* GetEnvWithDefault(const char* envVarName, const char* defaultValue)
{
LPSTR pCmdLine = nullptr;
LPWSTR pwCmdLine = GetCommandLineW();
// getenv isn't thread safe, but it's simple and sufficient since we are development-only tool
char* env = getenv(envVarName);
return env ? env : defaultValue;
}

if (pwCmdLine != nullptr)
std::string GetProcessCommandLine()
{
#ifdef TARGET_WINDOWS
return ::GetCommandLineA();
#else
FILE* fp = fopen("/proc/self/cmdline", "r");
if (fp != NULL)
{
// Convert to ASCII
std::string result;
char* cmdLine = nullptr;
size_t size = 0;

int n = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, nullptr, 0, nullptr, nullptr);
if (n == 0)
while (getdelim(&cmdLine, &size, '\0', fp) != -1)
{
LogError("MultiByteToWideChar failed %d", GetLastError());
return nullptr;
}
// /proc/self/cmdline uses \0 as delimiter, convert it to space
if (!result.empty())
result += ' ';

pCmdLine = new char[n];
result += cmdLine;

int n2 = WideCharToMultiByte(CP_ACP, 0, pwCmdLine, -1, pCmdLine, n, nullptr, nullptr);
if ((n2 == 0) || (n2 != n))
{
LogError("MultiByteToWideChar failed %d", GetLastError());
return nullptr;
free(cmdLine);
cmdLine = nullptr;
size = 0;
}

fclose(fp);
return result;
}

return pCmdLine;
return "";
#endif
}
#endif // TARGET_UNIX

bool LoadRealJitLib(HMODULE& jitLib, WCHAR* jitLibPath)
bool LoadRealJitLib(HMODULE& jitLib, const std::string& jitLibPath)
{
// Load Library
if (jitLib == NULL)
{
if (jitLibPath == nullptr)
if (jitLibPath.empty())
{
LogError("LoadRealJitLib - No real jit path");
return false;
}
jitLib = ::LoadLibraryExW(jitLibPath, NULL, 0);
#ifdef TARGET_WINDOWS
jitLib = ::LoadLibraryExA(jitLibPath.c_str(), NULL, 0);
if (jitLib == NULL)
{
LogError("LoadRealJitLib - LoadLibrary failed to load '%ws' (0x%08x)", jitLibPath, ::GetLastError());
LogError("LoadRealJitLib - LoadLibrary failed to load '%s' (0x%08x)", jitLibPath.c_str(), ::GetLastError());
return false;
}
#else
jitLib = ::dlopen(jitLibPath.c_str(), RTLD_LAZY);
// The simulated DllMain of JIT doesn't do any meaningful initialization. Skip it.
if (jitLib == NULL)
{
LogError("LoadRealJitLib - dlopen failed to load '%s' (%s)", jitLibPath.c_str(), ::dlerror());
return false;
}
#endif
}
return true;
}

void ReplaceIllegalCharacters(WCHAR* fileName)
void ReplaceIllegalCharacters(std::string& fileName)
{
WCHAR* quote = nullptr;

// Perform the following transforms:
// - Convert non-ASCII to ASCII for simplicity
// - Remove any illegal or annoying characters from the file name by
// converting them to underscores.
// - Replace any quotes in the file name with spaces.
for (quote = fileName; *quote != '\0'; quote++)

for (char& quote : fileName)
{
WCHAR ch = *quote;
char ch = quote;
if ((ch <= 32) || (ch >= 127)) // Only allow textual ASCII characters
{
*quote = W('_');
quote = '_';
}
else
{
switch (ch)
{
case W('('): case W(')'): case W('='): case W('<'):
case W('>'): case W(':'): case W('/'): case W('\\'):
case W('|'): case W('?'): case W('!'): case W('*'):
case W('.'): case W(','):
*quote = W('_');
case '(': case ')': case '=': case '<':
case '>': case ':': case '/': case '\\':
case '|': case '?': case '!': case '*':
case '.': case ',':
quote = '_';
break;
case W('"'):
*quote = W(' ');
case '"':
quote = ' ';
break;
default:
break;
Expand All @@ -187,67 +206,34 @@ void ReplaceIllegalCharacters(WCHAR* fileName)
}
}

// All lengths in this function exclude the terminal NULL.
WCHAR* GetResultFileName(const WCHAR* folderPath, const WCHAR* fileName, const WCHAR* extension)
std::string GetResultFileName(const std::string& folderPath,
const std::string& fileName,
const std::string& extension)
{
const size_t extensionLength = u16_strlen(extension);
const size_t fileNameLength = u16_strlen(fileName);
const size_t randomStringLength = 8;
const size_t maxPathLength = MAX_PATH - 50;

// See how long the folder part is, and start building the file path with the folder part.
// Append a random string to improve uniqueness.
//
WCHAR* fullPath = new WCHAR[MAX_PATH];
fullPath[0] = W('\0');
const size_t folderPathLength = GetFullPathNameW(folderPath, MAX_PATH, (LPWSTR)fullPath, NULL);
uint32_t randomNumber = 0;
minipal_get_non_cryptographically_secure_random_bytes((uint8_t*)&randomNumber, sizeof(randomNumber));
std::stringstream ss;
ss << std::hex << std::setw(8) << std::setfill('0') << randomNumber;
std::string suffix = ss.str() + extension;

if (folderPathLength == 0)
// Limit the total file name length to MAX_PATH - 50
int usableLength = MAX_PATH - 50 - (int)folderPath.size() - (int)suffix.size();
if (usableLength < 0)
{
LogError("GetResultFileName - can't resolve folder path '%ws'", folderPath);
return nullptr;
LogError("GetResultFileName - folder path '%s' length + minimal file name exceeds limit %d", folderPath.c_str(), MAX_PATH - 50);
return "";
}

// Account for the folder, directory separator and extension.
//
size_t fullPathLength = folderPathLength + 1 + extensionLength;

// If we won't have room for a minimal file name part, bail.
//
if ((fullPathLength + randomStringLength) > maxPathLength)
std::string copy = fileName;
if ((int)copy.size() > usableLength)
{
LogError("GetResultFileName - folder path '%ws' length + minimal file name exceeds limit %d", fullPath, maxPathLength);
return nullptr;
copy = copy.substr(0, usableLength);
}

// Now figure out the file name part.
//
const size_t maxFileNameLength = maxPathLength - fullPathLength;
size_t usableFileNameLength = min(fileNameLength, maxFileNameLength - randomStringLength);
fullPathLength += usableFileNameLength + randomStringLength;

// Append the file name part
//
wcsncat_s(fullPath, fullPathLength + 1, DIRECTORY_SEPARATOR_STR_W, 1);
wcsncat_s(fullPath, fullPathLength + 1, fileName, usableFileNameLength);

// Clean up anything in the file part that can't be in a file name.
//
ReplaceIllegalCharacters(fullPath + folderPathLength + 1);

// Append a random string to improve uniqueness.
//
unsigned int randomNumber = 0;
minipal_get_non_cryptographically_secure_random_bytes((uint8_t*)&randomNumber, sizeof(randomNumber));

WCHAR randomString[randomStringLength + 1];
FormatInteger(randomString, randomStringLength + 1, "%08X", randomNumber);
wcsncat_s(fullPath, fullPathLength + 1, randomString, randomStringLength);

// Append extension
//
wcsncat_s(fullPath, fullPathLength + 1, extension, extensionLength);

return fullPath;
ReplaceIllegalCharacters(copy);
return folderPath + DIRECTORY_SEPARATOR_CHAR_A + copy + suffix;
}

#ifdef TARGET_AMD64
Expand Down
18 changes: 13 additions & 5 deletions src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
#define _SPMIUtil

#include "methodcontext.h"
#ifdef TARGET_WINDOWS
#define GET_PROC_ADDRESS ::GetProcAddress
#else
#include <dlfcn.h>
#define GET_PROC_ADDRESS ::dlsym
#endif

bool BreakOnDebugBreakorAV();
void SetBreakOnDebugBreakOrAV(bool value);
Expand All @@ -21,13 +27,15 @@ char* GetEnvironmentVariableWithDefaultA(const char* envVarName, const char* def

WCHAR* GetEnvironmentVariableWithDefaultW(const WCHAR* envVarName, const WCHAR* defaultValue = nullptr);

#ifdef TARGET_UNIX
LPSTR GetCommandLineA();
#endif // TARGET_UNIX
const char* GetEnvWithDefault(const char* envVarName, const char* defaultValue = nullptr);

bool LoadRealJitLib(HMODULE& realJit, WCHAR* realJitPath);
std::string GetProcessCommandLine();

WCHAR* GetResultFileName(const WCHAR* folderPath, const WCHAR* fileName, const WCHAR* extension);
bool LoadRealJitLib(HMODULE& realJit, const std::string& realJitPath);

std::string GetResultFileName(const std::string& folderPath,
const std::string& fileName,
const std::string& extension);

// SuperPMI stores handles as unsigned 64-bit integers, no matter the platform the collection happens on
// (32 or 64 bit). Handles are defined as pointers. We need to be careful when converting from a handle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ CorJitResult interceptor_ICJC::compileMethod(ICorJitInfo* comp,
auto* mc = new MethodContext();
interceptor_ICJI our_ICorJitInfo(this, comp, mc);

mc->cr->recProcessName(GetCommandLineA());
mc->cr->recProcessName(GetProcessCommandLine().c_str());

mc->recCompileMethod(info, flags, currentOs);

Expand Down
Loading
Loading