Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include info about system call errors in some exceptions from operating on named mutexes #92603

Merged
merged 7 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,29 @@ private static unsafe void WriteValueSlow<T>(object ptr, int ofs, T val, Action<
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void SetLastPInvokeError(int error);

/// <summary>
/// Begins tracking system call errors on the calling thread during PAL APIs. The system call errors may include
/// information about the system call made, relevant arguments, return values, and error codes. A call to this method
/// should be followed by a call to <see cref="EndTrackingSystemCallErrors"/> on the same thread, which returns the set
/// of system call errors that occurred on the thread in that period. Only system call errors that lead to PAL API
/// failures may be tracked.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void BeginTrackingSystemCallErrors();

/// <summary>
/// Retrieves system call errors that occurred on the calling thread since <see cref="BeginTrackingSystemCallErrors"/>
/// was called.
/// </summary>
/// <param name="getSystemCallErrors">Indicates whether to return the accumulated system call errors.</param>
/// <returns>
/// System call errors that occurred on the calling thread since <see cref="BeginTrackingSystemCallErrors"/> was called.
/// Returns <code>null</code> if <see cref="BeginTrackingSystemCallErrors"/> has not been called, or if no system call
/// errors occurred on the calling thread since it was last called.
/// </returns>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string EndTrackingSystemCallErrors(bool getSystemCallErrors);

private static void PrelinkCore(MethodInfo m)
{
if (!(m is RuntimeMethodInfo rmi))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,27 @@ public static void SetLastPInvokeError(int error)
PInvokeMarshal.t_lastError = error;
}

/// <summary>
/// Begins tracking system call errors on the calling thread during PAL APIs. The system call errors may include
kouvel marked this conversation as resolved.
Show resolved Hide resolved
/// information about the system call made, relevant arguments, return values, and error codes. A call to this method
/// should be followed by a call to <see cref="EndTrackingSystemCallErrors"/> on the same thread, which returns the set
/// of system call errors that occurred on the thread in that period. Only system call errors that lead to PAL API
/// failures may be tracked.
/// </summary>
internal static void BeginTrackingSystemCallErrors() { }

/// <summary>
/// Retrieves system call errors that occurred on the calling thread since <see cref="BeginTrackingSystemCallErrors"/>
/// was called.
/// </summary>
/// <param name="getSystemCallErrors">Indicates whether to return the accumulated system call errors.</param>
/// <returns>
/// System call errors that occurred on the calling thread since <see cref="BeginTrackingSystemCallErrors"/> was called.
/// Returns <code>null</code> if <see cref="BeginTrackingSystemCallErrors"/> has not been called, or if no system call
/// errors occurred on the calling thread since it was last called.
/// </returns>
internal static string EndTrackingSystemCallErrors(bool getSystemCallErrors) => null;

internal static bool IsPinnable(object o)
{
return (o == null) || !o.GetEETypePtr().ContainsGCPointers;
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -3792,6 +3792,16 @@ PALAPI
SetLastError(
IN DWORD dwErrCode);

PALIMPORT
VOID
PALAPI
PAL_BeginTrackingSystemCallErrors();

PALIMPORT
LPCSTR
PALAPI
PAL_EndTrackingSystemCallErrors(bool getSystemCallErrors);

PALIMPORT
LPWSTR
PALAPI
Expand Down
8 changes: 5 additions & 3 deletions src/coreclr/pal/src/include/pal/sharedmemory.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,12 @@ class SharedMemoryHelpers
static int CreateOrOpenFile(LPCSTR path, bool createIfNotExist = true, bool *createdRef = nullptr);
static void CloseFile(int fileDescriptor);

static SIZE_T GetFileSize(int fileDescriptor);
static void SetFileSize(int fileDescriptor, SIZE_T byteCount);
static int ChangeMode(LPCSTR path, mode_t mode);

static void *MemoryMapFile(int fileDescriptor, SIZE_T byteCount);
static SIZE_T GetFileSize(LPCSTR filePath, int fileDescriptor);
static void SetFileSize(LPCSTR filePath, int fileDescriptor, SIZE_T byteCount);

static void *MemoryMapFile(LPCSTR filePath, int fileDescriptor, SIZE_T byteCount);

static bool TryAcquireFileLock(int fileDescriptor, int operation);
static void ReleaseFileLock(int fileDescriptor);
Expand Down
25 changes: 24 additions & 1 deletion src/coreclr/pal/src/include/pal/thread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ namespace CorUnix
// Signal handler's alternate stack to help with stack overflow
void* m_alternateStack;

bool m_isTrackingSystemCallErrors;
int m_systemCallErrorsLength;
char *m_systemCallErrors;

//
// The thread entry routine (called from InternalCreateThread)
//
Expand Down Expand Up @@ -358,7 +362,10 @@ namespace CorUnix
m_fStartStatusSet(FALSE),
m_stackBase(NULL),
m_stackLimit(NULL),
m_alternateStack(NULL)
m_alternateStack(NULL),
m_isTrackingSystemCallErrors(false),
m_systemCallErrorsLength(0),
m_systemCallErrors(NULL)
{
};

Expand Down Expand Up @@ -460,6 +467,22 @@ namespace CorUnix
return errno;
};

void
BeginTrackingSystemCallErrors(
void
);

static void
AppendSystemCallError(
LPCSTR format,
...
);

LPCSTR
EndTrackingSystemCallErrors(
bool getSystemCallErrors
);

void
SetExitCode(
DWORD dwExitCode
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/pal/src/include/pal/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,6 @@ class StringHolder

};
#endif /* _PAL_UTILS_H_ */

const int RawErrorCodeStringBufferSize = 12;
const char *GetFriendlyErrorCodeString(int errorCode, char (&rawErrorCodeStringBuffer)[RawErrorCodeStringBufferSize]);
67 changes: 67 additions & 0 deletions src/coreclr/pal/src/misc/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,70 @@ SetLastError(
CPalThread::SetLastError(dwErrCode);
}

/*++
Function:
PAL_BeginTrackingSystemCallErrors

PAL_BeginTrackingSystemCallErrors

The PAL_BeginTrackingSystemCallErrors function begins tracking system call errors on the calling thread during PAL APIs. The
system call errors may include information about the system call made, relevant arguments, return values, and error codes. A
call to this function should be followed by a call to PAL_EndTrackingSystemCallErrors on the same thread, which returns the set
of system call errors that occurred on the thread in that period. This may not track all system call errors, it may only track
system call errors that directly or indirectly lead to PAL API failures.

Parameters

This function has no parameters.

Return Values

This function does not return a value.

--*/
VOID
PALAPI
PAL_BeginTrackingSystemCallErrors(
VOID)
{
CPalThread *thread = GetCurrentPalThread();
if (thread != nullptr)
{
thread->BeginTrackingSystemCallErrors();
}
}

/*++
Function:
PAL_EndTrackingSystemCallErrors

PAL_EndTrackingSystemCallErrors

The PAL_EndTrackingSystemCallErrors function retrieves system call errors that occurred on the calling thread since
PAL_BeginTrackingSystemCallErrors was called.

Parameters

This function has no parameters.

Return Values

The return value is the system call errors that occurred on the calling thread since PAL_BeginTrackingSystemCallErrors was
called. Returns NULL if PAL_BeginTrackingSystemCallErrors has not been called, or if no system call errors occurred on the
calling thread since it was last called. If the returned pointer is not NULL, it is only safe to be used by the calling thread
and before the next call to PAL_BeginTrackingSystemCallErrors.

--*/
LPCSTR
PALAPI
PAL_EndTrackingSystemCallErrors(
bool getSystemCallErrors)
{
CPalThread *thread = GetCurrentPalThread();
if (thread != nullptr)
{
return thread->EndTrackingSystemCallErrors(getSystemCallErrors);
}

return nullptr;
}
58 changes: 58 additions & 0 deletions src/coreclr/pal/src/misc/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,61 @@ BOOL IsRunningOnMojaveHardenedRuntime()
}

#endif // __APPLE__

const char *GetFriendlyErrorCodeString(int errorCode, char (&rawErrorCodeStringBuffer)[RawErrorCodeStringBufferSize])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it wouldn't be better to use strerror_r (or one of the similar ones) to get a detailed description of the error instead. It tends to contain more useful information than just the error code.
If you want to stick just with the error code name, then there already is an API that does what you have here, the strerrorname_np

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like strerrorname_np doesn't exist everywhere, I added a check for it and retained the switch as a fallback. Also changed to use strerror for unknown errors.

{
switch (errorCode)
{
case EACCES: return "EACCES";
case EBADF: return "EBADF";
case EBUSY: return "EBUSY";
case EDQUOT: return "EDQUOT";
case EEXIST: return "EEXIST";
case EFAULT: return "EFAULT";
case EFBIG: return "EFBIG";
case EINVAL: return "EINVAL";
case EINTR: return "EINTR";
case EIO: return "EIO";
case EISDIR: return "EISDIR";
case ELOOP: return "ELOOP";
case EMFILE: return "EMFILE";
case EMLINK: return "EMLINK";
case ENAMETOOLONG: return "ENAMETOOLONG";
case ENFILE: return "ENFILE";
case ENODEV: return "ENODEV";
case ENOENT: return "ENOENT";
case ENOLCK: return "ENOLCK";
case ENOMEM: return "ENOMEM";
case ENOSPC: return "ENOSPC";
case ENOTDIR: return "ENOTDIR";
case ENOTEMPTY: return "ENOTEMPTY";
case ENXIO: return "ENXIO";
case EOVERFLOW: return "EOVERFLOW";
case EPERM: return "EPERM";
case EROFS: return "EROFS";
case ETXTBSY: return "ETXTBSY";
case EXDEV: return "EXDEV";
}

if (errorCode == EAGAIN || errorCode == EWOULDBLOCK)
{
if (EAGAIN == EWOULDBLOCK) return "EAGAIN/EWOULDBLOCK";
if (errorCode == EAGAIN) return "EAGAIN";
return "EWOULDBLOCK";
}
else if (errorCode == ENOTSUP || errorCode == EOPNOTSUPP)
{
if (ENOTSUP == EOPNOTSUPP) return "ENOTSUP/EOPNOTSUPP";
if (errorCode == ENOTSUP) return "ENOTSUP";
return "EOPNOTSUPP";
}

int result =
_snprintf_s(rawErrorCodeStringBuffer, RawErrorCodeStringBufferSize, RawErrorCodeStringBufferSize - 1, "%d", errorCode);
if (result <= 0 || result >= RawErrorCodeStringBufferSize)
{
rawErrorCodeStringBuffer[0] = '\0';
}

return rawErrorCodeStringBuffer;
}
Loading