Skip to content

Commit

Permalink
Include info about system call errors in some exceptions from operati…
Browse files Browse the repository at this point in the history
…ng on named mutexes

- Added `BeginTrackingSystemCallErrors` and `EndTrackingSystemCallErrors`, and called them around some named mutex operations
- `BeginTrackingSystemCallErrors` starts tracking system call errors in the relevant PAL APIs. When there is a system call failure that leads to the PAL API failing, some info is appeneded to a string held on the thread, including the system call, relevant arguments, return value, and `errno`.
- `EndTrackingSystemCallErrors` stops tracking system call errors in the relevant PAL APIs and returns the system call errors accumulated so far.
- When throwing an exception that doesn't already indicate clearly the reason for the exception, and there are system call errors to report, it would have an inner exception of type `SystemException` that contains the system call errors
- `chmod` on OSX seemingly can be interrupted by signals, fixed to retry. Also fixed a couple other small things.

Fixes dotnet#89090
  • Loading branch information
kouvel committed Sep 25, 2023
1 parent 51a694b commit c11ecef
Show file tree
Hide file tree
Showing 18 changed files with 663 additions and 42 deletions.
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
/// 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])
{
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

0 comments on commit c11ecef

Please sign in to comment.